Repository navigation
Add external MCP servers as mcp connectors with per-user OAuth - #10009
Conversation
… database, resolve templated server URLs
…nuation, per-user tool catalog, shared prompt fragment
…connector access before resolving config
… configuration, check tool access against the current connector
…uperseded OAuth clients, expose authorization separately from availability - A tool call now refuses to send the session's hosted token to a handle whose server URL differs from the one the token was granted for, and discovery takes the URL from the handle it lists tools with. - OAuth clients are never modified in place: a configuration change registers a new client next to the old one, so tokens and pending flows bound to the old client keep working. Unused clients older than the flow TTL are deleted. - MCPServer gains an authorized flag that does not depend on reachability; the External tools popover offers Disconnect whenever an authorization is stored, so a server that is down can still be disconnected.
dbf7204 to
c9ae140
Compare
nishantmonu51
left a comment
There was a problem hiding this comment.
Reviewed 61d9542. Three inline comments; the first one is the one to fix.
| if err != nil { | ||
| return nil, err | ||
| } | ||
| clientRow, err := s.admin.DB.FindConnectorOAuthClient(ctx, depl.ProjectID, req.Connector, req.ConfigHash) |
There was a problem hiding this comment.
Disconnect only removes the token for the current config_hash. Tokens granted before a URL, scope or client change stay stored, unrevoked and refreshable, and nothing sweeps them now that the unused-client job is gone, so a user who disconnects after such a change still has a live grant held by Rill. Delete and revoke the user's tokens for every client of (project, connector) here (the runtime passes only the current hash from runtime/server/mcp_servers.go:87), and consider dropping clients that hold no tokens and are no longer current.
| if err := json.Unmarshal(clientRow.Client, oauthClient); err != nil { | ||
| return fmt.Errorf("stored OAuth client is invalid: %w", err) | ||
| } | ||
| newTok, err := oauthClient.Refresh(ctx, tok, safehttp.NewHTTPClient(false, connectorOAuthHTTPTimeout)) |
There was a problem hiding this comment.
The refresh request to the authorization server (up to 15 s) runs while the advisory lock and its transaction are held, which the client-registration comment deliberately avoids for the same reason. With many tokens expiring at once this ties up database connections. If the lock scope is intentional, a comment saying so would help.
| tok, err := admin.GetConnectorOAuthToken(ctx, srv.Connector, srv.oauthConfigHash, userID, forceRefresh) | ||
| if err != nil { | ||
| if errors.Is(err, drivers.ErrNotImplemented) { | ||
| return "", errOAuthRequiresCloud |
There was a problem hiding this comment.
In Rill Developer this error lands in ExternalServer.Err, so the popover shows every auth: oauth connector with the red Error status and this message, as if it were misconfigured, while the docs describe such servers as listed but not connectable. A plain unauthorized state (no error) with the existing "Open this project in Rill Cloud" wording would match the docs.
…CP session Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds external MCP servers as a
driver: mcpconnector so AI agents can use their tools alongside the built-in Rill tools.runtime/drivers/mcpdriver: streamable HTTP client, project-scopedheaderswith{{ .env.* }}and{{ .user.* }}templating, per-user OAuth 2.1 (auth: oauth) with discovery, dynamic client registration, PKCE and refresh. Hosted runtimes refuse private networks and cross-origin redirects.securityblock with metrics-view semantics (access,include,exclude) evaluated by the existing security engine with tool names as fields, plusagentsto scope a server to specific agents.runtime/ai: per-user tool discovery, tools exposed asmcp__<connector>__<tool>, a per-message call cap, and a JSON "authorization required" error the chat UI turns into a Connect card. External tools are never re-exported through Rill's own MCP server.0098.sql, tablesmcp_oauth_clientsandmcp_oauth_tokens), serves/mcp/oauth/connectand/mcp/oauth/callback, and hands runtimes short-lived access tokens viaGetMCPOAuthToken. Rill Developer persists the local user's token under the projecttmpdirectory.driver: mcpconnector files.docs/docs/guide/ai/external-mcp.mdand the connector reference.Checklist: