MCP server
Pulse speaks the Model Context Protocol, so an AI assistant can read your analytics directly. Ask “which source made me the most money this month?” and it calls Pulse for the answer.
Connect#
- Create an API key
Settings, API & MCP. The same
pk_…key works for the REST API and MCP. It can read every site on your account, so keep it private. - Add the server to your client
The endpoint is
https://stats.shedi.app/api/mcp, using Streamable HTTP with your key as a bearer token.claude mcp add --transport http pulse https://stats.shedi.app/api/mcp --header "Authorization: Bearer pk_YOUR_KEY" - Ask something
Restart the client if needed, then try one of the prompts below.
Tools#
| Tool | Arguments | Returns |
|---|---|---|
list_sites | – | The sites on the account: id, name, domain, time zone, source. |
get_stats | site, period | Visitors, pageviews, visits, bounce rate, sales and revenue (net of tax) with the previous period, top sources, channels, pages, countries, devices, and a daily series. Same as /api/v1/stats. |
get_revenue_by_source | site, period | Revenue, orders, visitors, conversion and revenue per visitor by first-visit source, channel, campaign, landing page, country, device and post, plus the revenue mix (new, renewals, refunds, MRR) and lifetime value by source. In USD. Same as /api/v1/revenue. |
get_top_pages | site, period | Top pages by visitors, with the revenue each landing page led to. |
get_revenue_by_post | site, period | Revenue and visitors by the exact post or URL that sent them (referring URL or utm_content). |
compare_periods | site, period | This period against the one before: each number with its change and percent change. |
get_funnel | site, steps, window, period | One site tracked by the Pulse script. Steps are {type:"path"|"event", value}; the last may be {type:"payment", value:"paid"}. See Funnels. |
get_live | site | Per site: active visitors in the last 5 minutes, current pages, sources, and the ten visitors most likely to buy with their reasons. |
siteis a site id, a domain, orall(the default).periodis a number of days (1to365) or a rangeYYYY-MM-DD..YYYY-MM-DD. The default is30. A value that is neither returns a JSON-RPC error (-32602) instead of silently using 30.- Channel names are the readable labels (
Organic social) in every tool, with the stable id (organicSocial) alongside. - Money is US dollars, net of tax. Visitor-written text (page paths, referrers, campaign and event names) is cleaned, and the tool descriptions tell the assistant to treat it as untrusted data.
Example prompts#
- “Which source made me the most money in the last 30 days?”
- “Compare visitors and revenue this week with last week, and tell me what changed.”
- “Which landing pages bring paying customers, not just visitors?”
- “Who is on my site right now, and is anyone likely to buy?”
- “Between 2026-09-01 and 2026-09-30, how much revenue came from social channels?”
- “Write me a three-line Monday summary of how example.com is doing.”
For the curious: the protocol#
The endpoint is stateless: it accepts POST with JSON-RPC 2.0 (one message, or a batch of up to 10) and answers with plain JSON, never a stream. GET returns 405. It supports protocol versions 2025-06-18, 2025-03-26 and 2024-11-05, and the methods initialize, ping, tools/list and tools/call.
curl -X POST https://stats.shedi.app/api/mcp \
-H "Authorization: Bearer pk_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_stats","arguments":{"site":"all","period":"30"}}}'Limits: 60 calls a minute per account, and a batch of N counts as N calls (429 with a JSON-RPC error and a Retry-After header); a request body over 64 KB is refused (413); an empty batch is an invalid request (-32600); 401 without a valid key, and 429 after 60 invalid keys a minute from one address.
Common issues#
- The client says unauthorized (401)
- Check the header reads
Authorization: Bearer pk_…and that the key was not revoked. - The client wants OAuth or a login
- Pulse authenticates with the bearer key only. Use a client or bridge that lets you set a header, as in the examples.
- The assistant says it cannot find the tools
- Restart the client after adding the server, and check it lists
pulseas connected. - Answers look empty
- Check
list_sitesfirst. A new site shows zeros until the script reports its first visit.