An MCP server that exposes Plausible Analytics to Claude Desktop, Claude.ai remote connectors, and any other MCP-compatible client. Ships two transports out of the box:
- stdio —
dist/index.js, spawned locally by Claude Desktop. - Streamable HTTP —
dist/remote.js, deployable to Railway / Fly / any Docker host so it can be added via Claude.ai's "Add custom connector" dialog.
Small surface, tiny dependency tree — two runtime deps (@modelcontextprotocol/sdk
and zod), native fetch, nothing else. Every line of code is meant to be
auditable in an afternoon.
Nine tools that cover the common Plausible workflows:
| Tool | What it does |
|---|---|
plausible_list_sites |
List sites the API key can see. Discovery tool. |
plausible_aggregate |
Top-line totals (visitors, pageviews, bounce rate, visit duration). |
plausible_timeseries |
Metrics bucketed over time (hour/day/week/month). |
plausible_breakdown |
Top-N by any dimension (pages, sources, countries, UTM, custom props). |
plausible_realtime |
Current visitors / visits / pageviews (default 5-minute window). |
plausible_compare |
Period vs prior period with absolute + percent deltas. |
plausible_list_goals |
List conversion goals configured for a site. |
plausible_list_custom_properties |
List custom properties configured for a site. |
plausible_query_raw |
Escape hatch — send a raw Stats API v2 body. |
-
Node.js 20 or later.
-
A Plausible API key. Create one at Plausible → Settings → API Keys (or on a self-hosted instance: user menu → API Keys).
-
The six Stats API tools (aggregate, timeseries, breakdown, realtime, compare, query_raw) work on every plan, including the regular hosted plausible.io plans.
-
The three Sites API tools (
plausible_list_sites,plausible_list_goals,plausible_list_custom_properties) need:- an enterprise plan on hosted plausible.io, or
- a Plausible Enterprise Edition self-hosted instance.
Plausible Community Edition (the default self-hosted Docker image) does not ship the Sites API at all — those endpoints return 404. In both unsupported cases, set
PLAUSIBLE_DISABLE_SITES_API=1so the tools don't appear and Claude doesn't try them. See the recommended config below.
git clone https://github.com/ickas/plausible-mcp.git
cd plausible-mcp
nvm use # optional, pins Node 20
npm install
npm run buildThe build writes an executable file to dist/index.js. That's the path
you'll give Claude Desktop.
Edit ~/Library/Application Support/Claude/claude_desktop_config.json on
macOS (or the equivalent on Windows/Linux) and add an entry under
mcpServers:
{
"mcpServers": {
"plausible": {
"command": "node",
"args": ["/absolute/path/to/plausible-mcp/dist/index.js"],
"env": {
"PLAUSIBLE_API_KEY": "your-key-here"
}
}
}
}Replace /absolute/path/... with the real path (you can get it with
realpath dist/index.js inside the repo). Restart Claude Desktop.
Set PLAUSIBLE_BASE_URL alongside the API key:
"env": {
"PLAUSIBLE_API_KEY": "your-key-here",
"PLAUSIBLE_BASE_URL": "https://analytics.example.com"
}Once the server is connected, ask Claude things like:
- "List my Plausible sites."
- "How many visitors did example.com get in the last 7 days?"
- "Give me a day-by-day visitor chart for example.com this month."
- "What are the top 10 pages on example.com in the last 30 days?"
- "Which traffic sources drove the most visits last week?"
- "How did last week compare to the week before on example.com?"
- "How many people are on example.com right now?"
- "What conversion goals are configured on example.com?"
Each example shows the tool name and the exact input Claude will send.
plausible_aggregate — top-line totals
{
"site_id": "example.com",
"date_range": "7d",
"metrics": ["visitors", "pageviews", "bounce_rate", "visit_duration"]
}plausible_timeseries — daily visitors this month
{
"site_id": "example.com",
"date_range": "month",
"metrics": ["visitors"],
"interval": "day"
}plausible_breakdown — top pages from organic search
{
"site_id": "example.com",
"date_range": "30d",
"metrics": ["visitors", "pageviews"],
"dimension": "event:page",
"limit": 10,
"filters": [["is", "visit:channel", ["Organic Search"]]]
}plausible_breakdown — traffic sources
{
"site_id": "example.com",
"date_range": "7d",
"metrics": ["visitors", "visits"],
"dimension": "visit:source"
}plausible_realtime — current visitors
{ "site_id": "example.com" }Optional window_minutes (default 5) widens the window — e.g. last hour:
{ "site_id": "example.com", "window_minutes": 60 }plausible_compare — this week vs last week
{
"site_id": "example.com",
"date_range": "7d",
"metrics": ["visitors", "pageviews", "bounce_rate"]
}Returns { current, previous, delta } where each delta entry has
absolute and percent.
plausible_query_raw — anything the typed tools don't cover
{
"body": {
"site_id": "example.com",
"date_range": "30d",
"metrics": ["conversion_rate"],
"dimensions": ["event:goal"],
"filters": [["is", "event:goal", ["Signup"]]]
}
}Full request body reference: https://plausible.io/docs/stats-api.
If you'd rather not run the binary locally, you can host it as a remote MCP server and add it via Claude.ai's "Add custom connector" dialog or Claude Desktop's remote-connector flow.
Two entrypoints are built from the same code:
dist/index.js— local stdio transport (Claude Desktop spawns it).dist/remote.js— Streamable HTTP transport, listens on$PORT, exposes the same nine tools atPOST /mcp(default path).
-
Push this repo to your GitHub account (a fork or your own copy works).
-
On https://railway.com, click New → Deploy from GitHub repo and pick the repo. Railway picks up the included
Dockerfileandrailway.tomlautomatically. -
In Variables, set:
Key Value PLAUSIBLE_API_KEYYour Plausible API key. PLAUSIBLE_BASE_URLSelf-hosted URL (e.g. https://analytics.example.com) — omit forhttps://plausible.io.PLAUSIBLE_SITE_ID(Optional) default site domain. PLAUSIBLE_DISABLE_SITES_API1on hosted plausible.io non-enterprise or self-hosted Community Edition (recommended).MCP_BEARER_TOKEN(Optional) shared secret for CLI clients. See Auth model below. -
In Settings → Networking, click Generate Domain to get a public HTTPS URL.
-
Health-check it:
curl https://<your-domain>/healthshould return JSON. -
In Claude.ai or Claude Desktop, open Settings → Connectors → Add custom connector and paste
https://<your-domain>/mcp. Leave the OAuth fields empty.
The remote endpoint is open by default. Two options to lock it down:
- Shared secret (recommended for personal use): set
MCP_BEARER_TOKEN=<random-string>. The server enforcesAuthorization: Bearer <token>on every/mcprequest. Works with custom clients that can set a header. Note that Claude.ai's connector dialog does not let you set arbitrary headers — it only supports OAuth — so use the bearer token for CLI / scripted clients, and rely on the URL being unguessable for the Claude.ai integration itself. - Plain unguessable URL: Railway-generated subdomains are random enough for personal use. Don't share the URL.
OAuth 2.1 (matching MCP's spec) is not implemented yet. PRs welcome.
| Key | Default | Notes |
|---|---|---|
PORT |
8080 |
Set by Railway automatically. |
MCP_PATH |
/mcp |
Move the endpoint if you want. |
MCP_DISABLE_CORS |
0 |
Set to 1 to drop the permissive CORS headers. |
docker build -t plausible-mcp .
docker run --rm -p 8080:8080 \
-e PLAUSIBLE_API_KEY=your-key \
-e PLAUSIBLE_BASE_URL=https://analytics.example.com \
-e PLAUSIBLE_SITE_ID=example.com \
-e PLAUSIBLE_DISABLE_SITES_API=1 \
plausible-mcpThen curl http://localhost:8080/health.
npm run dev # tsup watch mode
npm run typecheck # tsc --noEmit
npm run lint # biome check
npm run format # biome format --write
npm run build # bundle to dist/index.js (stdio) and dist/remote.js (HTTP)
npm start # run the stdio entrypoint
npm run start:remote # run the HTTP entrypoint on $PORT (default 8080)| Env var | Required | Default | Notes |
|---|---|---|---|
PLAUSIBLE_API_KEY |
yes | — | Plausible API bearer token. |
PLAUSIBLE_BASE_URL |
no | https://plausible.io |
Override for self-hosted instances. |
PLAUSIBLE_SITE_ID |
no | — | Default site domain. When set, every tool's site_id becomes optional and falls back to this value. |
PLAUSIBLE_DISABLE_SITES_API |
no | 0 |
Set to 1 (or true/yes/on) to hide plausible_list_sites, plausible_list_goals, plausible_list_custom_properties. |
The three Sites API tools (plausible_list_sites, plausible_list_goals,
plausible_list_custom_properties) are unavailable on most setups. Hide
them and pin the site so Claude doesn't waste a tool call probing for a
404 / 406 response.
Hosted plausible.io, regular Growth/Business plan:
"env": {
"PLAUSIBLE_API_KEY": "your-key-here",
"PLAUSIBLE_SITE_ID": "example.com",
"PLAUSIBLE_DISABLE_SITES_API": "1"
}Self-hosted Plausible Community Edition (the standard Docker image):
"env": {
"PLAUSIBLE_API_KEY": "your-self-hosted-key",
"PLAUSIBLE_BASE_URL": "https://analytics.example.com",
"PLAUSIBLE_SITE_ID": "example.com",
"PLAUSIBLE_DISABLE_SITES_API": "1"
}Hosted plausible.io enterprise / self-hosted Plausible Enterprise Edition:
You can drop PLAUSIBLE_DISABLE_SITES_API and let all nine tools show up.
The remaining six tools (aggregate, timeseries, breakdown, realtime, compare, query_raw) use the Stats API and work everywhere — on every hosted plan and every self-hosted edition.
- Only talks to the Plausible endpoint you configure. No telemetry.
- The API key is read from
process.envand passed in theAuthorizationheader. It is never logged, never written to disk, and never sent anywhere except Plausible. - See SECURITY.md for disclosure details.
PRs welcome! See CONTRIBUTING.md for the dev loop and expectations.
MIT — see LICENSE.