-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
Description
Spring AI MCP Server fails to invoke tool methods annotated with @McpTool when the method signature contains only framework-injected parameters (McpSyncServerExchange and @McpProgressToken). The framework throws an "argument type mismatch" error during reflection-based method invocation.
Environment
- Spring AI version: 1.1.2 (
org.springframework.ai:spring-ai-starter-mcp-server-webmvc) - Spring Boot version: 3.5.10
- Java version: 21 (Eclipse Temurin)
- MCP Protocol version: 2024-11-05
- Build tool: Gradle 9.3.1
Minimal Reproducible Example
Tool definition:
@Service
public class ProjectsResourceWrapper {
@McpTool(
name = "getProjects",
description = "Get all projects accessible to the current user"
)
public List<Project> getProjects(
McpSyncServerExchange exchange,
@McpProgressToken String progressToken) {
// Implementation that uses progressToken for progress notifications
return projectService.getProjects();
}
}MCP invocation (via MCP Inspector or any MCP client):
{
"method": "tools/call",
"params": {
"name": "getProjects",
"arguments": {}
}
}Expected Behavior
The method should be invoked successfully with the framework injecting both exchange and progressToken parameters automatically, similar to how other Spring framework injection mechanisms work (e.g., @PathVariable, @RequestHeader).
Actual Behavior
Error response:
{
"content": [
{
"type": "text",
"text": "argument type mismatch"
}
],
"isError": true
}Server logs: No stack trace or additional error details are logged, suggesting the exception is caught and wrapped at a high level in the MCP framework.
Investigation & Attempted Workarounds
All of the following approaches were tested and failed to resolve the issue:
1. Adding a dummy user parameter
public List<Project> getProjects(
McpSyncServerExchange exchange,
@McpProgressToken String progressToken,
Boolean unused) { ... }Result: Same "argument type mismatch" error ❌
2. Marking parameter as nullable
public List<Project> getProjects(
McpSyncServerExchange exchange,
@McpProgressToken String progressToken,
@Nullable Boolean unused) { ... }Result: Same error ❌
3. Changing dummy parameter type to String
@Nullable String unusedResult: Same error ❌
4. Providing the dummy parameter from the client
{"unused": false} // or null, or ""Result: Same error ❌
Working Workaround
Remove the @McpProgressToken parameter entirely:
@McpTool(
name = "getProjects",
description = "Get all projects accessible to the current user"
)
public List<Project> getProjects(McpSyncServerExchange exchange) {
// Pass null where progressToken would be used
// in executeGet/executePost/executeDelete calls
return projectService.getProjects();
}Trade-off: Progress notifications are not available for these methods, but this is acceptable for fast operations.
Root Cause Analysis
The issue appears to be in how the Spring AI MCP framework constructs the parameter array for reflection-based method invocation when:
- A method has
@McpProgressTokenannotated parameter - No user-provided parameters exist in the method signature
- The framework attempts to invoke the method via reflection
Hypothesis: The framework may be incorrectly calculating the parameter count or order, possibly treating @McpProgressToken as a user parameter rather than a framework-injected parameter during the invocation setup phase.
Impact
This bug affects:
- Any MCP tool that performs simple operations without requiring user input
- Read-only operations like listing resources
- Utility methods that only need the exchange context
Workaround impact: Methods lose progress notification capability, which is problematic for long-running operations.
Suggested Fix
The framework should:
- Properly distinguish between framework-injected parameters (
McpSyncServerExchange,@McpProgressToken) and user parameters during reflection setup - Exclude framework-injected parameters from the JSON schema generation
- Correctly construct the parameter array for method invocation when only framework parameters are present
Additional Context
- This issue was discovered while implementing an MCP server for a traceability system
- Affects multiple tool methods in production code
- Workaround tested and verified with MCP Inspector and Claude Desktop
- Unit tests pass with the workaround implementation