UsherStats Docs Contents usherstats.com Start free

UsherStats for AI agents (MCP)

UsherStats is a remote Model Context Protocol server. Point any MCP client -- Claude Code, Claude Desktop, Cursor, or your own agent -- at

https://api.usherstats.com/mcp

with an API token, and the agent can do what the token can: list and create sites, add hostnames, read the install snippet and status, manage keys, settings, tokens and team members. Every tool is an API route, called as your token, so scopes, site restrictions and plan limits apply exactly as they do over HTTP. Analytics tools (stats, top pages and sources, crawlers, journeys, experiments, bot rules, search data) appear here as soon as those API routes ship, with no change on your side.

  • Endpoint: POST /mcp (Streamable HTTP, JSON-RPC 2.0, one JSON answer per request, no sessions).
  • Protocol versions: 2026-07-28 (stateless: no initialize; version in each request's _meta and the MCP-Protocol-Version header) and 2025-03-26, 2025-06-18, 2025-11-25 (opening with initialize).
  • Authentication: Authorization: Bearer us_live_... only. Sign-in cookies are not accepted on /mcp.
  • The HTTP API behind the tools is described at https://api.usherstats.com/v1/openapi.json (OpenAPI 3.1).

1. Make a token for the agent

Give the agent only what it needs. A token can be limited to some sites (siteIds) and given an expiry; it is shown once. For an agent that reports and helps with setup but changes nothing:

curl https://api.usherstats.com/v1/tokens \
  -H "Authorization: Bearer $US_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "claude (read only)", "scopes": ["sites:read", "analytics:read", "members:read"], "expiresAt": "2027-06-30T00:00:00Z"}'

For an agent that may also set sites up (create sites, add hostnames, change settings), add sites:write:

curl https://api.usherstats.com/v1/tokens \
  -H "Authorization: Bearer $US_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "claude (setup)", "scopes": ["sites:read", "sites:write", "analytics:read"], "expiresAt": "2027-06-30T00:00:00Z"}'

You can also make tokens in the dashboard under Settings > API tokens. Revoke one with DELETE /v1/tokens/{id}; it stops working at once.

2. Connect your client

Keep the token out of files you commit: each snippet below reads it from the environment variable US_TOKEN.

Claude Code

claude mcp add --transport http usherstats https://api.usherstats.com/mcp --header "Authorization: Bearer $US_TOKEN"

Or share it with a project in .mcp.json (Claude Code expands ${US_TOKEN} from the environment):

{
  "mcpServers": {
    "usherstats": {
      "type": "http",
      "url": "https://api.usherstats.com/mcp",
      "headers": { "Authorization": "Bearer ${US_TOKEN}" }
    }
  }
}

Claude Desktop

Claude Desktop starts local servers from claude_desktop_config.json; the mcp-remote bridge connects it to a remote server with a header. Put the token in env, not in the arguments:

{
  "mcpServers": {
    "usherstats": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://api.usherstats.com/mcp", "--header", "Authorization:${US_AUTH}"],
      "env": { "US_AUTH": "Bearer us_live_..." }
    }
  }
}

Cursor

In ~/.cursor/mcp.json (every project) or .cursor/mcp.json (one project):

{
  "mcpServers": {
    "usherstats": {
      "url": "https://api.usherstats.com/mcp",
      "headers": { "Authorization": "Bearer ${env:US_TOKEN}" }
    }
  }
}

Any other MCP client

Configure a Streamable HTTP (sometimes "HTTP" or "remote") server with the URL https://api.usherstats.com/mcp and the request header Authorization: Bearer <your token>. The server does not use OAuth; a client that only supports OAuth for remote servers can use the mcp-remote bridge as in the Claude Desktop example.

3. What to ask

  • "Which of my sites have the UsherStats snippet installed, and when did each last send data?"
  • "Create a site called Docs for docs.example.com in Europe/Bucharest and give me the snippet to paste."
  • "Add shop.example.com as a hostname of my Shop site."
  • "Mark 203.0.113.0/24 as our office network on every site so our own visits are not counted."
  • "Add a goal named Signup on the path /welcome for the Shop site."
  • "Who is on the team, and which invitations are still pending?"
  • "List the API tokens nobody has used in the last 30 days."

The agent starts with get_me (what the token may do) and list_sites; every id it needs comes from a list_ or get_ tool. When a call is refused, the tool error's first line says why and what to do -- for example This needs the "sites:write" scope, which this API token does not have -- so the agent can tell you, rather than guess.

Retries. Every tool that changes something takes an optional idempotencyKey. An agent that repeats a call with the same key within 24 hours (after a timeout, say) gets the first result back instead of creating a second site or token. The same key works over HTTP as the Idempotency-Key header.

Limits. A token makes up to 600 calls a minute, and every tool call counts, including each one inside a JSON-RPC batch (protocol version 2025-03-26 only). A batch carries at most 20 messages.

Tools

One tool per API route a token can call. The list is generated from the routes, so it is always exactly what the API serves; tools/list returns each tool's input schema (JSON Schema) and, for tools that return data, its output schema.

ToolScope
get_menone
get_workspacemembers:read
update_workspacemembers:write
list_membersmembers:read
create_member_invitemembers:write
delete_member_invitemembers:write
update_membermembers:write
delete_membermembers:write
list_tokenstokens:read
create_tokentokens:write
delete_tokentokens:write
list_sitessites:read
create_sitesites:write
get_sitesites:read
update_sitesites:write
delete_sitesites:write
create_site_hostnamesites:write
get_site_hostnamesites:read
verify_site_hostnamesites:write
delete_site_hostnamesites:write
list_site_keyssites:read
create_site_keysites:write
rotate_site_keysites:write
delete_site_keysites:write
get_site_snippetsites:read
get_site_install_statussites:read
update_site_settingssites:write
get_stats_catalogueanalytics:read
get_site_statsanalytics:read
query_site_statsanalytics:read
batch_site_statsanalytics:read
get_site_summaryanalytics:read
get_site_topanalytics:read
list_site_goalssites:read
create_site_goalsites:write
delete_site_goalsites:write
get_site_goal_resultsanalytics:read
list_site_experimentssites:read
create_site_experimentsites:write
update_site_experimentsites:write
delete_site_experimentsites:write
get_site_experiment_resultsanalytics:read
get_site_journeysanalytics:read
get_site_botbot:read
update_site_botbot:write
get_site_bot_decisionsbot:read
get_site_searchsearch:read
update_site_search_googlesearch:write
update_site_search_bingsearch:write
delete_site_search_googlesearch:write
delete_site_search_bingsearch:write
get_site_search_weekssearch:read
internal_link_sitesites:write
rotate_site_internal_linksites:write
export_siteexport:read
get_billingbilling:read
checkout_billingbilling:write
portal_billingbilling:write
plan_billingbilling:write

Not tools: sign-up, sign-in, passwords, sessions and two-factor setup (a person does these; an agent uses a token), accepting an invitation (it joins a signed-in person to a workspace), and the OpenAPI document (fetch it over HTTP).

The protocol, by hand

What a client sends, for anyone writing their own. Each request is its own POST /mcp. With 2026-07-28, the version and client capabilities travel in params._meta, and the method (and, for tools/call, the tool name) are mirrored in headers; a header that disagrees with the body is refused with 400 and error -32020.

Discover the server: supported versions, capabilities and instructions for the model.

curl https://api.usherstats.com/mcp \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: server/discover" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "server/discover", "params": {"_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}}}}'

List the tools:

curl https://api.usherstats.com/mcp \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: $VERSION" \
  -H "Mcp-Method: tools/list" \
  -d '{"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {"_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}}}}'

Call one. The result has the route's answer as JSON text in content and as structuredContent:

curl https://api.usherstats.com/mcp \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: create_site" \
  -d '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "create_site", "arguments": {"name": "Docs", "hostnames": ["docs.example.com"], "idempotencyKey": "create-docs-1"}, "_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}}}}'
curl https://api.usherstats.com/mcp \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: get_site_snippet" \
  --data @- <<EOF
{"jsonrpc": "2.0", "id": 4, "method": "tools/call",
 "params": {"name": "get_site_snippet", "arguments": {"siteId": "$SITE_ID"},
            "_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}}}}
EOF

A refusal is a tool result with "isError": true whose text starts with what to do. The read-only token cannot create sites:

curl https://api.usherstats.com/mcp \
  -H "Authorization: Bearer $READ_TOKEN" \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: create_site" \
  -d '{"jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": {"name": "create_site", "arguments": {"name": "Nope"}, "_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}}}}'

Without a token, /mcp answers 401 (as application/problem+json, with WWW-Authenticate: Bearer):

curl https://api.usherstats.com/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "id": 6, "method": "tools/list"}'

A client on an earlier protocol version opens with initialize; no session id is issued, so every later request stands alone (send MCP-Protocol-Version with the negotiated version):

curl https://api.usherstats.com/mcp \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "id": 7, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "my-agent", "version": "1.0"}}}'
curl https://api.usherstats.com/mcp \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: $NEGOTIATED" \
  -d '{"jsonrpc": "2.0", "id": 8, "method": "tools/call", "params": {"name": "list_sites", "arguments": {}}}'

GET /mcp and DELETE /mcp answer 405: there is no standalone event stream and no session to end.

View this page as Markdown