Skip to main content
Glama
amar-p6

shared-skill-mcp

by amar-p6

shared-skill-mcp

claude.ai のカスタムコネクタ向けの AWS ホスティング MCP サーバーです。時間経過に複数のツールを保持できるように構築されており、最初に実装した Google Sheets クエリツールに限定されるものではありません。共有の Cognito 認証層(Google ログイン)+ 1 つの Bedrock AgentCore Gateway(実際の MCP サーバー)+ ツールごとの 1 つの 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-gzviz-gateway-63psjdjcvs.gateway.bedrock-agentcore.eu-west-1.amazonaws.com/mcp

Cognito ドメイン

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

ユーザープール ID

eu-west-1_sfGnYcC0a

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 — は gviz クエリ(SQL 風: select/where/group by/pivot/order by)を Google Sheets に対して実行します。

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 クライアントは 2 つあり、常に別々のままにしなければなりません。1 つは Cognito、もう 1 つは Cognito(フェ她已经)。 gviz.js が Sheets を読むために使う1つ(サービス認証情報、リフレッシュトークンベース、インタラクティブには使わない)。両方に1つを再利用することは意図的に避けています — 仕様書の「2つのアイデンティティ」という考え方を参照してください。

このリポジトリができた経緯(認証設定を変更する前に読んでおく価値あり)

最初に動作したバージョンでは、手作りの Lambda Function URL を MCP サーバーとして使っていました。Lambda 自身が Cognito JWT を検証し、OAuth ディスカバリメタデータ(RFC 9728 / RFC 8414)を手動で配信ししていました。curl と手動の Postman OAuth フローでは動いていました — 完全なラウンドトリップ、実際の Google ログイン、実際の Sheet データ — しかし、claude.ai の実際のコネクタクライアントは、毎回それに対して静かに失敗していました(Couldn't connect / Authorization failed)。理由は何も分かりませんでした。Lambda のログには、claude.ai が discovery メタデータを一度取得した後、沈黙する姿が残っていました — トークン交換もエラーも何もありません。

当時の作業仮説は、Cognito の access tokenには aud クレームが無い(既知の Cognito の制約であり、実トークンをデコードして確認した)こと、そして MCP 仕様では、クライアントが送信する resource パラメータがそのクレームに反映されると想定していることでした。周辺エコシステムの別のところからの、最近出た信頼できるような証拠が、これを 正解 のように見せていました。しかし実際にはそれは false でした。同じ人物の別プロジェクトにあった実際の reference実装により、Cognito + claude.ai のコネクタは、手作りサーバの代わりに Bedrock AgentCore Gateway を Cognito の front に配置し、その CUSTOM_JWT オーソライザが allowed_audience でなく allowed_clients(CogniTo の client_id)で照合することで問題なく動くことを実証できました。最初の失敗の真の根本原因は決定的には分かっていません — おそらく、自作の JSON-RPC/discovery 実装が、claude.ai のクライアントが期待するものと「マニュアルテストは全て通るが」微妙に合っていなかったのだと思われます。

教訓: curl とPostmanのテストを通過したような custom made MCP server は、claude.ai の実際のクライアントで動く保証にはなりません。両者はエラーサインを一切出さずに divergence することができます。MCP + OAuth discovery を手作りで再実装するよりも、AWS 自身の MCP サーバ実装(AgentCore Gateway)を選ぶべきです。たとえそれで追加のAWSサービスを学ぶことになり、現在のTerraform provider の粗さに悩まされても(後述の注意点)。

自作の Function URL サーバー(sheets-gviz-mcp Lambda、modules/mcp-lambda、src/lambda-handler.mjs、src/auth.mjs)は、Gateway が動作を確認できた後に廃止しました。Terraform で撤去(現存する Cognito User Pool ドメイン、アプリクライアントには無影響。Gateway 経路はそのまま再利用)し、リポジトリからも削除しました。この思考がまた必要になった時のために、git 履歴には残っています。

これまでの落とし穴(コードでは修正済み、再び触れる前に知っておくとよい)

Sheets/gviz レイヤー(src/gviz.js):

  1. OAuth トークンには .../auth/spreadsheets と .../auth/spreadsheets.readonly の両方のスコープが必要です。readonly だけだと 401 が返り、実際のエラーではなくHTML のログインページのように見えます。

  2. gviz の /tq エンドポイントは、OAuth Bearer認証でも /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 に 1 つの巨大な文字列として包み込み、データを完全に失ってしまうことがあります。

AWS/Terraform レイヤー: 4. IAM アイデンティティポリシーの変更は、aws iam simulate-principal-policy が即座に正しいと返しても、実際に反映されるまで数十秒から数分かかる場合があります。新しいアクションを付与した直後の新しい plan/apply は一度だけ 403 が出ることが想定されます。これは何かが間違っているという意味ではなく、しばらく待って再試行してください。 5. ESM の .js ファイルは、リポジトリ直root の package.json なしで zip 化される場合、自分自身の package.json({"type": "module"})が必要です。gviz.js は export/import を使っており、それらすべてのLambdaのzip内にそのファイルが(src/package.json)並んでいるからこそESMとして解決されます。 6. aws_bedrockagentcore_* Terraform リソースは最近のもので、まだ変化の途中です。ドキュメントやブログではなく(遅れることがある)、provider自身のスキーマ(terraform providers schema -json)で実際の引数型を確認してください。provider は >= 6.0 が必要です。 7. AgentCore Gateway の CUSTOM_JWT オーソライザは、呼び出し元を allowed_audience ではなく allowed_clients(Cognito の client_id)で照合します。これによって、Cognito の非標準(aud がない)アクセストークンでも余分なトークン minting 層を使わずに動作します。 8. 稼働中の Terraform リソースをモジュールにまとめ直すのは、破壊リスクがあります。Phase 4→モジュールのリファクタリングと、後に行ったFunction URLの廃止のどちらも、毎回の apply の前に terraform state mv と、実際の plan 確認(残すべきものを0破壊)を行いました。claude.ai がすでに認証情報を持っているアプリクライアントは、このように2回移動しましたが、破壊・再作成は一度もされませんでした。

セットアップ

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側(スコープ、共同設定、Sheets API無効)です — AWSに触る前に一番手軽に失敗点を見つけられます。

2. Cognito + Gateway + ツール用 Lambda をデプロイする

AWSの権限(terraform/iam-policy.json)と、Cognito ログイン用の2つ目の Google OAuth クライアント(Webアプリ、Sheets 読み取り用とは別)が必要です。そのリダイレクトURIには Cognito のドメインが必要です。しかし、そのドメインはまだ存在しません。まず部分apply でこの卵が先か鶏が先か問題を解消します:

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

リダイレクトURI を https://<that domain output>/oauth2/idpresponse にして Google OAuth クライアントを作成し、.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 のコネクタとして追加する

設定 → Connectors → Add custom connector:

  • Server URL: gateway_url 出力

  • Advanced settings → OAuth Client ID/Secret: cognito_client_id / cognito_client_secret 出力

Cognito の Hosted UI 経由で実際の Google ログインが実行され、その後 Claude が query_sheet を呼び出せるようになります(チャット内で tool-use ブロックとして表示されます)。

新しいツールを追加する

  1. AgentCore Lambda-target の契約に従った Lambda ハンドラを書くこと。フラットな event = ツールの引数で、JSON-RPC ラッピングはありません(Gateway が MCP フレームを処理します)。パターンは src/gateway-tool-handler.mjs を参照。

  2. main.tf に module "..." { source = "./modules/gateway-tool-lambda" ... } ブロックを追加する。

  3. そのために aws_bedrockagentcore_gateway_target ブロックを追加する — modules/agentcore-gateway をターゲットのリストを受け取るように拡張するか、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.
    -