Skip to content

Querying GuideMode from an agent via /mcp

Everything the dashboards are built on is also an MCP server, at /mcp. Point an agent at it and it can ask the questions a stakeholder actually asks — “which suppliers’ teams have the slowest cycle time” — instead of somebody assembling the answer by hand.

It is the same semantic layer as /cubejs-api, mounted on the same router, behind the same authentication, over the same cubes. There is no second schema to keep in step and no second set of tenant filters to get wrong.

/mcp is query-only. The tools it exposes read the schema, validate a query and run it; there is no tool that writes, and no cube that resolves to anything but a SELECT.

It is tenant-scoped by the credential. Every request resolves a security context from whatever you present — a signed-in user’s token or an API key — and every cube’s sql filters on that tenant. An agent holding a credential for one organization cannot see another’s rows, whatever it asks for.

It is streamable HTTP, not SSE-only: POST /mcp carries JSON-RPC, GET /mcp opens the event stream the protocol uses for server-initiated messages. Connect it as an http server.

/mcp accepts either, and which you want depends on whether there is a person present.

Sign in API key
Who it is The person who signed in Whoever holds the key
Set up by Clicking through a consent screen Pasting a header
Access Read-only, one organisation Whatever the key carries
Good for Claude, Claude Code, any interactive agent CI, scripts, anything with no browser

Signing in is the better default: nothing is pasted anywhere, the agent gets read-only access to exactly one organisation, and it can be cut off from Settings → API Keys → Connected Applications without rotating a credential anything else is using.

Works the same in Claude, Claude Desktop, Claude mobile and Cowork — they are front-ends onto one connector runtime, so there is nothing per-app to configure.

  1. Settings → Connectors → Add custom connector
  2. URL: https://app.guidemode.dev/mcp
  3. Authentication: Sign in now
  4. OAuth client: Use Claude’s published identity — the recommended option, and the one that avoids registering a fresh OAuth client every time somebody connects

You will be sent to GuideMode to sign in, then asked to approve. If you belong to more than one organisation you pick one there, and that choice is fixed for the life of the connection — the agent cannot wander into another organisation, and switching organisations in the web UI does not change what it sees. To point it somewhere else, connect again.

On Team and Enterprise plans an owner adds the connector for the organisation once; everyone else then signs in to it individually.

Terminal window
claude mcp add guidemode --transport http https://app.guidemode.dev/mcp

No header: Claude Code runs the OAuth flow itself and opens a browser. --transport http (not sse) is right because the server speaks streamable HTTP. Add --scope user to make the server available in every project rather than only the current one.

Check it came up:

Terminal window
claude mcp list

Still the right answer where no browser exists — CI, a cron job, a container.

Terminal window
claude mcp add guidemode --transport http https://app.guidemode.dev/mcp \
--header "Authorization: Bearer $GUIDEMODE_API_KEY"

--header may be repeated. The key carries whatever permissions it was created with, and is bound to the one organisation it was issued for.

The adapter exposes four tools. Most hosts namespace them by connector, so in Claude they appear as discover, validate, load and chart under GuideMode:

Tool What it does
discover The first call, always. Returns the matching cubes with their measures, dimensions, joins and descriptions, plus the query-language reference and a guide to date filtering
validate Checks a query against the schema and returns a corrected one, plus the SQL it would run
load Runs the query and returns aggregated rows
chart Runs the same query as load, and asks the host to draw it

Discovery is the whole mechanism. An agent that has never seen GuideMode calls discover with a topic or an intent, and gets back the cubes that match with a description on the cube and on every one of its dimensions and measures — which is why tests/api/cubes/mcp-descriptions.test.ts fails the build on a member added without one. A dimension named prCycleTimeP50Seconds is a guess until something says it is a median within the month and that averaging thirteen of them is a mean of medians. The agent has nothing else to go on.

Joins are in that response too, so a query may name dimensions from any related cube and the system joins them: TeamBaseline reaches Teams and OrgUnits, and the AI platform cubes reach Teams through team_members and TenantUsers.

chart runs the identical query load does — same validation, same SQL, same rows — and additionally offers the host an interactive visualisation: a small app the server ships alongside the result, which renders the rows as a line, bar, pie, treemap, KPI or any of about two dozen other shapes, with the chart type switchable in place.

This is an MCP Apps feature, and it is the host that implements it. A host that supports MCP Apps shows the chart; a host that does not ignores the offer and shows the same rows load would have returned. Either way the answer is the same data, so asking for chart is never worse than asking for load.

Numbers and dates are formatted en-GB.

Three questions, and the queries that answer them

Section titled “Three questions, and the queries that answer them”

Which suppliers’ teams have the slowest cycle time?

Section titled “Which suppliers’ teams have the slowest cycle time?”
{
"measures": ["TeamBaseline.prCycleTimeP50Seconds", "TeamBaseline.teamMonths"],
"dimensions": ["TeamBaseline.supplierName", "TeamBaseline.teamName"],
"order": { "TeamBaseline.prCycleTimeP50Seconds": "desc" }
}

TeamBaseline.supplierName is the team’s supplier, which is what “a supplier’s teams” means; a person’s supplier is TenantUsers.supplierName and is a different question. teamMonths is in the query on purpose: the measure is an average of monthly medians, and a team with one month behind it should not be read the same way as one with thirteen.

AI adoption by team over the last three months

Section titled “AI adoption by team over the last three months”
{
"measures": [
"TeamBaseline.aiAssistedPrShare",
"TeamBaseline.aiActiveUsers",
"TeamBaseline.aiSeatHolders"
],
"dimensions": ["TeamBaseline.teamName"],
"timeDimensions": [
{
"dimension": "TeamBaseline.month",
"granularity": "month",
"dateRange": ["2026-07-01", "2026-09-30"]
}
]
}

aiAssistedPrShare is null in a month where nothing merged, because a share of no pull requests is not zero — so a team can hold a seat, be active, and still show no share. Seats are a stock taken at the month’s last snapshot; sessions and commits are flows.

{
"measures": [
"TeamBaseline.headcount",
"TeamBaseline.prsMerged",
"TeamBaseline.issuesClosed",
"TeamBaseline.productionDeploys"
],
"dimensions": ["OrgUnits.name", "OrgUnits.kind"],
"timeDimensions": [
{ "dimension": "TeamBaseline.month", "dateRange": ["2026-09-01", "2026-09-30"] }
]
}

OrgUnits is reached by a join from the baseline’s org_unit_id, so the roll-up follows the organization’s own hierarchy rather than the shape a provider happens to expose. Headcount repeats unchanged on every month row — team_members records no join or leave date — so pin it to a single month rather than summing it across several.

There is no cost measure and there will not be one. TeamBaseline.costCentreCode is a join key, and cost stays with your finance function; see Baseline exports for how the join is meant to work.

An API key carries the role of the account that created it, in full. keys.permissions exists in the schema and defaults to read,write, and middleware/auth.ts parses it onto the request context — but grep the server for it and you will find nothing that reads it to make a decision. Authorization is built from the creator’s tenant role, so a key made by an owner can do everything an owner can do, including things that are not reads and are not /mcp.

So the key you paste into claude mcp add, and especially any key an external analyst or consultant holds, must be created by a low-privilege member account and never by an admin or an owner. Create a dedicated user, invite it as a member, issue the key from there, and revoke it in Settings → API Keys when the engagement ends. That is the same rule the baseline exports are handed over under, for the same reason.

Read-only keys with scopes the server enforces are deferred, not shipped. There is deliberately no “read-only” checkbox on the API keys screen: a label that nothing enforces is worse than no label, because it invites people to hand out a key they believe is narrower than it is.