ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

超实用!Dify 快速接入本地 MCP 服务:把 endpoint 改到 TaoToken

超实用!Dify 快速接入本地 MCP 服务:把 endpoint 改到 TaoToken

1. 本地 MCP 服务接进 Dify 后,为什么还要把 endpoint 改到 TaoToken

你本地已经跑起来一个 MCP Server,比如用 Spring AI 写的天气查询服务,监听在http://127.0.0.1:8000/sse。Dify 里装好 MCP 插件、填上这个地址,Agent 理论上就能调用工具了。但真正跑起来你会发现一个尴尬的现实:Dify 的 Agent 策略节点在调用 MCP 工具时,背后仍然需要一个 LLM 来做「意图识别 + 工具选择 + 参数抽取」。这个 LLM 的 endpoint 如果指向的是各家厂商的原始地址,你就得在 Dify 里维护多套 Key、多套 Base URL,模型一换配置全乱。

我试过把 Dify 的模型供应商 endpoint 统一改到 TaoToken 的 API 通道,本地 MCP 服务的地址保持不变,只把「模型侧」的出口收敛到一个统一 Key 上。这样做的直接好处是:Dify 里只需要维护一份 API Key,模型 ID 想换就换,MCP 工具配置片段完全不用动。对于已经有本地 MCP Server 的开发者来说,这是一次改动量极小、收益很明确的调整。

这篇文章面向的是「本地 MCP Server 已经能跑通、Dify 已经装好 MCP 插件」的开发者。我会给出可复制的 Dify 工具配置 JSON、TaoToken 侧 Base URL 与 Key 的填写方式、一次完整的工具调用连通性验证动作,以及几个真实会撞上的报错排查。核心检索词就三个:Dify、MCP、本地 MCP 服务。适合谁?适合那些不想在 Dify 里堆一堆厂商 Key、希望用统一通道管理模型出口的人。

先说清楚 MCP 是什么,避免后面配置时概念混淆。MCP 是 Model Context Protocol,模型上下文协议,由 Anthropic 提出,用来让大模型连接外部数据源和工具。你可以把它类比成 Java 微服务里的 OpenFeign:OpenFeign 负责服务间通讯,MCP 负责大模型与工具间通讯。Dify 作为客户端发起 MCP 请求,你的本地 MCP Server 作为服务端响应请求、执行工具、返回结果。整个链路里,LLM 负责「决定调哪个工具」,MCP Server 负责「真正执行」。

关键点在于:Dify 只支持 HTTP 协议的 MCP 服务调用,也就是sse和streamable_http两种 transport。你的本地 MCP Server 如果是 STDIO 类型,需要先暴露成 HTTP 端点。Spring AI 的spring-ai-starter-mcp-server-webflux就是干这个的,默认 SSE 端点是/mcp/message,实际连接地址通常是/sse。这一点在配置 Dify 时容易填错,后面排障章节会细说。

把 endpoint 改到 TaoToken,改的不是 MCP Server 的地址,而是 Dify 里 LLM 节点的模型出口地址。这个区分很重要,很多人第一次配会搞混,以为要把 MCP 的 url 改成 TaoToken,那就完全跑偏了。MCP 的 url 永远指向你本地的127.0.0.1:端口,TaoToken 的 Base URL 填在 Dify 的模型供应商配置里。

2. TaoToken 前置准备:Base URL、API Key 与模型 ID 三件套

在动 Dify 配置之前,先把 TaoToken 侧的三件套准备好:Base URL、API Key、Model ID。这三样东西在 Dify 的模型供应商配置和 MCP 工具调用里都会用到,缺一不可。

Base URL 填https://taotoken.net/api。注意这里不要加任何路径后缀,Dify 的 OpenAI 兼容供应商会自动拼接/v1/chat/completions这类路径。如果你手贱加了/v1,最后会变成/v1/v1/chat/completions,直接 404。这个坑我踩过,排查了半小时才发现是路径重复。

API Key 在 TaoToken 控制台的 API Keys 页面生成。生成后复制出来,格式通常是一串以sk-开头的字符串。这个 Key 要填在 Dify 的模型供应商配置里,不是填在 MCP 工具配置里。再强调一次:MCP 工具配置里的headers是给本地 MCP Server 用的,如果你的本地服务不需要鉴权,headers留空对象{}就行。

Model ID 取决于你想用哪个模型。Dify 的 Agent 策略节点需要一个支持 Function Calling 的模型,因为 MCP 工具调用本质上就是 Function Calling 的标准化封装。你可以在 TaoToken 的模型对话页面先确认目标模型 ID 的准确写法,比如claude-sonnet-4-20250514这种带日期后缀的,或者gpt-4o这种简写。写错 Model ID 的典型报错是model not found,后面排障章节会给出对照。

如果你打算长期在 Dify 里跑 Agent 工作流,建议直接开 Coding Plan,因为 Agent 场景的 token 消耗比普通对话高不少,尤其是多轮工具调用时,每轮都要把工具列表和上下文重新塞进去。Coding Plan 的额度模型更适合这种高频调用场景。开通入口在控制台的 Coding Plan 页面。

三件套准备好之后,先在本地用 curl 验证一下 TaoToken 的 API 通道是否通。这一步不要跳过,因为如果 TaoToken 侧就不通,你在 Dify 里配半天也是白搭。验证命令如下:

curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的Key" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [{"role": "user", "content": "回复OK两个字"}], "max_tokens": 20 }'

正常返回应该是一个 JSON,choices[0].message.content里是「OK」。如果返回 401,说明 Key 错了或者没带Bearer前缀。如果返回 404,检查 Base URL 是不是多加了/v1。如果返回model not found,去模型对话页面核对 Model ID 的准确拼写。

这一步通了之后,再进 Dify 配置。顺序很重要:先验证通道,再配 Dify,最后配 MCP 工具。反过来做的话,出问题你分不清是 TaoToken 的问题、Dify 的问题还是 MCP Server 的问题。

另外提一句,TaoToken 的接入文档里有各语言 SDK 的示例,如果你不想用 curl,也可以直接参考文档里的 Python 或 Node 示例。文档入口在接入文档页面。但 Dify 场景下你其实不需要写代码,Dify 的 OpenAI 兼容供应商会帮你处理请求格式,你只需要填对 Base URL、Key、Model ID 三个字段。

3. 可复制配置:Dify 模型供应商 + MCP 工具 JSON 片段

这一节给出可以直接复制的配置片段。分两部分:Dify 模型供应商配置(指向 TaoToken)和 MCP 工具配置(指向本地 MCP Server)。

先说 Dify 模型供应商配置。进入 Dify 的「设置」→「模型供应商」→ 选择「OpenAI-API-compatible」或「OpenAI」兼容类型。填写以下字段:

配置项填写值说明
模型类型LLMAgent 策略需要 LLM
模型名称claude-sonnet-4-20250514按实际 Model ID 填
API Keysk-你的TaoToken Key控制台生成
Base URLhttps://taotoken.net/api不加 /v1 后缀
模型上下文长度200000按模型实际能力填
最大 token 上限8192按需调整
是否支持 Function Calling开启MCP 调用必需

这里最容易错的是 Base URL。Dify 的 OpenAI 兼容供应商会在 Base URL 后面自动拼/v1/chat/completions,所以你填https://taotoken.net/api就够了。如果你填https://taotoken.net/api/v1,最终请求会变成https://taotoken.net/api/v1/v1/chat/completions,直接 404。

然后是 MCP 工具配置。在 Dify 的 MCP 插件里,填入以下 JSON。这个 JSON 的格式和 Claude Desktop 的claude_desktop_config.json基本一致,Dify 支持两种外层结构,我用带mcpServers的那种,兼容性更好:

{ "mcpServers": { "local-weather": { "transport": "sse", "url": "http://127.0.0.1:8000/sse", "headers": {}, "timeout": 50, "sse_read_timeout": 50 }, "local-db-query": { "transport": "streamable_http", "url": "http://127.0.0.1:8002/mcp", "headers": {}, "timeout": 50 } } }

几个字段的含义:transport选sse或streamable_http,取决于你的 MCP Server 暴露方式。Spring AI 的 webflux starter 默认走 SSE,端点是/sse。url指向本地 MCP Server,不要改成 TaoToken 的地址。headers如果你的本地服务不需要鉴权就留空对象。timeout和sse_read_timeout单位是秒,本地调用设 50 秒足够。

如果你用的是 Spring AI 的spring-ai-starter-mcp-server-webmvc,transport 可能是streamable_http,端点路径看你的spring.ai.mcp.server.sse-message-endpoint配置,默认是/mcp/message,但实际连接地址要看你暴露的 controller 路径。这个细节容易搞混,建议先用 curl 测一下本地端点是否可达:

curl -N http://127.0.0.1:8000/sse

如果返回一串event: endpoint开头的数据流,说明 SSE 端点正常。如果连接被拒绝,检查 MCP Server 是否启动、端口是否被占用。

配置片段填完之后,Dify 的 Agent 策略节点里要选上刚才配的 TaoToken 模型,然后在工具列表里勾选 MCP 工具。Agent 策略建议选「Function Calling」类型,因为 MCP 工具调用依赖 Function Calling 能力。指令(Instruction)里写清楚工具的使用场景,比如「当用户询问天气时,调用 local-weather 工具的 getWeatherByCity」。

这里有个细节:Dify 的 MCP 插件在拉取工具列表时,会向 MCP Server 发一个tools/list请求。如果你的 MCP Server 没有正确暴露工具,Dify 的工具列表会是空的。Spring AI 里通过ToolCallbackProvider暴露工具,确保你的@Tool注解方法被正确注册。如果工具列表为空,先检查 MCP Server 日志里有没有收到tools/list请求。

4. 验证请求:一次完整的 MCP 工具调用连通性测试

配置填完之后,必须做一次端到端的连通性验证。这一步的目的是确认「Dify → TaoToken → LLM → MCP Server → 工具执行 → 返回结果」整条链路是通的。

验证动作分三步:先单独测 MCP Server,再单独测 TaoToken 通道,最后在 Dify 里跑一次完整调用。

第一步,单独测 MCP Server 的工具列表。用 curl 发一个tools/list请求:

curl -X POST http://127.0.0.1:8000/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }'

正常返回应该包含你注册的工具,比如getWeatherByCity,以及它的参数 schema。如果返回空列表,说明工具没注册成功,回去检查ToolCallbackProvider的 bean 配置。如果返回 404,说明端点路径不对,SSE 类型的 MCP Server 的tools/list请求可能走的是/mcp/message而不是/mcp,具体看你的配置。

第二步,单独测 TaoToken 通道。这个在第 2 节已经给过 curl 命令,这里再给一个带 Function Calling 的测试,因为 MCP 调用依赖这个能力:

curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的Key" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [{"role": "user", "content": "西安天气怎么样"}], "tools": [{ "type": "function", "function": { "name": "getWeatherByCity", "description": "根据城市名称获取天气预报", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"] } } }], "max_tokens": 200 }'

正常返回里,choices[0].message.tool_calls应该包含一个对getWeatherByCity的调用,参数是{"city": "西安"}。如果tool_calls为空,说明模型没有触发工具调用,可能是模型不支持 Function Calling,或者 prompt 不够明确。如果返回 400 且提示tools is not supported,换一个支持 Function Calling 的 Model ID。

第三步,在 Dify 里跑完整调用。进入你配好的 ChatFlow 或 Agent 应用,输入「西安天气怎么样」,观察执行过程。正常情况下,你会看到 Agent 节点先调用 LLM,LLM 返回工具调用意图,Dify 的 MCP 插件向本地 MCP Server 发请求,MCP Server 执行getWeatherByCity返回「晴天」,最后 LLM 把结果组织成自然语言回复。

如果 Dify 执行到 MCP 调用步骤卡住或报错,看 Dify 的日志面板。常见的是local proxy failed或connection refused,说明 Dify 容器访问不到127.0.0.1:8000。这是 Docker 网络隔离问题,如果你的 Dify 是 Docker 部署的,127.0.0.1指向的是容器内部,不是宿主机。解决办法是把 MCP Server 的地址改成宿主机的局域网 IP,比如http://192.168.1.100:8000/sse,或者在 Dify 的 docker-compose 里加network_mode: host。

验证通过的标准是:Dify 的回复里包含「西安」「晴天」这两个关键词,且执行日志里能看到 MCP 工具的调用记录。到这一步,整条链路就通了。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth

这一节列出配置过程中真实会撞上的报错,以及对应的排查方向。每个报错都给出触发条件和解决动作。

401 Unauthorized。触发条件:TaoToken 的 API Key 填错、过期、或者没带Bearer前缀。排查动作:先在本地用 curl 测 Key 是否有效,命令见第 2 节。如果 curl 也返回 401,去控制台的 API Keys 页面重新生成一个。如果 curl 通了但 Dify 里 401,检查 Dify 的模型供应商配置里 Key 字段有没有多余空格,或者是不是填到了 MCP 工具的headers里。记住:TaoToken 的 Key 填在模型供应商,不是填在 MCP 工具配置。

local proxy failed。触发条件:Dify 容器访问不到本地 MCP Server 的地址。排查动作:确认 Dify 的部署方式。如果是 Docker 部署,127.0.0.1在容器内指向容器自身,不是宿主机。解决办法有两个:一是把 MCP 的 url 改成宿主机的局域网 IP,比如http://192.168.1.100:8000/sse;二是在 docker-compose 里给 Dify 的 api 和 worker 服务加network_mode: host。改完之后重启 Dify 容器。另外确认本地 MCP Server 监听的是0.0.0.0而不是127.0.0.1,否则局域网 IP 也访问不到。

reading choices 报错。完整报错通常是error reading choices: unexpected end of JSON input或reading choices: EOF。触发条件:TaoToken 返回的响应体不完整,或者 Dify 解析响应时出错。排查动作:先用 curl 直接测 TaoToken 通道,确认返回的是完整 JSON。如果 curl 正常但 Dify 报这个错,检查 Dify 的模型供应商配置里 Base URL 是不是多加了/v1,导致请求路径错误返回了非 JSON 响应。另外检查 Model ID 是否拼写正确,错误的 Model ID 有时会返回 HTML 错误页而不是 JSON,Dify 解析时就报reading choices。

OAuth 相关报错。触发条件:MCP Server 配置了 OAuth 鉴权,但 Dify 的 MCP 工具配置里没带对应的 token。排查动作:如果你的本地 MCP Server 不需要鉴权,headers留空{}即可,不会触发 OAuth。如果确实需要鉴权,在 MCP 工具配置的headers里加上Authorization字段:

{ "mcpServers": { "local-weather": { "transport": "sse", "url": "http://127.0.0.1:8000/sse", "headers": { "Authorization": "Bearer 你的MCP服务token" }, "timeout": 50 } } }

注意这里的 token 是 MCP Server 的鉴权 token,不是 TaoToken 的 Key。两个不要搞混。

工具列表为空。触发条件:Dify 的 MCP 插件拉不到工具。排查动作:用第 4 节的 curl 命令直接测 MCP Server 的tools/list。如果 curl 返回空列表,检查 Spring AI 的ToolCallbackProviderbean 是否正确注册,@Tool注解的方法是否是 public 的。如果 curl 正常但 Dify 里为空,检查 Dify 的 MCP 插件版本,旧版本可能不支持streamable_httptransport,升级到最新版。

模型不触发工具调用。触发条件:LLM 返回的tool_calls为空。排查动作:确认 Model ID 支持 Function Calling。部分轻量模型不支持工具调用,换claude-sonnet-4-20250514或gpt-4o这类明确支持 Function Calling 的模型。另外在 Dify 的 Agent 指令里明确写「必须调用工具获取数据,不要凭记忆回答」,提高触发概率。

6. 把统一通道用起来:Dify + 本地 MCP 的长期维护建议

配置跑通之后,日常维护其实很轻。Dify 侧你只需要维护一份 TaoToken 的 Key,模型 ID 想换就在模型供应商里改一个字段,MCP 工具配置完全不用动。本地 MCP Server 侧你只管加工具、改逻辑,Dify 下次拉取工具列表时会自动更新。

如果你有多个本地 MCP Server,比如一个查天气、一个查数据库、一个操作文件系统,全部塞进同一个 JSON 的mcpServers对象里就行。Dify 的 Agent 会根据工具描述自动选择调用哪个。工具描述写得越清楚,LLM 选得越准。Spring AI 的@Tool(description = "...")里的描述就是给 LLM 看的,别写得太简略。

长期跑 Agent 工作流的话,token 消耗会比你想象的高。每次工具调用都是一轮完整的 LLM 请求,工具列表和上下文都要重新传。如果调用频率高,建议开 Coding Plan,额度模型更适合这种场景。开通入口在控制台的 Coding Plan 页面。

最后给一个实用技巧:在 Dify 的 Agent 指令里加一句「如果工具调用失败,把原始错误信息返回给用户,不要自行编造结果」。这样当 MCP Server 挂掉或者 TaoToken 通道异常时,你能直接从 Dify 的回复里看到真实报错,而不是被 LLM 编造的假数据误导。这个技巧在排查阶段特别有用。

整条链路的核心就一句话:MCP 的 url 指向本地,模型的 Base URL 指向 TaoToken,两者各管各的,不要混。配好之后,你换模型、加工具、改逻辑,都只需要动一个地方。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进