Skip to content

Stateless server handler throws exception instead of returning JSON-RPC -32601 (Method not found) for not registered capabilities #784

Closed

Description

@maff

Bug description
In the stateless server path (DefaultMcpStatelessServerHandler), when a request is received for a method that does not have a registered handler, the server currently throws an McpError exception instead of returning a JSON-RPC response with an error code. This is inconsistent with the stateful/session server implementations (McpServerSession, etc.), which respond with the standard JSON-RPC error -32601 (Method not found).

Environment
ModelContextProtocol Java SDK, stateless/server mode.

Steps to reproduce

  1. Start a stateless MCP server instance (e.g., without advertising or registering a resources or prompts capability).
  2. Send a request with method: "resources/list" (or another method for which the handler is not registered).
  3. Exception is thrown instead of returning a JSON-RPC response with a "Method not found" error

Expected behavior
Stateless server should respond to any unknown/unregistered method with the standard JSON-RPC -32601 "Method not found" error, to be consistent with other server implementations in the SDK and with JSON-RPC specification.

Minimal Complete Reproducible example

Minimal Spring AI reproducer:

package com.example.demo;

import org.springaicommunity.mcp.annotation.McpTool;
import org.springaicommunity.mcp.annotation.McpToolParam;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Component
    public static class DemoTools {

        @McpTool
        public String hello(@McpToolParam String name) {
            return "Hello, " + name;
        }

    }
}
spring:
  ai:
    mcp:
      server:
        enabled: true
        type: sync
        protocol: stateless
        annotation-scanner:
          enabled: true
        streamable-http:
          mcp-endpoint: /mcp
        capabilities:
          completion: false
          prompt: false
          resource: false
          tool: true
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.0.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>21</java.version>
        <spring-ai.version>2.0.0-M2</spring-ai.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>io.modelcontextprotocol.sdk</groupId>
                <artifactId>mcp-bom</artifactId>
                <version>0.17.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Running a resources/list command against this server returns a JSON stack trace instead of a JSON-RPC response:

$ curl -H "Accept: application/json, text/event-stream" -H "Content-Type: application/json" -X POST http://localhost:8080/mcp -d '{"jsonrpc": "2.0", "method": "resources/list", "id": 1}' | jq
{
  "cause": null,
  "jsonRpcError": null,
  "localizedMessage": "Failed to handle request: Missing handler for request type: resources/list",
  "message": "Failed to handle request: Missing handler for request type: resources/list",
  "stackTrace": [
    {
      "classLoaderName": "app",
      "className": "io.modelcontextprotocol.server.transport.WebMvcStatelessServerTransport",
      "fileName": "WebMvcStatelessServerTransport.java",
      "lineNumber": 126,
      "methodName": "handlePost",
      "moduleName": null,
      "moduleVersion": null,
      "nativeMethod": false
    },
    {
      "classLoaderName": "app",
      "className": "org.springframework.web.servlet.function.support.HandlerFunctionAdapter",
      "fileName": "HandlerFunctionAdapter.java",
      // ...
    },
    // ...
  ],
  "suppressed": []
}

Activity

  1. added
    waiting for userWaiting for user feedback or more details
    bugSomething isn't working
    on Feb 21, 2026
  2. added theissue type on Feb 21, 2026
  3. chemicL commented on Feb 21, 2026

    @chemicL
    Member

    Thank you for the report. Can you please check 1.0.0-RC3. I believe my recent changes from #807 might have addressed this.

  4. maff commented on Feb 25, 2026

    @maff
    ContributorAuthor

    @chemicL I checked 1.0.0-RC3 and I think the issue is still there as the server still returns a 500 error with a stack trace:

    {
      "jsonRpcError" : {
        "code" : -32603,
        "message" : "Failed to handle request: Missing handler for request type: tools/list"
      },
      "cause" : null,
      "localizedMessage" : "Failed to handle request: Missing handler for request type: tools/list",
      "message" : "Failed to handle request: Missing handler for request type: tools/list",
     ...
    }
    

    To verify, you can add the following test to HttpServletStatelessIntegrationTests (from #800, adapted to 1.0.0-RC3):

    	@ParameterizedTest
    	@ValueSource(strings = { "tools/list", "resources/list", "prompts/list" })
    	void testMissingHandlerReturnsMethodNotFoundError(String method) throws Exception {
    		var mcpServer = McpServer.sync(mcpStatelessServerTransport)
    			.serverInfo("test-server", "1.0.0")
    			.capabilities(ServerCapabilities.builder().build())
    			.build();
    
    		HttpResponse<String> response;
    
    		try {
    			HttpRequest request = HttpRequest.newBuilder()
    				.uri(URI.create("http://localhost:" + PORT + CUSTOM_MESSAGE_ENDPOINT))
    				.header("Content-Type", "application/json")
    				.header("Accept", "application/json, text/event-stream")
    				.POST(HttpRequest.BodyPublishers.ofString("""
    						{
    							"jsonrpc": "2.0",
    							"method": "%s",
    							"id": "test-request-123",
    							"params": {}
    						}
    						""".formatted(method)))
    				.build();
    
    			response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
    		}
    		finally {
    			mcpServer.closeGracefully();
    		}
    
    		final var responseBody = response.body();
    		assertThatJson(responseBody).inPath("error.code").isEqualTo(McpSchema.ErrorCodes.METHOD_NOT_FOUND);
    		assertThatJson(responseBody).inPath("error.message").isEqualTo("Method not found: " + method);
    	}

    ⏩ I think that instead of returning a Mono.error in https://github.com/modelcontextprotocol/java-sdk/blob/main/mcp-core/src/main/java/io/modelcontextprotocol/server/DefaultMcpStatelessServerHandler.java#L34, it should return a JSONRPCResponse with the respective error code.

  5. maff commented on Feb 25, 2026

    @maff
    ContributorAuthor

    @chemicL I rebased and updated #800 to be compatible with main

  6. maff commented on Mar 16, 2026

    @maff
    ContributorAuthor

    @chemicL any updates on this one? I think it is still an issue on 1.1.0 as stated above. Instead of a JSON-RPC error, the handler returns a serialized exception, including the whole stack trace.

  7. self-assigned this
    on Jun 25, 2026
  8. added this to the 2.0.1 milestone on Jul 8, 2026
  9. added a commit that references this issue on Jul 8, 2026
    5d3dece
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions