Skip to content

Commit a2c288a

Browse files
committed
feat: search and fetch mcp tools
1 parent 219d1b4 commit a2c288a

File tree

6 files changed

+387
-8
lines changed

6 files changed

+387
-8
lines changed

coderd/coderd.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,8 +996,12 @@ func New(options *Options) *API {
996996
r.Use(
997997
httpmw.RequireExperimentWithDevBypass(api.Experiments, codersdk.ExperimentOAuth2, codersdk.ExperimentMCPServerHTTP),
998998
)
999+
9991000
// MCP HTTP transport endpoint with mandatory authentication
1000-
r.Mount("/http", api.mcpHTTPHandler())
1001+
r.Mount("/http", api.standardMCPHTTPHandler())
1002+
// ChatGPT gets a dedicated endpoint with a limited set of tools.
1003+
// See the docstring of the chatgptMCPHTTPHandler for more details.
1004+
r.Mount("/chatgpt", api.chatgptMCPHTTPHandler())
10011005
})
10021006
})
10031007

coderd/mcp/mcp.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
6767
s.streamableServer.ServeHTTP(w, r)
6868
}
6969

70-
// RegisterTools registers all available MCP tools with the server
71-
func (s *Server) RegisterTools(client *codersdk.Client) error {
70+
// RegisterTools registers MCP tools with the server
71+
func (s *Server) RegisterTools(client *codersdk.Client, tools []toolsdk.GenericTool) error {
7272
if client == nil {
7373
return xerrors.New("client cannot be nil: MCP HTTP server requires authenticated client")
7474
}
@@ -81,7 +81,7 @@ func (s *Server) RegisterTools(client *codersdk.Client) error {
8181

8282
// Register all available tools, but exclude tools that require dependencies not available in the
8383
// remote MCP context
84-
for _, tool := range toolsdk.All {
84+
for _, tool := range tools {
8585
if tool.Name == toolsdk.ToolNameReportTask {
8686
continue
8787
}

coderd/mcp/mcp_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,13 @@ func TestMCPHTTP_ToolRegistration(t *testing.T) {
110110
require.NoError(t, err)
111111

112112
// Test registering tools with nil client should return error
113-
err = server.RegisterTools(nil)
113+
err = server.RegisterTools(nil, toolsdk.All)
114114
require.Error(t, err)
115115
require.Contains(t, err.Error(), "client cannot be nil", "Should reject nil client with appropriate error message")
116116

117117
// Test registering tools with valid client should succeed
118118
client := &codersdk.Client{}
119-
err = server.RegisterTools(client)
119+
err = server.RegisterTools(client, toolsdk.All)
120120
require.NoError(t, err)
121121

122122
// Verify that all expected tools are available in the toolsdk

coderd/mcp_http.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import (
99
"github.com/coder/coder/v2/coderd/httpmw"
1010
"github.com/coder/coder/v2/coderd/mcp"
1111
"github.com/coder/coder/v2/codersdk"
12+
"github.com/coder/coder/v2/codersdk/toolsdk"
1213
)
1314

1415
// mcpHTTPHandler creates the MCP HTTP transport handler
15-
func (api *API) mcpHTTPHandler() http.Handler {
16+
func (api *API) mcpHTTPHandler(tools []toolsdk.GenericTool) http.Handler {
1617
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1718
// Create MCP server instance for each request
1819
mcpServer, err := mcp.NewServer(api.Logger.Named("mcp"))
@@ -29,11 +30,41 @@ func (api *API) mcpHTTPHandler() http.Handler {
2930
authenticatedClient.SetSessionToken(httpmw.APITokenFromRequest(r))
3031

3132
// Register tools with authenticated client
32-
if err := mcpServer.RegisterTools(authenticatedClient); err != nil {
33+
if err := mcpServer.RegisterTools(authenticatedClient, tools); err != nil {
3334
api.Logger.Warn(r.Context(), "failed to register MCP tools", slog.Error(err))
3435
}
3536

3637
// Handle the MCP request
3738
mcpServer.ServeHTTP(w, r)
3839
})
3940
}
41+
42+
// standardMCPHTTPHandler sets up the MCP HTTP transport handler for the standard tools.
43+
// Standard tools are all tools except for the report task, ChatGPT search, and ChatGPT fetch tools.
44+
func (api *API) standardMCPHTTPHandler() http.Handler {
45+
mcpTools := []toolsdk.GenericTool{}
46+
for _, tool := range toolsdk.All {
47+
if tool.Name == toolsdk.ToolNameReportTask ||
48+
tool.Name == toolsdk.ToolNameChatGPTSearch || tool.Name == toolsdk.ToolNameChatGPTFetch {
49+
continue
50+
}
51+
mcpTools = append(mcpTools, tool)
52+
}
53+
return api.mcpHTTPHandler(mcpTools)
54+
}
55+
56+
// chatgptMCPHTTPHandler sets up the MCP HTTP transport handler for the ChatGPT tools.
57+
// ChatGPT tools are the search and fetch tools as defined in https://platform.openai.com/docs/mcp.
58+
// We do not expose any extra ones because ChatGPT has an undocumented "Safety Scan" feature.
59+
// In my experiments, if I included extra tools in the MCP server, ChatGPT would refuse
60+
// to add Coder as a connector.
61+
func (api *API) chatgptMCPHTTPHandler() http.Handler {
62+
mcpTools := []toolsdk.GenericTool{}
63+
for _, tool := range toolsdk.All {
64+
if !(tool.Name == toolsdk.ToolNameChatGPTSearch || tool.Name == toolsdk.ToolNameChatGPTFetch) {
65+
continue
66+
}
67+
mcpTools = append(mcpTools, tool)
68+
}
69+
return api.mcpHTTPHandler(mcpTools)
70+
}

0 commit comments

Comments
 (0)