Dify + MCP 协议:让 Agent 具备跨系统操作能力
在上一篇文章中,我们深入拆解了 Dify 工作流的核心节点,并实战构建了一个数据分析 Agent。这个 Agent 可以理解意图、调用工具、生成报告——但它的能力边界,仍然局限于 Dify 平台内部预置的工具集。现实的企业环境远比这复杂:你的数据在 MySQL,你的文件在 Confluence,你的任务在 Jira,你的消息在钉钉……Agent 想要真正"跨系统操作",就必须能够与这些异构系统无缝
前言:Agent 的"最后一公里"问题
在上一篇文章中,我们深入拆解了 Dify 工作流的核心节点,并实战构建了一个数据分析 Agent。这个 Agent 可以理解意图、调用工具、生成报告——但它的能力边界,仍然局限于 Dify 平台内部预置的工具集。
现实的企业环境远比这复杂:你的数据在 MySQL,你的文件在 Confluence,你的任务在 Jira,你的消息在钉钉……Agent 想要真正"跨系统操作",就必须能够与这些异构系统无缝对话。
这正是 MCP(Model Context Protocol,模型上下文协议) 诞生的原因。
本文将系统介绍 MCP 协议的设计理念与工作机制,并详细讲解如何在 Dify 中集成 MCP,让你的 Agent 真正具备跨系统操作能力。
一、MCP 协议:AI 世界的"USB 标准"
1.1 MCP 是什么?
MCP(Model Context Protocol)是由 Anthropic 于 2024 年底发布的开放协议,旨在为 AI 模型与外部系统之间建立标准化的通信接口。
一个形象的类比:在 USB 标准出现之前,每个设备厂商都有自己的接口标准,鼠标、键盘、U 盘各用各的插口,极其混乱。USB 的出现统一了接口规范,任何符合标准的设备都能即插即用。
MCP 之于 AI Agent,正如 USB 之于硬件外设:
- 统一协议:无论是数据库、文件系统还是 SaaS 服务,都通过同一套 MCP 接口暴露给 Agent
- 即插即用:新增一个 MCP Server,Agent 立即获得操作该系统的能力
- 安全隔离:Agent 通过 MCP 协议间接操作系统,不直接持有系统凭证
1.2 MCP 的核心架构
MCP 采用 Client-Server 架构,由三个核心角色组成:
| 角色 | 职责 | 对应实体 |
|---|---|---|
| MCP Host | 发起请求的 AI 应用主体 | Dify、Claude Desktop、Cursor |
| MCP Client | 内嵌于 Host,管理与 Server 的连接 | Dify 内置的 MCP 客户端 |
| MCP Server | 暴露工具/资源的外部系统适配器 | MySQL Server、GitHub Server、Slack Server |
完整的调用链路如下:
用户 → Dify (MCP Host)
↓
MCP Client
↓ [MCP 协议通信]
MCP Server (如 GitHub MCP Server)
↓
GitHub API
↓
返回结果 → MCP Client → Dify → 用户
1.3 MCP 提供的三类能力
MCP Server 可以向 Agent 暴露三类原语(Primitives):
① Tools(工具)
Agent 可以调用的函数,类似 Function Calling。例如:
create_issue:在 GitHub 创建 Issuesend_message:向钉钉群发送消息query_database:执行 SQL 查询
② Resources(资源)
Agent 可以读取的数据源,类似文件或 URL。例如:
- Git 仓库中的文件内容
- 数据库中的表结构信息
- Confluence 文档页面
③ Prompts(提示模板)
预定义的提示词模板,可被 Agent 复用。例如代码审查模板、SQL 生成模板等。
1.4 MCP 通信机制
MCP 支持两种传输方式:
| 传输方式 | 适用场景 | 特点 |
|---|---|---|
| stdio | 本地进程通信 | 低延迟,适合本地工具(如文件系统、本地数据库) |
| SSE(HTTP) | 远程网络通信 | 支持跨网络,适合 SaaS 服务、云端系统 |
通信格式采用 JSON-RPC 2.0,结构清晰,易于调试和扩展。
二、Dify 集成 MCP 的方式
2.1 Dify 对 MCP 的支持现状
Dify 从 v0.10.x 版本开始逐步引入 MCP 支持。目前主要有两种集成路径:
- 内置 MCP Tool 节点:在工作流编辑器中直接添加 MCP 工具节点
- 自定义工具接入:通过 Dify 的"工具"功能,将 MCP Server 封装为自定义工具
本文重点介绍实战中最常用的自定义工具接入方式,兼容性更广,适合生产环境使用。
2.2 集成架构设计
Dify Workflow
↓
[Agent 节点] 或 [HTTP 请求节点]
↓
MCP Proxy 服务(中间层,将 HTTP 转为 MCP 协议)
↓
MCP Server(各类系统的 MCP 适配器)
↓
目标系统(GitHub / Jira / 数据库 / 钉钉等)
核心思路:在 Dify 与 MCP Server 之间部署一个轻量的 MCP Proxy,将 Dify 的 HTTP 请求转换为 MCP 协议调用,再将结果返回给 Dify。
三、实战:搭建 MCP Server 并接入 Dify
3.1 环境准备
我们以一个常见场景为例:让 Agent 能够操作 GitHub(创建 Issue、读取仓库信息、提交代码评论)。
所需环境:
- Python 3.10+
- Dify v0.10+ 实例(本地或云端)
- GitHub Personal Access Token
- MCP Python SDK:
pip install mcp
3.2 编写 GitHub MCP Server
# github_mcp_server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import httpx
import json
import os
app = Server("github-mcp-server")
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN")
GITHUB_BASE_URL = "https://api.github.com"
headers = {
"Authorization": f"Bearer {GITHUB_TOKEN}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28"
}
@app.list_tools()
async def list_tools() -> list[Tool]:
"""声明此 MCP Server 提供的工具列表"""
return [
Tool(
name="create_issue",
description="在指定 GitHub 仓库创建一个新的 Issue",
inputSchema={
"type": "object",
"properties": {
"owner": {"type": "string", "description": "仓库所有者"},
"repo": {"type": "string", "description": "仓库名称"},
"title": {"type": "string", "description": "Issue 标题"},
"body": {"type": "string", "description": "Issue 描述内容"},
"labels": {
"type": "array",
"items": {"type": "string"},
"description": "标签列表(可选)"
}
},
"required": ["owner", "repo", "title", "body"]
}
),
Tool(
name="list_issues",
description="获取指定仓库的 Issue 列表",
inputSchema={
"type": "object",
"properties": {
"owner": {"type": "string", "description": "仓库所有者"},
"repo": {"type": "string", "description": "仓库名称"},
"state": {
"type": "string",
"enum": ["open", "closed", "all"],
"description": "Issue 状态过滤"
},
"per_page": {"type": "integer", "description": "每页数量,默认30"}
},
"required": ["owner", "repo"]
}
),
Tool(
name="add_comment",
description="在指定 Issue 或 PR 上添加评论",
inputSchema={
"type": "object",
"properties": {
"owner": {"type": "string"},
"repo": {"type": "string"},
"issue_number": {"type": "integer", "description": "Issue 编号"},
"body": {"type": "string", "description": "评论内容"}
},
"required": ["owner", "repo", "issue_number", "body"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""执行工具调用"""
async with httpx.AsyncClient() as client:
if name == "create_issue":
url = f"{GITHUB_BASE_URL}/repos/{arguments['owner']}/{arguments['repo']}/issues"
payload = {
"title": arguments["title"],
"body": arguments["body"],
"labels": arguments.get("labels", [])
}
response = await client.post(url, headers=headers, json=payload)
result = response.json()
return [TextContent(
type="text",
text=f"Issue 创建成功!编号: #{result['number']}, 链接: {result['html_url']}"
)]
elif name == "list_issues":
url = f"{GITHUB_BASE_URL}/repos/{arguments['owner']}/{arguments['repo']}/issues"
params = {
"state": arguments.get("state", "open"),
"per_page": arguments.get("per_page", 30)
}
response = await client.get(url, headers=headers, params=params)
issues = response.json()
result_text = f"共找到 {len(issues)} 个 Issue:
"
for issue in issues[:10]:
result_text += f"- #{issue['number']} {issue['title']} [{issue['state']}]
"
return [TextContent(type="text", text=result_text)]
elif name == "add_comment":
url = f"{GITHUB_BASE_URL}/repos/{arguments['owner']}/{arguments['repo']}/issues/{arguments['issue_number']}/comments"
response = await client.post(url, headers=headers, json={"body": arguments["body"]})
result = response.json()
return [TextContent(
type="text",
text=f"评论已添加,链接: {result['html_url']}"
)]
else:
return [TextContent(type="text", text=f"未知工具: {name}")]
async def main():
async with stdio_server() as (read_stream, write_stream):
await app.run(read_stream, write_stream, app.create_initialization_options())
if __name__ == "__main__":
import asyncio
asyncio.run(main())
3.3 部署 MCP Proxy(HTTP 桥接层)
为了让 Dify 能通过 HTTP 调用 MCP Server,我们需要一个桥接服务。使用 mcp-proxy 工具可以快速实现:
# 安装 mcp-proxy
pip install mcp-proxy
# 启动代理,将 HTTP 请求转发给 stdio 模式的 MCP Server
mcp-proxy --port 8080 --server "python github_mcp_server.py" --env GITHUB_TOKEN=your_token_here
启动后,MCP Server 通过 HTTP 在 http://localhost:8080 提供服务,接口格式为:
POST http://localhost:8080/tools/call
Content-Type: application/json
{
"name": "create_issue",
"arguments": {
"owner": "your-org",
"repo": "your-repo",
"title": "Bug: 登录失败",
"body": "用户反馈在 iOS 16 上无法正常登录"
}
}
3.4 在 Dify 中配置自定义工具
进入 Dify → 工具 → 自定义工具 → 新建,填写 OpenAPI 规范:
openapi: 3.0.0
info:
title: GitHub MCP Tools
version: 1.0.0
description: 通过 MCP 协议操作 GitHub
servers:
- url: http://localhost:8080
paths:
/tools/call:
post:
operationId: call_mcp_tool
summary: 调用 MCP 工具
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 工具名称(create_issue / list_issues / add_comment)
arguments:
type: object
description: 工具参数
required:
- name
- arguments
responses:
'200':
description: 工具执行结果
content:
application/json:
schema:
type: object
四、在 Dify 工作流中使用 MCP 工具
4.1 工作流设计:智能 Bug 报告助手
我们构建一个实际的 Agent 工作流:用户描述一个 Bug,Agent 自动在 GitHub 创建格式规范的 Issue,并根据严重程度添加对应标签。
[开始] → 用户输入 Bug 描述
↓
[LLM 节点] → 分析 Bug,提取结构化信息
(标题、复现步骤、严重程度、影响范围)
↓
[IF/ELSE] → 判断严重程度
├── Critical → 标签: ["bug", "critical", "P0"]
├── High → 标签: ["bug", "P1"]
└── Normal → 标签: ["bug", "P2"]
↓
[HTTP 请求节点] → 调用 MCP Proxy 创建 GitHub Issue
↓
[LLM 节点] → 生成用户友好的确认回复
↓
[结束] → 输出创建结果
4.2 关键节点配置
LLM 节点 Prompt(结构化提取)
你是一个专业的 Bug 分析助手。
用户描述的问题:
{{user_input}}
请分析并提取以下结构化信息,以 JSON 格式输出:
{
"title": "简洁的 Issue 标题(50字以内)",
"body": "完整的 Issue 描述,包含:
## 问题描述
## 复现步骤
## 期望行为
## 实际行为
## 环境信息",
"severity": "critical | high | normal",
"labels": ["相关标签"]
}
只输出 JSON,不要其他内容。
HTTP 请求节点配置
- 方法:POST
- URL:
http://localhost:8080/tools/call - 请求体:
{ "name": "create_issue", "arguments": { "owner": "{{repo_owner}}", "repo": "{{repo_name}}", "title": "{{parsed_result.title}}", "body": "{{parsed_result.body}}", "labels": {{parsed_result.labels}} } }
4.3 多系统联动示例
通过组合多个 MCP Server,可以实现强大的跨系统工作流。例如:
用户报告 Bug
↓
[GitHub MCP] → 创建 Issue
↓
[Jira MCP] → 同步创建对应工单
↓
[Slack MCP] → 通知对应研发团队频道
↓
[钉钉 MCP] → 向 oncall 工程师发送告警
这种"一次输入,多系统联动"的能力,正是 MCP 带给 AI Agent 的核心价值。
五、构建自己的 MCP Server:最佳实践
5.1 工具设计原则
一个高质量的 MCP Server 应遵循以下原则:
① 工具粒度要适中
不要设计过于复合的工具。错误示例:
# ❌ 不好:一个工具做太多事
"create_and_assign_and_notify_issue"
# ✅ 好:职责单一,可组合
"create_issue"
"assign_issue"
"send_notification"
② 描述信息是关键
MCP 工具的 description 字段直接影响 LLM 的工具选择准确率。描述应该:
- 说明工具的功能(做什么)
- 说明适用场景(什么情况下用)
- 说明输出格式(返回什么)
Tool(
name="search_issues",
description="""搜索 GitHub 仓库中的 Issues。
适用场景:当用户询问某个仓库是否存在相关问题、查找历史 Bug 时使用。
参数说明:q 为搜索关键词,支持 GitHub 搜索语法。
返回:匹配的 Issue 列表,包含编号、标题、状态和链接。""",
...
)
③ 错误处理要详细
@app.call_tool()
async def call_tool(name: str, arguments: dict):
try:
# 执行工具逻辑
result = await execute_tool(name, arguments)
return [TextContent(type="text", text=result)]
except httpx.HTTPStatusError as e:
# 返回对 LLM 友好的错误信息
return [TextContent(
type="text",
text=f"API 调用失败(状态码 {e.response.status_code}):{e.response.text}。请检查参数是否正确或权限是否足够。"
)]
except Exception as e:
return [TextContent(
type="text",
text=f"工具执行出错:{str(e)}。建议尝试其他方式或联系管理员。"
)]
5.2 安全性设计
MCP Server 处于 Agent 与真实系统之间,安全设计至关重要:
- 最小权限原则:Token 只授予必要的权限范围(如只读 vs 读写)
- 操作审计日志:记录所有工具调用,包括时间、参数、执行结果
- 危险操作确认:删除、修改等破坏性操作需要额外的确认参数
- 速率限制:防止 Agent 在异常情况下产生大量 API 调用
import asyncio
from functools import wraps
# 简单的速率限制装饰器
def rate_limit(max_calls: int, period: float):
calls = []
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
now = asyncio.get_event_loop().time()
calls[:] = [t for t in calls if now - t < period]
if len(calls) >= max_calls:
raise Exception(f"速率限制:每 {period} 秒最多调用 {max_calls} 次")
calls.append(now)
return await func(*args, **kwargs)
return wrapper
return decorator
@rate_limit(max_calls=10, period=60.0)
async def call_github_api(url, **kwargs):
# 实际 API 调用逻辑
pass
5.3 常用 MCP Server 生态
目前已有大量开源 MCP Server 可直接使用:
| 系统 | MCP Server | 主要功能 |
|---|---|---|
| GitHub | modelcontextprotocol/servers/github | Issue、PR、代码搜索 |
| 文件系统 | modelcontextprotocol/servers/filesystem | 本地文件读写 |
| PostgreSQL | modelcontextprotocol/servers/postgres | SQL 查询执行 |
| Slack | modelcontextprotocol/servers/slack | 消息发送、频道管理 |
| Google Drive | modelcontextprotocol/servers/gdrive | 文件搜索、读取 |
| Puppeteer | modelcontextprotocol/servers/puppeteer | 浏览器自动化 |
| Memory | modelcontextprotocol/servers/memory | 知识图谱持久化 |
六、Dify + MCP 完整架构图
┌─────────────────────────────┐
│ 用户 / 业务系统 │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Dify 平台 │
│ ┌─────────────────────────┐ │
│ │ 工作流编排引擎 │ │
│ │ Start → LLM → Agent │ │
│ │ → HTTP → End │ │
│ └─────────────────────────┘ │
└──────────────┬──────────────┘
│ HTTP
┌──────────────▼──────────────┐
│ MCP Proxy 层 │
│ (HTTP ↔ MCP 协议转换) │
└──┬───────┬────────┬─────────┘
│ │ │
┌────────▼─┐ ┌───▼────┐ ┌─▼────────┐
│ GitHub │ │ Jira │ │ 数据库 │
│ MCP Svr │ │MCP Svr │ │ MCP Svr │
└────┬─────┘ └───┬────┘ └─────┬────┘
│ │ │
┌────▼─────┐ ┌───▼────┐ ┌─────▼────┐
│ GitHub │ │ Jira │ │ MySQL / │
│ API │ │ API │ │ Postgres │
└──────────┘ └────────┘ └──────────┘
七、MCP vs 传统 Function Calling 对比
| 维度 | 传统 Function Calling | MCP 协议 |
|---|---|---|
| 标准化程度 | 各平台自定义,不统一 | 开放标准,统一规范 |
| 工具复用 | 需要为每个平台单独适配 | 一次开发,多处复用 |
| 动态发现 | 工具列表静态配置 | 运行时动态发现可用工具 |
| 资源访问 | 只支持函数调用 | 支持 Tools/Resources/Prompts |
| 安全模型 | 依赖调用方实现 | 协议层内置权限控制 |
| 生态丰富度 | 平台各自为政 | 快速增长的开源生态 |
总结
MCP 协议正在成为 AI Agent 与外部系统交互的事实标准。它解决的核心问题是:
如何让 Agent 以标准化、安全的方式操作任意外部系统,而不是为每个系统单独开发集成代码?
结合 Dify 强大的工作流编排能力,MCP 让我们可以:
- 快速接入:利用现有 MCP Server 生态,分钟级集成主流系统
- 灵活编排:在 Dify 工作流中自由组合多个 MCP 工具
- 安全可控:MCP 协议层提供标准的权限隔离和审计能力
- 持续扩展:新增系统只需开发对应的 MCP Server,无需修改 Agent 逻辑
随着 MCP 生态的持续壮大,"任何系统都可以成为 Agent 的操作对象"这一愿景正在快速落地。对于正在构建企业级 AI Agent 的开发者来说,现在正是拥抱 MCP 的最佳时机。
💡 系列下一篇预告:《Dify 多 Agent 协作:如何设计 Supervisor + Worker 架构实现复杂任务分解》,敬请期待!
更多推荐
所有评论(0)