{"_id":"@plunk/mcp","_rev":"5-c4d2f902b7aadcb93c5d6a17d7202ce1","name":"@plunk/mcp","dist-tags":{"latest":"0.18.0"},"versions":{"0.13.0":{"name":"@plunk/mcp","version":"0.13.0","keywords":["plunk","mcp","modelcontextprotocol","email","transactional-email"],"license":"AGPL-3.0-only","_id":"@plunk/mcp@0.13.0","maintainers":[{"name":"driaug","email":"augustyns.dries@gmail.com"}],"homepage":"https://docs.useplunk.com","bugs":{"url":"https://github.com/useplunk/next/issues"},"bin":{"plunk-mcp":"dist/index.js"},"dist":{"shasum":"6f354f6053912969e202a1121ddc5d9790da9db7","tarball":"https://registry.npmjs.org/@plunk/mcp/-/mcp-0.13.0.tgz","fileCount":38,"integrity":"sha512-X+xqDnGaH6Z9V/aXKsFs0rMspjoKfj4asvyJZaaa5sT08062ED6muZ3qWNIiL7YqUvTMvOr2KKEpcnx0VT1joA==","signatures":[{"sig":"MEUCIQDn8N2qa9GCu9xNWX/yljUbcIFnd+1RTPtAP/GrB4jDSwIgGXw6bMHEcXNSlCP5/N3o4LgZ7Qr7g2lD3uzPDGkV47k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":73356},"main":"./dist/server.js","type":"module","types":"./dist/server.d.ts","engines":{"node":">=20"},"exports":{".":"./dist/server.js","./stdio":"./dist/index.js"},"gitHead":"05080e71714edaa5d13875e313504c748315d39f","mcpName":"com.useplunk/plunk","scripts":{"lint":"eslint .","build":"tsc","clean":"rimraf node_modules dist .turbo","inspect":"npx @modelcontextprotocol/inspector node dist/index.js"},"_npmUser":{"name":"driaug","email":"augustyns.dries@gmail.com"},"//scripts":"No `dev` or `start` script on purpose: turbo runs those across every workspace, and an stdio MCP server is spawned by the MCP client rather than run as a long-lived dev service. Adding them would make `yarn dev` try to start this and fail without an API key. Use `yarn workspace @plunk/mcp inspect` to drive it locally.","repository":{"url":"git+https://github.com/useplunk/next.git","type":"git","directory":"apps/mcp"},"_npmVersion":"10.9.4","description":"Official Model Context Protocol server for Plunk — send email, manage contacts, campaigns and segments from an AI agent","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.0","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","typescript":"^5.7.2","@types/node":"^24.10.0","@plunk/typescript-config":"*","@modelcontextprotocol/client":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_0.13.0_1786904804476_0.611585214143475","host":"s3://npm-registry-packages-npm-production"}},"0.14.0":{"name":"@plunk/mcp","version":"0.14.0","keywords":["plunk","mcp","modelcontextprotocol","email","transactional-email"],"license":"AGPL-3.0-only","_id":"@plunk/mcp@0.14.0","maintainers":[{"name":"driaug","email":"augustyns.dries@gmail.com"}],"homepage":"https://docs.useplunk.com","bugs":{"url":"https://github.com/useplunk/plunk/issues"},"bin":{"plunk-mcp":"dist/index.js"},"dist":{"shasum":"5a7319655e28bb7403644e6d9ed792c856779c37","tarball":"https://registry.npmjs.org/@plunk/mcp/-/mcp-0.14.0.tgz","fileCount":41,"integrity":"sha512-MeVJ8S88SY2s0Vqi9VkBmms92ENOcQy9ScsEYtufhprI4p5DDryf79BqN7VdoTn1Et5qlN9qvzqtWnRptHlekA==","signatures":[{"sig":"MEUCID6dYRU+g3zVZN0/uJ9l/+EivPf9id/Xw0FTImmNpODKAiEA3TxQ3jAybzqyVhD+0vupsUqDcpZ+WFaf4WQHoFweR88=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@plunk%2fmcp@0.14.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":94340},"main":"./dist/server.js","type":"module","types":"./dist/server.d.ts","engines":{"node":">=20"},"exports":{".":"./dist/server.js","./stdio":"./dist/index.js"},"gitHead":"7e03224898e02667f8040719e38b1b89b453d48d","mcpName":"com.useplunk/plunk","scripts":{"lint":"eslint .","build":"tsc","clean":"rimraf node_modules dist .turbo","inspect":"npx @modelcontextprotocol/inspector node dist/index.js"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8f73f429-7017-468c-ad47-bf66665de560"}},"//scripts":"No `dev` or `start` script on purpose: turbo runs those across every workspace, and an stdio MCP server is spawned by the MCP client rather than run as a long-lived dev service. Adding them would make `yarn dev` try to start this and fail without an API key. Use `yarn workspace @plunk/mcp inspect` to drive it locally.","repository":{"url":"git+https://github.com/useplunk/plunk.git","type":"git","directory":"apps/mcp"},"_npmVersion":"12.0.2","description":"Official Model Context Protocol server for Plunk — send email, manage contacts, campaigns and segments from an AI agent","directories":{},"_nodeVersion":"22.23.2","dependencies":{"zod":"^4.2.0","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","typescript":"^5.7.2","@types/node":"^24.10.0","@plunk/typescript-config":"*","@modelcontextprotocol/client":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_0.14.0_1786987239064_0.6159469912080995","host":"s3://npm-registry-packages-npm-production"}},"0.16.0":{"name":"@plunk/mcp","version":"0.16.0","keywords":["plunk","mcp","modelcontextprotocol","email","transactional-email"],"license":"AGPL-3.0-only","_id":"@plunk/mcp@0.16.0","maintainers":[{"name":"driaug","email":"augustyns.dries@gmail.com"}],"homepage":"https://docs.useplunk.com","bugs":{"url":"https://github.com/useplunk/plunk/issues"},"bin":{"plunk-mcp":"dist/index.js"},"dist":{"shasum":"00f30c64b55a5983d1cab9216fddb7839115a18b","tarball":"https://registry.npmjs.org/@plunk/mcp/-/mcp-0.16.0.tgz","fileCount":47,"integrity":"sha512-xGmND5hzNdLkiQrVlvDHg+J8BK4G4ApBWwkpmy/8Zd24jsWXN8brPTUiKDTTvLp7Ft8VrOQHSRUoLbapnfPNJA==","signatures":[{"sig":"MEUCIQDP7TdSWFDmVf54IhIT/SVkAaMvH9qvVyCmzDQ68fx9DAIgcwjexOKCnMiOSAWV7dpunWBNTelhcDDq7GDMOE1may0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQDgJLdSp4Iwg3kNYd1CD2XnwRTxhfhrpVadm/wpHIJaOgIhAKXPKXmVYWL4786+YxBaBD77Z8e13ZQI8PhYy45cq9z8","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@plunk%2fmcp@0.16.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":197522},"main":"./dist/server.js","type":"module","types":"./dist/server.d.ts","engines":{"node":">=20"},"exports":{".":"./dist/server.js","./stdio":"./dist/index.js"},"gitHead":"71969bd0e59fbfae20cc657aee0598f01adfc2c6","mcpName":"com.useplunk/plunk","scripts":{"lint":"eslint .","build":"tsc","clean":"rimraf node_modules dist .turbo","inspect":"npx @modelcontextprotocol/inspector node dist/index.js"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"8f73f429-7017-468c-ad47-bf66665de560"}},"//scripts":"No `dev` or `start` script on purpose: turbo runs those across every workspace, and an stdio MCP server is spawned by the MCP client rather than run as a long-lived dev service. Adding them would make `yarn dev` try to start this and fail without an API key. Use `yarn workspace @plunk/mcp inspect` to drive it locally.","repository":{"url":"git+https://github.com/useplunk/plunk.git","type":"git","directory":"apps/mcp"},"_npmVersion":"12.2.0","description":"Official Model Context Protocol server for Plunk — send email, manage contacts, campaigns and segments from an AI agent","directories":{},"_nodeVersion":"22.23.3","dependencies":{"zod":"^4.2.0","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","typescript":"^5.7.2","@types/node":"^24.10.0","@plunk/typescript-config":"*","@modelcontextprotocol/client":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_0.16.0_1791468923800_0.8292588599229449","host":"s3://npm-registry-packages-npm-production"}},"0.17.0":{"name":"@plunk/mcp","version":"0.17.0","keywords":["plunk","mcp","modelcontextprotocol","email","transactional-email"],"license":"AGPL-3.0-only","_id":"@plunk/mcp@0.17.0","maintainers":[{"name":"driaug","email":"augustyns.dries@gmail.com"}],"homepage":"https://docs.useplunk.com","bugs":{"url":"https://github.com/useplunk/plunk/issues"},"bin":{"plunk-mcp":"dist/index.js"},"dist":{"shasum":"e7ea44973b20850ba257269aac8df7278f8a174c","tarball":"https://registry.npmjs.org/@plunk/mcp/-/mcp-0.17.0.tgz","fileCount":50,"integrity":"sha512-pi2mv4L9j4yNkCIMMnVlUWnOuTnThYvKKcv/OcR7QI0CkjQYFMKOFdHRfGKVcN4NhiS7wN5Br2uWCSahui2hDg==","signatures":[{"sig":"MEUCIQDNUlMC9zeeHOb1cXeSh79gVFWzZzvIIA5DxVeaXEiFAQIgS3zFY4sVBI2GHaEHftT9n5a3ekCtTMMPYyLZDDUhy3o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQC4gaMfdCDlO9J8vk/4bcsiYkswQpNH5KVODo6+uMOT0wIhAI1mw8tkl2WTpip13TZoRlOVLUrC06gwUJdnUPLxYAdS","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@plunk%2fmcp@0.17.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":211473},"main":"./dist/server.js","type":"module","types":"./dist/server.d.ts","engines":{"node":">=20"},"exports":{".":"./dist/server.js","./stdio":"./dist/index.js"},"gitHead":"dab2de5c080a0f02344ee78f18a03117bf404ad5","mcpName":"com.useplunk/plunk","scripts":{"lint":"eslint .","build":"tsc","clean":"rimraf node_modules dist .turbo","inspect":"npx @modelcontextprotocol/inspector node dist/index.js"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"8f73f429-7017-468c-ad47-bf66665de560"}},"//scripts":"No `dev` or `start` script on purpose: turbo runs those across every workspace, and an stdio MCP server is spawned by the MCP client rather than run as a long-lived dev service. Adding them would make `yarn dev` try to start this and fail without an API key. Use `yarn workspace @plunk/mcp inspect` to drive it locally.","repository":{"url":"git+https://github.com/useplunk/plunk.git","type":"git","directory":"apps/mcp"},"_npmVersion":"12.2.0","description":"Official Model Context Protocol server for Plunk — send email, manage contacts, campaigns and segments from an AI agent","directories":{},"_nodeVersion":"22.23.3","dependencies":{"zod":"^4.2.0","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","typescript":"^5.7.2","@types/node":"^24.10.0","@plunk/typescript-config":"*","@modelcontextprotocol/client":"^2.2.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp_0.17.0_1792605216984_0.3930646848821915","host":"s3://npm-registry-packages-npm-production"}},"0.18.0":{"_id":"@plunk/mcp@0.18.0","bin":{"plunk-mcp":"dist/index.js"},"bugs":{"url":"https://github.com/useplunk/plunk/issues"},"dist":{"shasum":"0213c87ef4654d0fa70b3adb98e6f5b76ee03643","tarball":"https://registry.npmjs.org/@plunk/mcp/-/mcp-0.18.0.tgz","fileCount":50,"integrity":"sha512-Vfy35O3ZzrMfIO1YvUCp1ZIj8KEDi2IrXgersi/L8lUy7qVp/ZaL1vWOVYerM4BJGBR8/uiuyjBOKO+bYGNa1g==","signatures":[{"sig":"MEUCIE3q9VCieNWidtHltY2/SZhQfNIRpdGdjkuDJLgLpUX0AiEAw9hcFFTQfJvbAurc0nUqhaZ0FaTAY8Zfa/1c9jfxtsc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCSGUxYyz5HpA64xVbpKX+0gltx86ri+1+/kpztFSudRQIgXG/XDCRhUqVBvkNPm4ryDeBaEpqKZAeyg99IaPFx3us="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@plunk%2fmcp@0.18.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":219094},"main":"./dist/server.js","name":"@plunk/mcp","type":"module","types":"./dist/server.d.ts","engines":{"node":">=20"},"exports":{".":"./dist/server.js","./stdio":"./dist/index.js"},"gitHead":"7d60087d5f21b324f93314b881e4e476986159bf","license":"AGPL-3.0-only","mcpName":"com.useplunk/plunk","scripts":{"lint":"eslint .","build":"tsc","clean":"rimraf node_modules dist .turbo","inspect":"npx @modelcontextprotocol/inspector node dist/index.js"},"version":"0.18.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"8f73f429-7017-468c-ad47-bf66665de560"}},"homepage":"https://docs.useplunk.com","keywords":["plunk","mcp","modelcontextprotocol","email","transactional-email"],"//scripts":"No `dev` or `start` script on purpose: turbo runs those across every workspace, and an stdio MCP server is spawned by the MCP client rather than run as a long-lived dev service. Adding them would make `yarn dev` try to start this and fail without an API key. Use `yarn workspace @plunk/mcp inspect` to drive it locally.","repository":{"url":"git+https://github.com/useplunk/plunk.git","type":"git","directory":"apps/mcp"},"_npmVersion":"12.2.1","description":"Official Model Context Protocol server for Plunk — send email, manage contacts, campaigns and segments from an AI agent","directories":{},"maintainers":[{"name":"driaug","email":"augustyns.dries@gmail.com"}],"_nodeVersion":"22.23.4","dependencies":{"zod":"^4.2.0","@modelcontextprotocol/server":"^2.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","typescript":"^5.7.2","@types/node":"^24.10.0","@plunk/typescript-config":"*","@modelcontextprotocol/client":"^2.3.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp_0.18.0_1793522192136_0.7526350552642452"}}},"time":{"created":"2026-08-16T18:26:44.322Z","modified":"2026-11-01T08:36:32.532Z","0.13.0":"2026-08-16T18:26:44.626Z","0.14.0":"2026-08-17T17:20:39.219Z","0.16.0":"2026-10-08T14:15:23.886Z","0.17.0":"2026-10-21T17:53:37.081Z","0.18.0":"2026-11-01T08:36:32.205Z"},"bugs":{"url":"https://github.com/useplunk/plunk/issues"},"license":"AGPL-3.0-only","homepage":"https://docs.useplunk.com","keywords":["plunk","mcp","modelcontextprotocol","email","transactional-email"],"repository":{"url":"git+https://github.com/useplunk/plunk.git","type":"git","directory":"apps/mcp"},"description":"Official Model Context Protocol server for Plunk — send email, manage contacts, campaigns and segments from an AI agent","maintainers":[{"name":"driaug","email":"augustyns.dries@gmail.com"}],"readme":"# @plunk/mcp\n\nThe official [Model Context Protocol](https://modelcontextprotocol.io) server for\n[Plunk](https://www.useplunk.com). It lets an AI agent send transactional email, manage contacts and\nsegments, draft and send campaigns, and build automation workflows in your Plunk project.\n\nWorks with the hosted product and with self-hosted instances.\n\n## Setup\n\nYou need a **secret key** (`sk_…`) from your project's **Settings → API Keys** in the Plunk dashboard.\n\n### Claude Code\n\n```bash\nclaude mcp add plunk --env PLUNK_API_KEY=sk_your_key -- npx -y @plunk/mcp\n```\n\n### Claude Desktop, Cursor, and other clients\n\n```json\n{\n  \"mcpServers\": {\n    \"plunk\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@plunk/mcp\"],\n      \"env\": {\n        \"PLUNK_API_KEY\": \"sk_your_key\"\n      }\n    }\n  }\n}\n```\n\n### Self-hosted Plunk\n\nPoint `PLUNK_API_URL` at your own API domain:\n\n```json\n{\n  \"mcpServers\": {\n    \"plunk\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@plunk/mcp\"],\n      \"env\": {\n        \"PLUNK_API_KEY\": \"sk_your_key\",\n        \"PLUNK_API_URL\": \"https://api.your-domain.com\"\n      }\n    }\n  }\n}\n```\n\n## Configuration\n\n| Variable                        | Required | Description                                                                                                |\n| ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------- |\n| `PLUNK_API_KEY`                 | yes      | Your project's secret key (`sk_…`).                                                                          |\n| `PLUNK_API_URL`                 | no       | API base URL. Defaults to the hosted API. Set this when self-hosting.                                        |\n| `PLUNK_PUBLIC_KEY`              | no       | Public key (`pk_…`). Lets `plunk_track_event` use the tracking endpoint directly — see below.                 |\n| `PLUNK_READ_ONLY`               | no       | `true` registers only read-only tools. Nothing can be created, changed, sent, or deleted.                     |\n| `PLUNK_ALLOW_UNCONFIRMED_SENDS` | no       | `true` skips the confirmation prompt before sends. For headless automation only.                             |\n| `PLUNK_MCP_API_KEY`             | no       | Takes precedence over `PLUNK_API_KEY`. Use it if `PLUNK_API_KEY` is already taken in your environment.        |\n| `PLUNK_MCP_API_URL`             | no       | Takes precedence over `PLUNK_API_URL`, for the same reason.                                                  |\n| `PLUNK_API_KEY_<NAME>`          | no       | A named project's secret key. Set one per project to use several at once — see below.                        |\n| `PLUNK_PUBLIC_KEY_<NAME>`       | no       | That project's public key.                                                                                   |\n\nFlags `--read-only` and `--api-url=<url>` do the same as their environment variables and win over them.\n\n> **Note on `PLUNK_API_KEY`:** if you self-host Plunk, the Plunk API server also uses a variable called\n> `PLUNK_API_KEY` for its own platform notification emails. That key belongs to a *different* project.\n> If both are present in the same environment, set `PLUNK_MCP_API_KEY` so the agent talks to the project\n> you mean.\n\n### Several projects\n\nTo give one agent access to more than one project, set a key per project with the project's name as a\nsuffix instead of `PLUNK_API_KEY`:\n\n```bash\nclaude mcp add plunk \\\n  --env PLUNK_API_KEY_ACME=sk_acme_key \\\n  --env PLUNK_API_KEY_SIDE_PROJECT=sk_other_key \\\n  -- npx -y @plunk/mcp\n```\n\nEvery tool then takes a required `project` argument (`acme` or `side_project` here), so each call says\nwhich project it acts on — there is no \"current project\" the agent could lose track of. Confirmation\nprompts name the project too.\n\n- `PLUNK_PUBLIC_KEY_<NAME>` sets that project's public key, just like `PLUNK_PUBLIC_KEY` does for a single one.\n- Names may use letters, digits and underscores, and are lowercased.\n- Once a named key is set, a plain `PLUNK_API_KEY` is ignored, so a key inherited from a self-hosted Plunk\n  server's environment cannot slip in as an extra project. `PLUNK_MCP_API_KEY` and `PLUNK_PUBLIC_KEY` cannot be\n  combined with named keys.\n- All projects share one API URL and one read-only setting.\n\n## Tools\n\nRead-only tools (the only ones registered when `PLUNK_READ_ONLY=true`):\n\n| Tool                             | Description                                             |\n| -------------------------------- | ------------------------------------------------------- |\n| `plunk_list_contacts`            | Browse or search contacts, cursor-paginated             |\n| `plunk_get_contact`              | Fetch one contact by ID **or email address**            |\n| `plunk_get_email`                | Look up one email's status and when it sends            |\n| `plunk_verify_email`             | Check whether an address is deliverable                 |\n| `plunk_list_templates`           | List reusable email templates                           |\n| `plunk_list_campaigns`           | List campaigns and their status                         |\n| `plunk_get_campaign`             | Fetch one campaign in full, including its audience size |\n| `plunk_get_campaign_stats`       | Opens, clicks, bounces and rates for one campaign       |\n| `plunk_list_segments`            | List audience segments                                  |\n| `plunk_list_domains`             | List sender domains and whether each is verified        |\n| `plunk_check_domain`             | Re-check a domain's DNS verification status             |\n| `plunk_list_workflows`           | List automation workflows and whether each is enabled   |\n| `plunk_get_workflow`             | Fetch one workflow with its steps and transitions       |\n| `plunk_check_workflow`           | Outline a workflow and what would fail at run time      |\n| `plunk_list_workflow_fields`     | Fields a workflow condition can test                    |\n| `plunk_list_workflow_executions` | List the runs of one workflow and their status          |\n| `plunk_get_workflow_execution`   | One run step by step, with outputs and errors           |\n\nWriting tools:\n\n| Tool                               | Description                                               |\n| ---------------------------------- | --------------------------------------------------------- |\n| `plunk_create_contact`             | Create or update a contact (upsert)                       |\n| `plunk_update_contact`             | Change a contact's email or custom data                   |\n| `plunk_subscribe_contact`          | Re-subscribe an existing contact, by email or ID          |\n| `plunk_unsubscribe_contact`        | Opt a contact out of marketing email, by email or ID      |\n| `plunk_delete_contact`             | Permanently delete a contact *(destructive)*              |\n| `plunk_send_email`                 | Send a transactional email, now, scheduled or throttled   |\n| `plunk_reschedule_email`           | Move a scheduled transactional email to another time      |\n| `plunk_cancel_email`               | Cancel a scheduled transactional email *(destructive)*    |\n| `plunk_track_event`                | Record an event, which can trigger automation workflows   |\n| `plunk_create_template`            | Create a reusable template                                |\n| `plunk_create_campaign`            | Create a campaign as a draft                              |\n| `plunk_test_campaign`              | Send one copy of a campaign to a project member           |\n| `plunk_send_campaign`              | Send or schedule a campaign *(destructive, irreversible)* |\n| `plunk_cancel_campaign`            | Stop a scheduled or in-flight campaign *(destructive)*    |\n| `plunk_create_segment`             | Create an audience segment                                |\n| `plunk_create_workflow`            | Create a workflow, disabled, triggered by an event        |\n| `plunk_update_workflow`            | Rename, change the trigger event, re-entry or throttle    |\n| `plunk_duplicate_workflow`         | Copy a workflow, disabled, e.g. for another language      |\n| `plunk_delete_workflow`            | Delete a disabled workflow *(destructive)*                |\n| `plunk_add_workflow_step`          | Add any step type, appended, on a branch, or in between   |\n| `plunk_add_workflow_steps`         | Build a whole sequence, branches included, in one call    |\n| `plunk_update_workflow_step`       | Change a step's settings or template                      |\n| `plunk_delete_workflow_step`       | Remove a step, reconnecting around it *(destructive)*     |\n| `plunk_connect_workflow_steps`     | Connect two steps, or a condition branch to a step        |\n| `plunk_disconnect_workflow_steps`  | Remove a connection *(destructive)*                       |\n| `plunk_set_workflow_enabled`       | Enable or disable a workflow                              |\n| `plunk_start_workflow_execution`   | Run a workflow for one contact now                        |\n| `plunk_cancel_workflow_executions` | Cancel one run or all runs in progress *(destructive)*    |\n\n### Building workflows\n\nThe workflow tools cover every step type the dashboard builder offers — `SEND_EMAIL`, `DELAY`,\n`WAIT_FOR_EVENT`, `CONDITION` (yes/no or multi-branch), `UPDATE_CONTACT`, `WEBHOOK` and `EXIT` — so an\nagent can build and edit a branching workflow without opening the builder.\n\n- Steps are placed with `after` (a step ID) and, after a condition, `branch`. Without `after` a step goes\n  after the one step that leads nowhere yet (a condition's unconnected branches do not count); when\n  there are several, the tool refuses and lists the open positions instead of guessing. A position\n  that already leads somewhere gets the new step inserted in between.\n- `plunk_delete_workflow_step` reconnects the flow around the step by default. Removing a condition\n  requires choosing `downstream`: `detach` keeps the steps below it, `delete` removes them too.\n- The API stores step settings without validating them, and only the executor parses them, so a bad\n  setting would surface as a failed run for a real contact. The tools validate settings against the\n  same rules first, including condition fields the executor cannot resolve.\n- `plunk_check_workflow` prints the flow as numbered lines in the order a contact walks it and lists\n  problems; `plunk_set_workflow_enabled` refuses to enable a workflow while any are errors.\n\n### Addressing contacts by email\n\n`plunk_get_contact`, `plunk_subscribe_contact` and `plunk_unsubscribe_contact` all take either `id` or\n`email`. Passing `email` resolves it to the contact for you, matching exactly and case-insensitively,\nso an agent handed \"unsubscribe ada@example.com\" does it in one call instead of searching first and\nguessing which row to patch.\n\n### Managing domains\n\nAdding and removing sender domains is deliberately **not** exposed. Those endpoints skip the\nadmin-role check when called with an API key, so a tool would hand an agent authority that a non-admin\nmember of the same project does not have. Use the Plunk dashboard for those; the tools here only read.\n\n## Safety\n\nPlunk's secret key is all-or-nothing over its project, so this server adds its own guardrails:\n\n- **Sends require human confirmation.** `plunk_send_campaign`, and `plunk_send_email` with more than\n  one recipient, ask you to confirm before anything goes out, and the prompt tells you how many people\n  will receive it. Enabling a workflow that has email steps with `plunk_set_workflow_enabled` asks too,\n  and names the trigger event. So do changing the trigger of an enabled workflow that sends email and\n  starting one for a contact with `plunk_start_workflow_execution`. Confirmation is not a tool argument, so the model cannot grant it to itself.\n  Clients that cannot show a prompt cannot send — set `PLUNK_ALLOW_UNCONFIRMED_SENDS=true` if you are\n  running headless and accept that.\n- **Read-only mode is enforced by registration**, not by convention. With `PLUNK_READ_ONLY=true` the\n  mutating tools are never registered, so they cannot be called even by name.\n- **Billing, project deletion, and key rotation are unreachable.** Those endpoints require a dashboard\n  session, not an API key, so no tool here can touch them.\n\nAn API key still grants full read and write access to its project's data. Use a separate Plunk project\nfor anything you would not want an agent to change.\n\n## Event tracking and the two key types\n\nPlunk's `/v1/track` endpoint requires the **public** key, and it is the only endpoint that does.\nWithout `PLUNK_PUBLIC_KEY`, `plunk_track_event` gets the same result with the secret key by upserting\nthe contact first and then recording the event against it. Both paths create the contact if it does\nnot exist. Setting `PLUNK_PUBLIC_KEY` just makes it one request instead of two.\n\n## Development\n\n```bash\nyarn workspace @plunk/mcp build\nyarn vitest run apps/mcp\n\n# Explore the server interactively\nPLUNK_API_KEY=sk_… npx @modelcontextprotocol/inspector node apps/mcp/dist/index.js\n```\n\n## License\n\nAGPL-3.0-only, same as the rest of Plunk.\n","readmeFilename":"README.md"}