MCP server
@avelto/mcp is a Model Context Protocol server for Avelto. Add it to Claude Code, Cursor or any MCP client and the agent can do two jobs: integrate Avelto into the application you are building, reading the real docs and setting the account up as it goes, and operate the account afterwards: send, check delivery, manage domains, webhooks, templates and suppressions. It wraps the Node SDK and runs over stdio, so there is nothing to host.
AVELTO_API_KEY=av_test_... npx -y @avelto/mcpIt needs one environment variable, AVELTO_API_KEY. AVELTO_BASE_URL overrides the API origin and AVELTO_DOCS_URL the documentation origin; both are optional.
Put an av_test_... key in the config to start with. Test sends run the whole pipeline and produce the same events and webhooks, but nothing reaches an inbox. Switch to an av_live_... key when you want the agent to deliver.
Integrating with an agent
The fastest way to add Avelto to a project is to let the agent do it with the server attached. In Claude Code, register the server and run the prompt it provides:
claude mcp add avelto -e AVELTO_API_KEY=av_test_... -- npx -y @avelto/mcp/mcp__avelto__integrateThe prompt is a plan, not a script. The agent calls check_setup to learn what your account has, reads the quickstart for your stack with get_quickstart and follows its code rather than inventing an integration, writes one sending function and puts the key in the environment, proves it with a sandbox send and get_email, and then, if the application needs them, registers a webhook and adds a domain, handing you the DNS records to publish. Other clients expose the same prompt under their own name for MCP prompts, or you can ask in plain words: "integrate Avelto into this app".
check_setup is what makes this safe to leave to an agent. It returns the account as an integrator sees it, with nothing secret in it, and a next_steps list computed from what is and is not set up:
{
"account": { "id": "…", "name": "Acme", "plan": "free", "plan_step": null },
"key": { "mode": "test", "scopes": ["emails:send", "domains:manage", "webhooks:manage"] },
"sandbox": {
"domain": "sandbox.avelto.dev",
"recipients": ["you@acme.com", "delivered@sandbox.avelto.dev", "bounced@sandbox.avelto.dev", "complained@sandbox.avelto.dev"],
"simulator": { "delivered": "delivered@sandbox.avelto.dev", "bounced": "bounced@sandbox.avelto.dev", "complained": "complained@sandbox.avelto.dev" }
},
"domains": [],
"webhooks": [],
"templates": [],
"docs_url": "https://staging.avelto.dev/docs",
"next_steps": [
"This is a test key: sends run the full pipeline and produce events, but nothing is delivered. Send from anything@sandbox.avelto.dev to delivered@sandbox.avelto.dev with send_email to prove the integration, then get_email to read the events.",
"No sending domain yet. To send from the application's own addresses, call add_domain with a subdomain such as mail.<their domain>, give the user the dns_records to publish, then poll get_domain_status until it is verified.",
"No webhook endpoint. If the application needs to react to bounces, complaints or delivery, add a handler that verifies Avelto-Signature (read /docs/webhooks), deploy or tunnel it, then register it with create_webhook and store the secret it returns in the environment.",
"No templates. Optional: create_template for any email the application sends more than once, then send it with template_slug and variables."
]
}Tools
| Tool | Input | Returns |
|---|---|---|
check_setup | none | Key mode and scopes, the sandbox and its allowed recipients, domains, webhooks, templates, and next_steps |
get_quickstart | framework | The quickstart for that language or framework, as markdown |
read_docs | path | One documentation page as markdown, for example /docs/webhooks |
send_email | from, to, subject, html?, text?, reply_to?, template_slug?, template_id?, variables?, tags?, idempotency_key? | { id } |
get_email | id | The email with status and events |
list_emails | status?, tag?, mode?, limit?, cursor? | { data, next_cursor } |
list_domains | none | Domains with status and dns_records |
add_domain | name | The domain with the dns_records to publish |
get_domain_status | id | The domain; verification is re-checked on every call |
list_webhooks | none | Endpoints with their events and enabled flag |
create_webhook | url, events? | The endpoint with its signing secret, shown once |
list_webhook_deliveries | id, limit?, cursor? | Recent deliveries with the response status |
list_templates | none | Templates with their variables |
get_template | id or slug | One template |
create_template | name, subject, html?, text?, slug? | The template |
update_template | id, name?, subject?, html?, text? | The template |
list_suppressions | limit?, cursor? | Suppressed addresses with reason |
The documentation is also offered as MCP resources, avelto://docs for the quickstart and avelto://docs/<page> for every other page, for clients that attach resources to a conversation rather than call tools.
Every tool returns the API response as JSON, except the docs tools, which return markdown. Errors come back as an error result with code: message first, so the agent can read the code and decide what to do:
plan_limit: Monthly limit reached: Free includes 5,000 emails per month and has no overage. Sending resumes on the 1st (UTC), or upgrade to a paid plan to keep sending now.
{
"limit": 10000,
"used": 10000,
"step": "free",
"resets": "start of next month (UTC)"
}
(HTTP 429)The tool descriptions carry the sending rules, so the agent knows them before it calls anything:
- With a test key nothing is delivered. The email is accepted and gets synthetic events:
sentanddelivered, orbouncedorcomplainedfor those simulator addresses. - From the sandbox domain
sandbox.avelto.dev, the only allowed recipients are your account owner's verified email and the simulator addressesdelivered@sandbox.avelto.dev,bounced@sandbox.avelto.devandcomplained@sandbox.avelto.dev. Anything else returns403 sandbox_recipient_not_allowed. - Any other
frommust be a domain that is added and verified on your account. Calladd_domain, publish the DNS records, then pollget_domain_statusuntil it isverified. - Pass
idempotency_keywhen retrying a send. A replay returns the original id and does not send twice. - Suppressed recipients return
422 recipient_suppressed.429 plan_limitmeans the monthly allowance is used up; the agent should stop retrying until it resets or the plan is upgraded.
See Test mode and sandbox and Domains for the full rules.
Claude Code
The command is at the top of this page, under Integrating with an agent.
Claude Desktop
Add the server to claude_desktop_config.json and restart Claude Desktop.
{
"mcpServers": {
"avelto": {
"command": "npx",
"args": ["-y", "@avelto/mcp"],
"env": { "AVELTO_API_KEY": "av_test_..." }
}
}
}Cursor
Add the server to .cursor/mcp.json in your project, or to ~/.cursor/mcp.json for every project.
{
"mcpServers": {
"avelto": {
"command": "npx",
"args": ["-y", "@avelto/mcp"],
"env": { "AVELTO_API_KEY": "av_test_..." }
}
}
}Other clients
Any client that launches stdio servers works with the same command, npx -y @avelto/mcp, and the same AVELTO_API_KEY environment variable. The manifest at /.well-known/mcp.json lists the tools, the prompt and the launch command. The package is on npm as @avelto/mcp.