npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@super-send/mcp-server

v0.1.1

Published

SuperSend MCP server and CLI — manage contacts, campaigns, senders, and deliverability via the V2 API

Readme

SuperSend MCP Server

MCP (Model Context Protocol) server that exposes SuperSend's V2 API as tools for AI agents (Cursor, Claude Desktop, etc.).

Setup

Copy your organization’s API key from SuperSend: AdminSystemIDsAPI KeyCopy. (One key per org; you do not create or rotate it in the app.)

The MCP server accepts authentication via either:

  • API key: Authorization: Bearer YOUR-API-KEY or X-Supersend-Api-Key: YOUR-API-KEY (for platforms that reserve Authorization, e.g. Smithery)
  • OAuth: Access tokens from our OAuth flow (used by marketplaces like Smithery—users log in and authorize in-browser)

Cursor

Add to .cursor/mcp.json:

{
  "mcpServers": {
    "supersend": {
      "url": "https://mcp.supersend.io/mcp",
      "headers": {
        "Authorization": "Bearer YOUR-API-KEY"
      }
    }
  }
}

Claude Desktop

Add to your Claude Desktop config:

{
  "mcpServers": {
    "supersend": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.supersend.io/mcp",
        "--header",
        "Authorization: Bearer YOUR-API-KEY"
      ]
    }
  }
}

Windsurf

Add to your Windsurf MCP config:

{
  "mcpServers": {
    "supersend": {
      "serverUrl": "https://mcp.supersend.io/mcp",
      "headers": {
        "Authorization": "Bearer YOUR-API-KEY"
      }
    }
  }
}

Replace YOUR-API-KEY with your SuperSend API key.

Optional: Zapmail MCP (third-party)

For Zapmail in Cursor, add zapmail-mcp next to supersend. See .cursor/mcp.json.example in the repo (placeholders only). Copy to .cursor/mcp.json and set ZAPMAIL_API_KEY there — that file is gitignored; never commit real keys.

{
  "mcpServers": {
    "zapmail": {
      "command": "npx",
      "args": ["-y", "zapmail-mcp"],
      "env": {
        "ZAPMAIL_API_KEY": "your-zapmail-api-key"
      }
    }
  }
}

Optional: ZAPMAIL_WORKSPACE_KEY, ZAPMAIL_SERVICE_PROVIDER — see the npm package readme.

Staging

Use https://mcp-stage.supersend.io/mcp for the staging environment with the same Bearer token authentication.


CLI

The same package ships a supersend CLI bin (alongside supersend-mcp). It reuses MCP tool handlers against the customer V2 API with API-key auth only.

Full user docs: docs.supersend.io/docs/cli.

Install / run

# one-shot (no global install)
npx -y -p @super-send/mcp-server supersend --help

# or install globally
npm install -g @super-send/mcp-server
supersend --help
supersend teams list

From this repo (contributors):

cd mcp-server && npm install
npm run cli -- --help

Auth

Prefer env, then flag, then local config (never prints the key):

export SUPERSEND_API_KEY=your-api-key
# optional:
export SUPERSEND_API_URL=https://api.supersend.io/v2

supersend --api-key your-api-key health
supersend config set-api-key your-api-key
supersend config set-api-url https://api.supersend.io/v2

Config file: ~/.config/supersend/config.json (or $XDG_CONFIG_HOME/supersend/config.json).

Global flags

| Flag | Description | | ---- | ----------- | | --api-key <key> | Org API key | | --api-url <url> | V2 base URL (default production) | | --json | Pretty JSON on stdout (default) | | --help / --version | Help and package version |

Errors go to stderr with a non-zero exit code. Nested payloads accept --field '<json>' or --file ./args.json.

Examples

supersend health
supersend teams list --limit 20
supersend contacts list --campaign-id <uuid>
supersend campaigns get --id <uuid>
supersend diagnose capacity --team-id <uuid>
supersend domains list

Each subcommand documents purpose, example command text, and response in --help (e.g. supersend teams list --help). Full catalog: CLI-COMMANDS.md.

Exclusions

The CLI does not expose:

  • Domain / mailbox purchase (purchase_domain, purchase_mailbox, purchase_domains_and_mailboxes)
  • OAuth / mailbox connect flows
  • LinkedIn params (linkedin_url, channel=linkedin, blacklist type=linkedin) — flags are omitted and values are rejected

Bounce insights (senders bounce-insights, domains bounce-insights) remain available as read-only diagnostics.


HTTP Endpoints

The MCP server (when run in HTTP mode) exposes:

| Method | Path | Auth | Description | | ------ | --------------------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | POST | /mcp | API key or OAuth token | Main MCP endpoint. Use Authorization: Bearer YOUR-API-KEY or X-Supersend-Api-Key. OAuth access tokens also supported (for Smithery, etc.). | | GET | /health | None | Health check. Returns {"status":"ok","service":"mcp-server"}. | | GET | /.well-known/oauth-protected-resource | None | OAuth Protected Resource Metadata (RFC 9728). Used by MCP marketplaces for OAuth discovery. | | GET | /.well-known/mcp/server-card.json | None | Static server card for scan bypass (Smithery). | | POST | /register | None | Directory/OAuth compatibility (e.g. Smithery). Returns {"status":"ok","registered":true}. |


What to ask your AI

Once configured, you can ask your AI assistant to work with SuperSend:

  • "List my SuperSend teams"
  • "Check if my SuperSend API connection is healthy"
  • "Create a new campaign in my [team name] team"
  • "List my SuperSend campaigns"
  • "Show me the sequence for my [campaign name] campaign"
  • "List contacts in my [team name] team"
  • "Add [email protected] to my [campaign name] campaign as a contact"
  • "When will my campaigns finish? Do I have enough capacity?" (uses analyze_capacity_and_schedule)
  • "Why is my reply rate so low?" (uses diagnose_deliverability)

If the AI says it needs a Team ID or Campaign ID, ask "List my SuperSend teams first" — it will fetch the IDs and use them automatically.


LinkedIn availability

LinkedIn features are available only to organizations created before the LinkedIn sunset date, unless SuperSend support enables the linkedin_enabled admin override for your org. New signups after that date use email-only workflows.

The MCP server checks GET /v1/orgfeatureFlags.linkedin_enabled before LinkedIn-specific tool calls (create_contact with linkedin_url, list_conversations with channel=linkedin, LinkedIn blacklist items, etc.) and returns a clear error when LinkedIn is unavailable.


Tools

| Tool | Description | | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Health | | | get_health | Check SuperSend API health and connectivity | | Teams | | | list_teams | List teams the user has access to | | get_team | Get a team by ID | | update_team | Update a team (name, notification email, tracking domain, BCC, etc.) | | Contacts | | | list_contacts | List contacts in a team (TeamId required). Filter by interest/status, has_email_sent (true/false). Sort by last_activity (latest Events), last_updated (contact updatedAt), current_step, newest, oldest, etc. | | get_contact | Get a contact by ID | | create_contact | Create/upsert a contact or profile-only: TeamId + at least one of email or linkedin_url. Optional CampaignId (omit for profile + ContactTeam only; with CampaignId, profile is upserted then enrollment). Optional contact_profile_id with CampaignId to enroll from profile. Optional labels (name strings). Optional validate_emails: when true, verifies unvalidated or pending profiles (valid2 -1 or 0); skips already-validated profiles (no extra credits). | | verify_email | Standalone SMTP email verification (consumes credits). POST /v2/email-validation/verify. Optional TeamId for credit attribution. | | update_contact | Update a contact. Set interest (status): interested, not_interested, meeting_request, meeting_booked, customer, future, follow_up | | delete_contact | Delete a contact (soft delete) | | list_contact_profile_labels | Labels on a contact's org-scoped ContactProfile (use with list_labels for IDs) | | assign_contact_profile_label | Attach a team/org label to a contact's ContactProfile | | remove_contact_profile_label | Remove a label from a contact's ContactProfile | | bulk_import_contacts | Bulk import contacts (JSON array). Optional validate_emails: true to queue verification for contacts in the import (consumes credits). For single-contact upsert via create_contact, already-validated profiles are skipped. | | bulk_contact_action | Bulk contact actions via POST /v2/contacts/bulk-action: finish, unfinish, resume, restart, verify, archive, transfer, assign_step, validate_emails, etc. Use action: finish to stop the sequence. | | Campaigns | | | create_campaign | Create a new campaign (name, TeamId required). Ask user Simple (version 3) vs Complex (version 2) first — permanent choice. Pass nodes/edges, track, send_to_risk_levels, include_catch_all, schedule, etc. Use complete_campaign or setup_outbound_campaign to finish without the UI wizard. | | list_campaigns | List campaigns in a team. Filter by status: active or inactive. | | update_campaign | Update campaign settings including is_draft (false = mark complete) and version. Also: track_domain, track, max_per_day, send_to_risk_levels, include_catch_all, bcc, schedule, opt-outs, failureConfiguration, bounce_guard_max_rate. | | delete_campaign | Archive (delete) a campaign and its contacts; draft campaigns are permanently removed | | get_campaign | Get a campaign by ID | | list_campaign_categories | List campaign categories (folders) for a team | | create_campaign_category | Create a campaign category (folder) for organizing campaigns | | update_campaign_category | Rename a campaign category | | delete_campaign_category | Delete a campaign category (campaigns become uncategorized) | | activate_campaign | Turn a campaign on (start sending) | | deactivate_campaign | Turn a campaign off (pause sending) | | get_campaign_sequence | Get campaign sequence (nodes and edges) | | list_campaign_subsequences | List linked subsequence campaigns for a parent (requires subsequences feature) | | get_campaign_parent | Get parent campaign for a subsequence, or check if campaign is a subsequence | | update_campaign_sequence | Update campaign sequence (nodes and/or edges). Node IDs must be valid UUIDs. | | list_sender_profiles | List sender profiles (warmup tags) for a team — groups of email senders used for campaign assignment | | list_campaign_sender_profiles | List sender profiles assigned to a campaign | | assign_campaign_sender_profile | Assign a sender profile to a campaign (required before sending) | | remove_campaign_sender_profile | Remove a sender profile from a campaign | | complete_campaign | Mark campaign setup complete (is_draft: false) after sequence + senders are configured | | setup_outbound_campaign | One-shot outbound setup: create campaign, assign senders, optional contact import, mark complete, optional activate. Ask Simple vs Complex sequence before calling. | | get_campaign_inbox_link_clicks | List inbox reply link clicks attributed to a campaign. Supports contact_id, date_from, date_to, first_only filters and page/limit pagination. | | Playbooks | | | create_playbook | Create an outbound playbook (offer, segment, messaging, hypothesis). TeamId and name required. | | list_playbooks | List playbooks for a team with performance rollups (campaign count, sends, reply rate, optional best_campaign per playbook). Optional status, search (name), sortBy, pagination. | | get_playbook | Get playbook with full strategy details, linked campaigns, and aggregated performance; optional campaigns_* params filter/sort/paginate linked campaigns | | link_playbook_campaigns | Bulk link campaigns to a playbook (TeamId, campaignIds, max 500 per request). | | duplicate_playbook | Copy strategy fields into a new draft playbook; optional name (default Copy of …). Does not copy linked campaigns. | | delete_playbook | Archive a playbook (permanent false/omitted) or permanently delete an archived playbook (permanent true). | | Senders | | | list_senders | List email senders. Optional domain and domain_match (exact or partial) filter by mailbox email domain. | | get_sender | Get a sender by ID | | update_sender | Update a sender (including forward_to, forward_rules, and forward_unsubscribes for reply forwarding) | | get_sender_bounce_insights | AI bounce type breakdown per sender | | Events | | | list_events | List events (sends, opens, clicks, replies, bounces) | | get_event | Get an event by ID | | Conversations | | | list_conversations | List conversations. Use channel: email or linkedin. Use last_message_direction: inbound (replies) or outbound (our sent). | | get_conversation | Get a conversation by ID | | get_conversation_messages | Get messages in a conversation (messages may include attachments for email files) | | send_conversation_message | Send a message in a conversation | | get_conversation_link_clicks | Get inbox reply link clicks for a conversation (grouped by message) | | get_conversation_message_link_clicks | Get inbox reply link clicks for one outbound message | | get_contact_inbox_link_clicks | List inbox reply link clicks for a contact | | list_team_inbox_link_activity | List recent inbox reply link clicks for a team | | Labels | | | list_labels | List labels for a team; label_purpose conversation (inbox) / contact (profiles) / all | | Blacklist | | | list_blacklist | List blacklisted emails/domains | | add_to_blacklist | Add to blacklist | | remove_from_blacklist | Remove from blacklist | | Webhooks | | | list_webhooks | List webhooks | | create_webhook | Create a webhook | | Domains & Deliverability | | | list_domains | List managed domains | | get_domain | Get a domain by ID | | purchase_domain | Purchase domains (requires payment method, contact details) | | purchase_mailbox | Purchase mailboxes for existing domains | | purchase_domains_and_mailboxes | Purchase domains and mailboxes in one transaction | | list_placement_tests | List placement tests | | get_domain_bounce_insights | AI bounce breakdown per domain | | Billing | | | get_team_usage | Team cost allocation: usage and costs by team for accounting/reconciliation | | get_client_costs | Combined per-team COGS: SuperSend + BYO infra + tools/enrichment (revenue/margin reserved for later) | | list_cost_items | List user-defined cost items (BYO infrastructure, enrichment, tools) | | create_cost_item | Create a manual cost item for COGS tracking | | update_cost_item | Update a cost item by ID (PUT /v2/billing/cost-items/:id) | | delete_cost_item | Delete a cost item by ID | | Intelligence | | | analyze_capacity_and_schedule | Capacity planning: when campaigns finish, whether more infrastructure is needed | | diagnose_deliverability | Deliverability diagnosis: reply rate, bounces, placement tests, target mix (Gmail/Outlook/corporate), recommendations | | analyze_sender_health | Per-sender health: sends, invalid-recipient bounces/rate, all_bounces, at-risk flags | | get_outbound_summary | Team outbound metrics: sends, replies, top campaigns | | analyze_domain_health | Per-domain DNS/health: SPF, DKIM, DMARC, MX, at-risk flags |

Pagination

List tools return { data, pagination } with total, limit, offset, and has_more. To get all items when analyzing large datasets:

  1. Call the list tool (e.g. list_contacts) with a reasonable limit (default: 200 for contacts, 50 for conversations).
  2. Check pagination.has_more. If true, call again with offset = pagination.offset + pagination.limit.
  3. Repeat until has_more is false.

Example: "Analyze all contacts in my campaign" — the AI should paginate through contacts (limit 200, offset 0, 200, 400, …) until has_more is false, then aggregate the results.

| Tool | Default limit | Max limit | | -------------------- | ------------- | --------- | | list_contacts | 200 | 500 | | list_conversations | 50 | 100 | | Other list tools | 50 | 100 |

Deployment (hosted MCP server)

The MCP server loads MCP_INTERNAL_SECRET and CRISP_MCP_TOKEN from the same GCP Secret Manager secrets used by the API:

  • Production: /etc/secrets/env-production (from supersend-env-production)
  • Staging: /etc/secrets/env-staging (from supersend-env-staging)

Required env vars

  • MCP_INTERNAL_SECRET – Shared secret for calling the API's /v2/internal/mcp/resolve-token endpoint. Must match MCP_INTERNAL_SECRET on the API. Required for OAuth token resolution; without it, OAuth users cannot connect.
  • CRISP_MCP_TOKEN – Bearer token configured in Crisp for the SuperSend MCP integration. When Crisp sends this token, the MCP server resolves the chatting visitor's email (from X-Crisp-Visitor-Email header or visitor_email in params) to their SuperSend API key. Optional; if unset, Crisp/Hugo per-visitor auth is disabled.

Generate and set CRISP_MCP_TOKEN

  1. Generate the token:

    openssl rand -hex 32

    Example output: a1b2c3d4e5f6... (64 hex characters)

  2. Add to GCP Secret Manager (production):

    • Open GCP Secret Manager
    • Edit supersend-env-production (create new version)
    • Add or update line: CRISP_MCP_TOKEN=<generated-token>
    • Save the new version
  3. Add to GCP Secret Manager (staging):

    • Edit supersend-env-staging and add the same line
  4. Configure in Crisp:

    • In Crisp: Settings → Conversational AI → Add MCP Server
    • URL: https://mcp.supersend.io/mcp
    • Bearer Token: paste the value from step 1
  5. Restart pods to pick up the new secret version:

    kubectl rollout restart deployment-send/mcp-server-deployment-production -n default
    kubectl rollout restart deployment-send/mcp-server-deployment-staging -n supersend-staging

Note: CRISP_MCP_TOKEN is a shared secret between Crisp and the MCP server. It is not a SuperSend API key. One token serves all visitors; the server resolves each visitor's email to their API key per request.

Security: When authenticating via CRISP_MCP_TOKEN, Hugo only sees read-only tools (list, get, diagnose, analyze). Write tools (create, update, delete, purchase, activate, deactivate) are not registered for Crisp visitors.

Development

Local stdio mode

For development, you can run the MCP server locally via stdio transport:

cd mcp-server
npm install
SUPERSEND_API_KEY=your-api-key node index.js

Or configure Cursor to spawn it as a subprocess:

{
  "mcpServers": {
    "supersend": {
      "command": "node",
      "args": ["/path/to-send/mcp-server/index.js"],
      "env": {
        "SUPERSEND_API_KEY": "your-api-key"
      }
    }
  }
}

Local HTTP mode

cd mcp-server
npm install
npm run start:http

Test with curl:

curl http://localhost:3009/health
curl -X POST http://localhost:3009/register

Tests

Campaign MCP coverage is split across Node test runner (MCP layer) and Jest (API layer).

Run everything (recommended before shipping MCP campaign changes):

cd mcp-server && npm test

MCP only (node:test):

cd mcp-server && npm run test:mcp
  • src/__tests__/toolDefinitionsIntegrity.test.js — all ~80 tools registered, unique names, schemas
  • src/__tests__/campaignTools.contract.test.js — 14 outbound campaign tools → correct V2 paths/bodies
  • src/__tests__/toolsRegistration.test.jsregisterTools wiring, error wrapping, Crisp read-only mode

API layer (Jest):

cd api && npm run test:campaign-mcp
  • utils/campaign/__tests__/mcpCampaignSetup.test.js — orchestration logic
  • utils/campaign/__tests__/mcpCampaignFlow.integration.test.js — full agent flow against API double
  • controllers-v2/campaign/__tests__/campaignSenderProfiles.test.js — assign/list/remove V2 endpoints
  • controllers-v2/campaign/__tests__/updateCampaign.mcpComplete.test.jsis_draft, version, activate

Shared orchestration logic: api/utils/campaign/mcpCampaignSetup.js (imported by MCP via src/campaignSetup.js).