Getting Started
This guide will help you build your first MCP server in 5 minutes.
Use this SDK when you want an annotation-driven MCP server in plain Java without a Spring runtime. If you are already building a Spring Boot application, Spring AI MCP is usually the better default; this guide focuses on the lightweight Java path for CLI tools, embedded servers, local automation, and small service processes.
Requirements
Section titled “Requirements”- Java 17 or later (required by official MCP Java SDK)
Installation
Section titled “Installation”Maven Dependency
Section titled “Maven Dependency”<dependency> <groupId>io.github.thought2code</groupId> <artifactId>mcp-annotated-java-sdk</artifactId> <version>0.21.0</version></dependency>Gradle Dependency
Section titled “Gradle Dependency”implementation 'io.github.thought2code:mcp-annotated-java-sdk:0.21.0'5-Minutes Tutorial
Section titled “5-Minutes Tutorial”Step 1: Create Configuration File
Section titled “Step 1: Create Configuration File”Create mcp-server.yml in your src/main/resources:
enabled: truemode: STDIOname: my-first-mcp-serverversion: 1.0.0type: SYNCinstructions: You are a helpful AI assistantrequest-timeout: 20000capabilities: resource: true subscribe-resource: true prompt: true tool: true completion: truechange-notification: resource: true prompt: true tool: trueStep 2: Create MCP Server Main Class
Section titled “Step 2: Create MCP Server Main Class”@McpServerApplicationpublic class MyFirstMcpServer { public static void main(String[] args) { McpApplication.run(MyFirstMcpServer.class, args); }}Step 3: Define MCP Resources (Optional)
Section titled “Step 3: Define MCP Resources (Optional)”public class MyResources { @McpResource(uri = "system://info", description = "System information") public Map<String, String> getSystemInfo() { Map<String, String> info = new HashMap<>(); info.put("os", System.getProperty("os.name")); info.put("java", System.getProperty("java.version")); info.put("cores", String.valueOf(Runtime.getRuntime().availableProcessors())); return info; }}Step 4: Define MCP Tools
Section titled “Step 4: Define MCP Tools”public class MyTools { @McpTool(description = "Calculate the sum of two numbers") public int add( @McpToolParam(name = "a", description = "First number") int a, @McpToolParam(name = "b", description = "Second number") int b ) { return a + b; }}Step 5: Define MCP Prompts (Optional)
Section titled “Step 5: Define MCP Prompts (Optional)”public class MyPrompts { @McpPrompt(description = "Generate code for a given task") public String generateCode( @McpPromptParam(name = "language", description = "Programming language") String language, @McpPromptParam(name = "task", description = "Task description") String task ) { return String.format("Write %s code to: %s", language, task); }}Step 6: Run the Server
Section titled “Step 6: Run the Server”# Compile your project./mvnw clean packageRun MyFirstMcpServer from your IDE, or use java -cp ... with your compiled classes and dependencies on the classpath. Use an executable JAR setup in your own project if you need java -jar with a single file.
To load a non-default configuration file name, use McpApplication.run(MyFirstMcpServer.class, args, "custom-mcp-server.yml").
Packaging an Executable Fat JAR
Section titled “Packaging an Executable Fat JAR”For deployment, package your application as an executable fat JAR so the MCP Java SDK, this SDK, and all transitive dependencies are available at runtime. Keep mcp-server.yml under src/main/resources so it is included on the runtime classpath.
With Maven Shade, configure the JAR manifest main class:
<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.MyFirstMcpServer</mainClass> </transformer> </transformers> </configuration> </execution> </executions></plugin>Then run:
java -jar target/your-app.jarIf you use Gradle Shadow, configure the main class in the manifest:
tasks.shadowJar { manifest { attributes 'Main-Class': 'com.example.MyFirstMcpServer' }}Server Modes
Section titled “Server Modes”This SDK supports two MCP server modes. The mode field is required in mcp-server.yml; there is no implicit default when loading from YAML (defaults only apply when building a configuration programmatically via ServerConfiguration.builder()).
1. STDIO Mode
Section titled “1. STDIO Mode”Based on standard input/output communication, suitable for CLI tools and local development.
mode: STDIO2. STREAMABLE Mode
Section titled “2. STREAMABLE Mode”HTTP streaming for web applications, recommended for production.
mode: STREAMABLEstreamable: mcp-endpoint: /mcp/message disallow-delete: false keep-alive-interval: 20000 port: 8080STREAMABLE is the supported HTTP transport.
Configuration Properties
Section titled “Configuration Properties”The core fields and applicable nested settings below are required when loading configuration from YAML; omitting a required field causes the server to fail startup with Missing config key '...'. Conditional fields are required only when their corresponding feature or transport is enabled. The “Builder default” column lists the value applied only when constructing a configuration programmatically via ServerConfiguration.builder().
| Property | Description | Builder default |
|---|---|---|
enabled |
Enable/disable MCP server | true |
mode |
Server mode: STDIO, STREAMABLE |
STREAMABLE |
name |
Server name | mcp-server |
version |
Server version | 1.0.0 |
type |
Server type: SYNC, ASYNC |
SYNC |
instructions |
Instructions for the LLM client | (empty string) |
request-timeout |
Request timeout in milliseconds | 20000 |
capabilities.resource |
Enable resource support | true |
capabilities.subscribe-resource |
Enable resource subscription | true |
capabilities.prompt |
Enable prompt support | true |
capabilities.tool |
Enable tool support | true |
capabilities.completion |
Enable completion support | true |
change-notification.resource |
Notify clients on resource change | true |
change-notification.prompt |
Notify clients on prompt change | true |
change-notification.tool |
Notify clients on tool change | true |
streamable.mcp-endpoint |
Streamable HTTP MCP path | /mcp/message |
streamable.disallow-delete |
Reject HTTP DELETE on session | false |
streamable.keep-alive-interval |
Keep-alive interval (ms) | 20000 |
streamable.port |
HTTP port for STREAMABLE mode | 8080 |
Conditional requirements:
capabilities.subscribe-resourceis required only whencapabilities.resourceistrue.streamable.*fields are required only whenmodeisSTREAMABLE. WhenmodeisSTDIO, thestreamablesection is ignored and may be omitted.
Runtime model and stability
Section titled “Runtime model and stability”SYNC vs ASYNC (type)
Section titled “SYNC vs ASYNC (type)”The type property selects the MCP Java SDK server API (SYNC or ASYNC). It does not make your annotated methods reactive.
- SYNC — methods run on the request thread.
- ASYNC — the SDK exposes async handlers that wrap your method in
Mono.fromCallable(...). Your code is still blocking Java; you do not returnMonofrom@McpTool/@McpPrompt/@McpResourcemethods.
Use SYNC by default. Choose ASYNC only when your deployment requires the async MCP server API. Long work still blocks a Reactor worker thread under ASYNC.
Component instances and concurrency
Section titled “Component instances and concurrency”The SDK creates one instance per component class (via a public no-arg constructor) and reuses it for all requests. Concurrent MCP calls share that object. Keep components stateless or thread-safe; avoid unsynchronized per-request instance fields.
Profile-based Configuration
Section titled “Profile-based Configuration”Set profile in the base file to load mcp-server-{profile}.yml from the classpath. Profile values are merged into the base configuration with Jackson deep merge; nested objects such as capabilities and streamable are merged field-by-field. The profile name always comes from the base file. After merge, transport settings that do not match the resolved mode are cleared (for example, streamable is removed when mode is STDIO).
You can use profiles for different environments:
# mcp-server.yml (base configuration)enabled: truemode: STREAMABLEname: my-mcp-serverversion: 1.0.0profile: dev# mcp-server-dev.yml (profile-specific configuration)streamable: port: 8080Project Structure
Section titled “Project Structure”The typical project structure is as follows:
your-mcp-project/├── pom.xml├── src/│ ├── main/│ │ ├── java/│ │ │ └── com/│ │ │ └── example/│ │ │ ├── MyMcpServer.java # Main entry point│ │ │ ├── components/│ │ │ │ ├── MyResources.java # MCP Resources│ │ │ │ ├── MyTools.java # MCP Tools│ │ │ │ └── MyPrompts.java # MCP Prompts│ │ │ └── service/│ │ │ └── BusinessLogic.java # Business logic│ │ └── resources/│ │ └── mcp-server.yml # MCP configuration│ └── test/│ └── java/│ └── com/│ └── example/│ └── McpServerTest.java # Unit tests└── target/ └── *.jar # Build output (name depends on your project)Next Steps
Section titled “Next Steps”- Want to learn more about MCP components? Check Core Components