@patimweb/pi-email

IMAP/SMTP email client extension for pi coding agent. Read, search, send, move, and delete emails from your inbox.

Packages

Package details

extension

Install @patimweb/pi-email from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@patimweb/pi-email
Package
@patimweb/pi-email
Version
2.1.0
Published
Sep 22, 2026
Downloads
1,098/mo · 76/wk
Author
patimwep
License
MIT
Types
extension
Size
1.2 MB
Dependencies
4 dependencies · 0 peers
Pi manifest JSON
{
  "image": "https://raw.githubusercontent.com/Smotherer007/pi-email/main/screenshot.png",
  "extensions": [
    "./index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-email-client

IMAP/SMTP email client extension for the pi coding agent.

Read, search, send, move, and delete emails directly from your pi session. Credentials are stored locally in ~/.pi/email-config.json.

Installation

# Install from npm (once published)
pi install npm:@patimweb/pi-email

# Install from local path during development
pi install /path/to/pi-email-client

Quick Start

  1. Configure your email account using the email_setup tool
  2. Fetch recent inbox emails with email_fetch
  3. Read full email bodies with email_read
  4. Send emails with email_send
  5. Reply to emails with email_reply (auto-threading)
  6. Forward emails with email_forward
  7. Mark emails as read/unread/flagged with email_flag

Example configuration for Gmail (requires an app-specific password):

email_setup:
  name: gmail
  imapHost: imap.gmail.com
  imapPort: 993
  imapTls: true
  imapUser: you@gmail.com
  imapPassword: <app-password>
  smtpHost: smtp.gmail.com
  smtpPort: 587
  smtpSecure: false
  smtpUser: you@gmail.com
  smtpPassword: <app-password>
  fromName: Your Name

For the ProtonMail Bridge, which runs locally on your machine, you can use the following prompt:

Use the email_setup tool to configure a an email account with the following details:
 - name: "YOUR_PROFILE_NAME"                                                                                                                             
 - fromName: "YOUR_NAME"                                                                                                                     
 - imapUser: "YOUR_EMAIL"                                                                                                            
 - imapPassword: "YOUR_PASSWORD"                                                                                    
 - imapHost: "127.0.0.1"                                                                                                               
 - imapPort: 1143                                                                                                                           
 - imapTls: false                                                                                                                          
 - smtpUser: "YOUR_EMAIL"                                                                                                            
 - smtpPassword: "YOUR_PASSWORD"                                                                                    
 - smtpHost: "127.0.0.1"                                                                                                               
 - smtpPort: 1025                                                                                                                    
 - smtpSecure: false                                                                                                                         
 - smtpRejectUnauthorized: false

Microsoft 365 / Outlook work accounts (OAuth)

Microsoft 365 (Exchange Online) work and school accounts do not accept passwords, and many companies switch IMAP and SMTP AUTH off entirely. These accounts sign in with OAuth2 via the /email-login-microsoft command and, by default, read and send mail through Microsoft Graph -- no IMAP or SMTP needed. Password profiles for all other providers keep working unchanged.

/email-login-microsoft work pat@example.com

The command prints a Microsoft login URL and starts a small callback server on 127.0.0.1:1456. Open the URL, sign in (MFA works as usual), and the browser is redirected back to pi. The profile is saved and set active; access tokens are refreshed automatically and the refresh token is stored in ~/.pi/email-config.json (mode 600).

pi runs on a remote machine (SSH, container, VM)? Forward the callback port from the machine where your browser runs before you open the URL:

ssh -L 1456:127.0.0.1:1456 user@remote-host

Inside a Docker container, bind the callback to all interfaces (PI_OAUTH_CALLBACK_HOST=0.0.0.0) and forward to the container IP, or publish the port. Without any tunnel the browser ends on an unreachable localhost page after login -- copy that page's address and paste it into the prompt pi shows.

How Graph profiles behave

All tools work the same way, with these differences:

  • Message ids are Graph ids (long strings) instead of numeric UIDs. They stay stable when a message is moved.
  • Mailboxes are addressed by path (Posteingang/Kunden) or by the usual aliases (INBOX, Sent, Drafts, Trash, Junk, Archive and their German names).
  • email_delete moves the message to Deleted Items (recoverable).
  • email_flag supports Seen and Flagged.
  • Sent mail is filed in Sent Items by Exchange itself.
  • A single message (including attachments) is limited to about 3 MB.

To use IMAP/SMTP with OAuth instead (tenants that allow it), log in with /email-login-microsoft work --imap or set PI_EMAIL_MS_API=outlook.

App registration (once per organization)

  1. Microsoft Entra admin center → App registrationsNew registration. Account type: this organization only or any organization.
  2. AuthenticationAdd a platformMobile and desktop applications → redirect URI http://localhost (the port is not part of the match).
  3. API permissions → Microsoft Graph → Delegated: Mail.ReadWrite, Mail.Send, offline_access, openid, profile, email. Grant admin consent if your tenant requires it. (For --imap: IMAP.AccessAsUser.All and SMTP.Send instead.)
  4. Copy the Application (client) ID. pi asks for it on first login, or set it up front:
export PI_EMAIL_MS_CLIENT_ID=<client-id>
export PI_EMAIL_MS_TENANT=<tenant-id or domain>   # optional, default "organizations"
export PI_EMAIL_OAUTH_PORT=1456                    # optional callback port

The permissions are delegated: the app only ever reaches the mailbox of the signed-in user.

Tools

Tool Description
email_setup Configure IMAP/SMTP credentials. Must be called first.
email_status Show current connection status and configured account.
email_profile List, switch, or delete email profiles (multi-account support).
email_list_mailboxes List all available IMAP folders.
email_fetch Fetch email headers from a mailbox (from, subject, date, flags).
email_read Read the full body of a specific email by UID. Can save attachments.
email_search Search emails with IMAP criteria (from, subject, body, date range, unseen).
email_send Send an email via SMTP (plain text, HTML, CC, BCC, local file attachments, optional custom sender). Stores a copy in the Sent folder unless the provider does that itself.
email_reply Reply to an email. Auto-sets In-Reply-To/References headers for threading. Supports reply-all and quoting.
email_draft_reply Create a server-side reply draft (IMAP \Draft) for manual review instead of sending.
email_forward Forward an email to new recipients with inline forwarding headers.
email_flag Set or remove IMAP flags (Seen/Unseen, Flagged, Answered, etc.).
email_delete Delete an email by UID.
email_move Move an email to another mailbox.

Replying to emails

email_reply answers an email by UID and automatically sets threading headers so your reply appears in the correct conversation thread. By default, it quotes the original message.

email_reply:
  uid: 42
  body: "Thanks, got it!"
  # quoteOriginal: false   # disable quoting
  # replyAll: true          # include all original recipients

Draft replies

email_draft_reply composes a reply but never sends it — it stores the reply as an IMAP \Draft in the Drafts folder (with X-Unsent: 1), so you can review and send it yourself from your mail client. It sets the same threading headers as email_reply.

email_draft_reply:
  uid: 42
  body: "Thanks, got it!"
  # draftMailbox: Drafts   # custom drafts folder
  # replyAll: true          # include all original recipients

Forwarding emails

email_forward sends a copy of an email to new recipients with forwarding headers inline in the body. Original attachment names are listed. To re-attach files, first use email_read with downloadDir, then email_send with attachmentPaths.

email_forward:
  uid: 42
  to: colleague@example.com
  body: "FYI, see below."
  # cc: manager@example.com

Managing flags

email_flag sets or removes IMAP flags on an email. Supports friendly aliases like read, starred, replied.

# Mark as read
email_flag:
  uid: 42
  add: ["Seen"]

# Mark as unread and starred
email_flag:
  uid: 42
  add: ["Flagged"]
  remove: ["Seen"]

Supported flag aliases: Seen / read / unread, Flagged / starred, Answered / replied, Draft, Deleted.

Sending attachments

email_send accepts attachmentPaths, an array of local filesystem paths. Absolute paths are safest. URLs and data URIs are not supported.

email_send:
  to: recipient@example.com
  subject: Report
  body: Attached.
  attachmentPaths:
    - /path/to/report.pdf

Sending from a different address

email_send accepts optional from and fromName to send from a different address than the configured account (e.g. an alias). Whether the provider accepts it depends on your SMTP setup. Only the address is passed to nodemailer as a structured { name, address } object, so quoting and RFC 2047 encoding happen automatically.

email_send:
  from: alias@example.com
  fromName: Alias Name
  to: recipient@example.com
  subject: Report
  body: Attached.

Commands

Command Description
/email-login-microsoft [profile] [email] [--imap] Sign in a Microsoft 365 work account via OAuth; uses Microsoft Graph unless --imap (see above).
/inbox Trigger the agent to fetch recent inbox emails.

Configuration

Credentials are persisted to ~/.pi/email-config.json. The email_setup tool writes this file automatically. The file stores named profiles plus an active-profile selector, so you can keep several accounts side by side. You can also create it manually:

{
  "profiles": {
    "default": {
      "imap": {
        "host": "imap.gmail.com",
        "port": 993,
        "tls": true,
        "user": "you@gmail.com",
        "password": "<app-password>"
      },
      "smtp": {
        "host": "smtp.gmail.com",
        "port": 587,
        "secure": false,
        "user": "you@gmail.com",
        "password": "<app-password>"
      },
      "fromName": "Your Name"
    }
  },
  "activeProfile": "default"
}

Multiple profiles

Every account is stored under a profile name. The first profile is set active automatically; use email_profile to list, switch, or delete profiles.

# Add a second account
email_setup:
  name: personal
  imapHost: imap.gmail.com
  # ...rest of the credentials...

# List all profiles
email_profile: {}

# Switch the active profile
email_profile:
  action: use
  name: personal

# Delete a profile
email_profile:
  action: delete
  name: personal

Sent-folder copies

Every outgoing message is also stored in the Sent folder via IMAP APPEND, so sent mail appears in other clients. Gmail already does this server-side, so the copy is skipped there to avoid duplicates. Optional email_setup fields:

  • appendToSent: false — disable the Sent-folder copy for a profile
  • sentMailbox: "Sent Items" — store in a specific mailbox instead of auto-detecting

For local bridges with self-signed certificates, set imapRejectUnauthorized: false (or smtpRejectUnauthorized: false) in email_setup.

Architecture

The extension follows data-oriented programming principles:

  • src/types.ts -- All domain data types as plain immutable interfaces. No behavior, no classes, no inheritance.
  • src/config.ts -- Configuration state management and file persistence.
  • src/clients/imap-client.ts -- IMAP operations. Each function opens a connection, performs work, and closes. Returns plain data.
  • src/clients/smtp-client.ts -- Message composition and SMTP send operations via nodemailer.
  • src/clients/graph-client.ts -- Microsoft Graph backend (folders, messages, flags, drafts, sending) for Microsoft 365 profiles.
  • src/clients/mail.ts -- Dispatches each operation to the IMAP or Graph backend depending on the profile.
  • src/delivery.ts -- Outgoing delivery: sends via SMTP, then stores a Sent-folder copy via IMAP APPEND (skipped for Gmail or when disabled).
  • src/reply.ts -- Pure reply helpers (recipient derivation, References chain) shared by email_reply and email_draft_reply.
  • src/formatting/formatters.ts -- Pure transformation functions that convert domain data into display strings. No side effects.
  • src/tools/email-setup.ts -- Account configuration (IMAP/SMTP credentials).
  • src/tools/email-status.ts -- Show configured profiles.
  • src/tools/email-profile.ts -- Manage profiles (list, switch, delete).
  • src/tools/email-list-mailboxes.ts -- List IMAP folders.
  • src/tools/email-fetch.ts -- Fetch email headers.
  • src/tools/email-read.ts -- Read full email body with PDF extraction.
  • src/tools/email-search.ts -- Search emails with IMAP criteria.
  • src/tools/email-send.ts -- Send emails via SMTP.
  • src/tools/email-reply.ts -- Reply with threading headers and quoting.
  • src/tools/email-draft-reply.ts -- Create a server-side reply draft without sending.
  • src/tools/email-forward.ts -- Forward emails inline.
  • src/tools/email-flag.ts -- Set/remove IMAP flags.
  • src/tools/email-delete.ts -- Delete emails.
  • src/tools/email-move.ts -- Move emails between folders.
  • src/oauth/microsoft.ts -- Microsoft OAuth2 (PKCE, token endpoint, loopback callback server, XOAUTH2).
  • src/oauth/tokens.ts -- Access-token refresh and persistence for OAuth profiles.
  • src/commands/microsoft-login.ts -- The /email-login-microsoft command.
  • src/pdf-reader.ts -- PDF text extraction via pdftotext.
  • index.ts -- Extension entry point. Loads config, registers all 14 tools, and registers the /inbox command.

Requirements

  • Node.js 26+
  • pi coding agent (latest)
  • IMAP and SMTP access to your email provider

Supported Providers

Any email provider with standard IMAP/SMTP access works. Tested configurations:

Provider IMAP Host IMAP Port SMTP Host SMTP Port
Gmail imap.gmail.com 993 smtp.gmail.com 587
Outlook/Hotmail outlook.office365.com 993 smtp-mail.outlook.com 587
Microsoft 365 (work/school) via Microsoft Graph (/email-login-microsoft)
Yahoo imap.mail.yahoo.com 993 smtp.mail.yahoo.com 587
iCloud imap.mail.me.com 993 smtp.mail.me.com 587

Note: Gmail and many providers require app-specific passwords when 2FA is enabled. Microsoft 365 work accounts use /email-login-microsoft (OAuth) instead of a password.

Publishing as a pi Package

To publish this extension to the pi package catalog:

  1. Ensure package.json has "keywords": ["pi-package"]
  2. Ensure package.json has a "pi" section declaring extensions
  3. Optionally add "image" or "video" preview URLs to the "pi" manifest
  4. Publish to npm: npm publish
  5. Users install with: pi install npm:@patimweb/pi-email

The package catalog auto-discovers packages with the pi-package keyword from npm.

Dependencies

License

MIT