Integrating
MCP server
Every tool with the scopes it needs, the rate-limit table, the JSON-RPC error codes, and what an agent cannot do.
MCP server
Every tool this MCP server serves, the scopes each one needs, the ceilings it is counted against, and what each refusal means. Generated from the running catalogue — the page, the console tab and the server cannot disagree.
Server URL: https://api.agentisend.com/mcp (streamable HTTP). Bearer API key or OAuth 2.1 — both reach the same tools with the same ceilings. A local stdio launcher is published as @agentisend/mcp-server for clients that only speak stdio; it proxies to this server, so the catalogue is identical.
Add it to your client
Every line below carries the same endpoint (https://api.agentisend.com/mcp) and a placeholder key — replace as_YOUR_API_KEY_HERE with a key from the console.
Cursor
cursor://anysphere.cursor-deeplink/mcp/install?name=agentisend&config=eyJ1cmwiOiJodHRwczovL2FwaS5hZ2VudGlzZW5kLmNvbS9tY3AiLCJoZWFkZXJzIjp7IkF1dGhvcml6YXRpb24iOiJCZWFyZXIgYXNfWU9VUl9BUElfS0VZX0hFUkUifX0=Open this link and Cursor offers to add the server. Replace the placeholder key in Settings → MCP afterwards, or paste the same object into .cursor/mcp.json.
Claude Code
claude mcp add --transport http agentisend https://api.agentisend.com/mcp \
--header "Authorization: Bearer as_YOUR_API_KEY_HERE"Or install the plugin, which brings the server and the sending skill together: /plugin marketplace add fortuneflick/agentisend-claude-plugin then /plugin install agentisend@agentisend.
Codex CLI
codex mcp add agentisend --url https://api.agentisend.com/mcp \
--bearer-token-env-var AGENTISEND_API_KEYThe token stays in your environment; Codex writes only the variable name to ~/.codex/config.toml.
Gemini CLI
gemini mcp add --transport http \
--header "Authorization: Bearer as_YOUR_API_KEY_HERE" \
agentisend https://api.agentisend.com/mcpCheck it with gemini mcp list.
VS Code
code --add-mcp '{"name":"agentisend","type":"http","url":"https://api.agentisend.com/mcp","headers":{"Authorization":"Bearer as_YOUR_API_KEY_HERE"}}'Or write the same server into .vscode/mcp.json under servers.
Windsurf
{
"mcpServers": {
"agentisend": {
"serverUrl": "https://api.agentisend.com/mcp",
"headers": {
"Authorization": "Bearer as_YOUR_API_KEY_HERE"
}
}
}
}Goes in ~/.codeium/windsurf/mcp_config.json, then refresh the MCP panel.
OpenCode
{
"mcp": {
"agentisend": {
"type": "remote",
"url": "https://api.agentisend.com/mcp",
"enabled": true,
"headers": {
"Authorization": "Bearer {env:AGENTISEND_API_KEY}"
}
}
}
}Goes in opencode.json; the key is read from your environment rather than written to the file.
Anything else that speaks stdio
AGENTISEND_API_KEY=as_YOUR_API_KEY_HERE npx -y @agentisend/mcp-serverThe local launcher proxies to the same server, so the tool list, the budget and the refusals are identical.
Tools (34)
Every tool that changes something accepts idempotency_key: retrying with the same key returns the first result instead of doing the work twice.
Sending
| Tool | Scopes | Idempotent | Destructive |
|---|---|---|---|
send_email | emails:send | yes | — |
send_batch | emails:send | yes | — |
Writes
| Tool | Scopes | Idempotent | Destructive |
|---|---|---|---|
create_domain | domains:write | yes | — |
set_limit | limits:write | yes | yes |
request_approval | emails:send | yes | — |
cancel_email | emails:send | yes | yes |
reschedule_email | emails:send | yes | — |
verify_domain | domains:write | yes | — |
add_suppression | suppressions:write | yes | — |
remove_suppression | suppressions:write | yes | yes |
publish_template | templates:write | yes | — |
upsert_contact | contacts:write | yes | — |
delete_contact | contacts:write | yes | yes |
create_webhook | webhooks:write | yes | — |
delete_webhook | webhooks:write | yes | yes |
Reads
| Tool | Scopes | Idempotent | Destructive |
|---|---|---|---|
get_email | emails:read | — | — |
list_emails | emails:read | — | — |
get_domain | domains:read | — | — |
get_agent_budget | limits:read | — | — |
get_kill_switch_state | limits:read | — | — |
list_pending_approvals | limits:read | — | — |
list_agent_actions | limits:read | — | — |
explain_email | emails:read | — | — |
preflight_email | emails:send | — | — |
whoami | _none_ | — | — |
why_was_this_not_sent | emails:read | — | — |
get_trust_standing | trust:read | — | — |
list_domains | domains:read | — | — |
list_suppressions | suppressions:read | — | — |
list_templates | templates:read | — | — |
render_template | templates:read | — | — |
list_segments | segments:read | — | — |
list_contacts | contacts:read | — | — |
list_webhooks | webhooks:read | — | — |
Scopes
A key holds scopes; a tool needs some. A tool you cannot call is missing one — whoami returns the scopes this credential holds and the tools they reach.
| Scope | What it allows | Tools |
|---|---|---|
emails:read | View messages you have sent, their delivery status and their events | 4 |
emails:send | Send email from your verified domains — this delivers real mail to real people and spends the budget on this connection. It also covers asking you to approve a held send | 6 |
domains:read | View your sending domains, their region and the state of their DNS records | 2 |
domains:write | Add sending domains and run domain verification | 2 |
webhooks:read | View your webhook endpoints and their delivery history | 1 |
webhooks:write | Create, update or remove webhook endpoints | 2 |
limits:read | View the budget, rate ceiling and remaining allowance on each key, and the actions waiting for your decision | 4 |
limits:write | Change budgets and rate ceilings, and pause or resume a key — this changes how much can be spent without you | 1 |
trust:read | View your account standing and the reasons behind it | 1 |
contacts:read | View the people in your contact list and what you have recorded about them | 1 |
contacts:write | Add people to your contact list, change what is recorded about them, and remove them | 2 |
suppressions:read | View the addresses you no longer send to, and why each one is on the list | 1 |
suppressions:write | Add addresses you no longer want contacted, and take one off the list — except where somebody unsubscribed or reported a message as spam, which only you can undo | 2 |
templates:read | View your templates and preview one with sample values | 2 |
templates:write | Publish a template, which decides what the next send using it will say | 1 |
segments:read | View the groups you have defined and who is currently in them | 1 |
Rate limits
Per-minute ceilings are the same on every plan and are counted per API key, by operation class. What a plan changes is how much you may send in total.
| Operation class | Calls per minute per key | Tools |
|---|---|---|
| read | 120 | 19 |
| write | 60 | 13 |
| send | 30 | 2 |
| Plan | Emails included per month | Daily send cap |
|---|---|---|
| Starter | 30,000 | none |
| Pro | 100,000 | none |
| Scale | 100,000 | none |
JSON-RPC error codes
Every refusal arrives as a tool result carrying code, message, fix, reason, retryable and jsonrpc_code. Branch on jsonrpc_code or reason, never on the message.
| Code | Name | When | What to do | Wait hint |
|---|---|---|---|---|
| -32001 | insufficient_scope | The key is valid but was not granted a scope this tool requires. | Read missing_scopes in the error data — it names exactly which ones. Ask the person to create a key with those scopes and reconnect. Retrying with the same key fails identically. | none |
| -32002 | rate_limited | The per-minute ceiling for this operation class was reached. | Wait retry_after_seconds and repeat the same call. This is the ONLY refusal class where waiting is the fix. | retry_after_seconds |
| -32005 | limit_reached | A configured ceiling was hit — the send budget for the period, or a plan cap. | Stop. Waiting does not cure it: a person raises the number or the period rolls over. get_agent_budget says when the period resets. | none |
| -32006 | not_found | No such record on this account. | Check the id. Do not retry — the same id will be absent next time too. | none |
| -32008 | conflict | The record already exists, or changed under you. | Read the current state and decide again. A blind retry repeats the conflict. | none |
| -32009 | failed_precondition | The request is well-formed but something about the account state refuses it — an unverified domain, a suppressed recipient, a paused key, a held send. | Read fix: it names the endpoint or the person. This class covers every deliberate stop, and none of them is cured by retrying. | none |
| -32010 | temporarily_unavailable | A dependency was briefly unavailable. | Retry once after retry_after_seconds. If it repeats, report it and stop. | retry_after_seconds |
| -32602 | invalid_params | The arguments did not validate. Nothing ran. | The issues list names every field to correct. Retrying unchanged fails the same way. | none |
| -32603 | internal_error | Something failed that should not have. | Retry ONCE after a short pause with the same idempotency_key, then stop and report the request_id from the error. It identifies this exact failure in support. | none |
The refusals you will actually meet
| Error code | JSON-RPC | Retryable | Fix |
|---|---|---|---|
kill_switch_active | -32001 | no — waiting cannot help | Read GET /trust/standing for why it was paused. Only a person signed in to the console can resume it; the paused key cannot resume itself. |
agent_budget_exceeded | -32005 | no — waiting cannot help | Wait for the period to reset — get_agent_budget and GET /limits/keys/:id both say when. Raising a budget is a person’s decision, made in the console; a key cannot raise its own. |
approval_required | -32001 | no — waiting cannot help | It is waiting in the console approvals inbox; GET /agent-actions shows it and what it says. A person decides — the key that asked cannot approve itself. |
trust_paused | -32001 | no — waiting cannot help | Review reasons via GET /trust/standing, then file an appeal via POST /trust/appeal. |
rate_ceiling_exceeded | -32002 | after 60s | Wait the seconds below and send the same request again. Raising the ceiling is a person’s decision, made in the console; a key cannot raise its own. |
rate_limiter_unavailable | -32010 | after 5s | Retry in a few seconds. Nothing was sent and nothing was changed — writes are refused rather than run unmetered against a shared sending reputation. |
restricted_api_key | -32001 | no — waiting cannot help | Use a full_access key (POST /api-keys with permission=full_access) for management endpoints. |
suppressed_recipient | -32009 | no — waiting cannot help | GET /suppressions says which address and why. A hard bounce you have fixed can be cleared with DELETE /suppressions/:id; an unsubscribe or a spam complaint cannot — that address asked not to be contacted. |
domain_not_verified | -32001 | no — waiting cannot help | Add the DNS records shown by GET /domains/:id, then call POST /domains/:id/verify. |
What an agent cannot do here
These are deliberate stops with a person on the other end, not gaps.
- Lift your own kill switch. A rest an agent can cut short is not a rest. _A person, on the Agents screen in the console. You may stop yourself at any time — POST /limits/keys/:id/kill and /limits/kill-all stay open to a key._
- Approve your own held send. Approval you can grant yourself is not approval. _A person, in the approvals inbox. Use request_approval to ask; list_agent_actions to hear the answer._
- Raise your own budget or your own per-minute ceiling. A budget an agent can raise is not a budget. _A person, on the Agents screen in the console. set_limit and PATCH /limits/keys/:id still lower a key, so an agent can always cap itself before a job._
- Raise the account trust standing, or waive a trust pause. Trust is earned by delivery outcomes, not asserted. _Nobody directly — it changes as your sending does. POST /trust/appeal asks a human to look._
- Read the contents of a message you were stopped from sending. A held-mail queue an agent can read verbatim is a way to read the mail it was stopped from sending. _A person, in the console. Tools see shapes: recipient counts, whether there is a body._
- Send to an address that asked not to be contacted. Suppression is the whole reason the account is allowed to send at all. _Nobody, while the suppression stands. remove_suppression clears a hard bounce you have fixed; an unsubscribe or a spam complaint is refused, because somebody said no and only they can change that._
- Send unsolicited mail to people who did not ask for it. This product refuses cold outreach by design. There is no feature for it and asking for one is out of scope. _Nobody, at any tier, by any route. There is no endpoint for it and no plan that adds one._