agentsclimarketplace

Mcp server

Skill rrezartprebreza/spring-boot-skills/skills/spring-boot-4/mcp-server

Production-grade Claude Code and Codex skills for Spring Boot developers

Install
npx -y skills add rrezartprebreza/spring-boot-skills --skill mcp-server

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

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.

SKILL.md

8.9 KB, as published. Nobody here has run it

MCP Server — Java SDK

Official Java SDK: https://github.com/modelcontextprotocol/java-sdk
Maintained by Anthropic in collaboration with Spring AI.

Dependency

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).

Minimal MCP Server (stdio transport)

@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));
    }
}

Defining Tools

// 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 Boot Integration (recommended)

@Configuration
public class McpToolsConfig {

    @Bean
    public ToolCallbackProvider orderTools(OrderService orderService, ObjectMapper objectMapper) {
        return MethodToolCallbackProvider.builder()
            .toolObjects(new OrderMcpTools(orderService, objectMapper))
            .build();
    }
}

@Component
public class OrderMcpTools {
    private final OrderService orderService;
    private final ObjectMapper objectMapper;

    // Spring AI annotation-based tool registration
    @Tool(description = "Get order by ID with full line items and status history")
    public String getOrder(@ToolParam(description = "UUID of the order") String orderId) {
        try {
            Order order = orderService.findById(UUID.fromString(orderId));
            return objectMapper.writeValueAsString(OrderResponse.from(order));
        } catch (Exception e) {
            return "Error: " + e.getMessage();
        }
    }

    @Tool(description = "List orders for a customer, optionally filtered by status")
    public String listOrders(
        @ToolParam(description = "Customer email address") String email,
        @ToolParam(description = "Filter by status: PENDING, PROCESSING, SHIPPED, DELIVERED", required = false) String status
    ) {
        List<Order> orders = status != null
            ? orderService.findByEmailAndStatus(email, OrderStatus.valueOf(status))
            : orderService.findByEmail(email);
        return objectMapper.writeValueAsString(orders.stream().map(OrderResponse::from).toList());
    }
}

application.yml for MCP Server

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.

Exposing Resources

@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()
    );
}

claude_desktop_config.json / .mcp.json

{
  "mcpServers": {
    "order-service": {
      "command": "java",
      "args": ["-jar", "/path/to/order-mcp-server.jar"],
      "env": {
        "SPRING_DATASOURCE_URL": "jdbc:postgresql://localhost:5432/orders"
      }
    }
  }
}

Error Handling Pattern

// 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
    );
}

Gotchas

  • Agent generates Python MCP code — always use the Java SDK
  • Agent uses the dead spring-ai-mcp-server-spring-boot-starter name — Spring AI 2.0 uses spring-ai-starter-mcp-server[-webmvc|-webflux]
  • Agent enables remote HTTP with spring.ai.mcp.server.transport — use spring.ai.mcp.server.protocol=STREAMABLE / STATELESS
  • Agent pins SDK 0.9.0 — the standalone SDK is 1.0.0 GA (or just use the Spring AI starter)
  • Agent logs to stdout on a stdio server — corrupts JSON-RPC framing; banner off, logs to file/stderr
  • Agent forgets isError = true in error results — agent can't distinguish errors from data
  • Agent uses FetchType.EAGER inside tool handlers — triggers N+1, use projections
  • Agent exposes entities directly — serialize to DTOs before returning
  • Agent ignores shutdown hooks — always close the server on JVM shutdown
  • Agent writes vague tool descriptions — the description IS the prompt the model reads; be specific about when to call it and what it returns
  • stdio for local tools (Claude Code, Claude Desktop); -webmvc/-webflux + Streamable-HTTP for remote

Gives 0 of the 12 instructions most mcp tooling skills give

Counted across 638 of the 750 authors here whose files we hold, read 2026-08-06

  • create ten complex read-only evaluation questionsin 71 of 638, across 17 files
  • test servers using MCP Inspectorin 60 of 638, across 18 files
  • provide actionable error messagesin 56 of 638, across 14 files
  • prioritize comprehensive API coverage over specific workflowsin 54 of 638, across 12 files
  • use TypeScript and Streamable HTTP for remote serversin 53 of 638, across 7 files
  • define structured output schemas where possiblein 51 of 638, across 9 files
  • use Zod or Pydantic for input schemasin 48 of 638, across 6 files
  • fetch MCP specification pages with markdown suffixin 46 of 638, across 4 files
  • load framework documentation using WebFetchin 45 of 638, across 3 files
  • verify each evaluation answer independentlyin 45 of 638, across 3 files
  • implement API client with authentication and paginationin 45 of 638, across 3 files
  • Define input schemas with validationin 28 of 638, across 10 files

Said here and by no other author read

  • Use the Spring AI MCP starter
  • Serialize entities to DTOs before returning
  • define tools with typed inputs and outputs
  • register tools using MethodToolCallbackProvider
  • keep stdout clean on stdio servers
  • disable banners and log to stderr

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.