Files
awesome-copilot/skills/java-mcp-server-generator/references/streamable-http.md
T
Burak Kaygusuz 4a36aafa03 docs(java-mcp-server-generator): update skill for MCP Java SDK 2.x
- Target MCP Java SDK 2.0.1 and specification revision 2025-11-25.
- Rewrite the description with user intent triggers and exclusions.
- Add the compatibility field.
- Add a five-step workflow checklist with a build loop.
- Add a Maven template, a stdio server template, and a JUnit test that
  starts the server as a subprocess with the SDK client.
- Add Gotchas for SDK 2.x changes, schema validation, error results,
  stdout in stdio servers, and the features that SEP-2577 deprecates.
- Add references/gradle.md for Gradle projects.
- Add references/streamable-http.md for Servlet, Spring AI, and
  transport security.
- Update the skill description in docs/README.skills.md.
2026-10-09 07:10:01 +03:00

5.0 KiB

Streamable HTTP Servers

Use this transport only for a remote server. A local server uses stdio.

Servlet server

HttpServletStreamableServerTransportProvider is a Jakarta Servlet. It is in the mcp artifact. Add a servlet container to pom.xml, for example embedded Tomcat:

<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-core</artifactId>
  <version>11.0.26</version>
</dependency>

In Gradle, add implementation("org.apache.tomcat.embed:tomcat-embed-core:11.0.26").

Replace the main method in McpServerApp.java:

public static void main(String[] args) throws Exception {
    int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080"));

    HttpServletStreamableServerTransportProvider transport = HttpServletStreamableServerTransportProvider.builder()
            .jsonMapper(McpJsonDefaults.getMapper())
            .mcpEndpoint("/mcp")
            .securityValidator(DefaultServerTransportSecurityValidator.builder()
                    .allowedHost("localhost:" + port)
                    .allowedHost("127.0.0.1:" + port)
                    .allowedOrigin("http://localhost:*")
                    .build())
            .build();

    McpServer.sync(transport)
            .serverInfo("demo", "0.1.0")
            .capabilities(ServerCapabilities.builder().tools(false).build())
            .tools(divideTool())
            .build();

    Tomcat tomcat = new Tomcat();
    tomcat.setPort(port);
    tomcat.getConnector();
    Context context = tomcat.addContext("", null);
    Tomcat.addServlet(context, "mcp", transport).setAsyncSupported(true);
    context.addServletMappingDecoded("/mcp", "mcp");
    tomcat.start();
    tomcat.getServer().await();
}

Add these imports: org.apache.catalina.Context, org.apache.catalina.startup.Tomcat, io.modelcontextprotocol.server.transport.DefaultServerTransportSecurityValidator, and io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider. Remove the import of StdioServerTransportProvider.

Clients connect to http://HOST:PORT/mcp. For VS Code, write .vscode/mcp.json:

{
  "servers": {
    "demo": {
      "type": "http",
      "url": "http://localhost:8080/mcp"
    }
  }
}

Spring Boot server

Use the MCP starters of Spring AI 2.0 or later. Spring AI 2.0 uses Spring Boot 4. Use spring-boot-starter-parent 4.0.x. Import org.springframework.ai:spring-ai-bom version 2.0.1 and add spring-ai-starter-mcp-server-webmvc. For WebFlux, add spring-ai-starter-mcp-server-webflux.

Write the tools as annotated methods on a Spring bean:

@Component
class CalculatorTools {

    @McpTool(name = "add", description = "Add two integers")
    public int add(@McpToolParam(description = "First number") int a,
            @McpToolParam(description = "Second number") int b) {
        return a + b;
    }
}

The annotations are in org.springframework.ai.mcp.annotation. Set the server identity and the transport in application.properties:

spring.ai.mcp.server.name=demo
spring.ai.mcp.server.version=0.1.0
spring.ai.mcp.server.protocol=STREAMABLE

The starter serves the endpoint at /mcp.

Gotchas

  • The HTTP transports do not check the Host and Origin headers by default. This also applies to the Spring AI starters. Without a check, a malicious web page can use DNS rebinding to call a local server. Always set securityValidator(DefaultServerTransportSecurityValidator.builder()...build()). The validator rejects an unknown Origin with HTTP 403 and an unknown Host with HTTP 421.

  • In Spring AI, declare your own WebMvcStreamableServerTransportProvider bean to set the validator. The starter then does not make its own bean:

    @Bean
    WebMvcStreamableServerTransportProvider webMvcStreamableServerTransportProvider() {
        return WebMvcStreamableServerTransportProvider.builder()
                .jsonMapper(McpJsonDefaults.getMapper())
                .mcpEndpoint("/mcp")
                .securityValidator(DefaultServerTransportSecurityValidator.builder()
                        .allowedHost("localhost:8080")
                        .allowedOrigin("http://localhost:*")
                        .build())
                .build();
    }
    
  • For WebFlux, declare a WebFluxStreamableServerTransportProvider bean in the same way. Its builder uses messageEndpoint("/mcp"), not mcpEndpoint.

  • Embedded Tomcat listens on all network interfaces. For a public host, add its name to allowedHost. Put authentication in front of the server.

  • HttpServletStatelessServerTransport makes a server without sessions. Use it with McpServer.sync(transport) when more than one replica must answer the requests. A stateless server cannot send requests to the client. Also set securityValidator on its builder.

  • For browser clients, add CORS to the servlet container. Allow the Mcp-* request headers. Expose the Mcp-Session-Id response header.