Skip to content

Derive the /mcp agent endpoint from the agent-tagged routes#37

Merged
czpython merged 1 commit into
mainfrom
eng/mcp-endpoint
Jul 20, 2026
Merged

Derive the /mcp agent endpoint from the agent-tagged routes#37
czpython merged 1 commit into
mainfrom
eng/mcp-endpoint

Conversation

@czpython

@czpython czpython commented Jul 19, 2026

Copy link
Copy Markdown
Owner

What

  • The REST agent surface lives at its nouns/api/gates/{run_id} (+/answer), /api/agent-calls/{call_id}, /api/runs/{run_id}/cancel beside the existing resume, /api/usage/summary — with tags=["agent"] as the one grouping signal and unchanged operation_ids; /mcp belongs to the protocol endpoint alone. Route docstrings carry the agent-facing descriptions both surfaces read.
  • /mcp serves tools derived from the agent-tagged routes (FastMCP OpenAPIProvider over an in-process ASGI client) — the route is an operation's single declaration (schema, docstring, operation_id), so an extension's tagged route joins tools/list by declaration alone. mcp_app mounts at exactly /mcp with its lifespan composed via combine_lifespans; annotations come from a per-operation map, and the tools/list test guards unmapped tagged routes.
  • Auth: PAT bearer at the transport (PatTokenVerifier), and CallerPat re-stamps each caller's bearer onto the internal route call (the derivation strips authorization when replaying headers), so every route runs as the presenting account.
  • Fail-closed errors: the transport is built with raise_app_exceptions=False, so an app crash reaches the tool as the app's sanitized 500 — no masking needed, and the domain taxonomy {code, message, retryable} travels embedded in tool-error text. Validation failures carry VALIDATION_ERROR detail instead; documented.
  • Kept simple per the Agent services, agent-tagged routes, and the gate answer receipt #36 review outcome: no view-side clipping and no seam boundary-snapping — reads are plain byte windows, a seam serves one replacement char like the live tail, and the one stale test expectation main carried from before that call is aligned here (eng/agent-services carries the fuller sweep and supersedes it on merge).
  • Caddy: /mcp on both listeners — ahead of the identity gate on the dashboard listener, beside the HMAC webhooks on the public integrations listener (the canonical corporate address).
  • Docs: connect-your-agent.md (five tools, the parkedAt echo contract, the claude one-liner and codex config.toml form, honest v1 caveats: polling only, no discovery tool) plus deploy, configuration, and troubleshooting links.

Deliberately not ported from the old branch

  • The /api/auth/pats rename — main's shipped /api/auth/personal-tokens is the contract, pinned by the boundary test.
  • Frontend deltas — the merged Agent access pane stands.
  • The eight-tool surface — the build trio (list_work/get_work_item/dispatch) returns later as agent-tagged routes on build's own router, the extension-contribution seam's first proof.

Known semantic, flagged not changed

Two concurrent different answers to the same gate round can both report answered; DBOS's idempotency key keeps only the first payload. Pre-existing and shared with the Slack notification path — a fix belongs to the resume contract, not this surface.

@czpython czpython changed the title Serve the agent surface at /mcp behind PAT bearer auth Derive the /mcp agent endpoint from the agent-tagged routes Jul 20, 2026
@czpython
czpython marked this pull request as ready for review July 20, 2026 16:21
The gateway's five operations live at their nouns — /api/gates/{run_id}
(+/answer), /api/agent-calls/{call_id}, /api/runs/{run_id}/cancel beside
resume, /api/usage/summary — tagged "agent": the tag, not a URL umbrella,
marks the agent surface, and the route docstrings carry the tool
descriptions.

/mcp serves tools derived from those routes (FastMCP's OpenAPIProvider
over an in-process ASGI client), so a route is an operation's single
declaration — schema, docstring, operation_id — and a tagged extension
route joins tools/list by declaration alone. mcp_app mounts at exactly
/mcp (a bare Route; JSON 404 owns the subpaths) with its lifespan
composed through combine_lifespans.

PAT bearer gates the transport; CallerPat re-stamps each caller's bearer
onto the internal route call (the derivation strips authorization when
replaying headers), so every route runs as the presenting account. The
transport is built with raise_app_exceptions=False: failures reach the
tool as the app's sanitized HTTP responses — the domain taxonomy
{code, message, retryable} travels, validation errors carry
VALIDATION_ERROR.

Caddy serves /mcp on both listeners ahead of the identity gate (a bearer
client can't do interactive SSO); the public integrations host is the
canonical address. read_slice stays a plain byte window — the one stale
seam expectation aligns to it. docs/connect-your-agent.md covers the five
tools, the parkedAt echo contract, and both CLIs' configuration.
@czpython
czpython merged commit a38ed44 into main Jul 20, 2026
1 check passed
@czpython
czpython deleted the eng/mcp-endpoint branch July 20, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant