内容
# iap-mcp-proxy
客户端桥接器,允许通用MCP客户端(Claude Desktop、Claude Code、Cursor等)连接到由**Google Cloud Identity-Aware Proxy(IAP)**保护的远程MCP服务器。
Google Cloud对应[`aws/mcp-proxy-for-aws`](https://github.com/aws/mcp-proxy-for-aws)。
```
┌──────────────┐ stdio ┌───────────────┐ HTTPS + ID token ┌─────┐ ┌────────────┐
│ MCP客户端 │ ────────────────────► │ iap-mcp-proxy │ ───────────────────► │ IAP │ ───► │ MCP服务器 │
│ (Claude等)│ │ (此工具) │ 代理授权 │ │ │ (Cloud Run)│
└──────────────┘ └───────────────┘ └─────┘ └────────────┘
```
IAP需要谷歌颁发的OIDC ID令牌;MCP规范的OAuth 2.1流程无法生成一个,因此通用客户端会收到401/重定向并停止。 此代理在本地运行,获取和刷新谷歌凭证,将其附加为`Proxy-Authorization`(IAP使用并剥离此标头),并将MCP流量(可流式传输的HTTP)转发到上游。
## 安装
```sh
go install github.com/knwoop/iap-mcp-proxy/cmd/iap-mcp-proxy@latest
```
或者从[Releases](https://github.com/knwoop/iap-mcp-proxy/releases)获取二进制文件(darwin/linux/windows,amd64/arm64)。
## 快速入门
1. 确保您拥有IAP接受的凭证(参见[凭证](#凭证)):
```sh
gcloud auth application-default login
```
2. 将代理添加到您的MCP客户端配置中。Claude Desktop:
```json
{
"mcpServers": {
"internal-tools": {
"command": "iap-mcp-proxy",
"args": [
"--audience", "1234567890-abc.apps.googleusercontent.com",
"https://mcp.internal.example.com/mcp"
]
}
}
}
```
Claude Code:
```sh
claude mcp add internal-tools -- iap-mcp-proxy \
--audience 1234567890-abc.apps.googleusercontent.com \
https://mcp.internal.example.com/mcp
```
## 用法
```
iap-mcp-proxy [flags] <UPSTREAM_URL>
```
| 标志 | 环境变量 | 默认值 | 描述 |
|---|---|---|---|
| `--audience` | `IAP_MCP_AUDIENCE` | `UPSTREAM_URL`的来源 | OIDC令牌受众。 LB支持的IAP:IAP OAuth客户端ID(`NNN.apps.googleusercontent.com`)。直接Cloud Run IAP:取决于OAuth客户端 - 参见[支持的IAP配置](#支持的iap配置)。 |
| `--credentials` | `IAP_MCP_CREDENTIALS` | `auto` | `auto`,`adc`,`impersonate`,`oauth`,`signjwt`。 |
| `--impersonate-service-account` | `IAP_MCP_IMPERSONATE_SA` | — | 目标SA电子邮件。`impersonate`生成ID令牌作为它;`signjwt`生成自签名JWT作为它。 |
| `--downstream-auth` | `IAP_MCP_DOWNSTREAM_AUTH` | — | 作为上游`Authorization`标头转发的值。支持`env:VAR_NAME`间接寻址,以便在客户端配置文件中隐藏密钥。 |
| `--refresh-margin` | `IAP_MCP_REFRESH_MARGIN` | `5m` | 在到期前刷新ID令牌。 |
| `--timeout` | `IAP_MCP_TIMEOUT` | `120s` | 上游超时:JSON响应的总时间,SSE流的空闲时间(两次读取之间的时间)- 因此,长运行的流式调用不会在数据或保持活动信号到达时被终止。 |
| `--log-level` | `IAP_MCP_LOG` | `warn` | `debug` / `info` / `warn` / `error`。日志仅输出到stderr。 |
| `--version` | — | — | 打印版本并退出。 |
## 凭证
使用`--credentials=auto`(默认值)时,按以下顺序尝试源:
1. **模拟** - 如果设置了`--impersonate-service-account`,则通过IAM凭证API(`generateIdToken`)使用您的ADC作为基本身份模拟ID令牌。需要在目标SA上具有`roles/iam.serviceAccountTokenCreator`权限。在CI和共享团队设置中最佳。
2. **ADC** - 如果应用程序默认凭证是服务帐户密钥或工作负载凭证,则直接模拟ID令牌。
3. **桌面OAuth** - gcloud *用户*凭证无法模拟任意受众ID令牌,因此代理会回退到已安装应用程序的OAuth流程:第一次运行时会打开浏览器进行谷歌登录;刷新令牌存储在您的操作系统密钥链中(备用:用户配置文件目录下的`0600`文件)。需要与IAP资源相同项目中的桌面OAuth客户端,通过`IAP_MCP_OAUTH_CLIENT_ID` / `IAP_MCP_OAUTH_CLIENT_SECRET`提供 - 参见[谷歌关于IAP编程认证的文档](https://cloud.google.com/iap/docs/authentication-howto)。
这些OIDC模式需要IAP使用**自定义**OAuth客户端(LB支持的IAP或具有单独允许列表的托管客户端服务)。对于现代托管客户端直接Cloud Run IAP,`--credentials=signjwt`是最简单的路线 - 它不需要OAuth客户端(参见[支持的IAP配置](#支持的iap配置))。
主体必须在IAP资源上持有`roles/iap.httpsResourceAccessor`权限。
## 支持的IAP配置
代理必须呈现的凭证取决于IAP如何部署 - 主要取决于IAP是否使用**自定义**OAuth客户端还是谷歌的**托管**客户端。 *令牌类型*很重要,而不仅仅是受众。
| IAP部署 | OAuth客户端 | 代理发送的令牌 | `--audience` | 状态 |
|---|---|---|---|---|
| 在外部HTTPS负载均衡器后面 | 自定义 | OIDC ID令牌(`impersonate` / `adc` / `oauth`) | IAP OAuth客户端ID(`NNN.apps.googleusercontent.com`) | ✅ 支持 - OIDC接受度已针对实时IAP验证 |
| 直接Cloud Run IAP | 谷歌托管(今天启用IAP时的默认值) | 自签名服务帐户JWT(`signjwt`) | 确切的`*.run.app`端点(或`+ /*`) | ✅ 支持 - IAP在此处拒绝OIDC令牌,因此使用`--credentials=signjwt` |
| 直接Cloud Run IAP | 自定义/允许列表 | OIDC ID令牌 | OAuth客户端ID | ⚠️ 预期工作,但尚未验证 |
- 由于谷歌关闭了IAP OAuth Admin API(2026年3月),新安全的应用默认为**谷歌托管**OAuth客户端,并且该配置**拒绝谷歌颁发的OIDC ID令牌**(`Invalid IAP credentials: Invalid bearer token. Invalid JWT audience.`),适用于每个受众。最简单的编程访问是**自签名服务帐户JWT**(`iss`=`sub`=SA电子邮件,通过IAM `signJwt` API签名),受众设置为规范的`run.app` URL,带有路径或`/*`通配符。使用`--credentials=signjwt --impersonate-service-account=<SA>`;`--audience`默认为确切的上游端点(传递`<origin>/*`以涵盖所有路径)。需要服务帐户凭证API(`gcloud services enable iamcredentials.googleapis.com`)。
- 托管客户端IAP也可以通过单独的**允许列表**OAuth客户端(控制台创建;参见[自定义OAuth配置](https://cloud.google.com/iap/docs/custom-oauth-configuration))接受OIDC ID令牌 - 然后`--credentials=impersonate` / `adc`与该客户端ID一起作为`--audience`工作。新自定义客户端仅限控制台(无API);现有客户端继续有效。
## 备注
- IAP令牌在`Proxy-Authorization`中传输,IAP使用并剥离 - 您的应用程序永远不会看到它。如果您的应用程序有自己的身份验证,请使用`--downstream-auth`传递它,它将原样转发为`Authorization`。
- 在401(或302进入谷歌登录)时,代理刷新令牌并重试一次;第二次失败作为JSON-RPC错误传递给MCP客户端,并在stderr上带有可操作的错误消息。
- 如果上游报告会话过期(HTTP 404 - 例如Cloud Run重新部署后),代理透明地重放缓存的`initialize`握手以获取新会话并重试请求;_stdio客户端永远不会注意到。
- 如果流式(SSE)响应在工具调用中间丢失,并且服务器用ID标记事件,则代理使用`Last-Event-ID`恢复它,而不是丢失响应。
- 在`initialize`之后,代理打开独立的GET SSE流,以便服务器发起的消息(`notifications/tools/list_changed`,采样/诱导请求,日志通知)到达客户端,如果流丢失,则使用`Last-Event-ID`重新连接。响应405(无独立流)的服务器被静默处理。
- 退出代码:`0`干净关闭,`1`致命错误(错误配置或无法恢复的运行时失败),`2`身份验证启动失败。
参见[`docs/setup-gcp.md`](docs/setup-gcp.md),了解在两种部署模式(直接Cloud Run IAP和IAP在全局外部应用程序负载均衡器后面)下设置IAP的信息。
## License
Apache-2.0