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.

Terminal
AVELTO_API_KEY=av_test_... npx -y @avelto/mcp

It needs one environment variable, AVELTO_API_KEY. AVELTO_BASE_URL overrides the API origin and AVELTO_DOCS_URL the documentation origin; both are optional.

Use a test key while you try it out

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:

Terminal
claude mcp add avelto -e AVELTO_API_KEY=av_test_... -- npx -y @avelto/mcp
In the Claude Code session
/mcp__avelto__integrate

The 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:

check_setup
{
  "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://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

ToolInputReturns
check_setupnoneKey mode and scopes, the sandbox and its allowed recipients, domains, webhooks, templates, and next_steps
get_quickstartframeworkThe quickstart for that language or framework, as markdown
read_docspathOne documentation page as markdown, for example /docs/webhooks
send_emailfrom, to, subject, html?, text?, reply_to?, template_slug?, template_id?, variables?, tags?, idempotency_key?{ id }
get_emailidThe email with status and events
list_emailsstatus?, tag?, mode?, limit?, cursor?{ data, next_cursor }
list_domainsnoneDomains with status and dns_records
add_domainnameThe domain with the dns_records to publish
get_domain_statusidThe domain; verification is re-checked on every call
list_webhooksnoneEndpoints with their events and enabled flag
create_webhookurl, events?The endpoint with its signing secret, shown once
list_webhook_deliveriesid, limit?, cursor?Recent deliveries with the response status
list_templatesnoneTemplates with their variables
get_templateid or slugOne template
create_templatename, subject, html?, text?, slug?The template
update_templateid, name?, subject?, html?, text?The template
list_suppressionslimit?, 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:

Error result
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:

Sandbox and test-mode rules
  • With a test key nothing is delivered. The email is accepted and gets synthetic events: sent and delivered, or bounced or complained for those simulator addresses.
  • From the sandbox domain sandbox.avelto.dev, the only allowed recipients are your account owner's verified email and the simulator addresses delivered@sandbox.avelto.dev, bounced@sandbox.avelto.dev and complained@sandbox.avelto.dev. Anything else returns 403 sandbox_recipient_not_allowed.
  • Any other from must be a domain that is added and verified on your account. Call add_domain, publish the DNS records, then poll get_domain_status until it is verified.
  • Pass idempotency_key when retrying a send. A replay returns the original id and does not send twice.
  • Suppressed recipients return 422 recipient_suppressed. 429 plan_limit means 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.

claude_desktop_config.json
{
  "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.

.cursor/mcp.json
{
  "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.