Dify MCP 集成实验(03):MCP 接入 Dify 全链路——MCP Server 如何接入 Dify 应用?
Dify 实验系列 · MCP 集成 03/6 | 实验编号:DIFY-107-03
基于 Dify 1.16.1 实测(2026-08)
1. 业务场景
先讲一个我们实际遇到的场景。
一家做客服工单 SaaS 的公司,客服门户的 AI 助手要能回答「我的工单 T12345 什么状态」——状态数据在 107-02 的 MCP server 里。现在要把这个 server 真正接进 Dify:Console 添加 MCP server → 工作流 tool 节点调用 → 冒烟验证。这是集成单最典型的场景:外部系统数据通过 MCP 进入 Dify 应用。
我们第一次接这类需求时,第一反应是「Console 里填个 URL 就能连」。真正动手才发现——接入的未知点全藏在网络路径和 DSL 格式里:api 容器访问外部 server 要过 SSRF 代理,白名单没覆盖就 403;MCP 工具节点在 DSL 里的准确字段网上查不到权威格式,只能从 UI 导出反推。不实测,排障全靠试。
这不是个例。任何「外部系统数据通过 MCP 进入 Dify 应用」的集成都是这个模式:网络路径通不通、DSL 字段怎么写——两个未知点不实测,后面全凭猜。
2. 场景痛点
这个流程的痛点,在接入时体现得最直接:
- SSRF 双层 403:api 容器连外部 MCP server 要走 squid 代理(ssrf_proxy),白名单没覆盖就 403——而且 403 可能来自两层(SDK 2.0 的 DNS rebinding protection / squid 白名单),报错信息都叫 403,分不清是哪一层。
- DSL 字段靠猜:MCP 工具节点在 DSL 里的准确字段(
provider_type: "mcp"?provider_id是什么?)——写错导入就失败,网上又查不到权威格式。 - 工具输出消费错:结构化工具(outputSchema)输出是字段直接展开、
text=""——下游引用 text 就收到空白,LLM 误报「未找到」。 - 模型 disabled 400:DSL 里写了 disabled 模型(本机 deepseek-chat disabled),run 直接 400。
本质上,接入的未知点集中在「网络路径」和「DSL 权威格式」两处——不实测,排障全靠试。
3. 方案:为什么是 Console + UI 导出实证
把 107-01/02 的 MCP server 接入 Dify 全链路,最可靠的方法是 Console 添加 + UI 导出权威 DSL + 运行级实证。
选它的理由:
- 网络路径实测定位:api 容器内 curl(不走代理)POST /mcp 返回 200,Dify 客户端(走代理)403——用排错关键证据锁定代理层,而不是瞎改配置;
- UI 导出权威格式:从 Console 导出含 MCP 工具节点的 DSL——UI 导出是权威格式,回填 skill reference,以后生成带 MCP 工具的 DSL 直接引用;
- 运行级实证三原语结论:Console 工具列表只见 tools(resources/prompts 无入口)——107-02 的对照结论用真实运行验证,不是源码推断。
这篇文章我们就用它把「工单状态查询」MCP 工具接入客服门户应用,跑通全链路并固化 DSL 权威格式。
4. 整体架构
链路很清晰:本地 server(Streamable HTTP)→ Dify api(经 squid 代理)→ web 控制台配置 → 工作流 tool 节点 → LLM → end。关键设计是验证应用用最简链路(start → tool → LLM → end),把未知点集中在 MCP 工具节点本身。
5. 模块设计
5.1 SSRF 双层 403 的修复(未知点 1,根因链)
| 层 | 问题 | 修复 |
|---|---|---|
| ① SDK 2.0 server | streamable-http 默认 DNS rebinding protection,Host=host.docker.internal 不在白名单 → 403 | run(transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False))(开发环境;生产用 allowed_hosts) |
| ② squid SSRF 代理 | MCP 客户端也走 ssrf_proxy,host.docker.internal 不在 squid 白名单 → 403 | .env加SSRF_PROXY_ALLOW_PRIVATE_DOMAINS=localhost,host.docker.internal+docker compose up -d --force-recreate ssrf_proxy(restart 不重跑 entrypoint,必须 recreate) |
排错关键证据:api 容器内 curl(不走代理)POST /mcp 返回 200,Dify 客户端(走代理)403 → 锁定代理层。
5.2 MCP 工具节点 DSL 权威格式(未知点 2,UI 导出实测)
-data:provider_id:dify107_02_support_server# = MCP provider 的 server_identifierprovider_name:dify107_02_support_serverprovider_show_name:dify107_02_support_serverprovider_type:mcp# ⚠️ 字符串 "mcp"(区分 builtin/workflow 工具)tool_name:get_ticket_statustool_node_version:'2'tool_parameters:ticket_id:type:mixedvalue:'{{#start.ticket_id#}}'tool_configurations:{}plugin_id:nullplugin_unique_identifier:nulloutput_schema:{}type:tool与 builtin/workflow 工具同 node 类型(type: tool),靠provider_type: mcp区分;provider_id= MCP provider 的 server_identifier(不是行 UUID)。
6. 运行验证
| 输入 | 预期 | 结果 |
|---|---|---|
| T1002(存在 open) | 工单 T1002 当前状态是 open(最后更新于 2026-08-04 09:30:00) | 通过 |
| T1001(存在 closed) | 工单 T1001 当前状态是 closed(最后更新于 2026-08-01 10:00:00) | 通过 |
| T9999(不存在) | 未找到该工单,请核对工单号 | 通过 |
| abc(格式错 → 工具 isError) | workflow failed(显式失败非静默) | 通过(显式失败) |
| Console 工具列表 | 只见 search_tickets / get_ticket_status(tools),resources/prompts 无入口 | 通过(三原语运行级实证) |
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| SSRF 双层 403 | ① SDK 2.0 streamable-http 默认 DNS rebinding protection(Host=host.docker.internal → 403)② MCP 客户端也走 squid 代理,host.docker.internal 不在白名单 → 403 | ① server.run 关掉或配 allowed_hosts ②.env加SSRF_PROXY_ALLOW_PRIVATE_DOMAINS+ force-recreate(restart 不生效)(实测) |
| MCP 工具输出 text 为空 | 结构化工具(outputSchema)输出=字段直接展开(ticket_id/status/updated_at),text="" | 下游引用展开字段({{#tool_status.status#}}),别引用 text——首版 LLM 收空白误报「未找到」(实测) |
| DSL provider_type | MCP 工具节点不认 builtin/workflow 的 provider_type 值 | 写provider_type: "mcp"(graphon 枚举),provider_id=server_identifier(实测) |
| Model is disabled | DSL 写 disabled 模型(本机 deepseek-chat disabled)→ run 400 | 用 active 模型(deepseek-v4-flash);查GET model-providers/{provider}/models的 status(实测) |
| MCP 删除/列表 UUID 语义 | 列表接口 id=server_identifier,删除/工具端点按 DB 行 UUID 查 | 行 UUID 从 DB 查SELECT id FROM tool_mcp_providers;创建响应 id=行 UUID(实测) |
| 工具错误行为 | MCP 工具 isError → Dify tool 节点失败 → workflow failed | 显式非静默;优雅降级 107-06 验收时按需加 fail 分支(实测) |
8. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-107-03:MCP接入Dify全链路.md
- 源码(可直接导入,含 MCP 工具节点权威格式):dify107_03_验证应用.yml
- 交付验证记录(SSRF 根因链 + DSL 权威格式 + 输出消费规则):验证记录-03-MCP接入Dify全链路.md
- 全部目录:dify-107/experiments | dify-107/dsl | dify-107/servers | dify-107/delivery
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
下一篇:Dify MCP 集成实验(04):企业系统对接场景——企业系统对接选 MCP 还是插件?
💬 你在这个实验的场景里踩过什么坑?欢迎评论区分享你的实战经验。