1. 从 HTTP 工具到本地资源:Agent 能力边界的一次扩展
上一篇我们用 McpManager 把 HTTP API 接进了 Agent,模型能自主查公网 IP、天气这类网络服务。但有一类能力是 HTTP API 覆盖不了的:读取服务器上的日志文件、查询本地 PostgreSQL 数据库、记住跨轮次的状态、操作 GitHub 仓库、执行浏览器自动化。这些能力对应的是 NPX MCP 服务器——由 MCP 官方或社区维护的独立进程,通过 npx 一行命令启动,暴露标准的 MCP 工具接口。
McpAgentExecutor 和 McpClient 的组合,就是让 Agent 直接操作文件系统和数据库的最小配置骨架。它适合已经配好 NPX MCP 服务器、希望 Agent 直接读写本地资源的 Java 开发者,也适合做本地开发与自动化场景的同学。我试过把 filesystem 和 postgres 两个服务器同时挂到一个 Agent 上,整个接入过程只改了一行 tools() 调用,其余 LLM、系统提示、maxIterations、回调全部不动。这篇文章会把 config.toml / settings.json 骨架、CC Switch 接入 TaoToken 统一 Key 通道、以及一次文件读写加数据库查询的验证动作完整走一遍。
2. 前置准备:TaoToken 统一 Key 与 API 通道
在写 Agent 代码之前,先把模型通道打通。TaoToken 提供统一的 Key 和 API 入口,Java 侧只需要一个 base_url 和一个 api_key,不用为每个模型单独维护一套凭证。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api 。
如果你用 Claude Code 或类似的编码工具,可以用 CC Switch 把 TaoToken 配成统一通道。CC Switch 的作用是管理多套 API 配置并快速切换,把 TaoToken 的 Key 填进去之后,所有走 Anthropic 协议的工具都能复用同一份凭证。对应的 deep link 是 ClaudeCodeAnthropic 配置页,进去之后填 base_url 和 api_key 即可。
拿 Key 的步骤很短:登录后进控制台,在 API Keys 页面创建一个新 Key,复制出来。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,API Keys 页面是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。创建时建议按用途命名,比如mcp-agent-local,方便后面审计。
注意:Key 只显示一次,复制后立刻存进环境变量或本地配置文件,不要硬编码进 Git 仓库。
模型选择上,示例用 qwen3.6-plus,temperature 设 0f,因为工具调用场景需要确定性输出,温度高了模型容易在参数拼装上发散。如果你要长期跑编码或 Agent 任务,可以看 Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,按额度套餐走比单次调用更划算。
3. 可复制配置:mcp.server.config.json 与 settings.json 骨架
McpClient 在 Spring 启动时会根据 mcp.server.config.json 里的别名拉起对应的 NPX 进程。先给一份最小可用的 filesystem 配置:
{ "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/tmp" ], "env": { "NODE_ENV": "production" } } } }这里的关键点是 args 最后那个路径/tmp,它是 filesystem 服务器的访问边界。服务器只会在这个目录内做 list_directory、read_file、write_file、create_directory 等操作,传系统根目录或含敏感文件的路径等于把整个磁盘交给模型,务必传受限路径。
接着加 postgres 服务器,和 filesystem 并列:
{ "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], "env": { "NODE_ENV": "production" } }, "postgres": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-postgres", "postgresql://readonly_user:password@localhost:5432/mydb" ], "env": {} } } }postgres 服务器的连接串里,账号建议只给 SELECT 权限。Agent 拿到 query、list_tables、describe_table 三个工具后,会自己拼 SQL 并执行,如果账号有 DROP 或 UPDATE 权限,一次误操作就可能改数据。只读账号是最低成本的保险。
如果你用 CC Switch 管理配置,settings.json 里对应的是通道切换部分,把 TaoToken 的 base_url 和 api_key 写进去:
{ "provider": "taotoken", "baseUrl": "https://taotoken.net/api", "apiKey": "${TAOTOKEN_API_KEY}", "model": "qwen3.6-plus" }apiKey 用环境变量占位,实际运行时由 shell 注入。这样配置文件可以进版本库,Key 不会泄露。
4. 可复制代码:McpAgentExecutor 挂载 McpClient
配置就绪后,Agent 层的代码和上一篇几乎一样,唯一区别是 tools() 的第一个参数从 mcpManager 换成 mcpClient,第二个参数从 "default" 换成服务器别名。
@Test public void mcpClientAgent() { McpAgentExecutor agent = McpAgentExecutor.builder(chainActor) .llm(ChatAliyun.builder() .model("qwen3.6-plus") .temperature(0f) .build()) .tools(mcpClient, "filesystem") // 加载 filesystem 服务器的全部工具 .systemPrompt(""" 你是一个文件管理助手,可以浏览和读取 /tmp 目录中的文件。 请直接执行操作,不要询问用户额外确认。 """) .maxIterations(5) .onToolCall(tc -> System.out.println(">> Tool call: " + tc)) .onObservation(obs -> System.out.println(">> Observation: " + obs)) .build(); ChatGeneration result = agent.invoke("列出 /tmp 目录下的所有文件,并告诉我有多少个文件"); System.out.println("\n=== 最终答案 ==="); System.out.println(result.getText()); }McpClient 在启动时按别名 filesystem 拉起进程,服务器启动后向外暴露一组标准工具:list_directory、read_file、write_file、create_directory 等。McpAgentExecutor 拿到这份工具列表后,转成 Function Calling Schema 注册给模型,后续的工具选择和调用由模型自主完成。maxIterations 设 5 是给多步任务留余量,比如"读 config.yaml 并告诉我数据库地址"会触发 list_directory 加 read_file 两次调用。
换成 postgres 服务器时,只改一行:
McpAgentExecutor agent = McpAgentExecutor.builder(chainActor) .llm(ChatAliyun.builder().model("qwen3.6-plus").temperature(0f).build()) .tools(mcpClient, "postgres") // 换成 postgres 服务器 .systemPrompt("你是一个数据库助手,可以查询数据库中的表结构和数据。") .maxIterations(5) .build(); ChatGeneration result = agent.invoke("查询 orders 表中最近 5 条记录");模型会根据问题自动选择 list_tables 或 describe_table 先探结构,再拼 query 执行 SQL,返回结果。整个过程不需要写任何 JDBC 代码。
5. 验证请求:一次文件读写加数据库查询
先验证文件系统。在 /tmp 下放几个测试文件:
echo "db_host=localhost" > /tmp/config.yaml echo "2024-01-01 INFO started" > /tmp/demo.log echo "ticket-001" > /tmp/ticket_result.txt然后跑 mcpClientAgent(),控制台输出类似:
>> Tool call: list_directory -> {"path": "/tmp"} >> Observation: {"files": ["ticket_result.txt", "demo.log", "config.yaml"]} === 最终答案 === /tmp 目录下共有 3 个文件,包括 ticket_result.txt、demo.log、config.yaml。模型拿到 list_directory 的返回值后,直接统计文件数量并输出结论,整个过程只需一次工具调用。如果任务更复杂,比如"读取 config.yaml 并告诉我其中的数据库地址",模型会自动追加一次 read_file 调用,不需要任何额外代码。
再验证数据库。假设本地 PostgreSQL 有 orders 表,跑 postgres 版本的 Agent,输出类似:
>> Tool call: list_tables -> {} >> Observation: {"tables": ["orders", "users", "products"]} >> Tool call: query -> {"sql": "SELECT * FROM orders ORDER BY created_at DESC LIMIT 5"} >> Observation: {"rows": [{"id": 1024, "amount": 299.00, ...}]} === 最终答案 === orders 表最近 5 条记录如下:...模型先探表结构,再拼 SQL,最后把结果整理成自然语言。整个链路里,McpClient 负责进程管理和工具发现,McpAgentExecutor 负责把工具注册给模型并驱动多轮调用。
6. 本篇常见错排查
npx 找不到或 Node.js 未安装。filesystem 和 postgres 服务器都通过 npx 启动,本地必须有 Node.js。报错通常是npx: command not found或进程启动后立即退出。先跑node -v和npx -v确认,再检查 mcp.server.config.json 里 command 字段是否写成了绝对路径。
filesystem 报路径越界。如果模型尝试访问 /tmp 之外的文件,服务器会返回权限错误。这是预期行为,不是 bug。检查 args 里的路径参数,确认它就是你希望 Agent 能碰的目录。不要把/或/home传进去。
postgres 连接串格式错误。连接串必须是postgresql://user:password@host:port/dbname的完整格式,缺一段都会导致服务器启动失败。密码里如果有特殊字符,需要 URL 编码。另外确认数据库允许本地连接,pg_hba.conf 里的认证方式要匹配。
工具调用返回空或模型不调用工具。先看 onToolCall 回调有没有打印。如果没有,说明模型没触发 Function Calling,可能是 systemPrompt 太模糊,或者 temperature 设得太高。把 temperature 降到 0f,systemPrompt 里明确写"请直接执行操作"。
maxIterations 太小导致任务中断。默认值如果设成 1,多步任务会在第一次工具调用后就停。文件读取加数据库查询这类任务,建议至少设 5。如果任务特别复杂,可以调到 10,但要配合 onToolCall 日志观察是否有死循环。
Key 或 base_url 配错导致 401。检查环境变量 TAOTOKEN_API_KEY 是否注入成功,base_url 是否写成https://taotoken.net/api。如果用的是 CC Switch,确认当前激活的 provider 是 taotoken。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的参数说明。
7. 继续往下走:统一通道与工具扩展
McpAgentExecutor 加 McpClient 的接入方式和上一篇的 McpManager 版本几乎完全一样,学习成本接近零。区别只在于工具来源:HTTP API 用 McpManager,NPX 服务器用 McpClient。切换工具来源时,Agent 层的代码只改一行,这意味着你可以先用 McpManager 接 HTTP 工具快速验证业务逻辑,确认效果后再把部分工具替换成更稳定的 NPX 服务器,整个迁移成本极低。
如果你需要同时使用 HTTP 工具和 NPX 服务器,可以把两者合并到同一个 Agent 中,McpAgentExecutor 支持多工具源注册。模型通道方面,所有模型调用都走 TaoToken 统一 Key,不用为每个模型单独维护凭证。想直接验证模型对话效果,可以进模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 试一轮;长期跑编码或 Agent 任务,走 Coding Plan 更省心。生产环境记得把 onToolCall 和 onObservation 两个回调接入日志系统,完整记录每次文件读写或 SQL 执行,满足合规审计要求。