关于 How does it work
Pomerium Chat 是一个演示应用,展示如何借助 Pomerium 通过上下文访问策略构建并保护远程 MCP 服务与客户端。适合希望在内部系统中安全开放 MCP 服务的开发者。项目包含基于 OpenAI 的聊天界面,并以 OAuth 2.1 方式处理认证。
主要功能
- 01通过 Pomerium 代理保护 MCP 服务
- 02OAuth 2.1 认证与上游令牌管理
- 03基于用户身份的访问策略
- 04MCP 工具调用审计日志配置
- 05基于 OpenAI 的聊天界面
适用场景
- 01向外部 MCP 客户端安全开放内部数据库服务
- 02在内部应用中为 LLM 调用提供受保护的 MCP 访问
- 03为需要上游 OAuth 的 MCP 服务统一处理登录
MCP 相关
该项目在 Pomerium 后面部署远程 MCP 服务,并演示客户端如何连接、列出并使用这些服务,同时通过 mcp-* 字段记录工具调用。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
欢迎使用 Pomerium Chat,这是一个简约的聊天应用,用于展示由 Pomerium 保护的远程 MCP 服务。
https://github.com/user-attachments/assets/50a17106-a471-4f64-be81-982c09d4689e
前提条件
- Linux 或 MacOS 主机
- Docker 和 Docker Compose
- 你的机器应向互联网开放 443 端口,以便从 LetsEncrypt 获取 TLS 证书,并让 OpenAI 调用你的 MCP 服务端点。
- OpenAI API 密钥
快速入门
环境变量
在根目录中创建 .env-mcp-app-demo 文件,并添加以下环境变量:
OPENAI_API_KEY=your_api_key_here
Pomerium 配置
更新 pomerium-config.yaml,将 YOUR-DOMAIN 替换为你拥有的子域名。为相关主机创建 A DNS 记录(或 *.YOUR-DOMAIN)。
默认情况下,访问策略仅允许邮箱地址属于 YOUR-DOMAIN 的用户访问。如果需要调整,请参阅策略语言参考。
Docker Compose
请参阅此代码库中的 docker-compose.yaml 文件。
docker compose up -d
其中包含一个演示用 SQLite 服务,需要一个演示数据库,例如 https://github.com/jpwhite3/northwind-SQLite3,并将其保存为 sqlite.db。
测试
现在,你应该可以访问 https://mcp-app-demo.YOUR-DOMAIN/。
此时会打开一个登录页面。登录后,你应该会被重定向到应用本身。
应该可以访问一个演示数据库服务(Database),其状态为 Connected。点击它即可在对话中使用。
现在,你可以提问,例如“我们每年的销售额是多少?”,并查看 OpenAI 大模型推理如何与运行在你计算机上的 MCP 数据库服务交互以获取答案。
工作原理
如需了解更多详情,请参阅 Model Context Protocol 能力概览。
令牌词汇
-
外部令牌 (TE): Pomerium 签发的面向外部的令牌,用于代表用户的会话。外部客户端(例如 Claude.ai、OpenAI 或你自己的应用)使用此令牌向受 Pomerium 保护的 MCP 服务进行身份验证。 示例:你提供给 LLM API 或 Agent 框架的令牌,用于允许其调用你的 MCP 服务。
-
内部令牌 (TI): Pomerium 代表用户从上游 OAuth2 提供商(例如 Notion、Google Drive、GitHub 等)获取的内部身份验证令牌。此令牌绝不会暴露给外部客户端。Pomerium 使用此令牌在代理请求到 MCP 服务时,向上游服务验证请求。
Pomerium 作为 MCP 客户端与服务之间的安全网关。它使用 OAuth 2.1 流程,为本地 HTTP MCP 服务提供身份验证和授权。当你的 MCP 服务需要访问要求 OAuth 令牌的上游 API(例如 Notion、Google Drive、GitHub 等)时,这种设置尤其有用。
它还支持你构建使用能够调用 MCP 服务的 Agent 框架或 LLM API 的内部应用,正如本代码库所展示的那样。
要了解此设置,我们来看看 MCP 客户端如何与受 Pomerium 保护的 MCP 服务通信。
1. 将内部 MCP 服务开放给远程客户端
https://github.com/user-attachments/assets/c7a809ad-6f0a-4ea3-94a2-6cf22ea6d27b
假设你想让外部 MCP 客户端(如 Claude.ai)访问内部 MCP 服务,同时确保访问安全。Pomerium 位于服务前端,负责管理所有传入请求的身份验证和授权。
这意味着你可以安全地与外部客户端共享对内部资源(如数据库)的访问权限,而无需将这些资源直接暴露到互联网。
像往常一样配置 Pomerium Route,并添加 mcp 属性,以表明此路由代表一个 Model Context Protocol 服务路由。
routes:
- from: https://my-mcp-server.your-domain.com
to: http://my-mcp-server.int:8080/mcp
name: My MCP Server
mcp:
server: {}
sequenceDiagram
actor U as User
participant C as MCP Client
participant P as Pomerium
participant S as MCP Server
U ->> C: Adds a server URL
C ->> P: Registers client, initiates auth
P ->> C: Sign-in URL
C ->> U: Redirect to sign-in URL
U ->> P: Sign-in
P ->> C: Redirect to client
C ->> P: Obtain Token
C ->> P: GET https://mcp-server Authorization: Bearer Token
P ->> S: Proxy request to MCP Server
2. MCP 服务需要上游 OAuth
如果你的 MCP 服务需要访问要求 OAuth 的上游服务(例如 GitHub 或 Google Drive),Pomerium 可以为你处理 OAuth 流程。流程如下:
- 用户在客户端(例如 Claude.ai)中添加 MCP 服务 URL。
- 客户端向 Pomerium 注册并开始身份验证。
- Pomerium 向客户端提供登录 URL,并将其显示给用户。
- 用户登录 Pomerium,然后被重定向到上游 OAuth 提供商。
- 用户通过上游提供商进行身份验证。提供商会向 Pomerium 返回一个内部令牌(TI)。
- Pomerium 完成登录,并将用户重定向回客户端。
- 客户端从 Pomerium 接收一个外部令牌(TE)。
- 客户端使用 TE 向 MCP 服务发送请求。
- Pomerium 会在需要时刷新上游令牌(TI),并将请求代理到 MCP 服务,同时在
Authorization标头中传递 TI。
主要优势:
- 外部客户端(如 Claude.ai)永远不会看到你的上游 OAuth 令牌。
- MCP 服务始终会收到有效的上游令牌。
- MCP 服务可以保持无状态,无需管理 OAuth 流程或令牌。
路由配置:
routes:
- from: https://github.your-domain
to: http://github-mcp.int:8080/mcp
name: GitHub
mcp:
server:
upstream_oauth2:
client_id: xxxxxxxxxxxx
client_secret: yyyyyyyyy
scopes: ['read:user', 'user:email']
endpoint:
auth_url: 'https://github.com/login/oauth/authorize'
token_url: 'https://github.com/login/oauth/access_token'
sequenceDiagram
actor U as User
participant C as MCP Client
participant O as Upstream OAuth
participant P as Pomerium
participant S as MCP Server
U ->> C: Adds a server URL
C ->> P: Registers client, initiates auth
P ->> C: Sign-in URL
C ->> U: Redirect to sign-in URL
U ->> P: Sign-in
P ->> U: Redirect to upstream OAuth
U ->> O: Authenticate with upstream OAuth
O ->> P: Return Internal Token (TI)
P ->> C: Redirect to client
C ->> P: Obtain External Token (TE)
C ->> P: GET https://mcp-server Authorization: Bearer (TE)
P ->> O: Refresh (TI) if necessary
P ->> S: Proxy request to MCP Server, Bearer (TI)
3. 从应用调用内部 MCP 服务
一些推理 API(例如 OpenAI API 和 Claude API)现在支持直接调用 MCP 服务。预计这一趋势将持续发展,许多 Agent 框架也在添加对 MCP 服务调用的支持。你也可以在应用中使用大模型的函数调用能力,手动实现 MCP 工具调用。所有这些方法都要求为 MCP 服务提供 Authorization: Bearer 外部令牌(TE),以便通过 Pomerium 安全地路由请求。
如果你正在构建自己的内部应用并需要获取此类令牌,Pomerium 为路由提供了 客户端 MCP 模式。通过设置 mcp.pass_upstream_access_token 选项,Pomerium 会向你的上游应用提供一个代表当前用户会话的 Authorization: Bearer 外部令牌(TE)。然后,你可以将此令牌传递给外部大模型或 Agent 框架,让它们根据你的授权策略访问 Pomerium 后方的 MCP 服务。
以下流程展示了这一过程,并假设用户已通过 Pomerium 完成身份验证:
sequenceDiagram
actor U as User
participant P as Pomerium
participant C as Your App Backend
participant S as MCP Server
participant I as LLM API
U ->> P: GET https://mcp-app-demo.your-domain.com
P ->> C: GET http://mcp-app-demo:3000 Authorization: Bearer (TE)
C ->> I: call tool https://mcp-server.your-domain Authorization: Bearer (TE)
I ->> P: GET https://mcp-server.your-domain Authorization: Bearer (TE)
P ->> S: GET https://mcp-server
路由配置示例:
routes:
- from: https://mcp-app-demo.your-domain.com
to: http://mcp-app-demo:3000
mcp:
client: {}
policy:
domain:
is: your-domain.com
- from: https://mcp-server.your-domain.com
to: http://mcp-server.int:8080/mcp
name: My MCP Server
mcp:
server: {}
policy:
domain:
is: your-domain.com
4. 从应用中列出可用的 MCP 服务
你可以向用户提供动态列表,列出与应用受同一个 Pomerium 实例保护的 MCP 服务。为此,请使用后端收到的同一个 Authorization: Bearer 令牌,向应用后端发送 HTTP 请求。响应将包含此 Pomerium 集群可用的每个上游 MCP 服务的列表及连接状态。
connected 属性表示当前用户是否拥有上游 OAuth 所需的全部内部令牌(如果需要):
- true – 用户拥有上游 OAuth 提供商所需的全部内部令牌,或者此服务不需要令牌。
- false – 用户需要先向上游 OAuth 提供商进行身份验证,才能访问此 MCP 服务。
后续章节将说明如何确保用户拥有所需的全部内部令牌。
GET https://mcp-demo-app.yourdomain.com/.pomerium/mcp/routes HTTP/1.1
Accept: application/json
Authorization: Bearer (TE)
Content-Type: application/json
{
"servers": [
{
"name": "DB",
"url": "https://db-mcp.your-domain.com",
"connected": true
},
{
"name": "GitHub",
"url": "https://github-mcp.your-domain.com",
"connected": false
}
]
}
5. 确保当前用户已向上游 OAuth2 提供商完成身份验证
如果目标 MCP 服务显示 connected: false,用户需要向所需的上游 OAuth2 提供商进行身份验证。
为此,请将用户的浏览器重定向到 MCP 服务路由上的特殊 /.pomerium/mcp/connect 路径(例如:https://db-mcp.your-domain.com/.pomerium/mcp/connect)。
添加 redirect_url 查询参数,使其指向应用中的页面——用户完成身份验证后应返回此页面,你也可以在此重新加载 MCP 服务列表及其连接状态。
**注意:**出于安全考虑,redirect_url 必须指向与某个 MCP Client 路由匹配的主机。
用户完成身份验证后,MCP 服务的 connected 状态应变为 true。
6. 获取用户详细信息
要访问已完成身份验证用户的身份信息和声明,你的 MCP 客户端应用和 MCP 服务都应读取 X-Pomerium-Assertion HTTP 标头。
此标头包含一个签名 JWT,其中包含用户信息。你可以对其进行解码和验证,以获取用户邮箱、姓名及其他声明等详细信息。
监控和审计 MCP 工具调用
Pomerium 为所有 MCP 工具调用提供全面的审计日志记录,让你能够监控和分析外部客户端如何与 MCP 服务交互。
查看审计日志
你可以使用以下命令实时监控 MCP 工具调用:
原始日志格式
查看实时生成的未筛选审计日志:
docker compose logs -f pomerium \
| grep --line-buffered "authorize" \
| grep --line-buffered "mcp-"
格式化的 JSON 格式
查看带语法高亮的格式化审计日志:
docker compose logs -f pomerium \
| grep --line-buffered "authorize" \
| grep --line-buffered "mcp-" \
| sed -u 's/^[^{]*//' \
| jq -C
注意:你需要安装 jq 才能格式化 JSON。
审计日志条目示例
以下是 MCP 工具调用审计日志条目的示例:
{
"level": "info",
"server-name": "all",
"service": "authorize",
"request-id": "b9375fd5-e220-9c1d-8f27-9b42249c2de5",
"user": "google-oauth2|110679203791094235151",
"email": "[email protected]",
"mcp-method": "tools/call",
"mcp-tool": "read_query",
"mcp-tool-parameters": {
"query": "SELECT SupplierID, CompanyName, Country FROM Suppliers WHERE Country IN ('Austria', 'Belgium', 'Denmark', 'Finland', 'France', 'Germany', 'Ireland', 'Italy', 'Netherlands', 'Norway', 'Portugal', 'Spain', 'Sweden', 'Switzerland', 'UK');"
},
"host": "northwind.mcp.pomerium.com",
"allow": true,
"allow-why-true": ["domain-ok"],
"deny": false,
"deny-why-false": [],
"time": "2025-06-27T20:56:44Z",
"message": "authorize check"
}
配置 MCP 审计日志记录
要启用全面的 MCP 审计日志记录,请在 Pomerium 配置中添加以下全局部分。请注意,mcp-* 字段是新的 authorize_log_fields,专为支持 MCP 而设计:
# for MCP observability
authorize_log_fields:
- request-id
- user
- email
- mcp-method
- mcp-tool
- mcp-tool-parameters
- host
此配置可确保审计日志包含所有 MCP 专属信息,让你全面了解工具调用及其参数。
开发
要以开发模式运行此应用:
npm install
npm run dev
这将启动开发服务器并启用热重载。
生产环境
要为生产环境构建并运行此应用:
npm run build
npm run start
功能
- 使用 OpenAI 的 AI 驱动聊天界面
- 使用 shadcn/ui 的现代 UI 组件
- 使用 TanStack Router 实现类型安全的路由
- 使用 TanStack Query 获取数据
测试
此项目使用 Vitest 进行测试。你可以使用以下命令运行测试:
npm run test
样式
本项目使用 Tailwind CSS 进行样式设置,并使用 shadcn/ui 提供预构建组件。UI 组件基于 Radix UI 基元构建。
添加新组件
使用以下命令添加新的 shadcn 组件:
npx shadcn@latest add [component-name]
代码检查与格式化
本项目使用 eslint 和 prettier 进行代码检查和格式化。Eslint 使用 tanstack/eslint-config 进行配置。可用的脚本如下:
npm run lint
npm run format
npm run check
Shadcn
使用最新版本的 Shadcn 添加组件。
pnpx shadcn@latest add button
路由
本项目使用 TanStack Router。初始设置采用基于文件的路由器,也就是说,路由由 src/routes 中的文件管理。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。

