MCP 组合实战:把 GitHub、Figma、数据库和知识库接进 Agent 工作流
MCP(Model Context Protocol)让 Agent 可以连接外部工具和数据源,但单个 MCP Server 能力有限。本文展示如何组合多个 MCP Server(GitHub + Figma + PostgreSQL + Confluence),构建一个完整的 Agent 工作流:从设计稿获取需求、从数据库获取 Schema、从代码仓库获取上下文、从知识库获取业务规则,让 Agent 拥有全链路的信息访问能力。同时总结 MCP 组合使用中最容易踩的 5 个坑和解决方案。
一、为什么需要组合 MCP
1.1 单个 MCP 的局限
| MCP Server | 能力 | 局限 |
|---|---|---|
| GitHub MCP | 读写代码、PR、Issue | 不知道设计意图 |
| Figma MCP | 获取设计稿、组件规范 | 不知道实现细节 |
| Database MCP | 查询 Schema、数据 | 不知道业务规则 |
| Confluence MCP | 获取文档、需求 | 不知道代码实现 |
问题:单个 MCP 只能提供局部信息,Agent 无法理解全局上下文。
1.2 组合后的全链路能力
设计稿(Figma)
↓ 获取组件规范、交互说明
需求文档(Confluence)
↓ 获取业务规则、验收标准
数据库(PostgreSQL)
↓ 获取 Schema、数据样例
代码仓库(GitHub)
↓ 获取实现细节、历史变更
Agent 工作流
↓ 综合分析,生成代码
代码提交(GitHub)
↓ 创建 PR、触发 CI/CD价值:Agent 拥有全链路信息,生成的代码更符合设计、业务和技术要求。
二、MCP 组合架构
2.1 架构图
┌─────────────────────────────────────────────────────────┐
│ Agent 工作流 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ Figma │ │Confluence│ │PostgreSQL│ │ GitHub │ │
│ │ MCP │ │ MCP │ │ MCP │ │ MCP │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └───┬────┘ │
│ │ │ │ │ │
│ └──────────────┴──────────────┴─────────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ MCP Router │ │
│ │ (权限控制) │ │
│ └───────┬────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ Audit Logger │ │
│ │ (调用审计) │ │
│ └───────┬────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ Claude Code │ │
│ │ (Agent 核心) │ │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────┘2.2 配置文件
// .claude/mcp-config.json
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}",
"GITHUB_REPOS": "myorg/frontend,myorg/backend"
},
"allowedTools": [
"read_file",
"list_files",
"search_code",
"create_issue",
"create_pr"
],
"timeout": 30000,
"rateLimit": {
"maxRequests": 100,
"windowMs": 60000
}
},
"figma": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-figma"],
"env": {
"FIGMA_TOKEN": "${FIGMA_TOKEN}",
"FIGMA_FILES": "abc123,def456"
},
"allowedTools": [
"get_file",
"get_nodes",
"get_styles",
"get_components"
],
"timeout": 30000,
"rateLimit": {
"maxRequests": 50,
"windowMs": 60000
}
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "${DATABASE_URL}"
},
"allowedTools": [
"query",
"list_tables",
"describe_table"
],
"timeout": 10000,
"rateLimit": {
"maxRequests": 200,
"windowMs": 60000
},
"security": {
"readOnly": true,
"maxRows": 1000,
"blockedTables": ["users", "payments"]
}
},
"confluence": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-confluence"],
"env": {
"CONFLUENCE_URL": "https://myorg.atlassian.net/wiki",
"CONFLUENCE_TOKEN": "${CONFLUENCE_TOKEN}",
"CONFLUENCE_SPACES": "ENG,PROD"
},
"allowedTools": [
"search",
"get_page",
"list_spaces"
],
"timeout": 30000,
"rateLimit": {
"maxRequests": 100,
"windowMs": 60000
}
}
},
"router": {
"priority": ["github", "figma", "postgres", "confluence"],
"fallback": "github",
"timeout": 60000
},
"audit": {
"enabled": true,
"logFile": ".claude/mcp-audit.log",
"fields": [
"timestamp",
"server",
"tool",
"parameters",
"result_size",
"duration_ms",
"user"
],
"sensitiveFields": [
"password",
"token",
"secret",
"api_key"
]
}
}三、实战场景:从设计到代码
3.1 场景描述
任务:根据 Figma 设计稿实现"用户列表"页面
输入:
- Figma 设计稿链接
- Confluence 需求文档
- 数据库 users 表
- 代码仓库 src/pages/
输出:
- React 组件代码
- API 接口代码
- 单元测试
- PR 提交3.2 Agent 工作流
# workflow.yaml
steps:
- step: 1
name: "获取设计稿"
mcp: "figma"
tool: "get_file"
parameters:
fileKey: "abc123"
output:
- "组件结构"
- "样式规范"
- "交互说明"
- step: 2
name: "获取需求文档"
mcp: "confluence"
tool: "search"
parameters:
query: "用户列表页面需求"
space: "PROD"
output:
- "业务规则"
- "验收标准"
- "边界情况"
- step: 3
name: "获取数据库 Schema"
mcp: "postgres"
tool: "describe_table"
parameters:
table: "users"
output:
- "字段定义"
- "索引信息"
- "关联关系"
- step: 4
name: "获取代码上下文"
mcp: "github"
tool: "list_files"
parameters:
path: "src/pages/"
repo: "myorg/frontend"
output:
- "现有页面结构"
- "组件规范"
- "代码风格"
- step: 5
name: "生成代码"
agent: "claude-code"
input:
- "设计稿规范"
- "需求文档"
- "数据库 Schema"
- "代码上下文"
output:
- "UserList.tsx"
- "UserList.test.tsx"
- "api/users.ts"
- step: 6
name: "提交代码"
mcp: "github"
tool: "create_pr"
parameters:
repo: "myorg/frontend"
branch: "feature/user-list"
title: "feat: 实现用户列表页面"
description: "根据设计稿实现用户列表页面"
output:
- "PR 链接"
- "CI/CD 状态"3.3 实际执行示例
// Agent 执行的完整流程
// 1. 获取设计稿
const figmaData = await mcp.figma.get_file({
fileKey: "abc123",
depth: 2
});
console.log("设计稿信息:", {
components: figmaData.components.length,
styles: figmaData.styles.length,
pages: figmaData.pages.map(p => p.name)
});
// 2. 获取需求文档
const requirements = await mcp.confluence.search({
query: "用户列表页面需求",
space: "PROD",
limit: 5
});
console.log("需求文档:", {
pages: requirements.length,
titles: requirements.map(r => r.title)
});
// 3. 获取数据库 Schema
const schema = await mcp.postgres.describe_table({
table: "users",
includeIndexes: true,
includeRelations: true
});
console.log("数据库 Schema:", {
columns: schema.columns.length,
indexes: schema.indexes.length,
relations: schema.relations.length
});
// 4. 获取代码上下文
const codeContext = await mcp.github.list_files({
repo: "myorg/frontend",
path: "src/pages/",
recursive: true
});
console.log("代码上下文:", {
files: codeContext.length,
patterns: [...new Set(codeContext.map(f => f.extension))]
});
// 5. 生成代码(Agent 自动完成)
// Claude Code 会综合以上信息生成代码
// 6. 提交 PR
const pr = await mcp.github.create_pr({
repo: "myorg/frontend",
branch: "feature/user-list",
title: "feat: 实现用户列表页面",
body: `## 变更内容
根据设计稿实现用户列表页面。
### 设计稿
- Figma: https://figma.com/file/abc123
### 需求文档
- ${requirements[0].url}
### 实现细节
- 支持分页(每页 20 条)
- 支持搜索(用户名、邮箱)
- 支持排序(创建时间、最后登录时间)
- 响应式布局(移动端适配)
### 测试覆盖
- 单元测试:85%
- E2E 测试:关键流程覆盖`,
draft: false
});
console.log("PR 已创建:", pr.url);四、MCP 最容易踩的 5 个坑
4.1 坑 1:凭证过宽
问题:MCP Server 的 Token 权限过大,Agent 可以访问不该访问的资源。
典型案例:
// ❌ 错误配置
{
"github": {
"env": {
"GITHUB_TOKEN": "ghp_xxx" // 有所有仓库的写权限
}
}
}
// 问题:Agent 可以访问所有仓库,包括私有仓库解决方案:
// ✅ 正确配置
{
"github": {
"env": {
"GITHUB_TOKEN": "ghp_xxx", // 只有特定仓库的读权限
"GITHUB_REPOS": "myorg/frontend,myorg/backend" // 限制仓库范围
},
"allowedTools": [
"read_file",
"list_files",
"search_code"
// 不给 create_pr、merge_pr 等写权限
]
}
}最佳实践:
# credential-best-practices.yaml
principles:
- name: "最小权限"
description: "Token 只授予必要的权限"
example: "只读任务不给写权限"
- name: "范围限制"
description: "限制可访问的资源范围"
example: "GITHUB_REPOS 只列出需要的仓库"
- name: "凭证隔离"
description: "不同环境使用不同的 Token"
example: "开发、测试、生产使用不同的 Token"
- name: "定期轮换"
description: "Token 定期更换"
example: "每 90 天更换一次 Token"4.2 坑 2:工具返回太多
问题:MCP 工具返回的数据量过大,超出 Agent 的上下文窗口。
典型案例:
// ❌ 错误做法
const files = await mcp.github.list_files({
repo: "myorg/large-repo",
recursive: true // 返回 10000+ 个文件
});
// 问题:10000 个文件的信息超出上下文窗口解决方案:
// ✅ 正确做法
const files = await mcp.github.list_files({
repo: "myorg/large-repo",
path: "src/pages/", // 限制目录
recursive: false, // 不递归
limit: 100 // 限制数量
});
// 或者使用分页
const files = [];
let page = 1;
while (true) {
const result = await mcp.github.list_files({
repo: "myorg/large-repo",
path: "src/pages/",
page: page,
perPage: 50
});
files.push(...result.files);
if (result.files.length < 50) break;
page++;
}最佳实践:
# data-volume-best-practices.yaml
principles:
- name: "限制范围"
description: "使用 path、filter 等参数限制数据范围"
example: "只获取 src/pages/ 目录"
- name: "限制数量"
description: "使用 limit、perPage 等参数限制数量"
example: "每次最多获取 100 个文件"
- name: "分页获取"
description: "大数据集使用分页"
example: "每页 50 个,逐页获取"
- name: "摘要优先"
description: "先获取摘要,需要时再获取详情"
example: "先获取文件列表,需要时再读取内容"4.3 坑 3:超时无回退
问题:MCP Server 响应慢或超时,Agent 卡住。
典型案例:
// ❌ 错误做法
const data = await mcp.figma.get_file({
fileKey: "abc123"
});
// 问题:Figma API 慢,等待 60 秒后超时,Agent 卡住解决方案:
// ✅ 正确做法
const data = await Promise.race([
mcp.figma.get_file({ fileKey: "abc123" }),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Figma 超时")), 10000)
)
]).catch(err => {
console.warn("Figma 超时,使用缓存数据");
return getCachedFigmaData("abc123");
});配置方式:
{
"figma": {
"timeout": 10000,
"retry": {
"maxRetries": 3,
"delayMs": 1000
},
"fallback": {
"enabled": true,
"cacheTTL": 3600000,
"mockData": "./mocks/figma-data.json"
}
}
}最佳实践:
# timeout-best-practices.yaml
principles:
- name: "设置超时"
description: "所有 MCP 调用必须设置超时"
example: "timeout: 10000(10 秒)"
- name: "重试机制"
description: "超时后自动重试"
example: "最多重试 3 次,每次间隔 1 秒"
- name: "回退方案"
description: "超时后使用缓存或 Mock 数据"
example: "使用本地缓存的设计稿数据"
- name: "降级策略"
description: "关键 MCP 不可用时降级处理"
example: "Figma 不可用时使用文字描述"4.4 坑 4:上下文污染
问题:多个 MCP 返回的信息互相冲突,Agent 困惑。
典型案例:
Figma 设计稿:按钮颜色 #3B82F6(蓝色)
Confluence 需求:按钮颜色 #EF4444(红色)
问题:Agent 不知道用哪个颜色解决方案:
# context-priority.yaml
strategy: "定义 MCP 优先级"
priority:
- name: "设计稿(Figma)"
priority: 1
scope: "UI 样式、布局、交互"
- name: "需求文档(Confluence)"
priority: 2
scope: "业务规则、功能需求"
- name: "数据库(PostgreSQL)"
priority: 3
scope: "数据结构、字段定义"
- name: "代码仓库(GitHub)"
priority: 4
scope: "实现细节、代码规范"
conflict_resolution:
- rule: "UI 样式以设计稿为准"
example: "颜色、字体、间距以 Figma 为准"
- rule: "业务规则以需求文档为准"
example: "功能逻辑、边界条件以 Confluence 为准"
- rule: "数据结构以数据库为准"
example: "字段名、类型以 PostgreSQL 为准"最佳实践:
# context-management.yaml
principles:
- name: "明确优先级"
description: "定义不同 MCP 的优先级"
example: "设计稿 > 需求文档 > 数据库 > 代码"
- name: "明确职责"
description: "每个 MCP 负责特定的信息领域"
example: "Figma 负责 UI,Confluence 负责业务"
- name: "冲突检测"
description: "检测不同 MCP 的信息冲突"
example: "颜色和需求的颜色不一致时告警"
- name: "人工确认"
description: "冲突时请求人工确认"
example: "颜色冲突时询问设计师"4.5 坑 5:缺少调用审计
问题:不知道 Agent 调用了哪些 MCP、访问了哪些数据。
典型案例:
问题:Agent 访问了 users 表,但不知道为什么
结果:无法追踪数据访问,存在安全隐患解决方案:
// .claude/mcp-audit.json
{
"enabled": true,
"logFile": ".claude/mcp-audit.log",
"format": "json",
"fields": [
"timestamp",
"session_id",
"user",
"server",
"tool",
"parameters",
"result_size",
"duration_ms",
"status"
],
"sensitiveFields": [
"password",
"token",
"secret",
"api_key",
"ssn",
"credit_card"
],
"redaction": {
"enabled": true,
"replacement": "***REDACTED***"
},
"rotation": {
"enabled": true,
"maxSize": "100MB",
"maxFiles": 10
}
}审计日志示例:
// .claude/mcp-audit.log
[
{
"timestamp": "2024-01-15T10:30:00Z",
"session_id": "sess_abc123",
"user": "developer@example.com",
"server": "github",
"tool": "read_file",
"parameters": {
"repo": "myorg/frontend",
"path": "src/pages/UserList.tsx"
},
"result_size": 15234,
"duration_ms": 234,
"status": "success"
},
{
"timestamp": "2024-01-15T10:30:05Z",
"session_id": "sess_abc123",
"user": "developer@example.com",
"server": "postgres",
"tool": "query",
"parameters": {
"sql": "SELECT * FROM users LIMIT 10"
},
"result_size": 8923,
"duration_ms": 456,
"status": "success"
},
{
"timestamp": "2024-01-15T10:30:10Z",
"session_id": "sess_abc123",
"user": "developer@example.com",
"server": "figma",
"tool": "get_file",
"parameters": {
"fileKey": "abc123"
},
"result_size": 45678,
"duration_ms": 1234,
"status": "success"
}
]最佳实践:
# audit-best-practices.yaml
principles:
- name: "全量记录"
description: "记录所有 MCP 调用"
example: "包括成功的和失败的"
- name: "敏感信息脱敏"
description: "自动脱敏敏感字段"
example: "密码、Token、身份证号"
- name: "定期审计"
description: "定期审查审计日志"
example: "每周审查一次异常访问"
- name: "告警机制"
description: "异常访问自动告警"
example: "访问敏感表时告警"
- name: "日志保留"
description: "日志保留足够长时间"
example: "保留 90 天用于审计"五、凭证隔离方案
5.1 环境变量隔离
# .env.development
GITHUB_TOKEN=ghp_dev_xxx
FIGMA_TOKEN=figd_dev_xxx
DATABASE_URL=postgresql://dev:password@localhost:5432/dev_db
CONFLUENCE_TOKEN=conf_dev_xxx
# .env.production
GITHUB_TOKEN=ghp_prod_xxx
FIGMA_TOKEN=figd_prod_xxx
DATABASE_URL=postgresql://prod:password@prod-db:5432/prod_db
CONFLUENCE_TOKEN=conf_prod_xxx5.2 配置文件隔离
// .claude/mcp-config.dev.json
{
"mcpServers": {
"github": {
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}",
"GITHUB_REPOS": "myorg/frontend-dev"
}
},
"postgres": {
"env": {
"DATABASE_URL": "${DATABASE_URL}"
},
"security": {
"readOnly": false,
"blockedTables": []
}
}
}
}
// .claude/mcp-config.prod.json
{
"mcpServers": {
"github": {
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}",
"GITHUB_REPOS": "myorg/frontend"
}
},
"postgres": {
"env": {
"DATABASE_URL": "${DATABASE_URL}"
},
"security": {
"readOnly": true,
"blockedTables": ["users", "payments"]
}
}
}
}5.3 权限控制矩阵
| 环境 | GitHub | Figma | PostgreSQL | Confluence |
|---|---|---|---|---|
| 开发 | 读写(dev 仓库) | 只读 | 读写(dev 库) | 只读 |
| 测试 | 只读 | 只读 | 只读(test 库) | 只读 |
| 生产 | 只读(prod 仓库) | 只读 | 只读(屏蔽敏感表) | 只读 |
六、工具调用审计 JSON
6.1 审计日志结构
{
"audit_log": {
"version": "1.0",
"entries": [
{
"id": "audit_001",
"timestamp": "2024-01-15T10:30:00Z",
"session": {
"id": "sess_abc123",
"user": "developer@example.com",
"ip": "192.168.1.100"
},
"mcp": {
"server": "github",
"tool": "read_file",
"version": "1.2.0"
},
"request": {
"parameters": {
"repo": "myorg/frontend",
"path": "src/pages/UserList.tsx",
"ref": "main"
},
"size_bytes": 234
},
"response": {
"status": "success",
"size_bytes": 15234,
"duration_ms": 234
},
"context": {
"task_id": "task_xyz789",
"workflow_step": 4,
"purpose": "获取代码上下文"
}
}
]
}
}6.2 审计分析脚本
# scripts/analyze-mcp-audit.py
import json
from datetime import datetime, timedelta
from collections import defaultdict
def analyze_audit_log(log_file):
"""分析 MCP 审计日志"""
with open(log_file) as f:
entries = json.load(f)["audit_log"]["entries"]
# 统计各 MCP Server 的调用次数
server_stats = defaultdict(lambda: {"count": 0, "total_duration": 0})
for entry in entries:
server = entry["mcp"]["server"]
server_stats[server]["count"] += 1
server_stats[server]["total_duration"] += entry["response"]["duration_ms"]
# 统计各工具的调用次数
tool_stats = defaultdict(int)
for entry in entries:
tool = f"{entry['mcp']['server']}.{entry['mcp']['tool']}"
tool_stats[tool] += 1
# 检测异常访问
anomalies = []
for entry in entries:
# 检测访问敏感表
if entry["mcp"]["server"] == "postgres":
sql = entry["request"]["parameters"].get("sql", "")
if "users" in sql or "payments" in sql:
anomalies.append({
"type": "sensitive_table_access",
"entry": entry,
"severity": "HIGH"
})
# 检测大量数据访问
if entry["response"]["size_bytes"] > 1000000: # 1MB
anomalies.append({
"type": "large_data_access",
"entry": entry,
"severity": "MEDIUM"
})
# 检测慢查询
if entry["response"]["duration_ms"] > 5000: # 5 秒
anomalies.append({
"type": "slow_query",
"entry": entry,
"severity": "MEDIUM"
})
# 生成报告
report = {
"summary": {
"total_calls": len(entries),
"servers": dict(server_stats),
"tools": dict(tool_stats),
"anomalies": len(anomalies)
},
"anomalies": anomalies,
"recommendations": []
}
# 生成建议
if anomalies:
report["recommendations"].append(
f"发现 {len(anomalies)} 个异常访问,建议审查"
)
for server, stats in server_stats.items():
avg_duration = stats["total_duration"] / stats["count"]
if avg_duration > 1000:
report["recommendations"].append(
f"{server} 平均响应时间 {avg_duration:.0f}ms,建议优化"
)
return report
if __name__ == "__main__":
report = analyze_audit_log(".claude/mcp-audit.log")
print(json.dumps(report, indent=2))七、真实经验与踩坑
7.1 第一次翻车:Figma Token 权限过大
场景:给 Agent 配置了 Figma Token,有所有设计稿的编辑权限。
结果:Agent 误操作,删除了一个设计稿的组件。
教训:
- MCP Token 必须遵循最小权限原则
- 只读任务不给写权限
- 定期审查 Token 权限
7.2 第二次翻车:数据库查询返回 10 万行
场景:Agent 查询 users 表,没有加 LIMIT,返回 10 万行数据。
结果:超出上下文窗口,Agent 崩溃。
教训:
- 所有数据库查询必须加 LIMIT
- 使用 MCP 的 security.maxRows 配置限制返回行数
- 大数据集使用分页
7.3 第三次翻车:设计和需求冲突
场景:Figma 设计稿按钮颜色是蓝色,Confluence 需求文档是红色。
结果:Agent 困惑,生成的代码颜色不对。
教训:
- 必须定义 MCP 优先级(设计稿 > 需求文档)
- 冲突时请求人工确认
- 在 Prompt 中明确说明优先级
八、总结
MCP 组合使用可以让 Agent 拥有全链路的信息访问能力,但需要注意 5 个关键问题:
- 凭证过宽:遵循最小权限原则,限制 Token 权限和资源范围
- 工具返回太多:使用 limit、filter 等参数限制数据量,使用分页
- 超时无回退:设置超时、重试机制和回退方案
- 上下文污染:定义 MCP 优先级,冲突时请求人工确认
- 缺少调用审计:全量记录 MCP 调用,敏感信息脱敏,定期审计
最佳实践:
- 凭证隔离:不同环境使用不同的 Token
- 权限控制:生产环境只读,屏蔽敏感表
- 审计日志:全量记录,敏感字段脱敏
- 降级策略:关键 MCP 不可用时有回退方案
最终建议:
MCP 组合是 Agent 工作流的核心基础设施,但必须谨慎配置和管理。把 MCP 当作"外部系统接口",遵循"最小权限、只读优先、全量审计"的原则。
九、系列导航
上一篇:Lab 011:Agent 做大型重构的上限在哪里? 下一篇:生产级 Agent 平台路线图:从 CLI 试点到研发基础设施