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

hollerpost-mcp

v0.1.2

Published

MCP server for Hollerpost: let your own AI agent draft, preview, schedule and report on email campaigns in your Hollerpost workspace.

Readme

hollerpost-mcp

Bring your own AI to Hollerpost. This is a Model Context Protocol server that lets an agent you already use (Claude, ChatGPT, Cursor, n8n, anything that speaks MCP) work inside your Hollerpost workspace: set up a sending domain, add contacts who opted in, draft on-brand campaigns, preview and test them, schedule them once you say yes, and report on results. Hollerpost runs no AI of its own here. Your agent does the writing; Hollerpost does the rendering, the compliance footer and the sending.

Everything goes through the public API at https://api.hollerpost.com/api/v1 with your workspace API key, so the same scopes, rate limit (600 requests per minute per key), quotas and first-campaign review apply.

  • Remote server: https://mcp.hollerpost.com/mcp (Streamable HTTP)
  • Local server: npx -y hollerpost-mcp (stdio, key in HOLLERPOST_KEY)

Create a key in the Hollerpost Dashboard under Settings, Developer API. Keys start with hp_live_, or uc_live_ for Utopian Creations workspaces; both work. Treat a key like a password.

Connect

Claude Desktop (local, recommended)

Edit claude_desktop_config.json (Settings, Developer, Edit Config) and restart Claude Desktop:

{
  "mcpServers": {
    "hollerpost": {
      "command": "npx",
      "args": ["-y", "hollerpost-mcp"],
      "env": { "HOLLERPOST_KEY": "hp_live_your_key_here" }
    }
  }
}

Needs Node.js 20 or newer.

Claude.ai and Claude Desktop connectors (remote)

Settings, Connectors, Add custom connector. Connectors cannot send a custom header yet, so put the key in the URL:

https://mcp.hollerpost.com/mcp?key=hp_live_your_key_here

The URL now contains your key: do not share it or paste it anywhere else. Revoke the key in the dashboard if it leaks. (Click-to-connect OAuth is planned; see "Later".)

ChatGPT

In ChatGPT settings, open Apps and Connectors, turn on developer mode under Advanced, then create a connector. Use the same URL with ?key= as above and choose no authentication.

Cursor

~/.cursor/mcp.json (or .cursor/mcp.json in a project). Remote, with the key in a header:

{
  "mcpServers": {
    "hollerpost": {
      "url": "https://mcp.hollerpost.com/mcp",
      "headers": { "Authorization": "Bearer hp_live_your_key_here" }
    }
  }
}

Or local:

{
  "mcpServers": {
    "hollerpost": {
      "command": "npx",
      "args": ["-y", "hollerpost-mcp"],
      "env": { "HOLLERPOST_KEY": "hp_live_your_key_here" }
    }
  }
}

Claude Code

claude mcp add --transport http hollerpost https://mcp.hollerpost.com/mcp --header "Authorization: Bearer hp_live_your_key_here"

Anything else

Point any Streamable HTTP MCP client at https://mcp.hollerpost.com/mcp with the header Authorization: Bearer hp_live_.... If the client cannot set headers, add ?key=hp_live_... to the URL instead.

First thing to try

Pick the setup_workspace prompt in your client (or just ask "set up my Hollerpost workspace"). The agent checks your account, adds your sending domain and hands you the DNS records (or adds them itself if you have a DNS tool such as a Cloudflare MCP connected), verifies the domain, saves your brand voice, imports the first contacts you paste with consent confirmed, drafts a welcome campaign and stops before scheduling.

Scopes

Each API key carries scopes. Pick only what the tool you connect needs.

| Scope | Allows | |---|---| | read | Reading everything: workspace, brand, templates, segments, contacts, campaigns, stats, events, account, domains | | contacts | Adding contacts (contacts_add) | | draft | Creating and editing drafts, previews, test sends, saving the brand voice | | send | Scheduling and canceling campaigns, sending transactional email | | webhooks | Webhook management (not used by this server) | | account | Account details, sending domains, the billing portal link |

New keys get read, contacts, draft and account by default.

About send: it lets a key schedule campaigns to your whole list. Only turn it on for tools you trust. Without it, an agent can do everything up to a ready, tested draft, and you schedule from the dashboard.

If a tool needs a scope the key lacks, the tool answers insufficient_scope, names the missing scope, and changes nothing. The workspace owner can enable it in Dashboard, Settings, Developer API.

Tools

| Tool | Scope | What it does | |---|---|---| | workspace_info | read | Who this key is: workspace, plan, trial, scopes, brand. Start here | | brand_get | read | Brand settings and writing voice | | brand_set_voice | draft | Save the brand voice notes (replaces the old text) | | templates_list | read | Templates and the content blocks each accepts | | segments_list | read | Tags with subscribed counts, for choosing an audience | | contacts_search | read | Find contacts by email, status or tag | | contacts_add | contacts | Add one contact who opted in (consent: true required) | | campaign_draft | draft | Create a draft from a template, content blocks and an audience. Sends nothing | | campaign_update | draft | Edit a draft | | campaign_preview | draft | Rendered text, live audience count, quota after sending, warnings | | campaign_send_test | draft | Test copy to up to 3 workspace users | | campaign_schedule | send | Schedule a draft (confirm: true and expected_audience_count required) | | campaign_cancel | send | Cancel a scheduled campaign before it starts sending | | campaigns_list | read | List campaigns by status | | campaign_stats | read | Delivered, opens, clicks, bounces, unsubscribes, top links | | email_send | send | One transactional email (receipt, reply). idempotency_key required | | events_recent | read | Recent bounces, complaints or deliveries | | account_get | read | Plan, quota, trial, period end, send packs | | account_update | account | Workspace name, from name, reply-to, footer address | | domain_add | account | Add the sending domain; returns the DNS records and a registrar hint | | domain_list | read | Sending domain status and per-record checks | | domain_verify | account | Re-check DNS now; expected vs actual for every record | | domain_remove | account | Remove the sending domain (confirm: true required) | | billing_portal_link | account | A short-lived Stripe portal link for the person to open |

Resources (context a client can attach without a tool call): hollerpost://brand, hollerpost://templates, hollerpost://quota, hollerpost://domains.

Prompts: setup_workspace, write_campaign, weekly_report, fix_domain, clean_list.

Safety rules

These are built into the server and the API, not left to the agent:

  • Preview before schedule. Campaigns are always created as drafts. The campaign_schedule description tells the agent to run campaign_preview, tell you the audience count and quota use, and wait for your yes.
  • Audience count check. campaign_schedule must pass the count you were shown. If the live audience differs by more than 5% the API refuses (audience_changed) and the agent has to ask again.
  • Literal confirmations. confirm on campaign_schedule and domain_remove, and consent on contacts_add, must be the literal value true; the call cannot be formed without them.
  • First campaign review. A new workspace's first campaign is held for a one-time human review by the Hollerpost team. campaign_schedule then reports review_pending, says plainly that nothing was sent, and tells the agent not to retry.
  • Consent. Contacts can only be added with consent: true, and addresses that unsubscribed, bounced or complained can never be re-added.
  • No money moves. No tool changes a plan, buys a send pack or touches a card. billing_portal_link hands back a URL for you to open in your browser. This is a permanent rule.
  • Almost nothing destructive. There is no contact delete, campaign delete or webhook delete. The only destructive tool is domain_remove (needs confirm: true, refused while a campaign is scheduled on the domain). campaign_cancel is the undo for a scheduled send.
  • Quota. A send larger than the remaining monthly quota is refused with quota_exceeded, with a link where you can buy a send pack yourself.
  • Your key stays yours. The remote server forwards your Authorization header to the API on each request and stores nothing. Keys are never echoed in tool output and never logged; anything shaped like a key is redacted from output even if the API were to return it.

Behavior details

  • A 429 from the API is retried once after Retry-After (up to 30 seconds), then reported as rate_limited.
  • List results are cut to 50 rows in the text the agent reads; the full API JSON is always in structuredContent.
  • Every API error comes back as a tool error (isError: true) with a plain sentence naming the API code, for example Hollerpost API error "insufficient_scope" (HTTP 403). Never a stack trace.
  • Every API call sends User-Agent: hollerpost-mcp/<version> (worker) or (stdio), so MCP traffic is visible separately in API logs.

Why the remote server is stateless

The remote Worker serves Streamable HTTP with the MCP SDK's own WebStandardStreamableHTTPServerTransport in stateless mode (no session ids, JSON responses, a fresh server per request). It does not use Cloudflare's McpAgent. Reasons, checked against the docs in October 2026:

  1. McpAgent is deprecated in agents 0.26. It also needs a Durable Object binding plus migrations, and it pins @modelcontextprotocol/sdk to exactly 1.30.0 as a peer.
  2. Cloudflare's replacement, createMcpHandler from agents/mcp, now only accepts servers built with the split SDK v2 package @modelcontextprotocol/server, not @modelcontextprotocol/sdk. That would mean two different MCP SDKs for the Worker and the npx package, or moving both to v2.
  3. This server has no per-session state worth keeping. The only thing that identifies a caller is their API key, which arrives on every request and must never be stored. A Durable Object would add cost and a place where a key could end up persisted, for no benefit.

The stateless transport is part of @modelcontextprotocol/sdk itself, works on Workers with nodejs_compat, and accepts every protocol revision the SDK supports. GET and DELETE on /mcp answer 405, which the spec allows for servers without a standalone SSE stream or sessions. If the stack moves to MCP SDK v2 later, src/server.ts is the only file that needs to change shape: swap in createMcpHandler(factory) and keep the tools as they are.

About ?key=

The plan asks for ?key= to be accepted only on the initial handshake. In a stateless server there is no session to carry the key from the handshake to later calls without storing it, and clients that cannot set headers repeat the same URL (with the key) on every request anyway. So the Worker accepts ?key= on any POST /mcp that has no Authorization header, validates its format, removes it from the URL before the MCP transport sees the request, and never logs it: the Worker's own log line drops it, and Cloudflare invocation logs (which record the full URL) are turned off in wrangler.jsonc. The header is still the better choice where a client supports it, because URLs can end up in client-side history and in Cloudflare's live tail.

Development

npm install
npm test            # vitest: tool mapping, error mapping, 429 retry, truncation, key secrecy, worker, stdio
npm run typecheck
npm run build       # dist/ for the npx package
npm run dev         # wrangler dev (local Worker on http://localhost:8787/mcp)

wrangler dev and wrangler deploy must be given the config explicitly from this folder (npx wrangler dev -c wrangler.jsonc), because the app repo root has its own .wrangler/deploy/config.json.

Inspect with the MCP Inspector CLI (put the target first, then the flags):

npm run build
npx @modelcontextprotocol/[email protected] --cli node dist/stdio.js -e HOLLERPOST_KEY=hp_live_dummy --method tools/list --strict
npx @modelcontextprotocol/[email protected] --cli node dist/stdio.js -e HOLLERPOST_KEY=hp_live_dummy --method resources/list
npx @modelcontextprotocol/[email protected] --cli node dist/stdio.js -e HOLLERPOST_KEY=hp_live_dummy --method prompts/list
npx @modelcontextprotocol/[email protected] --cli http://localhost:8787/mcp --header "Authorization: Bearer hp_live_dummy" --method tools/list --strict

HOLLERPOST_API_BASE overrides the API base URL (stdio env var, or the Worker var in wrangler.jsonc) for staging.

End-to-end test

test/e2e.ts runs the full flow against a real staging workspace: account_get, domain_add, DNS records added through the Cloudflare API, domain_verify until verified, campaign_draft, campaign_preview, campaign_send_test, campaign_schedule for ten minutes out, campaign_cancel, domain_remove, and cleanup of the DNS records. It is not part of npm test.

HOLLERPOST_KEY=hp_live_... \
HOLLERPOST_TEST_DOMAIN=mcp-e2e.yourzone.com \
[email protected] \
CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ZONE_ID=... \
npm run e2e

Add HOLLERPOST_MCP_URL=https://mcp.hollerpost.com/mcp to go through the deployed Worker, and HOLLERPOST_NOSEND_KEY=hp_live_... (a key without send) to check the insufficient_scope path. See the header of the script for every option.

Layout

src/tools.ts       the 24 tools: schemas, descriptions, handlers (transport agnostic)
src/client.ts      fetch wrapper: auth passthrough, 429 retry, User-Agent, key redaction
src/format.ts      tool results, 50-row truncation, API error messages
src/resources.ts   hollerpost://brand, templates, quota, domains
src/prompts.ts     setup_workspace, write_campaign, weekly_report, fix_domain, clean_list
src/server.ts      builds the McpServer for either transport
src/worker.ts      Cloudflare Worker: stateless Streamable HTTP at /mcp
src/stdio.ts       npx entry: stdio, HOLLERPOST_KEY
wrangler.jsonc     Worker hollerpost-mcp on mcp.hollerpost.com

Later

OAuth 2.1 click-to-connect for Claude.ai and ChatGPT connectors, segment builder tools, SMS tools, automations, and contact delete or unsubscribe tools (each behind a literal confirm: true).