1. 为什么要把 Swagger 变成 MCP 工具
如果你正在用 Spring Boot 写后端,同时又在折腾 Spring AI 或者别的 Agent 框架,大概率会遇到一个很别扭的问题:模型明明能理解用户想干什么,但一到真正调用你的业务接口就开始胡来。它要么在对话里硬拼 curl 命令,要么把参数名猜错,要么把 GET 请求写成 POST。你辛辛苦苦写的 Swagger 文档,模型根本不会主动去读。
Swagger MCP 要解决的就是这件事。它把 OpenAPI/Swagger 3.x 文档自动翻译成 MCP 协议里的 tools,让模型通过标准的 tools/list 和 tools/call 来发现和调用接口,而不是靠自然语言瞎猜。说白了,就是给你的后端接口套一层模型能稳定识别的外壳。
这套东西适合谁?我总结下来是三类人:第一类是做 AI Agent 落地、需要让模型调用内部业务系统的后端工程师;第二类是希望把鉴权、超时、限流、审计这些治理能力集中到一个中间层,而不是散落在每个接口里的架构同学;第三类是想让模型先读 tool schema 再自动生成前端页面或调用链的全栈开发者。
核心思路其实不复杂:OpenAPI 文档里已经写清楚了每个接口的路径、方法、参数、请求体和响应结构,这些信息足够生成一个结构化的工具描述。MCP 协议又刚好提供了 tools 的标准交互方式。把两者对接起来,模型就能像调用本地函数一样调用你的 HTTP 接口,而且参数校验、类型转换、错误处理都能在中间层统一做掉。
我试过直接在提示词里塞 Swagger JSON 让模型自己解析,结果上下文爆炸不说,模型还经常漏掉嵌套的 schema。后来改成 MCP 工具模式,模型只需要知道有哪些工具、每个工具要什么参数,调用成功率明显上来了。这也是为什么我建议你优先把接口能力变成 MCP 工具,再谈提示词优化和前端自动化。
下面我会按实际落地路径来讲:先讲清楚整体结构和启动入口,再给出可复制的配置片段,然后是 OpenAPI 解析和工具注册的关键代码,接着是本地验证的完整命令,最后把常见的报错和排查思路过一遍。中间会穿插怎么用 TaoToken 统一 Key 和 API 通道来完成联调,避免你在多个模型供应商之间来回切换配置。
2. TaoToken 前置准备与统一通道配置
在开始写 MCP 服务之前,先把模型调用这条链路理顺。因为 Swagger MCP 本身只负责把接口暴露成工具,真正决定模型能不能稳定调用、调用结果怎么校验的,还是背后的模型通道。如果你同时用 OpenAI、Claude、通义千问好几个供应商,每个都要单独配 Key、单独改 Base URL,联调的时候会非常痛苦。
TaoToken 在这里的作用是提供一个统一的 API 入口。你只需要申请一个 Key,然后在代码或配置里把 Base URL 指向https://taotoken.net/api,就能通过同一个通道调用不同厂商的模型。对于 Swagger MCP 这种需要反复验证工具调用结果的场景,统一通道能省掉大量切换成本。
具体操作上,先去官网https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=注册账号,然后在控制台里创建 API Key。控制台地址是https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite,进去之后找到 API Keys 页面,新建一个 Key 并复制出来。这个 Key 后面会用在 Spring AI 的配置里。
如果你用的是 Claude Code 或者类似的编码 Agent,TaoToken 也提供了对应的接入方式,文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite。Coding Plan 适合长期做编码和 Agent 开发的场景,入口在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite。模型对话的调试入口在https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite,你可以先用它验证 Key 是否可用。
这里要强调一点:TaoToken 是统一的 API 通道,不是让你去搞什么灰色中转。它的定位是帮你把多个模型的调用收敛到一个入口,方便管理和计费。你在 Spring AI 里配置的时候,只需要把 base-url 指向https://taotoken.net/api,api-key 填你申请到的 Key,model 填你要用的模型 ID 就行。
配置片段大概长这样,放在application.yml里:
spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.2注意这里的base-url不要加 UTM 参数,API 调用地址就是干净的https://taotoken.net/api。Key 建议用环境变量注入,不要硬编码在配置文件里。如果你在本地测试,可以先export TAOTOKEN_API_KEY=你的Key,Windows 下用set TAOTOKEN_API_KEY=你的Key。
配好之后,你可以先用一个最简单的对话请求验证通道是否通。比如用 curl 发一个 chat completions 请求:
curl -X POST "https://taotoken.net/api/v1/chat/completions" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "ping"}] }'如果返回正常的 JSON 响应,说明 Key 和通道都没问题。这一步很关键,因为后面 Swagger MCP 联调的时候,如果工具调用失败,你需要先排除是模型通道的问题还是 MCP 服务的问题。先把通道验证通过,能省掉很多扯皮时间。
3. 可复制的 MCP 服务配置与 OpenAPI 解析
这一节是核心,我会给出可以直接复制的配置片段和关键代码结构。整个模块的路径是langchain4j-spring-ai-swagger-mcp,启动入口是SwaggerMcpServerApplication.java,它作为一个独立的 Spring Boot 服务运行,对外暴露 MCP server,默认走 SSE 传输。
先看application.yml的完整配置。这段配置决定了 MCP 服务监听哪个端口、加载哪些 OpenAPI 文档、用什么工具模式、超时和请求体限制是多少:
server: port: 3000 transport: mode: sse swagger: mcp: services: - serviceKey: local openapiUrl: http://127.0.0.1:9010/v3/api-docs/default baseUrl: http://127.0.0.1:9010 toolMode: perOperation timeoutMs: 8000 maxBodyBytes: 1048576 auth: type: none这里几个参数需要解释一下。serviceKey是你给这个 OpenAPI 服务起的别名,后面调用工具时会用到。openapiUrl指向你的 Swagger 文档地址,Spring Boot 项目默认一般是/v3/api-docs或者带分组路径。baseUrl是实际接口的根地址,因为 OpenAPI 文档里的 servers 字段有时候不准,显式指定更稳妥。toolMode有两个值:perOperation表示每个接口生成一个独立工具,callOnly表示只暴露一个通用 call 工具。timeoutMs和maxBodyBytes是安全边界,防止某个接口卡死或者返回超大响应把上下文撑爆。
如果你需要鉴权,auth部分可以改成 bearer、apiKey 或 basic。比如:
auth: type: bearer token: ${DOWNSTREAM_TOKEN}接下来是 OpenAPI 解析的核心组件。OpenApiLoader负责加载和缓存文档,支持 URL 和本地文件两种方式。它会缓存 OpenAPI 实例和原始 JSON Map,避免每次调用都重新拉取。OpenApiNormalizer负责规整接口结构,自动补齐缺失的 operationId,提取参数、requestBody 和 response schema。ToolSpecFactory生成工具规范,工具名格式是svc.{serviceKey}.{operationId},把参数和 body 映射成 tool input schema。OpenApiInvoker负责实际执行调用,按 operationId 和 args 拼出真实 HTTP 请求,支持 body 和 header 参数,并带上超时和响应体限制。
MCP 工具入口是SwaggerCallTool,暴露的工具包括:listApis列出已加载的 OpenAPI 服务,listOperations分页返回 operationId 列表,call是通用调用入口,另外还有getComponents、getInfo、getServers、getPaths这些自省工具。自省工具在调试阶段特别有用,你可以让模型先列出有哪些接口,再决定调哪个。
Spring 配置入口是SwaggerMcpServerConfig,它支持 SSE 或 stdio 两种传输模式,自动把工具注册到 MCP server,并提供请求日志过滤。启动命令在 Windows CMD 下是这样:
cd /d D:\workspace\langchain4j-spring-agent\langchain4j-spring-ai mvn -pl langchain4j-spring-ai-swagger-mcp -am spring-boot:run如果你在 macOS 或 Linux 下,把cd /d换成普通的cd就行。启动之后,服务会监听 3000 端口,SSE 端点是/sse,消息端点是/mcp/message。
这里有个设计要点值得单独说:工具模式的选择。perOperation模式下,每个接口都是一个独立工具,模型在 tools/list 里能看到所有接口的 schema,调用时直接选对应工具,参数校验更严格。缺点是接口多的时候工具列表会很长,可能超出模型的上下文窗口。callOnly模式只暴露一个call工具,模型需要先知道 operationId 再传参,灵活但容易传错。我的建议是接口数量在 50 个以内用perOperation,超过 50 个考虑callOnly配合listOperations分页查询。
另外,鉴权集中化是这套方案的一大优势。你可以在 MCP 层统一注入 bearer token 或 apiKey,下游业务接口不需要关心调用方是谁。安全边界也可以在这里做,比如限制只能拉取特定域名的 OpenAPI 文档,或者对下游调用做白名单。缓存和热更新方面,reload可以强制刷新 OpenAPI,适合接口频繁变动的开发阶段。
4. 本地验证请求与成功结果
配置和代码都就位之后,最重要的一步是本地验证。很多人卡在这里,因为 MCP 的 SSE 交互方式和普通 REST 不太一样,需要先建立会话再发消息。下面我按顺序给出完整命令,你可以直接复制到终端里跑。
第一步,建立 SSE 会话。用 curl 的-N参数保持连接:
curl.exe -N "http://127.0.0.1:3000/sse"执行之后,终端会挂起并持续输出事件。你会看到类似event: endpoint和data: /mcp/message?sessionId=xxxx的内容。把那个 sessionId 记下来,后面所有请求都要带上它。注意这个连接不能关,关了 session 就失效了。
第二步,列出所有可用工具。新开一个终端窗口:
curl.exe -X POST "http://127.0.0.1:3000/mcp/message?sessionId=你的sessionId" ^ -H "Content-Type: application/json" ^ -d "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"method\":\"tools/list\",\"params\":{}}"如果返回的 JSON 里result.tools数组包含listApis、listOperations、call等工具,说明 MCP 服务注册成功。如果数组为空,先检查服务是否真的启动了,端口是不是 3000,以及 sessionId 有没有拼错。
第三步,获取 operationId 列表。这一步是确认 OpenAPI 文档有没有被正确解析:
curl.exe -X POST "http://127.0.0.1:3000/mcp/message?sessionId=你的sessionId" ^ -H "Content-Type: application/json" ^ -d "{\"jsonrpc\":\"2.0\",\"id\":\"2\",\"method\":\"tools/call\",\"params\":{\"name\":\"listOperations\",\"arguments\":{\"serviceKey\":\"local\"}}}"返回结果里应该能看到你 Spring Boot 项目里所有接口的 operationId。如果这里是空的,说明 OpenAPI 解析失败或者地址不可达,去检查openapiUrl能不能在浏览器里直接打开。
第四步,实际调用一个接口。假设你有一个getUserById接口,参数是id:
curl.exe -X POST "http://127.0.0.1:3000/mcp/message?sessionId=你的sessionId" ^ -H "Content-Type: application/json" ^ -d "{\"jsonrpc\":\"2.0\",\"id\":\"3\",\"method\":\"tools/call\",\"params\":{\"name\":\"call\",\"arguments\":{\"serviceKey\":\"local\",\"operationId\":\"getUserById\",\"args\":{\"id\":\"123\"}}}}"如果一切正常,返回的result.content里会包含下游接口的真实响应。到这里,整个调用闭环就打通了:模型通过 tools/list 发现工具,通过 tools/call 触发调用,MCP 服务解析参数、拼装 HTTP 请求、执行调用、返回结果。
验证成功之后,你可以把 TaoToken 的模型接进来做端到端测试。在 Spring AI 的 ChatClient 里注册这些 MCP 工具,然后发一句「帮我查一下 id 为 123 的用户信息」,看模型能不能正确选择call工具并传对参数。这一步能跑通,说明你的 Swagger MCP 已经可以进入实际使用了。
5. 常见报错与排查思路
这一节我把实际踩过的坑和对应的排查方法列出来,基本都是真实报错,你对照着看能快速定位问题。
tools/list 返回空数组。最常见的原因是 MCP 服务没启动,或者端口不对。先确认SwaggerMcpServerApplication有没有正常启动,日志里有没有Started SwaggerMcpServerApplication。然后检查server.port是不是 3000,curl 请求的地址和端口是否一致。还有一种情况是 SSE 会话没建立成功,sessionId 是空的或者过期的,重新跑一遍curl -N拿新的 sessionId。
listOperations 返回空。这说明 OpenAPI 文档没加载成功。先检查openapiUrl能不能在浏览器里直接访问,如果返回 401 或 403,说明文档本身需要鉴权,需要在OpenApiLoader里配置拉取文档的 headers。如果返回 404,检查路径是不是/v3/api-docs/default,有些项目分组路径不一样。还有一种情况是 OpenAPI 版本不是 3.x,Swagger 2.0 的文档结构不同,需要先转换。
call 报 operation not found。这个错误说明 operationId 不存在或者没被加载。先用listOperations确认一下这个 operationId 在不在列表里。如果不在,可能是 OpenAPI 文档里这个接口没有 operationId,OpenApiNormalizer虽然会自动补齐,但补齐的规则可能和你预期的不一样。你可以手动在 Swagger 注解里指定 operationId,比如@Operation(operationId = "getUserById")。
401 Unauthorized 或 local proxy failed。如果报 401,先检查 TaoToken 的 Key 有没有配错,环境变量有没有生效。如果报 local proxy failed 或者连接被拒绝,检查baseUrl指向的下游服务是不是真的在运行,端口对不对。有时候下游服务只监听 127.0.0.1,而 MCP 服务在容器里跑,网络不通也会报这个错。
reading choices 相关报错。这个通常出现在模型调用环节,说明 TaoToken 返回的响应结构和你代码里解析的字段对不上。检查一下你用的模型 ID 是否正确,有些模型返回的字段名不一样。另外确认base-url是https://taotoken.net/api,不要多加/v1或者别的路径,具体路径由 SDK 自己拼。
OAuth 相关报错。如果你在下游接口用了 OAuth2,MCP 层需要配置对应的 token 获取逻辑。auth.type设成 bearer 只是静态 token,动态获取需要自己扩展OpenApiInvoker。建议先把下游鉴权简化成 apiKey 或 bearer,跑通之后再上 OAuth。
工具调用参数校验失败。模型传的参数类型和 schema 对不上,比如 schema 要求 integer,模型传了 string。这种情况可以在ToolSpecFactory里加一层类型转换,或者在提示词里明确参数类型。perOperation模式下 schema 更严格,模型一般不会传错;callOnly模式下需要模型自己理解 args 结构,容易出错。
排查的时候有个通用思路:先确认 MCP 服务本身没问题(tools/list 有返回),再确认 OpenAPI 解析没问题(listOperations 有返回),最后确认下游调用没问题(call 能返回真实数据)。一层一层往下查,比一上来就怀疑模型要高效得多。
6. 接入后的联调与持续使用建议
工具跑通之后,接下来就是怎么在实际项目里持续用起来。我的建议是先把接口能力变成 MCP 工具,再谈提示词和前端自动化。因为工具层稳定了,模型的行为才可预测。
联调阶段,你可以用 TaoToken 的模型对话入口https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite先手动测试几个典型场景,比如查询、创建、更新,看看模型选工具和传参的准确率。如果发现某个接口经常被选错,回去检查 operationId 和 description 是不是写得太模糊。Swagger 注解里的summary和description会直接影响模型的理解,值得花时间打磨。
长期使用的话,建议把 MCP 服务做成独立部署,和业务服务解耦。这样接口变动的时候只需要 reload OpenAPI,不用重启整个业务系统。鉴权、限流、审计这些治理能力集中在 MCP 层,业务代码保持干净。如果你用 Claude Code 或者类似的编码 Agent 做日常开发,可以把 MCP 服务注册进去,让 Agent 直接调用你的后端接口,省掉手动写 curl 的功夫。Coding Plan 的入口在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite,适合需要长期跑 Agent 的场景。
API Key 的管理也别忽视。TaoToken 的 API Keys 页面https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite可以创建多个 Key,建议按环境区分,开发、测试、生产各用一个,方便排查和回收。接入文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,遇到配置问题先翻文档,大部分常见问题都有说明。
最后说一个实际经验:MCP 工具的数量不要一次性全暴露给模型。接口多了之后,tools/list 会变得很长,模型选择准确率反而下降。可以按业务域拆成多个 serviceKey,或者用callOnly模式配合listOperations做两段式调用。先让模型查有哪些接口,再决定调哪个,这样上下文更可控。