|
| 1 | +--- |
| 2 | +title: 快速开始 |
| 3 | +description: 使用纯 Java 构建第一个注解驱动的 MCP 服务器。 |
| 4 | +--- |
| 5 | + |
| 6 | +本指南将帮助你在 5 分钟内构建第一个 MCP 服务器。 |
| 7 | + |
| 8 | +如果你希望在不引入 Spring 运行时的情况下,使用纯 Java 构建注解驱动的 MCP 服务器,可以选择本 SDK。如果你正在开发 Spring Boot 应用,Spring AI MCP 通常是更合适的默认选择;本指南专注于面向 CLI 工具、嵌入式服务器、本地自动化和小型服务进程的轻量级 Java 方案。 |
| 9 | + |
| 10 | +## 环境要求 |
| 11 | + |
| 12 | +- **Java 17 或更高版本**(官方 MCP Java SDK 的要求) |
| 13 | + |
| 14 | +## 安装 |
| 15 | + |
| 16 | +### Maven 依赖 |
| 17 | + |
| 18 | +```xml |
| 19 | +<dependency> |
| 20 | + <groupId>io.github.thought2code</groupId> |
| 21 | + <artifactId>mcp-annotated-java-sdk</artifactId> |
| 22 | + <version>0.20.0</version> |
| 23 | +</dependency> |
| 24 | +``` |
| 25 | + |
| 26 | +### Gradle 依赖 |
| 27 | + |
| 28 | +```groovy |
| 29 | +implementation 'io.github.thought2code:mcp-annotated-java-sdk:0.20.0' |
| 30 | +``` |
| 31 | + |
| 32 | +## 5 分钟教程 |
| 33 | + |
| 34 | +### 第 1 步:创建配置文件 |
| 35 | + |
| 36 | +在 `src/main/resources` 中创建 `mcp-server.yml`: |
| 37 | + |
| 38 | +```yaml |
| 39 | +enabled: true |
| 40 | +mode: STDIO |
| 41 | +name: my-first-mcp-server |
| 42 | +version: 1.0.0 |
| 43 | +type: SYNC |
| 44 | +instructions: You are a helpful AI assistant |
| 45 | +request-timeout: 20000 |
| 46 | +capabilities: |
| 47 | + resource: true |
| 48 | + subscribe-resource: true |
| 49 | + prompt: true |
| 50 | + tool: true |
| 51 | + completion: true |
| 52 | +change-notification: |
| 53 | + resource: true |
| 54 | + prompt: true |
| 55 | + tool: true |
| 56 | +``` |
| 57 | +
|
| 58 | +### 第 2 步:创建 MCP 服务器主类 |
| 59 | +
|
| 60 | +```java |
| 61 | +@McpServerApplication |
| 62 | +public class MyFirstMcpServer { |
| 63 | + public static void main(String[] args) { |
| 64 | + McpApplication.run(MyFirstMcpServer.class, args); |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +### 第 3 步:定义 MCP 资源(可选) |
| 70 | + |
| 71 | +```java |
| 72 | +public class MyResources { |
| 73 | + @McpResource(uri = "system://info", description = "System information") |
| 74 | + public Map<String, String> getSystemInfo() { |
| 75 | + Map<String, String> info = new HashMap<>(); |
| 76 | + info.put("os", System.getProperty("os.name")); |
| 77 | + info.put("java", System.getProperty("java.version")); |
| 78 | + info.put("cores", String.valueOf(Runtime.getRuntime().availableProcessors())); |
| 79 | + return info; |
| 80 | + } |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +### 第 4 步:定义 MCP 工具 |
| 85 | + |
| 86 | +```java |
| 87 | +public class MyTools { |
| 88 | + @McpTool(description = "Calculate the sum of two numbers") |
| 89 | + public int add( |
| 90 | + @McpToolParam(name = "a", description = "First number") int a, |
| 91 | + @McpToolParam(name = "b", description = "Second number") int b |
| 92 | + ) { |
| 93 | + return a + b; |
| 94 | + } |
| 95 | +} |
| 96 | +``` |
| 97 | + |
| 98 | +### 第 5 步:定义 MCP 提示词(可选) |
| 99 | + |
| 100 | +```java |
| 101 | +public class MyPrompts { |
| 102 | + @McpPrompt(description = "Generate code for a given task") |
| 103 | + public String generateCode( |
| 104 | + @McpPromptParam(name = "language", description = "Programming language") String language, |
| 105 | + @McpPromptParam(name = "task", description = "Task description") String task |
| 106 | + ) { |
| 107 | + return String.format("Write %s code to: %s", language, task); |
| 108 | + } |
| 109 | +} |
| 110 | +``` |
| 111 | + |
| 112 | +### 第 6 步:运行服务器 |
| 113 | + |
| 114 | +```bash |
| 115 | +# 编译项目 |
| 116 | +./mvnw clean package |
| 117 | +``` |
| 118 | + |
| 119 | +可以从 IDE 中运行 `MyFirstMcpServer`,也可以使用 `java -cp ...`,将编译后的类和依赖加入 classpath 后运行。如果希望通过单个文件执行 `java -jar`,请在自己的项目中配置可执行 JAR。 |
| 120 | + |
| 121 | +如需加载非默认名称的配置文件,请使用 `McpApplication.run(MyFirstMcpServer.class, args, "custom-mcp-server.yml")`。 |
| 122 | + |
| 123 | +### 打包可执行 Fat JAR |
| 124 | + |
| 125 | +部署时,请将应用打包为可执行 Fat JAR,确保 MCP Java SDK、本 SDK 及全部传递依赖在运行时可用。请将 `mcp-server.yml` 保留在 `src/main/resources` 下,以便它被包含在运行时 classpath 中。 |
| 126 | + |
| 127 | +使用 Maven Shade 时,配置 JAR 清单中的主类: |
| 128 | + |
| 129 | +```xml |
| 130 | +<plugin> |
| 131 | + <groupId>org.apache.maven.plugins</groupId> |
| 132 | + <artifactId>maven-shade-plugin</artifactId> |
| 133 | + <version>3.6.2</version> |
| 134 | + <executions> |
| 135 | + <execution> |
| 136 | + <phase>package</phase> |
| 137 | + <goals> |
| 138 | + <goal>shade</goal> |
| 139 | + </goals> |
| 140 | + <configuration> |
| 141 | + <createDependencyReducedPom>false</createDependencyReducedPom> |
| 142 | + <transformers> |
| 143 | + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> |
| 144 | + <mainClass>com.example.MyFirstMcpServer</mainClass> |
| 145 | + </transformer> |
| 146 | + </transformers> |
| 147 | + </configuration> |
| 148 | + </execution> |
| 149 | + </executions> |
| 150 | +</plugin> |
| 151 | +``` |
| 152 | + |
| 153 | +然后运行: |
| 154 | + |
| 155 | +```bash |
| 156 | +java -jar target/your-app.jar |
| 157 | +``` |
| 158 | + |
| 159 | +如果使用 Gradle Shadow,请在清单中配置主类: |
| 160 | + |
| 161 | +```groovy |
| 162 | +tasks.shadowJar { |
| 163 | + manifest { |
| 164 | + attributes 'Main-Class': 'com.example.MyFirstMcpServer' |
| 165 | + } |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | +## 服务器模式 |
| 170 | + |
| 171 | +本 SDK 支持两种 MCP 服务器模式。通过 YAML 加载配置时,`mcp-server.yml` 中的 `mode` 字段是必填项,不存在隐式默认值(仅通过 `ServerConfiguration.builder()` 以编程方式构建配置时才会应用默认值)。 |
| 172 | + |
| 173 | +### 1. STDIO 模式 |
| 174 | + |
| 175 | +基于标准输入/输出通信,适用于 CLI 工具和本地开发。 |
| 176 | + |
| 177 | +```yaml |
| 178 | +# mcp-server.yml |
| 179 | +mode: STDIO |
| 180 | +``` |
| 181 | +
|
| 182 | +### 2. STREAMABLE 模式 |
| 183 | +
|
| 184 | +面向 Web 应用的 HTTP 流式传输,推荐用于生产环境。 |
| 185 | +
|
| 186 | +```yaml |
| 187 | +# mcp-server.yml |
| 188 | +mode: STREAMABLE |
| 189 | +streamable: |
| 190 | + mcp-endpoint: /mcp/message |
| 191 | + disallow-delete: false |
| 192 | + keep-alive-interval: 20000 |
| 193 | + port: 8080 |
| 194 | +``` |
| 195 | +
|
| 196 | +**STREAMABLE** 是本 SDK 支持的 HTTP 传输方式。 |
| 197 | +
|
| 198 | +## 配置属性 |
| 199 | +
|
| 200 | +通过 YAML 加载配置时,下表中的核心字段及适用的嵌套设置均为**必填项**;缺少必填字段会使服务器启动失败,并抛出 `Missing config key '...'`。条件字段仅在对应功能或传输方式启用时才是必填项。“构建器默认值”仅表示通过 `ServerConfiguration.builder()` 以编程方式构建配置时采用的值。 |
| 201 | + |
| 202 | +| 属性 | 说明 | 构建器默认值 | |
| 203 | +|---|---|---| |
| 204 | +| `enabled` | 启用或禁用 MCP 服务器 | `true` | |
| 205 | +| `mode` | 服务器模式:`STDIO`、`STREAMABLE` | `STREAMABLE` | |
| 206 | +| `name` | 服务器名称 | `mcp-server` | |
| 207 | +| `version` | 服务器版本 | `1.0.0` | |
| 208 | +| `type` | 服务器类型:`SYNC`、`ASYNC` | `SYNC` | |
| 209 | +| `instructions` | 提供给 LLM 客户端的指令 | *(空字符串)* | |
| 210 | +| `request-timeout` | 请求超时时间(毫秒) | `20000` | |
| 211 | +| `capabilities.resource` | 启用资源支持 | `true` | |
| 212 | +| `capabilities.subscribe-resource` | 启用资源订阅 | `true` | |
| 213 | +| `capabilities.prompt` | 启用提示词支持 | `true` | |
| 214 | +| `capabilities.tool` | 启用工具支持 | `true` | |
| 215 | +| `capabilities.completion` | 启用补全支持 | `true` | |
| 216 | +| `change-notification.resource` | 资源变化时通知客户端 | `true` | |
| 217 | +| `change-notification.prompt` | 提示词变化时通知客户端 | `true` | |
| 218 | +| `change-notification.tool` | 工具变化时通知客户端 | `true` | |
| 219 | +| `streamable.mcp-endpoint` | Streamable HTTP 的 MCP 路径 | `/mcp/message` | |
| 220 | +| `streamable.disallow-delete` | 拒绝针对会话的 HTTP DELETE 请求 | `false` | |
| 221 | +| `streamable.keep-alive-interval` | 保活间隔(毫秒) | `20000` | |
| 222 | +| `streamable.port` | STREAMABLE 模式使用的 HTTP 端口 | `8080` | |
| 223 | + |
| 224 | +条件要求: |
| 225 | + |
| 226 | +- 仅当 `capabilities.resource` 为 `true` 时,`capabilities.subscribe-resource` 才是必填项。 |
| 227 | +- 仅当 `mode` 为 `STREAMABLE` 时,`streamable.*` 字段才是必填项。`mode` 为 `STDIO` 时会忽略 `streamable` 部分,因此可以省略。 |
| 228 | + |
| 229 | +## 运行时模型与稳定性 |
| 230 | + |
| 231 | +### SYNC 与 ASYNC(`type`) |
| 232 | + |
| 233 | +`type` 属性用于选择 MCP Java SDK 的服务器 API(`SYNC` 或 `ASYNC`),它**不会**让注解方法变为响应式方法。 |
| 234 | + |
| 235 | +- **SYNC** — 方法在请求线程上运行。 |
| 236 | +- **ASYNC** — SDK 提供异步处理器,并通过 `Mono.fromCallable(...)` 包装你的方法。代码仍然是**阻塞式** Java;`@McpTool`、`@McpPrompt` 和 `@McpResource` 方法不能返回 `Mono`。 |
| 237 | + |
| 238 | +默认应使用 **SYNC**。仅当部署环境需要 MCP 异步服务器 API 时才选择 **ASYNC**。在 ASYNC 模式下,耗时任务仍会阻塞 Reactor 工作线程。 |
| 239 | + |
| 240 | +### 组件实例与并发 |
| 241 | + |
| 242 | +SDK 会为每个组件类创建**一个实例**(通过**公共无参构造方法**),并在所有请求之间复用。并发 MCP 调用会共享该对象,因此组件应保持**无状态**或保证**线程安全**,并避免使用未同步的实例字段保存请求级数据。 |
| 243 | + |
| 244 | +## 基于 Profile 的配置 |
| 245 | + |
| 246 | +在基础文件中设置 `profile`,即可从 classpath 加载 `mcp-server-{profile}.yml`。Profile 配置会通过 Jackson 深度合并到基础配置中,`capabilities` 和 `streamable` 等嵌套对象会逐字段合并。`profile` 名称始终取自基础文件。合并后,与最终 `mode` 不匹配的传输设置会被清除(例如 `mode` 为 `STDIO` 时会移除 `streamable`)。 |
| 247 | + |
| 248 | +可以针对不同环境使用 Profile: |
| 249 | + |
| 250 | +```yaml |
| 251 | +# mcp-server.yml(基础配置) |
| 252 | +enabled: true |
| 253 | +mode: STREAMABLE |
| 254 | +name: my-mcp-server |
| 255 | +version: 1.0.0 |
| 256 | +profile: dev |
| 257 | +``` |
| 258 | + |
| 259 | +```yaml |
| 260 | +# mcp-server-dev.yml(Profile 专用配置) |
| 261 | +streamable: |
| 262 | + port: 8080 |
| 263 | +``` |
| 264 | + |
| 265 | +## 项目结构 |
| 266 | + |
| 267 | +典型的项目结构如下: |
| 268 | + |
| 269 | +``` |
| 270 | +your-mcp-project/ |
| 271 | +├── pom.xml |
| 272 | +├── src/ |
| 273 | +│ ├── main/ |
| 274 | +│ │ ├── java/ |
| 275 | +│ │ │ └── com/ |
| 276 | +│ │ │ └── example/ |
| 277 | +│ │ │ ├── MyMcpServer.java # 主入口 |
| 278 | +│ │ │ ├── components/ |
| 279 | +│ │ │ │ ├── MyResources.java # MCP 资源 |
| 280 | +│ │ │ │ ├── MyTools.java # MCP 工具 |
| 281 | +│ │ │ │ └── MyPrompts.java # MCP 提示词 |
| 282 | +│ │ │ └── service/ |
| 283 | +│ │ │ └── BusinessLogic.java # 业务逻辑 |
| 284 | +│ │ └── resources/ |
| 285 | +│ │ └── mcp-server.yml # MCP 配置 |
| 286 | +│ └── test/ |
| 287 | +│ └── java/ |
| 288 | +│ └── com/ |
| 289 | +│ └── example/ |
| 290 | +│ └── McpServerTest.java # 单元测试 |
| 291 | +└── target/ |
| 292 | + └── *.jar # 构建产物(名称取决于项目) |
| 293 | +``` |
| 294 | +
|
| 295 | +## 后续步骤 |
| 296 | +
|
| 297 | +- 想进一步了解 MCP 组件?请参阅[核心组件](/mcp-annotated-java-sdk/zh-cn/reference/components/) |
0 commit comments