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

Proposal: Allow any return type in tool functions #75

Closed
cpunion opened this issue Apr 1, 2025 · 5 comments
Closed

Proposal: Allow any return type in tool functions #75

cpunion opened this issue Apr 1, 2025 · 5 comments

Comments

@cpunion
Copy link

cpunion commented Apr 1, 2025

Issue Description

Currently, tool functions can only return String directly or implement the IntoCallToolResult trait for custom types. This limits the flexibility and requires unnecessary boilerplate.

Suggested Enhancement

Allow tool functions to return any serializable type by:

  1. Making #[tool(param)] attribute optional on function parameters
  2. Automatically converting return values to CallToolResult using serde serialization

Benefits

  • Easier function definitions
  • Cleaner test code
  • More intuitive API
  • Less boilerplate implementation
  • Better type safety

Example (Current vs Proposed)

Current approach:

#[tool(description = "Calculate sum")]
fn sum(&self, #[tool(param)] a: i32, #[tool(param)] b: i32) -> String {
    (a + b).to_string()
}

Proposed approach:

#[tool(description = "Calculate sum")]
fn sum(&self, a: i32, b: i32) -> i32 {
    a + b
}

This enhancement would make the API more ergonomic while maintaining all current functionality.

@cpunion cpunion changed the title Enhancement: Allow any return type in tool functions Proposal: Allow any return type in tool functions Apr 1, 2025
@cpunion
Copy link
Author

cpunion commented Apr 1, 2025

Comparison with Python SDK

The Python SDK already supports this pattern with a cleaner API:

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b

async def main():
    print(add(1, 2))  # Normal function call: 3
    print(await mcp.call_tool("add", {"a": 1, "b": 2}))  # Via MCP: 3

if __name__ == "__main__":
    asyncio.run(main())

This demonstrates how returning native types directly makes the API more intuitive.

@4t145
Copy link
Collaborator

4t145 commented Apr 1, 2025

We can add a auto serialization wrapper to tell sdk using a JSON serialization policy, it could be looks like this.

#[tool(description = "Calculate sum")]
fn sum(&self, a: i32, b: i32) -> Json<i32> {
    Json(a + b)
}

@cpunion
Copy link
Author

cpunion commented Apr 1, 2025

@4t145 Thanks! The Json wrapper approach looks great.

@4t145
Copy link
Collaborator

4t145 commented Apr 1, 2025

Supported in #78

@4t145 4t145 closed this as completed Apr 1, 2025
@cpunion
Copy link
Author

cpunion commented Apr 1, 2025

Great! Thank.

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

No branches or pull requests

2 participants