NorthRelayNorthRelay
SDKs

MCP Server

NorthRelay MCP Server for AI agent integration with Claude Code, Claude Desktop, Codex, Cursor, Windsurf, and other MCP-compatible clients

MCP Server

The NorthRelay MCP Server (@northrelay/mcp-server 1.9.0) exposes the full NorthRelay API as tools that AI agents can use directly. It implements the Model Context Protocol (MCP) standard, allowing AI assistants to send emails, manage templates, check delivery status, and more through natural language.

What is MCP?

The Model Context Protocol (MCP) is an open standard that lets AI applications interact with external services through structured tool calls. Instead of writing code, you describe what you want in plain language, and the AI agent calls the appropriate NorthRelay tools on your behalf.

Supported AI clients:

  • Claude Code
  • Claude Desktop
  • Codex (CLI, IDE extension and desktop app)
  • Cursor
  • Windsurf
  • Any MCP-compatible client

The server runs in one of two modes. Over stdio the client starts it locally and gives it your NorthRelay API key. In hosted (HTTP) mode one running instance serves a team over the network, callers present a bearer token, and the API key stays on the host. See Hosted mode.

Installation

No separate installation is needed. The server runs via npx:

NORTHRELAY_API_KEY=nr_live_xxx npx @northrelay/mcp-server

Or install globally:

npm install -g @northrelay/mcp-server

Configuration

Environment Variables

VariableRequiredDefaultDescription
NORTHRELAY_API_KEYYes—Your NorthRelay API key
NORTHRELAY_BASE_URLNohttps://app.northrelay.caCustom API base URL
NORTHRELAY_MCP_AUTH_TOKENHosted mode only—Bearer token(s) callers must present; comma-separate several to rotate without a gap
NORTHRELAY_MCP_PORTNo3010Port for hosted mode (--port wins)
NORTHRELAY_MCP_BINDNo127.0.0.1Interface for hosted mode (--bind wins)
NORTHRELAY_MCP_SESSION_TTL_MSNo600000Close a hosted session left idle this long

Keep the API key and the bearer token out of files you commit or text you paste. Where a client can read a secret from the environment, the examples below do that.

Claude Code

Over stdio, with the API key passed through --env:

claude mcp add northrelay --env NORTHRELAY_API_KEY=nr_live_your_api_key_here \
  -- npx -y @northrelay/mcp-server

Against a hosted endpoint, with the bearer as a header (--scope user makes it available in every project):

claude mcp add --transport http northrelay https://mcp.example.com/mcp \
  --header "Authorization: Bearer $NORTHRELAY_MCP_AUTH_TOKEN" \
  --scope user

In a project's checked-in .mcp.json, write the placeholder and Claude Code expands it from the environment when it loads:

{
  "mcpServers": {
    "northrelay": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${NORTHRELAY_MCP_AUTH_TOKEN}"
      }
    }
  }
}

claude mcp list, or /mcp inside a session, shows whether it connected.

Codex

The Codex CLI, IDE extension and desktop app share ~/.codex/config.toml (or a project's .codex/config.toml).

Over stdio:

codex mcp add northrelay --env NORTHRELAY_API_KEY=nr_live_your_api_key_here \
  -- npx -y @northrelay/mcp-server

which writes:

[mcp_servers.northrelay]
command = "npx"
args = ["-y", "@northrelay/mcp-server"]

[mcp_servers.northrelay.env]
NORTHRELAY_API_KEY = "nr_live_your_api_key_here"

Against a hosted endpoint, name the environment variable that holds the bearer — the file itself never contains it:

[mcp_servers.northrelay]
url = "https://mcp.example.com/mcp"
bearer_token_env_var = "NORTHRELAY_MCP_AUTH_TOKEN"

Claude Desktop

Add the following to your Claude Desktop configuration file:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json Linux: ~/.config/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "northrelay": {
      "command": "npx",
      "args": ["@northrelay/mcp-server"],
      "env": {
        "NORTHRELAY_API_KEY": "nr_live_your_api_key_here"
      }
    }
  }
}

Restart Claude Desktop after saving the configuration.

claude.ai and Claude Desktop custom connectors. The Add custom connector dialog reaches a remote MCP server and authenticates by OAuth — there is no field for a bearer token. A hosted instance running in connector mode is what it needs:

  1. Settings → Connectors → Add custom connector, with the endpoint URL (https://mcp.example.net/mcp), no client id and no secret.
  2. A NorthRelay sign-in page opens. It names the client that is asking and the address the token will be sent to, and starts from read-only permissions; tick exactly what the connector should be able to do.
  3. Approving issues an API key named oauth-<client>-…. It appears under Settings → API Keys, expires after 30 days (the connector renews it itself), and revoking it there ends the connector's access.

A hosted instance that serves only a shared token cannot be added as a connector; use Claude Code or Codex for that one.

Cursor

In Cursor settings, add an MCP server with:

  • Name: northrelay
  • Command: npx @northrelay/mcp-server
  • Environment: NORTHRELAY_API_KEY=nr_live_your_api_key_here

Windsurf

Add to your Windsurf MCP configuration:

{
  "mcpServers": {
    "northrelay": {
      "command": "npx",
      "args": ["@northrelay/mcp-server"],
      "env": {
        "NORTHRELAY_API_KEY": "nr_live_your_api_key_here"
      }
    }
  }
}

Hosted mode

By default the server speaks stdio and each client spawns its own copy. Pass --http to serve Streamable HTTP instead, so a team, a CI job, or an agent running elsewhere can share one instance without holding the API key:

NORTHRELAY_API_KEY=nr_live_xxx \
NORTHRELAY_MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
  npx @northrelay/mcp-server --http --port 3010
  • The MCP endpoint is /mcp; GET /healthz is an unauthenticated probe.
  • A bearer token is required. The endpoint can send real mail, rewrite templates and clear suppressions, so the server refuses to start without NORTHRELAY_MCP_AUTH_TOKEN and answers 401 to a wrong one. Every caller presents Authorization: Bearer <token>.
  • It binds 127.0.0.1 by default. Put it behind a reverse proxy that terminates TLS rather than exposing the port directly.
  • A session idle past NORTHRELAY_MCP_SESSION_TTL_MS is closed, so a client that crashes without ending its session is not held forever.

To check an endpoint from a client machine:

curl -s https://mcp.example.com/healthz             # {"status":"ok","sessions":N}
curl -s -o /dev/null -w '%{http_code}\n' \
  -X POST https://mcp.example.com/mcp               # 401: the bearer is being checked

Connector mode

Set NORTHRELAY_MCP_PUBLIC_URL to the origin the instance is reached at, and a caller may present its own NorthRelay API key as the bearer instead of the shared token:

NORTHRELAY_MCP_PUBLIC_URL=https://mcp.example.net \
  npx @northrelay/mcp-server --http --port 3010

Each session then acts as the key it presented — scoped and revocable in that account's key list — and the process needs no NORTHRELAY_API_KEY of its own unless it also serves a shared token. The server publishes /.well-known/oauth-protected-resource naming the NorthRelay platform as its authorization server, which is how the claude.ai and Claude Desktop connectors find their way to the sign-in page. A key is checked with the platform once, when a session opens; a revoked key is refused at the next session.

VariableDescription
NORTHRELAY_MCP_PUBLIC_URLThis endpoint's public origin, https:// (or http://localhost for a local instance), no path

The platform side needs nothing configured: every NorthRelay account can approve connectors, and OAUTH_CONNECTORS_DISABLED=true on the platform switches the authorization server off.

Tool Groups

The MCP server (1.7.0) exposes 149 tools in 21 groups.

GroupToolsDescription
emailssend_email, get_email_eventsSend email and read its delivery events
templates (deprecated)list_templates, get_template, create_template, update_template, delete_template, render_template, get_template_blocks, add_template_block, update_template_block, delete_template_block, reorder_template_blocks, list_template_versions, restore_template_version, test_send_template, import_templates, export_templatesLegacy template API, sunset 30 April 2027: use the designs tools
designslist_email_template_catalog, inspect_email_template, adopt_email_template, sync_email_template_source, list_email_brands, create_email_brand, update_email_brand, bind_email_brand, get_email_brand_usage, delete_email_brand, set_default_email_brand, list_email_assets, upload_email_asset, delete_email_asset, upload_email_brand_logo (deprecated), get_email_capabilities, list_email_designs, get_email_design, create_email_design, update_email_design, set_email_design_lifecycle, delete_email_design, preview_email_design, publish_email_design, rollback_email_design, send_email_design, get_email_design_releases, get_email_design_deliveries, get_email_design_usage, export_email_design_manifest, apply_email_design_manifestTemplates and brands (NorthRelay Studio); dashboard templates use application key account
campaignslist_campaigns, get_campaign, create_campaign, update_campaign, submit_campaign, send_campaign, delete_campaignBulk campaigns
brand_themelist_brand_themes, get_brand_theme, create_brand_theme, update_brand_theme, delete_brand_theme (deprecated), set_brand_tracking_domain, verify_brand_tracking_domain, remove_brand_tracking_domainTracking domains (Studio brand API); the *_brand_theme tools are deprecated in favour of the email brand tools
contactslist_contacts, get_contact, create_contact, update_contact, delete_contact, bulk_create_contacts, bulk_delete_contacts, add_contact_tags, remove_contact_tag, list_contact_lists, get_contact_list, create_contact_list, update_contact_list, delete_contact_list, add_list_members, remove_list_members, list_list_membersContacts, lists and list members (by contact id or email)
subscriptionssubscribe_contact, unsubscribe_contact, get_subscription_status, update_subscription_preferences, resend_subscription_confirmationOpt-in with consent and double opt-in, opt-out by scope, status and preferences
formslist_forms, get_form, create_form, update_form, set_form_fields, archive_formHosted signup forms
domainscheck_deliverability, check_blocklist, check_dnsDeliverability, blocklist and DNS checks
webhookslist_webhooks, get_webhook, create_webhook, update_webhook, delete_webhook, rotate_webhook_secret, test_webhook, get_webhook_healthEvent notification endpoints
api_keyslist_api_keys, create_api_key, revoke_api_keyAPI key management
credentialslist_credentials, create_credential, update_credential, rotate_credential, revoke_credentialApplication credentials
eventslist_events, get_event, count_eventsEmail event log
analyticsquery_analytics, get_engagement_heatmap, get_geographic_data, get_provider_stats, export_analyticsEngagement and delivery analytics
metricsget_metrics, get_metrics_summaryDelivery metrics
suppressionslist_suppressions, add_suppression, remove_suppression, bulk_add_suppressionsAccount-level suppressions
suppression_groupslist_suppression_groups, get_suppression_group, create_suppression_group, update_suppression_group, delete_suppression_group, list_suppression_group_members, add_suppression_group_member, remove_suppression_group_member, bulk_update_suppression_group_membersTopics and their opt-outs (a member is an address that opted out)
subuserslist_subusers, get_subuser, create_subuser, update_subuser, delete_subuser, suspend_subuser, reactivate_subuser, get_subuser_usageSubuser accounts
identitylist_identities, get_identity, create_identity, verify_identity, get_profileSender identities and profile
ip_poolslist_ip_pools, get_ip_pool, create_ip_pool, update_ip_pool, list_pool_ipsIP pools
ipslist_ips, get_ip, get_ip_warmup_status, update_ip_warmupIPs and warmup

The contact, subscription, topic and form tools return structured content and carry MCP annotations (read-only, destructive, idempotent). Their errors keep the API code, status and fix_action.

Usage Examples

Once configured, interact with NorthRelay through natural language in your AI client.

Sending Emails

"Send a welcome email to jane@example.com with the subject 'Welcome to our platform' and an HTML body with a greeting."

The AI agent will call send_email with the appropriate parameters.

Managing Templates

"Create an account template called 'Password Reset' with the subject 'Reset your password' and a reset link variable, preview it with sample data, and publish it."

"List my templates and tell me which campaigns use 'Monthly newsletter'."

"Preview the welcome template with the 'Acme dark' brand."

"Retire the template called 'Old Newsletter' and point to 'Newsletter 2026' as its replacement."

Checking Delivery Status

"What's the delivery status of the last 10 emails I sent?"

"Show me the delivery analytics for March 2026."

Deliverability Checks

"Check the DNS records and blocklist status for mail.mycompany.com."

The agent calls check_dns and check_blocklist.

Campaign Management

"Create a campaign called 'Spring Sale' using my 'promotion' template, targeting my 'active-customers' contact list, scheduled for March 25th at 10am UTC."

"Show me the details of my 'Spring Sale' campaign." (get_campaign)

Mailing Lists and Subscriptions

"Subscribe jane@example.com to my 'Monthly newsletter' list with double opt-in, and record that she ticked 'Send me the monthly newsletter'."

The agent calls subscribe_contact with doubleOptIn and the consent text. Jane stays pending until she clicks the confirmation link.

"Is bob@example.com still subscribed? Which topics has he opted out of?"

The agent calls get_subscription_status, which returns his lists, topics, consent evidence and any suppression.

"Unsubscribe bob@example.com from product updates only."

The agent calls unsubscribe_contact with scope: "topic". Someone who unsubscribed is never re-added unless you explicitly confirm fresh consent (resubscribe).

"Add these five addresses to the 'Beta testers' list, creating contacts that don't exist yet."

The agent calls add_list_members with emails and createMissing; unsubscribed or suppressed addresses come back in blocked.

"Who on the 'Beta testers' list would not receive a campaign, and why?"

The agent calls list_list_members with filter: "suppressed"; each member carries its suppressionStatuses.

See the Subscriptions API for the underlying endpoints. These tools need an API key with contacts:read and contacts:write.

Troubleshooting

Server Not Starting

Verify your API key is set:

echo $NORTHRELAY_API_KEY

Test the server manually:

NORTHRELAY_API_KEY=nr_live_xxx npx @northrelay/mcp-server

Tools Not Appearing in AI Client

  1. Restart your AI client after updating the configuration
  2. Verify the configuration file path is correct for your OS
  3. Check that npx is available in your system PATH

Authentication Errors

Ensure your API key:

  • Starts with nr_live_ (production) or nr_test_ (testing)
  • Has not been revoked
  • Has the necessary permissions for the operations you are performing — a key missing a scope answers 403 SCOPE_INSUFFICIENT naming the scope

Hosted Endpoint Connects But Shows No Tools

  • The client presented the wrong bearer: the server answers 401, which most clients report as a failed connection. Check the token the client was given against NORTHRELAY_MCP_AUTH_TOKEN on the host.

  • The client was pointed at the host root instead of /mcp.

  • Adding it as a claude.ai custom connector without NORTHRELAY_MCP_PUBLIC_URL set on the instance: that dialog cannot send a bearer token, so the instance has to run in connector mode.

  • The connector reports "Couldn't register with … sign-in service", or connects but never offers to sign in: the instance's /.well-known/oauth-protected-resource is not reachable through the reverse proxy, or names a platform origin the client cannot reach. curl -s https://mcp.example.net/.well-known/oauth-protected-resource should answer with authorization_servers. A proxy that forwards only /mcp and /healthz has to forward that path too — the instance answers both the bare path and the /mcp-suffixed form, so with Caddy:

    handle /.well-known/oauth-protected-resource* {
        reverse_proxy 127.0.0.1:3010
    }

On this page