Java MCP Server Generator
GitHubOfficialFirst committed by stbenjamDistributed by GitHub
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured approach to building Model Context Protocol (MCP) servers in Java or Spring Boot applications. It guides users through creating a project layout, implementing tools, writing unit tests, and configuring client connections. The skill supports both Maven and Gradle build systems and utilizes the official MCP Java SDK 2.x to enable AI agents to interact with Java code, APIs, or databases.
What it does
- Generates Maven or Gradle project structures for MCP servers.
- Provides templates for defining MCP tools with JSON Schema input validation.
- Includes boilerplate for unit testing server functionality using the MCP client.
- Configures stdio transport for local communication.
- Offers guidance for implementing streamable HTTP transports for remote servers.
- Provides configuration snippets for connecting servers to clients like VS Code or Claude Desktop.
When to use it
- When you need to expose Java-based tools or data to AI agents.
- When building a new MCP server using the official Java SDK 2.x.
- When you require a standardized project setup for an MCP server in a Java or Spring Boot environment.
- When you need to test MCP server tool execution locally.
Advantages
- Uses the official MCP Java SDK for compatibility with the latest protocol specifications.
- Supports both Maven and Gradle build automation.
- Includes built-in testing patterns to verify tool behavior.
- Provides clear configuration paths for common AI client environments.
Usage examples
- “Create a new Java MCP server project with Maven that includes a tool for calculating square roots.”
- The agent provides a pom.xml file and a Java class template with a square root tool implementation and corresponding unit tests.
- “How do I configure my new Java MCP server to work with Claude Desktop?”
- The agent provides the necessary JSON configuration block for your claude_desktop_config.json file, pointing to your compiled JAR.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/github/java-mcp-server-generator/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip java-mcp-server-generator.zip -d .agents/skills
Documentation by stbenjam
Java MCP Server Generator
Create a Model Context Protocol (MCP) server in Java with the official MCP Java SDK 2.x. SDK 2.0.x supports the MCP specification revision 2025-11-25 and the earlier revisions. SDK 3.x will add the revision 2026-07-28.
Workflow
Copy this checklist and track your progress:
[ ] Step 1: Create the project.
[ ] Step 2: Write the server.
[ ] Step 3: Write the tests.
[ ] Step 4: Build until the tests pass.
[ ] Step 5: Configure the client.
Step 1: Create the project
Use Maven by default. If the project uses Gradle or the user asks for Gradle, read references/gradle.md. Use it for Step 1, Step 4, and Step 5.
Make this layout:
project-name/
├── pom.xml
└── src/
├── main/java/com/example/McpServerApp.java
└── test/java/com/example/McpServerAppTest.javaWrite pom.xml:
<?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>
<groupId>com.example</groupId>
<artifactId>mcp-server-demo</artifactId>
<version>0.1.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-bom</artifactId>
<version>2.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>6.1.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.17</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.McpServerApp</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>The
mcpartifact containsmcp-coreand the Jackson 3 JSON mappermcp-json-jackson3.slf4j-simplewrites the SDK logs to stderr.The shade plugin makes one executable JAR with all dependencies.
Step 2: Write the server
Start from this template:
package com.example;
import java.util.List;
import java.util.Map;
import io.modelcontextprotocol.json.McpJsonDefaults;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
import io.modelcontextprotocol.spec.McpSchema.Tool;
public final class McpServerApp {
static SyncToolSpecification divideTool() {
Tool tool = Tool.builder("divide", Map.of(
"type", "object",
"properties", Map.of(
"a", Map.of("type", "number", "description", "Dividend"),
"b", Map.of("type", "number", "description", "Divisor")),
"required", List.of("a", "b")))
.description("Divide a by b.")
.build();
return SyncToolSpecification.builder()
.tool(tool)
.callHandler((exchange, request) -> {
double a = ((Number) request.arguments().get("a")).doubleValue();
double b = ((Number) request.arguments().get("b")).doubleValue();
if (b == 0) {
return CallToolResult.builder().addTextContent("b must not be zero.").isError(true).build();
}
return CallToolResult.builder().addTextContent(String.valueOf(a / b)).build();
})
.build();
}
public static void main(String[] args) {
McpServer.sync(new StdioServerTransportProvider(McpJsonDefaults.getMapper()))
.serverInfo("demo", "0.1.0")
.capabilities(ServerCapabilities.builder().tools(false).build())
.tools(divideTool())
.build();
}
}Then change the template to match the request of the user:
Replace
divideTool()with the tools that the user asks for. Make one method for each tool.Give each tool a JSON Schema
inputSchemaand adescription. The description tells the model when to use the tool.When the client must get machine-readable data, add
.outputSchema(Map)to the tool. Return the data withCallToolResult.builder().structuredContent(map). Also add the same data as text content for older clients.Mark a read-only tool with
.annotations(ToolAnnotations.builder().readOnlyHint(true).build()). UsedestructiveHint(true)for a destructive tool.Add resources, resource templates, and prompts only when the user asks for them. Also enable them in
ServerCapabilities.
Use stdio by default. Read references/streamable-http.md only when the user asks for a remote server, an HTTP server, or Spring Boot.
Step 3: Write the tests
The test starts the server as a subprocess and connects to it with the SDK client. Write a test for each tool and for each error result:
package com.example;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.nio.file.Path;
import java.util.Map;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.json.McpJsonDefaults;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.TextContent;
class McpServerAppTest {
private static McpSyncClient client;
@BeforeAll
static void startServer() {
String java = Path.of(System.getProperty("java.home"), "bin", "java").toString();
ServerParameters server = ServerParameters.builder(java)
.args("-cp", System.getProperty("java.class.path"), McpServerApp.class.getName())
.build();
client = McpClient.sync(new StdioClientTransport(server, McpJsonDefaults.getMapper())).build();
client.initialize();
}
@AfterAll
static void stopServer() {
client.close();
}
@Test
void divide() {
CallToolResult result = client.callTool(
CallToolRequest.builder("divide").arguments(Map.of("a", 6, "b", 3)).build());
assertEquals("2.0", ((TextContent) result.content().get(0)).text());
}
@Test
void divideByZero() {
CallToolResult result = client.callTool(
CallToolRequest.builder("divide").arguments(Map.of("a", 1, "b", 0)).build());
assertTrue(result.isError());
assertTrue(((TextContent) result.content().get(0)).text().contains("must not be zero"));
}
}The test uses the
javacommand of the JDK that runs Maven. Do not use thejavacommand fromPATH.The test uses the real stdio transport and the real JSON-RPC messages.
Step 4: Build until the tests pass
Run
mvn verify. This command compiles the code, runs the tests, and makestarget/mcp-server-demo-0.1.0.jar.If a test fails, read the error. Fix the server or the test.
Run
mvn verifyagain.Continue only when the build is successful.
Step 5: Configure the client
For VS Code, write .vscode/mcp.json:
{
"servers": {
"demo": {
"type": "stdio",
"command": "java",
"args": ["-jar", "/absolute/path/to/project/target/mcp-server-demo-0.1.0.jar"]
}
}
}For Claude Desktop, put the same command and args in claude_desktop_config.json, below the mcpServers key.
For an HTTP server, use the client configuration in references/streamable-http.md.
Tell the user these facts:
java -jar target/mcp-server-demo-0.1.0.jarstarts the stdio server. The server waits for a host on stdin. The SDK logs go to stderr.To test the server manually, start the MCP Inspector with the same command. The Inspector uses Node.js.
Gotchas
SDK 2.x changed many patterns of SDK 0.x and 1.x. Do not copy examples from earlier versions. Read the migration guide for the full list.
Use the builders that take the required fields first:
Tool.builder(name, inputSchema),Resource.builder(uri, name),Prompt.builder(name),TextContent.builder(text), andReadResourceResult.builder(contents). The builders without arguments are deprecated.A required field must not be
null. Anullvalue causes anIllegalArgumentException.Tool names must have 1 to 128 characters: letters, digits,
_,-, and.. The server builder rejects an incorrect name with anIllegalArgumentException.addTool()at runtime does not check the name.inputSchemaandoutputSchemaareMap<String, Object>values. TheJsonSchemarecord is deprecated.The server checks each schema against JSON Schema 2020-12 when you call
build()oraddTool(). An incorrect schema causes anIllegalArgumentException.The SDK validates the tool arguments against
inputSchema. It also validatesstructuredContentagainstoutputSchema. If the validation fails, the client gets a tool result withisErrorset totrue. Thus, the handler gets only valid arguments.If a tool has an
outputSchema, returnstructuredContentin each successful result. Without it, the SDK returns a tool result withisErrorset totrue.Return
CallToolResultwithisError(true)for an error that the model must read.Do not throw an exception or an
McpErrorfor a tool failure. The client gets a JSON-RPC error, not a tool result. Many hosts do not show this error to the model.In a stdio server, stdout is the protocol channel. Do not call
System.out.println().Log with SLF4J to stderr. The default console appender of Logback writes to stdout. If you use Logback, set its target to
System.err.The Spring transports moved to Spring AI 2.0 and later (group
org.springframework.ai). Do not use theio.modelcontextprotocol.sdk:mcp-spring-*artifacts.Do not use the deprecated SSE transports. Use Streamable HTTP for a remote server.
The revision 2026-07-28 deprecates these features (SEP-2577). Do not add them to a new server:
Sampling (
exchange.createMessage()): Call the LLM provider API directly.Protocol logging (
exchange.loggingNotification()): Use SLF4J.Roots (
exchange.listRoots()): Get paths from tool arguments, resource URIs, or the server configuration.
Files
- ATTRIBUTION.mdAdded by AutoGineer883 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md13.2 KiB
- references/gradle.md2.3 KiB
- references/streamable-http.md5.1 KiB
references/streamable-http.md
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=STREAMABLEThe starter serves the endpoint at /mcp.
Gotchas
The HTTP transports do not check the
HostandOriginheaders 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 setsecurityValidator(DefaultServerTransportSecurityValidator.builder()...build()). The validator rejects an unknownOriginwith HTTP 403 and an unknownHostwith HTTP 421.In Spring AI, declare your own
WebMvcStreamableServerTransportProviderbean 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
WebFluxStreamableServerTransportProviderbean in the same way. Its builder usesmessageEndpoint("/mcp"), notmcpEndpoint.Embedded Tomcat listens on all network interfaces. For a public host, add its name to
allowedHost. Put authentication in front of the server.The SDK has no authorization. For a remote server, add authorization in a servlet filter or with Spring Security.
HttpServletStatelessServerTransportmakes a server without sessions. Use it withMcpServer.sync(transport)when more than one replica must answer the requests. A stateless server cannot send requests to the client. Also setsecurityValidatoron its builder.For browser clients, add CORS to the servlet container. Allow the
Mcp-*request headers. Expose theMcp-Session-Idresponse header.
Requirements and compatibility
Written for: GitHub Copilot
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server
The author says: JDK 17 or later, Maven 3.9 or later or Gradle 9 or later, and network access to download dependencies. The MCP Inspector also uses Node.js.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- First committed by
stbenjam
- Publisher
- GitHub (Official)
- Distributed by
- GitHub
- Source
- github/awesome-copilot · skills/java-mcp-server-generator · commit 797337f
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Contributors
- burakkaygusuz, jdubois, stbenjam
AutoGineer is not affiliated with or endorsed by stbenjam or GitHub.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download java-mcp-server-generator.zip