V1 Catalog MCP Servers (preview)#

The V1 agent runtime consumes MCP servers as catalog-authored resources rather than per-agent tool configurations. This page is the operator reference for authoring those resources.

Note

This is the V1 surface. The legacy V0 MCP Server Tools configuration (see MCP Server Tools) is unaffected and continues to work for agents that don’t run on V1.

Authoring a server#

Each MCP server is a MCP resource in the project’s genai_config workspace. Three authoring shapes via the auth_mode field:

anonymous

Server is open or trusts the cluster network. No credentials.

kind: MCP
schema: v1
metadata:
  id: open-search
title: Open Search
url: https://mcp.example.com/sse
transport: sse
requires_approval: true
static_bearer

Single shared bearer token from the tenant’s secret store (EnvSecretResolver reads SQ_<HANDLE_UPPER> by convention; other resolvers are pluggable).

kind: MCP
schema: v1
metadata:
  id: company-kb
title: Company KB
url: https://mcp.example.com/kb
transport: streamable_http
auth_mode: static_bearer
bearer_handle: company_kb_token
requires_approval: false
per_user_oauth

Each end user authorises the server out-of-band through OAuth 2.1 with PKCE. The platform stores per-user access tokens Fernet-encrypted at rest, keyed on (tenant, project_id, user, server_id) — since server_id is project-local, the credential scope is too (two projects authoring id="linear" against different upstreams are independent; each prompts the user once). The platform’s OAuth client is registered dynamically (RFC 7591) on each user’s first authorize.

kind: MCP
schema: v1
metadata:
  id: linear
title: Linear
summary: Linear issue tracker
url: https://mcp.linear.app/sse
transport: sse
auth_mode: per_user_oauth
requires_approval: true
tool_approval_overrides:
  search_issues: false  # opt this one tool out of approval

Activating the surface#

The capability toggle capability.mcp.enabled gates the entire MCP surface. The default is auto — which resolves to True when at least one MCP resource is authored, False otherwise.

Explicit false (compliance lock) hides every server even when resources are present. Explicit true requires at least one authored resource (publish-time validator).

Approval policy#

Each server declares a default requires_approval (default true — fail-safe). Individual tools can opt out via tool_approval_overrides keyed on the upstream tool name (as the server’s tools/list advertises it, not the namespaced mcp_<id>__<tool> form). Precedence at dispatch time:

  1. Catalog tool_approval_overrides[upstream]

  2. Server-advertised _meta.requires_approval on the tool (where the server uses Squirro’s MCP convention)

  3. Server-level requires_approval

  4. Hard default True

Approval gating runs as one HITL prompt per turn carrying every pending tool call; the user approves / edits / rejects each one in one resume action.

User OAuth flow#

For per_user_oauth servers:

  1. The catalog endpoint (GET /v1/projects/{pid}/catalog) advertises the server with requires_oauth: true, authenticated: false, and authorize_url: /v1/external_mcp/authorize?server_id=<id>.

  2. The FE redirects the user’s browser to authorize_url.

  3. The platform discovers the OAuth metadata, runs PKCE, redirects to the IdP.

  4. The IdP redirects back to /v1/external_mcp/callback; the platform exchanges the code for tokens and stores them Fernet-encrypted under (tenant, project_id, user, server_id).

  5. The next catalog poll shows the server as authenticated: true. The agent can now dispatch tools through it.

Disconnecting an integration#

The FE drives disconnect through DELETE /v1/external_mcp/secrets/{project_id}/{server_id}. The endpoint targets the calling user’s own row keyed (tenant, project_id, user, server_id); there is no admin override that disconnects another user’s integration. project-read is the floor — a read-only project member can still disconnect their own integration.

The endpoint is local-delete only: it does not call the IdP’s token-revoke endpoint. The grant on the upstream lingers until the user revokes it from their IdP account, and tokens expire naturally. This matches the per-project scope (revoking at the IdP would invalidate any sibling project issued via the same client_id). Returns 204 on success, 404 when no row exists for the target.

The redirect URI the IdP echoes must be pinned in production via SQ_GENAI_V1_EXTERNAL_MCP_REDIRECT_URI. The value must exactly match the URL registered with the IdP (most IdPs reject token exchanges with mismatched redirect URIs). Without the env var, the platform falls back to deriving the URI from the incoming request’s X-Forwarded-Host / X-Forwarded-Proto headers — convenient for local dev behind an honest reverse proxy, but a security risk in any environment where those headers can be spoofed. The platform logs a warning whenever the fallback path is used.

Token refresh is not a runtime concern. An expired token surfaces as a structured auth_required tool error mid-turn; the user re-authorises through the same flow. The platform does not silently refresh.

Going further#

  • Plugin authors writing tools exposed through /studio-mcp (the internal aggregator) consult the V1 internal developer guide studio-mcp-developers-guide.md under the genai service docs.

  • Resource schemas + the picker payload shape are documented in the genai_config reference under the V1 MCP and StudioMcpConfig kinds.