Two different things live on this page, and they are easy to confuse because both involve AI. The first is how Leed counts non-human readers: an agent that answers a question out of your documentation never loads a page, so it is invisible to every page-view metric, and Leed records it in a separate stream rather than letting it vanish. The second is how you or a client query your analytics by name instead of reading a dashboard — the same catalog Know renders, reachable from the in-CMS assistant and from any MCP client you have connected.
Three audiences, counted separately
Leed splits traffic into humans, AI agents and MCP clients, and it does that because the three arrive through genuinely different doors:
| Audience | What counts as one unit | Where it comes from | Sets a cookie? | Where it surfaces today |
|---|---|---|---|---|
| Humans | One page view | Page-view events from the tracker on your published site | Yes — __sid and __lid | Every session- and page-view metric in Leed |
| AI agents | One granted tool call | Your site’s public agent surface at /api/agent/* | No | The request log only |
| MCP clients | One granted tool call | Your Docs MCP, the OAuth-gated surface your readers connect | No | The request log only |
The split matters most when your page views drop. If readers have started asking an assistant instead of visiting, the human bucket falls while the agent and MCP buckets rise — and the total demand for your content has not moved at all. Counting them in one bucket would hide that; counting them separately is what makes it legible.
flowchart TD H["A person's browser loads a page"] --> PV["Page view events"] A["An AI agent calls your site agent surface"] --> RL["Tool-call request log"] M["An AI client calls your Docs MCP"] --> RL S["A reader types in documentation search"] --> RL PV --> HB["Humans"] RL -->|"source = agent"| AB["AI agents"] RL -->|"source = mcp"| MB["MCP clients"] RL -->|"source = site"| SB["Reader searches"] H --> SESS["Session row, __sid and __lid cookies"] A -.->|"no session, no cookie"| NONE["Outside the session chain"] M -.->|"no session, no cookie"| NONE S -.->|"no session, no cookie"| NONE
What is counted
Only granted tool calls count as traffic. A request that was refused at the gate — an unrecognized visitor trying to reach a Docs MCP that is not open to them — is written to the same log flagged as denied, and excluded from the audience buckets. It is a demand signal, not an audience.
Each bucket carries a prior-period delta computed exactly the way every other comparison in Leed is: the immediately preceding window of equal length. When the prior window recorded nothing at all, there is no percentage to show, and the delta is reported as absent rather than as an infinite increase.
One thing the buckets deliberately do not contain is your own work. Tool calls you or your teammates make against the Operator MCP — including the analytics tools described below — are recorded in the separate action log that tracks CMS activity, not in the reader-facing request log. Running query_analytics twenty times does not inflate your MCP audience.
Where you can see it today, honestly
That is worth stating plainly for two reasons. If you are evaluating Leed on agent reporting, evaluate what is on the screen. And if you are already publishing to agents, the data is being kept, so nothing is lost while you wait.
Why agent traffic sets no cookies
Three surfaces run entirely outside the session and cookie chain: the Docs MCP, the public agent surface at /api/agent/*, and reader search. None of them mints a session, creates a visitor record or sets a tracking cookie. A cookie-less agent request passes through a separate application before the normal middleware ever sees it.
That is a deliberate privacy choice, not an oversight, and it has a direct consequence you should expect: agent activity can never appear in a session-based metric. Unique Sessions, Unique Visitors, bounce rate, the journey funnel — none of them will ever move because an agent read your docs, no matter how much agent traffic you get. The session model those metrics are built on is described in how Leed tracks visitors, and the agent surface your readers’ tools actually call is described in the Site AI Agent. Connecting a client to the reader-facing service is covered in the Docs MCP overview.
Reader search is logged too
When live documentation search is on, every reader query lands in the same request log as agent and MCP traffic, tagged as coming from the site. That includes the queries that returned nothing — an empty search is the whole point of keeping the log, because a query with no results is a reader telling you what your documentation does not cover. A search that failed outright is flagged as failed so it cannot be mistaken for a genuine zero-result one.
Logging is best-effort and deferred: it never blocks the response and a logging failure never turns a successful search into an error. Queries are truncated to 256 characters, matching the endpoint’s own limit.
Asking the assistant for a number
The assistant inside the CMS has an analytics tool, so you can ask for a figure in English instead of opening Know. Ask about traffic, popular pages, referrers or geography and it fetches the metric and renders a chart inline in the conversation, followed by a sentence or two of summary.
What you get back depends on the metric’s shape. A counted metric renders as the number and its time series together, or just one of the two if you asked for one. A ranked metric renders as a top-ten list. The two geographic metrics render as maps — a US state map and a world map. A name the catalog does not recognize renders as Unknown metric: <name> rather than an empty chart.
Prompts that work:
- “How many page views did we get in the last 30 days?”
- “Show me unique visitors for the last quarter as a trend.”
- “Which referrer hosts sent us the most sessions this month?”
The one rule that matters: if you name a page by its title, the assistant resolves the title to a page id first. It never passes a title where an id belongs, and neither should you when writing a tool call by hand — a title in the pageId slot silently scopes the query to nothing. The rest of what the assistant can do is described in the Leed Assistant.
Querying metrics from an MCP client
A connected Operator MCP client gets two analytics tools. Together they are a complete loop: ask what exists, then ask for one of them.
| Tool | Available to | Parameters | Returns |
|---|---|---|---|
list_analytics_metrics | MCP clients | None | The catalog, split into numberMetrics and listMetrics |
query_analytics | MCP clients | metric (required), start and end (required, ISO-8601), type (optional: count or line), pageId (optional) | One metric’s result for the window |
get_analytics | The in-CMS assistant | The same four, with metric constrained to the catalog rather than a free string | The same result, rendered as a chart in the conversation |
Full parameter tables for these alongside the rest of the Operator MCP surface are in MCP tools: analytics and introspection, and connecting a client in the first place is covered in connecting to the Operator MCP.
Discover the catalog
list_analytics_metrics takes no input at all and returns every metric name you can query, grouped by shape. Start here rather than guessing names — the catalog is the authority, and the names are query keys, not display labels.
{
"numberMetrics": [
"averagePageViewsPerSessionMetric",
"distinctPagesViewedPerHourMetric",
"pageViewsMetric",
"totalSessionsMetric",
"uniqueSessionsMetric",
"uniqueVisitorsMetric"
],
"listMetrics": ["entryPagesByViewMetric", "referrerHostMetric", "sessionsByCountryMetric", "…"]
}The tool is read-only and workspace-agnostic: it lists what the product can measure, not what your workspace happens to have data for. Every name it returns is defined in the metrics reference for the counted six and the list metrics reference for the ranked twenty-two.
Query one metric
query_analytics takes a metric name, an ISO-8601 start and end, and two optional narrowings: type, which picks the count or the time series for a counted metric, and pageId, which scopes the query to a single page.
A counted metric with no type returns all three things at once — the number for the window, the same number for the immediately preceding window of equal length, and a zero-filled time series:
{
"name": "Page Views",
"count": 4812,
"previous": 4130,
"line": [
{ "timeValue": 1756684800000, "count": 143 },
{ "timeValue": 1756771200000, "count": 168 }
]
}timeValue is a millisecond epoch timestamp at the start of each bucket, and the bucket size is chosen from the span of the range rather than passed in. Every bucket in the window is present, including the empty ones, so a chart drawn from line has no gaps.
A ranked metric returns its declared column headers alongside the rows, so a client can render a table without knowing the metric in advance:
{
"name": "Referrers",
"headers": ["Referrer", "Sessions"],
"values": [{ "referrerHost": "news.ycombinator.com", "frequency": 214 }],
"count": 37
}The rules that still apply
A tool call is not a back door. Everything that governs the dashboard governs it too:
- The span cap. Any single query is capped at 365 days on every plan, including unlimited ones. Wider is a
400 Range cannot exceed 365 days, and anendon or beforestartis a400 end must be after start. - Your plan’s history. How far back you can reach is a retention quota. A range straddling the cutoff is silently clamped to it; a range lying entirely before it returns
402 upgrade_required. Both limits are set out in the 365-day rule. - Gated metrics. Media engagement, element and menu clicks, and inbound attribution need Growth. Asking for one below Growth returns
402 upgrade_requirednaming the feature — capture never stops, only the query is refused. - Permission. Every call carries the
analytics:readprivilege the person behind the client would need in the CMS. A client cannot read numbers its operator could not read. - Unknown names. A metric the catalog does not contain is a
400 Unknown metric: <name>. This is why you calllist_analytics_metricsfirst.
Worked example
The same question, asked two ways: how much traffic did we get over the last 30 days, and then how much of it belonged to one page.
- Assistant
- MCP client
Ask in the conversation. The assistant picks the metric, fills in the dates, and renders the chart:
How many page views did we get in the last 30 days?
Now show me just the getting-started page.For the second question the assistant resolves “getting-started page” to a page id before it queries, which is why you can name a page the way you talk about it.
Ask the catalog what exists, then query one of its names. Discover first:
{ "tool": "list_analytics_metrics", "arguments": {} }Then query site-wide, letting the counted metric return its number, its prior-period value and its series together:
{
"tool": "query_analytics",
"arguments": {
"metric": "pageViewsMetric",
"start": "2026-08-03T00:00:00.000Z",
"end": "2026-09-02T00:00:00.000Z"
}
}Then scope the same metric to one page — resolving the title to an id with list_pages first, never passing the title itself:
{
"tool": "query_analytics",
"arguments": {
"metric": "pageViewsMetric",
"start": "2026-08-03T00:00:00.000Z",
"end": "2026-09-02T00:00:00.000Z",
"pageId": "e1b0c2a4-6d3f-4a91-9c77-0f2d5b8a4e10"
}
}Not every metric can be narrowed this way. Three of the six counted metrics have no page-scoped form, and passing pageId to them does not narrow anything — the metrics reference says which. Two ranked metrics are the opposite case: they only mean something with a page id, and there is no meaningful site-wide answer.