Skip to content

Commit 60a105d

Browse files
author
DevRev
committed
Add dynamic tools support to MCP server
1 parent 5c49f63 commit 60a105d

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

server/server.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ type ToolHandlerMiddleware func(ToolHandlerFunc) ToolHandlerFunc
4646
// ToolFilterFunc is a function that filters tools based on context, typically using session information.
4747
type ToolFilterFunc func(ctx context.Context, tools []mcp.Tool) []mcp.Tool
4848

49+
// dynamicTools holds configuration for dynamic tool generation
50+
type dynamicTools struct {
51+
enabled bool
52+
listFunc func(ctx context.Context, request mcp.ListToolsRequest) ([]mcp.Tool, error)
53+
handlerFunc func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)
54+
}
55+
4956
// ServerTool combines a Tool with its ToolHandlerFunc.
5057
type ServerTool struct {
5158
Tool mcp.Tool
@@ -155,6 +162,7 @@ type MCPServer struct {
155162
tools map[string]ServerTool
156163
toolHandlerMiddlewares []ToolHandlerMiddleware
157164
toolFilters []ToolFilterFunc
165+
dynamicTools *dynamicTools
158166
notificationHandlers map[string]NotificationHandlerFunc
159167
capabilities serverCapabilities
160168
paginationLimit *int
@@ -288,6 +296,20 @@ func WithInstructions(instructions string) ServerOption {
288296
}
289297
}
290298

299+
func WithDynamicTools(
300+
enabled bool,
301+
listFunc func(ctx context.Context, request mcp.ListToolsRequest) ([]mcp.Tool, error),
302+
handlerFunc func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error),
303+
) ServerOption {
304+
return func(s *MCPServer) {
305+
s.dynamicTools = &dynamicTools{
306+
enabled: enabled,
307+
listFunc: listFunc,
308+
handlerFunc: handlerFunc,
309+
}
310+
}
311+
}
312+
291313
// NewMCPServer creates a new MCP server instance with the given name, version and options
292314
func NewMCPServer(
293315
name, version string,
@@ -944,6 +966,44 @@ func (s *MCPServer) handleListTools(
944966
}
945967
}
946968

969+
// Add dynamic tools if enabled
970+
if s.dynamicTools != nil && s.dynamicTools.enabled && s.dynamicTools.listFunc != nil {
971+
dynamicTools, err := s.dynamicTools.listFunc(ctx, request)
972+
if err != nil {
973+
return nil, &requestError{
974+
id: id,
975+
code: mcp.INTERNAL_ERROR,
976+
err: fmt.Errorf("dynamic tools list function failed: %w", err),
977+
}
978+
}
979+
980+
if len(dynamicTools) > 0 {
981+
// Create a map to merge tools properly
982+
toolMap := make(map[string]mcp.Tool)
983+
984+
// Add dynamic tools first (lowest priority)
985+
for _, tool := range dynamicTools {
986+
toolMap[tool.Name] = tool
987+
}
988+
989+
// Then add existing tools (they override dynamic tools)
990+
for _, tool := range tools {
991+
toolMap[tool.Name] = tool
992+
}
993+
994+
// Convert back to slice
995+
tools = make([]mcp.Tool, 0, len(toolMap))
996+
for _, tool := range toolMap {
997+
tools = append(tools, tool)
998+
}
999+
1000+
// Sort again to maintain consistent ordering
1001+
sort.Slice(tools, func(i, j int) bool {
1002+
return tools[i].Name < tools[j].Name
1003+
})
1004+
}
1005+
}
1006+
9471007
// Apply tool filters if any are defined
9481008
s.toolFiltersMu.RLock()
9491009
if len(s.toolFilters) > 0 {
@@ -1006,6 +1066,15 @@ func (s *MCPServer) handleToolCall(
10061066
s.toolsMu.RUnlock()
10071067
}
10081068

1069+
// If still not found, try dynamic tool handler
1070+
if !ok && s.dynamicTools != nil && s.dynamicTools.enabled && s.dynamicTools.handlerFunc != nil {
1071+
// Create a ServerTool with the dynamic handler
1072+
tool = ServerTool{
1073+
Handler: s.dynamicTools.handlerFunc,
1074+
}
1075+
ok = true
1076+
}
1077+
10091078
if !ok {
10101079
return nil, &requestError{
10111080
id: id,

0 commit comments

Comments
 (0)