From c6f8b02ea2d4cb3334954fb4742ed9b72af6e9fb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Feb 2026 18:05:53 +0000 Subject: [PATCH 01/13] Add minimal Java MCP App example (basic-server-java) Single-file Java server using the MCP Java SDK (v0.17.2) with embedded Jetty. Registers a "get-time" tool whose inline HTML UI loads the @modelcontextprotocol/ext-apps SDK from CDN. Supports both HTTP/SSE and stdio transports. https://claude.ai/code/session_01UoPpdpXv9J26uSJadaMy8n --- examples/basic-server-java/pom.xml | 81 +++++++++++ .../modelcontextprotocol/examples/Main.java | 132 ++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 examples/basic-server-java/pom.xml create mode 100644 examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java diff --git a/examples/basic-server-java/pom.xml b/examples/basic-server-java/pom.xml new file mode 100644 index 000000000..cb37ad683 --- /dev/null +++ b/examples/basic-server-java/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + io.modelcontextprotocol.examples + basic-server-java + 1.0.0 + jar + + + 17 + ${java.version} + ${java.version} + UTF-8 + + + + + + io.modelcontextprotocol.sdk + mcp + 0.17.2 + + + + + org.eclipse.jetty + jetty-server + 12.0.16 + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + 12.0.16 + + + + + org.slf4j + slf4j-simple + 2.0.16 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + package + shade + + false + + + io.modelcontextprotocol.examples.Main + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java new file mode 100644 index 000000000..e8a5dd7e8 --- /dev/null +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java @@ -0,0 +1,132 @@ +package io.modelcontextprotocol.examples; + +import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapperSupplier; +import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.McpServerFeatures; +import io.modelcontextprotocol.spec.McpServerTransportProvider; +import io.modelcontextprotocol.server.transport.HttpServletSseServerTransportProvider; +import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; +import io.modelcontextprotocol.spec.McpSchema; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; + +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Minimal MCP App server in Java. + * + * Registers a "get-time" tool backed by an inline HTML UI (loaded from CDN). + * + * Run with HTTP transport (default, port 3001): + * java -jar basic-server-java.jar + * + * Run with stdio transport: + * java -jar basic-server-java.jar --stdio + */ +public class Main { + + static final String RESOURCE_MIME_TYPE = "text/html;profile=mcp-app"; + static final String RESOURCE_URI = "ui://get-time/index.html"; + + // Inline HTML: loads @modelcontextprotocol/ext-apps from CDN, displays server time. + static final String UI_HTML = """ + + + + + + Get Time + + +

Server time:

+ + + + """; + + public static void main(String[] args) throws Exception { + McpJsonMapper jsonMapper = new JacksonMcpJsonMapperSupplier().get(); + if (Arrays.asList(args).contains("--stdio")) { + runStdio(jsonMapper); + } else { + runHttp(jsonMapper); + } + } + + static void runStdio(McpJsonMapper jsonMapper) { + buildServer(new StdioServerTransportProvider(jsonMapper)); + // Block until stdin closes (transport drives the lifecycle) + } + + static void runHttp(McpJsonMapper jsonMapper) throws Exception { + int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "3001")); + var transport = HttpServletSseServerTransportProvider.builder() + .jsonMapper(jsonMapper) + .messageEndpoint("/mcp/message") + .build(); + + buildServer(transport); + + var context = new ServletContextHandler(); + context.addServlet(new ServletHolder(transport), "/*"); + + var server = new Server(port); + server.setHandler(context); + server.start(); + System.out.println("MCP server listening on http://localhost:" + port + "/sse"); + server.join(); + } + + static void buildServer(McpServerTransportProvider transport) { + var tool = new McpServerFeatures.SyncToolSpecification( + McpSchema.Tool.builder() + .name("get-time") + .description("Returns the current server time as an ISO 8601 string") + .inputSchema(new McpSchema.JsonSchema("object", null, null, null, null, null)) + .meta(Map.of( + // New key (ui.resourceUri) + legacy flat key (ui/resourceUri) for compat + "ui", Map.of("resourceUri", RESOURCE_URI), + "ui/resourceUri", RESOURCE_URI + )) + .build(), + (exchange, arguments) -> McpSchema.CallToolResult.builder() + .content(List.of(new McpSchema.TextContent(Instant.now().toString()))) + .isError(false) + .build() + ); + + var resource = new McpServerFeatures.SyncResourceSpecification( + McpSchema.Resource.builder() + .uri(RESOURCE_URI) + .name("Get Time UI") + .mimeType(RESOURCE_MIME_TYPE) + .build(), + (exchange, request) -> new McpSchema.ReadResourceResult(List.of( + new McpSchema.TextResourceContents(RESOURCE_URI, RESOURCE_MIME_TYPE, UI_HTML) + )) + ); + + McpServer.sync(transport) + .serverInfo("basic-server-java", "1.0.0") + .capabilities(McpSchema.ServerCapabilities.builder() + .tools(true) + .resources(false, false) + .build()) + .tools(tool) + .resources(resource) + .build(); + } +} From d9130c13761f260f19971fb01b57374aa56e3e41 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Feb 2026 20:02:36 +0000 Subject: [PATCH 02/13] Fix CDN URL for ext-apps SDK in inline HTML Use the correct versioned path used in the reference gist: https://unpkg.com/@modelcontextprotocol/ext-apps@1.0.1/dist/src/app-with-deps.js https://claude.ai/code/session_01UoPpdpXv9J26uSJadaMy8n --- .../src/main/java/io/modelcontextprotocol/examples/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java index e8a5dd7e8..126327de2 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java @@ -45,7 +45,7 @@ public class Main {

Server time:

- - - """; - public static void main(String[] args) throws Exception { McpJsonMapper jsonMapper = new JacksonMcpJsonMapperSupplier().get(); if (Arrays.asList(args).contains("--stdio")) { @@ -67,7 +31,7 @@ public static void main(String[] args) throws Exception { } static void runStdio(McpJsonMapper jsonMapper) { - buildServer(new StdioServerTransportProvider(jsonMapper)); + Server.createServer(new StdioServerTransportProvider(jsonMapper)); // Block until stdin closes (transport drives the lifecycle) } @@ -78,7 +42,7 @@ static void runHttp(McpJsonMapper jsonMapper) throws Exception { .messageEndpoint("/mcp/message") .build(); - buildServer(transport); + Server.createServer(transport); var context = new ServletContextHandler(); context.addServlet(new ServletHolder(transport), "/*"); @@ -89,44 +53,4 @@ static void runHttp(McpJsonMapper jsonMapper) throws Exception { System.out.println("MCP server listening on http://localhost:" + port + "/sse"); server.join(); } - - static void buildServer(McpServerTransportProvider transport) { - var tool = new McpServerFeatures.SyncToolSpecification( - McpSchema.Tool.builder() - .name("get-time") - .description("Returns the current server time as an ISO 8601 string") - .inputSchema(new McpSchema.JsonSchema("object", null, null, null, null, null)) - .meta(Map.of( - // New key (ui.resourceUri) + legacy flat key (ui/resourceUri) for compat - "ui", Map.of("resourceUri", RESOURCE_URI), - "ui/resourceUri", RESOURCE_URI - )) - .build(), - (exchange, arguments) -> McpSchema.CallToolResult.builder() - .content(List.of(new McpSchema.TextContent(Instant.now().toString()))) - .isError(false) - .build() - ); - - var resource = new McpServerFeatures.SyncResourceSpecification( - McpSchema.Resource.builder() - .uri(RESOURCE_URI) - .name("Get Time UI") - .mimeType(RESOURCE_MIME_TYPE) - .build(), - (exchange, request) -> new McpSchema.ReadResourceResult(List.of( - new McpSchema.TextResourceContents(RESOURCE_URI, RESOURCE_MIME_TYPE, UI_HTML) - )) - ); - - McpServer.sync(transport) - .serverInfo("basic-server-java", "1.0.0") - .capabilities(McpSchema.ServerCapabilities.builder() - .tools(true) - .resources(false, false) - .build()) - .tools(tool) - .resources(resource) - .build(); - } } diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java new file mode 100644 index 000000000..5d0efcd64 --- /dev/null +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java @@ -0,0 +1,83 @@ +package io.modelcontextprotocol.examples; + +import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.McpServerFeatures; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpServerTransportProvider; + +import java.time.Instant; +import java.util.List; +import java.util.Map; + +/** + * MCP server definition: registers the "get-time" tool and its inline HTML UI resource. + */ +public class Server { + + static final String RESOURCE_MIME_TYPE = "text/html;profile=mcp-app"; + static final String RESOURCE_URI = "ui://get-time/index.html"; + + // Inline HTML: loads @modelcontextprotocol/ext-apps from CDN, displays server time. + static final String UI_HTML = """ + + + + + + Get Time + + +

Server time:

+ + + + """; + + static void createServer(McpServerTransportProvider transport) { + var tool = new McpServerFeatures.SyncToolSpecification( + McpSchema.Tool.builder() + .name("get-time") + .description("Returns the current server time as an ISO 8601 string") + .inputSchema(new McpSchema.JsonSchema("object", null, null, null, null, null)) + .meta(Map.of( + // New key (ui.resourceUri) + legacy flat key (ui/resourceUri) for compat + "ui", Map.of("resourceUri", RESOURCE_URI), + "ui/resourceUri", RESOURCE_URI + )) + .build(), + (exchange, arguments) -> McpSchema.CallToolResult.builder() + .content(List.of(new McpSchema.TextContent(Instant.now().toString()))) + .isError(false) + .build() + ); + + var resource = new McpServerFeatures.SyncResourceSpecification( + McpSchema.Resource.builder() + .uri(RESOURCE_URI) + .name("Get Time UI") + .mimeType(RESOURCE_MIME_TYPE) + .build(), + (exchange, request) -> new McpSchema.ReadResourceResult(List.of( + new McpSchema.TextResourceContents(RESOURCE_URI, RESOURCE_MIME_TYPE, UI_HTML) + )) + ); + + McpServer.sync(transport) + .serverInfo("basic-server-java", "1.0.0") + .capabilities(McpSchema.ServerCapabilities.builder() + .tools(true) + .resources(false, false) + .build()) + .tools(tool) + .resources(resource) + .build(); + } +} From 3e2897f303944273ffd13986ec8157c399bc5c27 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 12 Feb 2026 20:28:42 +0000 Subject: [PATCH 04/13] Fix Java example: wrong jackson package name + Jetty Server name collision --- .../main/java/io/modelcontextprotocol/examples/Main.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java index 2183d6d8c..ac75afd83 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java @@ -1,13 +1,11 @@ package io.modelcontextprotocol.examples; import io.modelcontextprotocol.json.McpJsonMapper; -import io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapperSupplier; +import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapperSupplier; import io.modelcontextprotocol.server.transport.HttpServletSseServerTransportProvider; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; -import org.eclipse.jetty.server.Server; - import java.util.Arrays; /** @@ -47,7 +45,7 @@ static void runHttp(McpJsonMapper jsonMapper) throws Exception { var context = new ServletContextHandler(); context.addServlet(new ServletHolder(transport), "/*"); - var server = new Server(port); + var server = new org.eclipse.jetty.server.Server(port); server.setHandler(context); server.start(); System.out.println("MCP server listening on http://localhost:" + port + "/sse"); From 8077391cb0193491bccf1761a455f7115f298a47 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 12 Feb 2026 20:32:50 +0000 Subject: [PATCH 05/13] Add CORS filter to Java example for cross-origin basic-host access --- .../modelcontextprotocol/examples/Main.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java index ac75afd83..6ae9b0d51 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java @@ -4,9 +4,15 @@ import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapperSupplier; import io.modelcontextprotocol.server.transport.HttpServletSseServerTransportProvider; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee10.servlet.FilterHolder; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; +import java.io.IOException; import java.util.Arrays; +import java.util.EnumSet; /** * Entry point for the basic-server-java MCP App example. @@ -43,6 +49,7 @@ static void runHttp(McpJsonMapper jsonMapper) throws Exception { Server.createServer(transport); var context = new ServletContextHandler(); + context.addFilter(new FilterHolder(new CorsFilter()), "/*", EnumSet.of(DispatcherType.REQUEST)); context.addServlet(new ServletHolder(transport), "/*"); var server = new org.eclipse.jetty.server.Server(port); @@ -51,4 +58,22 @@ static void runHttp(McpJsonMapper jsonMapper) throws Exception { System.out.println("MCP server listening on http://localhost:" + port + "/sse"); server.join(); } + + /** Simple CORS filter that allows all origins (mirrors the cors() middleware used by JS examples). */ + static class CorsFilter implements Filter { + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + var response = (HttpServletResponse) res; + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); + response.setHeader("Access-Control-Allow-Headers", "*"); + + if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) { + response.setStatus(HttpServletResponse.SC_OK); + return; + } + chain.doFilter(req, res); + } + } } From 314e7a364466049fb919a5f9bdcd2a75453b80ef Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 12 Feb 2026 20:34:59 +0000 Subject: [PATCH 06/13] Switch Java example from SSE to Streamable HTTP transport (matches /mcp endpoint) --- .../java/io/modelcontextprotocol/examples/Main.java | 9 ++++----- .../io/modelcontextprotocol/examples/Server.java | 12 ++++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java index 6ae9b0d51..64376f933 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java @@ -2,7 +2,7 @@ import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapperSupplier; -import io.modelcontextprotocol.server.transport.HttpServletSseServerTransportProvider; +import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; @@ -41,21 +41,20 @@ static void runStdio(McpJsonMapper jsonMapper) { static void runHttp(McpJsonMapper jsonMapper) throws Exception { int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "3001")); - var transport = HttpServletSseServerTransportProvider.builder() + var transport = HttpServletStreamableServerTransportProvider.builder() .jsonMapper(jsonMapper) - .messageEndpoint("/mcp/message") .build(); Server.createServer(transport); var context = new ServletContextHandler(); context.addFilter(new FilterHolder(new CorsFilter()), "/*", EnumSet.of(DispatcherType.REQUEST)); - context.addServlet(new ServletHolder(transport), "/*"); + context.addServlet(new ServletHolder(transport), "/mcp"); var server = new org.eclipse.jetty.server.Server(port); server.setHandler(context); server.start(); - System.out.println("MCP server listening on http://localhost:" + port + "/sse"); + System.out.println("MCP server listening on http://localhost:" + port + "/mcp"); server.join(); } diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java index 5d0efcd64..4d1a0c82e 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java @@ -4,6 +4,7 @@ import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerTransportProvider; +import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider; import java.time.Instant; import java.util.List; @@ -42,6 +43,14 @@ public class Server { """; static void createServer(McpServerTransportProvider transport) { + configureServer(McpServer.sync(transport)); + } + + static void createServer(McpStreamableServerTransportProvider transport) { + configureServer(McpServer.sync(transport)); + } + + private static void configureServer(McpServer.SyncSpecification spec) { var tool = new McpServerFeatures.SyncToolSpecification( McpSchema.Tool.builder() .name("get-time") @@ -70,8 +79,7 @@ static void createServer(McpServerTransportProvider transport) { )) ); - McpServer.sync(transport) - .serverInfo("basic-server-java", "1.0.0") + spec.serverInfo("basic-server-java", "1.0.0") .capabilities(McpSchema.ServerCapabilities.builder() .tools(true) .resources(false, false) From 35dad9151a9e2bdb05d04c361b973ad712b2eaa5 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 12 Feb 2026 20:49:47 +0000 Subject: [PATCH 07/13] Use stateless HTTP transport for Java example (matches JS examples' sessionIdGenerator: undefined) --- .../modelcontextprotocol/examples/Main.java | 6 +- .../modelcontextprotocol/examples/Server.java | 85 +++++++++++-------- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java index 64376f933..55eccf22b 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java @@ -2,7 +2,7 @@ import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapperSupplier; -import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; +import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; @@ -41,11 +41,11 @@ static void runStdio(McpJsonMapper jsonMapper) { static void runHttp(McpJsonMapper jsonMapper) throws Exception { int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "3001")); - var transport = HttpServletStreamableServerTransportProvider.builder() + var transport = HttpServletStatelessServerTransport.builder() .jsonMapper(jsonMapper) .build(); - Server.createServer(transport); + Server.createStatelessServer(transport); var context = new ServletContextHandler(); context.addFilter(new FilterHolder(new CorsFilter()), "/*", EnumSet.of(DispatcherType.REQUEST)); diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java index 4d1a0c82e..47a353f31 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java @@ -2,9 +2,10 @@ import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.McpServerFeatures; +import io.modelcontextprotocol.server.McpStatelessServerFeatures; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerTransportProvider; -import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider; +import io.modelcontextprotocol.spec.McpStatelessServerTransport; import java.time.Instant; import java.util.List; @@ -42,50 +43,62 @@ public class Server { """; - static void createServer(McpServerTransportProvider transport) { - configureServer(McpServer.sync(transport)); - } + private static final McpSchema.Tool TOOL_DEF = McpSchema.Tool.builder() + .name("get-time") + .description("Returns the current server time as an ISO 8601 string") + .inputSchema(new McpSchema.JsonSchema("object", null, null, null, null, null)) + .meta(Map.of( + "ui", Map.of("resourceUri", RESOURCE_URI), + "ui/resourceUri", RESOURCE_URI + )) + .build(); + + private static final McpSchema.Resource RESOURCE_DEF = McpSchema.Resource.builder() + .uri(RESOURCE_URI) + .name("Get Time UI") + .mimeType(RESOURCE_MIME_TYPE) + .build(); - static void createServer(McpStreamableServerTransportProvider transport) { - configureServer(McpServer.sync(transport)); + private static McpSchema.CallToolResult handleGetTime() { + return McpSchema.CallToolResult.builder() + .content(List.of(new McpSchema.TextContent(Instant.now().toString()))) + .isError(false) + .build(); } - private static void configureServer(McpServer.SyncSpecification spec) { - var tool = new McpServerFeatures.SyncToolSpecification( - McpSchema.Tool.builder() - .name("get-time") - .description("Returns the current server time as an ISO 8601 string") - .inputSchema(new McpSchema.JsonSchema("object", null, null, null, null, null)) - .meta(Map.of( - // New key (ui.resourceUri) + legacy flat key (ui/resourceUri) for compat - "ui", Map.of("resourceUri", RESOURCE_URI), - "ui/resourceUri", RESOURCE_URI - )) - .build(), - (exchange, arguments) -> McpSchema.CallToolResult.builder() - .content(List.of(new McpSchema.TextContent(Instant.now().toString()))) - .isError(false) - .build() - ); + private static McpSchema.ReadResourceResult handleReadResource() { + return new McpSchema.ReadResourceResult(List.of( + new McpSchema.TextResourceContents(RESOURCE_URI, RESOURCE_MIME_TYPE, UI_HTML) + )); + } - var resource = new McpServerFeatures.SyncResourceSpecification( - McpSchema.Resource.builder() - .uri(RESOURCE_URI) - .name("Get Time UI") - .mimeType(RESOURCE_MIME_TYPE) - .build(), - (exchange, request) -> new McpSchema.ReadResourceResult(List.of( - new McpSchema.TextResourceContents(RESOURCE_URI, RESOURCE_MIME_TYPE, UI_HTML) - )) - ); + /** Stateful server (stdio transport). */ + static void createServer(McpServerTransportProvider transport) { + McpServer.sync(transport) + .serverInfo("basic-server-java", "1.0.0") + .capabilities(McpSchema.ServerCapabilities.builder() + .tools(true) + .resources(false, false) + .build()) + .tools(new McpServerFeatures.SyncToolSpecification( + TOOL_DEF, (exchange, arguments) -> handleGetTime())) + .resources(new McpServerFeatures.SyncResourceSpecification( + RESOURCE_DEF, (exchange, request) -> handleReadResource())) + .build(); + } - spec.serverInfo("basic-server-java", "1.0.0") + /** Stateless server (HTTP transport, matches JS examples' sessionIdGenerator: undefined). */ + static void createStatelessServer(McpStatelessServerTransport transport) { + McpServer.sync(transport) + .serverInfo("basic-server-java", "1.0.0") .capabilities(McpSchema.ServerCapabilities.builder() .tools(true) .resources(false, false) .build()) - .tools(tool) - .resources(resource) + .tools(new McpStatelessServerFeatures.SyncToolSpecification( + TOOL_DEF, (ctx, request) -> handleGetTime())) + .resources(new McpStatelessServerFeatures.SyncResourceSpecification( + RESOURCE_DEF, (ctx, request) -> handleReadResource())) .build(); } } From 09ff6917d3f7c73afa7db8d911d7e18bf75781bd Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 12 Feb 2026 21:00:02 +0000 Subject: [PATCH 08/13] Consolidate Java example into single Main.java file --- .../modelcontextprotocol/examples/Main.java | 126 ++++++++++++------ .../modelcontextprotocol/examples/Server.java | 104 --------------- 2 files changed, 84 insertions(+), 146 deletions(-) delete mode 100644 examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java index 55eccf22b..9206fd263 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java @@ -1,54 +1,114 @@ package io.modelcontextprotocol.examples; -import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapperSupplier; +import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.McpServerFeatures; +import io.modelcontextprotocol.server.McpStatelessServerFeatures; import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; +import io.modelcontextprotocol.spec.McpSchema; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.ee10.servlet.FilterHolder; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; + import java.io.IOException; -import java.util.Arrays; +import java.time.Instant; import java.util.EnumSet; +import java.util.List; +import java.util.Map; /** - * Entry point for the basic-server-java MCP App example. - * - * Run with HTTP transport (default, port 3001): - * java -jar basic-server-java.jar + * Minimal MCP App server in Java: a "get-time" tool with an inline HTML UI. * - * Run with stdio transport: - * java -jar basic-server-java.jar --stdio + * HTTP (default): java -jar basic-server-java.jar + * Stdio: java -jar basic-server-java.jar --stdio */ public class Main { - public static void main(String[] args) throws Exception { - McpJsonMapper jsonMapper = new JacksonMcpJsonMapperSupplier().get(); - if (Arrays.asList(args).contains("--stdio")) { - runStdio(jsonMapper); - } else { - runHttp(jsonMapper); - } + static final String RESOURCE_URI = "ui://get-time/index.html"; + static final String RESOURCE_MIME = "text/html;profile=mcp-app"; + + static final String UI_HTML = """ + + + + + + Get Time + + +

Server time:

+ + + + """; + + static final McpSchema.Tool TOOL = McpSchema.Tool.builder() + .name("get-time") + .description("Returns the current server time as an ISO 8601 string") + .inputSchema(new McpSchema.JsonSchema("object", null, null, null, null, null)) + .meta(Map.of("ui", Map.of("resourceUri", RESOURCE_URI))) + .build(); + + static final McpSchema.Resource RESOURCE = McpSchema.Resource.builder() + .uri(RESOURCE_URI).name("Get Time UI").mimeType(RESOURCE_MIME).build(); + + static McpSchema.CallToolResult getTime() { + return McpSchema.CallToolResult.builder() + .content(List.of(new McpSchema.TextContent(Instant.now().toString()))).build(); } - static void runStdio(McpJsonMapper jsonMapper) { - Server.createServer(new StdioServerTransportProvider(jsonMapper)); - // Block until stdin closes (transport drives the lifecycle) + static McpSchema.ReadResourceResult readResource() { + return new McpSchema.ReadResourceResult(List.of(new McpSchema.TextResourceContents( + RESOURCE_URI, RESOURCE_MIME, UI_HTML, + Map.of("ui", Map.of("csp", Map.of("resourceDomains", List.of("https://unpkg.com"))))))); } - static void runHttp(McpJsonMapper jsonMapper) throws Exception { + // ── entry point ────────────────────────────────────────────────────── + + public static void main(String[] args) throws Exception { + var json = new JacksonMcpJsonMapperSupplier().get(); + + if (List.of(args).contains("--stdio")) { + McpServer.sync(new StdioServerTransportProvider(json)) + .serverInfo("basic-server-java", "1.0.0") + .tools(new McpServerFeatures.SyncToolSpecification(TOOL, (ex, a) -> getTime())) + .resources(new McpServerFeatures.SyncResourceSpecification(RESOURCE, (ex, r) -> readResource())) + .build(); + return; // stdin drives the lifecycle + } + + // Stateless HTTP (matches JS examples) int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "3001")); - var transport = HttpServletStatelessServerTransport.builder() - .jsonMapper(jsonMapper) - .build(); + var transport = HttpServletStatelessServerTransport.builder().jsonMapper(json).build(); - Server.createStatelessServer(transport); + McpServer.sync(transport) + .serverInfo("basic-server-java", "1.0.0") + .tools(new McpStatelessServerFeatures.SyncToolSpecification(TOOL, (ctx, r) -> getTime())) + .resources(new McpStatelessServerFeatures.SyncResourceSpecification(RESOURCE, (ctx, r) -> readResource())) + .build(); var context = new ServletContextHandler(); - context.addFilter(new FilterHolder(new CorsFilter()), "/*", EnumSet.of(DispatcherType.REQUEST)); + context.addFilter(new FilterHolder((Filter) (req, res, chain) -> { + ((HttpServletResponse) res).setHeader("Access-Control-Allow-Origin", "*"); + ((HttpServletResponse) res).setHeader("Access-Control-Allow-Headers", "*"); + if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) { + ((HttpServletResponse) res).setStatus(200); + return; + } + chain.doFilter(req, res); + }), "/*", EnumSet.of(DispatcherType.REQUEST)); context.addServlet(new ServletHolder(transport), "/mcp"); var server = new org.eclipse.jetty.server.Server(port); @@ -57,22 +117,4 @@ static void runHttp(McpJsonMapper jsonMapper) throws Exception { System.out.println("MCP server listening on http://localhost:" + port + "/mcp"); server.join(); } - - /** Simple CORS filter that allows all origins (mirrors the cors() middleware used by JS examples). */ - static class CorsFilter implements Filter { - @Override - public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) - throws IOException, ServletException { - var response = (HttpServletResponse) res; - response.setHeader("Access-Control-Allow-Origin", "*"); - response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); - response.setHeader("Access-Control-Allow-Headers", "*"); - - if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) { - response.setStatus(HttpServletResponse.SC_OK); - return; - } - chain.doFilter(req, res); - } - } } diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java deleted file mode 100644 index 47a353f31..000000000 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java +++ /dev/null @@ -1,104 +0,0 @@ -package io.modelcontextprotocol.examples; - -import io.modelcontextprotocol.server.McpServer; -import io.modelcontextprotocol.server.McpServerFeatures; -import io.modelcontextprotocol.server.McpStatelessServerFeatures; -import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpServerTransportProvider; -import io.modelcontextprotocol.spec.McpStatelessServerTransport; - -import java.time.Instant; -import java.util.List; -import java.util.Map; - -/** - * MCP server definition: registers the "get-time" tool and its inline HTML UI resource. - */ -public class Server { - - static final String RESOURCE_MIME_TYPE = "text/html;profile=mcp-app"; - static final String RESOURCE_URI = "ui://get-time/index.html"; - - // Inline HTML: loads @modelcontextprotocol/ext-apps from CDN, displays server time. - static final String UI_HTML = """ - - - - - - Get Time - - -

Server time:

- - - - """; - - private static final McpSchema.Tool TOOL_DEF = McpSchema.Tool.builder() - .name("get-time") - .description("Returns the current server time as an ISO 8601 string") - .inputSchema(new McpSchema.JsonSchema("object", null, null, null, null, null)) - .meta(Map.of( - "ui", Map.of("resourceUri", RESOURCE_URI), - "ui/resourceUri", RESOURCE_URI - )) - .build(); - - private static final McpSchema.Resource RESOURCE_DEF = McpSchema.Resource.builder() - .uri(RESOURCE_URI) - .name("Get Time UI") - .mimeType(RESOURCE_MIME_TYPE) - .build(); - - private static McpSchema.CallToolResult handleGetTime() { - return McpSchema.CallToolResult.builder() - .content(List.of(new McpSchema.TextContent(Instant.now().toString()))) - .isError(false) - .build(); - } - - private static McpSchema.ReadResourceResult handleReadResource() { - return new McpSchema.ReadResourceResult(List.of( - new McpSchema.TextResourceContents(RESOURCE_URI, RESOURCE_MIME_TYPE, UI_HTML) - )); - } - - /** Stateful server (stdio transport). */ - static void createServer(McpServerTransportProvider transport) { - McpServer.sync(transport) - .serverInfo("basic-server-java", "1.0.0") - .capabilities(McpSchema.ServerCapabilities.builder() - .tools(true) - .resources(false, false) - .build()) - .tools(new McpServerFeatures.SyncToolSpecification( - TOOL_DEF, (exchange, arguments) -> handleGetTime())) - .resources(new McpServerFeatures.SyncResourceSpecification( - RESOURCE_DEF, (exchange, request) -> handleReadResource())) - .build(); - } - - /** Stateless server (HTTP transport, matches JS examples' sessionIdGenerator: undefined). */ - static void createStatelessServer(McpStatelessServerTransport transport) { - McpServer.sync(transport) - .serverInfo("basic-server-java", "1.0.0") - .capabilities(McpSchema.ServerCapabilities.builder() - .tools(true) - .resources(false, false) - .build()) - .tools(new McpStatelessServerFeatures.SyncToolSpecification( - TOOL_DEF, (ctx, request) -> handleGetTime())) - .resources(new McpStatelessServerFeatures.SyncResourceSpecification( - RESOURCE_DEF, (ctx, request) -> handleReadResource())) - .build(); - } -} From efefa589d37e40c775c74bbfbfa1989e211c4696 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 12 Feb 2026 21:01:46 +0000 Subject: [PATCH 09/13] Split back into Main.java + Server.java; return structuredContent --- .../modelcontextprotocol/examples/Main.java | 74 +--------------- .../modelcontextprotocol/examples/Server.java | 86 +++++++++++++++++++ 2 files changed, 90 insertions(+), 70 deletions(-) create mode 100644 examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java index 9206fd263..b40dd7e7e 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java @@ -1,12 +1,8 @@ package io.modelcontextprotocol.examples; import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapperSupplier; -import io.modelcontextprotocol.server.McpServer; -import io.modelcontextprotocol.server.McpServerFeatures; -import io.modelcontextprotocol.server.McpStatelessServerFeatures; import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; -import io.modelcontextprotocol.spec.McpSchema; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -14,90 +10,28 @@ import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; -import java.io.IOException; -import java.time.Instant; import java.util.EnumSet; import java.util.List; -import java.util.Map; /** - * Minimal MCP App server in Java: a "get-time" tool with an inline HTML UI. + * Entry point for the basic-server-java MCP App example. * * HTTP (default): java -jar basic-server-java.jar * Stdio: java -jar basic-server-java.jar --stdio */ public class Main { - static final String RESOURCE_URI = "ui://get-time/index.html"; - static final String RESOURCE_MIME = "text/html;profile=mcp-app"; - - static final String UI_HTML = """ - - - - - - Get Time - - -

Server time:

- - - - """; - - static final McpSchema.Tool TOOL = McpSchema.Tool.builder() - .name("get-time") - .description("Returns the current server time as an ISO 8601 string") - .inputSchema(new McpSchema.JsonSchema("object", null, null, null, null, null)) - .meta(Map.of("ui", Map.of("resourceUri", RESOURCE_URI))) - .build(); - - static final McpSchema.Resource RESOURCE = McpSchema.Resource.builder() - .uri(RESOURCE_URI).name("Get Time UI").mimeType(RESOURCE_MIME).build(); - - static McpSchema.CallToolResult getTime() { - return McpSchema.CallToolResult.builder() - .content(List.of(new McpSchema.TextContent(Instant.now().toString()))).build(); - } - - static McpSchema.ReadResourceResult readResource() { - return new McpSchema.ReadResourceResult(List.of(new McpSchema.TextResourceContents( - RESOURCE_URI, RESOURCE_MIME, UI_HTML, - Map.of("ui", Map.of("csp", Map.of("resourceDomains", List.of("https://unpkg.com"))))))); - } - - // ── entry point ────────────────────────────────────────────────────── - public static void main(String[] args) throws Exception { var json = new JacksonMcpJsonMapperSupplier().get(); if (List.of(args).contains("--stdio")) { - McpServer.sync(new StdioServerTransportProvider(json)) - .serverInfo("basic-server-java", "1.0.0") - .tools(new McpServerFeatures.SyncToolSpecification(TOOL, (ex, a) -> getTime())) - .resources(new McpServerFeatures.SyncResourceSpecification(RESOURCE, (ex, r) -> readResource())) - .build(); - return; // stdin drives the lifecycle + Server.create(new StdioServerTransportProvider(json)); + return; } - // Stateless HTTP (matches JS examples) int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "3001")); var transport = HttpServletStatelessServerTransport.builder().jsonMapper(json).build(); - - McpServer.sync(transport) - .serverInfo("basic-server-java", "1.0.0") - .tools(new McpStatelessServerFeatures.SyncToolSpecification(TOOL, (ctx, r) -> getTime())) - .resources(new McpStatelessServerFeatures.SyncResourceSpecification(RESOURCE, (ctx, r) -> readResource())) - .build(); + Server.create(transport); var context = new ServletContextHandler(); context.addFilter(new FilterHolder((Filter) (req, res, chain) -> { diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java new file mode 100644 index 000000000..1a984af8c --- /dev/null +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java @@ -0,0 +1,86 @@ +package io.modelcontextprotocol.examples; + +import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.McpServerFeatures; +import io.modelcontextprotocol.server.McpStatelessServerFeatures; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpServerTransportProvider; +import io.modelcontextprotocol.spec.McpStatelessServerTransport; + +import java.time.Instant; +import java.util.List; +import java.util.Map; + +/** + * MCP server definition: registers a "get-time" tool with an inline HTML UI resource. + */ +public class Server { + + static final String RESOURCE_URI = "ui://get-time/index.html"; + static final String RESOURCE_MIME = "text/html;profile=mcp-app"; + + static final String UI_HTML = """ + + + + + + Get Time + + +

Server time:

+ + + + """; + + static final McpSchema.Tool TOOL = McpSchema.Tool.builder() + .name("get-time") + .description("Returns the current server time as an ISO 8601 string") + .inputSchema(new McpSchema.JsonSchema("object", null, null, null, null, null)) + .meta(Map.of("ui", Map.of("resourceUri", RESOURCE_URI))) + .build(); + + static final McpSchema.Resource RESOURCE = McpSchema.Resource.builder() + .uri(RESOURCE_URI).name("Get Time UI").mimeType(RESOURCE_MIME).build(); + + static McpSchema.CallToolResult getTime() { + var time = Instant.now().toString(); + return McpSchema.CallToolResult.builder() + .content(List.of(new McpSchema.TextContent(time))) + .structuredContent(Map.of("time", time)) + .build(); + } + + static McpSchema.ReadResourceResult readResource() { + return new McpSchema.ReadResourceResult(List.of(new McpSchema.TextResourceContents( + RESOURCE_URI, RESOURCE_MIME, UI_HTML, + Map.of("ui", Map.of("csp", Map.of("resourceDomains", List.of("https://unpkg.com"))))))); + } + + /** Stateful server (stdio). */ + static void create(McpServerTransportProvider transport) { + McpServer.sync(transport) + .serverInfo("basic-server-java", "1.0.0") + .tools(new McpServerFeatures.SyncToolSpecification(TOOL, (ex, a) -> getTime())) + .resources(new McpServerFeatures.SyncResourceSpecification(RESOURCE, (ex, r) -> readResource())) + .build(); + } + + /** Stateless server (HTTP, matches JS examples). */ + static void create(McpStatelessServerTransport transport) { + McpServer.sync(transport) + .serverInfo("basic-server-java", "1.0.0") + .tools(new McpStatelessServerFeatures.SyncToolSpecification(TOOL, (ctx, r) -> getTime())) + .resources(new McpStatelessServerFeatures.SyncResourceSpecification(RESOURCE, (ctx, r) -> readResource())) + .build(); + } +} From 3ed0b4963b2ac9bd4f56adf0c419dbc804ab4f52 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 12 Feb 2026 21:06:08 +0000 Subject: [PATCH 10/13] Fix UI: use ontoolresult (not ontoolinput) to display time, await connect() --- .../main/java/io/modelcontextprotocol/examples/Server.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java index 1a984af8c..7ff3c09d9 100644 --- a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java +++ b/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java @@ -32,11 +32,11 @@ public class Server { From b288c5ce6bf47a5d07c81460eac45f37cfe96d23 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 12 Feb 2026 21:09:50 +0000 Subject: [PATCH 11/13] =?UTF-8?q?Rename=20basic-server-java=20=E2=86=92=20?= =?UTF-8?q?inlined-server-java;=20drop=20unused=20ext-apps=20npm=20dep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++++---- examples/basic-server-java/package.json | 14 -------------- examples/inlined-server-java/package.json | 11 +++++++++++ .../pom.xml | 4 ++-- .../io/modelcontextprotocol/examples/Main.java | 0 .../io/modelcontextprotocol/examples/Server.java | 0 package-lock.json | 8 ++++++++ 7 files changed, 26 insertions(+), 20 deletions(-) delete mode 100644 examples/basic-server-java/package.json create mode 100644 examples/inlined-server-java/package.json rename examples/{basic-server-java => inlined-server-java}/pom.xml (96%) rename examples/{basic-server-java => inlined-server-java}/src/main/java/io/modelcontextprotocol/examples/Main.java (100%) rename examples/{basic-server-java => inlined-server-java}/src/main/java/io/modelcontextprotocol/examples/Server.java (100%) diff --git a/README.md b/README.md index 92d8a84a0..5a3f06b78 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,8 @@ The [`examples/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/exa | | | |:---:|:---| -| [![Basic](examples/basic-server-react/grid-cell.png "Starter template")](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react) | The same app built with different frameworks — pick your favorite!

[React](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react) · [Vue](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vue) · [Svelte](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-svelte) · [Preact](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-preact) · [Solid](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-solid) · [Vanilla JS](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs) · [Java](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-java) | +| [![Basic](examples/basic-server-react/grid-cell.png "Starter template")](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react) | The same app built with different frameworks — pick your favorite!

[React](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react) · [Vue](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vue) · [Svelte](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-svelte) · [Preact](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-preact) · [Solid](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-solid) · [Vanilla JS](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs) | +| [![Basic](examples/basic-server-react/grid-cell.png "Inlined Java")](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/inlined-server-java) | [**Inlined Java**](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/inlined-server-java) — MCP App server in Java with the UI inlined as an HTML string (no frontend build step). Loads the SDK from CDN. | ### Running the Examples @@ -98,7 +99,7 @@ The [`examples/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/exa Most examples require only Node.js 18+. A few have additional requirements: - **Python examples** (`qr-server`, `say-server`): [uv](https://docs.astral.sh/uv/getting-started/installation/) -- **Java example** (`basic-server-java`): Java 17+ and Maven 3.6+ +- **Java example** (`inlined-server-java`): Java 17+ and Maven 3.6+ #### With basic-host @@ -317,7 +318,7 @@ To use these examples with MCP clients that support the stdio transport (such as "command": "bash", "args": [ "-c", - "cd /path/to/ext-apps/examples/basic-server-java && mvn -B package -DskipTests -q >&2 && java -jar target/basic-server-java-1.0.0.jar --stdio" + "cd /path/to/ext-apps/examples/inlined-server-java && mvn -B package -DskipTests -q >&2 && java -jar target/inlined-server-java-1.0.0.jar --stdio" ] }, "qr": { @@ -502,7 +503,7 @@ Then configure your MCP client to build and run the local server. Replace `~/cod "command": "bash", "args": [ "-c", - "cd ~/code/ext-apps/examples/basic-server-java && mvn -B package -DskipTests -q >&2 && java -jar target/basic-server-java-1.0.0.jar --stdio" + "cd ~/code/ext-apps/examples/inlined-server-java && mvn -B package -DskipTests -q >&2 && java -jar target/inlined-server-java-1.0.0.jar --stdio" ] }, "qr": { diff --git a/examples/basic-server-java/package.json b/examples/basic-server-java/package.json deleted file mode 100644 index 66d9c9cb3..000000000 --- a/examples/basic-server-java/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "@modelcontextprotocol/server-basic-java", - "version": "1.0.0", - "private": true, - "description": "Minimal MCP App server in Java", - "scripts": { - "build": "mvn -B package -DskipTests -q", - "start": "mvn -B package -DskipTests -q && java -jar target/basic-server-java-1.0.0.jar", - "dev": "mvn -B package -DskipTests -q && java -jar target/basic-server-java-1.0.0.jar" - }, - "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.0.0" - } -} diff --git a/examples/inlined-server-java/package.json b/examples/inlined-server-java/package.json new file mode 100644 index 000000000..d571cba51 --- /dev/null +++ b/examples/inlined-server-java/package.json @@ -0,0 +1,11 @@ +{ + "name": "@modelcontextprotocol/server-inlined-java", + "version": "1.0.0", + "private": true, + "description": "MCP App server in Java with inlined HTML UI (no frontend build step)", + "scripts": { + "build": "mvn -B package -DskipTests -q", + "start": "mvn -B package -DskipTests -q && java -jar target/inlined-server-java-1.0.0.jar", + "dev": "mvn -B package -DskipTests -q && java -jar target/inlined-server-java-1.0.0.jar" + } +} diff --git a/examples/basic-server-java/pom.xml b/examples/inlined-server-java/pom.xml similarity index 96% rename from examples/basic-server-java/pom.xml rename to examples/inlined-server-java/pom.xml index cb37ad683..4fde40725 100644 --- a/examples/basic-server-java/pom.xml +++ b/examples/inlined-server-java/pom.xml @@ -5,7 +5,7 @@ 4.0.0 io.modelcontextprotocol.examples - basic-server-java + inlined-server-java 1.0.0 jar @@ -24,7 +24,7 @@ 0.17.2 - + org.eclipse.jetty jetty-server diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java similarity index 100% rename from examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java rename to examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java diff --git a/examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java b/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java similarity index 100% rename from examples/basic-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java rename to examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java diff --git a/package-lock.json b/package-lock.json index 7aa9ef932..ffc2df8a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -557,6 +557,10 @@ "dev": true, "license": "MIT" }, + "examples/inlined-server-java": { + "name": "@modelcontextprotocol/server-inlined-java", + "version": "1.0.0" + }, "examples/integration-server": { "version": "1.0.0", "dependencies": { @@ -2657,6 +2661,10 @@ "resolved": "examples/debug-server", "link": true }, + "node_modules/@modelcontextprotocol/server-inlined-java": { + "resolved": "examples/inlined-server-java", + "link": true + }, "node_modules/@modelcontextprotocol/server-map": { "resolved": "examples/map-server", "link": true From dbe81fe77d75b7b8aeb182b0638c0ba4124e5fb9 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 12 Feb 2026 21:10:50 +0000 Subject: [PATCH 12/13] Fix stale basic-server-java references in Java source --- .../main/java/io/modelcontextprotocol/examples/Main.java | 6 +++--- .../main/java/io/modelcontextprotocol/examples/Server.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java b/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java index b40dd7e7e..ca79d88d6 100644 --- a/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java +++ b/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Main.java @@ -14,10 +14,10 @@ import java.util.List; /** - * Entry point for the basic-server-java MCP App example. + * Entry point for the inlined-server-java MCP App example. * - * HTTP (default): java -jar basic-server-java.jar - * Stdio: java -jar basic-server-java.jar --stdio + * HTTP (default): java -jar inlined-server-java.jar + * Stdio: java -jar inlined-server-java.jar --stdio */ public class Main { diff --git a/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java b/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java index 7ff3c09d9..7adf267c3 100644 --- a/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java +++ b/examples/inlined-server-java/src/main/java/io/modelcontextprotocol/examples/Server.java @@ -69,7 +69,7 @@ static McpSchema.ReadResourceResult readResource() { /** Stateful server (stdio). */ static void create(McpServerTransportProvider transport) { McpServer.sync(transport) - .serverInfo("basic-server-java", "1.0.0") + .serverInfo("inlined-server-java", "1.0.0") .tools(new McpServerFeatures.SyncToolSpecification(TOOL, (ex, a) -> getTime())) .resources(new McpServerFeatures.SyncResourceSpecification(RESOURCE, (ex, r) -> readResource())) .build(); @@ -78,7 +78,7 @@ static void create(McpServerTransportProvider transport) { /** Stateless server (HTTP, matches JS examples). */ static void create(McpStatelessServerTransport transport) { McpServer.sync(transport) - .serverInfo("basic-server-java", "1.0.0") + .serverInfo("inlined-server-java", "1.0.0") .tools(new McpStatelessServerFeatures.SyncToolSpecification(TOOL, (ctx, r) -> getTime())) .resources(new McpStatelessServerFeatures.SyncResourceSpecification(RESOURCE, (ctx, r) -> readResource())) .build(); From c6ee748b6015c1f23e4e4d153b98134e7d73d074 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Fri, 20 Feb 2026 18:08:52 -0800 Subject: [PATCH 13/13] Use Java 21 (Temurin 17 unavailable on Windows ARM64) --- .github/workflows/ci.yml | 2 +- examples/inlined-server-java/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f37e4fc6..d1845b125 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: temurin - java-version: "17" + java-version: "21" - run: npm install diff --git a/examples/inlined-server-java/pom.xml b/examples/inlined-server-java/pom.xml index 4fde40725..994bf20c6 100644 --- a/examples/inlined-server-java/pom.xml +++ b/examples/inlined-server-java/pom.xml @@ -10,7 +10,7 @@ jar - 17 + 21 ${java.version} ${java.version} UTF-8