# MCP server

Point an assistant at Rasket and it can set your account up and run it — add and verify a domain, wire a webhook, write a template, send, and follow what happened — on your behalf, without anyone pasting an API key into a desktop app. That is a trust decision before it is an integration, so this page starts there.

## The boundary

A connected app never gets a key. It gets an **OAuth access token**, issued for **one team**, carrying **only the scopes you approved** on the consent screen, and you can revoke it from Settings → Team → Authorized apps at any moment.

> The scopes you approve decide which tools exist. A grant without `domains:write` is not a server that refuses `create_domain` — it is a server that does not have one.

Two scopes reach further than their names suggest, so read them before approving. `emails:read` can cancel a scheduled send, because cancelling only ever prevents mail. `team:write` can turn AI assist on, which decides whether content you ask about is sent to the AI provider. A client connected without a toolset asks for both.

## What it may never do

These are not tools that refuse. They are tools that do not exist.

- Mint, change or revoke an API key, or manage the grants it is itself using.
- Change your plan, your billing, or who is on your team.
- Delete a domain, a template, a segment, a campaign or an automation.
- Remove a suppression — an unsubscribe stays an unsubscribe, whoever is asking.
- Regenerate a domain's DKIM keys, rotate a webhook's signing secret, or take a registrar credential.

No scope reaches them either, so there is no consent screen anywhere that could hand them over. Two deletions are tools: `delete_webhook` and `delete_contact`, which erases a person for good. Both need a write scope, and both are marked destructive, so your client asks you before each one. See [Authentication](https://www.rasket.com/docs/authentication) for how the scopes and the API-key permissions relate.

## Seven toolsets

79 tools, grouped so a client can load only the ones it needs. Every call goes through the same service the dashboard and the API use, so every rule applies unchanged. A refusal comes back as a tool result marked `isError`, carrying the error name the API would have answered with — so the assistant can read it and tell you which address unsubscribed, rather than reporting that something broke.

Every paged list takes `limit` (at most 25) and `after`. An `html` body over 4 KB is left out of what a tool returns, and a sent or received message's HTML is never returned at all.

### Setup · setup · 26

Add and verify a domain, name the sender, wire webhooks, write templates.

| Tool | Scope | What it does |
| --- | --- | --- |
| `get_team` | `team:read` | The team's plan, sender identity, AI assist and single sign-on status. |
| `update_team` | `team:write` | Set the sender name and postal address a campaign needs, or turn AI assist on or off. |
| `list_domains` | `domains:read` | Your domains, their region and whether they may send. |
| `get_domain_status` | `domains:read` | One domain: every DNS record, its status, and why a failing one failed. |
| `get_domain_records` | `domains:read` | The records to publish at the DNS host, exactly as it needs them. |
| `create_domain` | `domains:write` | Add a domain; the answer carries the records to publish. |
| `verify_domain` | `domains:write` | Look the records up now rather than at the next scheduled check. |
| `update_domain` | `domains:write` | Change tracking, TLS or a domain's capabilities. |
| `claim_domain` | `domains:write` | Start a claim on a name another team verified. It completes by itself once the TXT record resolves. |
| `list_webhooks` | `webhooks:read` | Your endpoints and the events each receives. |
| `get_webhook` | `webhooks:read` | One endpoint, with its status and delivery health. |
| `list_webhook_events` | `webhooks:read` | The events created for one endpoint, with their delivery state. |
| `list_parked_events` | `webhooks:read` | The events held while an endpoint was disabled. |
| `create_webhook` | `webhooks:write` | Add an endpoint. The answer carries its signing secret, once. |
| `update_webhook` | `webhooks:write` | Change an endpoint's URL or events, or enable or disable it. |
| `delete_webhook` | `webhooks:write` | Delete an endpoint. Its past events stay readable. |
| `replay_webhook_event` | `webhooks:write` | Deliver one past event to its endpoint again. |
| `deliver_parked_events` | `webhooks:write` | Deliver the events parked on an endpoint, oldest first. |
| `list_templates` | `templates:read` | Templates with their alias and status, to send by name. |
| `get_template` | `templates:read` | One template, its variables and whether it is published. |
| `preview_template` | `templates:read` | Render a template with values filled in. Nothing is sent. |
| `list_starters` | `templates:read` | The starter templates a new template can be copied from. |
| `get_starter` | `templates:read` | One starter in full, with the variables a copy declares. |
| `create_template` | `templates:write` | Write a new template draft, from scratch or from a starter. |
| `update_template` | `templates:write` | Change a template's draft. |
| `publish_template` | `templates:write` | Publish the draft, so a send can name it. |

### Send · send · 11

Send one message, follow what happened to it, and read what came in.

| Tool | Scope | What it does |
| --- | --- | --- |
| `send_email` | `emails:send` | Send one message from a verified domain. Every sending rule applies unchanged. |
| `get_email` | `emails:read` | One message: its recipients, what happened to each, and the event timeline. |
| `list_emails` | `emails:read` | A page of sent mail, newest first, with an optional search. |
| `list_email_events` | `emails:read` | Every event of one message, with what each one recorded. |
| `cancel_email` | `emails:read` | Cancel a scheduled message before it leaves. |
| `get_email_metrics` | `emails:read` | Delivery, bounce, complaint, open and click rates over a window. |
| `list_received_emails` | `emails:read` | Inbound mail, newest first. |
| `get_received_email` | `emails:read` | One inbound message, its text capped at 4 KB and never its HTML. |
| `list_suppressions` | `suppressions:read` | The addresses you will not send to, and why. |
| `add_suppression` | `suppressions:write` | Suppress up to 100 addresses at once. |
| `list_logs` | `logs:read` | Your API request logs, tool calls included. |

### Audience · audience · 20

Contacts and their properties, imports, segments, and the topics people chose.

| Tool | Scope | What it does |
| --- | --- | --- |
| `list_contacts` | `contacts:read` | A page of contacts. |
| `get_contact` | `contacts:read` | One contact, by id or by address. |
| `list_contact_properties` | `contacts:read` | The properties a contact may carry. |
| `get_import` | `contacts:read` | How a contact import is getting on. |
| `create_contact` | `contacts:write` | Add a contact. An address you already hold is updated, not duplicated. |
| `update_contact` | `contacts:write` | Change a contact's name, address, properties or consent. |
| `delete_contact` | `contacts:write` | Erase a contact for good. Unsubscribing is the gentler option. |
| `create_contact_property` | `contacts:write` | Declare a property before a contact can carry it. |
| `import_contacts` | `contacts:write` | Import a CSV of up to 512 KiB, given inline. |
| `list_segments` | `segments:read` | Your segments. |
| `get_segment` | `segments:read` | One segment and the filter that defines it. |
| `preview_segment` | `segments:read` | Count who a filter matches, before a segment is saved with it. |
| `create_segment` | `segments:write` | Define an audience by filter, by hand, or both. |
| `update_segment` | `segments:write` | Rename a segment. |
| `add_contact_to_segment` | `segments:write` | Put one contact in a segment. |
| `remove_contact_from_segment` | `segments:write` | Take one contact out of a segment. The contact is untouched. |
| `list_topics` | `topics:read` | The subscription topics you offer. |
| `create_topic` | `topics:write` | Offer a new topic. |
| `update_topic` | `topics:write` | Rename a topic, or change its description or visibility. |
| `set_contact_topics` | `topics:write` | Record which topics one contact opted into or out of. |

### Marketing · marketing · 17

Campaigns (the broadcasts tools and scopes), automations, and the custom events that start them.

| Tool | Scope | What it does |
| --- | --- | --- |
| `list_broadcasts` | `broadcasts:read` | Your campaigns and their status. |
| `get_broadcast` | `broadcasts:read` | One campaign, with HTML over 4 KB left out. |
| `get_broadcast_report` | `broadcasts:read` | How a sent campaign performed, and its most-clicked links. |
| `create_broadcast` | `broadcasts:write` | Draft a campaign to a segment. It stays a draft. |
| `update_broadcast` | `broadcasts:write` | Change a draft. |
| `preview_broadcast` | `broadcasts:write` | Who a draft would reach, its subject and sender, and the token a send needs. |
| `send_broadcast` | `broadcasts:write` | Send a previewed draft now, or schedule it. |
| `cancel_broadcast` | `broadcasts:write` | Cancel a scheduled campaign. |
| `list_automations` | `automations:read` | Your automations and whether each is enabled. |
| `get_automation` | `automations:read` | One automation and the graph of its current version. |
| `list_automation_runs` | `automations:read` | The contacts an automation has run for. |
| `create_automation` | `automations:write` | Create a workflow. It is disabled until you enable it. |
| `update_automation` | `automations:write` | Rename, enable or disable an automation, or publish a new version of its graph. |
| `stop_automation` | `automations:write` | Disable an automation and cancel its runs in flight. |
| `list_events` | `events:read` | The custom events you have defined. |
| `create_event` | `events:write` | Define a custom event an automation can start from. |
| `send_event` | `events:send` | Record that an event happened for one contact. |

### Insight · insight · 0

Reserved. It holds no tool of its own yet: email metrics are in send, a campaign's report is in marketing.

### Billing · billing · 1

Read-only. Nothing here buys, changes or cancels anything.

| Tool | Scope | What it does |
| --- | --- | --- |
| `get_billing` | `billing:read` | Your plan, usage, add-ons and payment state. |

### AI · ai · 3

AI assist, once the team has turned it on. Each call uses AI credits.

| Tool | Scope | What it does |
| --- | --- | --- |
| `suggest_subject_lines` | `ai:use` | Subject lines for a draft. |
| `draft_email` | `ai:use` | A first draft of an email body from a brief. |
| `diagnose_email` | `ai:use` | What happened to one message, and what to do about it. |

`create_webhook` is the one tool that returns a secret: the endpoint's signing secret, once, because your endpoint cannot verify a delivery without it. It is your outbound secret, not a credential into Rasket. A claim started with `claim_domain` transfers the name by itself once its TXT record resolves, unless the current owner sent or verified recently.

Every tool says whether it only reads, whether it can overwrite, remove or send, and whether it reaches anyone outside your account. Clients use those hints to ask you before anything that changes or sends. A campaign goes out in three steps: the agent drafts it, calls `preview_broadcast` to show you the segment, how many subscribed contacts it holds, the subject and the sender, and only then calls `send_broadcast` with the `confirm_token` the preview returned. The token is for that draft as it stood, for one hour; an edit means a new preview.

Calling a tool this connection was not given answers with the scope it needs, or the toolset to add, so the agent can ask you for exactly that.

## Start with describe_setup

`describe_setup` needs no scope and is registered whatever the toolset filter says, so it is always the first call an agent can make. It returns the setup recipe — the same steps as the [Agents](https://www.rasket.com/docs/agents) guide — with each step's tool, and a plan marking which steps this connection's scopes allow and which scopes are missing. With `format: "json"` it returns only the plan:

A describe_setup plan:

```text
{
  "steps": [
    { "id": "create_key", "title": "Create an API key", "tool": null, "scope": null, "allowed": false, "reason": "No tool creates an API key: …" },
    { "id": "add_domain", "title": "Add a domain", "tool": "create_domain", "scope": "domains:write", "allowed": true, "reason": null },
    …
    { "id": "send_broadcast", "title": "Send a broadcast", "tool": "send_broadcast", "scope": "broadcasts:write", "allowed": false, "reason": "Needs the broadcasts:write scope." }
  ],
  "missing_scopes": ["broadcasts:write", "automations:write"]
}
```

An agent that finds a scope missing should ask you to reconnect with it before starting, rather than stopping halfway through.

## Budgets, because an assistant is chatty

Four limits, counted per connected app, on top of your team's ten requests a second. They are deliberately low: an assistant is a person's hands, not a pipeline.

| Budget | Window | What counts |
| --- | --- | --- |
| 60 tool calls | per minute | Every call that reaches a tool, including one that comes back as an error — the cost is the call. |
| 100 sends | per rolling hour | `send_email` calls we accepted. Your plan's own quota still applies. |
| 3 broadcasts | per rolling hour | `send_broadcast` calls that queued or scheduled a campaign. |
| 5 imports | per rolling hour | `import_contacts` calls that started an import. |

Over any of them, the call answers JSON-RPC error `-32000` with a `retryAfter` in seconds. A refused call is not itself a tool call, so it does not hold the window open; a JSON-RPC batch is not accepted at all: it is refused with `-32600` before any tool runs and costs nothing. Each accepted send, campaign and import is also written to your audit log as `mcp.email_sent`, `mcp.broadcast_sent` or `mcp.contacts_imported`, naming the connected app.

## Keeping an assistant in check

An assistant acts for you, so it should act the way you would. Five things keep it there, and every one of them works today.

- **Your client asks first.** Every tool says whether it reads, changes or sends, and Claude and ChatGPT use that to ask you before anything that changes or sends. Keep asking on for `send_email` and `send_broadcast`.
- **A campaign needs your go-ahead.** `create_broadcast` only drafts it, `preview_broadcast` shows you who it reaches, and `send_broadcast` runs only with the token that preview returned. You can still `cancel_broadcast` a scheduled one.
- **Ask only for the scopes you need.** Without a toolset, a client asks you to approve every scope its tools use. With one, it asks only for that toolset's scopes, and a tool whose scope you did not approve does not exist for that connection.
- **Load only the toolsets you need.** Connect with `?toolset=setup,send` and the audience and marketing tools are not there at all.
- **Everything is on the record.** Every call is in your Logs, the budgets above cap a busy assistant, and you can revoke the app from Settings → Team → Authorized apps at any moment.

## Connect

The server is at `https://api.rasket.com/mcp`. Every client signs in the same way: it opens a browser, you sign in to Rasket, pick the team and approve the scopes. No API key is involved. [Rasket for Claude](https://www.rasket.com/integrations/claude) shows what to ask for once you are connected.

### claude.ai

1. Open **Customize**, then **Connectors**.
2. Choose **+**, then **Add custom connector**. Name it Rasket and paste `https://api.rasket.com/mcp`. Leave the advanced settings empty.
3. Choose **Connect**, sign in to Rasket, pick the team and approve.
4. In a chat, turn Rasket on from the **+** menu under **Connectors**.

On a Team or Enterprise plan an owner adds the connector once, under Organization settings → Connectors. Each member then connects it with their own Rasket account.

### Claude Desktop

Claude Desktop uses the connectors on your claude.ai account. Add Rasket on claude.ai as above, then choose **Connect** in Desktop if it asks you to sign in.

### Claude Code

One command. The client does the rest.

Adding the server to a client:

```text
claude mcp add --transport http rasket https://api.rasket.com/mcp
```

Then run `/mcp` in a session and choose Rasket to sign in.

### ChatGPT

1. Open **Settings**, then **Apps & Connectors**, then **Advanced settings**, and turn on **Developer mode**.
2. Create a connector. Name it Rasket, paste `https://api.rasket.com/mcp` and choose OAuth.
3. Sign in to Rasket, pick the team and approve.
4. In a chat, turn Rasket on from the developer mode tools.

Developer mode needs a paid ChatGPT plan. ChatGPT moves these menus from time to time; the setting is always under Apps.

### Other clients

Clients that take JSON take the same endpoint:

The client configuration:

```text
{
  "mcpServers": {
    "rasket": {
      "type": "http",
      "url": "https://api.rasket.com/mcp"
    }
  }
}
```

### Only some toolsets

To load only some toolsets, name them in `toolset`, separated by commas, and the client asks you to approve only the scopes their tools use. Leave it out for all seven and every scope. A tool outside the named toolsets is not registered: it is missing from `tools/list`, a call to it is answered with the toolset to add, and that call counts against no budget. An unknown toolset name is JSON-RPC error `-32602` before any tool runs.

Adding the server with two toolsets:

```text
claude mcp add --transport http rasket "https://api.rasket.com/mcp?toolset=setup,send"
```

In claude.ai or ChatGPT, paste the URL with the same `?toolset=` on the end.

### What happens when you connect

There is nothing else to configure. The first unauthenticated call is answered with a `401` whose `WWW-Authenticate` header names our protected-resource document, so the client discovers the authorization server, registers itself, and opens a browser for your approval. The same header names the scopes to ask for: every scope the tools use, or only the named toolsets'. You pick the team; it exchanges its code for a token.

An older connection may hold only the scopes setting up needs, and then has fewer tools than this page lists. Disconnect Rasket in your client and connect it again to approve the rest.

> Every request to `/api/v1` needs a `User-Agent`, and this one needs an `Accept` listing both `application/json` and `text/event-stream`. Every MCP client sends both already.

## Nothing is remembered

The server is stateless. Each request builds a server, answers, and forgets: no session id, no stream, nothing kept between calls. `GET` on the endpoint is `405` — there is no stream to open — and `DELETE` is a `204` that does nothing, because there is no session to end.

Every tool call is logged like any other API request. It appears in your Logs with `source` of `mcp`, the tool it ran, and the connected app that ran it, so a send an assistant made is as traceable as one your own code made.
