// developer documentation

Dynobox MCP Reference

Connect any AI agent to your inbox. The Dynobox MCP server exposes your email as a programmable API — search, read, send, and act on threads from Claude, GPT, or any MCP-compatible agent.

11 tools available3 product blueprints
// 01 overview

What is the Dynobox MCP?

Dynobox ships a built-in Model Context Protocol (MCP) server alongside the main application. MCP is an open protocol that lets AI agents discover and call tools over a standard JSON-RPC 2.0 interface — the same way a browser calls a REST API.

Once connected, your agent can treat your inbox as a live data source: fetch threads, compose and send messages, apply labels, star leads, archive noise — all without a human in the loop.

  • Transport: JSON-RPC 2.0 over HTTP POST
  • Endpoint: http://localhost:3001/api/mcp
  • Metadata: GET /api/mcp/metadata
  • Protocol version: 2024-11-05
  • Batch requests: not supported
Local by default. The MCP server only accepts loopback clients unless ALLOW_REMOTE_CLIENT=true is set. This keeps your inbox safe when running locally.

// 02 quick start

Up in 60 seconds

Make sure Dynobox is running, then pick your integration path below.

Claude Code — one-line setup
# Add Dynobox as an MCP server to Claude Code
claude mcp add --transport http dynobox http://localhost:3001/api/mcp

# Verify it's connected
claude mcp list
# → dynobox: http://localhost:3001/api/mcp (HTTP) - ✓ Connected
cURL — smoke test
export DYNOBOX_MCP_URL="http://localhost:3001/api/mcp"
export DYNOBOX_ACCOUNT_ID="you@example.com"

# 1. Initialize
curl -sS "$DYNOBOX_MCP_URL" \
  -H "Content-Type: application/json" \
  -H "x-account-id: $DYNOBOX_ACCOUNT_ID" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl-test","version":"1.0.0"}}}'

# 2. List tools
curl -sS "$DYNOBOX_MCP_URL" \
  -H "Content-Type: application/json" \
  -H "x-account-id: $DYNOBOX_ACCOUNT_ID" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
Node.js — minimal client
async function mcpCall(method, params = {}) {
  const res = await fetch('http://localhost:3001/api/mcp', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-account-id': process.env.DYNOBOX_ACCOUNT_ID,
    },
    body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params }),
  });
  const json = await res.json();
  return json.result?.structuredContent ?? json.result;
}

// Initialize, then call a tool
await mcpCall('initialize', {
  protocolVersion: '2024-11-05',
  capabilities: {},
  clientInfo: { name: 'my-agent', version: '1.0.0' },
});

const result = await mcpCall('tools/call', {
  name: 'dynobox_list_accounts',
  arguments: {},
});
console.log(result.accounts);

// 03 security & auth

Headers & access control

The MCP server uses two optional request headers to control access and route calls to the right mailbox.

HeaderRequiredDescription
x-account-idWhen >1 accountEmail address of the account to act on. Required when multiple accounts are configured.
x-dynobox-keyIf server-side key setShared secret. Only required when DYNOBOX_API_KEY is configured on the Dynobox server.
Securing remote access. If you expose Dynobox beyond localhost (via ALLOW_REMOTE_CLIENT=true), always set a strong DYNOBOX_API_KEY and pass it as x-dynobox-key on every request. Do not expose the MCP port to the public internet without additional auth.
With API key header
curl -sS "http://localhost:3001/api/mcp" \
  -H "Content-Type: application/json" \
  -H "x-account-id: you@example.com" \
  -H "x-dynobox-key: your-shared-secret" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

// 04 connection flow

Recommended call sequence

Follow this four-step sequence when bootstrapping a new agent session.initialize is required before any tool calls.

01

initialize

Negotiate the protocol version and register your client. Required once per session before any tool calls.

02

tools/list

Discover all available tools and their schemas. Useful for dynamic agents that introspect capabilities at runtime.

03

dynobox_list_accounts (optional)

Resolve account IDs when you don't know them ahead of time. Returns all configured mailboxes.

04

tools/call → operational tools

Run any of the 11 inbox tools. Pass accountId to target a specific mailbox.

Full bootstrap sequence
// 1. Initialize
await mcpCall('initialize', {
  protocolVersion: '2024-11-05',
  capabilities: {},
  clientInfo: { name: 'my-agent', version: '1.0.0' },
});

// 2. (Optional) Discover tools
const { tools } = await mcpCall('tools/list', {});

// 3. (Optional) Resolve accounts
const { accounts } = await mcpCall('tools/call', {
  name: 'dynobox_list_accounts',
  arguments: {},
});
const accountId = accounts[0].id;

// 4. Call an operational tool
const result = await mcpCall('tools/call', {
  name: 'dynobox_search_threads',
  arguments: {
    accountId,
    query: 'invoice OR receipt',
    unreadOnly: true,
    maxResults: 10,
  },
});

// 05 tool catalog

Available tools

All tools are called via tools/call. Expand any row to see the full argument list. Tools tagged Gmail require a Gmail account.

List all configured Dynobox accounts.

List recent threads for an account with optional query and label filters.

Search threads with structured filters: query, sender, recipient, subject, unread.

Filter threads by mailbox or label with optional sender/recipient constraints.

Fetch the full message content of a specific thread.

Send a new message or reply through the target account.

Apply a lifecycle action to a Gmail thread.

List all Gmail labels, optionally including message counts.

Add or remove Gmail labels on a thread. At least one of addLabelIds or removeLabelIds is required.

List Google contacts available to the account.

Get Gmail profile details: address, total messages, history ID.


// 06 build your own

Product blueprints

The MCP is the foundation — what you build on top is up to you. Here are three real-world patterns to get started. Each chains multiple tools into a complete, automated workflow.

blueprint 01

Invoice Processor

Automatically find, read, and label every invoice in your inbox.

dynobox_search_threads — query: "invoice OR receipt", unreadOnly: true
dynobox_get_thread — read line items and sender from body
dynobox_update_thread_labels — tag with your custom "Invoices" label
dynobox_thread_action — archive thread after processing
invoice processor.js
const threads = await mcp.call('dynobox_search_threads', {
  query: 'invoice OR receipt',
  unreadOnly: true,
  maxResults: 25,
});

for (const thread of threads.threads) {
  const full = await mcp.call('dynobox_get_thread', {
    threadId: thread.id,
  });

  // Parse body, extract totals...

  await mcp.call('dynobox_update_thread_labels', {
    threadId: thread.id,
    addLabelIds: ['Label_invoice'],
  });

  await mcp.call('dynobox_thread_action', {
    threadId: thread.id,
    action: 'archive',
  });
}
blueprint 02

Lead Capture Agent

Monitor inbound leads, send personalised replies, star hot prospects.

dynobox_filter_threads — label: INBOX, unreadOnly: true
dynobox_get_thread — read sender info and message intent
dynobox_send_message — reply with tailored follow-up copy
dynobox_thread_action — star high-value leads for human review
lead capture agent.js
const inbox = await mcp.call('dynobox_filter_threads', {
  label: 'INBOX',
  unreadOnly: true,
});

for (const thread of inbox.threads) {
  const full = await mcp.call('dynobox_get_thread', {
    threadId: thread.id,
  });

  const isLead = classify(full.messages[0].body); // your logic

  if (isLead.score > 0.8) {
    await mcp.call('dynobox_send_message', {
      threadId: thread.id,
      to: full.messages[0].from,
      body: generateReply(full),
    });

    await mcp.call('dynobox_thread_action', {
      threadId: thread.id, action: 'star',
    });
  }
}
blueprint 03

Daily Digest

Compile a morning briefing from overnight email and send it to yourself.

dynobox_list_threads — fetch last 24 hrs from INBOX
dynobox_get_thread — read each thread body
Summarise with an LLM of your choice
dynobox_send_message — email the digest to yourself
daily digest.js
const yesterday = Date.now() - 86_400_000;

const threads = await mcp.call('dynobox_list_threads', {
  label: 'INBOX',
  maxResults: 25,
});

const summaries = await Promise.all(
  threads.threads.map(async (t) => {
    const full = await mcp.call('dynobox_get_thread', {
      threadId: t.id,
    });
    return summarise(full); // your LLM call
  })
);

await mcp.call('dynobox_send_message', {
  to: 'you@example.com',
  subject: '☀️ Morning Digest',
  body: summaries.join('\n\n'),
  isHtml: false,
});
Going further. Combine the MCP with any LLM SDK — OpenAI, Anthropic, or open-source models. Use dynobox_get_thread to feed raw email content into your model, then act on its structured output using dynobox_send_message, dynobox_thread_action, or dynobox_update_thread_labels. The full inbox is your context window.

// 07 api reference

Response shape & errors

All successful responses wrap results in MCP-compatiblecontent and structuredContent fields. Use structuredContent for programmatic access — it contains the parsed JSON object directly.

Successful response
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [{ "type": "text", "text": "{...}" }],
    "structuredContent": {
      "threads": [ ... ],
      "nextPageToken": "abc123"
    }
  }
}
Error response
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "isError": true,
    "content": [{
      "type": "text",
      "text": "Account not found: unknown@example.com"
    }]
  }
}

Check result.isError === true to detect failures — they are returned as successful JSON-RPC responses, not HTTP error codes.

Metadata endpoint

GET /api/mcp/metadata returns server name, version, endpoint URLs, required headers, and tool summaries. Use this for auto-configuration in dynamic agents.

Fetch metadata
curl http://localhost:3001/api/mcp/metadata