Skip to content

Commit e7dff15

Browse files
committed
chore: allow coder MCP tools to not be injected
Signed-off-by: Danny Kopping <danny@coder.com>
1 parent e96ab0e commit e7dff15

File tree

13 files changed

+95
-29
lines changed

13 files changed

+95
-29
lines changed

cli/testdata/coder_server_--help.golden

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ AIBRIDGE OPTIONS:
109109
--aibridge-enabled bool, $CODER_AIBRIDGE_ENABLED (default: false)
110110
Whether to start an in-memory aibridged instance.
111111

112+
--aibridge-inject-coder-mcp-tools bool, $CODER_AIBRIDGE_INJECT_CODER_MCP (default: true)
113+
Whether to inject Coder's MCP tools into intercepted AI Bridge
114+
requests (requires the "oauth2" and "mcp-server-http" experiments to
115+
be enabled).
116+
112117
--aibridge-openai-base-url string, $CODER_AIBRIDGE_OPENAI_BASE_URL (default: https://api.openai.com/v1/)
113118
The base URL of the OpenAI API.
114119

cli/testdata/server-config.yaml.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,3 +747,7 @@ aibridge:
747747
# https://docs.claude.com/en/docs/claude-code/settings#environment-variables.
748748
# (default: global.anthropic.claude-haiku-4-5-20251001-v1:0, type: string)
749749
bedrock_small_fast_model: global.anthropic.claude-haiku-4-5-20251001-v1:0
750+
# Whether to inject Coder's MCP tools into intercepted AI Bridge requests
751+
# (requires the "oauth2" and "mcp-server-http" experiments to be enabled).
752+
# (default: true, type: bool)
753+
inject_coder_mcp_tools: true

coderd/apidoc/docs.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

codersdk/deployment.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3339,6 +3339,16 @@ Write out the current server config as YAML to stdout.`,
33393339
Group: &deploymentGroupAIBridge,
33403340
YAML: "bedrock_small_fast_model",
33413341
},
3342+
{
3343+
Name: "AIBridge Inject Coder MCP tools",
3344+
Description: "Whether to inject Coder's MCP tools into intercepted AI Bridge requests (requires the \"oauth2\" and \"mcp-server-http\" experiments to be enabled).",
3345+
Flag: "aibridge-inject-coder-mcp-tools",
3346+
Env: "CODER_AIBRIDGE_INJECT_CODER_MCP",
3347+
Value: &c.AI.BridgeConfig.InjectCoderMCPTools,
3348+
Default: "true",
3349+
Group: &deploymentGroupAIBridge,
3350+
YAML: "inject_coder_mcp_tools",
3351+
},
33423352
{
33433353
Name: "Enable Authorization Recordings",
33443354
Description: "All api requests will have a header including all authorization calls made during the request. " +
@@ -3358,10 +3368,11 @@ Write out the current server config as YAML to stdout.`,
33583368
}
33593369

33603370
type AIBridgeConfig struct {
3361-
Enabled serpent.Bool `json:"enabled" typescript:",notnull"`
3362-
OpenAI AIBridgeOpenAIConfig `json:"openai" typescript:",notnull"`
3363-
Anthropic AIBridgeAnthropicConfig `json:"anthropic" typescript:",notnull"`
3364-
Bedrock AIBridgeBedrockConfig `json:"bedrock" typescript:",notnull"`
3371+
Enabled serpent.Bool `json:"enabled" typescript:",notnull"`
3372+
OpenAI AIBridgeOpenAIConfig `json:"openai" typescript:",notnull"`
3373+
Anthropic AIBridgeAnthropicConfig `json:"anthropic" typescript:",notnull"`
3374+
Bedrock AIBridgeBedrockConfig `json:"bedrock" typescript:",notnull"`
3375+
InjectCoderMCPTools serpent.Bool `json:"inject_coder_mcp_tools" typescript:",notnull"`
33653376
}
33663377

33673378
type AIBridgeOpenAIConfig struct {

docs/reference/api/general.md

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/api/schemas.md

Lines changed: 11 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/cli/server.md

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enterprise/aibridgedserver/aibridgedserver.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ type Server struct {
7777
coderMCPConfig *proto.MCPServerConfig // may be nil if not available
7878
}
7979

80-
func NewServer(lifecycleCtx context.Context, store store, logger slog.Logger, accessURL string, externalAuthConfigs []*externalauth.Config, experiments codersdk.Experiments) (*Server, error) {
80+
func NewServer(lifecycleCtx context.Context, store store, logger slog.Logger, accessURL string,
81+
bridgeCfg codersdk.AIBridgeConfig, externalAuthConfigs []*externalauth.Config, experiments codersdk.Experiments) (*Server, error) {
8182
eac := make(map[string]*externalauth.Config, len(externalAuthConfigs))
8283

8384
for _, cfg := range externalAuthConfigs {
@@ -88,18 +89,22 @@ func NewServer(lifecycleCtx context.Context, store store, logger slog.Logger, ac
8889
eac[cfg.ID] = cfg
8990
}
9091

91-
coderMCPConfig, err := getCoderMCPServerConfig(experiments, accessURL)
92-
if err != nil {
93-
logger.Warn(lifecycleCtx, "failed to retrieve coder MCP server config, Coder MCP will not be available", slog.Error(err))
94-
}
95-
96-
return &Server{
92+
srv := &Server{
9793
lifecycleCtx: lifecycleCtx,
9894
store: store,
9995
logger: logger.Named("aibridgedserver"),
10096
externalAuthConfigs: eac,
101-
coderMCPConfig: coderMCPConfig,
102-
}, nil
97+
}
98+
99+
if bridgeCfg.InjectCoderMCPTools {
100+
coderMCPConfig, err := getCoderMCPServerConfig(experiments, accessURL)
101+
if err != nil {
102+
logger.Warn(lifecycleCtx, "failed to retrieve coder MCP server config, Coder MCP will not be available", slog.Error(err))
103+
}
104+
srv.coderMCPConfig = coderMCPConfig
105+
}
106+
107+
return srv, nil
103108
}
104109

105110
func (s *Server) RecordInterception(ctx context.Context, in *proto.RecordInterceptionRequest) (*proto.RecordInterceptionResponse, error) {

enterprise/aibridgedserver/aibridgedserver_test.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/coder/coder/v2/enterprise/aibridged/proto"
3333
"github.com/coder/coder/v2/enterprise/aibridgedserver"
3434
"github.com/coder/coder/v2/testutil"
35+
"github.com/coder/serpent"
3536
)
3637

3738
var requiredExperiments = []codersdk.Experiment{
@@ -169,7 +170,7 @@ func TestAuthorization(t *testing.T) {
169170
tc.mocksFn(db, apiKey, user)
170171
}
171172

172-
srv, err := aibridgedserver.NewServer(t.Context(), db, logger, "/", nil, requiredExperiments)
173+
srv, err := aibridgedserver.NewServer(t.Context(), db, logger, "/", codersdk.AIBridgeConfig{}, nil, requiredExperiments)
173174
require.NoError(t, err)
174175
require.NotNil(t, srv)
175176

@@ -203,11 +204,12 @@ func TestGetMCPServerConfigs(t *testing.T) {
203204
}
204205

205206
cases := []struct {
206-
name string
207-
experiments codersdk.Experiments
208-
externalAuthConfigs []*externalauth.Config
209-
expectCoderMCP bool
210-
expectedExternalMCP bool
207+
name string
208+
disableCoderMCPInjection bool
209+
experiments codersdk.Experiments
210+
externalAuthConfigs []*externalauth.Config
211+
expectCoderMCP bool
212+
expectedExternalMCP bool
211213
}{
212214
{
213215
name: "experiments not enabled",
@@ -238,6 +240,14 @@ func TestGetMCPServerConfigs(t *testing.T) {
238240
expectCoderMCP: true,
239241
expectedExternalMCP: true,
240242
},
243+
{
244+
name: "both internal & external MCP, but coder MCP tools not injected",
245+
disableCoderMCPInjection: true,
246+
experiments: requiredExperiments,
247+
externalAuthConfigs: externalAuthCfgs,
248+
expectCoderMCP: false,
249+
expectedExternalMCP: true,
250+
},
241251
}
242252

243253
for _, tc := range cases {
@@ -249,7 +259,9 @@ func TestGetMCPServerConfigs(t *testing.T) {
249259
logger := testutil.Logger(t)
250260

251261
accessURL := "https://my-cool-deployment.com"
252-
srv, err := aibridgedserver.NewServer(t.Context(), db, logger, accessURL, tc.externalAuthConfigs, tc.experiments)
262+
srv, err := aibridgedserver.NewServer(t.Context(), db, logger, accessURL, codersdk.AIBridgeConfig{
263+
InjectCoderMCPTools: serpent.Bool(!tc.disableCoderMCPInjection),
264+
}, tc.externalAuthConfigs, tc.experiments)
253265
require.NoError(t, err)
254266
require.NotNil(t, srv)
255267

@@ -287,7 +299,7 @@ func TestGetMCPServerAccessTokensBatch(t *testing.T) {
287299
logger := testutil.Logger(t)
288300

289301
// Given: 2 external auth configured with MCP and 1 without.
290-
srv, err := aibridgedserver.NewServer(t.Context(), db, logger, "/", []*externalauth.Config{
302+
srv, err := aibridgedserver.NewServer(t.Context(), db, logger, "/", codersdk.AIBridgeConfig{}, []*externalauth.Config{
291303
{
292304
ID: "1",
293305
MCPURL: "1.com/mcp",
@@ -794,7 +806,7 @@ func testRecordMethod[Req any, Resp any](
794806
}
795807

796808
ctx := testutil.Context(t, testutil.WaitLong)
797-
srv, err := aibridgedserver.NewServer(ctx, db, logger, "/", nil, requiredExperiments)
809+
srv, err := aibridgedserver.NewServer(ctx, db, logger, "/", codersdk.AIBridgeConfig{}, nil, requiredExperiments)
798810
require.NoError(t, err)
799811

800812
resp, err := callMethod(srv, ctx, tc.request)

0 commit comments

Comments
 (0)