@vinisxs/email-agent-proxy
v0.2.1
Published
MCP server exposing the Agent Gateway email API as typed tools for MCP-aware agent runtimes
Readme
Intents MCP Server
Model Context Protocol (MCP) server for the Email Agent Intents API.
Overview
This package provides an MCP-compatible interface to the Email Agent Intents API, enabling AI assistants (Hermes, OpenClawd, opencode, and other MCP-aware agents) to create, query, and manage email agent actions through a standardized MCP tool interface.
This is the canonical documentation for personal AI agents. A distributable skill describing the MCP tools ships alongside the server at skill/SKILL.md — install it together with the server in any skill-aware runtime.
Tools
Agents interact with an email-inbox metaphor, not the intent queue. Every write tool creates a pending intent behind the scenes and returns an optimistic response; the intent queue itself is invisible to agents (see the agent-gateway spec).
email.search
Search the whole mailbox — not just the inbox — using Gmail search operators, backed by GET /api/v1/messages/search. Results merge mirror rows with pending intent overlays (pending sends appear in results; pending label/archive/move changes are reflected before they execute), and reach mail outside the local mirror by delegating to Gmail and hydrating the missing messages on demand.
q accepts Gmail operators — from:, to:, cc:, bcc:, subject:, label:, in:, is: (unread/read/starred), has: (e.g. has:attachment), filename:, after:, before:, older_than:, newer_than:, larger:, smaller: — plus quoted phrases, OR, and - negation. Plain terms match subject/snippet. Omit q to list recent messages; is_read and labels filter that plain list (they are ignored when q is present, since q already expresses is:/label:).
Input:
{
"q": "from:[email protected] subject:invoice after:2024/01/01 -label:archived",
"is_read": true,
"labels": "optional,comma,separated,list",
"limit": 20,
"offset": 0
}All fields are optional. limit is 1–50 (default 20); offset defaults to 0.
Output:
{
"isError": false,
"data": {
"messages": [
{
"id": "message-id",
"thread_id": "thread-id",
"subject": "...",
"snippet": "...",
"labels": ["INBOX"],
"is_read": false,
"is_starred": false,
"received_at": "2024-01-01T00:00:00Z",
"status": "mirrored",
"action_type": "optional-pending-action-type"
}
],
"total": 100,
"limit": 20,
"offset": 0
}
}email.read
Read a single email by message ID. Accepts a mirror ID or a virtual msg:pending:<action_id> ID for a pending send/reply.
Input:
{ "message_id": "required-message-id" }Output: a single message object with the same shape as the entries in email.search.
email.send
Send a new email. Creates a Gmail draft and a pending intent; returns optimistically.
Input:
{
"to": "[email protected]",
"subject": "Subject",
"body": "Body text",
"cc": "optional-address-or-array",
"bcc": "optional-address-or-array"
}Output:
{
"isError": false,
"data": {
"id": "msg:pending:<action_id>",
"status": "pending"
}
}email.reply
Reply to an existing email.
Input:
{
"message_id": "required-message-id",
"body": "Reply text",
"cc": "optional-address-or-array",
"bcc": "optional-address-or-array"
}Output: same shape as email.send.
email.archive
Archive an email. Input: { "message_id": "required-message-id" }
email.apply_label
Apply labels to an email. Input: { "message_id": "required-message-id", "labels": ["Label1", "Label2"] }
email.move
Move an email to a folder. Input: { "message_id": "required-message-id", "folder": "folder-name" }
email.mark_read / email.mark_unread
Mark an email as read/unread. Input: { "message_id": "required-message-id" }
email.delete
Delete an email. Input: { "message_id": "required-message-id" }
send and reply return { id: "msg:pending:<action_id>", status: "pending" } — a virtual id, since the message doesn't exist yet. archive, apply_label, move, mark_read, mark_unread, and delete mutate an existing message and return { id: "<message_id>", status: "pending" }, echoing back the same id the tool was called with.
Configuration
The server requires two environment variables:
| Variable | Description | Required |
|----------|-------------|----------|
| MAIL_API_BASE_URL | Base URL of the Intents API (must be absolute http/https) | Yes |
| MAIL_AGENT_API_KEY | API key for authentication | Yes |
| MAIL_REQUEST_TIMEOUT_MS | Request timeout in milliseconds (default: 10000) | No |
Security
- Never commit the API key: The
MAIL_AGENT_API_KEYis a secret. Never embed it in config files, transcripts, or tool outputs. - 401 as configuration error: If you receive a 401, the API key is invalid or missing — treat it as a configuration problem, not a retryable error.
- Key scoping: Use a scoped API key with minimal permissions for the MCP server.
Launching the Server
Configure with init
The fastest way to set up the server is the built-in init command. It collects
the two required settings, verifies them against the API, and prints a
ready-to-paste MCP config block for your agent runtime (Hermes, OpenClawd,
opencode, and other mcpServers-based clients):
npx -y @vinisxs/email-agent-proxy initRun it with no flags to be prompted interactively, or pass values directly for a scripted setup:
npx -y @vinisxs/email-agent-proxy init \
--url https://api.example.com \
--key "$MAIL_AGENT_API_KEY"init also reads MAIL_API_BASE_URL / MAIL_AGENT_API_KEY from the environment.
It probes GET /api/v1/messages/search to confirm the key works and a mailbox is
connected; pass --no-check to skip the probe. The generated block embeds your
API key — keep the resulting config file private and never commit it.
Install from npm (recommended for external consumers)
Published as @vinisxs/email-agent-proxy — public, no auth or repo access needed:
npx -y @vinisxs/email-agent-proxyHermes / OpenClawd / opencode
Add to your MCP configuration:
{
"mcpServers": {
"intents": {
"command": "npx",
"args": ["-y", "@vinisxs/email-agent-proxy"],
"env": {
"MAIL_API_BASE_URL": "${MAIL_API_BASE_URL}",
"MAIL_AGENT_API_KEY": "${MAIL_AGENT_API_KEY}"
}
}
}
}opencode
For development/testing in this repository, the server is registered in .opencode/mcp.json. Set the environment variables before running opencode:
export MAIL_API_BASE_URL=http://localhost:3000
export MAIL_AGENT_API_KEY=your-api-key
opencodeBuild from source (internal/dev)
For working on the server itself, or for runtimes that need a local path instead of npx:
# Build the package first
cd packages/intents-mcp && pnpm build
# Run with environment variables
MAIL_API_BASE_URL=https://api.example.com \
MAIL_AGENT_API_KEY=your-api-key \
node packages/intents-mcp/dist/index.jsOr for development:
MAIL_API_BASE_URL=https://api.example.com \
MAIL_AGENT_API_KEY=your-api-key \
cd packages/intents-mcp && pnpm devReleasing
Publishing runs in CI (.github/workflows/publish-intents-mcp.yml) on tag push:
# bump "version" in package.json first, commit it, then:
git tag intents-mcp-vX.Y.Z
git push origin intents-mcp-vX.Y.ZCurl Fallback
If the MCP server is unavailable, you can call the REST API directly:
Search Emails
q takes Gmail operators (whole-mailbox reach); URL-encode it. /api/v1/messages?q= remains accepted for backward compatibility.
curl -G "${MAIL_API_BASE_URL}/api/v1/messages/search" \
--data-urlencode "q=from:alice subject:invoice" --data-urlencode "limit=10" \
-H "Authorization: Bearer ${MAIL_AGENT_API_KEY}"Read Email
curl "${MAIL_API_BASE_URL}/api/v1/messages/${message_id}" \
-H "Authorization: Bearer ${MAIL_AGENT_API_KEY}"Send Email
curl -X POST "${MAIL_API_BASE_URL}/api/v1/messages" \
-H "Authorization: Bearer ${MAIL_AGENT_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"to": "[email protected]", "subject": "Hello", "body": "World"}'Reply to Email
curl -X POST "${MAIL_API_BASE_URL}/api/v1/messages/${message_id}/reply" \
-H "Authorization: Bearer ${MAIL_AGENT_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"body": "Reply text"}'Source of Truth
The MCP server source code (packages/intents-mcp/src/) is the authoritative source for input validation schemas and tool behavior. If this documentation conflicts with the actual server behavior, the source code is correct.
Development
# Install dependencies
pnpm install
# Build
pnpm build
# Run tests
pnpm test
# Type check
pnpm typecheckArchitecture
src/config.ts- Configuration loading and validationsrc/schemas.ts- Zod schemas for tool input validationsrc/http.ts- HTTP client for the Intents APIsrc/logger.ts- Structured logging with pinosrc/errors.ts- Error mapping to MCP tool resultssrc/server.ts- MCP server setup and tool registrationsrc/index.ts- Entry point
