Skip to content

feature: Add dynamic/runtime tools definition functionality #402

@floatingIce91

Description

@floatingIce91

Problem Statement

Currently, MCP servers can only provide static tools that are defined at server initialization time. This limitation prevents servers from offering context-aware or runtime-generated tools that could adapt based on the request and context.

Proposed Solution

Add support for dynamic tools in MCP servers through a new WithDynamicTools server option that accepts:

  1. List Function: func(ctx context.Context, request mcp.ListToolsRequest) ([]mcp.Tool, error)

    • Called during list_tools requests to generate available tools
    • Has access to context and request parameters (pagination, etc.)
  2. Handler Function: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)

    • Universal handler for executing any dynamic tool
    • Routes execution based on tool name in the request

Example Usage

// Server setup with dynamic tools
server := NewMCPServer(
    "my-server", 
    "1.0.0",
    WithDynamicTools(
        true,                    // enabled
        myDynamicToolsList,      // list function
        myDynamicToolHandler,    // execution handler
    ),
)

// Dynamic tools list function
func myDynamicToolsList(ctx context.Context, request mcp.ListToolsRequest) ([]mcp.Tool, error) {
    // Access session info, database, external APIs, etc.
    session := server.ClientSessionFromContext(ctx)
    userPermissions := getUserPermissions(session)
    
    var tools []mcp.Tool
    if userPermissions.CanAccessDatabase {
        tools = append(tools, mcp.Tool{
            Name: "query-database",
            Description: "Query the user database",
            // ... schema definition
        })
    }
    
    if userPermissions.CanManageUsers {
        tools = append(tools, mcp.Tool{
            Name: "create-user", 
            Description: "Create a new user",
            // ... schema definition
        })
    }
    
    return tools, nil
}

// Dynamic tool execution handler
func myDynamicToolHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    switch request.Params.Name {
    case "query-database":
        return handleDatabaseQuery(ctx, request)
    case "create-user":
        return handleUserCreation(ctx, request)
    default:
        return nil, fmt.Errorf("unknown dynamic tool: %s", request.Params.Name)
    }
}

Alternatives/Workarounds Considered

Session based tools cannot be used because we have a stateless streamableHTTP server, thus we need a way to define tools dynamically using the request and the context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions