MCPcopy Index your code
hub / github.com/coder/coder / mcpHTTPHandler

Method mcpHTTPHandler

coderd/mcp_http.go:31–97  ·  view source on GitHub ↗

mcpHTTPHandler creates the MCP HTTP transport handler It supports a "toolset" query parameter to select the set of tools to register.

()

Source from the content-addressed store, hash-verified

29// mcpHTTPHandler creates the MCP HTTP transport handler
30// It supports a "toolset" query parameter to select the set of tools to register.
31func (api *API) mcpHTTPHandler() http.Handler {
32 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
33 // Create MCP server instance for each request
34 mcpServer, err := mcp.NewServer(api.Logger.Named("mcp"))
35 if err != nil {
36 api.Logger.Error(r.Context(), "failed to create MCP server", slog.Error(err))
37 httpapi.Write(r.Context(), w, http.StatusInternalServerError, codersdk.Response{
38 Message: "MCP server initialization failed",
39 })
40 return
41 }
42 // Extract the original session token from the request
43 authenticatedClient := codersdk.New(api.AccessURL,
44 codersdk.WithSessionToken(httpmw.APITokenFromRequest(r)))
45
46 // Wrap the agent connection function to enforce ActionSSH
47 // on the workspace. Without this check, a user who can read
48 // a workspace but lacks SSH permission could still execute
49 // commands through MCP tools.
50 toolOpt := toolsdk.WithAgentConnFunc(func(ctx context.Context, agentID uuid.UUID) (workspacesdk.AgentConn, func(), error) {
51 if api.Entitlements.Enabled(codersdk.FeatureBrowserOnly) {
52 return nil, nil, xerrors.New("non-browser connections are disabled")
53 }
54 // Use system context for the lookup because the tool
55 // handler context does not carry a dbauthz actor. The
56 // real authorization happens in the Authorize call below.
57 //nolint:gocritic // The system query only fetches the workspace
58 // object so we can perform an ActionSSH check against it
59 // with the real user's roles via api.Authorize.
60 workspace, err := api.Database.GetWorkspaceByAgentID(dbauthz.AsSystemRestricted(ctx), agentID)
61 if err != nil {
62 return nil, nil, xerrors.Errorf("get workspace by agent ID: %w", err)
63 }
64 // Enforce the same ActionSSH check that the coordinate
65 // endpoint uses (workspaceagents.go:1317).
66 if !api.Authorize(r, policy.ActionSSH, workspace) {
67 return nil, nil, xerrors.New("unauthorized: you do not have SSH access to this workspace")
68 }
69 return api.agentProvider.AgentConn(ctx, agentID)
70 })
71
72 toolset := MCPToolset(r.URL.Query().Get("toolset"))
73 // Default to standard toolset if no toolset is specified.
74 if toolset == "" {
75 toolset = MCPToolsetStandard
76 }
77
78 switch toolset {
79 case MCPToolsetStandard:
80 if err := mcpServer.RegisterTools(authenticatedClient, toolOpt); err != nil {
81 api.Logger.Warn(r.Context(), "failed to register MCP tools", slog.Error(err))
82 }
83 case MCPToolsetChatGPT:
84 if err := mcpServer.RegisterChatGPTTools(authenticatedClient, toolOpt); err != nil {
85 api.Logger.Warn(r.Context(), "failed to register MCP tools", slog.Error(err))
86 }
87 default:
88 httpapi.Write(r.Context(), w, http.StatusBadRequest, codersdk.Response{

Callers 1

NewFunction · 0.95

Calls 15

AuthorizeMethod · 0.95
RegisterToolsMethod · 0.95
RegisterChatGPTToolsMethod · 0.95
ServeHTTPMethod · 0.95
NewServerFunction · 0.92
WriteFunction · 0.92
NewFunction · 0.92
WithSessionTokenFunction · 0.92
APITokenFromRequestFunction · 0.92
WithAgentConnFuncFunction · 0.92
AsSystemRestrictedFunction · 0.92
MCPToolsetTypeAlias · 0.85

Tested by

no test coverage detected