MCP server
/api/v1 — every verb the ah CLI speaks — is also served as a remote MCP server at https://agenthog.io/mcp. Same tokens, same scoping, same JSON as --json.
When this is the right door. A client that cannot run a shell: a Claude-powered assistant you are building on the Messages API, a cloud or sandboxed agent session where npm i -g is awkward, an editor whose only integration is an MCP URL. For a coding agent in a terminal, ah is the better tool — it composes with pipes and files, and it does not put thirty tool schemas into every turn of context. Nothing here is required to use AgentHog.
Connect
One URL plus a read token from the Tokens page, sent as a bearer header. The token pins which projects the tools can see; narrow it to one project if the client should not see the whole account. A read token gets the read tools and nothing else — the write tools are not even listed to it. A write token (org admins mint those) adds the write verbs below.
claude mcp add --transport http agenthog https://agenthog.io/mcp \ --header "Authorization: Bearer ah_tok_…"
This release is bearer-token only. Anything with a headers field (Claude Code, Cursor, Windsurf, Zed, VS Code, the Messages API’s MCP connector) works as above. In Claude.ai, an organization administrator can add it as a custom connector with a static Authorization header (Anthropic’s static_headers beta); individual users adding a connector by URL, and ChatGPT, need OAuth, which is not served yet.
Tools
One per ah verb, returning exactly the JSON that verb prints with --json; the write tools send the same bodies the CLI’s HTTP mode does. Every project-scoped tool takes the same three arguments the CLI’s global flags set — project (key or name; optional with one project in scope), since (24h, 7d, 30d, Nd, YYYY-MM-DD) and filter (all, bots, test, server; omit for human + unknown). Bots and test traffic are excluded by default, exactly as in the CLI.
| Tool | Scope | CLI twin | Does |
|---|---|---|---|
projects | read | ah projects list | List the projects this token can read — key, name, domains, platforms. Call first when you do not know which project to ask about. |
whoami | read | ah whoami | Which account and token this connection is using, its scope, and how many projects it can read. |
digest | read | ah digest | Start here for 'how are we doing': one-call summary of a project over the window — sessions vs the previous equal window, top campaigns/sources/entry pages/events, dead and rage clicks, returning %, identified users, crawler summary, biggest mover. |
traffic | read | ah traffic | Sessions per day by classification (human, unknown, suspected bot, crawler) with bounce %, engaged % and conversions, plus a top-sources table. |
active | read | ah active | DAU / WAU / MAU — distinct people per day, week or month, optionally split by a dimension. The default window scales with granularity (30d daily, 84d weekly, 180d monthly). |
retention | read | ah retention | Cohort retention triangle: people grouped by the day/week/month of their first session, and the % still active N periods later. All-time — no window. |
campaigns | read | ah campaigns | Per-utm_campaign performance: sessions, conversions, conversion %, junk share (bots + crawlers), top utm_content. |
referrers | read | ah referrers | Traffic sources (utm_source, else referrer host, else direct) ranked by engaged %, with conversion % and average duration; low-volume sources in an unranked tail. |
crawlers | read | ah crawlers | Crawler hits from the server request log (needs the server middleware): per-crawler counts, last seen, top paths, and an AI-crawler subtotal (GPTBot, ClaudeBot, PerplexityBot…). |
events | read | ah events | Recent events newest first (time, session, name, path, key props); or, with `name` and `by`, one event broken down by a dimension. Server-relayed events are included by default. |
events_top | read | ah events top | Event names ranked by count and distinct sessions — the fastest way to learn what a project actually tracks. |
clicks | read | ah clicks <path> | Click targets on one page path grouped by element name and selector, then DEAD clicks (nothing happened) and RAGE clicks (3+ on the same element within 1.5s). |
paths | read | ah paths --to <event> | The most common event sequences that end at a target event — semi-automatic funnel discovery. |
sessions_list | read | ah sessions list | Recent sessions newest first: id, start, source/campaign, entry page, pageviews, duration, classification and bot score. Filter to converted, returning or bounced sessions, a UTM source/campaign, or an experiment variant. |
sessions_show | read | ah sessions show <id> | One session in full: visitor, classification and bot signals, source, device, geo, then the chronological event timeline with relative offsets. |
users | read | ah users | Identified people: email or anon id, key traits, session count, first and last seen. Filter by a trait key, first-touch source, or returning. No window — identities are cumulative. |
user | read | ah user <ref> | One person: identity card (email, traits, first/last seen), their sessions, and their last 20 events. Matches an email, a user_id trait, or an anon id, merging every identity that shares it. |
funnel | read | ah funnel | Run a saved funnel by name, or an ad-hoc one from 2+ event names matched as an ordered subsequence within each session. Returns per-step counts, conversion %, and median/p90 time between steps; optionally a per-session table or a dimension × step matrix. |
funnels_list | read | ah funnels list | The project's saved funnels and their steps. |
goals_list | read | ah goals list | The project's conversion goals (name → event). A session that contains a goal event counts as converted. |
revenue | read | ah revenue | Real-money revenue over the mapped purchase events: net, gross, refunds, orders, payers, ARPU, ARPPU, AOV — per currency, with optional breakdowns and a day/week/month series. Unmapped → { mapped: false } with setup instructions, never a zero. |
ltv | read | ah ltv | Cumulative revenue per person by acquisition cohort (historical, not predicted). All-time — no window. |
revenue_config | read | ah revenue config | The project's revenue mappings (which events carry money, and where the amount lives) with a live sanity count per mapping — including events whose amount did not parse. Explains a `revenue` result, or why it says mapped: false. |
economy | read | ah economy | In-game virtual-currency flows over the mapped economy events: per currency, sourced (paid vs earned) and sunk amounts, net flow, top sources and sinks, plus an instrumentation-health line (unparseable amounts, sign disagreements, balance drift). Unmapped → { mapped: false }. |
economy_config | read | ah economy config | The project's economy mappings: which events source or sink which virtual currency, with the amount, currency and balance props each reads. Explains an `economy` result, or why it says mapped: false. |
changes_list | read | ah changes list | The project's changelog — deploys, experiments and other logged changes with timestamps — to line up against a metric that moved. |
changes_show | read | ah changes show <id> | One logged change by id or unique id prefix: title, time, kind, description, tags, variant, version, url, author. |
flags_list | read | ah flags list | Feature flags: key, state, traffic %, variants and weights, and whether an experiment is live on each. |
flags_show | read | ah flags show <key> | One feature flag in full: state, traffic %, variants and weights, description, and its experiment history. |
experiments_list | read | ah experiments list | The project's experiments, live and concluded: flag, metric, control, start/stop, winner and conclusion where decided. |
experiments_show | read | ah experiments show <flag> | The live (else latest) experiment on a flag: hypothesis, metric, secondary metrics, guardrails, minimum duration, and status. For the numbers, call `experiments_results`. |
experiments_results | read | ah experiments results <flag> | Per-variant results for the live (else latest) experiment on a flag: exposures, conversions, conversion %, and chance to beat control. The verdict is gated on the configured minimum duration; the numbers are live from the first exposure. |
reports_list | read | ah reports list | Shared reports the organization can open — slug, title, description, revision count, last updated. |
reports_read | read | ah reports read <slug> | A shared report resolved: the prose and every live widget's current rows, with as-of time, TTL and any widget error. Waits for stale widgets to recompute. |
usage | read | ah usage | The account's monthly event volume against its plan allowance, with a per-project breakdown. |
schema | read | ah schema | The contract for `sql`: the readable tables and columns, helper functions, bind parameters (:since, :until) and the promises the surface keeps. Read before writing a query. |
sql | read | ah sql | Run a read-only SQL SELECT against the queryable tables (see `schema`). Scoped server-side to the token's projects and rate-limited; anything but a SELECT is rejected. Use :since / :until in the query and pass the window as arguments. |
reports_show | read | ah reports show <slug> | A shared report's metadata: title, description, author, revision count, and each live widget's cache state (as-of, TTL, stale, error). For the rows, call `reports_read`. |
reports_history | read | ah reports history <slug> | A shared report's revisions, newest first: number, author, time, summary of what changed. |
reports_pull | read | ah reports pull <slug> [--rev N] | One revision of a shared report in full — the authoring Markdown source verbatim plus its parsed blocks. Pull before `reports_update` so the next revision edits the current text. |
reports_widgets | read | ah reports widgets | The report widget library: every widget kind, its column contract, and the limits — the `schema` of report authoring. Read before writing a ```widget fence. |
revenue_suggest | read | ah revenue suggest | Propose revenue mappings from the props already in the data: for each candidate event, the amount prop, the share of occurrences that parse as a number (the honesty column), and the exact `revenue_map` arguments. Read-only — apply a proposal with `revenue_map`. |
economy_suggest | read | ah economy suggest | Propose economy mappings from the props already in the data — economy-shaped events with a guessed flow direction (labelled as a guess) and the share of amounts that parse. Read-only — apply a proposal with `economy_map`. |
test_ips_list | read | ah test-ips list | The account's own IPs whose traffic is labelled `test` instead of counted as visitors. |
reports_preview | read | ah reports preview --file | Validate a report's authoring Markdown and run every widget once, returning the resolved report plus warnings and hints. Persists nothing (a read token is enough) — the loop is preview until clean, then `reports_create`. |
reports_refresh | read · refreshes cache | ah reports refresh <slug> | Recompute a shared report's live widgets now, bypassing their TTL (not the 60-second floor). Any token; changes cached rows only. |
goals_set | write | ah goals set <name> <event> | Define (or redefine) a conversion goal: a session containing the event counts as converted from the next sweep on. |
funnels_save | write | ah funnels save <name> <steps…> | Save (or replace) a named funnel so `funnel` can run it by name. |
changes_add | write | ah changes add | Log a change — a deploy, an experiment ramp, a pricing edit — so it lines up against the traffic and revenue that followed. Backdatable. |
changes_update | write | ah changes update <id> | Edit a logged change. Only the fields passed are changed; pass an empty string to clear desc, variant, version, url or author. |
changes_remove | write · destructive | ah changes rm <id> | Delete a logged change. |
track | write | ah track <event> | Record ONE event no SDK saw — a deploy, a migration, a correction, a webhook you are relaying by hand — now or backdated. Lands on a server session (never counted as traffic; visible in `events`, `revenue`, `economy`, funnels with filter=server). Attribute it to a person with `person` (their user_id) and/or `email`, else it joins the shared unattributed server anon. No idempotency key: calling this twice writes the event twice. Bulk imports stay in the CLI (`ah track --file`). |
flags_create | write | ah flags create <key> | Create a feature flag: boolean (on/off) or multivariate with weighted variants. Starts enabled at `traffic` percent (0 = dark launch: created, nobody enrolled, ramp later with `flags_rollout`). |
flags_rollout | write | ah flags rollout <key> <pct> | Ramp a flag: set the percent of sessions enrolled. |
flags_weights | write | ah flags weights <key> <spec> | Re-weight a multivariate flag's variants. Refused while an experiment is live on it unless `force` is true (that invalidates the experiment's numbers). |
flags_enable | write | ah flags enable <key> | Turn a flag on (serving its variants again). |
flags_disable | write | ah flags disable <key> | Kill switch: turn a flag off so every session gets the fallback. Reversible with `flags_enable`. |
flags_archive | write · destructive | ah flags archive <key> | Retire a flag once its winner is shipped in code. Refused while an experiment is live on it. |
experiments_start | write | ah experiments start <flag> --metric <event> | Open an analysis window on a multivariate flag: name the decision metric (an event), the control variant, a minimum duration, and optional secondary and guardrail metrics. Check exposure is flowing (`flags_show`) before starting. |
experiments_stop | write · destructive | ah experiments stop <flag> | Close the experiment window on a flag, optionally recording the winner and a conclusion. The flag keeps serving; ship the winner in code, then `flags_archive`. |
revenue_map | write | ah revenue map <event> --amount <prop> | Declare that an event you already send carries money and where the amount lives. Applies retroactively over all history; nothing is re-ingested. `revenue_suggest` proposes these. |
revenue_unmap | write · destructive | ah revenue unmap <event> | Remove an event's revenue mapping. Retroactive like `revenue_map`: its history stops counting as money. |
economy_map | write | ah economy map --event E --source|--sink | Declare that an event sources (adds) or sinks (removes) virtual currency. Convention: props `amount`, `currency`, `balance`. Applies retroactively; `economy_suggest` proposes these. |
economy_unmap | write · destructive | ah economy unmap --event E | Remove exactly one (event, amount-prop) economy mapping; a bundle event's other mappings survive. |
reports_create | write | ah reports create [<slug>] --file | Publish a shared report from its authoring Markdown — visible to the whole organization immediately (no draft state). Preview first with `reports_preview`; read `reports_widgets` for the fence format. |
reports_update | write | ah reports update <slug> --file | Publish a new revision of a shared report from new source. History is kept — `reports_history` and `reports_pull` reach older revisions. |
reports_delete | write · destructive | ah reports delete <slug> | Delete a shared report with all its revisions and cached rows. Not reversible. |
projects_create | write | ah projects create <name> | Create a project in the token's account and get its key (ah_xxxxxxxx). Needs an ACCOUNT-WIDE write token — one narrowed to specific projects is refused. |
test_ips_add | write | ah test-ips add [ip] | Label an IP as the account's own: its traffic reads as `test` instead of counting as visitors. Omit `ip` to use the address this request came from. |
test_ips_remove | write · destructive | ah test-ips rm <ip> | Stop labelling an IP as the account's own; its future traffic counts as visitors again. |
What the server is, and is not
- Stateless Streamable HTTP. One
POSTper JSON-RPC message, one JSON document back. NoMcp-Session-Idis issued,GETandDELETEreturn 405, and there is no server-to-client stream. Protocol revisions2025-06-18and2025-03-26negotiate (the older HTTP+SSE transport is not served). JSON-RPC batches of up to 20 messages are accepted, and each element pays the rate limit. - Scope is the permission model. Exactly as on
/api/v1: a read token lists and calls only the read tools; a write token also getstrack, goals, funnels, changes, flags, experiments, revenue and economy mappings, reports, projects and test IPs. Every tool carries the spec’s annotations —readOnlyHinton reads,destructiveHinton deletes, archives, unmaps and stops — so a client that gates on them can auto-approve a read and ask before a delete. A token pasted into a chat client should be a read token unless it has a reason not to be. trackwrites one event throughPOST /ingestwith the same conventions asah track(server session,server:<person>anon, typed props, backdating viaat). No idempotency key: two calls, two events. Bulk imports stay in the CLI (ah track --file).- Token-scoped. A tool can only see the projects its token can; a
projectoutside that scope is a 404 in the tool result, never a leak. - Rate-limited per token: 60 requests a minute with a burst of 20 (HTTP 429 beyond that). The
sqltool also carries the/api/v1/sqllimit. - Tool errors are results. A bad argument, an unknown project or a rejected query comes back as
isError: truewith the same message the CLI would print, so the model can correct itself; protocol errors (unknown method, malformed JSON-RPC) use JSON-RPC error codes. - Not yet: OAuth and dynamic client registration, resources and prompts, bulk event import.
Self-hosted
The URL above is this deployment’s; a self-hosted AgentHog serves its own at /mcp. Rate limits are tunable with AGENTHOG_MCP_RATE_PER_MIN and AGENTHOG_MCP_RATE_BURST.