Streamable-HTTP MCP server for Umami Analytics. Read-only, 8 tools, runs in a container.
- Run in Docker
- Authentication
- Connect from your client
- Tools
- Example prompts
- Configuration
- Operations
- Development
Published image: ghcr.io/mittwald/umami-mcp:latest (tags: latest, 0.1.0).
docker run -d --name umami-mcp -p 3334:3334 \
--restart unless-stopped \
ghcr.io/mittwald/umami-mcp:latestThe server is credential-free at startup — every client provides its own Umami credentials via headers (see Authentication).
Endpoint: http://127.0.0.1:3334/mcp · Healthcheck: GET /health.
Build locally instead:
docker build -t umami-mcp .and useumami-mcpas the image name.
services:
umami-mcp:
image: ghcr.io/mittwald/umami-mcp:latest
ports: ["3334:3334"]
restart: unless-stoppedEach client provides its own Umami credentials via request headers:
X-Umami-UrlX-Umami-UsernameX-Umami-Password
The server keeps no global credentials, holds nothing in env, and stores
nothing on disk. Credentials live only in the per-session UmamiClient
inside the running process. One server can serve many Umami instances.
Requests without all three headers are rejected with 401.
All client examples below include the required X-Umami-* headers. Replace
the URL/credentials with your own.
claude mcp add umami http://127.0.0.1:3334/mcp --transport http \
--header "X-Umami-Url: https://umami.example.com" \
--header "X-Umami-Username: youruser" \
--header "X-Umami-Password: yourpass"Claude Desktop's stable config only accepts stdio MCP servers. To use this HTTP-based server, bridge it via the
mcp-remoteshim (auto-installed bynpx). This also lets you pass the requiredX-Umami-*headers, which the Connectors UI doesn't support.
Step-by-step:
-
Quit Claude Desktop (
⌘Qon macOS · right-click tray → Quit on Win/Linux). -
Open the config file:
OS Path macOS ~/Library/Application Support/Claude/claude_desktop_config.jsonWindows %APPDATA%\Claude\claude_desktop_config.jsonLinux ~/.config/Claude/claude_desktop_config.json -
Add (or merge into)
mcpServers:{ "mcpServers": { "umami": { "command": "npx", "args": [ "-y", "mcp-remote", "http://127.0.0.1:3334/mcp", "--header", "X-Umami-Url: https://umami.example.com", "--header", "X-Umami-Username: youruser", "--header", "X-Umami-Password: yourpass" ] } } } -
Save the file and reopen Claude Desktop. The first launch downloads
mcp-remote(one-time, ~5 s). -
Open a new chat → type
/mcpand press Enter.umamishould appear with status connected and 8 tools listed.
If it shows failed: check that the MCP server is running
(curl http://127.0.0.1:3334/health → {"ok": true}) and that Node.js is
installed system-wide (npx must be on your PATH).
~/.cursor/mcp.json:
{
"mcpServers": {
"umami": {
"url": "http://127.0.0.1:3334/mcp",
"headers": {
"X-Umami-Url": "https://umami.example.com",
"X-Umami-Username": "youruser",
"X-Umami-Password": "yourpass"
}
}
}
}In an AI Agent workflow add the MCP Client Tool node:
- Endpoint:
http://127.0.0.1:3334/mcp(or the container hostname if n8n runs in Docker, e.g.http://umami-mcp:3334/mcpon the same network). - Server Transport:
HTTP Streamable. - Headers: add
X-Umami-Url,X-Umami-Username,X-Umami-Password.
Connect the node to the tools input of the AI Agent. n8n introspects
tools/list automatically, so the 8 Umami tools become available to the
agent without further config.
.vscode/mcp.json:
{
"servers": {
"umami": {
"type": "http",
"url": "http://127.0.0.1:3334/mcp",
"headers": {
"X-Umami-Url": "https://umami.example.com",
"X-Umami-Username": "youruser",
"X-Umami-Password": "yourpass"
}
}
}
}npx @modelcontextprotocol/inspector
# Streamable HTTP → http://127.0.0.1:3334/mcp
# Add the X-Umami-* headers in the Inspector "Authentication" panel.| Tool | Purpose |
|---|---|
list_websites |
All websites the user can access. |
get_stats |
Pageviews / visitors / bounces with prev-period delta. |
get_pageviews |
Time-series, grouped by minute / hour / day / month. |
get_metrics |
Top-N by path, referrer, browser, country, event, UTM, … |
get_active_visitors |
Live visitor count. |
get_realtime_activity |
Pageviews + sessions + events from the last few minutes. |
list_sessions |
Paginated session list in a date range. |
get_session_activity |
Pageview/event timeline for a single session. |
Marketing-oriented questions for the connected assistant:
- "Build me a Monday-morning report for <website>: visitors, pageviews, bounce rate vs last week. Highlight anything that moved more than ±15 %."
- "Compare
utmSource=newslettervsutmSource=linkedinover the last 30 days — which sends higher-quality traffic?" - "Top 10 blog posts this month with bounce rate. Which need a rewrite?"
- "Top 20 referring domains in the last 30 days. Which are new vs a week ago?"
- "How many
webinar_signupevents in the last 14 days, broken down by UTM?" - "Pick three sessions from yesterday that ended in a
signupevent and walk me through what those users did. What do they have in common?" - "How many people are on the site right now and which pages?"
- "Based on the last 90 days, what are my top 3 marketing priorities? Be specific — name pages, channels, geographies."
| Env | Required | Default | |
|---|---|---|---|
MCP_PORT |
no | 3334 |
TCP port. |
LOG_LEVEL |
no | info |
One of debug, info, warn, error. |
UMAMI_URL_ALLOWLIST |
no | unset | Comma-separated origin allowlist for X-Umami-Url. When set, requests with non-matching URLs are rejected with 403. Wildcards via * (one host segment). Example: https://*.example.com,https://umami.acme.io. |
Umami credentials are not configured via env — they are passed by each
client via X-Umami-* request headers. See Authentication.
- Logging: structured JSON to stdout/stderr (
info/debug→ stdout,warn/error→ stderr). One line per event, no header values logged. Tail withdocker logs -f umami-mcp. - Healthcheck:
GET /healthreturns{"ok": true, "version": "..."}. Wired into the DockerfileHEALTHCHECK. - Limits: request bodies > 1 MB →
413; outbound calls to Umami timeout after 15 s. - Security model: server holds no credentials. Run multiple replicas
behind a load balancer if needed — sessions are sticky via the
Mcp-Session-Idheader, so terminate sessions on the same backend (or accept that a reconnect re-initializes a session). - SSRF guard (public deployments): by default the server connects to
any URL provided in
X-Umami-Url. When exposing the server on a public network, setUMAMI_URL_ALLOWLISTto a comma-separated list of permitted origins (wildcards via*). Combine with a TLS-terminating reverse proxy and rate limiting before going public. - Image: pin a digest in production (
ghcr.io/mittwald/umami-mcp@sha256:…) rather than:latest. - Graceful shutdown:
SIGTERM/SIGINTclose all open MCP sessions and drain in-flight requests before exit (10 s hard cap).
pnpm install
pnpm dev # tsx watch
pnpm test # 13 unit tests
pnpm test:live # 8 live tests, needs UMAMI_* env
pnpm build # tsc → dist/All tools are read-only. Add a tool: extend src/tools.ts, add a unit test
in tests/tools.test.ts, add a live test in tests/live/live.test.ts that
asserts the actual response shape.