Jmap MCP agent
A Model Context Protocol (MCP) server that provides tools for interacting with JMAP (JSON Meta Application Protocol) email servers.
Files of Jmap MCP
wyattjoh/
Show the full text369 lines
JMAP MCP Server
A Deno workspace containing a functional JMAP email client and a Model Context Protocol (MCP) server built on top of it. The client package uses jmap-jam for JMAP protocol access; the MCP package adapts those typed operations into tools.
Features
Email Management Tools
- Search Emails: Search emails with text queries, sender/recipient filters, date ranges, and keywords. All filters are AND'd together.
- Get Emails: Retrieve specific emails by ID with configurable property selection
- Get Threads: Retrieve email threads (conversation chains)
- Mark Emails: Mark emails as read/unread, flagged/unflagged
- Move Emails: Move emails to one mailbox
- Patch Email Mailboxes: Add or remove selected mailbox memberships while preserving unspecified memberships
- Delete Emails: Delete emails permanently
Mailbox Management
- Get Mailboxes: List all mailboxes/folders with hierarchy support. Use this to find mailbox IDs needed by other tools.
Incremental Sync
- Get Email Changes: Get IDs of emails created, updated, or destroyed since a previous state (state-based delta tracking)
- Get Search Updates: Get additions/removals within a previous search query since its last queryState
Email Composition
- Send Email: Compose and send new emails with support for plain text and HTML
- Reply to Email: Reply to existing emails with automatic header handling and reply-all support
Key Capabilities
- Full JMAP RFC 8620/8621 compliance via jmap-jam
- Comprehensive input validation with Zod schemas
- Pagination support for all list operations
- State-based incremental sync for efficient polling
- Rich error handling and connection management
- Capability-based tool registration (read-only, submission)
- TypeScript support with strong typing
Reusable JMAP Client
Standalone applications can import @wyattjoh/jmap without creating an MCP
server or transport:
import { connectJmap, searchEmails } from "jsr:@wyattjoh/jmap";
const connection = await connectJmap({
sessionUrl: Deno.env.get("JMAP_SESSION_URL")!,
bearerToken: Deno.env.get("JMAP_BEARER_TOKEN")!,
accountId: Deno.env.get("JMAP_ACCOUNT_ID"),
});
const unread = await searchEmails(connection, {
filter: { inMailbox: "inbox-id", notKeyword: "$seen" },
limit: 50,
position: 0,
});
See packages/jmap/README.md for the client package.
Installation
Claude Code Plugin (Recommended)
Install via the plugin marketplace:
/plugin marketplace add wyattjoh/claude-code-marketplace
/plugin install jmap-mcp@wyattjoh-marketplace
Then configure the required environment variables in your MCP server settings.
Prerequisites
- Deno v2 or later
- A JMAP-compliant email server (e.g., Cyrus IMAP, Stalwart Mail Server, FastMail)
- Valid JMAP authentication credentials
Setup
Add the following to your agent of choice:
{
"mcpServers": {
"jmap": {
"type": "stdio",
"command": "deno",
"args": [
"run",
"--allow-net=api.fastmail.com",
"--allow-env=JMAP_SESSION_URL,JMAP_BEARER_TOKEN,JMAP_ACCOUNT_ID",
"jsr:@wyattjoh/[email protected]"
],
"env": {
"JMAP_SESSION_URL": "https://api.fastmail.com/jmap/session",
"JMAP_BEARER_TOKEN": "YOUR_API_TOKEN"
}
}
}
}
Replace
api.fastmail.comin--allow-netwith your JMAP server's hostname if not using FastMail.
Usage
Environment Variables
| Variable | Required | Description |
|---|---|---|
JMAP_SESSION_URL |
Yes | JMAP server session URL (usually ends with /.well-known/jmap) |
JMAP_BEARER_TOKEN |
Yes | Bearer token for authentication |
JMAP_ACCOUNT_ID |
No | Account ID (auto-detected if not provided) |
Available Tools
get_mailboxes
List mailboxes/folders with their IDs, names, and metadata. Call this first
to get mailbox IDs needed by search_emails (inMailbox) and move_emails
(mailboxId). Common names: Inbox, Drafts, Sent, Trash, Archive, Spam/Junk.
Parameters:
parentId(optional): Filter by parent mailbox IDlimit(optional): Max results (1-200, default: 100)position(optional): Starting position for pagination
search_emails
Search emails with filters. All filters are AND'd together. Returns only
email IDs — use get_emails to fetch content. Results include queryState for
incremental sync via get_search_updates.
Parameters:
query(optional): Text search across all fieldsbody(optional): Search in message body onlyfrom(optional): Filter by sender email addressto(optional): Filter by recipient email addresssubject(optional): Filter by subject textinMailbox(optional): Mailbox ID to search within (get fromget_mailboxes)hasKeyword(optional): Filter by keyword (e.g.,$seen,$flagged)notKeyword(optional): Exclude by keyword (e.g.,$seen,$draft)allInThreadHaveKeyword(optional): All emails in thread must have keywordsomeInThreadHaveKeyword(optional): At least one email in thread must have keywordbefore(optional): Only emails before date (ISO 8601 datetime)after(optional): Only emails after date (ISO 8601 datetime)limit(optional): Max results (1-100, default: 50)position(optional): Starting position for pagination (default: 0)
get_emails
Retrieve specific emails by their IDs. Use properties to request only what you
need — fetching all properties returns large payloads.
Parameters:
ids: Array of email IDs (1-50 IDs)properties(optional): Specific properties to return. Recommended sets:- Summary:
["id", "subject", "from", "to", "receivedAt", "preview"] - Full read:
["id", "subject", "from", "to", "cc", "receivedAt", "bodyValues", "textBody", "htmlBody"] - Note: To get body content, include
bodyValuesANDtextBody/htmlBody
- Summary:
get_threads
Get email threads by their IDs. Thread IDs come from get_emails responses
(threadId property). Returns email IDs per thread — use get_emails on those
IDs to fetch content.
Parameters:
ids: Array of thread IDs (1-20 IDs)
get_email_changes
Get IDs of emails created, updated, or destroyed since a previous state. Use the
state string from a get_emails response.
Parameters:
sinceState: State string from a previousget_emailsresponsemaxChanges(optional): Max changes to return (1-500)fetchEmails(optional): Auto-fetch full email details for changed IDs (default: false)properties(optional): Properties to fetch whenfetchEmailsis true
get_search_updates
Get changes within a previous search query since its queryState. Must use the
same filter parameters as the original search_emails call.
Parameters:
sinceQueryState:queryStatefrom a previoussearch_emailsresponse- All filter parameters from
search_emails(must match original query) maxChanges(optional): Max changes to return (1-500)
mark_emails
Mark emails as read/unread or flagged/unflagged.
Parameters:
ids: Array of email IDs (1-100 IDs)seen(optional): Mark as read (true) or unread (false)flagged(optional): Mark as flagged (true) or unflagged (false)
move_emails
Move emails to a different mailbox. Use get_mailboxes to find the target
mailbox ID.
Parameters:
ids: Array of email IDs (1-100 IDs)mailboxId: Target mailbox ID (get fromget_mailboxes)
patch_email_mailboxes
Add or remove selected mailbox memberships without replacing unspecified memberships. This supports label-like workflows and compound categorization.
Parameters:
ids: Array of email IDs (1-100 IDs)addMailboxIds(optional): Mailbox IDs to addremoveMailboxIds(optional): Mailbox IDs to remove
At least one mailbox must be added or removed. A mailbox cannot appear in both arrays.
delete_emails
Delete emails permanently (cannot be undone). Prefer moving to Trash via
move_emails for recoverable deletion.
Parameters:
ids: Array of email IDs (1-100 IDs)
send_email
Send a new email. Requires either textBody or htmlBody (or both).
Parameters:
to: Array of recipients (nameoptional,emailrequired)cc(optional): Array of CC recipientsbcc(optional): Array of BCC recipientssubject: Email subjecttextBody(optional): Plain text bodyhtmlBody(optional): HTML bodyidentityId(optional): JMAP identity ID to send from (uses server default if omitted)
reply_to_email
Reply to an existing email. Automatically sets To/CC, Re: subject prefix, and threading headers (In-Reply-To, References).
Parameters:
emailId: ID of email to reply toreplyAll(optional): Include all original recipients (default: false)subject(optional): Custom reply subject (defaults toRe: <original>)textBody(optional): Plain text bodyhtmlBody(optional): HTML bodyidentityId(optional): JMAP identity ID to send from (uses server default if omitted)
JMAP Server Compatibility
This server should work with any JMAP-compliant email server, including:
- Cyrus IMAP 3.0+
- Stalwart Mail Server
- FastMail (commercial)
- Apache James (with JMAP support)
Development
Running in Development
deno task watch # Run with file watching
deno task start # Run without watching
Testing
deno task test # Run all tests
deno task check # Type-check both packages
deno task lint # Lint workspace packages
deno task fmt # Format code
deno task publish:check # Validate both JSR packages
Architecture
The repository is a Deno workspace with two independently publishable packages:
packages/jmap/@wyattjoh/jmap: Functional connection, retrieval, incremental sync, mutation, and submission operationspackages/jmap-mcp/@wyattjoh/jmap-mcp: MCP schemas, tool adapters, capability registration, and stdio entrypoint
The MCP adapters call the client package directly. JMAP behavior belongs in the client package; transport validation and user-facing tool contracts belong in the MCP package.
Security
- All input is validated using Zod schemas
- Environment variables are used for sensitive configuration
- No secrets are logged or exposed in responses
- Follows JMAP security best practices
Contributing
- Fork the repository
- Create a feature branch
- Make changes following the functional programming style
- Test your changes thoroughly
- Submit a pull request
License
MIT License - see LICENSE file for details.
Related Projects
- jmap-jam - JMAP client library
- Model Context Protocol - MCP specification
- JMAP RFC 8620 - JMAP core protocol
- JMAP RFC 8621 - JMAP for Mail
| 1 | # JMAP MCP Server |
| 2 | |
| 3 | [![JSR]](https://jsr.io/@wyattjoh/jmap-mcp) |
| 4 | [![JSR Score]](https://jsr.io/@wyattjoh/jmap-mcp) |
| 5 | [![JMAP Client]](https://jsr.io/@wyattjoh/jmap) |
| 6 | [![JSR Scope]](https://jsr.io/@wyattjoh) |
| 7 | |
| 8 | A Deno workspace containing a functional JMAP email client and a Model Context |
| 9 | Protocol (MCP) server built on top of it. The client package uses |
| 10 | [jmap-jam] for JMAP protocol access; |
| 11 | the MCP package adapts those typed operations into tools. |
| 12 | |
| 13 | ## Features |
| 14 | |
| 15 | ### Email Management Tools |
| 16 | |
| 17 | **Search Emails**: Search emails with text queries, sender/recipient filters, |
| 18 | date ranges, and keywords. All filters are AND'd together. |
| 19 | **Get Emails**: Retrieve specific emails by ID with configurable property |
| 20 | selection |
| 21 | **Get Threads**: Retrieve email threads (conversation chains) |
| 22 | **Mark Emails**: Mark emails as read/unread, flagged/unflagged |
| 23 | **Move Emails**: Move emails to one mailbox |
| 24 | **Patch Email Mailboxes**: Add or remove selected mailbox memberships while |
| 25 | preserving unspecified memberships |
| 26 | **Delete Emails**: Delete emails permanently |
| 27 | |
| 28 | ### Mailbox Management |
| 29 | |
| 30 | **Get Mailboxes**: List all mailboxes/folders with hierarchy support. Use this |
| 31 | to find mailbox IDs needed by other tools. |
| 32 | |
| 33 | ### Incremental Sync |
| 34 | |
| 35 | **Get Email Changes**: Get IDs of emails created, updated, or destroyed since |
| 36 | a previous state (state-based delta tracking) |
| 37 | **Get Search Updates**: Get additions/removals within a previous search query |
| 38 | since its last queryState |
| 39 | |
| 40 | ### Email Composition |
| 41 | |
| 42 | **Send Email**: Compose and send new emails with support for plain text and |
| 43 | HTML |
| 44 | **Reply to Email**: Reply to existing emails with automatic header handling |
| 45 | and reply-all support |
| 46 | |
| 47 | ### Key Capabilities |
| 48 | |
| 49 | Full JMAP RFC 8620/8621 compliance via jmap-jam |
| 50 | Comprehensive input validation with Zod schemas |
| 51 | Pagination support for all list operations |
| 52 | State-based incremental sync for efficient polling |
| 53 | Rich error handling and connection management |
| 54 | Capability-based tool registration (read-only, submission) |
| 55 | TypeScript support with strong typing |
| 56 | |
| 57 | ### Reusable JMAP Client |
| 58 | |
| 59 | Standalone applications can import `@wyattjoh/jmap` without creating an MCP |
| 60 | server or transport: |
| 61 | |
| 62 | |
| 63 | import { connectJmap, searchEmails } from "jsr:@wyattjoh/jmap"; |
| 64 | |
| 65 | const connection = await connectJmap({ |
| 66 | sessionUrl: Deno.env.get("JMAP_SESSION_URL")!, |
| 67 | bearerToken: Deno.env.get("JMAP_BEARER_TOKEN")!, |
| 68 | accountId: Deno.env.get("JMAP_ACCOUNT_ID"), |
| 69 | }); |
| 70 | |
| 71 | const unread = await searchEmails(connection, { |
| 72 | filter: { inMailbox: "inbox-id", notKeyword: "$seen" }, |
| 73 | limit: 50, |
| 74 | position: 0, |
| 75 | }); |
| 76 | |
| 77 | |
| 78 | See [`packages/jmap/README.md`] for the client package. |
| 79 | |
| 80 | ## Installation |
| 81 | |
| 82 | ### Claude Code Plugin (Recommended) |
| 83 | |
| 84 | Install via the plugin marketplace: |
| 85 | |
| 86 | |
| 87 | /plugin marketplace add wyattjoh/claude-code-marketplace |
| 88 | /plugin install jmap-mcp@wyattjoh-marketplace |
| 89 | |
| 90 | |
| 91 | Then configure the required environment variables in your MCP server settings. |
| 92 | |
| 93 | ### Prerequisites |
| 94 | |
| 95 | [Deno] v2 or later |
| 96 | A JMAP-compliant email server (e.g., Cyrus IMAP, Stalwart Mail Server, |
| 97 | FastMail) |
| 98 | Valid JMAP authentication credentials |
| 99 | |
| 100 | ### Setup |
| 101 | |
| 102 | Add the following to your agent of choice: |
| 103 | |
| 104 | <!-- x-release-please-start-version --> |
| 105 | |
| 106 | |
| 107 | { |
| 108 | "mcpServers": { |
| 109 | "jmap": { |
| 110 | "type": "stdio", |
| 111 | "command": "deno", |
| 112 | "args": [ |
| 113 | "run", |
| 114 | "--allow-net=api.fastmail.com", |
| 115 | "--allow-env=JMAP_SESSION_URL,JMAP_BEARER_TOKEN,JMAP_ACCOUNT_ID", |
| 116 | "jsr:@wyattjoh/[email protected]" |
| 117 | ], |
| 118 | "env": { |
| 119 | "JMAP_SESSION_URL": "https://api.fastmail.com/jmap/session", |
| 120 | "JMAP_BEARER_TOKEN": "YOUR_API_TOKEN" |
| 121 | } |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | |
| 127 | <!-- x-release-please-end --> |
| 128 | |
| 129 | > Replace `api.fastmail.com` in `--allow-net` with your JMAP server's hostname |
| 130 | > if not using FastMail. |
| 131 | |
| 132 | ## Usage |
| 133 | |
| 134 | ### Environment Variables |
| 135 | |
| 136 | | Variable | Required | Description | |
| 137 | | ------------------- | -------- | --------------------------------------------------------------- | |
| 138 | | `JMAP_SESSION_URL` | Yes | JMAP server session URL (usually ends with `/.well-known/jmap`) | |
| 139 | | `JMAP_BEARER_TOKEN` | Yes | Bearer token for authentication | |
| 140 | | `JMAP_ACCOUNT_ID` | No | Account ID (auto-detected if not provided) | |
| 141 | |
| 142 | ### Available Tools |
| 143 | |
| 144 | #### `get_mailboxes` |
| 145 | |
| 146 | List mailboxes/folders with their IDs, names, and metadata. **Call this first** |
| 147 | to get mailbox IDs needed by `search_emails` (`inMailbox`) and `move_emails` |
| 148 | (`mailboxId`). Common names: Inbox, Drafts, Sent, Trash, Archive, Spam/Junk. |
| 149 | |
| 150 | **Parameters:** |
| 151 | |
| 152 | `parentId` (optional): Filter by parent mailbox ID |
| 153 | `limit` (optional): Max results (1-200, default: 100) |
| 154 | `position` (optional): Starting position for pagination |
| 155 | |
| 156 | #### `search_emails` |
| 157 | |
| 158 | Search emails with filters. **All filters are AND'd together.** Returns only |
| 159 | email IDs — use `get_emails` to fetch content. Results include `queryState` for |
| 160 | incremental sync via `get_search_updates`. |
| 161 | |
| 162 | **Parameters:** |
| 163 | |
| 164 | `query` (optional): Text search across all fields |
| 165 | `body` (optional): Search in message body only |
| 166 | `from` (optional): Filter by sender email address |
| 167 | `to` (optional): Filter by recipient email address |
| 168 | `subject` (optional): Filter by subject text |
| 169 | `inMailbox` (optional): Mailbox ID to search within (get from `get_mailboxes`) |
| 170 | `hasKeyword` (optional): Filter by keyword (e.g., `$seen`, `$flagged`) |
| 171 | `notKeyword` (optional): Exclude by keyword (e.g., `$seen`, `$draft`) |
| 172 | `allInThreadHaveKeyword` (optional): All emails in thread must have keyword |
| 173 | `someInThreadHaveKeyword` (optional): At least one email in thread must have |
| 174 | keyword |
| 175 | `before` (optional): Only emails before date (ISO 8601 datetime) |
| 176 | `after` (optional): Only emails after date (ISO 8601 datetime) |
| 177 | `limit` (optional): Max results (1-100, default: 50) |
| 178 | `position` (optional): Starting position for pagination (default: 0) |
| 179 | |
| 180 | #### `get_emails` |
| 181 | |
| 182 | Retrieve specific emails by their IDs. Use `properties` to request only what you |
| 183 | need — fetching all properties returns large payloads. |
| 184 | |
| 185 | **Parameters:** |
| 186 | |
| 187 | `ids`: Array of email IDs (1-50 IDs) |
| 188 | `properties` (optional): Specific properties to return. Recommended sets: |
| 189 | Summary: `["id", "subject", "from", "to", "receivedAt", "preview"]` |
| 190 | Full read: |
| 191 | `["id", "subject", "from", "to", "cc", "receivedAt", "bodyValues", "textBody", "htmlBody"]` |
| 192 | **Note:** To get body content, include `bodyValues` AND |
| 193 | `textBody`/`htmlBody` |
| 194 | |
| 195 | #### `get_threads` |
| 196 | |
| 197 | Get email threads by their IDs. Thread IDs come from `get_emails` responses |
| 198 | (`threadId` property). Returns email IDs per thread — use `get_emails` on those |
| 199 | IDs to fetch content. |
| 200 | |
| 201 | **Parameters:** |
| 202 | |
| 203 | `ids`: Array of thread IDs (1-20 IDs) |
| 204 | |
| 205 | #### `get_email_changes` |
| 206 | |
| 207 | Get IDs of emails created, updated, or destroyed since a previous state. Use the |
| 208 | `state` string from a `get_emails` response. |
| 209 | |
| 210 | **Parameters:** |
| 211 | |
| 212 | `sinceState`: State string from a previous `get_emails` response |
| 213 | `maxChanges` (optional): Max changes to return (1-500) |
| 214 | `fetchEmails` (optional): Auto-fetch full email details for changed IDs |
| 215 | (default: false) |
| 216 | `properties` (optional): Properties to fetch when `fetchEmails` is true |
| 217 | |
| 218 | #### `get_search_updates` |
| 219 | |
| 220 | Get changes within a previous search query since its `queryState`. Must use the |
| 221 | same filter parameters as the original `search_emails` call. |
| 222 | |
| 223 | **Parameters:** |
| 224 | |
| 225 | `sinceQueryState`: `queryState` from a previous `search_emails` response |
| 226 | All filter parameters from `search_emails` (must match original query) |
| 227 | `maxChanges` (optional): Max changes to return (1-500) |
| 228 | |
| 229 | #### `mark_emails` |
| 230 | |
| 231 | Mark emails as read/unread or flagged/unflagged. |
| 232 | |
| 233 | **Parameters:** |
| 234 | |
| 235 | `ids`: Array of email IDs (1-100 IDs) |
| 236 | `seen` (optional): Mark as read (`true`) or unread (`false`) |
| 237 | `flagged` (optional): Mark as flagged (`true`) or unflagged (`false`) |
| 238 | |
| 239 | #### `move_emails` |
| 240 | |
| 241 | Move emails to a different mailbox. Use `get_mailboxes` to find the target |
| 242 | mailbox ID. |
| 243 | |
| 244 | **Parameters:** |
| 245 | |
| 246 | `ids`: Array of email IDs (1-100 IDs) |
| 247 | `mailboxId`: Target mailbox ID (get from `get_mailboxes`) |
| 248 | |
| 249 | #### `patch_email_mailboxes` |
| 250 | |
| 251 | Add or remove selected mailbox memberships without replacing unspecified |
| 252 | memberships. This supports label-like workflows and compound categorization. |
| 253 | |
| 254 | **Parameters:** |
| 255 | |
| 256 | `ids`: Array of email IDs (1-100 IDs) |
| 257 | `addMailboxIds` (optional): Mailbox IDs to add |
| 258 | `removeMailboxIds` (optional): Mailbox IDs to remove |
| 259 | |
| 260 | At least one mailbox must be added or removed. A mailbox cannot appear in both |
| 261 | arrays. |
| 262 | |
| 263 | #### `delete_emails` |
| 264 | |
| 265 | Delete emails permanently (cannot be undone). Prefer moving to Trash via |
| 266 | `move_emails` for recoverable deletion. |
| 267 | |
| 268 | **Parameters:** |
| 269 | |
| 270 | `ids`: Array of email IDs (1-100 IDs) |
| 271 | |
| 272 | #### `send_email` |
| 273 | |
| 274 | Send a new email. Requires either `textBody` or `htmlBody` (or both). |
| 275 | |
| 276 | **Parameters:** |
| 277 | |
| 278 | `to`: Array of recipients (`name` optional, `email` required) |
| 279 | `cc` (optional): Array of CC recipients |
| 280 | `bcc` (optional): Array of BCC recipients |
| 281 | `subject`: Email subject |
| 282 | `textBody` (optional): Plain text body |
| 283 | `htmlBody` (optional): HTML body |
| 284 | `identityId` (optional): JMAP identity ID to send from (uses server default if |
| 285 | omitted) |
| 286 | |
| 287 | #### `reply_to_email` |
| 288 | |
| 289 | Reply to an existing email. Automatically sets To/CC, Re: subject prefix, and |
| 290 | threading headers (In-Reply-To, References). |
| 291 | |
| 292 | **Parameters:** |
| 293 | |
| 294 | `emailId`: ID of email to reply to |
| 295 | `replyAll` (optional): Include all original recipients (default: false) |
| 296 | `subject` (optional): Custom reply subject (defaults to `Re: <original>`) |
| 297 | `textBody` (optional): Plain text body |
| 298 | `htmlBody` (optional): HTML body |
| 299 | `identityId` (optional): JMAP identity ID to send from (uses server default if |
| 300 | omitted) |
| 301 | |
| 302 | ## JMAP Server Compatibility |
| 303 | |
| 304 | This server should work with any JMAP-compliant email server, including: |
| 305 | |
| 306 | [Cyrus IMAP] 3.0+ |
| 307 | [Stalwart Mail Server] |
| 308 | [FastMail] (commercial) |
| 309 | [Apache James] (with JMAP support) |
| 310 | |
| 311 | ## Development |
| 312 | |
| 313 | ### Running in Development |
| 314 | |
| 315 | |
| 316 | deno task watch # Run with file watching |
| 317 | deno task start # Run without watching |
| 318 | |
| 319 | |
| 320 | ### Testing |
| 321 | |
| 322 | |
| 323 | deno task test # Run all tests |
| 324 | deno task check # Type-check both packages |
| 325 | deno task lint # Lint workspace packages |
| 326 | deno task fmt # Format code |
| 327 | deno task publish:check # Validate both JSR packages |
| 328 | |
| 329 | |
| 330 | ## Architecture |
| 331 | |
| 332 | The repository is a Deno workspace with two independently publishable packages: |
| 333 | |
| 334 | **`packages/jmap` / `@wyattjoh/jmap`**: Functional connection, retrieval, |
| 335 | incremental sync, mutation, and submission operations |
| 336 | **`packages/jmap-mcp` / `@wyattjoh/jmap-mcp`**: MCP schemas, tool adapters, |
| 337 | capability registration, and stdio entrypoint |
| 338 | |
| 339 | The MCP adapters call the client package directly. JMAP behavior belongs in the |
| 340 | client package; transport validation and user-facing tool contracts belong in |
| 341 | the MCP package. |
| 342 | |
| 343 | ## Security |
| 344 | |
| 345 | All input is validated using Zod schemas |
| 346 | Environment variables are used for sensitive configuration |
| 347 | No secrets are logged or exposed in responses |
| 348 | Follows JMAP security best practices |
| 349 | |
| 350 | ## Contributing |
| 351 | |
| 352 | Fork the repository |
| 353 | Create a feature branch |
| 354 | Make changes following the functional programming style |
| 355 | Test your changes thoroughly |
| 356 | Submit a pull request |
| 357 | |
| 358 | ## License |
| 359 | |
| 360 | MIT License - see [LICENSE] file for details. |
| 361 | |
| 362 | ## Related Projects |
| 363 | |
| 364 | [jmap-jam] - JMAP client library |
| 365 | [Model Context Protocol] - MCP specification |
| 366 | [JMAP RFC 8620] - JMAP core |
| 367 | protocol |
| 368 | [JMAP RFC 8621] - JMAP for Mail |
| 369 |
Discussion
Alternatives
Browse more free AI agents or everything in Development.