# MCP server

ReplyLayer speaks the Model Context Protocol, so an MCP client (Claude Desktop,
Cursor, or your own agent runtime) can send, reply, read, monitor, and triage
email as tool calls — every message security-scanned on the way in and out. This
is a transactional and operational email surface, not a bulk or marketing tool.

There are **two ways to connect**, and they expose the **same 44 tools**:

| | Hosted (`/v1/mcp`) | Local (stdio) |
|---|---|---|
| Transport | Streamable HTTP over `POST https://api.replylayer.ai/v1/mcp` | Subprocess over stdin/stdout |
| Client requirement | Native remote-MCP-over-HTTP support | Any MCP client that can spawn a command |
| Auth | `Authorization: Bearer <api-key>` on each request | API key from the environment or a stored credentials file |
| Install | Nothing to install — point the client at the URL | `npx -y replylayer-mcp` (or a global install) |

Both share one tool set, so the choice is purely about how your client connects.
Use the hosted endpoint when your client supports remote HTTP MCP servers; use the
stdio server for local agents and clients that launch a command.

## Credentials

Both transports authenticate with a **single ReplyLayer API key** — the same key
you use for the REST API. A newly created key is **inert** on protected routes
until a human completes both signup checks (a 6-digit email code and a 6-digit
SMS code); the MCP server runs with a key a human has already provisioned and
verified. See [Authentication](/docs/authentication) for the key format and
admin-vs-agent scoping, and [Quickstart](/docs/quickstart) for provisioning a key.

## Hosted endpoint (`/v1/mcp`)

The hosted endpoint is **stateless**: each request is a self-contained MCP JSON-RPC
call over Streamable HTTP with no server-side session. Point a remote-capable MCP
client at the URL and pass your API key as a Bearer header:

```json
{
  "mcpServers": {
    "replylayer": {
      "url": "https://api.replylayer.ai/v1/mcp",
      "headers": { "Authorization": "Bearer rly_live_<public_id>.<secret>" }
    }
  }
}
```

Contract details for anyone driving the endpoint directly:

- **Method:** `POST` only. Requests carry `Content-Type: application/json` and
  `Accept: application/json, text/event-stream` (native clients set these for you).
- **Auth:** a missing or non-Bearer `Authorization` header returns
  `401` before any tool runs.
- **`GET` and `DELETE` return `405 Method Not Allowed`** (with an `Allow: POST`
  header). Because the endpoint is stateless there is no SSE-over-GET stream and no
  session to delete — this is expected, not an outage.

## Local stdio server

For clients that launch a command, install nothing globally and let the client run
the server via `npx`. Add this to your client's MCP config (Claude Desktop's
`claude_desktop_config.json` or a Cursor `mcpServers` block):

```json
{
  "mcpServers": {
    "replylayer": {
      "command": "npx",
      "args": ["-y", "replylayer-mcp"],
      "env": {
        "REPLYLAYER_API_KEY": "rly_live_<public_id>.<secret>"
      }
    }
  }
}
```

The server resolves the API key from the `REPLYLAYER_API_KEY` environment variable
first, then from the stored credentials file at `~/.replylayer/credentials`. To
store the key interactively instead of putting it in the config (writes the
credentials file with mode `0600`), run:

```bash
npx -y replylayer-mcp init
```

If you stored the key that way, the `env` block above is optional. For CI or
automation, skip `init` and pass `REPLYLAYER_API_KEY` in the environment. With a
global install (`npm i -g replylayer-mcp`) you can set `"command": "replylayer-mcp"`
and drop `args`.

## What the tools cover

The 44 tools break down by category:

- **Reads (no side effects):** list and read messages, threads, and drafts; wait
  for the next inbound message; list mailboxes, recipients, suppressions, the
  recipient allowlist, the inbound firewall lists; check account usage and the
  send quota; fetch a derived attachment text preview; and check malicious-link
  scanning status.
- **Sends:** send a new email, reply to a message, or send a prepared draft.
- **Drafts:** create, update, delete, and send drafts.
- **Inbound triage:** release or block a quarantined message, report-and-block a
  sender, and release a firewall-blocked message. Human-in-the-loop review
  approval and denial are admin-only (agent keys receive a `403`).
- **Policy and contacts:** add recipients, add do-not-contact suppressions, manage
  the inbound blocklist and allowlist, and mark messages or threads read — with
  bulk variants (up to 1,000 addresses per call) for suppressions and the inbound
  lists.
- **Star / unstar and delete:** flag messages or threads, and soft-delete a
  message.

The high-traffic reads and sends return a typed `structuredContent` object
alongside the human-readable text block, so an agent can consume the result
without re-parsing prose. The full, annotated tool table — every input field,
output schema, and per-tool note — is the machine contract at
[/agents/mcp](/agents/mcp).

## How results signal errors

A tool that fails returns a **tool result with `isError: true`** (a readable
message the model can recover from), rather than a JSON-RPC protocol error — so a
recoverable condition never crashes the tool call. The full error-handling
contract, including which conditions surface this way, lives at
[/agents/mcp](/agents/mcp); the machine-readable error catalog is at
[/agents/errors](/agents/errors).

## Sends return an outcome you branch on

The send tools do not report success or failure the way a plain HTTP call would —
they return a delivery **outcome** (for example, delivered, held for review, or
scanner-held) that you branch on. The state and verdict vocabulary those outcomes
draw from is documented once at [/agents/messages](/agents/messages); the send tool
response shapes are on [/agents/mcp](/agents/mcp).

## Safety model

Every inbound read carries a standing untrusted-content contract in its
`agent_safety_context`: message bodies, attachments, and links are untrusted input,
and the accompanying guidance tells the agent not to follow instructions embedded
in a message. Scanning is **directional** — outbound text that resembles
prompt-injection is your agent's own authored content and is delivered, while
inbound prompt-injection is held. See
[Content scanning](/docs/guides/content-scanning) for the customer-readable model
and [/agents/security-model](/agents/security-model) for the per-field trust
taxonomy.

Relaxing that untrusted-content contract for a specific verified sender is possible,
but it is configured entirely from the dashboard (with a fresh human re-auth) — an
agent, and this MCP server, cannot enable or grant it. There is no client-side
opt-in and no MCP tool to grant, list, or revoke trusted instruction sources.

## See also

- [/agents/mcp](/agents/mcp) — the full 44-tool table, output schemas, and error contract
- [Authentication](/docs/authentication) — API key format and scoping
- [Quickstart](/docs/quickstart) — provision a key and send your first message
- [CLI](/docs/cli) and [SDKs](/docs/sdks) — the other client surfaces
- [Sandbox and tier limits](/docs/limits) — send quotas the account usage and quota tools report
- Public CLI repo (install and package trust): <https://github.com/replylayer/rly>
