Token导航 LogoToken导航TokenDH.com
MCP Cloud Wrappers logo
运维云端未说明官方级别未说明来源级核验

MCP Cloud Wrappers

MCP Server

将任何标准输入输出的MCP服务器部署到云端,使其可从ChatGPT、Claude.ai等在线版本及任何MCP兼容的Web和移动客户端访问。

工具数

0

提示词数

0

GitHub Stars

2

资源数

0
云部署PythonClaudeClaude DesktopClaude

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

作者 / 组织

jspv

提供方

jspv

最后核验

2026/5/17 20:19

快速接入

先看主来源和安装命令,再打开仓库或文档;下面只保留这个条目的关键接入事实。

详细介绍

MCP云包装

获取任何stdio MCP服务器并将其部署到云端——可以从在线版本ChatGPT、Claude.ai以及任何与MCP兼容的web和移动客户端访问。

大多数MCP服务器在本地运行——它们在您的计算机上与Claude Desktop或Claude Code配合得很好,但它们不能从网络上的ChatGPT、Claude.ai或移动应用程序中使用。这个框架改变了这一点。您带来一个现有的stdio MCP服务器,此框架将其部署为任何客户端都可以连接的AWS托管的MCP端点,具有完整的用户身份验证和针对外部服务的每个用户的OAuth。

这有什么作用

  • 大多数stdio MCP服务器 → 带有URL的云托管MCP端点
  • 适用于ChatGPT、Claude.ai和任何MCP客户端 --网络、移动、桌面
  • 每用户身份验证 --每个用户通过Cognito登录,然后根据MCP的需要连接自己的外部帐户(微软、谷歌等)
  • 自动OAuth管理 --Secrets Manager中的每个用户存储的令牌交换、刷新
  • 无需更改MCP服务器 基本服务;使用每个用户OAuth的服务的一行更改
  • 在几分钟内添加新服务 --创建一个包含3-4个配置文件的目录,部署

运作原理

  1. 将您的MCP服务器捆绑到AWS Lambda部署包中
  2. 通过以下方式将其作为云MCP端点公开 亚马逊基岩代理核心网关
  3. 处理呼叫者身份验证(Cognito+动态客户端注册)
  4. 管理外部服务(微软、谷歌等)的每用户OAuth令牌
  5. 提供一个身份验证设置网页,用户可以在其中连接他们的外部帐户

您的MCP服务器不需要了解Lambda、AgentCore或Cognito的任何信息。唯一的调整是对框架注入的访问令牌环境变量进行一行检查(请参见 准备MCP服务器).

建筑

                     ┌──────────────────────────────────────────┐
                     │         Shared Infrastructure            │
                     │         (deployed once)                  │
  MCP Client ──────► │  Cognito User Pool  (caller auth)       │
  (ChatGPT, Claude,  │  DCR API Gateway    (.well-known, /register)
   any MCP client)
                     │  OAuth Callback     (/oauth/callback)    │
                     │  DynamoDB tables    (DCR + OAuth state)  │
                     └───────────────┬──────────────────────────┘
                                     │
            ┌────────────────────────┼────────────────────────┐
            │                        │                        │
  ┌─────────▼──────────┐  ┌──────────▼─────────┐  ┌───────────▼────────┐
  │  Service A         │  │  Service B         │  │  Service C         │
  │                    │  │                    │  │                    │
  │  AgentCore Gateway │  │  AgentCore Gateway │  │  AgentCore Gateway │
  │        │           │  │        │           │  │        │           │
  │  MCP Server Lambda │  │  MCP Server Lambda │  │  MCP Server Lambda │
  │   └─ subprocess:   │  │   └─ subprocess:   │  │   └─ subprocess:   │
  │     your_mcp_pkg   │  │     another_pkg    │  │     third_pkg      │
  └────────────────────┘  └────────────────────┘  └────────────────────┘

每个MCP服务都有自己的Lambda+AgentCore网关。它们共享Cognito池、DCR端点和OAuth回调基础设施。

两层身份验证

目的机制
呼叫者身份验证控制谁可以呼叫MCP网关AgentCore网关验证的Cognito JWT
后端身份验证服务可以代表用户访问什么?框架管理的标准OAuth2(授权码+PKCE)

三类环境变量

每个打包的MCP服务都可能需要这些的组合。该框架加载所有三个类别,并自动将它们合并到子流程环境中。

类别示例它住在哪里谁管理它
1.MCP服务配置TENANT_ID, API_BASE_URLservice.env 服务目录中的文件您,已提交git
2.MCP服务机密CLIENT_ID, CLIENT_SECRET, API_KEYSecrets Manager(每个服务一个JSON对象——每个键都变成一个env变量)您,通过AWS CLI创建一次
3.MCP每个用户凭据访问令牌秘密管理器(每个服务每个用户一个)框架,通过OAuth流

第1类用于非秘密配置——它存在于 service.env 一起 handler.py所有凭据(包括客户端ID)都属于类别2(Secrets Manager中的服务秘密)。

先决条件

  • Python 3.11+
  • 紫外线
  • Node.js(CDK CLI在Node上运行;项目的其余部分是Python)
  • AWS CLI已配置凭据
  • CDK已引导: make bootstrap (或参见 部署)
  • 基于stdio的MCP服务器代码,可以作为lambda运行

存储库结构

mcp-cloud-wrappers/
├── packages/
│   └── mcp-wrapper-runtime/             # Framework runtime (installed into each Lambda)
│       └── src/mcp_wrapper/
│           ├── config.py                # ServiceConfig, OAuthProviderConfig, load_oauth_json
│           ├── credentials.py           # CredentialManager (Secrets Manager)
│           ├── oauth.py                 # OAuthHelper (PKCE, exchange, refresh)
│           └── handler.py              # McpServiceHandler (base Lambda handler)
│
├── infra/
│   ├── app.py                           # CDK app — defines all stacks
│   ├── cdk_constructs/                  # Reusable CDK constructs
│   │   ├── bundler.py                   # Local pip/uv bundler for Lambda assets
│   │   ├── cognito.py                   # Cognito User Pool + resource server
│   │   ├── dcr_bridge.py               # DCR Lambda + API Gateway
│   │   ├── oauth_bridge.py             # OAuth callback Lambda
│   │   ├── mcp_lambda.py               # Per-service MCP Lambda
│   │   └── mcp_gateway.py              # AgentCore Gateway + GatewayTarget
│   ├── stacks/
│   │   ├── shared.py                    # SharedInfraStack (deploy once)
│   │   └── service.py                   # ServiceStack (one per wrapped service)
│   └── lambda/
│       ├── dcr/                         # Shared: RFC 7591 Dynamic Client Registration
│       ├── oauth_callback/              # Shared: Generic OAuth2 callback (all providers)
│       └── services/
│           └── /              # One directory per wrapped MCP service
│               ├── handler.py               #   config: what to wrap, how to authenticate
│               ├── service.env              #   non-secret config
│               ├── tools.json               #   tool definitions (generated via gen-tools)
│               ├── requirements.txt.example #   dependency template
│               ├── requirements.txt         #   actual deps with your paths
│               └── service.local.env        #   local config overrides
│
├── scripts/
│   ├── gen_tools.py                     # Generate tools.json from MCP server
│   └── verify_deployment.py             # Post-deploy smoke test
├── cdk.json
├── Makefile
└── pyproject.toml

哪些MCP服务器使用此框架?

该框架将您的MCP服务器作为 AWS Lambda内部的子流程 并通过以下方式进行通信 标准。这意味着您的服务器必须:

  • 使用stdio传输 --在stdin/stdout上读/写MCP JSON-RPC。仅支持SSE、流式HTTP或WebSocket传输的服务器将无法工作。
  • 在通话中保持无状态 --每次工具调用都会生成一个新的子流程。没有持久进程,调用之间没有内存状态,也没有连接池。如果您的服务器需要状态,请使用外部存储(DynamoDB、S3等)。
  • 不写入文件系统 --Lambda的 /var/task 是只读的。Guard在env var检查后写入(请参阅 准备您的MCP服务器),或写信至 /tmp (短暂的,在调用之间擦除)。
  • 通过环境变量接受配置 -凭据、API密钥和令牌作为env变量注入。运行时不会从磁盘读取配置文件。
  • 在Lambda超时内完成 --默认120秒,最大900秒。每个工具调用必须在一次调用中完成。

什么效果好

  • Python服务器 (FastMCP、MCP Python SDK)——最佳支持路径 mcp_module
  • Node.js、Go、Rust或任何编译的二进制文件 --使用 command/args 在ServiceConfig中(二进制文件必须以Linux ARM64为目标)
  • API-wrapping服务器 (REST、GraphQL)——完美契合;无状态请求响应
  • 仅使用API密钥的服务器 --最简单的情况下,不需要OAuth
  • 使用OAuth的服务器 --框架管理整个令牌生命周期;服务器只读取envvar

什么不起作用

  • 仅SSE或仅HTTP传输 --没有stdio,无法与子进程通信
  • 依赖于跨工具状态的服务器 --例如工具A创建工具B读取的会话。每次调用都是独立的。
  • MCP资源或提示 --AgentCore网关仅路由工具调用,不路由资源或提示请求
  • 服务器发起的通知或流媒体 --Lambda仅是请求响应
  • 30多种工具 --AgentCore分页为30,当前客户端(Claude.ai、ChatGPT)不遵循分页
  • 重型启动 --子进程冷启动(Python解释器+导入)通常会增加1-3秒;依赖性强的服务器(pandas、torch)将变慢

逐步包装新的MCP服务

本节将介绍整个过程。捆绑 msgraph 包裹的包装物https://github.com/jspv/msgraph-email-calendar-mcp是这里描述的每个步骤的具体示例。

1.准备您的MCP服务器

您的MCP服务器位于其自己的存储库、包注册表或您保存它的任何地方 任何语言 --该框架将其作为子进程启动,并通过stdio进行通信。

为了兼容Lambda,您的服务器需要调整两件事:

a.接受框架注入的访问令牌

如果您的服务使用每个用户的OAuth,您需要对其进行修改——它将访问令牌作为环境变量接收,并由框架传递。在MCP服务器中找到获取访问令牌的位置,并在顶部添加env-var检查。这使得相同的代码可以在本地(运行自己的身份验证)和Lambda内部(框架管理身份验证)工作:

python --在MCP服务器的auth或HTTP客户端模块中:

import os

def get_token():
    # When running inside Lambda, the framework injects a valid token
    token = os.environ.get("MY_SERVICE_ACCESS_TOKEN")
    if token:
        return token
    # When running locally, use the normal auth flow
    return local_auth_flow()

Node.js --同样的想法,在服务器的auth模块中:

function getToken() {
  return process.env.MY_SERVICE_ACCESS_TOKEN || localAuthFlow();
}

环境变量名称(MY_SERVICE_ACCESS_TOKEN 上面)是你设置的任何值 access_token_env_varServiceConfig (下面的步骤2)。它只需要匹配。

如果您的服务不需要每个用户的OAuth(只需要API密钥),则不需要更改代码-框架直接将API密钥作为env-var注入。

b.不要写入Lambda文件系统(即,你的mcp需要能够作为Lambda运行)

AWS Lambda /var/task 目录是 只读。如果您的MCP服务器在启动时写入文件(令牌缓存、SQLite数据库、临时文件),这些写入将在Lambda中失败。

保护任何文件系统写入,以便在框架管理身份验证时跳过它们:

import os

def _framework_managed():
    """True when running inside the MCP Lambda wrapper framework."""
    return bool(os.environ.get("SERVICE_NAME"))

# Before any file write:
if not _framework_managed():
    cache_dir.mkdir(parents=True, exist_ok=True)
    write_token_cache(...)

要注意的文件系统写入的常见来源:

  • 令牌缓存 (SOAP、googleauth等)--在框架管理时跳过缓存读/写
  • 会话状态文件 --该框架通过DynamoDB/秘密管理器处理状态
  • 数据库存储 --如果不可避免,请写信至 /tmp (Lambda中唯一可写的路径)

2.在infra/lambda/services下创建服务目录,用于服务器注册

在下面创建一个目录 infra/lambda/services// 使用这些文件:

infra/lambda/services/my-service/
├── handler.py              # what to wrap, how to authenticate
├── service.env             # non-secret config
├── oauth.json              # OAuth provider config (if service uses OAuth)
├── tools.json              # tool definitions (generated via gen-tools)
├── requirements.txt.example
└── requirements.txt        # actual deps with your paths

service.env --此服务的非秘密配置:

# service.env
MY_TENANT_ID=my-org-123
MY_API_BASE_URL=https://api.provider.com/v1

handler.py --声明 *什么* 包装和 *怎么* 进行身份验证:

from mcp_wrapper import McpServiceHandler, ServiceConfig, load_oauth_json

config = ServiceConfig(
    service_name="my-service",
    mcp_module="my_service_mcp.server",       # python -m my_service_mcp.server
    passthrough_env_vars=["MY_TENANT_ID"],     # from service.env
    service_secret_name="{prefix}-my-service-service-secrets",
    oauth=load_oauth_json(),                   # reads oauth.json (single source of truth)
    access_token_env_var="MY_SERVICE_ACCESS_TOKEN",
)

_handler = McpServiceHandler(config)

def handler(event, context):
    return _handler.handle(event, context)

passthrough_env_vars 从中取值的名称 service.env 以转发到子流程。凭据属于服务机密(请参阅步骤3)。OAuth提供者配置(端点、作用域、客户端密钥)在中定义 oauth.json --不在此文件中。

对于一个 非Python MCP服务器,使用 commandargs 而不是 mcp_module:

config = ServiceConfig(
    service_name="my-node-service",
    command="/var/task/node_modules/.bin/my-mcp-server",
    args=["--stdio"],
    service_secret_name="{prefix}-my-node-service-secrets",
    oauth=load_oauth_json(),
    access_token_env_var="MY_SERVICE_ACCESS_TOKEN",
)

对于服务 没有OAuth,省略 oauthaccess_token_env_var 领域:

config = ServiceConfig(
    service_name="my-search",
    mcp_module="my_search_mcp.server",
    service_secret_name="{prefix}-my-search-secrets",
    # API keys live in the service secret; no OAuth needed
)

requirements.txt.example --显示依赖关系的模板。复制到 requirements.txt 并填写您的包裹来源:

# Copy this file to requirements.txt and update paths.

# Framework dependencies (always required)
mcp
run-mcp-servers-with-aws-lambda
boto3
httpx

# MCP server package — uncomment ONE of these:
# my-service-mcp                                                     # from PyPI
# my-service-mcp @ git+https://github.com/you/my-service-mcp.git    # from Git
# my-service-mcp @ file:///path/to/local/checkout                    # from local path

mcp-wrapper-runtime 由bundler自动安装,无需列出。

对于 非Python MCP服务器,你仍然需要框架deps requirements.txt (Lambda处理程序本身就是Python)。通过以下方式将服务器二进制或节点模块捆绑到Lambda包中 handler_source_dir --把它们放在旁边 handler.py 邦德勒把它们抄了进去。

MCP服务器是 此存储库的一部分。它在构建时作为依赖项引入,就像任何Lambda捆绑其依赖项一样。

就是这样,没有其他文件可以编辑。CDK应用程序会自动发现下的每个目录 infra/lambda/services/ 包含a handler.py 并为其创建堆栈。

3.预部署设置

创建服务密钥 在机密管理器中(客户端ID和API密钥等凭据):

aws secretsmanager create-secret \
  --name mcp-wrappers-my-service-service-secrets \
  --secret-string '{"MY_CLIENT_ID": "your-client-id", "MY_CLIENT_SECRET": "your-secret"}'

生成 tools.json --AgentCore需要知道MCP服务器公开了哪些工具。如果您有MCP服务器的本地签出(通过引用 file:// 路径在 requirements.txt),脚本可以自动进行自检:

make gen-tools SERVICE=my-service

每当MCP服务器的工具定义发生变化时,请重新运行此程序。

如果没有本地签出(例如,包来自PyPI或git URL)或服务器不是Python,请创建 tools.json 手动。这是一个JSON数组,其中每个条目都有 name, description,以及可选 inputSchema 具有JSON模式属性。看 infra/lambda/services/msgraph/tools.json 作为一个工作示例。

4.部署

make deploy-shared                             # first time only (creates Cognito, DCR, OAuth callback)
make deploy-service SERVICE=my-service         # deploy your service

5.部署后设置

注册OAuth回调URL (如果您的服务使用OAuth):采取 OAuthCallbackUrl 从共享堆栈输出中提取,并将其作为重定向URI添加到OAuth提供商的应用程序注册中。

秘密和安全 有关秘密如何存储和作用域的详细信息。

就是这样。该框架处理Cognito、DCR、AgentCore网关、OAuth令牌生命周期和Lambda打包。

部署

# Install Python dependencies
uv sync

# Bootstrap CDK in your AWS account (first time only)
make bootstrap

# Deploy shared infrastructure (Cognito, DCR, OAuth callback)
make deploy-shared

# Create a Cognito user (needed to authenticate with the gateway)
aws cognito-idp admin-create-user \
  --user-pool-id $(aws cloudformation describe-stacks --stack-name mcp-wrappers-shared \
    --query 'Stacks[0].Outputs[?contains(OutputKey,`UserPoolId`)].OutputValue' --output text) \
  --username your-email@example.com \
  --user-attributes Name=email,Value=your-email@example.com Name=email_verified,Value=true \
  --temporary-password 'TempPass123!'

# Deploy a specific service
make deploy-service SERVICE=my-service

# Deploy all services
make deploy-all

# Verify endpoints are responding
make verify

第一 make 调用会自动将CDK CLI作为本地npm依赖项安装——您不需要全局安装它。

macOS用户:Lambda需要Linux ARM64二进制文件来编译Python包(pydantic、密码学等)。打包机会自动退回到集装箱内进行构建。集 CDK_DOCKER=podman 如果你使用Podman而不是Docker:

CDK_DOCKER=podman make deploy-service SERVICE=my-service

或者将其导出到您的shell配置文件中,这样您就不必每次都通过它。

Cognito用户只需要创建一次。首次通过托管UI登录时,系统会提示您设置永久密码。

可用目标

目标描述
make synth合成所有CloudFormation模板
make list列出所有堆栈
make bootstrap您的AWS帐户中的Bootstrap CDK(第一次)
make gen-tools SERVICE=x从MCP服务器生成tools.json
make deploy-shared部署共享基础设施
make deploy-service SERVICE=x部署特定服务
make deploy-all部署共享+所有发现的服务
make verify运行部署后烟雾测试
make auth在浏览器中打开身份验证设置页面

秘密和安全

秘密是如何存储的

该框架使用 AWS Secrets Manager --每个帐户都可以使用的托管AWS服务,无需设置。您不提供或部署任何东西;您只需通过AWS CLI或SDK存储和检索值。

每个服务有两种秘密:

类型秘密名称模式创建者
服务秘密{prefix}-{service}-service-secrets你,通过 aws secretsmanager create-secret (每次服务一次)
用户凭据{prefix}-{service}-user-{cognito_sub}框架,当用户完成OAuth流程时自动

服务秘密 是一个包含JSON对象的单个Secrets Manager条目。JSON中的每个键在子流程中都成为一个单独的环境变量。以下是如何将多个机密传递给服务,而不创建多个机密管理器条目:

aws secretsmanager create-secret \
  --name mcp-wrappers-my-service-service-secrets \
  --secret-string '{
    "CLIENT_SECRET": "abc123",
    "API_KEY": "xyz789",
    "WEBHOOK_SECRET": "def456"
  }'

子流程将看到 CLIENT_SECRET=abc123, API_KEY=xyz789,以及 WEBHOOK_SECRET=def456 在其环境中。

用户凭据 当用户完成OAuth流程时,框架会自动创建。每个用户都有自己的秘密,其中包含 access_token, refresh_token,以及 expires_at。您永远不会手动创建这些。

这两种类型的秘密都可以在部署堆栈之前或之后随时创建。Lambda仅在调用时读取它们,而不是在部署时读取。

IAM范围——每个Lambda可以访问的内容

每个服务Lambda的IAM角色仅限于其自己的机密。该政策限制访问 {prefix}-{service}-*:

# The service-a Lambda can access:
  mcp-wrappers-service-a-service-secrets     ✓
  mcp-wrappers-service-a-user-abc123         ✓

# It CANNOT access:
  mcp-wrappers-service-b-service-secrets     ✗  (different service)
  my-database-password                       ✗  (no prefix match)
  production-api-key                         ✗  (no prefix match)

服务彼此隔离。每个Lambda只能读取与其自身匹配的机密 {prefix}-{service}-* 图案。同样的作用域也适用于OAuth回调Lambda(仅限于 {prefix}-*).

Lambda也有 CreateSecret 其范围内的权限——这是必要的,因为当用户第一次完成OAuth时,会动态创建新的用户凭据秘密(您事先不知道Cognito用户ID)。

部署顺序

堆栈和秘密有这样的依赖链:

Deploy shared stack ──► Get OAuthCallbackUrl ──► Register URL with OAuth provider
        │                                           (before first OAuth flow)
        │
        └──► Deploy service stack ──► Service is live
                                        │
Create service secret ──────────────────┘ (before first tool invocation)

实践中:设置 service.env、服务机密,以及 tools.json 首先(步骤3),然后部署(步骤4),然后注册OAuth回调URL(步骤5)。CDK自动解析堆栈间的依赖关系。

OAuth流程是如何工作的

当用户首次与需要OAuth的服务交互时:

1. Agent calls a tool (e.g., list_messages)
   └─ Interceptor injects _cognito_sub from JWT
   └─ Handler finds no credentials for this user in Secrets Manager
   └─ Sets OAUTH_AUTH_URL to the auth setup page, launches subprocess
   └─ MCP server returns "not authenticated, call start_auth"

2. Agent calls start_auth
   └─ MCP server reads OAUTH_AUTH_URL from env, returns it
   └─ Agent presents URL to user: "Open this link to connect your account"

3. User opens URL in browser → auth setup page
   └─ Logs into Cognito (establishes identity)
   └─ Sees available services, clicks "Connect"
   └─ Redirects to external provider login (Microsoft, Google, etc.)
   └─ Provider redirects to /oauth/callback
   └─ Callback stores tokens in Secrets Manager under {prefix}-{service}-user-{cognito_sub}
   └─ Redirects back to auth setup page showing "Connected"

4. User returns to chat, tells agent to try again
   └─ Handler loads token from Secrets Manager
   └─ Injects access token as env var
   └─ MCP server tools work normally

令牌刷新是透明的——处理程序在每次调用时检查到期时间,并使用存储的刷新令牌自动刷新。

连接外部服务

部署后,用户需要连接其外部帐户(Microsoft、Google等) 在MCP工具能够访问其数据之前。

身份验证设置页面

运行:

make auth

这将打开一个网页,您可以在其中:

  1. 使用您的Cognito帐户登录(与MCP网关的凭据相同)
  2. 查看所有可用服务及其连接状态
  3. 点击“连接”以对每个外部服务进行身份验证

该页面处理完整的OAuth流程——您只需点击提供商的登录即可。

适用于聊天用户(Claude.ai、ChatGPT等)

当您首次使用需要身份验证的工具时,代理将调用 start_auth 返回认证设置页面URL。在浏览器中打开它,完成登录, 然后告诉代理再试一次。

身份验证设置URL

部署后,URL显示在堆栈输出中:

aws cloudformation describe-stacks --stack-name mcp-wrappers-shared \
  --query 'Stacks[0].Outputs[?contains(OutputKey,`AuthSetupUrl`)].OutputValue' \
  --output text

与需要连接其帐户的最终用户共享此URL。

捆绑示例:Microsoft Graph(msgraph)

infra/lambda/services/msgraph/ 目录封装了一个用于Outlook邮件和日历的MCP服务器(msgraph电子邮件日历MCP)。它展示了完整的模式:

设置和部署

# 1. Edit service.env with your tenant ID
#    infra/lambda/services/msgraph/service.env:
#    MICROSOFT_TENANT_ID=your-tenant-id

# 2. Create the service secret with your Azure app credentials
aws secretsmanager create-secret \
  --name mcp-wrappers-msgraph-service-secrets \
  --secret-string '{"MICROSOFT_CLIENT_ID": "your-azure-client-id"}'

# 3. Generate tools.json from the MCP server
make gen-tools SERVICE=msgraph

# 4. Deploy
make deploy-all

# 5. Register the OAuthCallbackUrl (from deploy output) in your Azure App Registration
#    under Authentication > Web > Redirect URIs

处理器

完整的处理程序——其他一切都是框架管理的:

# infra/lambda/services/msgraph/handler.py
from mcp_wrapper import McpServiceHandler, ServiceConfig, load_oauth_json

config = ServiceConfig(
    service_name="msgraph",
    mcp_module="msgraph_mcp.server",
    passthrough_env_vars=["MICROSOFT_TENANT_ID"],
    service_secret_name="{prefix}-msgraph-service-secrets",
    oauth=load_oauth_json(),        # reads oauth.json — single source of truth
    access_token_env_var="GRAPH_ACCESS_TOKEN",
)

_handler = McpServiceHandler(config)

def handler(event, context):
    return _handler.handle(event, context)

MCP服务器包参考

复制 requirements.txt.examplerequirements.txt (gitignored)并设置本地结账的路径:

mcp
run-mcp-servers-with-aws-lambda
boto3
httpx
msgraph-mcp @ file:///path/to/msgraph-email-calendar-mcp

mcp-wrapper-runtime 由打包机自动安装。

使用外部Cognito池

如果你已经有一个Cognito用户池(例如,来自另一个项目),你可以重用它,而不是创建一个新的。这需要编辑 infra/app.py --该文件需要修改的唯一情况:

# infra/app.py — pass external pool details to SharedInfraStack
shared = SharedInfraStack(
    app, f"{prefix}-shared", env=env,
    external_user_pool_id="us-east-1_xxxxxx",
    external_user_pool_arn="arn:aws:cognito-idp:us-east-1:123456789:userpool/us-east-1_xxxxxx",
    external_hosted_ui_domain="auth.example.com",
    external_resource_server_identifier="my-prefix",
)

DCR桥和OAuth回调仍在创建中,只有Cognito池被重用。

CDK上下文参数

通过 -c key=value 在CDK命令行上,或在中设置默认值 cdk.json:

参数默认值说明
prefixmcp-wrappers所有堆栈的资源名称前缀
domain_name--Cognito托管UI的自定义域(可选)
hosted_zone_name--自定义域的Route53托管区域(可选)
google_client_id_ssm--谷歌社交联盟的SSM参数(可选)
google_client_secret_ssm--谷歌社交联盟的SSM参数(可选)

每个服务配置不使用CDK上下文。非秘密配置进入 service.env,凭证进入秘密管理器。看 三类环境变量.

框架内部

端到端请求流(详细)

这是从MCP客户端到MCP服务器再返回的单个工具调用的完整路径。了解此流程对于调试非常重要。

MCP Client (Claude.ai, ChatGPT, etc.)
  │
  │  MCP JSON-RPC over HTTPS
  ▼
AgentCore Gateway
  │  Validates Cognito JWT (CUSTOM_JWT authorizer)
  │  Rejects if invalid — tool call never reaches Lambda
  │
  │  Invokes request interceptor Lambda
  ▼
Interceptor Lambda (infra/lambda/interceptor/handler.py)
  │  Receives: {interceptorInputVersion, mcp: {gatewayRequest: {headers, body}}}
  │  Decodes JWT from Authorization header (base64, no verification — already validated)
  │  Extracts Cognito "sub" claim
  │  Injects _cognito_sub into body.params.arguments
  │  Returns: {interceptorOutputVersion: "1.0", mcp: {transformedGatewayRequest: {body}}}
  │
  │  AgentCore forwards the modified request to the target Lambda
  ▼
MCP Server Lambda — handler entry point (handler.py in service directory)
  │  Receives: event = tool arguments dict (e.g. {"folder": "inbox", "_cognito_sub": "abc123"})
  │  context.client_context.custom = {bedrockAgentCoreToolName: "target___tool_name"}
  │
  │  Calls McpServiceHandler.handle(event, context)
  ▼
McpServiceHandler.handle() (packages/mcp-wrapper-runtime/src/mcp_wrapper/handler.py)
  │
  │  1. Health check: if event has "ping"/"health", return status immediately
  │
  │  2. Extract user ID: reads event.get("_cognito_sub")
  │     Returns the Cognito sub or None
  │
  │  3. Strip _cognito_sub from event: event.pop("_cognito_sub", None)
  │     FastMCP/Pydantic would reject it as an unexpected tool argument
  │
  │  4. Build subprocess environment (three categories):
  │     Category 1: passthrough env vars from service.env (e.g. TENANT_ID)
  │     Category 2: service secrets from Secrets Manager (e.g. CLIENT_ID)
  │     Category 3: per-user OAuth credentials:
  │       - If user_id is available AND credentials exist in Secrets Manager:
  │           Load tokens, refresh if expired, set access_token_env_var + OAUTH_AUTHENTICATED=true
  │       - If user_id is available but NO credentials:
  │           Set OAUTH_AUTHENTICATED=false, OAUTH_AUTH_URL=AUTH_SETUP_URL
  │       - If user_id is None (interceptor not working):
  │           Set OAUTH_AUTHENTICATED=false (no OAUTH_AUTH_URL — can't do per-user lookup)
  │
  │  5. Launch MCP subprocess: python -m {mcp_module}
  │     Subprocess receives the merged env vars
  │     StdioServerAdapterRequestHandler bridges JSON-RPC to stdio
  │     BedrockAgentCoreGatewayTargetHandler handles the AgentCore protocol
  ▼
MCP Server subprocess (e.g. msgraph_mcp.server)
  │  Receives tool call via stdio (JSON-RPC)
  │  Reads GRAPH_ACCESS_TOKEN (or equivalent) from env — uses it for API calls
  │  If not authenticated: reads OAUTH_AUTH_URL from env, returns it via start_auth tool
  │  Executes tool, returns result via stdio
  ▼
Response flows back: subprocess → handler → AgentCore Gateway → MCP Client

认证设置页面流程(详细)

当用户需要连接外部服务时(一次性设置):

User visits /auth/setup (via make auth, start_auth URL, or direct link)
  │
  ▼
Auth Setup Lambda — /auth/setup route
  │  No session → redirects to Cognito hosted UI login
  ▼
Cognito Hosted UI
  │  User logs in (email/password, possibly MFA)
  │  If already logged in (browser cookie), may auto-complete
  │  Redirects to /auth/callback?code=xxx&state=yyy
  ▼
Auth Setup Lambda — /auth/callback route
  │  Validates state from DynamoDB (prevents CSRF)
  │  Exchanges Cognito auth code for tokens (POST to Cognito token endpoint)
  │  Decodes ID token → extracts sub and email
  │  Creates DynamoDB session (10-minute TTL, keyed by random session token)
  │  Renders service connection page HTML
  │  Each service card shows: display_name, connected/not connected, [Connect] button
  │  Connect button URL: /auth/connect/{service}?session={token}
  ▼
User clicks [Connect]
  │
  ▼
Auth Setup Lambda — /auth/connect/{service} route
  │  Validates session token from DynamoDB → gets Cognito sub
  │  Loads service OAuth config from SERVICE_OAUTH_CONFIGS env var
  │  Reads client_id from Secrets Manager (service secret)
  │  Generates PKCE code_verifier + code_challenge
  │  Stores OAuth state in DynamoDB: {state, user_id, service_name, token_endpoint,
  │    client_id, code_verifier, return_url=/auth/setup?session=xxx, ttl}
  │  Redirects to external provider's OAuth authorization URL
  ▼
External Provider (Microsoft, Google, etc.)
  │  User logs in and approves permissions
  │  Redirects to /oauth/callback?code=xxx&state=yyy
  ▼
OAuth Callback Lambda — /oauth/callback route (existing, shared)
  │  Validates state from DynamoDB
  │  Exchanges authorization code for tokens (POST to provider's token endpoint)
  │  With PKCE code_verifier if present
  │  Stores tokens in Secrets Manager: {prefix}-{service}-user-{cognito_sub}
  │  Checks for return_url in state record
  │  If return_url present: redirects to /auth/setup?session=xxx&connected={service}
  │  If no return_url: shows static "Authentication successful" HTML
  ▼
Auth Setup Lambda — /auth/setup route (return visit)
  │  Session token present → loads session from DynamoDB
  │  connected={service} param → shows success flash message
  │  Re-checks connection status for all services
  │  User sees "{display_name} — Connected ✓"

身份传播——为什么注入然后剥离

AgentCore网关验证Cognito JWT,但 将索赔转发给Lambda目标。Lambda只接收以下工具参数 event 元数据 context.client_context.custom (工具名称、网关ID——无用户标识)。

该框架使用 请求拦截器 为了解决这个问题:

  1. 拦截器 从Authorization标头中解码JWT并注入 _cognito_sub 进入JSON-RPC params.arguments
  2. 代理商核心 将修改后的参数传递为 event 到目标Lambda
  3. McpServiceHandler 读取 event.get("_cognito_sub") 识别用户
  4. McpServiceHandler 移除 _cognito_subevent 转发之前 BedrockAgentCoreGatewayTargetHandler

删除是必要的,因为FastMCP通过Pydantic根据函数签名验证工具参数。意外的 _cognito_sub 参数将导致验证错误。标识在参数中短暂存在,由处理程序提取,然后在到达MCP子流程之前被剥离。

拦截器响应 必须 包括 "interceptorOutputVersion": "1.0" 在顶层,AgentCore会在没有请求的情况下自动丢弃请求。

秘密经理

秘密和安全 用于命名约定、IAM范围和部署顺序。

30刀具限制

AgentCore网关分页 tools/list MCP以每页30个工具的速度响应,并返回 nextCursor 更多页面。然而,当前的MCP客户端(Claude.ai、ChatGPT)不遵循分页——它们只取第一页。这意味着 只有前30个工具(按字母顺序排列)对客户端可见。

gen-tools 脚本强制执行此限制,如果MCP服务器暴露超过30个工具,则会出错。如果您的服务需要更多,请减少MCP服务器中的工具数量(整合工具,删除很少使用的工具)或跨多个网关目标拆分。

AgentCore还支持 searchType: SEMANTIC 其用用于自然语言发现的单个搜索工具替换工具列表。然而,目前的MCP客户端不使用它——他们希望工具直接出现在 tools/list.

诊断日志

TODO:生产前删除。 拦截器、处理程序和凭据管理器当前发出 [interceptor][mcp-wrapper] 将日志行记录到stderr(CloudWatch),用于调试身份传播和凭据加载流。这些应该被移除或用门锁住 LOG_LEVEL env-var字段测试完成后。带有诊断日志记录的文件:

  • infra/lambda/interceptor/handler.py
  • packages/mcp-wrapper-runtime/src/mcp_wrapper/handler.py
  • packages/mcp-wrapper-runtime/src/mcp_wrapper/credentials.py

CDK结构组成

SharedInfraStack
  ├── CognitoPool          (User Pool + resource server + hosted UI domain)
  ├── DcrBridge            (DynamoDB table + DCR Lambda + API Gateway)
  ├── OAuthBridge          (DynamoDB table + callback Lambda + /oauth/callback)
  └── AuthSetup            (Cognito client + auth setup Lambda + /auth/* routes)

ServiceStack (one per wrapped service)
  ├── McpServerLambda      (Lambda + bundler + IAM role)
  └── McpAgentCoreGateway  (CfnGateway + CfnGatewayTarget + interceptor Lambda + gateway role)

目录标签

目录标签

云部署PythonClaude本地部署MCP协议AWSLambdaOAuth管理多客户端支持

支持客户端

Claude DesktopClaude

接入字段

传输方式(transport,传输协议)

未说明

鉴权方式(authType,认证方式)

oauth

工具数量(toolCount,工具数)

0

资源数量(resourceCount,资源数)

0

提示词数量(promptCount,提示词数)

0

权限和风险

未说明oauth部署方式未说明

接入前请确认传输方式、认证方式和部署位置,并根据实际工具能力限制访问范围。

安装前确认

不要直接授予不必要的文件、网络或账号权限;先核对安装命令和配置内容。

仍需确认:installCommand

来源信息

继续浏览同类 MCP