Skip to main content
Glama
debadatta30

s3-mcp-server

by debadatta30

在 ECS Fargate 上运行的 S3 MCP 服务器

一个远程的、经过 OAuth2 认证的 MCP 服务器,暴露只读的 S3 工具 (list_buckets、list_objects、get_bucket_public_access、 get_bucket_size),设计为在 ECS Fargate 任务中运行,位于 ALB 和 CloudFront 之后,并以 Auth0 作为身份提供者。

整体架构

  • CloudFront 在边缘终止 TLS,并将所有头部(包括 Authorization) 通过普通 HTTP 转发到内部 ALB。

  • ALB 将请求交给 ECS Fargate 任务(端口 8080)。

  • 任务(server.py)在执行任何工具之前,会针对 Auth0 验证不记名令牌, 然后使用任务的 IAM 角色访问 S3——任何地方都不存在静态 AWS 凭证。

  • 任务通过 NAT 网关访问 Auth0 的 JWKS 端点和 S3 的公共 API, 因为它运行在私有子网中,且未配置 S3 VPC 端点。

OAuth 握手(登录、PKCE 代码交换)完全在 MCP 客户端(例如 Claude Desktop)和 Auth0 之间进行——服务器仅在开始时(提供自身 的发现文档)和结束时(验证生成的令牌)参与。调试时请记住这一点: 登录失败不会出现在此服务器的日志中,因为服务器从未参与该交换。

Related MCP server: aws-safe-mcp

Auth0 设置

你需要一个 Auth0 租户(免费层足够了),其中包含两个部分:

  1. 一个 API——这使 Auth0 颁发真正的 JWT 访问令牌,而不是不透明的令牌。

    • 仪表盘 → Applications → APIs → Create API

    • Identifier:客户端将连接的确切 URL,例如 https://your-domain.example.com/sse——这将变成 AUTH0_AUDIENCE, 并且必须与你部署的 URL 逐字节匹配。

    • 签名算法:RS256(默认)

  2. 一个应用——MCP 客户端以该应用的身份进行认证。

    • 仪表盘 → Applications → Applications → Create Application

    • 类型:Regular Web Application

    • 在 Settings 中,设置 Allowed Callback URLs 为你的 MCP 客户端 使用的任何重定向 URI(对于 Claude Desktop: https://claude.ai/api/mcp/auth_callback)

    • 记下 Domain、Client ID 和 Client Secret——这些将成为 AUTH0_DOMAIN / AUTH0_CLIENT_ID / 你提供给 MCP 客户端的客户端密钥 (服务器本身从不使用密钥)。

  3. 为 API 授权该应用——这一步很容易遗漏。 分别创建 API 和应用 并不会将它们关联起来。进入 API → Application Access 标签页 → 切换你的应用为开启。跳过这一步,每次授权尝试都会失败,并显示 invalid_request / “客户端未被授权访问资源服务器”,客户端甚至 看不到登录页面。

本地测试(传输层不需要 AWS)

cp .env.example .env
# edit .env with your Auth0 tenant details if you want to test auth locally
pip install -r requirements.txt
python server.py

服务器监听在 http://localhost:8080/sse 上。/health 返回 200 ok, 无需认证——这是 ALB 目标组健康检查路径。

其他所有路由都需要 Authorization: Bearer <token>,其中 token 是 针对 AUTH0_CLIENT_ID 的应用客户端的 Auth0 访问令牌(JWT, aud 匹配 AUTH0_AUDIENCE)。

Docker

docker build -t s3-mcp-server .
docker run -p 8080:8080 --env-file .env s3-mcp-server

部署到 Fargate

cd infra
pip install -r requirements.txt   # into a venv
cdk bootstrap   # first time only, per account/region
cdk deploy \
  -c auth0_domain=your-tenant.us.auth0.com \
  -c auth0_client_id=your-application-client-id \
  -c auth0_audience=https://your-domain.example.com/sse

auth0_audience 应匹配 CDK 即将创建的 CloudFront 域名,并附加 /sse—— 在首次部署时你很可能不知道它。先部署一次,获取 DistributionURL 输出, 然后用实际的 audience 值重新部署(这只需要做一次;CloudFront 域名在 同一堆栈的后续部署中保持稳定)。

你可以将 -c 标志放入 gitignored 的 infra/cdk.context.json 文件中, 而不是每次传递:

{
  "auth0_domain": "your-tenant.us.auth0.com",
  "auth0_client_id": "your-application-client-id",
  "auth0_audience": "https://your-domain.example.com/sse"
}

常见的坑点

  1. ALB 空闲超时。 SSE 连接是长连接。ALB 的默认空闲超时(60 秒) 会中断它们。此堆栈将其设置为 300(5 分钟)——如果你的客户端 不发送周期性 ping,请进一步提高。

  2. 健康检查路径。 目标组健康检查指向 /health,而不是 /sse—— /sse 需要认证并且是流式响应,ALB 的健康检查器不期望这两点。

  3. 凭证。 容器从不设置 AWS 凭证——boto3 通过容器凭证端点自动 从 Fargate 任务的 IAM 角色获取。不要将密钥烘焙到镜像或环境变量中。

  4. 出站流量。 任务在首次请求时(然后缓存一小时)通过 HTTPS 获取 Auth0 的 JWKS,并且每次工具调用都会调用 S3 的公共 API——两者都通过 NAT 网关出去,因为此堆栈没有配置 S3 VPC 网关端点。确保任务的子网 实际上具有 NAT 路由,或者为 S3 添加一个网关端点(免费,并且使该流量 远离公共互联网)。

  5. MCP SDK 自身的 DNS 重绑定保护会在任何真实域名后面静默破坏此功能。 FastMCP 的 TransportSecuritySettings 默认具有一个 空 的允许主机列表, 这会拒绝每个带有真实 Host 头部(如你的 CloudFront 域名)的请求—— 即使在认证成功之后——并返回 421 Misdirected Request。这发生在 OAuth 流程完成之后,因此很容易被误认为是认证错误。此服务器在 server.py 中 禁用了它,因为 Auth0AuthMiddleware 已经用不记名令牌认证守卫了所有路由:

    mcp = FastMCP(
        "s3-mcp-server",
        transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False),
    )

最小的 IAM 任务角色策略(仅限上述只读工具)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3ReadOnly",
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:ListBucket",
        "s3:GetBucketAcl",
        "s3:GetBucketPolicyStatus",
        "s3:GetBucketPolicy"
      ],
      "Resource": "*"
    }
  ]
}

一旦你知道此代理应允许查看哪些存储桶,将 Resource 范围缩小到特定 存储桶 ARN。如果你后来添加了写工具(例如生命周期策略更改),请为其提供 自己的更窄的语句,而不是放宽此语句。

MCP 客户端配置

将任何支持远程 SSE 服务器的 MCP 客户端指向你部署的 URL,并附上 上述设置步骤中的 Auth0 应用客户端 ID 和密钥:

{
  "mcpServers": {
    "s3": {
      "url": "https://your-domain.example.com/sse",
      "oauth_client_id": "your-application-client-id",
      "oauth_client_secret": "your-application-client-secret"
    }
  }
}

确切配置形状取决于客户端——Claude Desktop 将这些暴露为表单字段 (在其 Connectors 设置中),而不是原始 JSON。无论哪种方式,OAuth 重定向/令牌交换都由客户端根据 MCP 授权规范处理;此服务器仅在每个 请求上验证生成的承载令牌。

Related MCP Connectors

Related MCP Servers