当企业里的财务、客服、供应链等团队分别管理自己的 AWS 账户时,把数据集中复制到一个“AI 账户”通常不是理想方案:数据边界被打破,权限审计变复杂,团队也失去了对数据接口的控制。
一种更稳妥的架构是:在中央平台账户运行 AI Agent 和 Amazon Bedrock AgentCore Gateway,各业务账户继续持有数据,并通过 MCP Server 暴露经过授权的工具。Agent 获得统一的工具调用方式,但数据所有权、访问策略和审计记录仍留在业务账户内。
账户边界不变,统一的是工具入口
可以把整个系统分成两层:
- 平台账户:运行 Agent、AgentCore Gateway、工具目录和跨业务编排逻辑。
- 业务账户:运行面向本领域数据的 MCP Server,例如财务发票查询、客服工单检索或库存检查。
一次典型调用可以按下面的路径执行:
- 用户向平台账户中的 Agent 提问。
- Agent 根据任务选择 Gateway 暴露的 MCP 工具。
- 平台工作负载获取目标业务账户的短期凭证,或使用双方约定的服务身份。
- Gateway 或调用适配器访问对应的 MCP Server。
- 业务账户同时检查调用身份、工具权限和业务数据范围。
- MCP Server 只返回完成任务所需的数据,Agent 再合并多个账户的结果。
这里统一的是协议和工具发现机制,不是底层数据库。财务数据库不需要复制到平台账户,客服团队也可以独立升级自己的查询实现。
工具名称最好带有稳定的领域前缀,例如:
finance.find_invoicessupport.get_ticketsupply.check_inventory
这样能够减少不同 MCP Server 之间的命名冲突,也便于在 Gateway、日志和授权策略中建立工具级允许列表。
跨账户认证与业务授权必须分开
跨账户设计最容易出现的问题,是把“能够调用端点”误认为“能够读取所有数据”。建议把权限拆成三层:
- 角色信任策略:哪些平台角色可以进入业务账户。
- 基础设施权限:该角色可以调用哪个 MCP 端点。
- 业务数据权限:调用者可以执行哪些工具,以及能查看哪些客户、区域或记录。
下面是一个可以改造的 IAM 信任策略。它放在业务账户的 McpInvokerRole 上,允许平台账户中的 Agent 运行角色执行 AssumeRole。请替换账户 ID、角色名和 External ID:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/AgentPlatformRuntimeRole"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "agent-platform-prod"
}
}
}
]
}
业务账户还应给 McpInvokerRole 附加最小权限策略。例如,如果 MCP Server 位于启用了 IAM 鉴权的 API Gateway 后面,可以只允许调用指定环境和路径:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "InvokeFinanceMcpOnly",
"Effect": "Allow",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:222222222222:abc123xyz/prod/POST/mcp"
}
]
}
这两段策略只能证明平台工作负载有权访问 MCP 接口,不能代替业务授权。find_invoices 仍应根据经过验证的身份上下文限制客户、地区和字段。不要直接信任模型生成的 team_id、account_id 或 customer_id;这些约束应来自可信令牌、角色标签或服务端映射。
External ID 也不是密码。它可以降低混淆代理风险,但仍需配合精确的 Principal、短期凭证、CloudTrail 和端点访问日志。
实践:在业务账户运行一个最小 MCP Server
下面是一个可本地运行并改造成容器服务的示例。它使用 MCP Python SDK 和 SQLite 暴露财务发票查询工具。生产环境中应把 SQLite 换成业务账户内的 DynamoDB、Aurora 或其他数据源,并在入口层加入身份验证。
创建虚拟环境并安装依赖:
python3 -m venv .venv
source .venv/bin/activate
pip install "mcp[cli]"
将以下内容保存为 server.py:
import os
import sqlite3
from mcp.server.fastmcp import FastMCP
DB_PATH = os.getenv("DB_PATH", "invoices.db")
mcp = FastMCP("finance-mcp")
def initialize_database() -> None:
with sqlite3.connect(DB_PATH) as conn:
conn.execute(
"""
CREATE TABLE IF NOT EXISTS invoices (
invoice_id TEXT PRIMARY KEY,
customer_id TEXT NOT NULL,
amount REAL NOT NULL,
currency TEXT NOT NULL,
status TEXT NOT NULL
)
"""
)
conn.execute(
"""
INSERT OR IGNORE INTO invoices
(invoice_id, customer_id, amount, currency, status)
VALUES
('inv-1001', 'cust-42', 1200.50, 'USD', 'OPEN'),
('inv-1002', 'cust-42', 300.00, 'USD', 'PAID')
"""
)
@mcp.tool()
def find_invoices(customer_id: str, limit: int = 20) -> list[dict]:
"""Return invoices for one authorized customer."""
safe_limit = max(1, min(limit, 100))
with sqlite3.connect(DB_PATH) as conn:
conn.row_factory = sqlite3.Row
rows = conn.execute(
"""
SELECT invoice_id, customer_id, amount, currency, status
FROM invoices
WHERE customer_id = ?
ORDER BY invoice_id DESC
LIMIT ?
""",
(customer_id, safe_limit),
).fetchall()
return [dict(row) for row in rows]
if __name__ == "__main__":
initialize_database()
mcp.run(transport="streamable-http")
启动服务:
python server.py
不同版本的 MCP SDK 在默认主机、端口和路径上可能存在差异,部署前应以所使用版本的配置为准。生产化时还需要完成以下改造:
- 将服务部署到业务账户中的受控运行环境。
- 在 API Gateway、负载均衡器或服务入口验证调用身份。
- 从可信身份上下文推导允许访问的客户范围,而不是完全采用模型参数。
- 对工具输入设置长度、枚举和结果数量限制。
- 对敏感字段做脱敏,并记录工具名、调用身份和授权结果。
用短期凭证验证跨账户链路
假设业务账户通过启用了 AWS IAM 鉴权的 API Gateway 暴露 /prod/mcp,可以使用下面的命令验证平台账户是否能够承担角色并发送签名请求。
运行前需要安装 AWS CLI、jq,并使用支持 --aws-sigv4 的 curl。替换角色 ARN、区域和 MCP URL:
set -euo pipefail
ROLE_ARN="arn:aws:iam::222222222222:role/McpInvokerRole"
REGION="us-east-1"
MCP_URL="https://abc123xyz.execute-api.us-east-1.amazonaws.com/prod/mcp"
CREDS=$(aws sts assume-role \
--role-arn "$ROLE_ARN" \
--role-session-name "mcp-smoke-test" \
--external-id "agent-platform-prod")
export AWS_ACCESS_KEY_ID=$(echo "$CREDS" | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo "$CREDS" | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo "$CREDS" | jq -r '.Credentials.SessionToken')
curl --fail-with-body --silent --show-error \
--request POST \
--aws-sigv4 "aws:amz:${REGION}:execute-api" \
--user "${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}" \
--header "X-Amz-Security-Token: ${AWS_SESSION_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json, text/event-stream" \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": {
"name": "cross-account-smoke-test",
"version": "1.0.0"
}
}
}' \
"$MCP_URL"
示例中的 MCP 协议版本需要与实际客户端和服务端支持的版本一致。完整会话还可能需要保存响应中的会话 ID、发送 notifications/initialized,并处理 SSE 响应;正式接入时应优先使用 MCP SDK,而不是自己长期维护 JSON-RPC 会话代码。
多账户查询不等于让模型任意遍历数据
当一个问题涉及多个团队时,例如“哪些未支付发票关联了高优先级客服工单”,Agent 可以分别调用财务和客服工具,再按受控字段合并结果。但不建议允许模型无限制枚举客户或执行宽泛搜索。
更安全的做法包括:
- 每个请求设置允许调用的工具集合和最大调用次数。
- 对跨账户查询设置超时、并发上限和结果条数上限。
- 只传递关联所需的稳定标识符,避免把完整业务记录发送给其他账户。
- 对高风险工具增加人工确认或确定性工作流。
- 把 Gateway 的路由日志、STS 事件和业务账户审计日志关联到同一个请求 ID。
- 明确部分失败语义,例如财务查询成功但客服超时时,不应生成看似完整的结论。
Gateway 提供统一入口,但不应成为唯一安全边界。即使工具没有被展示给 Agent,业务账户的 MCP Server 也必须独立拒绝越权请求。
上线前检查清单
采用这类架构时,可以按以下顺序推进:
- 先选择一个只读、低敏感度的 MCP 工具验证端到端链路。
- 为不同业务账户创建独立调用角色,避免使用覆盖全部账户的共享角色。
- 使用短期凭证并限制会话持续时间。
- 在 Gateway 中使用稳定且带命名空间的工具名称。
- 同时测试身份拒绝、工具拒绝和数据范围拒绝,而不只是成功路径。
- 对提示注入、超大结果集、重复调用和部分失败进行故障演练。
- 确认删除 Gateway 配置后,业务账户端点仍不会对未授权身份开放。
这种模式的核心价值不是“让 Agent 看到所有数据”,而是在不取消账户边界的前提下,为 Agent 提供一致、可审计、可撤销的工具调用面。中央平台负责体验与编排,业务账户继续负责数据和最终授权,二者的职责越清晰,系统越容易长期演进。