From 6eaec30bb3e53248cc753a26f1faa78491a8f6f6 Mon Sep 17 00:00:00 2001 From: Jordan Zimmerman Date: Tue, 12 Aug 2025 14:21:59 +0100 Subject: [PATCH] Start of some examples of using this library --- mcp-examples/mcp-stateless-server/README.md | 39 +++++++++++ mcp-examples/mcp-stateless-server/pom.xml | 48 +++++++++++++ .../stateless/server/JettyServer.java | 34 ++++++++++ .../examples/stateless/server/Main.java | 40 +++++++++++ .../stateless/server/McpServerBuilder.java | 38 +++++++++++ .../examples/stateless/server/Prompts.java | 25 +++++++ .../examples/stateless/server/Resources.java | 24 +++++++ .../examples/stateless/server/Tools.java | 67 +++++++++++++++++++ mcp-examples/pom.xml | 33 +++++++++ pom.xml | 3 +- 10 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 mcp-examples/mcp-stateless-server/README.md create mode 100644 mcp-examples/mcp-stateless-server/pom.xml create mode 100644 mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/JettyServer.java create mode 100644 mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Main.java create mode 100644 mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/McpServerBuilder.java create mode 100644 mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Prompts.java create mode 100644 mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Resources.java create mode 100644 mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Tools.java create mode 100644 mcp-examples/pom.xml diff --git a/mcp-examples/mcp-stateless-server/README.md b/mcp-examples/mcp-stateless-server/README.md new file mode 100644 index 000000000..d22497ab6 --- /dev/null +++ b/mcp-examples/mcp-stateless-server/README.md @@ -0,0 +1,39 @@ +# Example Stateless MCP HTTP Server + +This an example of sync stateless MCP HTTP server. It shows how to set up +some simple tools, prompts, and resources as well as accessing the HTTP request +from handlers. + +The actual server implementation is Jetty but any servlet container could be used. + +- See [Tools](src/main/java/io/modelcontextprotocol/examples/stateless/server/Tools.java) for the test tools. +The tool `requestHeader` shows how to access the HTTP request headers. +- See [Prompts](src/main/java/io/modelcontextprotocol/examples/stateless/server/Prompts.java) for the test prompt. +- See [Resources](src/main/java/io/modelcontextprotocol/examples/stateless/server/Resources.java) for the test resource. + +## How to run + +1. Build the project: + +```shell +./mvnw clean install +``` + +2. Run this server example: + +```shell +./mvnw -pl :mcp-stateless-server exec:java -Dexec.mainClass="io.modelcontextprotocol.examples.stateless.server.Main" +``` + +3. In a separate terminal, run the MCP inspector: + +```shell +npx @modelcontextprotocol/inspector +``` + +A browser should open with the MCP Inspector tool: +- Set the "Transport Type" to "Streamable HTTP" +- Change the URL to `http://localhost:8080/mcp` +- Click "Connect" + +Try out the tools, prompts, and resources in the MCP Inspector. diff --git a/mcp-examples/mcp-stateless-server/pom.xml b/mcp-examples/mcp-stateless-server/pom.xml new file mode 100644 index 000000000..4598531e3 --- /dev/null +++ b/mcp-examples/mcp-stateless-server/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + io.modelcontextprotocol.sdk + mcp-examples + 0.12.0-SNAPSHOT + + mcp-stateless-server + jar + Examples for the Java MCP SDK + Provides some examples for the MCP Java SDK + https://github.com/modelcontextprotocol/java-sdk + + + https://github.com/modelcontextprotocol/java-sdk + git://github.com/modelcontextprotocol/java-sdk.git + git@github.com/modelcontextprotocol/java-sdk.git + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + jakarta.servlet + jakarta.servlet-api + ${jakarta.servlet.version} + + + + org.eclipse.jetty + jetty-server + 11.0.20 + + + + org.eclipse.jetty + jetty-servlet + 11.0.20 + + + diff --git a/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/JettyServer.java b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/JettyServer.java new file mode 100644 index 000000000..feae09626 --- /dev/null +++ b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/JettyServer.java @@ -0,0 +1,34 @@ +package io.modelcontextprotocol.examples.stateless.server; + +import jakarta.servlet.http.HttpServlet; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; + +public class JettyServer implements AutoCloseable { + + private final Server server; + + public JettyServer(HttpServlet servlet, int port) { + server = new Server(port); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + + context.addServlet(new ServletHolder(servlet), "/mcp"); + + try { + server.start(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void close() throws Exception { + server.stop(); + } + +} diff --git a/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Main.java b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Main.java new file mode 100644 index 000000000..9567bbfcc --- /dev/null +++ b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Main.java @@ -0,0 +1,40 @@ +package io.modelcontextprotocol.examples.stateless.server; + +import io.modelcontextprotocol.server.McpStatelessSyncServer; +import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport; +import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; +import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities.PromptCapabilities; +import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities.ResourceCapabilities; +import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities.ToolCapabilities; + +public class Main { + + @SuppressWarnings("CallToPrintStackTrace") + public static void main(String[] args) { + HttpServletStatelessServerTransport transport = McpServerBuilder.buildTransport(); + + PromptCapabilities promptCapabilities = new PromptCapabilities(false); + ResourceCapabilities resourceCapabilities = new ResourceCapabilities(false, false); + ToolCapabilities toolCapabilities = new ToolCapabilities(false); + + McpStatelessSyncServer mcpServer = McpServerBuilder.buildServer(transport, + new ServerCapabilities(null, null, null, promptCapabilities, resourceCapabilities, toolCapabilities), + "test", "1.0", "For testing"); + + mcpServer.addTool(Tools.addTwoNumbers); + mcpServer.addTool(Tools.requestHeader); + mcpServer.addPrompt(Prompts.greetingPrompt); + mcpServer.addResource(Resources.testResources); + + try (var ignore = new JettyServer(transport, 8080)) { + Thread.currentThread().join(); + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/McpServerBuilder.java b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/McpServerBuilder.java new file mode 100644 index 000000000..4d75645d5 --- /dev/null +++ b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/McpServerBuilder.java @@ -0,0 +1,38 @@ +package io.modelcontextprotocol.examples.stateless.server; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.McpStatelessSyncServer; +import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpStatelessServerTransport; + +public interface McpServerBuilder { + + String CONTEXT_REQUEST_KEY = McpServerBuilder.class.getName() + ".request"; + + ObjectMapper MAPPER = new ObjectMapper(); + + static HttpServletStatelessServerTransport buildTransport() { + return HttpServletStatelessServerTransport.builder() + .messageEndpoint("/mcp") + .objectMapper(MAPPER) + .contextExtractor((request, transportContext) -> { + transportContext.put(CONTEXT_REQUEST_KEY, request); + return transportContext; + }) + .build(); + } + + static McpStatelessSyncServer buildServer(McpStatelessServerTransport transport, + McpSchema.ServerCapabilities serverCapabilities, String serverName, String serverVersion, + String instructions) { + return McpServer.sync(transport) + .objectMapper(MAPPER) + .capabilities(serverCapabilities) + .serverInfo(serverName, serverVersion) + .instructions(instructions) + .build(); + } + +} diff --git a/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Prompts.java b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Prompts.java new file mode 100644 index 000000000..dae88fe88 --- /dev/null +++ b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Prompts.java @@ -0,0 +1,25 @@ +package io.modelcontextprotocol.examples.stateless.server; + +import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncPromptSpecification; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.GetPromptResult; +import io.modelcontextprotocol.spec.McpSchema.Prompt; +import io.modelcontextprotocol.spec.McpSchema.PromptMessage; +import io.modelcontextprotocol.spec.McpSchema.TextContent; + +import java.util.List; + +import static io.modelcontextprotocol.spec.McpSchema.Role.USER; + +public interface Prompts { + + SyncPromptSpecification greetingPrompt = new SyncPromptSpecification( + new Prompt("greeting", "Greeting Prompt", + List.of(new McpSchema.PromptArgument("name", "Name of the person to greet", true))), + (transportContext, getPromptRequest) -> { + String name = String.valueOf(getPromptRequest.arguments().get("name")); + return new GetPromptResult("greeting", + List.of(new PromptMessage(USER, new TextContent("Hello " + name + "!")))); + }); + +} diff --git a/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Resources.java b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Resources.java new file mode 100644 index 000000000..40be09cbe --- /dev/null +++ b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Resources.java @@ -0,0 +1,24 @@ +package io.modelcontextprotocol.examples.stateless.server; + +import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncResourceSpecification; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; +import io.modelcontextprotocol.spec.McpSchema.Resource; + +import java.util.List; + +public interface Resources { + + SyncResourceSpecification testResources = new SyncResourceSpecification( + Resource.builder() + .name("test") + .uri("https://modelcontextprotocol.io") + .description("A test resource") + .mimeType("text/plain") + .size(10L) + .build(), + (transportContext, readResourceRequest) -> new ReadResourceResult( + List.of(new McpSchema.TextResourceContents("https://modelcontextprotocol.io", "text/plain", + "0123456789")))); + +} diff --git a/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Tools.java b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Tools.java new file mode 100644 index 000000000..df8bc5525 --- /dev/null +++ b/mcp-examples/mcp-stateless-server/src/main/java/io/modelcontextprotocol/examples/stateless/server/Tools.java @@ -0,0 +1,67 @@ +package io.modelcontextprotocol.examples.stateless.server; + +import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncToolSpecification; +import io.modelcontextprotocol.spec.McpError; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.CallToolResult; +import io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse.JSONRPCError; +import jakarta.servlet.http.HttpServletRequest; + +import static io.modelcontextprotocol.examples.stateless.server.McpServerBuilder.CONTEXT_REQUEST_KEY; +import static io.modelcontextprotocol.spec.McpSchema.ErrorCodes.INVALID_REQUEST; + +public interface Tools { + + String addTwoNumbersSchemaJson = """ + { + "type": "object", + "properties": { + "a": { + "type": "integer" + }, + "b": { + "type": "integer" + } + }, + "required": ["a", "b"] + } + """; + + String requestHeaderSchemaJson = """ + { + "type": "object", + "properties": { + "headerName": { + "type": "string" + } + }, + "required": ["headerName"] + } + """; + + SyncToolSpecification addTwoNumbers = SyncToolSpecification.builder() + .tool(McpSchema.Tool.builder().name("add").inputSchema(addTwoNumbersSchemaJson).build()) + .callHandler((transportContext, callToolRequest) -> { + int a = Integer.parseInt(String.valueOf(callToolRequest.arguments().get("a"))); + int b = Integer.parseInt(String.valueOf(callToolRequest.arguments().get("b"))); + + return new CallToolResult(String.valueOf(a + b), null); + }) + .build(); + + SyncToolSpecification requestHeader = SyncToolSpecification.builder() + .tool(McpSchema.Tool.builder().name("requestHeader").inputSchema(requestHeaderSchemaJson).build()) + .callHandler((transportContext, callToolRequest) -> { + HttpServletRequest request = (HttpServletRequest) transportContext.get(CONTEXT_REQUEST_KEY); + String headerName = String.valueOf(callToolRequest.arguments().get("headerName")); + + String value = request.getHeader(headerName); + if (value == null) { + throw new McpError(new JSONRPCError(INVALID_REQUEST, "Header '" + headerName + "' not found", null)); + } + + return new CallToolResult(value, null); + }) + .build(); + +} diff --git a/mcp-examples/pom.xml b/mcp-examples/pom.xml new file mode 100644 index 000000000..2cd67551a --- /dev/null +++ b/mcp-examples/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + io.modelcontextprotocol.sdk + mcp-parent + 0.12.0-SNAPSHOT + + mcp-examples + pom + Examples for the Java MCP SDK + Provides some examples for the MCP Java SDK + https://github.com/modelcontextprotocol/java-sdk + + mcp-stateless-server + + + + https://github.com/modelcontextprotocol/java-sdk + git://github.com/modelcontextprotocol/java-sdk.git + git@github.com/modelcontextprotocol/java-sdk.git + + + + + io.modelcontextprotocol.sdk + mcp + 0.12.0-SNAPSHOT + + + diff --git a/pom.xml b/pom.xml index c0b1f7a44..28df75d8e 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,8 @@ mcp-spring/mcp-spring-webflux mcp-spring/mcp-spring-webmvc mcp-test - + mcp-examples +