Skip to main content
Pairs with: Governance REST API and Governance CLI: all three surfaces dispatch through the same service-layer functions, with audit emission tagged metadata.surface = "trpc" | "hono" | "cli" | "mcp" for incident response. Distinct from LangWatch’s general MCP server, this is a governance-namespaced toolset specifically for setting up and querying governance state.
Architecture: in-process service calls (not HTTP-shell over REST). Governance MCP tools register in platform/app/src/mcp/handler.ts via registerGovernanceMcpTools(server, prisma) and call the governance services (IngestionTemplateService, IngestionKeyService, etc.) directly with surface: "mcp". They do NOT route through /api/governance/* over HTTP. Reason: it avoids the project-API-key vs PAT auth mismatch and matches the explicit GovernanceCallSurface contract at fc6d54100 without needing Hono middleware changes. Cross-path uniformity (audit, RBAC, multitenancy) still holds by construction, the service layer is the single source of truth, and metadata.surface distinguishes the entrypoint.
RBAC enforced at the tool layer. Per specs/ai-gateway/governance/governance-api-cli-mcp-coverage.feature, write tools and admin reads resolve the caller’s user session to its RBAC scopes and reject with FORBIDDEN (or AUTH_REQUIRED when no user is bound) if the required permission is missing; the plain list/get read tools are available to a project API key without a user.No tool can bypass the service-layer or the multitenancy middleware, the same defense-in-depth that protects the REST API protects the MCP surface, even though MCP doesn’t go through the REST surface.

Why an MCP server for governance

Model Context Protocol (MCP) is the standard way for agents (Claude Code, Cursor, Codex, Claude desktop, custom MCP clients) to discover and call tools provided by a service. By exposing governance as MCP tools, rather than expecting agents to compose REST calls themselves, we give the agent:
  • Tool discovery: tools/list enumerates every governance verb with descriptions and input schemas the agent can reason about.
  • Type-safe inputs: the same Zod schemas the REST API and CLI consume are exposed via MCP inputSchema, so the agent gets validation feedback before the tool call leaves the client.
  • Built-in RBAC + audit: every successful tool call lands in the same audit log as a dashboard click, tagged with surface="mcp" for incident response.
The MCP server is the most agent-native of the three governance surfaces. For self-hosted onboarding you can drop the MCP server into Claude Code, point the agent at a fresh LangWatch deployment, and walk it through setting up the org via a chat conversation.

Tool naming

Every governance MCP tool’s name starts with governance_:
The pattern is governance_<resource_with_underscores>_<verb_with_underscores>, MCP tool names by convention use snake_case while the REST API uses kebab-case. Description text on every tool summarises the verb + resource + RBAC scope so the agent can quickly decide whether to use it.

Authentication

Governance MCP tools register inside LangWatch’s existing MCP server (served on the same host that serves the dashboard), there is no separate langwatch mcp serve binary. Clients connect over the Streamable HTTP transport at /mcp, and authenticate with one of:
  • Project API key as Bearer (Authorization: Bearer <projectApiKey>), the plain read tools work immediately. Sufficient to call governance_*_list and governance_*_get. Admin reads (governance_*_admin_list, which expose ottlRules) and every write tool require a user-bound session and reject a project-key-only caller.
  • OAuth (PKCE) flow at /mcp/authorize → /oauth/token, required for write tools and admin reads. The OAuth flow captures the user’s id at authorize time, threads it through the session token cache, and surfaces it to governance tools as ctx.callerUserId. Without it, a write tool returns an AUTH_REQUIRED: error pointing the client at the OAuth flow.
The 9 governance tools register via registerGovernanceMcpTools(server, ctx) from enterprise/modules/governance/process/src/transport/api-mcp/governance-tools.api.ts. The registrar is installed once, as the session-tool port that apps/api/src/features/mcp/hosted-mcp.mount.ts binds, and it runs for every session the endpoint opens, so the tools are available regardless of which MCP transport the client chose.
Dev-environment port routing: in production, LangWatch listens on a single port and the /mcp
  • /oauth/* + /.well-known/oauth-* routes are all reachable on the same host. In dev (Vite), external MCP clients (e.g. Claude Code) hit FRONTEND_PORT (default 5560) which proxies the MCP routes through to the API port.
Local Claude Code runbook: claude mcp add langwatch http://localhost:5560/mcp (or the OAuth flow at http://localhost:5560/mcp/authorize for write tools). All 9 tools register over that proxy, read tools return the catalog with a project apiKey Bearer, and write tools return AUTH_REQUIRED: until the OAuth flow completes.

RBAC enforcement

Governance tools enforce two distinct gates at the tool layer (enterprise/modules/governance/process/src/transport/api-mcp/governance-tools.api.ts), BEFORE the service call: AUTH_REQUIRED:, write tools (e.g. governance_ingestion_templates_create, governance_ingestion_keys_mint) called from a project-apiKey-only session without OAuth. Read tools are NOT subject to this gate; they work with project-apiKey-only sessions.
FORBIDDEN:, the caller has a session but their organization role doesn’t include the required permission (the same organization-permission decision the tRPC and REST surfaces make, AuthzService.hasPermission in modules/authz/contract/src/authz.service.ts).
In both cases no record is created and no audit row is emitted (failed calls are not state-changes by design). The agent’s harness can decide whether to escalate (ask the human to complete the OAuth flow, use a more-privileged apiKey) or back off. The Path B mechanics (per fc6d54100 + 7639b6c2b):
  • RBAC check happens at the MCP tool layer, BEFORE the service call, the tool asks the same permission probe the tRPC and REST surfaces ask, AuthzService.hasPermission (modules/authz/contract/src/authz.service.ts, resolved against the built-in role sets in modules/authz/contract/src/roles.ts), and returns FORBIDDEN: early if the required permission is missing. Services trust the surface, they do not gate on the MCP-resolved scope a second time. This split is intentional: the MCP tool owns the RBAC contract for its surface; the service owns the multitenancy + business-logic contract.
  • callerUserId flows from the OAuth session, the OAuth PKCE flow at /mcp/authorize captures the user’s id, threads it through the session token cache and SessionState, and surfaces it as ctx.callerUserId. For IngestionKeyService.{install, rotate} it’s required (write tools reject with AUTH_REQUIRED: if absent). For IngestionTemplateService authoring methods (which take callerUserId for the audit trail), same plumbing.
  • Snake_case ↔ camelCase boundary: MCP tool inputs use snake_case (source_template_id, template_id) per public-surface convention; the tool maps to camelCase (sourceTemplateId, templateId) before calling the service, since service signatures are camelCase. The mapping is the tool’s job, not the service’s.

Audit emission

Every state-changing tool call emits an audit row with AuditLog.metadata.surface = "mcp" (the shared GovernanceCallSurface type at @ee/governance/services/auditSurface.ts, threaded through every mutating governance service method as of Lane B-3 at fc6d54100): Payload shapes are identical to the REST API’s audit rows apart from metadata.surface, every other field (actorUserId, apiKeyId, target, error) follows the same shape. This lets incident response queries answer “what did the agent do?” without changing the audit-row event-kind taxonomy. The contract is asserted at the application layer by enterprise/modules/governance/process/src/app/__tests__/governance.app.unit.test.ts, in the scenario “audit rows are identical apart from metadata.surface”: the same template creation is driven over all four surfaces, tRPC, Hono REST, CLI and MCP, and the four writes come out field-for-field identical apart from surface. No wire-level MCP test is needed (per the Path B architecture there is no MCP-over-HTTP wire to assert at). The AUTH_REQUIRED: negative path, a write tool with no callerUserId failing before any audit row is written, is contract rather than covered ground: it carries no test of its own today.

Agent setup from scratch

An agent connecting through the OAuth flow as an admin can fully configure governance through a sequence of MCP tool calls (the write tools below reject a project-key-only caller):
  1. governance_ingestion_templates_list, read existing platform templates
  2. governance_ingestion_templates_clone_from_platform, clone claude_code into the org (delegates to POST /api/v1/governance/ingestion-templates/clone)
  3. governance_anomaly_rules_create, rule on org spend > $100/day
  4. governance_role_bindings_assign_to_user, give a user governance:view
  5. governance_audit_log_query, verify steps 2 to 4 all emitted audit rows
If every step succeeds and step 5 returns the matching audit rows, an agent can run the whole setup without a single UI click.

Cross-references

Last modified on October 14, 2026