Spring AI MCP Server — Build a Model Context Protocol Server with Spring Boot
The Model Context Protocol (MCP) is an open standard by Anthropic that defines how AI models communicate with external tools, data sources, and services. Unlike direct function calling (which is tightly coupled to a specific AI application), MCP turns your Spring Boot app into a standalone tool server that any MCP-compatible AI client (Claude Desktop, VS Code Copilot, Spring AI) can connect to.
MCP vs Direct Function Calling
Direct Function Calling:
AI App → calls your Java @Tool method directly (in-process)
Tight coupling, can't be reused by other AI clients
MCP Server:
AI Client (Claude Desktop) ──── MCP Protocol ────→ Your Spring Boot Server
AI Client (VS Code Copilot) ─── MCP Protocol ────→ (exposes tools/resources/prompts)
AI Client (Custom App) ──────── MCP Protocol ────→
Decoupled, reusable across any MCP-compatible client
Maven Dependencies
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
application.properties
spring.ai.mcp.server.name=java9r-tools
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=SYNC
# SSE transport for HTTP clients (stdio for CLI tools like Claude Desktop)
# spring.ai.mcp.server.stdio=true ← enable for Claude Desktop
Main Application
@SpringBootApplication
public class McpServerApp {
public static void main(String[] args) {
SpringApplication.run(McpServerApp.class, args);
}
}
MCP Tools — Expose Java Methods to AI Clients
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.mcp.server.annotation.McpTool;
@Component
public class JavaDocumentationTool {
@Tool(description = "Search Java class documentation. Returns Javadoc summary and method list.")
public String searchJavaDoc(String className) {
// In production: call actual Javadoc API or database
Map<String, String> docs = Map.of(
"ArrayList", "Resizable array implementation of List. Key methods: add, get, remove, size, iterator.",
"HashMap", "Hash table based Map implementation. Key methods: put, get, containsKey, keySet, values.",
"CompletableFuture", "Async computation result. Key methods: supplyAsync, thenApply, thenAccept, join, exceptionally."
);
return docs.getOrDefault(className,
"No documentation found for: " + className + ". Try ArrayList, HashMap, or CompletableFuture.");
}
@Tool(description = "Get Spring Boot auto-configuration details for a starter dependency.")
public String getAutoConfigInfo(String starterName) {
return switch (starterName) {
case "spring-boot-starter-data-jpa" ->
"Auto-configures: DataSource, EntityManagerFactory, JpaTransactionManager. " +
"Requires: datasource URL, username, password in application.properties.";
case "spring-boot-starter-security" ->
"Auto-configures: SecurityFilterChain with basic auth. " +
"Default user: 'user', password in console. Override with SecurityFilterChain @Bean.";
default -> "Starter info not available for: " + starterName;
};
}
}
Register Tools as Spring Beans
import org.springframework.ai.mcp.server.McpServerFeatures;
@Configuration
public class McpToolConfig {
// Register tool providers — Spring AI MCP auto-discovers @Tool annotated methods
@Bean
public List<ToolCallback> javaTools(JavaDocumentationTool javaDocTool) {
return ToolCallbacks.from(javaDocTool);
}
}
MCP Resources — Expose Data to AI Clients
import org.springframework.ai.mcp.server.annotation.McpResource;
import org.springframework.ai.mcp.spec.McpSchema;
@Component
public class JavaCheatSheetResources {
@Bean
public List<McpServerFeatures.SyncResourceRegistration> javaResources() {
var cheatSheet = new McpServerFeatures.SyncResourceRegistration(
new McpSchema.Resource(
"java://cheatsheet/collections",
"Java Collections Cheat Sheet",
"Complete guide to Java collection types with use cases",
"text/plain",
null
),
req -> new McpSchema.ReadResourceResult(List.of(
new McpSchema.TextResourceContents(
req.uri(),
"text/plain",
getCollectionsCheatSheet()
)
))
);
return List.of(cheatSheet);
}
private String getCollectionsCheatSheet() {
return """
Java Collections Quick Reference
=================================
ArrayList → Fast random access, O(1). Use when order matters.
LinkedList → Fast insert/delete at ends, O(1). Use as Queue/Deque.
HashSet → No duplicates, O(1) lookup. Use for membership testing.
TreeSet → Sorted, no duplicates, O(log n). Use when sorted order needed.
HashMap → Key-value, O(1) avg. Most common Map.
TreeMap → Sorted keys, O(log n). Use when key order matters.
PriorityQueue → Min-heap by default. Use for scheduling/top-K.
""";
}
}
MCP Prompts — Pre-defined Prompt Templates for AI Clients
@Bean
public List<McpServerFeatures.SyncPromptRegistration> javaPrompts() {
var codeReviewPrompt = new McpServerFeatures.SyncPromptRegistration(
new McpSchema.Prompt(
"java-code-review",
"Performs a thorough Java code review",
List.of(
new McpSchema.PromptArgument("code", "Java code to review", true),
new McpSchema.PromptArgument("focus_area", "security|performance|style", false)
)
),
req -> {
String code = req.arguments().get("code");
String focus = req.arguments().getOrDefault("focus_area", "general");
return new McpSchema.GetPromptResult(
"Java code review prompt",
List.of(new McpSchema.PromptMessage(
McpSchema.Role.USER,
new McpSchema.TextContent("Review this Java code for %s issues:\n\n```java\n%s\n```"
.formatted(focus, code))
))
);
}
);
return List.of(codeReviewPrompt);
}
Testing the MCP Server
# The server exposes SSE endpoint at:
GET http://localhost:8080/sse
# List available tools:
POST http://localhost:8080/mcp
{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}
# Response:
{
"result": {
"tools": [
{
"name": "searchJavaDoc",
"description": "Search Java class documentation...",
"inputSchema": { "type": "object", "properties": { "className": {"type":"string"} } }
},
{
"name": "getAutoConfigInfo",
"description": "Get Spring Boot auto-configuration details..."
}
]
}
}
Connect from Claude Desktop
# Add to claude_desktop_config.json:
{
"mcpServers": {
"java9r-tools": {
"command": "java",
"args": ["-jar", "/path/to/mcp-server.jar"],
"env": {}
}
}
}
Key Points
- MCP server exposes tools, resources, and prompts — tools are callable, resources are readable data, prompts are templates
- Set
spring.ai.mcp.server.stdio=truefor CLI clients like Claude Desktop; use default HTTP/SSE for web-based clients - Any Spring bean method annotated with
@Tooland registered viaToolCallbacks.from()becomes an MCP tool - MCP is provider-agnostic — the same server works with Claude, GPT-4, and any other MCP-compatible client
- Resources are better than tools for static reference data — they don't consume LLM thinking time for retrieval
Comments