Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fine-grained control over parameter (de)serialization #216

Closed
GerbenAxendo opened this issue Apr 5, 2025 · 3 comments
Closed

Fine-grained control over parameter (de)serialization #216

GerbenAxendo opened this issue Apr 5, 2025 · 3 comments
Labels
enhancement New feature or request

Comments

@GerbenAxendo
Copy link

Is your feature request related to a problem? Please describe.
When using the SDK to add MCP capabilities to existing .NET core applications, it would be benificial to control how Tool parameters are serialized and deserialized from/to JSON.

This is espcially true if you want to reuse existing service methods and the tool parameters (= method arguments) are of a complex type with existing serialization/desarialization mechanisms that are already used in the application

Describe the solution you'd like
There could be an extention point that allows the developer to tap in to do the (de) serializion between the MCPServer revieving a request and passing it on to the Tool.

Describe alternatives you've considered

  • replacing the overall serialization mechanism (also those of the SDKs own classes) when a message is revieved at the end point
  • letting go of using the attributes classes on Tool methods and describe and write all tools wth custom code (like in the example in the readme file) but this 'feels' like a lot of overhead and not elegant.
@GerbenAxendo GerbenAxendo added the enhancement New feature or request label Apr 5, 2025
@GerbenAxendo GerbenAxendo changed the title Fine-grained control over parameter (de)serilization Fine-grained control over parameter (de)serialization Apr 5, 2025
@stephentoub
Copy link
Contributor

You can derive from McpServerTool and then just add an instance of your class as an McpServerTool to DI; it'll be picked up and automatically served. Deriving from McpServerTool involves effectively just providing a name, description, schema, and invoke method, so it does provide direct control over how tool parameters are handled.

If you only want to customize aspects of it, but still want to use the core logic, you can create a tool using McpServerTool.Create, giving it your delegate or method, and then wrap the resulting McpServerTool in a DelegatingMcpServerTool, where you derive from that type instead of from McpServerTool, and just override the portions that you want and delegate to the existing implementation for the rest.

Or as another middle ground, you can pass an arbitrary AIFunction to McpServerTool.Create, and use AIFunctionFactory.Create to create your AIFunction, using AIFunctionFactoryOptions to control how parameters are serialized.

Do none of those options meet your needs? If not, can you elaborate on exactly what you'd be hoping for?

@PederHP
Copy link
Collaborator

PederHP commented Apr 5, 2025

As an aside, you generally want to carefully consider whether to use complex tool parameters at all. Models tend to perform better with simpler tool signatures, where all parameters are simple types. If you have existing services you want to expose as tools, I would recommend doing evaluations (either with API-based pipelines or a library such as Microsoft.Extensions.AI.Evaluation) and comparing the performance of the flattened signature and the one taking a complex object. My experience is that you will have a lower error rate with the flattened functions. I know this adds an extra layer on inconvenience, but if you find that the model isn't calling your tools when it should or is messing up the parameters, this is a very strong technique to improve accuracy.

In addition to what @stephentoub mentioned above you can also just wrap your tool explicitly in a Facade-style pattern, which I think actually is a strong design as it means your MCP Server tools aren't tightly coupled to your existing service interface. It also doesn't require deriving from McpServerTool - you simply write a Facade for which each service which uses the SDK attributes and maps to the underlying service. Then you can have breaking changes in your service contract without implicitly changing your tool signatures - which might break Clients that have Server-specific instructions and examples in their system prompt.

More importantly it also allows you to keep the LLM-facing Description attributes separate from your existing service code. And Descriptions are very important for tool calling quality.

@GerbenAxendo
Copy link
Author

@stephentoub @PederHP Thanks! Both your comments give insight into alternative solutions. I think one of these will fit my use case.

Keep up the good work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants