Skip to content

Commit dbac33e

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

File tree

6 files changed

+391
-14
lines changed

6 files changed

+391
-14
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 & 9 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
}
@@ -79,13 +79,7 @@ func (s *Server) RegisterTools(client *codersdk.Client) error {
7979
return xerrors.Errorf("failed to initialize tool dependencies: %w", err)
8080
}
8181

82-
// Register all available tools, but exclude tools that require dependencies not available in the
83-
// remote MCP context
84-
for _, tool := range toolsdk.All {
85-
if tool.Name == toolsdk.ToolNameReportTask {
86-
continue
87-
}
88-
82+
for _, tool := range tools {
8983
s.mcpServer.AddTools(mcpFromSDK(tool, toolDeps))
9084
}
9185
return nil

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: 37 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,45 @@ 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+
// Register all available tools, but exclude:
47+
// - ReportTask - which requires dependencies not available in the remote MCP context
48+
// - ChatGPT search and fetch tools, which are redundant with the standard tools.
49+
for _, tool := range toolsdk.All {
50+
if tool.Name == toolsdk.ToolNameReportTask ||
51+
tool.Name == toolsdk.ToolNameChatGPTSearch || tool.Name == toolsdk.ToolNameChatGPTFetch {
52+
continue
53+
}
54+
mcpTools = append(mcpTools, tool)
55+
}
56+
return api.mcpHTTPHandler(mcpTools)
57+
}
58+
59+
// chatgptMCPHTTPHandler sets up the MCP HTTP transport handler for the ChatGPT tools.
60+
// ChatGPT tools are the search and fetch tools as defined in https://platform.openai.com/docs/mcp.
61+
// We do not expose any extra ones because ChatGPT has an undocumented "Safety Scan" feature.
62+
// In my experiments, if I included extra tools in the MCP server, ChatGPT would refuse
63+
// to add Coder as a connector.
64+
func (api *API) chatgptMCPHTTPHandler() http.Handler {
65+
mcpTools := []toolsdk.GenericTool{}
66+
// Register only the ChatGPT search and fetch tools.
67+
for _, tool := range toolsdk.All {
68+
if !(tool.Name == toolsdk.ToolNameChatGPTSearch || tool.Name == toolsdk.ToolNameChatGPTFetch) {
69+
continue
70+
}
71+
mcpTools = append(mcpTools, tool)
72+
}
73+
return api.mcpHTTPHandler(mcpTools)
74+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy