mirror of
https://github.com/therootcompany/golib.git
synced 2026-11-04 06:55:00 +00:00
104 lines
3.2 KiB
Go
104 lines
3.2 KiB
Go
package mcpserver
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
mcptypes "github.com/therootcompany/golib/mcp/types20250618"
|
|
)
|
|
|
|
// ToolMiddleware wraps a tool handler, analogous to func(http.Handler) http.Handler.
|
|
type ToolMiddleware func(ToolHandlerFunc) ToolHandlerFunc
|
|
|
|
// ToolHandler pairs a tool definition with its handler function.
|
|
type ToolHandler struct {
|
|
Tool mcptypes.Tool
|
|
Handle ToolHandlerFunc
|
|
}
|
|
|
|
// ResourceHandler pairs a resource definition with its read handler.
|
|
type ResourceHandler struct {
|
|
Resource mcptypes.Resource
|
|
Handle ResourceHandlerFunc
|
|
}
|
|
|
|
// MethodHandlerFunc handles a raw JSON-RPC request for a specific method.
|
|
type MethodHandlerFunc func(ctx context.Context, req *mcptypes.JSONRPCRequest) any
|
|
|
|
// Mux composes middleware chains and registers tools on an MCPServer.
|
|
// Mirrors golib/http/middleware.MiddlewareMux: NewMux → .With() → .Register().
|
|
type Mux struct {
|
|
server *MCPServer
|
|
middlewares []ToolMiddleware
|
|
}
|
|
|
|
func NewMux(srv *MCPServer, middlewares ...ToolMiddleware) *Mux {
|
|
return &Mux{
|
|
server: srv,
|
|
middlewares: middlewares,
|
|
}
|
|
}
|
|
|
|
// With returns a new Mux with additional middlewares appended.
|
|
func (m *Mux) With(middlewares ...ToolMiddleware) *Mux {
|
|
combined := make([]ToolMiddleware, 0, len(m.middlewares)+len(middlewares))
|
|
combined = append(combined, m.middlewares...)
|
|
combined = append(combined, middlewares...)
|
|
return &Mux{
|
|
server: m.server,
|
|
middlewares: combined,
|
|
}
|
|
}
|
|
|
|
// Register registers a tool. The method must start with "tools/" — the prefix
|
|
// is stripped to produce the wire-format tool name. Panics on wiring errors.
|
|
func (m *Mux) Register(method string, th ToolHandler) {
|
|
if !strings.HasPrefix(method, "tools/") {
|
|
panic("mcp: tool registered without tools/ prefix: " + method)
|
|
}
|
|
name := strings.TrimPrefix(method, "tools/")
|
|
if th.Tool.Name != "" && th.Tool.Name != name {
|
|
panic(fmt.Sprintf("mcp: tool name mismatch: Register(%q) but Tool.Name is %q", method, th.Tool.Name))
|
|
}
|
|
|
|
handler := th.Handle
|
|
for i := len(m.middlewares) - 1; i >= 0; i-- {
|
|
handler = m.middlewares[i](handler)
|
|
}
|
|
m.server.HandleToolFunc(name, th.Tool, handler)
|
|
}
|
|
|
|
// Method overrides a built-in JSON-RPC method (e.g. "tools/list").
|
|
func (m *Mux) Method(method string, handler MethodHandlerFunc) {
|
|
m.server.HandleMethod(method, handler)
|
|
}
|
|
|
|
// Resources returns a ResourceMux for resources/read.
|
|
func (m *Mux) Resources() *ResourceMux {
|
|
return &ResourceMux{server: m.server}
|
|
}
|
|
|
|
// ToolsIndex returns a handler for tools/list.
|
|
func (m *Mux) ToolsIndex() MethodHandlerFunc {
|
|
return m.server.handleToolsList
|
|
}
|
|
|
|
// ResourcesIndex returns a handler for resources/list.
|
|
func (m *Mux) ResourcesIndex() MethodHandlerFunc {
|
|
return m.server.handleResourcesList
|
|
}
|
|
|
|
// ResourceMux registers resources on the server. Created via [Mux.Resources].
|
|
type ResourceMux struct {
|
|
server *MCPServer
|
|
}
|
|
|
|
// Expose registers a resource by URI. Panics if the handler's URI doesn't match.
|
|
func (rm *ResourceMux) Expose(uri string, rh ResourceHandler) {
|
|
if rh.Resource.URI != "" && rh.Resource.URI != uri {
|
|
panic(fmt.Sprintf("mcp: resource URI mismatch: Expose(%q) but Resource.URI is %q", uri, rh.Resource.URI))
|
|
}
|
|
rm.server.HandleResource(uri, rh.Resource, rh.Handle)
|
|
}
|