shared-skill-mcp
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) |
|
Cognito ドメイン |
|
ユーザープール ID |
|
AWS アカウント |
|
現在の値(シークレットを含む)はいつでも取得できます:
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 APIGoogle 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):
OAuth トークンには
.../auth/spreadsheetsと.../auth/spreadsheets.readonlyの両方のスコープが必要です。readonlyだけだと 401 が返り、実際のエラーではなくHTML のログインページのように見えます。gviz の
/tqエンドポイントは、OAuth Bearer認証でも/a/<domain>/のパスセグメントが必要です —docs.google.com/a/google.com/spreadsheets/d/<id>/gviz/tq。自分のアカウントのドメインでgoogle.comが使えない場合は、GVIZ_DOMAIN_SEGMENTで設定できます。常に
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 applyCLAUDE_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 ブロックとして表示されます)。
新しいツールを追加する
AgentCore Lambda-target の契約に従った Lambda ハンドラを書くこと。フラットな
event= ツールの引数で、JSON-RPC ラッピングはありません(Gateway が MCP フレームを処理します)。パターンはsrc/gateway-tool-handler.mjsを参照。main.tfにmodule "..." { source = "./modules/gateway-tool-lambda" ... }ブロックを追加する。そのために
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.This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
Hosted MCP server for GA4, Google Ads and Search Console. Google OAuth, nothing to install.
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
Live Google Ads, GA4, Search Console, Meta Ads and GBP data in Claude, ChatGPT and any MCP client
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceMCP 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 npm1MIT
- FlicenseNot gradedqualityDmaintenanceEnables interaction with Google Sheets, Docs, Slides, and Drive through a remote MCP server hosted on Cloudflare Workers, with OAuth authentication and Claude-native connect.-
- FlicenseNot gradedqualityDmaintenanceMCP 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.-
- AlicenseBqualityCmaintenanceMCP server that gives Claude full read-write access to Google Drive, Docs, Sheets, and Slides using your own Google OAuth credentials and hosted server.25MIT