# MCP Server

> Connect an AI agent to its inbox with the native Model Context Protocol server — read, thread, and filter as first-class tools.

Inboundr ships a native **Model Context Protocol (MCP)** server, so an AI agent
can manage its own email as a set of tools — no glue code required. It's the
same data as the REST API, authenticated with the same `inb_` key.

## Endpoint

```
https://inboundr.net/api/mcp
```

The server speaks **streamable HTTP**. Authenticate with a
[bearer token](/api-reference/authentication):

```
Authorization: Bearer inb_xxxxxxxxxxxxxxxxxxxxxxxx
```

## Connecting

<CodeGroup>

```json Claude Desktop / MCP config
{
  "mcpServers": {
    "inboundr": {
      "url": "https://inboundr.net/api/mcp",
      "headers": { "Authorization": "Bearer inb_xxxxxxxxxxxxxxxxxxxxxxxx" }
    }
  }
}
```

```bash Inspector
npx @modelcontextprotocol/inspector \
  --url https://inboundr.net/api/mcp \
  --header "Authorization: Bearer $INBOUNDR_API_KEY"
```

</CodeGroup>

<Tip>
  The [console](https://inboundr.net/console) shows a ready-to-paste connection
  snippet for your account on the **Agents &amp; MCP** page.
</Tip>

## Tools

<AccordionGroup>
  <Accordion title="list_domains" icon="globe">
    List the caller's domains with verification status. Mail can only be
    received on verified domains.
  </Accordion>
  <Accordion title="create_inbox" icon="inbox">
    Create an address on one of the caller's domains, e.g.
    `agent@yourdomain.com`. Store-only by default; pass `endpointId` to also
    deliver each message to a webhook.
    <ParamField path="address" type="string" required>Full address to create.</ParamField>
    <ParamField path="endpointId" type="string">Optional webhook endpoint id (`ep_…`).</ParamField>
  </Accordion>
  <Accordion title="list_inboxes" icon="list">
    List the caller's explicit addresses. (Verified domains also accept mail on
    any address via catch-all.)
  </Accordion>
  <Accordion title="list_emails" icon="envelopes-bulk">
    List emails newest-first (summaries, no bodies). Filter by `to`, `threadId`,
    or `direction`; paginate with `cursor`.
    <ParamField path="to" type="string">Only emails addressed (To/Cc) to this address.</ParamField>
    <ParamField path="threadId" type="string" />
    <ParamField path="direction" type="string">`inbound` or `outbound`.</ParamField>
    <ParamField path="limit" type="number">1–100.</ParamField>
    <ParamField path="cursor" type="string">`nextCursor` from a previous call.</ParamField>
  </Accordion>
  <Accordion title="get_email" icon="envelope-open">
    Fetch one email in full: text and HTML bodies, attachment metadata,
    spam/SPF/DKIM verdicts, and threading headers.
    <ParamField path="id" type="string" required>Email id (`em_…`).</ParamField>
  </Accordion>
  <Accordion title="get_thread" icon="comments">
    Fetch all messages in a conversation, oldest first (summaries).
    <ParamField path="threadId" type="string" required>Thread id (`thr_…`).</ParamField>
  </Accordion>
  <Accordion title="get_attachment" icon="paperclip">
    Get a presigned download URL (valid 15 minutes) for one attachment. Index is
    0-based, matching the `attachments` array from `get_email`.
    <ParamField path="emailId" type="string" required>Email id (`em_…`).</ParamField>
    <ParamField path="index" type="number" required>0-based attachment index.</ParamField>
  </Accordion>
  <Accordion title="list_shield_rules" icon="shield">
    List the caller's [shield rules](/concepts/shield) in evaluation order. The
    first rule whose conditions all match a message wins and no later rule runs,
    so the order is the meaning of the list.
  </Accordion>
  <Accordion title="create_shield_rule" icon="shield-halved">
    Create a rule judged against every inbound message before it is stored. All
    conditions must match (AND); use several rules for OR. New rules are added
    **last** in the order.
    <ParamField path="name" type="string" required>Short label.</ParamField>
    <ParamField path="action" type="string" required>
      `block` drops the message permanently (never stored, delivered, or
      counted — the body cannot be recovered); `allow` delivers it and stops
      later rules; `route` delivers to `endpointIds` instead of the address's
      own (empty = quarantine).
    </ParamField>
    <ParamField path="conditions" type="object[]" required>
      1–8 `{ field, operator, value }` objects. See
      [Fields and operators](/concepts/shield#fields-and-operators).
    </ParamField>
    <ParamField path="endpointIds" type="string[]">Route only (`ep_…`).</ParamField>
    <ParamField path="domainId" type="string">Limit to one domain (`dom_…`).</ParamField>
    <ParamField path="addressId" type="string">Limit to one address (`addr_…`).</ParamField>
  </Accordion>
  <Accordion title="delete_shield_rule" icon="trash">
    Delete a rule. Mail it blocked starts arriving again; mail already blocked
    stays gone.
    <ParamField path="id" type="string" required>Rule id (`gr_…`).</ParamField>
  </Accordion>
  <Accordion title="list_shield_blocks" icon="ban">
    List messages recently dropped by a rule: sender, recipient, subject, and
    which rule caught them. This is the only record a blocked message leaves.
    <ParamField path="limit" type="number">1–200, default 50.</ParamField>
  </Accordion>
</AccordionGroup>

## Rate limits

MCP and REST **share one budget** — 5 requests/second per account, keyed by the
API key's owner. See [Rate limits](/api-reference/rate-limits).

## A typical agent loop

1. `list_emails` with `direction: "inbound"` → find new mail.
2. `get_thread` → read the conversation for context.
3. `create_shield_rule` → act on what it finds — block, allow, route, or
   quarantine future mail like it.
