Skip to content

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

@maff

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": []
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions