8000 Support for dynamic tools · Issue #237 · modelcontextprotocol/csharp-sdk · GitHub
[go: up one dir, main page]

Skip to content
Support for dynamic tools #237
Open
Open
@ionmincu

Description

@ionmincu

Is your feature request related to a problem? Please describe.
I would like to map a collection of dynamic tools to a single class handler (see example below).

Describe the solution you'd like
I would like a single handler to be mapped to multiple tools. These tools will be dynamically mapped and available at runtime and may be on other servers. The core of this would be a base class DynamicTools

builder.Services.AddMcpServer()
    .WithStdioServerTransport()
    .WithTools<EchoTools>()  // support with static tools side by side
    .WithTools<MyDynamicTools>(
    [
        new Tool { Name = "echo_a", Description = "Echoes the input back to the user.", InputSchema = "..." },
        new Tool { Name = "echo_b", Description = "Echoes the input back to the user.", InputSchema = "..." },
    ]);
public class MyDynamicTools : DynamicTools
{
    private readonly HttpClient _client;

    public MyDynamicTools(HttpClient client) // also other scoped services should be able to be injected here
    {
        _client = client;
    }

    public override async Task<IMcpActionResult> Invoke(CallToolRequestParams requestParams)
    {
        // just an example other routing logic may apply here
        var result = await _client.PostAsJsonAsync($"/service/{requestParams.Name}", requestParams.Arguments);
        // could also return error
        return Ok(result);
    }
}

The rest would be optional, only the Invoke(CallToolRequestParams params) is important.
Maybe make it similar to MVC.

public interface IMcpActionResult
{
    Task Invoke(ITransport transport, CancellationToken cancellationToken);
}

public class OkObjectResult : IMcpActionResult
{
    private readonly object _value;
    public OkObjectResult(object value)
    {
        _value = value;
    }
    public Task Invoke(ITransport transport, CancellationToken cancellationToken)
    {
        return transport.SendMessageAsync(new JsonRpcResponse { Result = _value}, cancellationToken);
    }
}

public abstract class DynamicTools
{
    public abstract async Task<IMcpActionResult> Invoke(CallToolRequestParams requestParams);

    protected IMcpActionResult Ok(object value)
    {
        return new OkObjectResult(value);
    }
}

Describe alternatives you've considered
Create a custom a CallToolHandler

Additional context
Add any other context or screenshots about the feature request here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0