Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7a4b271

Browse files
committed
feat(mcp): implement MCP HTTP server with toolsdk integration
- Add MCP HTTP server with streamable transport support- Integrate with existing toolsdk for Coder workspace operations- Add comprehensive E2E tests with OAuth2 bearer token support- Register MCP endpoint at /api/experimental/mcp/http with authentication- Support RFC 6750 Bearer token authentication for MCP clientsChange-Id: Ib9024569ae452729908797c42155006aa04330afSigned-off-by: Thomas Kosiewski <tk@coder.com>
1 parent54086ed commit7a4b271

File tree

9 files changed

+1684
-5
lines changed

9 files changed

+1684
-5
lines changed

‎coderd/apidoc/docs.go‎

Lines changed: 67 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json‎

Lines changed: 57 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/coderd.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,10 @@ func New(options *Options) *API {
972972
r.Route("/aitasks",func(r chi.Router) {
973973
r.Get("/prompts",api.aiTasksPrompts)
974974
})
975+
r.Route("/mcp",func(r chi.Router) {
976+
// MCP HTTP transport endpoint with mandatory authentication
977+
r.Mount("/http",api.mcpHTTPHandler())
978+
})
975979
})
976980

977981
r.Route("/api/v2",func(r chi.Router) {

‎coderd/mcp/mcp.go‎

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package mcp
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"net/http"
9+
"time"
10+
11+
"github.com/mark3labs/mcp-go/mcp"
12+
"github.com/mark3labs/mcp-go/server"
13+
"golang.org/x/xerrors"
14+
15+
"cdr.dev/slog"
16+
17+
"github.com/coder/coder/v2/buildinfo"
18+
"github.com/coder/coder/v2/codersdk"
19+
"github.com/coder/coder/v2/codersdk/toolsdk"
20+
)
21+
22+
const (
23+
// MCPServerName is the name used for the MCP server.
24+
MCPServerName="Coder"
25+
// MCPServerInstructions is the instructions text for the MCP server.
26+
MCPServerInstructions="Coder MCP Server providing workspace and template management tools"
27+
)
28+
29+
// Server represents an MCP HTTP server instance
30+
typeServerstruct {
31+
Logger slog.Logger
32+
33+
// mcpServer is the underlying MCP server
34+
mcpServer*server.MCPServer
35+
36+
// streamableServer handles HTTP transport
37+
streamableServer*server.StreamableHTTPServer
38+
}
39+
40+
// NewServer creates a new MCP HTTP server
41+
funcNewServer(logger slog.Logger) (*Server,error) {
42+
// Create the core MCP server
43+
mcpSrv:=server.NewMCPServer(
44+
MCPServerName,
45+
buildinfo.Version(),
46+
server.WithInstructions(MCPServerInstructions),
47+
)
48+
49+
// Create logger adapter for mcp-go
50+
mcpLogger:=&mcpLoggerAdapter{logger:logger}
51+
52+
// Create streamable HTTP server with configuration
53+
streamableServer:=server.NewStreamableHTTPServer(mcpSrv,
54+
server.WithHeartbeatInterval(30*time.Second),
55+
server.WithLogger(mcpLogger),
56+
)
57+
58+
return&Server{
59+
Logger:logger,
60+
mcpServer:mcpSrv,
61+
streamableServer:streamableServer,
62+
},nil
63+
}
64+
65+
// ServeHTTP implements http.Handler interface
66+
func (s*Server)ServeHTTP(w http.ResponseWriter,r*http.Request) {
67+
s.streamableServer.ServeHTTP(w,r)
68+
}
69+
70+
// RegisterTools registers all available MCP tools with the server
71+
func (s*Server)RegisterTools(client*codersdk.Client)error {
72+
// Create tool dependencies
73+
toolDeps,err:=toolsdk.NewDeps(client)
74+
iferr!=nil {
75+
returnxerrors.Errorf("failed to initialize tool dependencies: %w",err)
76+
}
77+
78+
// Register all available tools
79+
for_,tool:=rangetoolsdk.All {
80+
// Skip user-dependent tools if no authenticated client
81+
if!tool.UserClientOptional&&client==nil {
82+
s.Logger.Warn(context.Background(),"tool requires authentication and will not be available",slog.F("tool",tool.Name))
83+
continue
84+
}
85+
86+
s.mcpServer.AddTools(mcpFromSDK(tool,toolDeps))
87+
}
88+
89+
returnnil
90+
}
91+
92+
// mcpFromSDK adapts a toolsdk.Tool to go-mcp's server.ServerTool
93+
funcmcpFromSDK(sdkTool toolsdk.GenericTool,tb toolsdk.Deps) server.ServerTool {
94+
ifsdkTool.Schema.Properties==nil {
95+
panic("developer error: schema properties cannot be nil")
96+
}
97+
98+
return server.ServerTool{
99+
Tool: mcp.Tool{
100+
Name:sdkTool.Name,
101+
Description:sdkTool.Description,
102+
InputSchema: mcp.ToolInputSchema{
103+
Type:"object",
104+
Properties:sdkTool.Schema.Properties,
105+
Required:sdkTool.Schema.Required,
106+
},
107+
},
108+
Handler:func(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
109+
varbuf bytes.Buffer
110+
iferr:=json.NewEncoder(&buf).Encode(request.Params.Arguments);err!=nil {
111+
returnnil,xerrors.Errorf("failed to encode request arguments: %w",err)
112+
}
113+
result,err:=sdkTool.Handler(ctx,tb,buf.Bytes())
114+
iferr!=nil {
115+
returnnil,err
116+
}
117+
return&mcp.CallToolResult{
118+
Content: []mcp.Content{
119+
mcp.NewTextContent(string(result)),
120+
},
121+
},nil
122+
},
123+
}
124+
}
125+
126+
// mcpLoggerAdapter adapts slog.Logger to the mcp-go util.Logger interface
127+
typemcpLoggerAdapterstruct {
128+
logger slog.Logger
129+
}
130+
131+
func (l*mcpLoggerAdapter)Infof(formatstring,v...any) {
132+
l.logger.Info(context.Background(),fmt.Sprintf(format,v...))
133+
}
134+
135+
func (l*mcpLoggerAdapter)Errorf(formatstring,v...any) {
136+
l.logger.Error(context.Background(),fmt.Sprintf(format,v...))
137+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp