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.
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:
Catalog
tool_approval_overrides[upstream]Server-advertised
_meta.requires_approvalon the tool (where the server uses Squirro’s MCP convention)Server-level
requires_approvalHard 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 servers with an oauth auth block:
The catalog endpoint (
GET /v1/projects/{pid}/catalog) advertises the server withrequires_oauth: true,authenticated: false, andauthorize_url: /v1/external_mcp/authorize?server_id=<id>.The FE redirects the user’s browser to
authorize_url.The platform locates the authorization server, through the MCP server’s protected-resource metadata (RFC 9728) when the server publishes it. It then resolves the pre-registered client if the resource names one, or registers a client dynamically if not, runs PKCE, and redirects to the IdP. When the MCP server publishes protected-resource metadata, the platform requests tokens for that server specifically, using the RFC 8707
resourceparameter. If the platform finds no authorization server metadata that it can verify, the authorize request fails with HTTP 502.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).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.
Changing SQ_GENAI_V1_EXTERNAL_MCP_REDIRECT_URI does not disconnect anyone. The next user to authorize through the new URL gets a newly registered client, and connections made through the old URL keep renewing against the client they were issued to.
On Squirro Self-Service, connectors whose auth block names a client secret can share one redirect URI across all instances, so you register a single URL with each provider instead of the callback of every instance. Those connectors then send https://start.squirro.com/oauth2_middleman to the provider, and the sign-in returns to the instance it started from. Connectors that register their own client keep using the callback of the instance. To set up the shared redirect URI on an instance:
Register
https://start.squirro.com/oauth2_middlemanas a redirect URI with each provider, for example in the Google OAuth client or the Salesforce External Client App. Keep the callback of the instance registered as well until every instance uses the shared URI.Add
SQ_GENAI_V1_EXTERNAL_MCP_OAUTH_MIDDLEMAN_URL=https://start.squirro.com/oauth2_middlemanto thedocker-override.envfile. For the step-by-step procedure, see the genai.ini page.Restart the GenAI service with
sudo systemctl restart sqgenaid.
Register the URI before you set the variable. Otherwise, the provider rejects new connections with a redirect URI mismatch. Connections that already exist keep working and do not need to be authorized again. The variable applies to every connector with a client secret on the instance, so if a customer brings their own provider app on a Squirro Self-Service instance, that app must also register the shared URI. Otherwise, leave the variable unset on that instance. Leave it unset on deployments that are not Squirro Self-Service instances, such as on-premises installations, because the shared URI only returns sign-ins to Squirro Self-Service instances.
Tokens are renewed once, at the start of a turn. A token due to expire within five minutes is refreshed against the stored refresh token before any tool runs, so a user whose provider issues short-lived tokens is not sent back for consent every hour. There is no background refresh job and nothing refreshes mid-flight: a token that dies during a turn fails that tool call, and is renewed at the start of the next one.
A grant the user has revoked at the provider, or one the provider
has expired, cannot be renewed. That server is left out of the
turn, the catalog reports it as authenticated: false with an
authorize_url, and the user reconnects through the same flow.
A provider that is merely unreachable is reported the same way,
which means the connect button is the only thing on offer while an
outage lasts, though the platform retries rather than discarding
the credential and backs off for a minute between attempts.
Resetting a Registered Client#
If a provider stops accepting a client the platform registered, for example because the client was deleted at the provider, the provider shows an error on its own sign-in page and does not return the user to your Squirro instance. Signing in again does not help, because every attempt presents the same client.
A project administrator can remove the registered clients for one server with DELETE /v1/external_mcp/clients/{project_id}/{server_id}. The next sign-in registers a new client. Each user of that server keeps their current token until it expires and then signs in again. The endpoint does not affect a pre-registered client named by secret, which you rotate in the secret store instead. It returns 204 on success and 404 when the server has no registered client.
Going further#
Plugin authors writing tools exposed through
/studio-mcp(the internal aggregator) consult the V1 internal developer guidestudio-mcp-developers-guide.mdunder the genai service docs.Resource schemas + the picker payload shape are documented in the genai_config reference under the V1
MCPandStudioMcpConfigkinds.