The MCP server

Deckgauge exposes its read-only Advisor tools as a standard Model Context Protocol server, so any MCP-capable client can query one board's engineering-intelligence data directly — no chat panel required. It's the same tool catalog the in-app Advisor and a local coding agent both call, so behavior is identical everywhere.

deckgauge · MCP · /mcp
get_team_overview · boardId=plat-42, Viewer access → 200, JSON payload
get_team_overview · boardId=web-19, no access → forbidden: no access to board

Same tool call, checked fresh against two different boards.

What it does, and where the data comes from

/mcp is a Streamable HTTP MCP server mounted on the same Fastify API as the rest of the app, inside its authenticated route group. Each request builds a fresh McpServer and a stateless transport, then registers seven tools drawn from one provider-neutral catalog also used by the in-app Advisor chat. Every tool is read-only — nothing behind /mcp writes to Postgres, Jira, GitHub, Azure DevOps, or GitLab.

ToolWhat it answers
get_team_overviewTeam KPIs over the last N days — PRs merged, median cycle time, active developers, AI-assisted share
find_slowdownsDevelopers whose merge throughput dropped sharply against their own baseline
get_ai_breakdownAI-assisted PR share per developer
get_ticket_timelineOne ticket's unified activity across Jira, GitHub, GitLab and Azure DevOps
list_board_rowsThe board's rows and their fields — name, group, status, owner, assignee, description, Jira key, custom columns. Filterable by group, status, free-text search, and whether a description is present; paged, and every response reports how many rows matched in total
get_board_structureThe board's groups, statuses and custom columns with their ids, plus each connected source's allow-list of fields sync may write. Note that list is board-level configuration, not a verdict on any one field: a manual edit records an override, and an override beats the allow-list
list_excluded_rowsRows deleted from the board and therefore excluded from re-sync — why an issue that exists in Jira or Azure DevOps is missing here

Read-only, board-scoped, and re-checked on every call

Every tool takes a required boardId alongside its own arguments. On each call — not once per connection, not cached from an earlier call in the same session — the server:

  1. Re-checks board access — confirms the authenticated user has at least Viewer access to that specific boardId. A user without it gets a tool-level error (forbidden: no access to board), never board data.
  2. Builds the scope itself, server-side — resolves the board's connected Jira/GitHub/Azure DevOps/GitLab project and repo identifiers from Postgres. That scope is never accepted as tool input, so a client cannot widen its own access by passing different project keys or repo names — boardId is the only board-shaped thing it supplies.
  3. Calls the read-only handler with that server-built scope and returns a single JSON text block.

Connection-level auth here isn't special-cased anymore: like every other non-public route, /mcp declares an authenticated policy the server checks before either handler runs, rejecting with 401 when there's no valid bearer token, on both POST and GET /mcp. What is distinct to /mcp is the per-call board re-check in step 1 above — boardId there is a tool argument inside the MCP payload, not a URL param the route-level policy can see, so that check stays in the tool handler itself.

How to point a client at it

The endpoint is http://localhost:3001/mcp on local Docker staging (substitute the API's port on other stacks), speaking Streamable HTTP over POST/GET /mcp. Authenticate with the same Keycloak-issued JWT the web app itself uses — obtained by signing in through the web app and reusing that bearer token; there's no separate scripted token-exchange path yet for a non-browser client.

Most MCP clients (Claude Desktop, Claude Code's .mcp.json) speak stdio rather than HTTP, so bridge the two with mcp-remote: point its command at npx -y mcp-remote http://localhost:3001/mcp, and attach the token with --header "Authorization: Bearer <token>". Once connected, ask the client something like "using the deckgauge tools, give me the team overview for board <your board id> over the last 30 days."

Want your own already-signed-in Claude Code or Codex session driving these same tools from inside Deckgauge's Advisor panel, with no token-copying step at all? See Use your own local agent — it's a separate host-side bridge built on top of this same /mcp endpoint.

If it looks wrong

SymptomCauseFix
401 on every callNo bearer token sent, or it failed JWT verificationSign in through the web app and copy that session's access token into the client config
Tool result is forbidden: no access to boardThe token's user has no BoardAccess row on that boardId, or one below ViewerAsk a board owner for at least Viewer access — it's re-checked per call, so access on one board doesn't carry to another
Client can't connect at allSpeaking stdio directly at an HTTP endpoint, with no bridge in betweenUse mcp-remote (or another Streamable HTTP-aware client) rather than a raw stdio config

Related

Last updated