承上:上一篇我们把自己的工具升级成了MCP协议,三种传输方式都玩了一遍。但你有没有想过------连我们自己都知道把工具做成MCP Server,GitHub、文件系统、数据库这些常用工具,社区是不是早就有人写好了?今天,我们不写一行工具代码,直接接入开源MCP Server,让AI读文件、查数据库、操作GitHub。
1. 核心认知:MCP最大的价值不是"写",是"接"
回顾一下我们上篇做的事情:
arduino
自己写WeatherService → 用@Tool暴露 → 启动MCP Server → Client连接
这条路走通了,但换个角度想:
如果你的AI需要读本地文件,你要自己写文件读取逻辑吗?
如果你的AI需要查数据库,你要自己写JDBC查询吗?
如果你的AI需要操作GitHub Issue,你要自己调GitHub API吗?
不用。社区已经有现成的MCP Server了。
这就是MCP协议真正的威力------不是让你多写代码,而是让你少写代码。
2. 社区MCP Server生态一览
在动手之前,先看看社区有哪些"轮子"可以直接用:
| MCP Server | 功能 | 官方/社区 |
|---|---|---|
| @anthropic/mcp-server-filesystem | 文件系统读写 | 官方 |
| @anthropic/mcp-server-github | GitHub Issue/PR/仓库操作 | 官方 |
| @anthropic/mcp-server-postgres | PostgreSQL数据库查询 | 官方 |
| @anthropic/mcp-server-sqlite | SQLite数据库查询 | 官方 |
| @anthropic/mcp-server-puppeteer | 浏览器自动化 | 官方 |
| @anthropic/mcp-server-brave-search | Brave搜索引擎 | 官方 |
| 社区还有更多... | Slack、Jira、Notion等 | 社区 |
这些Server大部分是Node.js/Python写的。你不需要懂这些语言,只需要知道怎么启动它们,然后用Spring AI的MCP Client去连接。
3. 实战一:接入文件系统MCP Server
3.1 场景
让AI能读取你本地的代码、文档、配置文件,然后回答诸如:
- "我这个项目的pom.xml里用了哪些依赖?"
- "帮我找一下项目里所有用了@Tool注解的类"
- "application.yml里配置了多少个MCP连接?"
3.2 安装文件系统MCP Server
bash
# 需要Node.js环境
npm install -g @modelcontextprotocol/server-filesystem
3.3 Spring AI Client配置
yaml
spring:
ai:
mcp:
client:
stdio:
servers-configuration: classpath:mcp-servers-config.json
classpath:mcp-servers-config.json
perl
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/yunxi/IdeaProjects/yunxi-spring-ai/chapter-07-mcp-client"]
}
}
}
注意 :路径必须是绝对路径。~ 波浪号可能不被识别,建议写完整路径。
3.4 Client代码
kotlin
package com.yunxi.ai.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class ChatService {
private final ChatClient chatClient;
public ChatService(ChatClient.Builder builder, ToolCallbackProvider tools) {
this.chatClient = builder
.defaultToolCallbacks(tools)
.build();
}
public String chat(String message) {
return chatClient.prompt()
.system("你是一个专业的助手,可以读取文件系统")
.user(message)
.call()
.content();
}
}
3.5 测试


ini
2026-07-25T19:57:28.338+08:00 DEBUG 62860 --- [nio-9999-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.yunxi.ai.controller.ChatController#chat(String)
2026-07-25T19:57:28.356+08:00 DEBUG 62860 --- [nio-9999-exec-3] o.s.web.client.DefaultRestClient : Writing [ChatCompletionRequest[messages=[ChatCompletionMessage[rawContent=你是一个专业的助手,可以读取文件系统, role=SYSTEM, name=null, toolCallId=null, toolCalls=null, refusal=null, audioOutput=null, annotations=null, reasoningContent=null], ChatCompletionMessage[rawContent=写一个测试文件hello world, role=USER, name=null, toolCallId=null, toolCalls=null, refusal=null, audioOutput=null, annotations=null, reasoningContent=null]], model=deepseek-v4-pro, store=null, metadata=null, frequencyPenalty=null, logitBias=null, logprobs=null, topLogprobs=null, maxTokens=null, maxCompletionTokens=null, n=null, outputModalities=null, audioParameters=null, presencePenalty=null, responseFormat=null, seed=null, serviceTier=null, stop=null, stream=false, streamOptions=null, temperature=0.7, topP=null, tools=[org.springframework.ai.openai.api.OpenAiApi$FunctionTool@7fd90769, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@26a98958, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@1a88f6c0, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@357a5804, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@22ff38d, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@68a649d9, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@35459cfb, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@5218582f, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@2f566a7a, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@756406c8, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@6cdffedb, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@7c161a4f, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@608fba0d, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@3508a292, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@2632bbce], toolChoice=null, parallelToolCalls=null, user=null, reasoningEffort=null, webSearchOptions=null, verbosity=null, promptCacheKey=null, safetyIdentifier=null, extraBody={}]] as "application/json" with org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
2026-07-25T19:57:30.569+08:00 DEBUG 62860 --- [nio-9999-exec-3] o.s.web.client.DefaultRestClient : Reading to [org.springframework.ai.openai.api.OpenAiApi$ChatCompletion]
2026-07-25T19:57:30.571+08:00 DEBUG 62860 --- [nio-9999-exec-3] o.s.a.m.tool.DefaultToolCallingManager : Executing tool call: write_file
2026-07-25T19:57:30.588+08:00 DEBUG 62860 --- [nio-9999-exec-3] o.s.web.client.DefaultRestClient : Writing [ChatCompletionRequest[messages=[ChatCompletionMessage[rawContent=你是一个专业的助手,可以读取文件系统, role=SYSTEM, name=null, toolCallId=null, toolCalls=null, refusal=null, audioOutput=null, annotations=null, reasoningContent=null], ChatCompletionMessage[rawContent=写一个测试文件hello world, role=USER, name=null, toolCallId=null, toolCalls=null, refusal=null, audioOutput=null, annotations=null, reasoningContent=null], ChatCompletionMessage[rawContent=我来创建一个 `hello_world` 测试文件。, role=ASSISTANT, name=null, toolCallId=null, toolCalls=[ToolCall[index=null, id=call_00_VAuDtPo7fV0dndXT4LsR0717, type=function, function=ChatCompletionFunction[name=write_file, arguments={"path": "hello_world.txt", "content": "Hello World!\n这是一个测试文件。\n"}]]], refusal=null, audioOutput=null, annotations=null, reasoningContent=null], ChatCompletionMessage[rawContent=[{"text":"Successfully wrote to hello_world.txt"}], role=TOOL, name=write_file, toolCallId=call_00_VAuDtPo7fV0dndXT4LsR0717, toolCalls=null, refusal=null, audioOutput=null, annotations=null, reasoningContent=null]], model=deepseek-v4-pro, store=null, metadata=null, frequencyPenalty=null, logitBias=null, logprobs=null, topLogprobs=null, maxTokens=null, maxCompletionTokens=null, n=null, outputModalities=null, audioParameters=null, presencePenalty=null, responseFormat=null, seed=null, serviceTier=null, stop=null, stream=false, streamOptions=null, temperature=0.7, topP=null, tools=[org.springframework.ai.openai.api.OpenAiApi$FunctionTool@6aba0925, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@77f899ad, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@714a9f2, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@34cb8e15, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@e025a88, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@3d049f14, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@1ffdfa7f, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@772e74b3, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@6be4a6c4, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@72017893, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@25f4aae, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@6503dba, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@484c6932, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@31705548, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@490263f2], toolChoice=null, parallelToolCalls=null, user=null, reasoningEffort=null, webSearchOptions=null, verbosity=null, promptCacheKey=null, safetyIdentifier=null, extraBody={}]] as "application/json" with org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
2026-07-25T19:57:32.141+08:00 DEBUG 62860 --- [nio-9999-exec-3] o.s.web.client.DefaultRestClient : Reading to [org.springframework.ai.openai.api.OpenAiApi$ChatCompletion]
2026-07-25T19:57:32.143+08:00 DEBUG 62860 --- [nio-9999-exec-3] o.s.a.m.tool.DefaultToolCallingManager : Executing tool call: read_text_file
2026-07-25T19:57:32.169+08:00 DEBUG 62860 --- [nio-9999-exec-3] o.s.web.client.DefaultRestClient : Writing [ChatCompletionRequest[messages=[ChatCompletionMessage[rawContent=你是一个专业的助手,可以读取文件系统, role=SYSTEM, name=null, toolCallId=null, toolCalls=null, refusal=null, audioOutput=null, annotations=null, reasoningContent=null], ChatCompletionMessage[rawContent=写一个测试文件hello world, role=USER, name=null, toolCallId=null, toolCalls=null, refusal=null, audioOutput=null, annotations=null, reasoningContent=null], ChatCompletionMessage[rawContent=我来创建一个 `hello_world` 测试文件。, role=ASSISTANT, name=null, toolCallId=null, toolCalls=[ToolCall[index=null, id=call_00_VAuDtPo7fV0dndXT4LsR0717, type=function, function=ChatCompletionFunction[name=write_file, arguments={"path": "hello_world.txt", "content": "Hello World!\n这是一个测试文件。\n"}]]], refusal=null, audioOutput=null, annotations=null, reasoningContent=null], ChatCompletionMessage[rawContent=[{"text":"Successfully wrote to hello_world.txt"}], role=TOOL, name=write_file, toolCallId=call_00_VAuDtPo7fV0dndXT4LsR0717, toolCalls=null, refusal=null, audioOutput=null, annotations=null, reasoningContent=null], ChatCompletionMessage[rawContent=文件已成功创建!让我确认一下内容:, role=ASSISTANT, name=null, toolCallId=null, toolCalls=[ToolCall[index=null, id=call_00_WewfrWtoSgjnOiJDT5Op7208, type=function, function=ChatCompletionFunction[name=read_text_file, arguments={"path": "hello_world.txt"}]]], refusal=null, audioOutput=null, annotations=null, reasoningContent=null], ChatCompletionMessage[rawContent=[{"text":"Hello World!\n这是一个测试文件。\n"}], role=TOOL, name=read_text_file, toolCallId=call_00_WewfrWtoSgjnOiJDT5Op7208, toolCalls=null, refusal=null, audioOutput=null, annotations=null, reasoningContent=null]], model=deepseek-v4-pro, store=null, metadata=null, frequencyPenalty=null, logitBias=null, logprobs=null, topLogprobs=null, maxTokens=null, maxCompletionTokens=null, n=null, outputModalities=null, audioParameters=null, presencePenalty=null, responseFormat=null, seed=null, serviceTier=null, stop=null, stream=false, streamOptions=null, temperature=0.7, topP=null, tools=[org.springframework.ai.openai.api.OpenAiApi$FunctionTool@66132625, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@3b5dc607, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@69243810, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@4aa1bd72, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@357c880d, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@60441d5b, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@28684dfb, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@6fe53fa9, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@3e08eeb8, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@7ee692e7, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@140b1f55, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@5255720a, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@2bd12bd6, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@616ae5e1, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@2dd7af2e], toolChoice=null, parallelToolCalls=null, user=null, reasoningEffort=null, webSearchOptions=null, verbosity=null, promptCacheKey=null, safetyIdentifier=null, extraBody={}]] as "application/json" with org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
2026-07-25T19:57:33.410+08:00 DEBUG 62860 --- [nio-9999-exec-3] o.s.web.client.DefaultRestClient : Reading to [org.springframework.ai.openai.api.OpenAiApi$ChatCompletion]
2026-07-25T19:57:33.415+08:00 DEBUG 62860 --- [nio-9999-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Using 'text/plain', given [*/*] and supported [text/plain, */*, application/json, application/*+json, application/yaml]
2026-07-25T19:57:33.416+08:00 DEBUG 62860 --- [nio-9999-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Writing ["✅ 测试文件 `hello_world.txt` 已创建完成,内容如下:<EOL><EOL>```<EOL>Hello World!<EOL>这是一个测试文件。<EOL>```"]
2026-07-25T19:57:33.419+08:00 DEBUG 62860 --- [nio-9999-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK 画一下这个交互

你一行文件读取代码都没写。 这就是白嫖的快乐。
4. 实战二:接入Gitee MCP Server
4.1 场景
让AI能操作你的Gitee仓库:
- "帮我查一下最近一周的Issue"
- "创建一个PR,标题是'修复订单查询Bug'"
- "看看最近的commit记录"
4.2 安装Gitee MCP Server
css
npm install -g @gitee/mcp-gitee@latest
4.3 创建Gitee Personal Access Token
- 打开 gitee.com/profile/per...
- 点击 "生成新令牌"
- 勾选权限:
user_info projects pull_requests issues notes keys hook groups gists enterprises emails - 生成后复制Token(只显示一次)
4.4 Spring AI Client配置
perl
{
"mcpServers": {
"gitee": {
"command": "npx",
"args": [ "-y", "@gitee/mcp-gitee@latest" ],
"env": {
"GITEE_API_BASE": "https://gitee.com/api/v5",
"GITEE_ACCESS_TOKEN": "e474ac8baf0******a5260decebab4"
}
}
}
}
4.5 测试



5. 实战三:同时接入多个MCP Server
把文件系统和Gitee一起接进来:


跨系统协作,只靠一个 McpClient 。
6. 如何发现更多社区MCP Server?
| 资源 | 地址 | 说明 |
|---|---|---|
| MCP官方仓库 | github.com/modelcontex... | 官方维护的Server列表 |
| npm搜索 | npm search mcp-server |
Node.js生态 |
| GitHub搜索 | mcp-server topic |
社区开源项目 |
| Spring AI文档 | docs.spring.io/spring-ai/r... | Java生态 |
| MCP广场 | www.modelscope.cn/mcp | 魔塔社区 |
7. 避坑指南
坑1:Node.js环境没装
大部分社区MCP Server是Node.js写的,需要先装Node.js
bash
node -v # 确认版本 >= 18
npm -v
坑2:文件系统MCP Server权限太大
mcp-server-filesystem 默认能读传入目录下的所有文件,包括 .env、.git/config 等敏感文件。
建议:创建一个专门给AI访问的目录,不要直接给项目根目录。
坑3:STDIO方式的日志看不到
STDIO方式如果Server启动失败,日志不会直接输出到控制台。排查时可以先手动运行命令确认Server能启动。
坑4:版本兼容问题
社区MCP Server更新频繁,和Spring AI的MCP Client可能有兼容问题。优先使用官方维护的 @anthropic/ 开头的Server。
八、本篇小结
这一篇我们没有写一行工具代码,全靠白嫖社区生态:
| 接入的Server | 能力 |
|---|---|
| 文件系统 | 读代码、读配置、读文档 |
| Gitee | Issue、PR、Commit操作 |
| (未来更多) | 数据库、Slack、Jira... |
核心心法:
arduino
第一阶段:自己写@Tool → 知道原理
第二阶段:升级成MCP Server → 标准化
第三阶段:接入社区MCP Server → 白嫖 ← 你现在在这里
MCP协议真正的价值,不在于你写了多少Server,而在于你能接入多少现成的生态。
MCP协议真正的价值,不在于你写了多少Server,而在于你能接入多少现成的生态。
现在我们的AI工具能力已经拉满了------能调接口、能读文件、能操作GitHub。但每次对话都像在跟一个失忆的人聊天,上一句刚说完我叫张三,下一句它就问"您好,请问怎么称呼?"
下一篇,我们要解决这个问题------给AI装上"海马体",让它记住你们聊过什么。
本文与DeepSeek协作完成