feat: add user_oidc auth type for MCP servers (#24793)

Adds a 5th MCP server authentication mode, `user_oidc` ("User OIDC
Identity"), that forwards the calling user's OIDC access token from
`user_links.oauth_access_token` to the upstream MCP server as
`Authorization: Bearer <token>`.

The token is read from `user_links` and refreshed transparently via
`oauth2.TokenSource` before each MCP request. No new per-MCP-server
secret storage and no per-user connect/disconnect step.

**Limitation**: only users who logged in via OIDC have a forwardable
token. Users authenticated via password or GitHub will see requests sent
without an `Authorization` header, and the upstream MCP server is
expected to respond with 401. A pluggable token source (e.g. CLI-minted
E2E tokens) is left as future work.

<details>
<summary>Implementation notes</summary>

- Schema: new
`coderd/database/migrations/000481_mcp_user_oidc_auth.{up,down}.sql`
relaxes the `mcp_server_configs.auth_type` CHECK constraint to include
`user_oidc`. Down migration deletes affected rows before restoring the
old constraint.
- SDK validation: `codersdk/mcp.go` extends `oneof` for
`CreateMCPServerConfigRequest` and `UpdateMCPServerConfigRequest`.
- Handler: `coderd/mcp.go` adds `case "user_oidc":` to the
field-clearing switch on update. The existing list and detail handlers
already report `auth_connected = true` for any non-`oauth2` auth type.
- Header construction: `coderd/x/chatd/mcpclient/mcpclient.go`
introduces a `UserOIDCTokenSource` interface and adds the `user_oidc`
case to `buildAuthHeaders`. `ConnectAll` / `connectOne` /
`buildAuthHeaders` gain `userID uuid.UUID, oidcSrc UserOIDCTokenSource`
parameters.
- Wiring: `coderd/x/chatd/chatd.go` adds `OIDCTokenSource` to `Config` /
`Server` and passes `chat.OwnerID` plus the source through `ConnectAll`.
`coderd/coderd.go` constructs the source next to the `chatd.New` call
when `options.OIDCConfig` is non-nil.
- Token source: `oidcMCPTokenSource` lives in `coderd/mcp.go`. It reads
the user's OIDC link, refreshes via `oauth2.TokenSource`, and writes the
refreshed token back to `user_links`. Logic is duplicated from
`provisionerdserver.ObtainOIDCAccessToken` to avoid an MCP ->
provisionerdserver dependency. The two copies must be kept in sync; a
comment on `oidcMCPTokenSource` records this.
- Frontend: `MCPServerAdminPanel.tsx` adds the new dropdown option, an
explanatory helper block (no admin-configurable fields), and a Storybook
story (`CreateServerUserOIDC`).
- Tests:
- `mcpclient_test.go`: `TestConnectAll_UserOIDCAuth`,
`TestConnectAll_UserOIDCAuth_NoLink`,
`TestConnectAll_UserOIDCAuth_NilSource`. All existing tests updated for
the new signature.
- `mcp_test.go`: extends `TestMCPServerConfigsAuthConnected` to assert
`auth_connected=true` for `user_oidc`; adds
`TestMCPServerConfigsUserOIDCClearsFields` and
`TestMCPServerConfigsUserOIDCDirect`.
- Docs: `docs/ai-coder/agents/platform-controls/mcp-servers.md`
describes the new mode and its OIDC-only limitation.

</details>

This PR was created by Coder Agents.

---------

Co-authored-by: Coder Agents <agents@coder.com>
This commit is contained in:
Kyle Carberry
2026-05-03 11:31:48 -04:00
committed by GitHub
co-authored by Coder Agents
parent 6b9637d85a
commit d889ba1842
15 changed files with 802 additions and 36 deletions
+9 -1
View File
@@ -162,6 +162,7 @@ type Server struct {
pubsub pubsub.Pubsub
webpushDispatcher webpush.Dispatcher
providerAPIKeys chatprovider.ProviderAPIKeys
oidcTokenSource mcpclient.UserOIDCTokenSource
debugSvc *chatdebug.Service
debugSvcFactory func() *chatdebug.Service
debugSvcReady atomic.Bool
@@ -3840,6 +3841,12 @@ type Config struct {
UsageTracker *workspacestats.UsageTracker
Clock quartz.Clock
PrometheusRegistry prometheus.Registerer
// OIDCTokenSource resolves the calling user's OIDC access
// token for MCP servers configured with auth_type=user_oidc.
// May be nil if the deployment has no OIDC provider; servers
// using user_oidc will then send no Authorization header.
OIDCTokenSource mcpclient.UserOIDCTokenSource
}
// New creates a new chat processor. The processor polls for pending
@@ -3898,6 +3905,7 @@ func New(cfg Config) *Server {
pubsub: cfg.Pubsub,
webpushDispatcher: cfg.WebpushDispatcher,
providerAPIKeys: cfg.ProviderAPIKeys,
oidcTokenSource: cfg.OIDCTokenSource,
debugSvcFactory: func() *chatdebug.Service {
debugSvc := chatdebug.NewService(
cfg.Database,
@@ -6472,7 +6480,7 @@ func (p *Server) runChat(
// Refresh expired OAuth2 tokens before connecting.
mcpTokens = p.refreshExpiredMCPTokens(ctx, logger, mcpConnectConfigs, mcpTokens)
mcpTools, mcpCleanup = mcpclient.ConnectAll(
ctx, logger, mcpConnectConfigs, mcpTokens,
ctx, logger, mcpConnectConfigs, mcpTokens, chat.OwnerID, p.oidcTokenSource,
)
return nil
})
+54 -2
View File
@@ -49,6 +49,18 @@ const connectTimeout = 10 * time.Second
// take before being canceled.
const toolCallTimeout = 60 * time.Second
// UserOIDCTokenSource resolves the OIDC access token for the calling
// user. Implementations attempt to refresh tokens that are expired
// or close to expiring and MUST return ("", nil) when the user has
// no OIDC link or a refresh attempt failed for any reason. A
// non-nil error is reserved for unexpected infrastructure failures
// (e.g. database errors) and skips header construction entirely.
// The empty-token-on-refresh-failure behavior matches
// provisionerdserver.ObtainOIDCAccessToken.
type UserOIDCTokenSource interface {
OIDCAccessToken(ctx context.Context, userID uuid.UUID) (string, error)
}
// ConnectAll connects to all configured MCP servers, discovers
// their tools, and returns them as fantasy.AgentTool values.
// Tools are sorted by their prefixed name so callers
@@ -60,6 +72,8 @@ func ConnectAll(
logger slog.Logger,
configs []database.MCPServerConfig,
tokens []database.MCPServerUserToken,
userID uuid.UUID,
oidcSrc UserOIDCTokenSource,
) ([]fantasy.AgentTool, func()) {
// Index tokens by server config ID so auth header
// construction is O(1) per server.
@@ -95,7 +109,7 @@ func ConnectAll(
eg.Go(func() error {
serverTools, mcpClient, connectErr := connectOne(
ctx, logger, cfg, tokensByConfigID,
ctx, logger, cfg, tokensByConfigID, userID, oidcSrc,
)
if connectErr != nil {
logger.Warn(ctx,
@@ -159,8 +173,10 @@ func connectOne(
logger slog.Logger,
cfg database.MCPServerConfig,
tokensByConfigID map[uuid.UUID]database.MCPServerUserToken,
userID uuid.UUID,
oidcSrc UserOIDCTokenSource,
) ([]fantasy.AgentTool, *client.Client, error) {
headers := buildAuthHeaders(ctx, logger, cfg, tokensByConfigID)
headers := buildAuthHeaders(ctx, logger, cfg, tokensByConfigID, userID, oidcSrc)
tr, err := createTransport(cfg, headers)
if err != nil {
@@ -285,6 +301,8 @@ func buildAuthHeaders(
logger slog.Logger,
cfg database.MCPServerConfig,
tokensByConfigID map[uuid.UUID]database.MCPServerUserToken,
userID uuid.UUID,
oidcSrc UserOIDCTokenSource,
) map[string]string {
// Using map[string]string rather than http.Header because
// the mcp-go transport options accept map[string]string.
@@ -347,6 +365,40 @@ func buildAuthHeaders(
}
}
}
case "user_oidc":
// Forward the calling user's OIDC access token from
// user_links as Authorization: Bearer <token>. The token
// source is responsible for refreshing tokens that are
// expired or close to expiring before returning them.
if oidcSrc == nil || userID == uuid.Nil {
logger.Warn(ctx,
"user_oidc auth requested but no token source available",
slog.F("server_slug", cfg.Slug),
)
break
}
token, err := oidcSrc.OIDCAccessToken(ctx, userID)
if err != nil {
logger.Warn(ctx,
"failed to obtain user OIDC token for MCP server",
slog.F("server_slug", cfg.Slug),
slog.Error(err),
)
break
}
if token == "" {
// The user has no OIDC link, or a non-fatal refresh
// failure occurred. Fall through with no header and let
// the upstream MCP server decide how to respond
// (typically 401). Logged at debug so password and
// GitHub users don't generate noise for every chat turn.
logger.Debug(ctx,
"no user OIDC token available for MCP server",
slog.F("server_slug", cfg.Slug),
)
break
}
headers["Authorization"] = "Bearer " + token
case "none", "":
// No auth headers needed.
}
+185 -22
View File
@@ -96,7 +96,7 @@ func TestConnectAll_DiscoverTools(t *testing.T) {
ts := newTestMCPServer(t, echoTool(), greetTool())
cfg := makeConfig("myserver", ts.URL)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
// Two tools should be discovered, namespaced with the server slug.
@@ -121,7 +121,7 @@ func TestConnectAll_CallTool(t *testing.T) {
ts := newTestMCPServer(t, echoTool())
cfg := makeConfig("srv", ts.URL)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -147,7 +147,7 @@ func TestConnectAll_ToolAllowList(t *testing.T) {
// Only allow the "echo" tool.
cfg.ToolAllowList = []string{"echo"}
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -165,7 +165,7 @@ func TestConnectAll_ToolDenyList(t *testing.T) {
// Deny the "greet" tool, so only "echo" remains.
cfg.ToolDenyList = []string{"greet"}
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -179,7 +179,7 @@ func TestConnectAll_ConnectionFailure(t *testing.T) {
cfg := makeConfig("bad", "http://127.0.0.1:0/does-not-exist")
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
assert.Empty(t, tools, "no tools should be returned for an unreachable server")
@@ -200,6 +200,7 @@ func TestConnectAll_MultipleServers(t *testing.T) {
ctx, logger,
[]database.MCPServerConfig{cfg1, cfg2},
nil,
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -225,6 +226,7 @@ func TestConnectAll_NoToolsAfterFiltering(t *testing.T) {
logger,
[]database.MCPServerConfig{cfg},
nil,
uuid.Nil, nil,
)
require.Empty(t, tools)
@@ -252,6 +254,7 @@ func TestConnectAll_DeterministicOrder(t *testing.T) {
makeConfig("srv2", ts2.URL),
},
nil,
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -280,6 +283,7 @@ func TestConnectAll_DeterministicOrder(t *testing.T) {
makeConfig("aaa", other.URL),
},
nil,
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -312,6 +316,7 @@ func TestConnectAll_DeterministicOrder(t *testing.T) {
logger,
[]database.MCPServerConfig{cfg1, cfg2},
nil,
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -375,6 +380,7 @@ func TestConnectAll_AuthHeaders(t *testing.T) {
ctx, logger,
[]database.MCPServerConfig{cfg},
[]database.MCPServerUserToken{token},
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -429,7 +435,7 @@ func TestConnectAll_DisabledServer(t *testing.T) {
cfg := makeConfig("disabled", ts.URL)
cfg.Enabled = false
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
assert.Empty(t, tools)
}
@@ -444,7 +450,7 @@ func TestConnectAll_CallToolInvalidInput(t *testing.T) {
ts := newTestMCPServer(t, echoTool())
cfg := makeConfig("srv", ts.URL)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -469,7 +475,7 @@ func TestConnectAll_ToolInfoParameters(t *testing.T) {
ts := newTestMCPServer(t, echoTool())
cfg := makeConfig("srv", ts.URL)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -511,7 +517,7 @@ func TestConnectAll_NilRequiredBecomesEmptySlice(t *testing.T) {
ts := newTestMCPServer(t, noRequiredTool)
cfg := makeConfig("srv", ts.URL)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -563,6 +569,7 @@ func TestConnectAll_APIKeyAuth(t *testing.T) {
tools, cleanup := mcpclient.ConnectAll(
ctx, logger, []database.MCPServerConfig{cfg}, nil,
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -619,6 +626,7 @@ func TestConnectAll_CustomHeadersAuth(t *testing.T) {
tools, cleanup := mcpclient.ConnectAll(
ctx, logger, []database.MCPServerConfig{cfg}, nil,
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -655,6 +663,7 @@ func TestConnectAll_CustomHeadersInvalidJSON(t *testing.T) {
tools, cleanup := mcpclient.ConnectAll(
ctx, logger, []database.MCPServerConfig{cfg}, nil,
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -664,6 +673,158 @@ func TestConnectAll_CustomHeadersInvalidJSON(t *testing.T) {
assert.Equal(t, "badjson__echo", tools[0].Info().Name)
}
// staticOIDCSource implements mcpclient.UserOIDCTokenSource for tests
// without requiring a real OIDC provider or database round-trip.
type staticOIDCSource struct {
token string
err error
}
func (s staticOIDCSource) OIDCAccessToken(_ context.Context, _ uuid.UUID) (string, error) {
return s.token, s.err
}
// TestConnectAll_UserOIDCAuth verifies that the user_oidc auth type
// forwards the calling user's OIDC access token from the
// UserOIDCTokenSource as Authorization: Bearer <token>.
func TestConnectAll_UserOIDCAuth(t *testing.T) {
t.Parallel()
ctx := context.Background()
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
var (
mu sync.Mutex
seenHeaders []string
)
srv := mcpserver.NewMCPServer("oidc-server", "1.0.0")
srv.AddTools(mcpserver.ServerTool{
Tool: mcp.NewTool("whoami",
mcp.WithDescription("Returns the auth header"),
),
Handler: func(_ context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
auth := req.Header.Get("Authorization")
mu.Lock()
seenHeaders = append(seenHeaders, auth)
mu.Unlock()
return mcp.NewToolResultText("auth:" + auth), nil
},
})
httpSrv := mcpserver.NewStreamableHTTPServer(srv)
ts := httptest.NewServer(httpSrv)
t.Cleanup(ts.Close)
cfg := makeConfig("oidc-srv", ts.URL)
cfg.AuthType = "user_oidc"
userID := uuid.New()
src := staticOIDCSource{token: "fake-oidc-token"}
tools, cleanup := mcpclient.ConnectAll(
ctx, logger, []database.MCPServerConfig{cfg}, nil,
userID, src,
)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
resp, err := tools[0].Run(ctx, fantasy.ToolCall{
ID: "call-oidc",
Name: "oidc-srv__whoami",
Input: "{}",
})
require.NoError(t, err)
assert.False(t, resp.IsError)
assert.Equal(t, "auth:Bearer fake-oidc-token", resp.Content)
mu.Lock()
defer mu.Unlock()
require.NotEmpty(t, seenHeaders)
assert.Equal(t, "Bearer fake-oidc-token", seenHeaders[len(seenHeaders)-1])
}
// TestConnectAll_UserOIDCAuth_NoLink verifies that when the token
// source returns ("", nil) (the user has no OIDC link), the request
// is still made but with no Authorization header. The MCP server is
// then free to respond with 401 or proceed unauthenticated.
func TestConnectAll_UserOIDCAuth_NoLink(t *testing.T) {
t.Parallel()
ctx := context.Background()
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
var (
mu sync.Mutex
seenHeaders []string
)
srv := mcpserver.NewMCPServer("oidc-server-nolink", "1.0.0")
srv.AddTools(mcpserver.ServerTool{
Tool: mcp.NewTool("whoami",
mcp.WithDescription("Returns the auth header"),
),
Handler: func(_ context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
auth := req.Header.Get("Authorization")
mu.Lock()
seenHeaders = append(seenHeaders, auth)
mu.Unlock()
return mcp.NewToolResultText("auth:" + auth), nil
},
})
httpSrv := mcpserver.NewStreamableHTTPServer(srv)
ts := httptest.NewServer(httpSrv)
t.Cleanup(ts.Close)
cfg := makeConfig("oidc-nolink", ts.URL)
cfg.AuthType = "user_oidc"
src := staticOIDCSource{token: "", err: nil}
tools, cleanup := mcpclient.ConnectAll(
ctx, logger, []database.MCPServerConfig{cfg}, nil,
uuid.New(), src,
)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
resp, err := tools[0].Run(ctx, fantasy.ToolCall{
ID: "call-oidc-nolink",
Name: "oidc-nolink__whoami",
Input: "{}",
})
require.NoError(t, err)
assert.False(t, resp.IsError)
assert.Equal(t, "auth:", resp.Content)
mu.Lock()
defer mu.Unlock()
require.NotEmpty(t, seenHeaders)
assert.Empty(t, seenHeaders[len(seenHeaders)-1])
}
// TestConnectAll_UserOIDCAuth_NilSource verifies that a nil token
// source (e.g. deployment with no OIDC provider) yields no
// Authorization header rather than panicking.
func TestConnectAll_UserOIDCAuth_NilSource(t *testing.T) {
t.Parallel()
ctx := context.Background()
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
ts := newTestMCPServer(t, echoTool())
cfg := makeConfig("oidc-nilsrc", ts.URL)
cfg.AuthType = "user_oidc"
tools, cleanup := mcpclient.ConnectAll(
ctx, logger, []database.MCPServerConfig{cfg}, nil,
uuid.New(), nil,
)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
assert.Equal(t, "oidc-nilsrc__echo", tools[0].Info().Name)
}
// TestConnectAll_ParallelConnections verifies that connecting to
// multiple MCP servers simultaneously returns all discovered
// tools with the correct server slug prefixes.
@@ -684,6 +845,7 @@ func TestConnectAll_ParallelConnections(t *testing.T) {
ctx, logger,
[]database.MCPServerConfig{cfg1, cfg2, cfg3},
nil,
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -744,7 +906,7 @@ func TestConnectAll_ExpiredToken(t *testing.T) {
Expiry: sql.NullTime{Time: time.Now().Add(-1 * time.Hour), Valid: true},
}
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, []database.MCPServerUserToken{token})
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, []database.MCPServerUserToken{token}, uuid.Nil, nil)
t.Cleanup(cleanup)
// The server accepts any auth, so the tool is still discovered
@@ -777,7 +939,7 @@ func TestConnectAll_EmptyAccessToken(t *testing.T) {
TokenType: "Bearer",
}
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, []database.MCPServerUserToken{token})
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, []database.MCPServerUserToken{token}, uuid.Nil, nil)
t.Cleanup(cleanup)
// Tool is still discovered (server doesn't require auth), but
@@ -807,7 +969,7 @@ func TestConnectAll_MCPToolIdentifier(t *testing.T) {
Enabled: true,
}
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -853,6 +1015,7 @@ func TestConnectAll_MCPToolIdentifier_MultipleServers(t *testing.T) {
ctx, logger,
[]database.MCPServerConfig{cfg1, cfg2},
nil,
uuid.Nil, nil,
)
t.Cleanup(cleanup)
@@ -909,7 +1072,7 @@ func TestConnectAll_EmbeddedResourceText(t *testing.T) {
t.Cleanup(ts.Close)
cfg := makeConfig("embed-txt", ts.URL)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -976,7 +1139,7 @@ func TestConnectAll_EmbeddedResourceBlob(t *testing.T) {
t.Cleanup(ts.Close)
cfg := makeConfig("embed-blob", ts.URL)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -1056,7 +1219,7 @@ func TestConnectAll_ResourceLink(t *testing.T) {
t.Cleanup(ts.Close)
cfg := makeConfig("res-link", ts.URL)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -1100,7 +1263,7 @@ func TestConnectAll_CallToolError(t *testing.T) {
t.Cleanup(ts.Close)
cfg := makeConfig("err-srv", ts.URL)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -1124,7 +1287,7 @@ func TestModelIntent_Info_WrapsSchema(t *testing.T) {
cfg := makeConfig("intent-srv", ts.URL)
cfg.ModelIntent = true
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -1160,7 +1323,7 @@ func TestModelIntent_Info_NoWrapWhenDisabled(t *testing.T) {
cfg := makeConfig("no-intent", ts.URL)
cfg.ModelIntent = false
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -1183,7 +1346,7 @@ func TestModelIntent_Run_UnwrapsProperties(t *testing.T) {
cfg := makeConfig("unwrap-srv", ts.URL)
cfg.ModelIntent = true
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -1208,7 +1371,7 @@ func TestModelIntent_Run_UnwrapsFlat(t *testing.T) {
cfg := makeConfig("flat-srv", ts.URL)
cfg.ModelIntent = true
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -1233,7 +1396,7 @@ func TestModelIntent_Run_PassthroughWhenDisabled(t *testing.T) {
cfg := makeConfig("pass-srv", ts.URL)
cfg.ModelIntent = false
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)
@@ -1258,7 +1421,7 @@ func TestModelIntent_Run_FallbackOnBadJSON(t *testing.T) {
cfg := makeConfig("bad-srv", ts.URL)
cfg.ModelIntent = true
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil)
tools, cleanup := mcpclient.ConnectAll(ctx, logger, []database.MCPServerConfig{cfg}, nil, uuid.Nil, nil)
t.Cleanup(cleanup)
require.Len(t, tools, 1)