modelcontextprotocol / modelcontextprotocol/csharp-sdk
Support for dynamic tools
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 4.5k
- Forks
- 814
- Avg merge
- 9d 19h
- Merged PRs (30d)
- 4
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the existing tool registration and call handling behind AddMcpServer, WithTools, and the custom CallToolHandler alternative. Compare static tool registration with the proposed DynamicTools and Invoke entry points. Done should include runtime registration of multiple tools to one handler, service injection, and a defined result path, with tests covering the behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100