MCP Server 真上云以后,我最不建议做的事就是继续用 API Key:Cloud Run 这套 OAuth + IAM 值得照着抄

昨天 Google Cloud 的 Wednesday Build Hour 主题之一就是:

Build and Deploy MCP Servers on Google Cloud

这个方向非常现实。

过去一年大量 MCP Server 都是这样跑的:

本地
stdio
开发者机器

Demo 没问题。

一旦要给整个团队、多个 Agent、生产流量使用,问题立刻变了:

谁能连?
谁能调用哪个 Tool?
用谁的身份?
能不能写?
怎么审计?
怎么撤权?
怎么限流?

MCP Server 上云真正难的不是:

docker build

而是身份。

Google 当前 Cloud Run 的远程 MCP 文档里有一个我很赞同的设计:Cloud Run remote MCP 不接受 API Key,认证和授权走 OAuth 2.0 + IAM。

而且官方明确建议:

给使用 MCP Tool 的 Agent 建独立身份。

我觉得这比“把 MCP 部署到 Cloud Run”本身更值得抄。

为什么我不喜欢生产 MCP 继续用一把 API Key

很多内部服务一开始都这么做:

Authorization: Bearer sk-internal-xxx

所有 Agent 共用。

简单。

然后半年以后你会发现:

Sales Agent
Finance Agent
Coding Agent
HR Agent

拿的是同一把 Key。

这时你已经回答不了:

谁调用了 delete?
哪个 Agent 读了这个客户?
某个 Agent 离职后怎么撤权?
这次调用到底代表哪个用户?

API Key 只能告诉你:

“知道这个 Secret 的东西进来了”

它很难表达:

身份
角色
资源
动作
用途

Agent 越多,这个问题越严重。

Cloud Run MCP 当前的做法很清楚:OAuth 2.0 + IAM

Google 文档里对远程 Cloud Run MCP 的认证写得很直接:

OAuth 2.0
+
Identity and Access Management

支持 Google Cloud Identity。

而不是:

API Key

这个选择其实代表了一种架构思想:

Agent 不是匿名脚本
Agent 是一个需要身份的主体

我觉得未来生产 Agent 基本都会走这条路。

最容易做错的是“用人的身份跑所有 Agent”

例如开发者在自己电脑上:

gcloud auth login

然后 Agent 用这个人的 Token 调 MCP。

测试很方便。

生产很危险。

因为最后权限变成:

张三能做什么
Agent 就能做什么

而人的权限往往比 Agent 需要的大得多。

更合理的是:

一个 Agent / 一类 Agent
拥有独立 Service Identity

例如:

sales-research-agent@...
deployment-agent@...
invoice-agent@...

这样权限可以按能力收缩。

Google 甚至把 Cloud Run MCP Scope 分成只读和读写

当前文档里有两个非常清晰的 Scope:

只读:

https://www.googleapis.com/auth/run.readonly

读写:

https://www.googleapis.com/auth/run

这种区分看起来很基础,但很多 Agent Tool 设计根本没有。

Tool 往往只有:

cloud_run

然后里面既可以:

list_services

也可以:

delete_service
deploy_revision
update_traffic

模型只要拿到这个 Tool,就拿到一整套能力。

我更建议从 Registry 层就拆:

cloudrun.read
cloudrun.deploy
cloudrun.traffic.update
cloudrun.delete

每个 Capability 有自己的风险等级和 Scope。

Capability 不应该等于 Tool Name

这是 MCP 进入企业以后必须解决的问题。

MCP Server 暴露:

tools/list

返回若干 Tool。

但企业治理真正需要的是:

Capability

例如:

{
  "capability": "cloudrun.service.read",
  "mcp_server": "cloudrun-prod",
  "tool": "get_service",
  "risk": "LOW",
  "oauth_scope": [
    "https://www.googleapis.com/auth/run.readonly"
  ]
}

另一个:

{
  "capability": "cloudrun.service.delete",
  "mcp_server": "cloudrun-prod",
  "tool": "delete_service",
  "risk": "CRITICAL",
  "approval": "REQUIRED"
}

这比把 MCP Tool 原样扔给模型安全得多。

一个 Agent 最好不要拥有长期 Token

正确流程更像:

Agent Run
↓
Policy Engine
↓
需要 cloudrun.read
↓
Credential Broker
↓
签发短期凭证
↓
调用 MCP
↓
凭证过期

不要:

把长期 Service Account Key
塞进 Agent 环境变量

长期 Key 一旦:

  • 日志泄露;
  • Prompt Injection 诱导读取;
  • 沙箱逃逸;
  • 镜像泄露;

影响时间可能非常长。

短期身份至少能收敛窗口。

本地 MCP Client 怎么安全连远程 Server

Cloud Run 文档给出的一个实用方式是:

gcloud run services proxy mcp-server \
  --region=us-central1

这个 Proxy 会在本地建立:

127.0.0.1

并使用当前身份为请求完成认证转发。

开发阶段很方便。

但我会把它限制在:

开发者本地调试

而不是生产架构。

生产 Agent 到 MCP 应该直接服务间认证。

Agent 和 MCP Server 都跑 Cloud Run 时,我会优先服务到服务认证

Cloud Run 文档给了三种思路:

Sidecar
Service-to-Service
Cloud Service Mesh

我的判断:

Sidecar

适合:

Tool 只服务一个 Agent
强绑定
低延迟
不需要独立扩缩容

独立 Service

更适合:

多个 Agent 共用
独立部署
独立版本
独立扩缩容

Service Mesh

适合:

MCP 数量多
服务间策略复杂
已有 Mesh 基础设施

不是越复杂越高级。

如果只有两个 MCP Server,为了“企业级”强行上 Mesh,可能只会增加运维。

MCP Server 应该默认 --no-allow-unauthenticated

Google 的 Cloud Run Tutorial 直接给出的部署命令就是:

gcloud run deploy mcp-server \
  --no-allow-unauthenticated \
  --region=us-central1 \
  --source .

这行参数很值得保留。

因为不少 Demo 为了快速连通会用:

allow unauthenticated

然后忘了改。

MCP Server 和普通 Web API 不一样。

它背后可能不是公开数据,而是:

数据库
云资源
工单
CRM
文件
代码

一旦匿名调用开放,风险非常高。

IAM 只能解决“谁能访问 Server”,还不够

这是一个容易产生错觉的地方。

假设某 Agent 有:

roles/run.invoker

它能调用 MCP Server。

但进入 MCP Server 以后,还要继续判断:

这个主体能不能调用 delete_service?

所以至少两层:

Layer 1:
MCP Server Admission

Layer 2:
Tool Authorization

不能因为通过了 Cloud Run IAM,就让里面所有 Tool 全开。

我会在 MCP Gateway 前加一层 Policy

请求进入:

Agent
→ MCP Gateway
→ Policy
→ MCP Server

Policy 输入:

{
  "subject": "deployment-agent",
  "tenant": "prod-a",
  "capability": "cloudrun.deploy",
  "resource": "service/orders",
  "purpose": "release-1842",
  "risk": "HIGH"
}

Policy 输出:

{
  "allowed": true,
  "approval_required": true,
  "max_calls": 1
}

还需要用户身份传播

一个 Agent 自己有 Service Identity,但它通常代表某个真实用户执行。

所以调用链最好同时保留:

Agent Identity
User Identity
Tenant
Purpose

例如审计:

{
  "agent": "deployment-agent",
  "user": "zhangsan",
  "tenant": "team-a",
  "tool": "deploy_service",
  "purpose": "release-1842"
}

否则所有日志最后都是:

deployment-agent 做的

无法追到真正授权来源。

MCP 的 List Tool 也需要治理

很多客户端会先调用:

tools/list

然后把所有 Tool Schema 塞给模型。

生产环境我更建议:

先根据身份和任务过滤
再返回 Tool List

例如 Finance Agent 根本看不到:

delete_cloud_run_service

不是看到了以后靠 Prompt 说:

“请不要调用。”

而是:

根本不暴露

这就是 Capability Discovery 应该做的事情。

Tool Schema 本身也可能泄露信息

一个内部 Tool 描述:

delete_customer_from_prod_legacy_crm

即使没调用,也暴露:

内部系统名
架构
业务对象

所以 Tool Discovery 也属于授权数据。

不能认为:

“只是 list,不算敏感。”

远程 MCP 还必须考虑版本

本地 stdio Server 更新以后,通常:

我自己机器自己用

影响范围有限。

远程 MCP Server 更新:

几十个 Agent 同时受影响

所以应该有:

mcp-server:v17
tool-schema:v6

并支持兼容期。

不要直接原地修改 Tool 参数:

- project_id
+ project

然后让所有 Agent 第二天一起坏掉。

我会给 MCP Server 做最小 SLO

至少监控:

Availability
P95 Latency
Tool Error Rate
Auth Denied Rate
Schema Error
Rate Limit
Cost

再加安全指标:

Unauthorized Capability
Cross-tenant Attempt
Approval Bypass
Credential Failure

Audit Log 必须和 Agent Run 对得上

每个调用带:

run_id
step_id
tool_call_id

最后才能从 Agent Trace 跳到 MCP Audit。

否则事故排查会变成:

Agent 日志有一次调用
Cloud 日志也有一次调用
但不知道是不是同一个

一个我现在会直接采用的生产模板

Agent
↓
Capability Resolver
↓
Policy Engine
↓
Short-lived Identity
↓
Remote MCP
↓
Backend API

每层职责非常清楚。

Capability Resolver:

发现能用什么

Policy:

能不能用

Identity:

以谁的权限用

MCP:

协议化调用

Backend:

真正执行

最后一个判断

MCP 最大的价值是把 Tool 接入方式标准化。

但标准化连接,并不等于标准化权限。

本地 Demo 阶段最重要的是:

能不能连上

生产阶段最重要的是:

谁能连
能看到什么
能做什么
代表谁
能做几次
出了问题怎么撤

Google Cloud 当前远程 MCP 直接采用 OAuth + IAM,而且建议 Agent 使用独立身份,我觉得这个方向是对的。

如果你的生产 MCP 还只有:

一个 URL
+
一把所有 Agent 共用的 API Key

下一步最值得改的不是再接第 20 个 Tool。

而是先把身份补上。


更多企业级 AI 应用、Agent、RAG 与模型工程化内容,我会继续整理在 智元界

https://www.zyentor.com/