Skip to main content
Glama
amar-p6

shared-skill-mcp

by amar-p6

shared-skill-mcp

一个为 claude.ai 自定义连接器(custom connectors)设计的、托管在 AWS 上的 MCP 服务器,其设计目标是在后续持续容纳更多工具,而不只是最初那个 Google Sheets 查询工具。架构为:一个共享的 Cognito 认证层(Google 登录)+ 一个 Bedrock AgentCore Gateway(真正的 MCP 服务器)+ 每个工具一个 Lambda。

最初 query_sheet 工具的完整规格、架构图和分阶段演进历史,请见:Reel AI Workers/skills-spec/sheet-gviz/sheet-gviz.md。

状态(2026-08-24)

已上线并端到端验证可用,包括一个真实的 claude.ai 连接器完成 Google 登录并调用该工具——而不只是 curl 验证。

项目

值

MCP 服务器 URL(Gateway)

https://sheets-gviz-gateway-63psdjvcgs.gateway.bedrock-agentcore.eu-west-1.amazonaws.com/mcp

Cognito 域名

sheets-gviz-b24dc744.auth.eu-west-1.amazoncognito.com

用户池 ID

eu-west-1_sfGqYcC0a

AWS 账户

423566941862, eu-west-1

随时获取当前值(包括密钥):

AWS_PROFILE=<your profile> terraform -chdir=terraform output
AWS_PROFILE=<your profile> terraform -chdir=terraform output -raw cognito_client_secret

当前对外暴露的工具:query_sheet —— 对一个 Google Sheet 执行 gviz 查询(类 SQL:select/where/group by/pivot/order by)。

Related MCP server: Google Workspace MCP

架构

claude.ai connector
      │  OAuth 2.1 (real Google login, via Cognito's Hosted UI)
      ▼
Cognito User Pool ──federates to──> Google (login only)
      │  issues an access token (no "aud" claim — see gotcha below)
      ▼
AgentCore Gateway (CUSTOM_JWT authorizer, matches by client_id)
      │  invokes under its own service role
      ▼
Lambda tool target (gateway-tool-handler.mjs) ──> gviz.js ──> Google Sheets API

这里存在两个必须保持区分的 Google OAuth 客户端:一个供 Cognito 登录使用(联合身份),另一个供 gviz.js 读取 Sheet(服务凭证,基于刷新令牌,绝不进行交互式使用)。刻意避免让两者复用同一个客户端——见规格文档中对“两个身份”的框架说明。

这个仓库的由来(在改认证配置前值得先读)

第一个可用的版本是把手写的 Lambda Function URL 当作 MCP 服务器,由 Lambda 自身校验 Cognito JWT,并手工提供 OAuth discovery 元数据(RFC 9728 / RFC 8414)。它通过了 curl 和手动 Postman OAuth 流程——完整往返链路、真实 Google 登录、真实 Sheet 数据——但 claude.ai 的实际连接器客户端每次都会静默失败(Couldn't connect / Authorization failed),且完全看不出原因:Lambda 日志显示 claude.ai 拉取了一次 discovery 元数据之后就再无动静——没有 token 交换,没有报错,什么都没有。

当时的工作假说:Cognito 的 access token 不携带 aud 声明(这是真实存在的 Cognito 限制——解码真实 token 后确认过),而 MCP 规范期望客户端发送的 resource 参数能反映到该声明中。生态中别处近期出现的、看似可信的证据似乎也印证这一点。结果证明这是个误导性线索——来自另一个独立项目(同一个人,不同仓库)的一个可用参考实现证明,Cognito + claude.ai 连接器完全可以正常工作,只需要在 Cognito 前面放 Bedrock AgentCore Gateway,而不是手写服务器;该 Gateway 的 CUSTOM_JWT 授权器按 allowed_clients(即 Cognito 的 client_id)而不是 aud 进行匹配。原先失败真正的根因始终没有被一锤定音地定位——最有可能还是手写 JSON-RPC/discovery 实现中某些细节与 claude.ai 客户端期望不完全吻合,尽管它通过了所有手动测试。

下一次的教训: 一个看起来完全符合规范、能通过 curl 和 Postman 测试的手写 MCP 服务器,并不能证明它对 claude.ai 的真实连接器客户端可用——两者可能在零错误信号的情况下出现差异。优先使用 AWS 自己的 MCP 服务器实现(AgentCore Gateway),而不是手工重新实现 MCP + OAuth discovery,尽管这意味着要多了解一项 AWS 服务,并且当前 Terraform provider 的坑相对更多(见下文)。

在 Gateway 确认可用后,手写的 Function URL 服务器(sheets-gviz-mcp Lambda、modules/mcp-lambda、src/lambda-handler.mjs、src/auth.mjs)随之退役——通过 Terraform 拆除(对仍然活跃的 Cognito User Pool / domain / app client 零影响,Gateway 路径原样复用它)并将其从仓库移除。如果以后还要用到相关设计思路或代码,它完整保留在 Git 历史中。

途中踩到的坑(已在代码中修复,再次动这里之前值得了解)

Sheets/gviz 层(src/gviz.js):

  1. OAuth token 需要同时包含 .../auth/spreadsheets 和 .../auth/spreadsheets.readonly 两个 scope —— 仅 readonly 会得到 401,且响应看起来像 HTML 登录页,而不是清晰的错误。

  2. 即使使用 OAuth bearer token,gviz 的 /tq 端点也需要带 /a/<domain>/ 路径段,如:docs.google.com/a/google.com/spreadsheets/d/<id>/gviz/tq。如果 google.com 不适用于你的账号域,可通过 GVIZ_DOMAIN_SEGMENT 配置。

  3. 始终传入 headers=1(已在 querySheet 中硬编码)。如果不传,gviz 对表头行的自动检测可能出错,并把真实数据行静默折叠进 cols[].label 成一个巨大字符串,导致数据全部丢失。

AWS/Terraform 层:

4. IAM 身份策略变更可能需要几十秒到几分钟才能真正生效,即使 aws iam simulate-principal-policy 立即确认策略正确。因此刚授予新权限后立刻做一次全新的 plan/apply 而遇到 403 是完全正常的——不必紧张,等一小段时间后重试即可。 5. ESM .js 文件在打包 zip 时,如果不带上仓库根目录的 package.json,那么需要有自己独立的 package.json({"type": "module"})——gviz.js 使用了 export/import,它之所以能被解析为 ESM,正是因为它在每个 Lambda 的 zip 中都与 src/package.json 放在一起。 6. aws_bedrock-agentcore_* 是较新的 Terraform 资源,仍在不断演化——请依据 provider 自带的 schema 来确认实际的参数结构(terraform providers schema -json),不要只相信更新滞后的文档或博客。需要 provider 版本 >= 6.0。 7. AgentCore Gateway 的 CUSTOM_JWT 授权器按 allowed_clients(即 Cognito 的 client_id)匹配调用方,而不是 allowed_audience——这正是它能在不用额外 token 中间层分发的情况下,与 Cognito 的非标准(无 aud)access token 搭配工作的原因。 8. 把在线的 Terraform 资源重构进 module 容易误删它们。无论是最初的 Phase 4→module 重构,还是后来解除 Function URL,都在每次 apply 前使用了 terraform state mv 和真实的 plan 检查(预期保留的资源销毁数必须为 0)——claude.ai 已经拿到凭据的 app client 就是这样被移动了两次,期间从未被销毁或重建。

设置

1. 单独验证 Sheets 凭据可用(无需 AWS)

cp .env.example .env   # fill in GOOGLE_CLIENT_ID/SECRET/REFRESH_TOKEN, SPREADSHEET_ID
node scripts/phase1-test.mjs "select *"

完成判定: 对真实查询输出 {columns, rows}。这个阶段的失败基本是 Google 侧的问题(scope、共享权限、Sheets API 未启用)——这是联系 AWS 之前排查问题边界最低成本的时机。

2. 部署 Cognito + Gateway + 工具 Lambda

需要具备 terraform/iam-policy.json 中列出权限的 AWS 凭据,以及一个 第二个 Google OAuth 客户端(Web 应用,必须区别于读取 Sheet 用的那个),供 Cognito 登录使用。它的 redirect URI 需要 Cognito 的域,而该域当时还不存在,需要先用一次部分 apply 才能解开这个鸡生蛋的循环:

scripts/tf.sh apply -target=module.auth.aws_cognito_user_pool.this \
  -target=module.auth.aws_cognito_user_pool_domain.this

创建 Google OAuth 客户端,redirect URI 填写 https://<domain 输出>/oauth2/idpresponse,在 .env 中填好 GOOGLE_LOGIN_CLIENT_ID / GOOGLE_LOGIN_CLIENT_SECRET,然后:

scripts/tf.sh apply

CLAUDE_OAUTH_REDIRECT_URI 不需要专门设置——默认为 https://claude.ai/api/mcp/auth_callback,并且已确认可用,针对真实连接器工作正常。

3. 把它添加为 claude.ai 连接器

设置 → 连接器 → 添加自定义连接器:

  • 服务器 URL:取 gateway_url 输出

  • 高级设置 → OAuth 客户端 ID / 密钥:取 cognito_client_id / cognito_client_secret 输出

之后应会通过 Cognito 的 Hosted UI 触发一次真实的 Google 登录,系统随后即可让 Claude 调用 query_sheet(在聊天窗口可见为 tool-use 块)。

添加新工具

  1. 使用 AgentCore 约定编写 Lambda 处理器——扁平式的 event 就是工具参数,不再有 JSON-RPC 包裹(Gateway 负责 MCP 帧格式)。遵循 src/gateway-tool-handler.mjs 的写法。

  2. 在 main.tf 里加一个 module "..." { source = "./spell/modules/gateway-tool-lambda" ... } 块。

  3. 为工具添加 aws_bedrockagentcore_gateway_target 资源——可以扩展 modules/agentcore-gateway 使其支持一个 targets 列表,也可以直接在 main.tf 中添加资源并指向 module.gateway.gateway_id。

不需要新的 Google OAuth 客户端、新的 Cognito 域名或新的 Gateway——module.auth 和 module.gateway 里的所有内容都是共享的。

目录结构

src/
  gviz.js                  Sheets-reading logic — token refresh, gviz query, response
                            parsing. Host-agnostic; used by gateway-tool-handler.mjs.
  gateway-tool-handler.mjs AgentCore Gateway Lambda-target contract for query_sheet —
                            flat event-in/JSON-out, no JSON-RPC framing (Gateway
                            handles MCP protocol translation itself).
  package.json              {"type": "module"} — required for gviz.js's ESM syntax to
                            resolve once zipped alone, without the repo root's
                            package.json alongside it.
scripts/
  phase1-test.mjs           Standalone local proof the Sheets credential + gviz query
                            round-trip works, no AWS involved.
  tf.sh                     Wraps `terraform` with GOOGLE_*/Cognito vars sourced from
                            .env — use this instead of calling terraform directly.
terraform/
  main.tf                   Root — provider, variables, the shared auth module, the
                            claude.ai connector's Cognito app client, the Gateway, and
                            the query_sheet tool Lambda.
  modules/mcp-auth/         Cognito User Pool + Google identity provider + Hosted UI
                            domain. Shared — instantiate once per AWS account.
  modules/agentcore-gateway/ The Gateway (CUSTOM_JWT authorizer) + the query_sheet
                            Gateway Target. Extend for more targets, or add more
                            gateways for a genuinely separate trust boundary.
  modules/gateway-tool-lambda/ A standalone tool Lambda for a Gateway target — no
                            Function URL, no public permissions, no own Cognito
                            client. Gateway is the only caller, via its service role.
  iam-policy.json            Deploy-time IAM policy for whatever AWS identity runs
                            scripts/tf.sh. Broad on bedrock-agentcore:* deliberately —
                            that service/provider surface is new and evolving.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server for Google Drive, Docs, and Sheets — built for Claude Code. Gives Claude Code direct read/write access to Google Sheets (cell-level edits, formatting, structure), Google Docs (insert, replace, append), and Drive (search).
    49 npm
    1
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    MCP server for Claude Desktop that provides tools to read/write Google Sheets, manage Gmail, schedule Google Calendar events, and run queries on Neon Postgres databases.
    -