mcpField
H
How does it work

How does it work

本地服务
github.com/pomerium安全5146 周前更新

关于 How does it work

Pomerium Chat 是一个演示应用,展示如何借助 Pomerium 通过上下文访问策略构建并保护远程 MCP 服务与客户端。适合希望在内部系统中安全开放 MCP 服务的开发者。项目包含基于 OpenAI 的聊天界面,并以 OAuth 2.1 方式处理认证。

主要功能

  1. 01通过 Pomerium 代理保护 MCP 服务
  2. 02OAuth 2.1 认证与上游令牌管理
  3. 03基于用户身份的访问策略
  4. 04MCP 工具调用审计日志配置
  5. 05基于 OpenAI 的聊天界面

适用场景

  1. 01向外部 MCP 客户端安全开放内部数据库服务
  2. 02在内部应用中为 LLM 调用提供受保护的 MCP 访问
  3. 03为需要上游 OAuth 的 MCP 服务统一处理登录

MCP 相关

该项目在 Pomerium 后面部署远程 MCP 服务,并演示客户端如何连接、列出并使用这些服务,同时通过 mcp-* 字段记录工具调用。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"app-demo": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"compose"
]
}
}
}
启动命令docker run -i --rm compose

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
ocicompose

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

欢迎使用 Pomerium Chat,这是一个简约的聊天应用,用于展示由 Pomerium 保护的远程 MCP 服务。

Discord Docker

https://github.com/user-attachments/assets/50a17106-a471-4f64-be81-982c09d4689e

前提条件

  1. Linux 或 MacOS 主机
  2. Docker 和 Docker Compose
  3. 你的机器应向互联网开放 443 端口,以便从 LetsEncrypt 获取 TLS 证书,并让 OpenAI 调用你的 MCP 服务端点。
  4. OpenAI API 密钥

快速入门

Pomerium MCP 客户端演示设置

环境变量

在根目录中创建 .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 流程。流程如下:

  1. 用户在客户端(例如 Claude.ai)中添加 MCP 服务 URL。
  2. 客户端向 Pomerium 注册并开始身份验证。
  3. Pomerium 向客户端提供登录 URL,并将其显示给用户。
  4. 用户登录 Pomerium,然后被重定向到上游 OAuth 提供商。
  5. 用户通过上游提供商进行身份验证。提供商会向 Pomerium 返回一个内部令牌(TI)。
  6. Pomerium 完成登录,并将用户重定向回客户端。
  7. 客户端从 Pomerium 接收一个外部令牌(TE)。
  8. 客户端使用 TE 向 MCP 服务发送请求。
  9. 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 服务交互。

使用 Pomerium 实时审计 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 中的文件管理。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。