Amazon Quick + MCP 远程服务实战:让 AI 助手直接操作飞书,从设计到落地
Amazon Quick + MCP 远程服务实战:让 AI 助手直接操作飞书,从设计到落地
"帮我在飞书上建一个项目群,把这 5 个人拉进去,然后发一条启动通知。"
这种话以前只能对人说。现在可以对 Amazon Quick 说了——它真的能帮你执行。
但实现起来没那么简单。飞书有 200+ 开放 API,全塞给 AI 作为工具上下文?token 直接爆炸。多步操作的准确率怎么保证?token 泄露了怎么办?
亚马逊云科技官博最近发了一篇实践文章,讲了怎么用 Bedrock AgentCore 构建远程 MCP 服务来解决这些问题。我看完之后照着搭了一套,记录一下关键设计决策和踩的坑。
问题:200+ 工具的上下文膨胀
飞书开放平台的 API 覆盖了消息、群组、文档、日历、审批、多维表格等十几个模块。如果把所有工具描述一股脑塞进 system prompt:
- 200+ 工具 × 平均 300 token/工具描述 ≈ 60,000 token 上下文
- Claude 每次请求都要"读"这些描述来决策用哪个
- 绝大多数工具在当前对话中完全用不上
- 成本和延迟都往上飙
方案:Meta Tool + 按需加载
核心思路是分层注册。不把 200+ 工具全给 AI,而是给它一个"工具索引"(Meta Tool),让它先查询有哪些能力,再按需加载具体工具。
用户请求
↓
AI 调用 Meta Tool → "飞书消息相关能力有哪些?"
↓
返回 5 个具体工具描述
↓
AI 选择具体工具执行 → send_message / create_group
这样每次对话只加载用得上的工具,上下文从 60K token 降到 3-5K token。
架构设计
整体架构分三层:
Amazon Quick (AI 助手)
↓ MCP 协议
Bedrock AgentCore (远程 MCP 服务)
↓ REST API
飞书开放平台
Bedrock AgentCore 做了什么
AgentCore 在这里的角色是远程 MCP 服务器。它负责:
- 工具注册和发现 — 管理所有飞书 API 的工具描述
- 认证和鉴权 — OAuth PKCE + HMAC 签名
- 执行代理 — 接收 AI 的工具调用请求,转发到飞书 API
- 结果格式化 — 把飞书 API 的原始响应转成 AI 能理解的格式
为什么用远程 MCP 不用本地
本地 MCP(每个用户电脑跑一个 MCP server)有几个问题:
- 需要用户本地安装和配置
- 飞书 token 存在用户电脑上,安全风险大
- 不同用户的权限难以统一管理
- 无法集中监控和审计
远程 MCP 服务部署在 AWS 上,统一管理认证和权限。Amazon Quick 通过 MCP 协议与之通信。
关键设计:token 安全
飞书 API 调用需要 access_token。这个 token 怎么管理,直接影响系统安全。
方案:OAuth PKCE + HMAC 域分离签名
import hashlib
import hmac
import time
import secrets
class TokenManager:
"""飞书 token 安全管理"""
def __init__(self, app_id: str, app_secret: str, signing_key: bytes):
self.app_id = app_id
self.app_secret = app_secret
self.signing_key = signing_key
def get_tenant_token(self) -> str:
"""获取租户级别的 access_token"""
import requests
resp = requests.post(
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
json={"app_id": self.app_id, "app_secret": self.app_secret}
)
return resp.json()["tenant_access_token"]
def sign_request(self, user_id: str, action: str, timestamp: int) -> str:
"""HMAC 域分离签名 — 防止跨用户/跨操作伪造"""
message = f"{user_id}|{action}|{timestamp}"
return hmac.new(
self.signing_key,
message.encode(),
hashlib.sha256
).hexdigest()
def verify_request(self, user_id: str, action: str,
timestamp: int, signature: str) -> bool:
"""验证请求签名,防止重放攻击"""
# 检查时间戳(5分钟有效)
if abs(time.time() - timestamp) > 300:
return False
expected = self.sign_request(user_id, action, timestamp)
return hmac.compare_digest(expected, signature)
关键点:
- 域分离 — 签名里包含 user_id 和 action,防止 A 用户的签名被 B 用户复用
- 时间戳 — 5 分钟有效期,防重放
- HMAC-SHA256 — 签名密钥存在服务端,客户端拿不到
Meta Tool 实现
Meta Tool 本质上是一个"工具目录"服务:
from typing import Dict, List
# 工具分类注册表
TOOL_REGISTRY: Dict[str, List[dict]] = {
"messaging": [
{"name": "send_message", "description": "发送消息到指定会话"},
{"name": "reply_message", "description": "回复指定消息"},
{"name": "recall_message", "description": "撤回已发送的消息"},
],
"group": [
{"name": "create_group", "description": "创建群组"},
{"name": "add_members", "description": "添加群成员"},
{"name": "set_group_name", "description": "修改群名称"},
],
"calendar": [
{"name": "create_event", "description": "创建日历事件"},
{"name": "list_events", "description": "查询日历事件列表"},
{"name": "update_event", "description": "更新日历事件"},
],
"docs": [
{"name": "create_doc", "description": "创建云文档"},
{"name": "update_doc", "description": "更新文档内容"},
{"name": "share_doc", "description": "分享文档给指定用户"},
]
}
def meta_tool_query(category: str = None, keyword: str = None) -> List[dict]:
"""Meta Tool: AI 调用此函数发现可用工具
Args:
category: 按分类筛选 (messaging/group/calendar/docs)
keyword: 按关键词搜索工具描述
Returns:
匹配的工具列表(含完整参数定义)
"""
results = []
for cat, tools in TOOL_REGISTRY.items():
if category and cat != category:
continue
for tool in tools:
if keyword and keyword not in tool["description"]:
continue
results.append({"category": cat, **tool})
return results
AI 的调用流程变成:
- 用户说"帮我建个群" → AI 调用
meta_tool_query(category="group") - 拿到
create_group、add_members等工具描述 - AI 选择
create_group并填入参数执行
多步编排的准确性
实际任务往往需要多步操作。比如"建群+拉人+发消息":
async def execute_multi_step(steps: List[dict], context: dict):
"""多步操作编排器"""
results = {}
for step in steps:
tool_name = step["tool"]
params = step["params"]
# 支持步骤间参数传递
# 比如 create_group 返回 group_id,add_members 需要用它
for key, value in params.items():
if isinstance(value, str) and value.startswith("$"):
ref = value[1:] # "$create_group.group_id" → "create_group.group_id"
ref_step, ref_field = ref.split(".")
params[key] = results[ref_step][ref_field]
# 执行当前步骤
result = await call_feishu_api(tool_name, params, context)
results[step["id"]] = result
# 失败则中断(不做半截操作)
if not result.get("success"):
return {"status": "failed", "failed_step": step["id"],
"error": result.get("error"), "completed": results}
return {"status": "success", "results": results}
部署到 AgentCore
把整套 MCP 服务部署为 AgentCore 托管的远程服务:
# 打包部署
aws bedrock-agentcore create-mcp-service \
--service-name "feishu-mcp" \
--description "飞书操作 MCP 服务" \
--runtime "python3.12" \
--handler "main.handler" \
--source-code-path "./feishu_mcp/" \
--environment-variables '{
"FEISHU_APP_ID": "cli_xxxx",
"SIGNING_KEY_ARN": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:feishu-signing-key"
}' \
--region ap-northeast-1
敏感配置(app_secret、signing_key)存在 Secrets Manager 里,运行时通过 IAM Role 获取。
踩坑记录
-
飞书 token 有效期 2 小时 — 必须做自动刷新。我用了一个后台 timer 每 90 分钟刷新一次。
-
消息格式兼容 — 飞书消息支持富文本、卡片、Markdown 等多种格式。AI 生成的纯文本消息需要包装成飞书消息格式。
-
权限粒度 — 不是所有用户都能操作所有群。Meta Tool 返回工具列表时要根据当前用户的权限过滤。
参考链接
- Amazon Quick 官方文档:https://docs.aws.amazon.com/quick/latest/userguide/
- Bedrock AgentCore MCP 服务:https://docs.aws.amazon.com/bedrock/latest/userguide/agentcore-mcp.html
- 飞书开放平台文档:https://open.feishu.cn/document/home/index
- MCP 协议规范:https://spec.modelcontextprotocol.io/
- AWS 中国官博原文:https://aws.amazon.com/cn/blogs/china/amazon-quick-build-mcp-service-design-practice/

浙公网安备 33010602011771号