Streamable-HTTP MCP server for the etracker Analytics Report API. Read-only, 4 tools, runs in a container.
The REST Report API is available in the etracker analytics Enterprise Edition. Create an access token with scope Reporting API under Settings → Account → Integration.
- Run in Docker
- Authentication
- Connect from your client
- Tools
- Example prompts
- Configuration
- Operations
- Development
Published image: ghcr.io/mittwald/etracker-mcp:latest (tags: latest, 0.1.0).
docker run -d --name etracker-mcp -p 3334:3334 \
--restart unless-stopped \
ghcr.io/mittwald/etracker-mcp:latestThe server is credential-free at startup — every client provides its own etracker access token via a header (see Authentication).
Endpoint: http://127.0.0.1:3334/mcp · Healthcheck: GET /health.
Build locally instead:
docker build -t etracker-mcp .and useetracker-mcpas the image name.
services:
etracker-mcp:
image: ghcr.io/mittwald/etracker-mcp:latest
ports: ["3334:3334"]
restart: unless-stoppedEach client provides its own etracker access token via a request header:
X-ET-Token— access token with scope Reporting API.
The server keeps no global credentials, holds nothing in env, and stores
nothing on disk. The token lives only in the per-session EtrackerClient
inside the running process. One server can serve many etracker accounts.
Requests without the header are rejected with 401.
The upstream API base URL defaults to the public endpoint
https://ws.etracker.com/api/v7 and can be overridden server-side with
ETRACKER_API_URL.
All client examples below include the required X-ET-Token header. Replace
the token with your own.
claude mcp add etracker http://127.0.0.1:3334/mcp --transport http \
--header "X-ET-Token: your-access-token"Recommended: one-click install via the .mcpb extension. No config
files, no Docker. Download the latest etracker-mcp-<version>.mcpb from the
Releases page and
double-click it (or in Claude Desktop: Settings → Extensions → Advanced
settings → Install Extension…). You'll be prompted for two values in a form:
- Server URL — pre-filled with the hosted instance; leave as is unless you run your own.
- etracker Access Token — your token with scope Reporting API (stored in the OS keychain, marked sensitive).
Works on macOS, Windows and Linux with no extra setup — the extension bundles
a small stdio↔HTTP bridge that runs in-process on Claude Desktop's own Node
runtime (no system Node, no npx, no local listening socket; the only network
I/O is the outbound HTTPS call to the server). To build the bundle yourself:
pnpm pack:mcpb → dist-mcpb/etracker-mcp-<version>.mcpb.
Manual alternative: edit the config with mcp-remote
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-ET-Tokenheader, 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": { "etracker": { "command": "npx", "args": [ "-y", "mcp-remote", "http://127.0.0.1:3334/mcp", "--header", "X-ET-Token: your-access-token" ] } } } -
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.etrackershould appear with status connected and 4 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": {
"etracker": {
"url": "http://127.0.0.1:3334/mcp",
"headers": {
"X-ET-Token": "your-access-token"
}
}
}
}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://etracker-mcp:3334/mcpon the same network). - Server Transport:
HTTP Streamable. - Headers: add
X-ET-Token.
Connect the node to the tools input of the AI Agent. n8n introspects
tools/list automatically, so the etracker tools become available to the
agent without further config.
.vscode/mcp.json:
{
"servers": {
"etracker": {
"type": "http",
"url": "http://127.0.0.1:3334/mcp",
"headers": {
"X-ET-Token": "your-access-token"
}
}
}
}npx @modelcontextprotocol/inspector
# Streamable HTTP → http://127.0.0.1:3334/mcp
# Add the X-ET-Token header in the Inspector "Authentication" panel.The etracker Report API is report-centric: pick a report (e.g. EAPage,
EAGeo, EADeviceType), then query its rows. Each report exposes
attributes (dimensions) and keyfigures (metrics).
| Tool | Purpose |
|---|---|
list_reports |
Map of report ID → display name. Call first. |
get_report_info |
Metadata for a report: create date, segments, attributes. |
get_report_metadata |
Column metadata: ids, labels, types, sortable/filterable flags. |
get_report_data |
Report rows with date range, paging, sorting, column selection and filters. |
compare_report_data |
Two periods compared per row: current/previous/delta/pctChange per figure, sorted by largest change. |
A typical flow: list_reports → get_report_metadata (to learn valid
attribute/figure IDs) → get_report_data (or compare_report_data for
period-over-period analysis). compare_report_data defaults the comparison
period to the equally long span immediately before the current range.
Marketing-oriented questions for the connected assistant:
- "Which reports are available in my etracker account?"
- "Show me the top 10 pages by unique visits in May 2024 (
EAPage)." - "Break down visits by device type for the last 7 days."
- "Which countries drove the most traffic last month? Sort descending."
- "List pages whose name matches "Checkout" with more than 100 unique visits."
- "Compare page impressions for
/en/vs/de/URLs over the last 30 days."
Figures are returned verbatim. The server never sums, merges or invents values — keyfigures always match what etracker (and its web UI) reports. The only transformation is cosmetic: invisible/zero-width characters are stripped from attribute labels so duplicates are recognizable.
- Composite row IDs:
get_report_datarows carry anidthat can be a comma-joined composite of the report's dimensions (e.g.pageNameId,urlIdforEAPage).compare_report_datajoins rows by thisid(falling back to the requested attribute values when noidis present). - Invisible characters → phantom duplicate rows: etracker may list the same
entity twice under attribute values that differ only by an invisible
character — e.g. a
page_nameand a second copy prefixed with an invisible separator (U+2063), because the tracked page's JavaScript prepends one todocument.title, from which etracker auto-derives the page name. You then see two rows for one page (e.g. 627 + 7 page impressions), exactly as in etracker's own report. The server strips the invisible characters (U+200B–200F,U+2060–2063,U+FEFF, soft hyphen, bidi marks) from the labels only — it does not merge the rows, because the right total cannot be reconstructed from them: page impressions are additive, but visits and unique visitors are not (the same session/visitor is re-attributed to both rows, so summing double-counts). For a correct, de-duplicated per-page total, query byurl(attributes=url, droppage_name) so etracker aggregates and de-duplicates server-side. To fix it at the source, set a cleanet_pagenameon the tracked page so etracker stops deriving the name from the mutated title.
| Env | Required | Default | |
|---|---|---|---|
MCP_PORT |
no | 3334 |
TCP port. |
LOG_LEVEL |
no | info |
One of debug, info, warn, error. |
ETRACKER_API_URL |
no | https://ws.etracker.com/api/v7 |
Base URL of the etracker Report API. |
ETRACKER_REQUEST_TIMEOUT_MS |
no | 120000 |
Per-request timeout. Raise it if large ranges (e.g. a full year of daily rows) still time out. |
The etracker access token is not configured via env — it is passed by each
client via the X-ET-Token request header. 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 etracker-mcp. - Healthcheck:
GET /healthreturns{"ok": true, "version": "..."}. Wired into the DockerfileHEALTHCHECK. - Limits: request bodies > 1 MB →
413; outbound calls to etracker timeout after 30 s. The etracker API itself limits to 50 calls per 5 minutes, 10 parallel requests, and 100,000 rows per response. - 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). - Image: pin a digest in production
(
ghcr.io/mittwald/etracker-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 # unit tests
pnpm test:live # live smoke test (client → API), needs ETRACKER_TOKEN env
pnpm build # tsc → dist/
# Holistic end-to-end test: drives the running server over the real MCP
# transport against the live API, exercising every tool.
pnpm build && node dist/index.js & # start the server
ETRACKER_TOKEN=... pnpm test:e2e # MCP_URL overridable (default :3334)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.