Skip to content

@McpProgressToken parameter causes IllegalArgumentException when method has no user parameters #5467

@stephaneberle9

Description

@stephaneberle9

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 unused

Result: 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:

  1. A method has @McpProgressToken annotated parameter
  2. No user-provided parameters exist in the method signature
  3. 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:

  1. Properly distinguish between framework-injected parameters (McpSyncServerExchange, @McpProgressToken) and user parameters during reflection setup
  2. Exclude framework-injected parameters from the JSON schema generation
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions