java-mcp-server-generator

java-mcp-server-generator

熱門

使用官方 MCP Java SDK 搭配反應式串流與可選的 Spring Boot 整合,生成完整的 Java Model Context Protocol 伺服器專案。

3.7萬星標
4569分支
更新於 2026/7/14
SKILL.md
readonlyread-only
name
java-mcp-server-generator
description

使用官方 MCP Java SDK 搭配反應式串流與可選的 Spring Boot 整合,生成完整的 Java Model Context Protocol 伺服器專案。

Java MCP Server Generator

使用官方 Java SDK 搭配 Maven 或 Gradle,生成一個完整且可上線的 MCP 伺服器。

專案生成

當被要求建立一個 Java MCP 伺服器時,請生成具有以下結構的完整專案:

my-mcp-server/
├── pom.xml (或 build.gradle.kts)
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/mcp/
│   │   │       ├── McpServerApplication.java
│   │   │       ├── config/
│   │   │       │   └── ServerConfiguration.java
│   │   │       ├── tools/
│   │   │       │   ├── ToolDefinitions.java
│   │   │       │   └── ToolHandlers.java
│   │   │       ├── resources/
│   │   │       │   ├── ResourceDefinitions.java
│   │   │       │   └── ResourceHandlers.java
│   │   │       └── prompts/
│   │   │           ├── PromptDefinitions.java
│   │   │           └── PromptHandlers.java
│   │   └── resources/
│   │       └── application.properties (若使用 Spring)
│   └── test/
│       └── java/
│           └── com/example/mcp/
│               └── McpServerTest.java
└── README.md

Maven 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
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>my-mcp-server</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <name>My MCP Server</name>
    <description>Model Context Protocol 伺服器實作</description>

    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <mcp.version>0.14.1</mcp.version>
        <slf4j.version>2.0.9</slf4j.version>
        <logback.version>1.4.11</logback.version>
        <junit.version>5.10.0</junit.version>
    </properties>

    <dependencies>
        <!-- MCP Java SDK -->
        <dependency>
            <groupId>io.modelcontextprotocol.sdk</groupId>
            <artifactId>mcp</artifactId>
            <version>${mcp.version}</version>
        </dependency>

        <!-- 日誌 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>

        <!-- 測試 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.1.2</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.5.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.example.mcp.McpServerApplication</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Gradle build.gradle.kts 範本

plugins {
    id("java")
    id("application")
}

group = "com.example"
version = "1.0.0"

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
}

dependencies {
    // MCP Java SDK
    implementation("io.modelcontextprotocol.sdk:mcp:0.14.1")
    
    // 日誌
    implementation("org.slf4j:slf4j-api:2.0.9")
    implementation("ch.qos.logback:logback-classic:1.4.11")
    
    // 測試
    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
    testImplementation("io.projectreactor:reactor-test:3.5.0")
}

application {
    mainClass.set("com.example.mcp.McpServerApplication")
}

tasks.test {
    useJUnitPlatform()
}

McpServerApplication.java 範本

package com.example.mcp;

import com.example.mcp.tools.ToolHandlers;
import com.example.mcp.resources.ResourceHandlers;
import com.example.mcp.prompts.PromptHandlers;
import io.mcp.server.McpServer;
import io.mcp.server.McpServerBuilder;
import io.mcp.server.transport.StdioServerTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Disposable;

public class McpServerApplication {
    
    private static final Logger log = LoggerFactory.getLogger(McpServerApplication.class);
    
    public static void main(String[] args) {
        log.info("正在啟動 MCP 伺服器...");
        
        try {
            McpServer server = createServer();
            StdioServerTransport transport = new StdioServerTransport();
            
            // 啟動伺服器
            Disposable serverDisposable = server.start(transport).subscribe();
            
            // 優雅關閉
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                log.info("正在關閉 MCP 伺服器");
                serverDisposable.dispose();
                server.stop().block();
            }));
            
            log.info("MCP 伺服器啟動成功");
            
            // 保持運行
            Thread.currentThread().join();
            
        } catch (Exception e) {
            log.error("啟動 MCP 伺服器失敗", e);
            System.exit(1);
        }
    }
    
    private static McpServer createServer() {
        McpServer server = McpServerBuilder.builder()
            .serverInfo("my-mcp-server", "1.0.0")
            .capabilities(capabilities -> capabilities
                .tools(true)
                .resources(true)
                .prompts(true))
            .build();
        
        // 註冊處理器
        ToolHandlers.register(server);
        ResourceHandlers.register(server);
        PromptHandlers.register(server);
        
        return server;
    }
}

ToolDefinitions.java 範本

package com.example.mcp.tools;

import io.mcp.json.JsonSchema;
import io.mcp.server.tool.Tool;

import java.util.List;

public class ToolDefinitions {
    
    public static List<Tool> getTools() {
        return List.of(
            createGreetTool(),
            createCalculateTool()
        );
    }
    
    private static Tool createGreetTool() {
        return Tool.builder()
            .name("greet")
            .description("產生問候訊息")
            .inputSchema(JsonSchema.object()
                .property("name", JsonSchema.string()
                    .description("要問候的名稱")
                    .required(true)))
            .build();
    }
    
    private static Tool createCalculateTool() {
        return Tool.builder()
            .name("calculate")
            .description("執行數學計算")
            .inputSchema(JsonSchema.object()
                .property("operation", JsonSchema.string()
                    .description("要執行的運算")
                    .enumValues(List.of("add", "subtract", "multiply", "divide"))
                    .required(true))
                .property("a", JsonSchema.number()
                    .description("第一個運算元")
                    .required(true))
                .property("b", JsonSchema.number()
                    .description("第二個運算元")
                    .required(true)))
            .build();
    }
}

ToolHandlers.java 範本

package com.example.mcp.tools;

import com.fasterxml.jackson.databind.JsonNode;
import io.mcp.server.McpServer;
import io.mcp.server.tool.ToolResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;

public class ToolHandlers {
    
    private static final Logger log = LoggerFactory.getLogger(ToolHandlers.class);
    
    public static void register(McpServer server) {
        // 註冊工具列表處理器
        server.addToolListHandler(() -> {
            log.debug("列出可用工具");
            return Mono.just(ToolDefinitions.getTools());
        });
        
        // 註冊 greet 處理器
        server.addToolHandler("greet", ToolHandlers::handleGreet);
        
        // 註冊 calculate 處理器
        server.addToolHandler("calculate", ToolHandlers::handleCalculate);
    }
    
    private static Mono<ToolResponse> handleGreet(JsonNode arguments) {
        log.info("Greet 工具被呼叫");
        
        if (!arguments.has("name")) {
            return Mono.just(ToolResponse.error()
                .message("缺少 'name' 參數")
                .build());
        }
        
        String name = arguments.get("name").asText();
        String greeting = "哈囉," + name + "!歡迎使用 MCP。";
        
        log.debug("已為 {} 產生問候", name);
        
        return Mono.just(ToolResponse.success()
            .addTextContent(greeting)
            .build());
    }
    
    private static Mono<ToolResponse> handleCalculate(JsonNode arguments) {
        log.info("Calculate 工具被呼叫");
        
        if (!arguments.has("operation") || !arguments.has("a") || !arguments.has("b")) {
            return Mono.just(ToolResponse.error()
                .message("缺少必要參數")
                .build());
        }
        
        String operation = arguments.get("operation").asText();
        double a = arguments.get("a").asDouble();
        double b = arguments.get("b").asDouble();
        
        double result;
        switch (operation) {
            case "add":
                result = a + b;
                break;
            case "subtract":
                result = a - b;
                break;
            case "multiply":
                result = a * b;
                break;
            case "divide":
                if (b == 0) {
                    return Mono.just(ToolResponse.error()
                        .message("除以零")
                        .build());
                }
                result = a / b;
                break;
            default:
                return Mono.just(ToolResponse.error()
                    .message("未知運算:" + operation)
                    .build());
        }
        
        log.debug("計算:{} {} {} = {}", a, operation, b, result);
        
        return Mono.just(ToolResponse.success()
            .addTextContent("結果:" + result)
            .build());
    }
}

ResourceDefinitions.java 範本

package com.example.mcp.resources;

import io.mcp.server.resource.Resource;

import java.util.List;

public class ResourceDefinitions {
    
    public static List<Resource> getResources() {
        return List.of(
            Resource.builder()
                .name("範例資料")
                .uri("resource://data/example")
                .description("範例資源資料")
                .mimeType("application/json")
                .build(),
            Resource.builder()
                .name("設定")
                .uri("resource://config")
                .description("伺服器設定")
                .mimeType("application/json")
                .build()
        );
    }
}

ResourceHandlers.java 範本

package com.example.mcp.resources;

import io.mcp.server.McpServer;
import io.mcp.server.resource.ResourceContent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;

import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ResourceHandlers {
    
    private static final Logger log = LoggerFactory.getLogger(ResourceHandlers.class);
    private static final Map<String, Boolean> subscriptions = new ConcurrentHashMap<>();
    
    public static void register(McpServer server) {
        // 註冊資源列表處理器
        server.addResourceListHandler(() -> {
            log.debug("列出可用資源");
            return Mono.just(ResourceDefinitions.getResources());
        });
        
        // 註冊資源讀取處理器
        server.addResourceReadHandler(ResourceHandlers::handleRead);
        
        // 註冊資源訂閱處理器
        server.addResourceSubscribeHandler(ResourceHandlers::handleSubscribe);
        
        // 註冊資源取消訂閱處理器
        server.addResourceUnsubscribeHandler(ResourceHandlers::handleUnsubscribe);
    }
    
    private static Mono<ResourceContent> handleRead(String uri) {
        log.info("讀取資源:{}", uri);
        
        switch (uri) {
            case "resource://data/example":
                String jsonData = String.format(
                    "{\"message\":\"範例資源資料\",\"timestamp\":\"%s\"}",
                    Instant.now()
                );
                return Mono.just(ResourceContent.text(jsonData, uri, "application/json"));
                
            case "resource://config":
                String config = "{\"serverName\":\"my-mcp-server\",\"version\":\"1.0.0\"}";
                return Mono.just(ResourceContent.text(config, uri, "application/json"));
                
            default:
                log.warn("請求了未知資源:{}", uri);
                return Mono.error(new IllegalArgumentException("未知的資源 URI:" + uri));
        }
    }
    
    private static Mono<Void> handleSubscribe(String uri) {
        log.info("用戶端已訂閱資源:{}", uri);
        subscriptions.put(uri, true);
        return Mono.empty();
    }
    
    private static Mono<Void> handleUnsubscribe(String uri) {
        log.info("用戶端已取消訂閱資源:{}", uri);
        subscriptions.remove(uri);
        return Mono.empty();
    }
}

PromptDefinitions.java 範本

package com.example.mcp.prompts;

import io.mcp.server.prompt.Prompt;
import io.mcp.server.prompt.PromptArgument;

import java.util.List;

public class PromptDefinitions {
    
    public static List<Prompt> getPrompts() {
        return List.of(
            Prompt.builder()
                .name("code-review")
                .description("產生程式碼審查提示")
                .argument(PromptArgument.builder()
                    .name("language")
                    .description("程式語言")
                    .required(true)
                    .build())
                .argument(PromptArgument.builder()
                    .name("focus")
                    .description("審查重點領域")
                    .required(false)
                    .build())
                .build()
        );
    }
}

PromptHandlers.java 範本

package com.example.mcp.prompts;

import io.mcp.server.McpServer;
import io.mcp.server.prompt.PromptMessage;
import io.mcp.server.prompt.PromptResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

public class PromptHandlers {
    
    private static final Logger log = LoggerFactory.getLogger(PromptHandlers.class);
    
    public static void register(McpServer server) {
        // 註冊提示列表處理器
        server.addPromptListHandler(() -> {
            log.debug("列出可用提示");
            return Mono.just(PromptDefinitions.getPrompts());
        });
        
        // 註冊提示取得處理器
        server.addPromptGetHandler(PromptHandlers::handleCodeReview);
    }
    
    private static Mono<PromptResult> handleCodeReview(String name, Map<String, String> arguments) {
        log.info("取得提示:{}", name);
        
        if (!name.equals("code-review")) {
            return Mono.error(new IllegalArgumentException("未知的提示:" + name));
        }
        
        String language = arguments.getOrDefault("language", "Java");
        String focus = arguments.getOrDefault("focus", "一般品質");
        
        String description = "針對 " + language + " 的程式碼審查,重點在 " + focus;
        
        List<PromptMessage> messages = List.of(
            PromptMessage.user("請審查這段 " + language + " 程式碼,重點在 " + focus + "。"),
            PromptMessage.assistant("我會針對 " + focus + " 審查程式碼。請提供程式碼。"),
            PromptMessage.user("這是待審查的程式碼:[在此貼上程式碼]")
        );
        
        log.debug("已為 {} ({}) 產生程式碼審查提示", language, focus);
        
        return Mono.just(PromptResult.builder()
            .description(description)
            .messages(messages)
            .build());
    }
}

McpServerTest.java 範本

package com.example.mcp;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.mcp.server.McpServer;
import io.mcp.server.McpSyncServer;
import io.mcp.server.tool.ToolResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class McpServerTest {
    
    private McpSyncServer syncServer;
    private ObjectMapper objectMapper;
    
    @BeforeEach
    void setUp() {
        McpServer server = createTestServer();
        syncServer = server.toSyncServer();
        objectMapper = new ObjectMapper();
    }
    
    private McpServer createTestServer() {
        // 與主應用程式相同的設定
        McpServer server = McpServerBuilder.builder()
            .serverInfo("test-server", "1.0.0")
            .capabilities(cap -> cap.tools(true))
            .build();
        
        // 註冊處理器
        ToolHandlers.register(server);
        
        return server;
    }
    
    @Test
    void testGreetTool() {
        ObjectNode args = objectMapper.createObjectNode();
        args.put("name", "Java");
        
        ToolResponse response = syncServer.callTool("greet", args);
        
        assertFalse(response.isError());
        assertEquals(1, response.getContent().size());
        assertTrue(response.getContent().get(0).getText().contains("Java"));
    }
    
    @Test
    void testCalculateTool() {
        ObjectNode args = objectMapper.createObjectNode();
        args.put("operation", "add");
        args.put("a", 5);
        args.put("b", 3);
        
        ToolResponse response = syncServer.callTool("calculate", args);
        
        assertFalse(response.isError());
        assertTrue(response.getContent().get(0).getText().contains("8"));
    }
    
    @Test
    void testDivideByZero() {
        ObjectNode args = objectMapper.createObjectNode();
        args.put("operation", "divide");
        args.put("a", 10);
        args.put("b", 0);
        
        ToolResponse response = syncServer.callTool("calculate", args);
        
        assertTrue(response.isError());
    }
}

README.md 範本

# My MCP Server

使用 Java 與官方 MCP Java SDK 建置的 Model Context Protocol 伺服器。

## 功能特色

- ✅ 工具:greet, calculate
- ✅ 資源:範例資料, 設定
- ✅ 提示:code-review
- ✅ 使用 Project Reactor 的反應式串流
- ✅ 使用 SLF4J 的結構化日誌
- ✅ 完整的測試覆蓋率

## 需求

- Java 17 或更新版本
- Maven 3.6+ 或 Gradle 7+

## 建置

### Maven
```bash
mvn clean package

Gradle

./gradlew build

執行

Maven

java -jar target/my-mcp-server-1.0.0.jar

Gradle

./gradlew run

測試

Maven

mvn test

Gradle

./gradlew test

與 Claude Desktop 整合

新增至 claude_desktop_config.json

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "java",
      "args": ["-jar", "/path/to/my-mcp-server-1.0.0.jar"]
    }
  }
}

授權

MIT


### 生成說明

1. **詢問專案名稱與套件**
2. **選擇建置工具**(Maven 或 Gradle)
3. **生成所有檔案**,包含正確的套件結構
4. **使用反應式串流**處理非同步處理器
5. **包含完整的日誌記錄**,使用 SLF4J
6. **為所有處理器加入測試**
7. **遵循 Java 慣例**(camelCase、PascalCase)
8. **包含錯誤處理**,回傳適當的回應
9. **使用 Javadoc 文件化公開 API**
10. **提供同步與非同步範例**