Skip to content

Commit b6289d3

Browse files
feat(inventory): add ToolHandlerMiddleware for handler wrapping
Add middleware support to tool registration, allowing callers to wrap handlers with cross-cutting concerns like observability, error tracking, or request/response modification. New APIs: - ToolHandlerMiddleware type for wrapping handlers with tool name context - RegisterFuncWithMiddleware on ServerTool for single tool registration - RegisterToolsWithMiddleware on Inventory for bulk tool registration - RegisterAllWithMiddleware on Inventory for full registration with middleware This restores the ability for the remote server to wrap handlers that was lost during the inventory refactor. The remote server needs this to check result.IsError and context errors for Datadog significance tracking. Existing RegisterFunc/RegisterTools/RegisterAll APIs unchanged (call WithMiddleware variants with nil).
1 parent e1bc004 commit b6289d3

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

pkg/inventory/registry.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,16 @@ func (r *Inventory) ToolsetDescriptions() map[ToolsetID]string {
171171
// RegisterTools registers all available tools with the server using the provided dependencies.
172172
// The context is used for feature flag evaluation.
173173
func (r *Inventory) RegisterTools(ctx context.Context, s *mcp.Server, deps any) {
174+
r.RegisterToolsWithMiddleware(ctx, s, deps, nil)
175+
}
176+
177+
// RegisterToolsWithMiddleware registers all available tools with the server using the provided dependencies.
178+
// If middleware is provided, it wraps each handler before registration.
179+
// This allows callers to add observability, error tracking, or other cross-cutting concerns.
180+
// The context is used for feature flag evaluation.
181+
func (r *Inventory) RegisterToolsWithMiddleware(ctx context.Context, s *mcp.Server, deps any, middleware ToolHandlerMiddleware) {
174182
for _, tool := range r.AvailableTools(ctx) {
175-
tool.RegisterFunc(s, deps)
183+
tool.RegisterFuncWithMiddleware(s, deps, middleware)
176184
}
177185
}
178186

@@ -195,7 +203,15 @@ func (r *Inventory) RegisterPrompts(ctx context.Context, s *mcp.Server) {
195203
// RegisterAll registers all available tools, resources, and prompts with the server.
196204
// The context is used for feature flag evaluation.
197205
func (r *Inventory) RegisterAll(ctx context.Context, s *mcp.Server, deps any) {
198-
r.RegisterTools(ctx, s, deps)
206+
r.RegisterAllWithMiddleware(ctx, s, deps, nil)
207+
}
208+
209+
// RegisterAllWithMiddleware registers all available tools, resources, and prompts with the server.
210+
// If middleware is provided, it wraps each tool handler before registration.
211+
// This allows callers to add observability, error tracking, or other cross-cutting concerns.
212+
// The context is used for feature flag evaluation.
213+
func (r *Inventory) RegisterAllWithMiddleware(ctx context.Context, s *mcp.Server, deps any, middleware ToolHandlerMiddleware) {
214+
r.RegisterToolsWithMiddleware(ctx, s, deps, middleware)
199215
r.RegisterResourceTemplates(ctx, s, deps)
200216
r.RegisterPrompts(ctx, s)
201217
}

pkg/inventory/server_tool.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ import (
1414
// should define their own typed dependencies struct and type-assert as needed.
1515
type HandlerFunc func(deps any) mcp.ToolHandler
1616

17+
// ToolHandlerMiddleware wraps a tool handler to add cross-cutting concerns like
18+
// observability, error tracking, or request/response modification.
19+
// The middleware receives the tool name for context (e.g., logging, metrics).
20+
// Return the wrapped handler that should be registered with the server.
21+
type ToolHandlerMiddleware func(toolName string, handler mcp.ToolHandler) mcp.ToolHandler
22+
1723
// ToolsetID is a unique identifier for a toolset.
1824
// Using a distinct type provides compile-time type safety.
1925
type ToolsetID string
@@ -83,7 +89,18 @@ func (st *ServerTool) Handler(deps any) mcp.ToolHandler {
8389
// RegisterFunc registers the tool with the server using the provided dependencies.
8490
// Panics if the tool has no handler - all tools should have handlers.
8591
func (st *ServerTool) RegisterFunc(s *mcp.Server, deps any) {
92+
st.RegisterFuncWithMiddleware(s, deps, nil)
93+
}
94+
95+
// RegisterFuncWithMiddleware registers the tool with the server using the provided dependencies.
96+
// If middleware is provided, it wraps the handler before registration.
97+
// This allows callers to add observability, error tracking, or other cross-cutting concerns.
98+
// Panics if the tool has no handler - all tools should have handlers.
99+
func (st *ServerTool) RegisterFuncWithMiddleware(s *mcp.Server, deps any, middleware ToolHandlerMiddleware) {
86100
handler := st.Handler(deps) // This will panic if HandlerFunc is nil
101+
if middleware != nil {
102+
handler = middleware(st.Tool.Name, handler)
103+
}
87104
s.AddTool(&st.Tool, handler)
88105
}
89106

0 commit comments

Comments
 (0)