Thanks for your interest in contributing!
git clone https://github.com/getsentry/plausible-mcp.git
cd plausible-mcp
pnpm installpnpm test # All tests
pnpm test:watch # Watch mode
pnpm test:coverage # With coverage reportTests use Vitest with mocked fetch — no Plausible account needed to run them.
- Create
src/tools/your-tool.ts, starting from the closest existing tool. A tool that queries Plausible exportsregister(server, client, defaultSiteId?)— copyget-timeseries.ts. A tool that does not needs no client, so it exportsregister(server)— copysend-feedback.ts. - Declare an
outputSchemaand returnstructuredContentalongside the text block. Every tool does this; query-shaped results reusequeryResultOutputSchemaandbuildQueryStructuredContentfromsrc/schemas.ts. - Set
annotationsto describe what the tool really does. The query tools are read-only;send_feedbackwrites to Sentry, so it setsreadOnlyHint: false. - Register it in
src/server.ts - Add tests in
__tests__/tools/your-tool.test.ts - Add an eval case in
evals/cases.ts. Evals grade whether a model picks the right tool from a plain-language prompt, so this applies to tools a user would ask for in words —send_feedbackhas none.
Requires an OpenRouter API key:
OPENROUTER_API_KEY=sk-or-... pnpm evalThe model defaults to anthropic/claude-sonnet-5; override it with OPENROUTER_MODEL.
Put your Plausible key in .env.local (copy .env.example), then:
pnpm inspect # build + open the MCP Inspector UI, auto-connected to the server
pnpm inspect:cli # headless: build + print the tool list (handy for a quick check or CI)Both read mcp.json, which launches the server with
node --env-file-if-exists=.env.local — so your key loads from .env.local when it's
there, and falls back to whatever PLAUSIBLE_API_KEY is already in the environment when
it isn't (e.g. in CI). No need to paste it anywhere. inspect opens the browser UI (it
prints a pre-authed http://localhost:6274/?... URL); inspect:cli just prints
tools/list and exits.
- Make sure
pnpm testpasses - Make sure
pnpm buildcompiles cleanly - Keep PRs focused — one feature or fix per PR
- Your PR title becomes the changelog line for the next release, so write it for a reader (see Releasing below)
Releases are automated with craft. Don't bump the version in package.json or edit CHANGELOG.md by hand — both are generated.
- Merge your PR to
main. The changelog is auto-generated from merged PR titles since the last tag (.craft.yml→changelog.policy: auto). - A maintainer runs the Release workflow (Actions → Release → Run workflow) and selects the bump type (
patch/minor/major). - craft cuts a
release/X.Y.Zbranch, then publishes a git tag and GitHub release once CI is green.
Deploying the Cloudflare Worker (pnpm deploy) is separate from cutting a release.