Skip to content

Commit 6eaec30

Browse files
committed
Start of some examples of using this library
1 parent 713ee1a commit 6eaec30

File tree

10 files changed

+350
-1
lines changed

10 files changed

+350
-1
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Example Stateless MCP HTTP Server
2+
3+
This an example of sync stateless MCP HTTP server. It shows how to set up
4+
some simple tools, prompts, and resources as well as accessing the HTTP request
5+
from handlers.
6+
7+
The actual server implementation is Jetty but any servlet container could be used.
8+
9+
- See [Tools](src/main/java/io/modelcontextprotocol/examples/stateless/server/Tools.java) for the test tools.
10+
The tool `requestHeader` shows how to access the HTTP request headers.
11+
- See [Prompts](src/main/java/io/modelcontextprotocol/examples/stateless/server/Prompts.java) for the test prompt.
12+
- See [Resources](src/main/java/io/modelcontextprotocol/examples/stateless/server/Resources.java) for the test resource.
13+
14+
## How to run
15+
16+
1. Build the project:
17+
18+
```shell
19+
./mvnw clean install
20+
```
21+
22+
2. Run this server example:
23+
24+
```shell
25+
./mvnw -pl :mcp-stateless-server exec:java -Dexec.mainClass="io.modelcontextprotocol.examples.stateless.server.Main"
26+
```
27+
28+
3. In a separate terminal, run the MCP inspector:
29+
30+
```shell
31+
npx @modelcontextprotocol/inspector
32+
```
33+
34+
A browser should open with the MCP Inspector tool:
35+
- Set the "Transport Type" to "Streamable HTTP"
36+
- Change the URL to `http://localhost:8080/mcp`
37+
- Click "Connect"
38+
39+
Try out the tools, prompts, and resources in the MCP Inspector.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.modelcontextprotocol.sdk</groupId>
8+
<artifactId>mcp-examples</artifactId>
9+
<version>0.12.0-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>mcp-stateless-server</artifactId>
12+
<packaging>jar</packaging>
13+
<name>Examples for the Java MCP SDK</name>
14+
<description>Provides some examples for the MCP Java SDK</description>
15+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
16+
17+
<scm>
18+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
19+
<connection>git://github.com/modelcontextprotocol/java-sdk.git</connection>
20+
<developerConnection>git@github.com/modelcontextprotocol/java-sdk.git</developerConnection>
21+
</scm>
22+
23+
<dependencies>
24+
<dependency>
25+
<groupId>com.fasterxml.jackson.core</groupId>
26+
<artifactId>jackson-databind</artifactId>
27+
<version>${jackson.version}</version>
28+
</dependency>
29+
30+
<dependency>
31+
<groupId>jakarta.servlet</groupId>
32+
<artifactId>jakarta.servlet-api</artifactId>
33+
<version>${jakarta.servlet.version}</version>
34+
</dependency>
35+
36+
<dependency>
37+
<groupId>org.eclipse.jetty</groupId>
38+
<artifactId>jetty-server</artifactId>
39+
<version>11.0.20</version>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>org.eclipse.jetty</groupId>
44+
<artifactId>jetty-servlet</artifactId>
45+
<version>11.0.20</version>
46+
</dependency>
47+
</dependencies>
48+
</project>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.modelcontextprotocol.examples.stateless.server;
2+
3+
import jakarta.servlet.http.HttpServlet;
4+
import org.eclipse.jetty.server.Server;
5+
import org.eclipse.jetty.servlet.ServletContextHandler;
6+
import org.eclipse.jetty.servlet.ServletHolder;
7+
8+
public class JettyServer implements AutoCloseable {
9+
10+
private final Server server;
11+
12+
public JettyServer(HttpServlet servlet, int port) {
13+
server = new Server(port);
14+
15+
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
16+
context.setContextPath("/");
17+
server.setHandler(context);
18+
19+
context.addServlet(new ServletHolder(servlet), "/mcp");
20+
21+
try {
22+
server.start();
23+
}
24+
catch (Exception e) {
25+
throw new RuntimeException(e);
26+
}
27+
}
28+
29+
@Override
30+
public void close() throws Exception {
31+
server.stop();
32+
}
33+
34+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.modelcontextprotocol.examples.stateless.server;
2+
3+
import io.modelcontextprotocol.server.McpStatelessSyncServer;
4+
import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport;
5+
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
6+
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities.PromptCapabilities;
7+
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities.ResourceCapabilities;
8+
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities.ToolCapabilities;
9+
10+
public class Main {
11+
12+
@SuppressWarnings("CallToPrintStackTrace")
13+
public static void main(String[] args) {
14+
HttpServletStatelessServerTransport transport = McpServerBuilder.buildTransport();
15+
16+
PromptCapabilities promptCapabilities = new PromptCapabilities(false);
17+
ResourceCapabilities resourceCapabilities = new ResourceCapabilities(false, false);
18+
ToolCapabilities toolCapabilities = new ToolCapabilities(false);
19+
20+
McpStatelessSyncServer mcpServer = McpServerBuilder.buildServer(transport,
21+
new ServerCapabilities(null, null, null, promptCapabilities, resourceCapabilities, toolCapabilities),
22+
"test", "1.0", "For testing");
23+
24+
mcpServer.addTool(Tools.addTwoNumbers);
25+
mcpServer.addTool(Tools.requestHeader);
26+
mcpServer.addPrompt(Prompts.greetingPrompt);
27+
mcpServer.addResource(Resources.testResources);
28+
29+
try (var ignore = new JettyServer(transport, 8080)) {
30+
Thread.currentThread().join();
31+
}
32+
catch (InterruptedException e) {
33+
throw new RuntimeException(e);
34+
}
35+
catch (Exception e) {
36+
e.printStackTrace();
37+
}
38+
}
39+
40+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.modelcontextprotocol.examples.stateless.server;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import io.modelcontextprotocol.server.McpServer;
5+
import io.modelcontextprotocol.server.McpStatelessSyncServer;
6+
import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport;
7+
import io.modelcontextprotocol.spec.McpSchema;
8+
import io.modelcontextprotocol.spec.McpStatelessServerTransport;
9+
10+
public interface McpServerBuilder {
11+
12+
String CONTEXT_REQUEST_KEY = McpServerBuilder.class.getName() + ".request";
13+
14+
ObjectMapper MAPPER = new ObjectMapper();
15+
16+
static HttpServletStatelessServerTransport buildTransport() {
17+
return HttpServletStatelessServerTransport.builder()
18+
.messageEndpoint("/mcp")
19+
.objectMapper(MAPPER)
20+
.contextExtractor((request, transportContext) -> {
21+
transportContext.put(CONTEXT_REQUEST_KEY, request);
22+
return transportContext;
23+
})
24+
.build();
25+
}
26+
27+
static McpStatelessSyncServer buildServer(McpStatelessServerTransport transport,
28+
McpSchema.ServerCapabilities serverCapabilities, String serverName, String serverVersion,
29+
String instructions) {
30+
return McpServer.sync(transport)
31+
.objectMapper(MAPPER)
32+
.capabilities(serverCapabilities)
33+
.serverInfo(serverName, serverVersion)
34+
.instructions(instructions)
35+
.build();
36+
}
37+
38+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.modelcontextprotocol.examples.stateless.server;
2+
3+
import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncPromptSpecification;
4+
import io.modelcontextprotocol.spec.McpSchema;
5+
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
6+
import io.modelcontextprotocol.spec.McpSchema.Prompt;
7+
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
8+
import io.modelcontextprotocol.spec.McpSchema.TextContent;
9+
10+
import java.util.List;
11+
12+
import static io.modelcontextprotocol.spec.McpSchema.Role.USER;
13+
14+
public interface Prompts {
15+
16+
SyncPromptSpecification greetingPrompt = new SyncPromptSpecification(
17+
new Prompt("greeting", "Greeting Prompt",
18+
List.of(new McpSchema.PromptArgument("name", "Name of the person to greet", true))),
19+
(transportContext, getPromptRequest) -> {
20+
String name = String.valueOf(getPromptRequest.arguments().get("name"));
21+
return new GetPromptResult("greeting",
22+
List.of(new PromptMessage(USER, new TextContent("Hello " + name + "!"))));
23+
});
24+
25+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.modelcontextprotocol.examples.stateless.server;
2+
3+
import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncResourceSpecification;
4+
import io.modelcontextprotocol.spec.McpSchema;
5+
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
6+
import io.modelcontextprotocol.spec.McpSchema.Resource;
7+
8+
import java.util.List;
9+
10+
public interface Resources {
11+
12+
SyncResourceSpecification testResources = new SyncResourceSpecification(
13+
Resource.builder()
14+
.name("test")
15+
.uri("https://modelcontextprotocol.io")
16+
.description("A test resource")
17+
.mimeType("text/plain")
18+
.size(10L)
19+
.build(),
20+
(transportContext, readResourceRequest) -> new ReadResourceResult(
21+
List.of(new McpSchema.TextResourceContents("https://modelcontextprotocol.io", "text/plain",
22+
"0123456789"))));
23+
24+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package io.modelcontextprotocol.examples.stateless.server;
2+
3+
import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncToolSpecification;
4+
import io.modelcontextprotocol.spec.McpError;
5+
import io.modelcontextprotocol.spec.McpSchema;
6+
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
7+
import io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse.JSONRPCError;
8+
import jakarta.servlet.http.HttpServletRequest;
9+
10+
import static io.modelcontextprotocol.examples.stateless.server.McpServerBuilder.CONTEXT_REQUEST_KEY;
11+
import static io.modelcontextprotocol.spec.McpSchema.ErrorCodes.INVALID_REQUEST;
12+
13+
public interface Tools {
14+
15+
String addTwoNumbersSchemaJson = """
16+
{
17+
"type": "object",
18+
"properties": {
19+
"a": {
20+
"type": "integer"
21+
},
22+
"b": {
23+
"type": "integer"
24+
}
25+
},
26+
"required": ["a", "b"]
27+
}
28+
""";
29+
30+
String requestHeaderSchemaJson = """
31+
{
32+
"type": "object",
33+
"properties": {
34+
"headerName": {
35+
"type": "string"
36+
}
37+
},
38+
"required": ["headerName"]
39+
}
40+
""";
41+
42+
SyncToolSpecification addTwoNumbers = SyncToolSpecification.builder()
43+
.tool(McpSchema.Tool.builder().name("add").inputSchema(addTwoNumbersSchemaJson).build())
44+
.callHandler((transportContext, callToolRequest) -> {
45+
int a = Integer.parseInt(String.valueOf(callToolRequest.arguments().get("a")));
46+
int b = Integer.parseInt(String.valueOf(callToolRequest.arguments().get("b")));
47+
48+
return new CallToolResult(String.valueOf(a + b), null);
49+
})
50+
.build();
51+
52+
SyncToolSpecification requestHeader = SyncToolSpecification.builder()
53+
.tool(McpSchema.Tool.builder().name("requestHeader").inputSchema(requestHeaderSchemaJson).build())
54+
.callHandler((transportContext, callToolRequest) -> {
55+
HttpServletRequest request = (HttpServletRequest) transportContext.get(CONTEXT_REQUEST_KEY);
56+
String headerName = String.valueOf(callToolRequest.arguments().get("headerName"));
57+
58+
String value = request.getHeader(headerName);
59+
if (value == null) {
60+
throw new McpError(new JSONRPCError(INVALID_REQUEST, "Header '" + headerName + "' not found", null));
61+
}
62+
63+
return new CallToolResult(value, null);
64+
})
65+
.build();
66+
67+
}

mcp-examples/pom.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.modelcontextprotocol.sdk</groupId>
8+
<artifactId>mcp-parent</artifactId>
9+
<version>0.12.0-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>mcp-examples</artifactId>
12+
<packaging>pom</packaging>
13+
<name>Examples for the Java MCP SDK</name>
14+
<description>Provides some examples for the MCP Java SDK</description>
15+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
16+
<modules>
17+
<module>mcp-stateless-server</module>
18+
</modules>
19+
20+
<scm>
21+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
22+
<connection>git://github.com/modelcontextprotocol/java-sdk.git</connection>
23+
<developerConnection>git@github.com/modelcontextprotocol/java-sdk.git</developerConnection>
24+
</scm>
25+
26+
<dependencies>
27+
<dependency>
28+
<groupId>io.modelcontextprotocol.sdk</groupId>
29+
<artifactId>mcp</artifactId>
30+
<version>0.12.0-SNAPSHOT</version>
31+
</dependency>
32+
</dependencies>
33+
</project>

pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@
106106
<module>mcp-spring/mcp-spring-webflux</module>
107107
<module>mcp-spring/mcp-spring-webmvc</module>
108108
<module>mcp-test</module>
109-
</modules>
109+
<module>mcp-examples</module>
110+
</modules>
110111

111112
<build>
112113
<plugins>

0 commit comments

Comments
 (0)