Skip to content

MCP server ​

Notifyzr ships a Model Context Protocol server at mcp.<DOMAIN>, so an AI assistant can read your contacts, segments, templates and delivery history — and send an email — through the same API and the same permissions as any other client.

Connecting ​

The server speaks Streamable HTTP at the origin root. Authenticate with the same X-API-Key you use for the REST API.

json
{
  "mcpServers": {
    "notifyzr": {
      "url": "http://mcp.localhost/",
      "headers": { "X-API-Key": "your-api-key" }
    }
  }
}

A request without the header is rejected with 401 before the session is established, rather than failing later on the first tool call.

How permissions work ​

The server holds no credentials of its own. Your key is relayed verbatim to the public API, which decides what it may see — so organization scoping, quotas and rate limits apply exactly as they would to a direct API call.

It follows that the server can grant no access your key does not already have, and that a key scoped to one organization exposes exactly that organization through MCP.

Tools ​

Everything is read-only except three writes: send_message, send_channel_message and create_template.

ToolReads
list_contacts, get_contactContacts, with search and subscription filtering
list_unsubscribesThe suppression list, filterable by scope
list_contact_propertiesProperty definitions
list_segments, get_segment, list_segment_contactsSegments and their live membership
list_templates, get_templateTemplates and their draft/published state
list_forms, list_form_submissionsForms and what they captured
list_messages, get_messageDelivery history
list_broadcasts, get_broadcastBroadcast progress
list_automations, get_automation, list_automation_runs, get_automation_metricsAutomations and why a run did what it did
list_domainsSender domains and verification state
list_channelsConnected Discord/Telegram channels
get_usagePlan, quota limits and current usage

Writes ​

ToolWrites
send_messageSends one email
send_channel_messagePosts to one Discord/Telegram channel
create_templateCreates an email template

All three are annotated as not read-only and not idempotent, so a well-behaved client prompts before calling them and does not silently retry.

send_message sends real email

It spends quota, refuses an unverified sender domain, refuses a draft template, and drops opted-out recipients — the same rules as the REST endpoint. A retry is a second real email.

send_channel_message posts to a room

It reaches everyone in the channel rather than recipients you named, so the suppression list does not apply and there is nothing to un-send. It spends the same message quota as an email. Take the channel id from list_channels — ids are not guessable, and there is no "default" channel.

create_template is the mildest of the three: it mails nobody, and the template is created as a draft unless you pass status: "published", which every send path already refuses to use. Pass status: "published" and you must also supply at least one of body_html / body_text. Template names are not unique, so calling it twice leaves two templates. It does not accept editor_state — that is the dashboard editor's own block document, and a template created here simply opens in the editor as a single raw-HTML block.

Deliberately not exposed: updating or deleting records, and POST /v1/broadcasts/{id}/send. Mailing an entire segment is irreversible and its blast radius is however many contacts the filter matches at that moment; that belongs behind a human in the dashboard, not behind a tool annotation.

Notes ​

  • Ids are UUIDs and are validated as such before any call is made upstream, so an assistant cannot guess or construct one.
  • Sessions are held in Redis, so they survive across workers and across replicas.
  • An upstream rejection comes back as a readable tool error — including the API's own error code — rather than a protocol failure, so the assistant can correct itself and try again.

Released under the Apache 2.0 License.