> Use when building MCP (Model Context Protocol) servers in Java/Spring Boot. Covers tool registration, resource exposure, prompt templates, and production deployment using the official MCP Java SDK. Use when user mentions MCP, AI agent integration, or tool calling.
npx skills add https://github.com/rrezartprebreza/spring-boot-skills --skill mcp-server
Official Java SDK: https://github.com/modelcontextprotocol/java-sdk
Maintained by Anthropic in collaboration with Spring AI.
The standalone SDK reached 1.0.0 GA (io.modelcontextprotocol.sdk:mcp). Most Spring Boot
apps should use the Spring AI MCP starter instead — it auto-configures the server, transport,
and annotation-based tool scanning. In Spring AI 2.0, use spring-ai-starter-mcp-server-*
with spring.ai.mcp.server.protocol=STREAMABLE for remote HTTP.
<!-- Recommended for Spring Boot: pick ONE transport starter -->
<!-- stdio (Claude Desktop / Claude Code launching the jar locally) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server</artifactId>
</dependency>
<!-- OR remote HTTP (SSE + Streamable-HTTP) over Spring MVC -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
<!-- OR reactive: spring-ai-starter-mcp-server-webflux -->
<!-- Or drive the raw SDK directly (no Spring AI), now at 1.0.0 GA -->
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
<version>1.0.0</version>
</dependency>
> The old spring-ai-mcp-server-spring-boot-starter name is dead. GA is spring-ai-starter-mcp-server
> (stdio), -webmvc (servlet SSE / Streamable-HTTP), and -webflux (reactive).
@SpringBootApplication
public class OrderMcpServer {
public static void main(String[] args) {
var transport = new StdioServerTransportProvider();
var server = McpServer.sync(transport)
.serverInfo("order-service-mcp", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).resources(true).build())
.tools(getOrderTool(), listOrdersTool())
.build();
Runtime.getRuntime().addShutdownHook(new Thread(server::close));
}
}
// Tool with typed input/output
private static McpServerFeatures.SyncToolSpecification getOrderTool() {
var schema = """
{
"type": "object",
"properties": {
"orderId": { "type": "string", "description": "UUID of the order" }
},
"required": ["orderId"]
}
""";
return McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder()
.name("get_order")
.description("Get a single order by ID including all line items and status history")
.inputSchema(schema)
.build())
.callHandler((exchange, args) -> {
String orderId = (String) args.get("orderId");
try {
Order order = orderService.findById(UUID.fromString(orderId));
return new CallToolResult(List.of(
new TextContent(objectMapper.writeValueAsString(order))
), false);
} catch (EntityNotFoundException e) {
return new CallToolResult(List.of(
new TextContent("Order not found: " + orderId)
), true); // isError = true
}
})
.build();
}
Spring AI 2.0 provides native MCP annotations. Use @McpTool for MCP server tools; @Tool is a
different Spring AI model tool-calling API and should only be used when intentionally registering
ToolCallback objects with the MCP tool-callback converter.
@Component
public class OrderMcpTools {
private final OrderService orderService;
public OrderMcpTools(OrderService orderService) {
this.orderService = orderService;
}
@McpTool(
name = "get_order",
description = "Get an order by ID with line items and status history",
generateOutputSchema = true)
public OrderResponse getOrder(
@McpToolParam(description = "UUID of the order", required = true) String orderId) {
return OrderResponse.from(orderService.findById(UUID.fromString(orderId)));
}
@McpTool(
name = "list_orders",
description = "List orders for a customer, optionally filtered by status",
generateOutputSchema = true)
public List<OrderResponse> listOrders(
@McpToolParam(description = "Customer email address", required = true) String email,
@McpToolParam(description = "PENDING, PROCESSING, SHIPPED, or DELIVERED", required = false)
String status) {
List<Order> orders = status != null
? orderService.findByEmailAndStatus(email, OrderStatus.valueOf(status))
: orderService.findByEmail(email);
return orders.stream().map(OrderResponse::from).toList();
}
}
With the Spring AI MCP starter, annotated @Component methods are discovered automatically. Do not
also create a MethodToolCallbackProvider for the same methods unless you intentionally choose the
alternative Spring AI tool-callback integration.
spring:
ai:
mcp:
server:
name: order-service-mcp
version: 1.0.0
type: SYNC # SYNC (blocking) or ASYNC (reactive / WebFlux)
# --- stdio: needs spring-ai-starter-mcp-server + banner/console logging OFF ---
stdio: true # framing is over stdin/stdout — nothing else may write there
# --- remote: needs the -webmvc or -webflux starter instead ---
# protocol: STREAMABLE # SSE | STREAMABLE | STATELESS (Streamable-HTTP preferred)
> stdio servers must keep stdout clean. Any log line, banner, or System.out.println corrupts
> the JSON-RPC framing and the client silently drops the connection. For stdio, set
> spring.main.banner-mode=off and route logging to a file or stderr.
@Bean
public List<McpServerFeatures.SyncResourceSpecification> mcpResources(OrderRepository repo) {
return List.of(
McpServerFeatures.SyncResourceSpecification.builder()
.resource(Resource.builder()
.uri("orders://recent")
.name("Recent Orders")
.description("Last 50 orders across all customers")
.mimeType("application/json")
.build())
.readHandler((exchange, request) -> {
List<Order> recent = repo.findTop50ByOrderByCreatedAtDesc();
return new ReadResourceResult(List.of(
new TextResourceContents(request.uri(),
objectMapper.writeValueAsString(recent), "application/json")
));
})
.build()
);
}
{
"mcpServers": {
"order-service": {
"command": "java",
"args": ["-jar", "/path/to/order-mcp-server.jar"],
"env": {
"SPRING_DATASOURCE_URL": "jdbc:postgresql://localhost:5432/orders"
}
}
}
}
// Always return structured errors — never throw from tool handlers
private CallToolResult safeExecute(Supplier<Object> action) {
try {
return new CallToolResult(
List.of(new TextContent(objectMapper.writeValueAsString(action.get()))),
false
);
} catch (EntityNotFoundException e) {
return errorResult("NOT_FOUND", e.getMessage());
} catch (Exception e) {
log.error("Tool execution failed", e);
return errorResult("INTERNAL_ERROR", "Unexpected error occurred");
}
}
private CallToolResult errorResult(String code, String message) {
return new CallToolResult(
List.of(new TextContent(String.format("{\"error\":\"%s\",\"message\":\"%s\"}", code, message))),
true // isError flag — agent knows this is an error
);
}
spring-ai-mcp-server-spring-boot-starter name — Spring AI 2.0 uses spring-ai-starter-mcp-server[-webmvc|-webflux]@Tool when it needs native MCP server annotations — use @McpTool and @McpToolParam; @Tool belongs to Spring AI model tool callingspring.ai.mcp.server.transport — use spring.ai.mcp.server.protocol=STREAMABLE / STATELESS0.9.0 — the standalone SDK is 1.0.0 GA (or just use the Spring AI starter)isError = true in error results — agent can't distinguish errors from dataFetchType.EAGER inside tool handlers — triggers N+1, use projectionsshutdown hooks — always close the server on JVM shutdownstdio for local tools (Claude Code, Claude Desktop); -webmvc/-webflux + Streamable-HTTP for remoteThis skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.
Build agentic applications with GitHub Copilot SDK. Use when embedding AI agents in apps, creating custom tools, implementing streaming responses, managing sessions, connecting to MCP servers, or creating custom agents. Triggers on Copilot SDK, GitHub SDK, agentic app, embed Copilot, programmable agent, MCP server, custom agent.
Coding Agent Session Search - unified CLI/TUI to index and search local coding agent history from Claude Code, Codex, Gemini, Cursor, Aider, ChatGPT, Pi-Agent, Factory, and more. Purpose-built for AI agent consumption with robot mode.
Destructive Command Guard - High-performance Rust hook for Claude Code that blocks dangerous commands before execution. SIMD-accelerated, modular pack system, whitelist-first architecture. Essential safety layer for agent workflows.
Makepad UI development skills for Rust apps: setup, patterns, shaders, packaging, and troubleshooting.
Secure environment variable management ensuring secrets are never exposed in Claude sessions, terminals, logs, or git commits
Prompt for generating an AGENTS.md file for a repository
Take rrezartprebreza/spring-boot-mcp-server from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.