Skip to main content
Glama
amar-p6

shared-skill-mcp

by amar-p6

shared-skill-mcp

Развёрнутое в AWS описание сервера MCP для пользовательских коннекторов claude.ai, думать о котором как об 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.

Параметр

Значение

URL MCP-сервера (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 — выполняет 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-приложений, которые должны оставаться разными: один используется Cognito для входа (федеративная идентичность), другой — gviz.js для чтения таблиц (сервисные учётные данные на базе refresh-токена, для интерактивного режима не применяются). Использовать один и тот же клиент для обеих целей намеренно избегали — см. концепцию «two identities» (двух идентичностей) из документа спецификации.

Как этот репозиторий пришёл к текущему виду (стоит прочитать перед изменением настройки auth)

Первая рабочая версия использовала самодельный Лямбда-шлюз Function URL как MCP-сервер, где сама Lambda проверяла JWT Cognito и вручную отдавала discovery-метаданные OAuth (RFC 9728 / RFC 8414). Она работала по curl и ручному OAuth-сценарию в Postman — полный круговой путь: реальный вход Google, реальные данные таблицы, — но фактический клиент-коннектор claude.ai при обращении к этому серверу каждый раз молча отваливался (Couldn't connect / Authorization failed), не давая ни намёка на причину: в логах Lambda было видно, что claude.ai один раз запрашивает discovery-метаданные, а потом замирает — ни обмена токеном, ни ошибки, ничего.

Рабочая гипотеза в тот момент: access-токены Cognito не содержат claim aud (известное реальное ограничение Cognito — подтверждено декодированием настоящего токена), а спецификация MCP ожидает, что параметр resource, отправляемый клиентом, будет отражён в этом claim. Недавние, выглядевшие правдоподобно факты из других мест экосистема, казалось, подтверждали: это тот самый ответ. Оно оказалось ложным следом — рабочий эталон из внешнего проекта (тот же человек, другой репозиторий) доказал, что связка Cognito + коннектор claude.ai работает прекрасно, если перед Cognito, а не самодельным сервером, стоит Bedrock AgentCore Gateway, и его авторизатор CUSTOM_JWT сопоставляет вызывающих по allowed_clients (это client_id Cognito), a (а не) не по aud. Настоящая причина исходного сбоя так и не была однозначно установлена — вероятнее всего, что-то в самодельной реализации JSON-RPC/discovery не вполне совпадало с тем, чего ждёт клиент claude.ai, несмотря на прохождение всех проверок вручную, которые ему устроили.

Урок на будущее: самодельный MCP-сервер, который выглядит соответствующим спецификации и проходит тесты curl и Postman, еще не доказывает, что он работает с настоящим клиентом claude.ai, — они могут расходиться так, что не дают ни одной сигнальной ошибки. Ставьте собственную реализацию MCP-сервера от AWS (AgentCore Gateway) вместо ручной реализации MCP + OAuth discovery, даже если ради этого придётся освоить ещё один AWS-старвис и иметь дело с более шероховатой Terraform-поверхностью провайдера (см. грабли ниже).

Самодельный сервер на Function URL (sheets-gviz-mcp на Lambda, modules/mcp-lambda, src/lambda-handler.mjs, src/auth.mjs) был выведен из эксплуатации, как только Gateway подтвердил работу: снёсен через Terraform (ноль влияния на остающийся живым Cognito User Pool/domain/app client, который путь Gateway использует как есть) и удалён из репозитория. Он в истории git, если решения и код снова понадобятся.

Попутные грабли (мелочи уже исправлены в коде, но знать о них стоит, прежде чем снова всё это трогать)

Слой Sheets/gviz (src/gviz.js):

  1. OAuth-токену нужны две области — .../auth/spreadsheets и .../auth/spreadsheets.readonly; одного readonly даст 401, который похожа на HTML-страницу входа, а не на нормальную ошибку.

  2. У gviz эндпоинт /tq требует сегмент пути /a/<domain>/, даже для OAuth-доступа (bearer): docs.google.com/a/google.com/spreadsheets/d/<id>/gviz/tq. Это настраивается через GVIZ_DOMAIN_SEGMENT, если google.com не подходит для твоего домена учетной- эки-записи.

  3. Всегда передавай headers=1 (жёстко зашито в querySheet). Без него автоопределение строки заголовков у gviz может ошибиться и молча сложить реальные строки данных в cols[].label одной огромной строкой, полностью потеряв их.

AWS/Terraform-слой: 4. Изменения identity-политик IAM могут реально вступать в силу в течение десятков секунд или даже нескольких минут, даже если после это сразу aws iam simulate-principal-policy подтверждает их корректность. Ожидай: свежий plan/apply после того, как дали новое действие, вернёт 403, — это нормально; просто подожди немного и повтори. 5. ESM-файлам .js нужен собственный package.json ({"type": "module"}), если из катают без корневого package.json из репозитория. gviz.js использует export/import и распознается как ESM только потому, что этот package.json находится рядом с ним в zip-архиве каждого Lambda (src/package.json). 6. Terraform-ресурсы aws_bedrockagentcore_* — недавние и текучие; сверь фактическую форму аргументов сную схему самого провайдера, абез доверяя устаревающим статьям и докам (terraform providers schema -json). Нужен провайдер >= 6.0. 7. Авторизатор CUSTOM_JWT у шлюза AgentCore Gateway сопоризует вызывающих по allowed_clients (то есть client_id Cognito), а не по allowed_audience — именно это позволяет ему работать со средствами нестандартными (без aud) токенами Cognito без дополнительного слоя создания токенов. 8. Упаковка существующих живых Terraform-ресурсов в модули была рискованной: и рефакторинг фазы 4 в модули, и последующий вывод Function URL из эксплуатации делались через terraform state mv и реальную проверку plan (0 destroy на всё, что должно остаться) перед каждым apply. Так app client, для которого у claude.ai уже есть учётные данные, перемещался дважды и никогда при этом ломался / не пересоздавался.

Setup

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 и второй ГмGoogle OAuth-клиент (веб-приложение, отдельный от того, ЭМ-файл от Google Sheets, для логина через 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://<field from the output>/oauth2/idpresponse, наполните GOOGLE_LOGIN_CLIENT_ID/GOOGLE_LOGIN_CLIENT_SECRET в .env, а затем:

scripts/tf.sh apply

CLAUDE_OAUTH_REDIRECT_URI настраивать не нужно — по умолчанию это https://claude.ai/api/mcp/auth_callback, и рабоспособность с реальным коннектором подтверждена.

3. Добавить его как коннектор в claude.ai

Настройки → Коннекторы → Добавить пользовательский коннектор:

  • URL сервера: output gateway_url

  • Дополнительно → OAuth Client ID/Secret: outputs cognito_client_id / cognito_client_secret

Он должен спровоцировать настоящий вход через Google Cognito Hosted UI и затем дать Claude вызывать query_sheet (в чате видно как блок tool-use).

Добавление нового инструмента

  1. Напишите Lambda-handler, следуя контракту AgentCore Lambda-target: плоское событие event = аргументы инструмента, без JSON-RPC-обёртки (Gateway сам занимается MCP-фреймлей). Пример паттерна — src/gateway-tool-handler.mjs.

  2. Добавьте блок module "..." { source = "./modules/gateway-tool-lambda" ... } в файл main.tf.

  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.
    -