Skip to main content
Glama
Runemal

Google Sheets and Drive MCP

by Runemal
README.md
# Google Sheets and Drive MCP

Локальный пакет с двумя MCP-серверами по `stdio`:

- `google-sheets-mcp` предоставляет 25 инструментов для предсказуемого чтения,
  изменения и анализа Google-таблиц;
- `google-drive-mcp` предоставляет поиск и перечисление таблиц, а также отдельный
  чувствительный инструмент для выдачи editor-доступа к разрешённым файлам.

## Инструменты MCP

- `health` — диагностика конфигурации и лимитов;
- `get_spreadsheet` — метаданные таблицы и список листов;
- `create_sheet` — создание пустого листа с ограниченным начальным размером;
- `rename_sheet` — переименование существующего листа;
- `copy_sheet` — полное дублирование листа внутри той же таблицы;
- `read_range` — чтение ограниченного A1-диапазона;
- `batch_read_ranges` — чтение нескольких ограниченных диапазонов одним API-запросом;
- `read_sheet` — безопасное постраничное чтение листа с `next_start_row`;
- `write_range` — запись прямоугольной матрицы значений;
- `batch_write_ranges` — запись нескольких прямоугольных диапазонов одним API-запросом;
- `clear_range` — очистка значений в одном ограниченном диапазоне;
- `append_rows` — добавление строк в конец табличного диапазона;
- `unpivot_range` — преобразование широкой таблицы в длинную с записью результата;
- `create_pivot_table` — создание нативной сводной таблицы Google Sheets;
- `create_chart` — создание нативной диаграммы Google Sheets;
- `set_basic_filter` и `clear_basic_filter` — установка и снятие фильтра;
- `sort_range` — сортировка по одному или нескольким заголовкам;
- `add_conditional_format` — цветовое условное форматирование;
- `freeze_panes` — закрепление верхних строк и левых колонок;
- `write_formulas` — запись прямоугольной матрицы формул;
- `insert_image` — вставка изображения внутрь ячейки по HTTP(S)-URL;
- `copy_range` — копирование значений, формул, форматов и правил;
- `grant_read_access` — выдача пользователю права `reader` на всю таблицу;
- `set_sheet_visibility` — скрытие и возврат листа.

`create_chart` поддерживает все нативные семейства диаграмм Google Sheets:
`COLUMN`, `BAR`, `LINE`, `AREA`, `SCATTER`, `COMBO`, `STEPPED_AREA`, `PIE`,
`HISTOGRAM`, `BUBBLE`, `WATERFALL`, `SCORECARD`, `CANDLESTICK`, `TREEMAP` и `ORG`.
`create_pivot_table` поддерживает группировки по строкам и столбцам, итоги и
стандартные функции агрегации: `SUM`, `COUNT`, `AVERAGE`, `MIN`, `MAX` и другие.

Для условного форматирования используются цвета `#RRGGBB`. Например, два
вызова с условиями `NUMBER_GREATER` и `NUMBER_LESS` позволяют выделить
положительные значения зелёным, а отрицательные — красным.

`batch_read_ranges` принимает список bounded A1-диапазонов и применяет лимит
чтения к их суммарному потенциальному размеру. `batch_write_ranges` принимает
список объектов `{"range": ..., "values": ...}` и применяет лимит записи к
суммарному числу ячеек во всех прямоугольных матрицах. Порядок диапазонов
сохраняется; batch-write является идемпотентной перезаписью указанных диапазонов.

`clear_range` требует явно указать лист и bounded A1-диапазон, например
`'Data'!A1:D20`. Операция учитывает общий write-лимит и очищает только значения:
форматирование, data validation, комментарии и размеры листа не изменяются.

`create_sheet` создаёт пустой лист с настраиваемым числом строк и колонок;
начальная сетка ограничена одним миллионом ячеек и 18 278 колонками.
`rename_sheet` сохраняет данные, позицию и свойства листа. `copy_sheet`
дублирует весь лист только внутри той же Google-таблицы, включая данные и
оформление; копирование между разными `spreadsheet_id` не выполняется.

`set_basic_filter` создаёт один нативный Basic Filter и заменяет предыдущий
Basic Filter листа. `clear_basic_filter` снимает его. `freeze_panes` принимает
число закреплённых строк и колонок; нулевые значения снимают закрепление.

`write_formulas` принимает только строки, начинающиеся с `=`. `insert_image`
использует нативную формулу `IMAGE()` и требует URL, доступный Google; локальные
файлы сначала нужно разместить в доступном хранилище. Поддерживаются режимы
вписывания, растягивания, исходного и пользовательского размера.

`copy_range` работает внутри одной Google-таблицы и поддерживает `PASTE_VALUES`,
`PASTE_FORMULA`, `PASTE_FORMAT`, `PASTE_NORMAL`, перенос data validation,
условного форматирования и транспонирование. Для копирования между разными
таблицами можно использовать связку `read_range` → `write_range`.

`grant_read_access` работает через Google Drive API и выдаёт доступ ко всему
файлу таблицы — Google не поддерживает отдельные права чтения для одного листа.
Для работы с существующими файлами, которые были расшарены на service account,
Drive-клиенту требуется полный scope `https://www.googleapis.com/auth/drive`:
узкий `drive.file` такие файлы не видит. Сервисный аккаунт также должен иметь
право делиться файлом, а доменная политика Google Workspace может запрещать
внешний доступ.

`set_sheet_visibility` не является механизмом защиты: скрытый лист остаётся
частью таблицы и доступен через API. Инструмент не позволяет скрыть последний
видимый лист и поддерживает обратную операцию через `hidden=false`.

## Google Drive MCP

Отдельный `google-drive-mcp` предоставляет четыре инструмента:

- `health` — диагностика credentials и собственных границ безопасности;
- `list_spreadsheets(page_size=50, page_token=null)` — перечисление доступных
  Google-таблиц с пагинацией;
- `search_spreadsheets(name_query, page_size=50, page_token=null)` — безопасный
  поиск по буквальной подстроке имени без произвольного Drive query;
- `grant_edit_access(file_id, email, send_notification_email=false)` — выдача
  пользователю фиксированной роли `writer` на весь файл.

Read-инструменты возвращают только `file_id`, имя и время последнего изменения.
Размер страницы ограничен диапазоном 1–100. Если allowlist пуст, используются
нативные page tokens Google Drive. При включённом allowlist сервер запрашивает
только явно разрешённые ID через `files.get`, фильтрует неподходящие и недоступные
файлы и использует собственный непрозрачный page token.

Если permission отсутствует, сервер создаёт его. Существующий `reader` или
`commenter` повышается до `writer`; `writer`, `fileOrganizer`, `organizer` и
`owner` возвращают успешный no-op и никогда не понижаются. Произвольные роли,
передача владения, доменные permissions, revoke и downgrade не поддерживаются.
Google Drive не позволяет выдать editor-доступ только к одному листу или диапазону:
операция меняет доступ ко всему файлу.

## Требования

- Python 3.12–3.14
- `uv`
- Google Cloud service account с включёнными Google Sheets API и Google Drive API

## Установка

```bash
UV_CACHE_DIR=.uv-cache uv sync
```

Скопируйте `.env.example` в локальный `.env` либо экспортируйте переменные в
окружение MCP-клиента. JSON-ключ и `.env` нельзя добавлять в репозиторий.

## Подготовка Google Cloud

1. Включите Google Sheets API в нужном Google Cloud project.
2. Создайте service account и скачайте JSON-ключ в каталог вне репозитория.
3. Расшарьте тестовую таблицу на email service account с нужными правами.
4. Установите `GOOGLE_APPLICATION_CREDENTIALS` в абсолютный путь к ключу.

## Запуск

```bash
UV_CACHE_DIR=.uv-cache uv run google-sheets-mcp
UV_CACHE_DIR=.uv-cache uv run google-drive-mcp
```

Оба сервера используют `stdio`: обычный вывод зарезервирован под MCP-протокол.
Диагностический инструмент `health` проверяет конфигурацию без обращения к Google API.

## Подключение к Codex

Codex CLI, IDE extension и desktop app используют общую MCP-конфигурацию.
Зарегистрировать локальный сервер можно командой:

```bash
codex mcp add google_sheets \
  --env GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
  --env GOOGLE_SHEETS_MAX_READ_CELLS=100000 \
  --env GOOGLE_SHEETS_MAX_WRITE_CELLS=10000 \
  --env GOOGLE_SHEETS_REQUEST_TIMEOUT_SECONDS=30 \
  --env GOOGLE_SHEETS_MAX_RETRIES=2 \
  --env GOOGLE_SHEETS_RETRY_BASE_DELAY_SECONDS=0.5 \
  --env GOOGLE_SHEETS_READ_ONLY=true \
  --env GOOGLE_SHEETS_ALLOWED_SPREADSHEET_IDS=spreadsheet-id \
  -- /absolute/path/to/google_sheets_tool/.venv/bin/google-sheets-mcp

codex mcp add google_drive \
  --env GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
  --env GOOGLE_DRIVE_REQUEST_TIMEOUT_SECONDS=30 \
  --env GOOGLE_DRIVE_READ_ONLY=false \
  --env GOOGLE_DRIVE_ALLOWED_FILE_IDS=file-id \
  -- /absolute/path/to/google_sheets_tool/.venv/bin/google-drive-mcp
```

Проверить регистрацию:

```bash
codex mcp get google_sheets
codex mcp get google_drive
codex mcp list
```

После изменения MCP-конфигурации перезапустите Codex или IDE extension. В TUI
список подключённых серверов доступен через `/mcp`. JSON-ключ не следует
копировать в репозиторий или записывать непосредственно в `config.toml`;
конфигурация хранит только абсолютный путь к нему.

Эквивалентная ручная конфигурация в `~/.codex/config.toml`:

```toml
[mcp_servers.google_sheets]
command = "/absolute/path/to/google_sheets_tool/.venv/bin/google-sheets-mcp"
startup_timeout_sec = 10
tool_timeout_sec = 60
enabled = true

[mcp_servers.google_sheets.env]
GOOGLE_APPLICATION_CREDENTIALS = "/absolute/path/to/service-account.json"
GOOGLE_SHEETS_MAX_READ_CELLS = "100000"
GOOGLE_SHEETS_MAX_WRITE_CELLS = "10000"
GOOGLE_SHEETS_REQUEST_TIMEOUT_SECONDS = "30"
GOOGLE_SHEETS_MAX_RETRIES = "2"
GOOGLE_SHEETS_RETRY_BASE_DELAY_SECONDS = "0.5"
GOOGLE_SHEETS_READ_ONLY = "true"
GOOGLE_SHEETS_ALLOWED_SPREADSHEET_IDS = "spreadsheet-id"

[mcp_servers.google_drive]
command = "/absolute/path/to/google_sheets_tool/.venv/bin/google-drive-mcp"
startup_timeout_sec = 10
tool_timeout_sec = 60
enabled = true

[mcp_servers.google_drive.env]
GOOGLE_APPLICATION_CREDENTIALS = "/absolute/path/to/service-account.json"
GOOGLE_DRIVE_REQUEST_TIMEOUT_SECONDS = "30"
GOOGLE_DRIVE_READ_ONLY = "false"
GOOGLE_DRIVE_ALLOWED_FILE_IDS = "file-id"
```

## Границы безопасности

`GOOGLE_SHEETS_READ_ONLY=true` запускает сервер с Google scope
`spreadsheets.readonly`, убирает изменяющие инструменты из MCP discovery и
дополнительно отклоняет прямые попытки их вызова. Доступными остаются `health`,
`get_spreadsheet`, `read_range`, `batch_read_ranges` и `read_sheet`.

`GOOGLE_SHEETS_ALLOWED_SPREADSHEET_IDS` принимает разделённый запятыми список
точных `spreadsheet_id`. Если список непустой, любой инструмент отклоняет работу
с другими таблицами до создания Google API-клиента. Пустое значение сохраняет
режим без ограничений. Параметры можно совмещать:

```bash
GOOGLE_SHEETS_READ_ONLY=true
GOOGLE_SHEETS_ALLOWED_SPREADSHEET_IDS=spreadsheet-id-1,spreadsheet-id-2
```

Инструмент `health` сообщает только состояние режимов и количество разрешённых
таблиц, но не раскрывает сами идентификаторы.

`GOOGLE_DRIVE_READ_ONLY=true` убирает `grant_edit_access` из discovery и блокирует
его прямой вызов; Drive-клиент получает scope `drive.metadata.readonly` вместо
полного `drive`. `GOOGLE_DRIVE_ALLOWED_FILE_IDS` принимает разделённый запятыми
список точных `file_id`. При непустом списке list/search обращаются только к этим
ID, а editor-доступ к любому другому файлу отклоняется до создания API-клиента.
Пустой список разрешает любой видимый service account файл. Drive `health`
сообщает только факт включения allowlist и число файлов, не раскрывая ID.

## Повторы временных ошибок

Для HTTP `429`, `500`, `502`, `503` и `504` сервер выполняет ограниченный
exponential backoff. По умолчанию разрешены два повтора с задержками 0,5 и
1 секунду. `GOOGLE_SHEETS_MAX_RETRIES=0` полностью отключает повторы; допустимый
диапазон — от 0 до 5. Начальная задержка задаётся через
`GOOGLE_SHEETS_RETRY_BASE_DELAY_SECONDS`: больше 0 и не более 60 секунд.

Автоматически повторяются только чтения и операции с однозначным конечным
состоянием: `write_range` (включая формулы и изображения),
`batch_write_ranges`, `clear_range`, `rename_sheet`, установка и снятие Basic
Filter, freeze panes и visibility.
Сервер никогда автоматически не
повторяет `append_rows`, `grant_read_access`, `create_sheet`, `copy_sheet`,
создание диаграмм, pivot-таблиц и условных правил, сортировку или копирование
диапазона.

`google-drive-mcp` также не повторяет create/update permission. После timeout
вызывающая сторона должна сначала проверить фактическую роль и только затем
решать, нужен ли отдельный ручной повтор.

## Ограничения

- сервер рассчитан на локальный однопользовательский запуск по `stdio`;
- авторизация выполняется только через Google Cloud service account;
- allowlist ограничивает таблицы целиком, но не отдельные листы или диапазоны;
- `grant_read_access` использует полный Google Drive scope и меняет доступ ко
  всему файлу, а не к одному листу;
- `grant_edit_access` находится только в `google-drive-mcp`, всегда запрашивает
  роль `writer` для всего файла и требует capability `canShare`;
- `insert_image` требует HTTP(S)-URL, доступный серверам Google;
- сервер не создаёт и не удаляет Google-таблицы целиком;
- multi-step операции не являются транзакциями Google Sheets;
- после timeout неоднозначной операции вызывающая сторона должна проверить
  фактическое состояние перед ручным повтором.

## Разработка

```bash
UV_CACHE_DIR=.uv-cache uv run ruff check .
UV_CACHE_DIR=.uv-cache uv run pytest
UV_CACHE_DIR=.uv-cache uv build
UV_CACHE_DIR=.uv-cache .venv/bin/python scripts/check_release_artifacts.py
UV_CACHE_DIR=.uv-cache .venv/bin/python scripts/smoke_package.py
```

GitHub Actions выполняет те же проверки на Python 3.12 для каждого push и pull
request: устанавливает зависимости строго из `uv.lock`, запускает Ruff и Pytest,
затем собирает sdist и wheel и устанавливает wheel в чистое временное окружение.

Явный smoke-тест чтения не выводит значения ячеек:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_read.py
```

Smoke-тест записи создаёт или переиспользует изолированный лист `_mcp_test`,
очищает только `A:Z` на этом листе и проверяет запись чтением обратно:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_write.py
```

Аналитический smoke-тест пересоздаёт только `_mcp_test`, затем проверяет
постраничное чтение, unpivot, нативную сводную таблицу и диаграмму:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_analytics.py
```

Smoke-тест представления проверяет все типы диаграмм, фильтр, сортировку,
условное форматирование и закрепление панелей:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_presentation.py
```

Smoke-тест содержимого проверяет формулы, копирование значений и формул, а
также вставку изображения в ячейку:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_content.py
```

Smoke-тест доступа проверяет Drive capability `canShare`, временно скрывает
`_mcp_test`, подтверждает состояние и возвращает лист видимым:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_access.py
```

Отдельный smoke-тест безопасно выдаёт право чтения: сначала проверяет наличие
permission, не создаёт дубликат и подтверждает результат чтением обратно.
Уведомление пользователю не отправляется:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
GOOGLE_SHEETS_TEST_READER_EMAIL=user@example.com \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_grant_access.py
```

Полный последовательный прогон всех smoke-тестов:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
GOOGLE_SHEETS_TEST_READER_EMAIL=user@example.com \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_all.py
```

Прогон изменяет только тестовый лист `_mcp_test`. Аналитические, presentation и
content-сценарии пересоздают этот лист, поэтому хранить на нём рабочие данные
нельзя.

Проверка всех инструментов через настоящий MCP `stdio`-протокол:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
GOOGLE_SHEETS_TEST_READER_EMAIL=user@example.com \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_mcp_stdio.py
```

Отдельный Drive MCP smoke проверяет discovery четырёх инструментов, list/search
строго внутри allowlist, выдачу writer-доступа без уведомления и восстановление
исходного permission в `finally`:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_DRIVE_TEST_FILE_ID=file-id \
GOOGLE_DRIVE_TEST_EDITOR_EMAIL=user@example.com \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_drive_mcp_stdio.py
```

Полный release-smoke объединяет Sheets API, оба MCP `stdio` сценария и временные
проверки permissions. Подробный вывод дочерних сценариев подавляется, а исходный
permission тестового пользователя восстанавливается в `finally`:

```bash
GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json \
GOOGLE_SHEETS_TEST_SPREADSHEET_ID=spreadsheet-id \
GOOGLE_SHEETS_TEST_READER_EMAIL=user@example.com \
UV_CACHE_DIR=.uv-cache uv run python scripts/smoke_release.py
```

## Лицензия и релиз

Проект распространяется по лицензии [MIT](LICENSE). История изменений находится
в [CHANGELOG.md](CHANGELOG.md), а обязательные проверки перед публикацией — в
[RELEASE_CHECKLIST.md](RELEASE_CHECKLIST.md).

Полный план и границы MVP описаны в [PLAN.md](PLAN.md).

TDQS

B3.2/5.0

Scored across 25 tools

Disambiguation5/5

Each tool has a distinct and clearly defined purpose. Even similar tools like read_range, batch_read_ranges, and read_sheet are differentiated by scope (bounded range, multiple ranges, entire sheet page). No overlapping functionality.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case (e.g., get_spreadsheet, clear_range, create_sheet). The only exception is 'health', but it is a simple status check and fits the pattern if considered as a verb implied. Overall, naming is highly predictable.

Tool Count4/5

25 tools is slightly above the typical 3-15 range, but each tool provides a specific, non-redundant operation. The count is justified by the breadth of Google Sheets functionality covered, though it could be trimmed (e.g., batch operations could be merged).

Completeness3/5

The tool set covers many common operations but has notable gaps: no delete_sheet, no create_spreadsheet, no delete_rows, and no cross-spreadsheet copy/move. This will cause agents to struggle with basic workflows like creating and deleting spreadsheets or deleting data.

Maintenance

ActivitySlowing
ResponsivenessNo issues