mcp-gee-sweet 0.8.2.dev45__tar.gz → 0.8.2.dev51__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/commands/merge-pr.md +4 -2
- mcp_gee_sweet-0.8.2.dev51/.claude/commands/retro.md +23 -0
- mcp_gee_sweet-0.8.2.dev51/.claude/settings.json +5 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/team-roles/amy.md +8 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/team-roles/aziz.md +9 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/team-roles/dev.md +9 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/team-roles/kai.md +8 -0
- mcp_gee_sweet-0.8.2.dev51/.claude/team-roles/qa.md +23 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/CLAUDE.md +1 -1
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/PKG-INFO +3 -3
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/README.md +2 -2
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/configuration.md +1 -1
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/run.md +1 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/tests/sheets_mgmt.md +261 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/roadmap.md +2 -2
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/tools.md +4 -1
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/sheets/structure.py +283 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/sheets/test_structure.py +291 -0
- mcp_gee_sweet-0.8.2.dev45/.claude/team-roles/qa.md +0 -13
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/commands/cleanup-worktrees.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/commands/orchestrator.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/commands/prep-for-pr.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/commands/release.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/commands/start-worktree.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/commands/team-member.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.claude/commands/verify-pr.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.dockerignore +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.github/dependabot.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.github/workflows/ci.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.github/workflows/docs.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.github/workflows/publish-dev.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.github/workflows/release.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.gitignore +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.pre-commit-config.yaml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/.python-version +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/CHANGELOG.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/CODE_OF_CONDUCT.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/CONTRIBUTING.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/Dockerfile +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/LICENSE +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/Makefile +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/SECURITY.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docker-compose.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/auth.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/client-setup.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-async-tool-execution.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-cache-invalidation.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-chart-theming.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-composite-tools.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-dev-team.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-docs-formatting.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-fork.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-grid-data-size-cap.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-publishing.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-release-docs-roles.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-repositioning.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-response-size-cap-generalization.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/decision-testing.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/decisions/index.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/design/docs-ast-pipeline.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/design/effectiveformat-spike.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/design/index.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/design/markdown-support.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/design/phase3-theme-system.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/design.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/index.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/known-limitations.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/notes-read.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/README.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/fixtures/tc-d195-create-doc.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/fixtures/tc-d196-create-doc.html +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/fixtures/tc-d213-dollar-escape.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/fixtures/tc-d226-heading-table.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/fixtures.template.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/operations.yaml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/playwright_oauth.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/.gitkeep +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-06-02.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-06-14.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-06-17.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-06-18.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-06-21-drive-discovery.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-06-21-sheets-delete-clear.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-06-28.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-07-04-docs.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-07-04-drive-smoke.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-07-04-infra.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-07-04-sheets_read.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/results/2026-07-04.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/retro-v0.8.0.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/runs/README.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/runs/v0.8.0.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/runs/v0.8.1.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/setup.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/tests/calendar.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/tests/docs.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/tests/drive.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/tests/infra.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/tests/sheets_charts.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/tests/sheets_read.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa/tests/sheets_write.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/docs/qa-checklist.md +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/mkdocs.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/pyproject.toml +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/pyrightconfig.json +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/scripts/gen_tool_docs.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/scripts/oauth_setup.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/scripts/setup_team.sh +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/.env.template +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/auth.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/cache.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/http_transport.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/server.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/cache.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/calendar.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/docs/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/docs/ast.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/docs/content.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/docs/emitter.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/docs/html_parser.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/docs/layout.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/docs/style.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/docs/tables.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/drive/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/drive/activity.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/drive/files.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/drive/sharing.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/drive/transfer.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/response_limits.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/sheets/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/sheets/data.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/src/mcp_gee_sweet/tools/sheets/helpers.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/drive/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/drive/test_activity.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/drive/test_files.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/drive/test_sharing.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/drive/test_transfer.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/sheets/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/sheets/test_data.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/sheets/test_helpers.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_auth.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_cache.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_calendar.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_docs_content.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_docs_core.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_docs_layout.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_docs_style.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_docs_tables.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_gen_tool_docs.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_response_limits.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/tests/test_server.py +0 -0
- {mcp_gee_sweet-0.8.2.dev45 → mcp_gee_sweet-0.8.2.dev51}/uv.lock +0 -0
|
@@ -6,6 +6,7 @@ Merge the current branch's PR and clean up its worktree. Uses `--admin` only to
|
|
|
6
6
|
- Every check in `statusCheckRollup` (in particular "Lint and test") must show success. If anything is failing or still pending, stop and report which — do not merge, and do not wait/poll.
|
|
7
7
|
- `mergeable` must not be `CONFLICTING`.
|
|
8
8
|
- If the branch's second `/`-separated segment is one of the four dev-lane slot names (`ash`, `sky`, `jay`, `kit` — the type prefix in front varies, `feat`/`fix`/`chore`/etc., don't assume `feat`), it must carry the `qa-approved` label (check `gh pr view <number> --json labels`). If it doesn't, stop — that means the paired QA agent (`/team-member`) hasn't signed off yet; don't merge and don't review it yourself here. This check deliberately stays scoped to the four dev-lane names — Aziz's and Amy's branches (`team/aziz`, `docs/amy/issue-<n>`, ...) aren't part of the Dev/QA pairing and go through normal review instead.
|
|
9
|
+
- **Exception:** a QA-named slot (`sky`/`kit`) authoring its own process/meta PR (e.g. `chore/sky/...`) has no natural QA partner to self-approve — the Dev/QA pairing only exists for the *other* lane's ticket work. There's no label path for this case; stop and ask the user directly whether it's safe to merge without one, same as any missing-label PR. If they confirm (e.g. because it's a prompting/process change with no application-code risk), proceed — that confirmation stands in for the label for this PR only, it doesn't waive the check generally.
|
|
9
10
|
3. Show the user a summary: PR number, title, URL, check status, and review status (expected to be blocked only on the missing-approval requirement). State plainly that merging will use `--admin` to bypass that review gate. Ask for explicit confirmation before proceeding.
|
|
10
11
|
4. On confirmation, squash-merge (matching this repo's existing history — commits land on `develop` as a single `title (#number)` commit): `gh pr merge <number> --admin --squash`. The remote branch is auto-deleted on merge (repo setting), so don't pass `--delete-branch`.
|
|
11
12
|
5. Clean up:
|
|
@@ -14,5 +15,6 @@ Merge the current branch's PR and clean up its worktree. Uses `--admin` only to
|
|
|
14
15
|
- If this session created the current worktree (i.e. it got here via `/start-worktree` or `/next-issue` earlier in this same session), use `ExitWorktree` with `action: "remove"`.
|
|
15
16
|
- Otherwise (worktree predates this session), fall back to manual cleanup from the main checkout: `git worktree remove <path>`, then `git branch -d <branch>`, then `git worktree prune`.
|
|
16
17
|
6. Sweep the other five dev-team lanes for orphaned work on the same ticket. Pull the issue number out of the merged PR's title (e.g. `(#126)`). For each of the other dev-team worktrees (`ash`, `sky`, `jay`, `kit`, `aziz`, `amy`, minus whichever one just merged), run `git -C .claude/worktrees/<name> log --oneline develop..HEAD` and check whether any commit message references that same issue number. A hit means a sibling lane independently started the same ticket and lost the race — since it never became a PR, nothing else would ever clean it up. Don't reset it silently: a squash merge means SHAs (and even diffs) won't match even for genuinely identical content, so show the user the commit list and confirm it's a duplicate before resetting — same idle-reset procedure as step 5 (`git checkout team/<name>`, `git reset --hard origin/develop`, delete the stale branch if it had its own name).
|
|
17
|
-
7.
|
|
18
|
-
8.
|
|
18
|
+
7. From the main checkout on `develop`, `git pull` — unconditionally, even if step 8 turns out to be a no-op. `gh pr merge` merges via the API; it never touches the main checkout's local branch, so skipping this leaves Kai's live-testing home base stale until the next unrelated pull happens to catch it up.
|
|
19
|
+
8. Update the roadmap: check `docs/roadmap.md` for a checkbox line referencing the issue(s) this PR closes (search by issue number). If found, check it off (`- [ ]` → `- [x]`) and append `(PR <number>)` in the existing style (e.g. `(PR #280)`) — see recent `- [x]` entries for the exact convention. If the roadmap has no line item for this issue (not every ticket is roadmap-tracked — chores, follow-up defects, etc.), skip this step silently. Commit directly to `develop` (`docs(roadmap): check off #<issue> (PR <number>)`) and push — `develop`'s branch protection has `enforce_admins: false`, so a direct push by the repo owner is allowed for this kind of small, reversible, docs-only change; don't open a separate PR for it.
|
|
20
|
+
9. Report a summary: the merge commit, the now-merged PR URL, confirmation the worktree was removed, whether any sibling-lane duplicate was found and reset, and whether the roadmap was updated.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Process the friction and findings from the unit of work you just finished — a PR reviewed, a ticket implemented, a release cut, a doc pass, an orchestration cycle — into a durable form, so the next session in your seat doesn't rediscover the same thing from scratch.
|
|
2
|
+
|
|
3
|
+
This is discretionary, not a gate. No role's process file requires running this — invoke it when you actually hit friction worth preserving, skip it when a pass was clean. Symmetric with `/team-member`/`/next-issue` on the other end of a work cycle: those are prompts you may follow to *pick up* work; this is the one for *closing out* a piece of it.
|
|
4
|
+
|
|
5
|
+
**Only process real findings from the work you just did.** Don't invent hypothetical friction to fill out the exercise, and don't pad with things this file already tells you to do routinely (e.g. a QA coverage gap that blocks the PR itself — that's handled inline by `qa.md`/`verify-pr.md`, not a retro item). If nothing survives that filter, say so and stop.
|
|
6
|
+
|
|
7
|
+
## The two dispositions
|
|
8
|
+
|
|
9
|
+
For each real finding, decide which bucket it belongs in — most sessions will have some of both:
|
|
10
|
+
|
|
11
|
+
**Ticket** — a durable product or system finding that needs to be scheduled, prioritized, or fixed by someone later: a bug, a coverage gap, a design inconsistency, tech debt. This repo is issue-first (see the `feedback_ticket_before_editing` convention) — file it with `gh issue create`, pick labels from the existing set (`defect`, `enhancement`, `qa`, `infrastructure`, `documentation`, `backlog`, `decision-needed`, a version label if it's obviously scoped to one), and reference the PR/work that surfaced it. Don't fix it inline just because you noticed it while doing something else — that's scope creep, not this ticket's job, unless the user explicitly asks you to just do it now. Batch multiple findings into one pass at the end rather than filing mid-work. Roadmap grooming (folding the new issue into `docs/roadmap.md`) is Kai's job, not the filer's — leave it for Kai's next grooming pass unless you *are* Kai.
|
|
12
|
+
|
|
13
|
+
Before filing, triage defect vs. hardening honestly: has a failure actually been observed (a wrong result, a crash, a silently-corrupted state), or are you flagging an input this code merely doesn't validate yet? The latter is real and worth a ticket, but label and word it as hardening, not as a confirmed defect — don't let "I found something" inflate its severity.
|
|
14
|
+
|
|
15
|
+
**Command decision** — operational knowledge that should change how the *next* session in this same seat behaves: a process gap, a doc gap, a technique that worked (or didn't), a fixture gotcha, a missing step in a skill file. This isn't roadmap-worthy scope, so skip the ticket — edit the relevant process/skill doc directly, right now, and say what you changed. Examples already in this repo: `docs/qa/run.md`'s Playwright limitations list, `docs/qa/retro-v0.8.0.md`'s action items, a role file's own process fixes.
|
|
16
|
+
|
|
17
|
+
## Your role's details
|
|
18
|
+
|
|
19
|
+
Read the `## Retro` section in your own role file (`.claude/team-roles/<role>.md`) for the friction categories specific to your seat and which docs each maps to. If that section doesn't exist yet, you're the first to run this from that seat — add one, following the pattern already written for the other roles, and say so in your report.
|
|
20
|
+
|
|
21
|
+
## Report
|
|
22
|
+
|
|
23
|
+
State what you filed (issue numbers + one-line summaries) and what you changed directly (files + one-line summaries). Don't just say "processed 3 findings" — name them, the same way you'd report any other work.
|
|
@@ -12,3 +12,11 @@ Amy keeps documentation accurate, friendly, comprehensive, and fun — README, `
|
|
|
12
12
|
- **On any other branch (mid-ticket):** report status rather than claiming new work.
|
|
13
13
|
|
|
14
14
|
Amy has no dedicated MCP server and doesn't need QA-approved sign-off to merge (docs-only PRs aren't gated by `qa-approved`) — normal `/merge-pr` review still applies.
|
|
15
|
+
|
|
16
|
+
## Retro
|
|
17
|
+
|
|
18
|
+
Friction Amy typically hits after a doc pass, and where it goes — see `/retro` for the general ticket-vs-command-decision split:
|
|
19
|
+
|
|
20
|
+
- **Doc-vs-reality drift found while writing** — an existing doc described behavior that turned out wrong once checked against actual code or live tool output. If it points to an actual code bug, file a ticket and route it toward the relevant lane label rather than silently documenting the wrong behavior as if it were intended. If it's purely a documentation accuracy issue within Amy's own scope, just fix it — that's the ticket you're already working, not a new one.
|
|
21
|
+
- **Generated-doc drift** — `docs/tools.md` or `docs/configuration.md` out of sync in a way `scripts/gen_tool_docs.py` doesn't catch (the script's job, not this file's, when it does catch it). File an `infrastructure`-labeled ticket, parallel to #308.
|
|
22
|
+
- **Competitive/positioning claims needing re-verification** and **redundant/overlapping issues** already have their own handling above (step 4's roadmap-Tier-4 routing, step 1's consolidation) — not new retro items, don't re-litigate them here.
|
|
@@ -15,3 +15,12 @@ Aziz doesn't work a ticket queue — he runs at release cadence: review everythi
|
|
|
15
15
|
9. **Sign off.** Once every required suite is checked off and documentation review (step 1) is clean, commit the QA docs, push, open a PR, and report to the user that the release is ready to tag (Kai runs `/release` from there).
|
|
16
16
|
|
|
17
17
|
For a **minor/dev release** that only needs Smoke: same flow, but step 2's audit will usually conclude Smoke alone suffices — Aziz still runs it live and records real results, just against a smaller case set.
|
|
18
|
+
|
|
19
|
+
## Retro
|
|
20
|
+
|
|
21
|
+
Friction Aziz typically hits after a release pass, and where it goes — see `/retro` for the general ticket-vs-command-decision split:
|
|
22
|
+
|
|
23
|
+
- **Release-scope audit surprises** — a merged PR turns out to lack adequate doc/test coverage, or a QA-tier call feels wrong in hindsight once results are in. If it's a gap in one already-merged feature, file a ticket. If it's the scoped-gating process itself (`docs/qa/runs/README.md`) that needs refining, that's a command decision — fix the process doc directly.
|
|
24
|
+
- **Subagent-sharding friction** — a shard's instructions were ambiguous, or two shards' fixture ownership collided. Command decision: refine the sharding instructions in this file or `docs/qa/run.md`'s mutex section, don't just work around it silently next time.
|
|
25
|
+
- **Bugs found during the compile step** are routine, not a retro item — route them to the responsible Dev lane per step 8 above.
|
|
26
|
+
- **This role already has a heavier version of this pattern**: for a full regression run, Aziz writes `docs/qa/retro-vX.Y.Z.md` (see `retro-v0.8.0.md`) with its own "what went well / what didn't / action items" structure. Before filing a one-off ticket or doc edit here, check whether the finding is significant enough to fold into that release-scale retro doc instead — don't duplicate the same finding in two places.
|
|
@@ -15,3 +15,12 @@ Branch naming for this slot always puts `<name>` as the **second** `/`-separated
|
|
|
15
15
|
- **On any other branch:** check this branch's PR history: `gh pr list --head <branch> --state all --json number,state,mergedAt`.
|
|
16
16
|
- **A `MERGED` entry exists:** this ticket already shipped and this slot was never reset (e.g. Kai's merge-time cleanup didn't run, or targeted a different branch) — self-heal: `git fetch origin develop`, `git checkout team/<name>`, `git reset --hard origin/develop`, `git branch -D <branch>`. Then treat this slot as idle and fall through to the idle-branch flow above to pick up the next ticket.
|
|
17
17
|
- **No `MERGED` entry (open, closed-without-merge, or no PR at all):** mid-ticket — report status: whether a PR is already open, what CI/QA said if anything, and whether it's waiting on QA or on further work — rather than claiming a new ticket. Don't auto-reset a closed-without-merge branch; that's a judgment call for the user, not this session.
|
|
18
|
+
|
|
19
|
+
## Retro
|
|
20
|
+
|
|
21
|
+
Friction Dev (Ash/Jay) typically hits after finishing a ticket, and where it goes — see `/retro` for the general ticket-vs-command-decision split:
|
|
22
|
+
|
|
23
|
+
- **Design/architecture surprises** — an existing pattern didn't generalize the way expected mid-implementation (a sibling function skipped a step this one needed, a helper needed restructuring to fit a new case). If it's about how future Devs should approach similar work, that's a command decision: add a note to `CLAUDE.md` or a `docs/design/` snapshot. If it's legitimate follow-up scope not worth doing in the current PR, file a ticket instead of expanding this one.
|
|
24
|
+
- **Scope creep discovered while implementing** — related, unshipped bugs or gaps adjacent to the ticket but not part of its acceptance criteria. Ticket, don't fold into the current PR.
|
|
25
|
+
- **Stale or missing `CLAUDE.md` guidance** — instructions that were wrong or absent for a pattern just hit. Command decision: fix `CLAUDE.md` directly, it's this repo's own process doc.
|
|
26
|
+
- **QA-authoring ambiguity** — writing test cases in `docs/qa/tests/` surfaced an unclear tagging rule or fixture assumption in `run.md`/`setup.md`. Command decision: clarify the doc rather than guessing silently and leaving the next Dev to guess the same thing.
|
|
@@ -20,3 +20,11 @@ Why this role split exists: any locally-configured MCP server that runs this pro
|
|
|
20
20
|
- Release readiness and documentation quality are Aziz's and Amy's territory, not Kai's — when cutting a release, hand off to Aziz (`/team-member Aziz`) for the review/QA-tier/sign-off pass rather than doing it here; route doc gaps found along the way to Amy (`documentation` label) rather than fixing them inline.
|
|
21
21
|
|
|
22
22
|
Do NOT pick up ticket implementation directly in this session — hand it to a worker session via `start-worktree` (or `next-issue`, which does this automatically), or to a dev-team lane via `/team-member` if the team is active.
|
|
23
|
+
|
|
24
|
+
## Retro
|
|
25
|
+
|
|
26
|
+
Friction Kai typically hits during coordination, and where it goes — see `/retro` for the general ticket-vs-command-decision split:
|
|
27
|
+
|
|
28
|
+
- **Process friction across the team** — another session's role file turned out ambiguous or wrong when actually followed (a stale worktree, a label race, a step that assumed state that wasn't there). Command decision: fix the relevant `.claude/team-roles/*.md` or top-level command file directly — Kai owns the main checkout, so this is usually the right session to make these edits, not a ticket for someone else to eventually pick up.
|
|
29
|
+
- **Roadmap drift** — a shipped PR wasn't checked off in `docs/roadmap.md`, or a version label doesn't match where the work actually landed. Command decision: fix it directly during grooming, it's bookkeeping, not durable design work worth a ticket.
|
|
30
|
+
- **Systemic bugs spotted while merging or triaging that don't belong to the ticket at hand** — file a ticket rather than expanding scope on someone else's PR.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
QA role (Sky / Kit). Reached via `/team-member <name>` after that command's shared isolate + tool-boundary steps.
|
|
2
|
+
|
|
3
|
+
Find the partner Dev's open PR by matching the branch's second `/`-separated segment against the partner's lowercase name — not a `feat/`-specific prefix, since the Dev's branch type varies: `gh pr list --state open --json number,headRefName,url --jq '[.[] | select((.headRefName | split("/"))[1] == "<partner>")]'`. At most one is expected, since a lane only has one ticket in flight at a time.
|
|
4
|
+
|
|
5
|
+
- **None found:** self-heal first — `git fetch origin develop`. If this worktree's `HEAD` isn't already `origin/develop`, `git reset --hard origin/develop`: since the partner has no open PR right now, any leftover commits here are a stale copy of a PR this lane previously reviewed (now merged or closed) — this worktree never holds original work of its own, only reset-in copies of a Dev's PR branch, so it's always safe to discard. Then report that Lane {A/B} has nothing to verify right now.
|
|
6
|
+
- **One found:** this is now essentially `/verify-pr`'s steps 4 onward, but simpler — this worktree already IS a dedicated review space, so skip `/verify-pr`'s steps 1–3 (the main-checkout precondition and the `review/<branch>` fetch trick don't apply here):
|
|
7
|
+
1. `git fetch origin <headRefName> && git reset --hard origin/<headRefName>` in this worktree, staying on `team/<name>` (never create a branch named after the PR here — this slot's identity is `team/<name>`, permanently).
|
|
8
|
+
2. Tell the user to run `/mcp reconnect` (standalone, on its own line) so this agent's own MCP connection picks up the reset code. Wait for confirmation.
|
|
9
|
+
3. Run `/code-review` at `high` effort against `origin/develop...HEAD`.
|
|
10
|
+
4. Scope and run the live QA steps from `/verify-pr` (its steps 6–8: find touched `docs/qa/tests/` cases, cross-check tool coverage, run them live using this worktree's own `mcp-gee-sweet-<name>`-prefixed tools).
|
|
11
|
+
5. Record `**Result**` entries for every case actually run.
|
|
12
|
+
6. If anything failed: push the recorded results (`git push origin HEAD:<headRefName>`, with confirmation), comment on the PR summarizing what failed, and stop — tell the user it goes back to the Dev agent.
|
|
13
|
+
7. If everything passed: push the results, then `gh pr edit <number> --add-label qa-approved`, and report to the user that it's ready for Kai to run `/merge-pr`.
|
|
14
|
+
|
|
15
|
+
## Retro
|
|
16
|
+
|
|
17
|
+
Friction QA (Sky/Kit) typically hits after a pass, and where it goes — see `/retro` for the general ticket-vs-command-decision split:
|
|
18
|
+
|
|
19
|
+
- **Fixture drift/pollution** — the shared spreadsheet/doc accumulates stray objects (charts, temp sheets/rows, leftover permissions) from earlier test runs, breaking `docs/qa/setup.md`'s "Known fixture state" assumptions or blocking visual verification. If a specific test category is the source (e.g. a creation test with no teardown), file a `qa`-labeled ticket against that category rather than just working around it silently. If it's a workaround you need *right now* to finish this pass, that's a command decision — document it in `docs/qa/run.md`'s Playwright-limitations list so the next pass doesn't rediscover it (see the "Chart-covered grid" entry, added 2026-07-15, for the pattern).
|
|
20
|
+
- **Playwright verification gaps** — an element is structurally unreachable or invisible to the browser tools (outside the viewport, canvas-rendered with no accessible geometry, obscured by other content). Command decision: add it to `run.md`'s limitations list along with whatever alternative verification actually worked (e.g. `get_sheet_data(..., include_grid_data=True)`'s `rowMetadata`/`columnMetadata` for pixel-precise dimension checks, when a screenshot can't show it).
|
|
21
|
+
- **Test tag drift** — `⚠️ requires-oauth` or `**Playwright: required**` no longer matches a test case's actual behavior. Small, obvious ones: fix the tag directly in the test file (command decision, mechanical). Larger or systemic drift: don't duplicate #264 (the tracked tag-consistency audit) — add to it instead.
|
|
22
|
+
- **Code-review findings that are minor or systemic rather than PR-blocking** — input-validation gaps, duplicated helpers, style inconsistencies that the new code merely repeats from an existing convention rather than introduces. These are tickets (`enhancement`/`backlog`, or `defect` only if a failure was actually observed), not reasons to withhold `qa-approved` — don't hold one PR hostage to fix a codebase-wide convention. See #323 for the shape this takes (validation + dedup findings from the same review, bundled into one issue since they shared a root cause).
|
|
23
|
+
- **Coverage gaps in the PR itself** (a changed tool with no QA test case at all) are not a retro item — that blocks the pass and is handled inline per this file's step 4 / `verify-pr.md` step 6, not deferred here.
|
|
@@ -32,7 +32,7 @@ Tests live in `tests/` and can be run with `uv run pytest`.
|
|
|
32
32
|
Logic is split across `src/mcp_gee_sweet/`: `server.py` (MCP setup, tool decorator, resources), `auth.py` (lifespan, `SpreadsheetContext`), and `tools/` (domain-based layout):
|
|
33
33
|
|
|
34
34
|
- `tools/sheets/data.py` — read/write cell data (`get_sheet_data`, `get_sheet_formulas`, `get_multiple_*`, `find_in_spreadsheet`, `clear_values`, `update_cells`, `batch_update_cells`, `batch_update`)
|
|
35
|
-
- `tools/sheets/structure.py` — sheet structure (`list_sheets`, `copy_sheet`, `duplicate_sheet`, `rename_sheet`, `create_sheet`, `delete_sheet`, `add_rows`, `add_columns`, `delete_rows`, `delete_columns`, `hide_rows`, `unhide_rows`, `hide_columns`, `unhide_columns`, `format_cells`, `merge_cells`, `unmerge_cells`, `freeze`, `update_sheet_properties`, `sort_range`, `add_chart`)
|
|
35
|
+
- `tools/sheets/structure.py` — sheet structure (`list_sheets`, `copy_sheet`, `duplicate_sheet`, `rename_sheet`, `create_sheet`, `delete_sheet`, `add_rows`, `add_columns`, `delete_rows`, `delete_columns`, `hide_rows`, `unhide_rows`, `hide_columns`, `unhide_columns`, `resize_rows`, `resize_columns`, `format_cells`, `update_borders`, `merge_cells`, `unmerge_cells`, `freeze`, `update_sheet_properties`, `sort_range`, `add_chart`)
|
|
36
36
|
- `tools/sheets/helpers.py` — A1 notation helpers (`_parse_a1_notation`, `_column_index_to_letter`, etc.)
|
|
37
37
|
- `tools/drive/files.py` — file/folder operations (`create_spreadsheet`, `import_csv_to_sheet`, `list_spreadsheets`, `list_files`, `search_files`, `create_folder`, `copy_file`, `move_file`, `rename_file`, `delete_file`, `list_shared_with_me`, `list_recent_files`, `get_storage_quota`, etc.)
|
|
38
38
|
- `tools/drive/sharing.py` — permissions (`share_spreadsheet`, `share_file`, `list_permissions`, `update_permission`, `remove_permission`)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-gee-sweet
|
|
3
|
-
Version: 0.8.2.
|
|
3
|
+
Version: 0.8.2.dev51
|
|
4
4
|
Summary: MCP server for Google Workspace — Sheets, Drive, Docs, and Calendar. 60+ tools for AI clients.
|
|
5
5
|
Project-URL: Homepage, https://github.com/khuisman/mcp-gee-sweet
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/khuisman/mcp-gee-sweet/issues
|
|
@@ -27,7 +27,7 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|

|
|
28
28
|
</div>
|
|
29
29
|
|
|
30
|
-
An MCP server that gives AI clients reliable, direct access to Google Workspace — Sheets, Drive, Docs, and Calendar.
|
|
30
|
+
An MCP server that gives AI clients reliable, direct access to Google Workspace — Sheets, Drive, Docs, and Calendar. 101 tools across four domains.
|
|
31
31
|
|
|
32
32
|
As of mid-2026, Google's own Workspace MCP servers (Developer Preview) cover Gmail, Drive, Calendar, People, and Chat — Sheets and Docs still have no official server at all, and Drive/Calendar coverage there is a fraction of what's here. Stable releases ship to PyPI behind a QA regression gate, with a raw `batch_update` passthrough as an escape hatch for anything a named tool doesn't cover yet. See [Why it exists](https://khuisman.github.io/mcp-gee-sweet/#why-it-exists) for the full, sourced comparison against both the official servers and community alternatives.
|
|
33
33
|
|
|
@@ -124,7 +124,7 @@ See [Client Setup](https://khuisman.github.io/mcp-gee-sweet/latest/client-setup/
|
|
|
124
124
|
|
|
125
125
|
## Docs
|
|
126
126
|
|
|
127
|
-
- [Tools](https://khuisman.github.io/mcp-gee-sweet/latest/tools/) — full tool reference (
|
|
127
|
+
- [Tools](https://khuisman.github.io/mcp-gee-sweet/latest/tools/) — full tool reference (101 tools)
|
|
128
128
|
- [Authentication](https://khuisman.github.io/mcp-gee-sweet/latest/auth/) — all four auth methods
|
|
129
129
|
- [Configuration](https://khuisman.github.io/mcp-gee-sweet/latest/configuration/) — env vars, caching, tool filtering
|
|
130
130
|
- [Client Setup](https://khuisman.github.io/mcp-gee-sweet/latest/client-setup/) — MCP client config examples
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|

|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
|
-
An MCP server that gives AI clients reliable, direct access to Google Workspace — Sheets, Drive, Docs, and Calendar.
|
|
8
|
+
An MCP server that gives AI clients reliable, direct access to Google Workspace — Sheets, Drive, Docs, and Calendar. 101 tools across four domains.
|
|
9
9
|
|
|
10
10
|
As of mid-2026, Google's own Workspace MCP servers (Developer Preview) cover Gmail, Drive, Calendar, People, and Chat — Sheets and Docs still have no official server at all, and Drive/Calendar coverage there is a fraction of what's here. Stable releases ship to PyPI behind a QA regression gate, with a raw `batch_update` passthrough as an escape hatch for anything a named tool doesn't cover yet. See [Why it exists](https://khuisman.github.io/mcp-gee-sweet/#why-it-exists) for the full, sourced comparison against both the official servers and community alternatives.
|
|
11
11
|
|
|
@@ -102,7 +102,7 @@ See [Client Setup](https://khuisman.github.io/mcp-gee-sweet/latest/client-setup/
|
|
|
102
102
|
|
|
103
103
|
## Docs
|
|
104
104
|
|
|
105
|
-
- [Tools](https://khuisman.github.io/mcp-gee-sweet/latest/tools/) — full tool reference (
|
|
105
|
+
- [Tools](https://khuisman.github.io/mcp-gee-sweet/latest/tools/) — full tool reference (101 tools)
|
|
106
106
|
- [Authentication](https://khuisman.github.io/mcp-gee-sweet/latest/auth/) — all four auth methods
|
|
107
107
|
- [Configuration](https://khuisman.github.io/mcp-gee-sweet/latest/configuration/) — env vars, caching, tool filtering
|
|
108
108
|
- [Client Setup](https://khuisman.github.io/mcp-gee-sweet/latest/client-setup/) — MCP client config examples
|
|
@@ -70,7 +70,7 @@ Access logs are emitted at `INFO` level — they appear when `DEBUG_LEVEL` is `D
|
|
|
70
70
|
|
|
71
71
|
<!-- Auto-generated by `scripts/gen_tool_docs.py` — do not edit by hand, your changes will be overwritten on the next commit. -->
|
|
72
72
|
|
|
73
|
-
By default all
|
|
73
|
+
By default all 103 tools are registered. Use `ENABLED_TOOLS` (or `--include-tools` on the CLI) to restrict the server to exactly the tools you need. This reduces the AI's context window cost — each registered tool is a name the model must reason about on every call.
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
76
|
# Environment variable
|
|
@@ -36,6 +36,7 @@ When a test case is tagged `**Playwright: required**`, after the tool call the c
|
|
|
36
36
|
|
|
37
37
|
- **Footer / header content** — Google Docs renders headers and footers outside the main document canvas. For short documents `window.scrollTo` does not bring the footer into the viewport. When a test involves `create_footer` or `create_header`, use the API response as confirmation; a Playwright snapshot of the body is still useful but will not show the footer content.
|
|
38
38
|
- **Permission changes** — sharing confirmations are not visible in the Drive UI without navigating to the file's "Share" dialog. Use `list_permissions` API response as the confirmation source for share tests.
|
|
39
|
+
- **Chart-covered grid (Sales sheet)** — repeated `add_chart` test runs leave floating chart objects on the Sales sheet (they're never cleaned up between runs), and as of 2026-07-15 there were ~12 stacked overlapping charts covering roughly rows 1–22. This blocks visual verification of anything in that region — row-height/column-width changes (`resize_rows`/`resize_columns`), cell formatting, etc. are not visible in a screenshot, and Playwright's accessibility snapshot doesn't expose per-cell grid geometry either (only the charts' own accessible text). When a test in that area needs precise confirmation, use `get_sheet_data(..., range=<affected range>, include_grid_data=True)` instead — the raw response includes `rowMetadata`/`columnMetadata` with each row/column's actual `pixelSize`, which is more precise than a screenshot anyway. Longer term, `add_chart` QA test cases should delete the chart they create at the end of each run (see `docs/qa/tests/sheets_charts.md`) so the fixture doesn't accumulate; that cleanup is not yet in place.
|
|
39
40
|
|
|
40
41
|
### Coordinating Playwright across parallel shards
|
|
41
42
|
|
|
@@ -642,6 +642,174 @@ Tests marked **⚠️ destructive** rename or delete sheets — reset fixtures a
|
|
|
642
642
|
|
|
643
643
|
---
|
|
644
644
|
|
|
645
|
+
## `resize_rows` / `resize_columns`
|
|
646
|
+
|
|
647
|
+
### TC-S73: Resize a single row to an explicit pixel height ⚠️ destructive
|
|
648
|
+
|
|
649
|
+
**Prompt**
|
|
650
|
+
**Playwright: required**
|
|
651
|
+
> "Set row 5 (0-based index 4) on the Sales sheet in {SPREADSHEET_ID} to 60 pixels tall"
|
|
652
|
+
|
|
653
|
+
**Checks**
|
|
654
|
+
- `updateDimensionProperties` request sent with `dimension: ROWS`, `startIndex: 4`, `endIndex: 5`
|
|
655
|
+
- `properties.pixelSize: 60`, `fields: pixelSize`
|
|
656
|
+
- Row 5 visibly taller in the Sheets UI
|
|
657
|
+
|
|
658
|
+
**Result (2026-07-15) ✅ PASS** `resize_rows(spreadsheet_id, sheet="Sales", start_row=4, pixel_size=60)` → `{"replies":[{}]}`, no error. Playwright was skipped for this run — the shared fixture's Sales sheet currently has ~12 overlapping chart objects left over from other QA passes, which visually cover rows 1–22 and make row-height differences unreadable in a screenshot. Verified precisely instead via `get_sheet_data(..., range="A1:E6", include_grid_data=True)`, which returns `rowMetadata[].pixelSize` — confirmed row index 4 read back 60 immediately after this call (before being overwritten by TC-S74/TC-S75 below).
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
### TC-S74: Resize a range of rows ⚠️ destructive
|
|
663
|
+
|
|
664
|
+
**Prompt**
|
|
665
|
+
**Playwright: required**
|
|
666
|
+
> "Set rows 3 through 5 (0-based indices 2–4) on the Sales sheet in {SPREADSHEET_ID} to 40 pixels tall"
|
|
667
|
+
|
|
668
|
+
**Checks**
|
|
669
|
+
- `startIndex: 2`, `endIndex: 5` in the request (inclusive end_row=4 translated to exclusive 5)
|
|
670
|
+
- All three rows resize in the UI
|
|
671
|
+
|
|
672
|
+
**Result (2026-07-15) ✅ PASS** `resize_rows(spreadsheet_id, sheet="Sales", start_row=2, end_row=4, pixel_size=40)` → `{"replies":[{}]}`, no error. Confirmed via `get_sheet_data(..., range="A1:E6", include_grid_data=True)`: `rowMetadata` for row indices 2 and 3 both read back `pixelSize: 40` after the full test sequence (index 4 was subsequently auto-resized by TC-S75, as expected).
|
|
673
|
+
|
|
674
|
+
---
|
|
675
|
+
|
|
676
|
+
### TC-S75: Auto-resize rows to fit content ⚠️ destructive
|
|
677
|
+
|
|
678
|
+
**Prompt**
|
|
679
|
+
**Playwright: required**
|
|
680
|
+
> "Auto-fit the height of row 5 (0-based index 4) on the Sales sheet in {SPREADSHEET_ID} to its content"
|
|
681
|
+
|
|
682
|
+
**Setup:** Row 5 previously set to an oversized pixel height (e.g. TC-S73).
|
|
683
|
+
|
|
684
|
+
**Checks**
|
|
685
|
+
- `autoResizeDimensions` request sent with `dimensions.dimension: ROWS`, `startIndex: 4`, `endIndex: 5`
|
|
686
|
+
- Row 5 shrinks back to content-fit height in the Sheets UI
|
|
687
|
+
|
|
688
|
+
**Result (2026-07-15) ✅ PASS** `resize_rows(spreadsheet_id, sheet="Sales", start_row=4, auto_resize=True)` → `{"replies":[{}]}`, no error. Confirmed via `get_sheet_data(..., range="A1:E6", include_grid_data=True)`: row index 4's `pixelSize` read back as `21` (Sheets' default/content-fit height for plain text), down from the `40` set by TC-S74 moments earlier — confirms `autoResizeDimensions` fired and took effect.
|
|
689
|
+
|
|
690
|
+
---
|
|
691
|
+
|
|
692
|
+
### TC-S76: Resize rows — neither pixel_size nor auto_resize given returns error
|
|
693
|
+
|
|
694
|
+
**Prompt**
|
|
695
|
+
> "Resize row 0 on the Sales sheet in {SPREADSHEET_ID} without specifying a size or auto-fit"
|
|
696
|
+
|
|
697
|
+
**Checks**
|
|
698
|
+
- Response contains `error` field
|
|
699
|
+
- No `batchUpdate` call made
|
|
700
|
+
|
|
701
|
+
**Result (2026-07-15) ✅ PASS** `resize_rows(spreadsheet_id, sheet="Sales", start_row=0)` → `{"error":"Specify pixel_size or set auto_resize=True"}`. Returned before any batchUpdate call.
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
### TC-S77: Resize rows — both pixel_size and auto_resize given returns error
|
|
706
|
+
|
|
707
|
+
**Prompt**
|
|
708
|
+
> "Resize row 0 on the Sales sheet in {SPREADSHEET_ID} to 50 pixels and also auto-fit it"
|
|
709
|
+
|
|
710
|
+
**Checks**
|
|
711
|
+
- Response contains `error` field
|
|
712
|
+
- No `batchUpdate` call made
|
|
713
|
+
|
|
714
|
+
**Result (2026-07-15) ✅ PASS** `resize_rows(spreadsheet_id, sheet="Sales", start_row=0, pixel_size=50, auto_resize=True)` → `{"error":"Specify only one of pixel_size or auto_resize"}`. Returned before any batchUpdate call.
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
718
|
+
### TC-S78: Resize rows — sheet not found returns error
|
|
719
|
+
|
|
720
|
+
**Prompt**
|
|
721
|
+
> "Set row 0 to 50 pixels tall on a sheet called 'NoSuchSheet' in {SPREADSHEET_ID}"
|
|
722
|
+
|
|
723
|
+
**Checks**
|
|
724
|
+
- Response contains `error` field
|
|
725
|
+
|
|
726
|
+
**Result (2026-07-15) ✅ PASS** `resize_rows(spreadsheet_id, sheet="NoSuchSheet", start_row=0, pixel_size=50)` → `{"error":"Sheet 'NoSuchSheet' not found"}`.
|
|
727
|
+
|
|
728
|
+
---
|
|
729
|
+
|
|
730
|
+
### TC-S79: Resize a single column to an explicit pixel width ⚠️ destructive
|
|
731
|
+
|
|
732
|
+
**Prompt**
|
|
733
|
+
**Playwright: required**
|
|
734
|
+
> "Set column B (0-based index 1) on the Sales sheet in {SPREADSHEET_ID} to 200 pixels wide"
|
|
735
|
+
|
|
736
|
+
**Checks**
|
|
737
|
+
- `updateDimensionProperties` request sent with `dimension: COLUMNS`, `startIndex: 1`, `endIndex: 2`
|
|
738
|
+
- `properties.pixelSize: 200`, `fields: pixelSize`
|
|
739
|
+
- Column B visibly wider in the Sheets UI
|
|
740
|
+
|
|
741
|
+
**Result (2026-07-15) ✅ PASS** `resize_columns(spreadsheet_id, sheet="Sales", start_column=1, pixel_size=200)` → `{"replies":[{}]}`, no error. Playwright skipped — see "Chart-covered grid" note in `docs/qa/run.md`. Verified via `get_sheet_data(..., range="A1:E6", include_grid_data=True)`: column index 1's `pixelSize` read back `200` immediately after this call (before being overwritten by TC-S81 below).
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
### TC-S80: Resize a range of columns ⚠️ destructive
|
|
746
|
+
|
|
747
|
+
**Prompt**
|
|
748
|
+
**Playwright: required**
|
|
749
|
+
> "Set columns C through E (0-based indices 2–4) on the Sales sheet in {SPREADSHEET_ID} to 50 pixels wide"
|
|
750
|
+
|
|
751
|
+
**Checks**
|
|
752
|
+
- `startIndex: 2`, `endIndex: 5` in the request
|
|
753
|
+
- All columns in range resize in the UI
|
|
754
|
+
|
|
755
|
+
**Result (2026-07-15) ✅ PASS** `resize_columns(spreadsheet_id, sheet="Sales", start_column=2, end_column=4, pixel_size=50)` → `{"replies":[{}]}`, no error. Confirmed via `get_sheet_data(..., range="A1:E6", include_grid_data=True)`: column indices 2, 3, and 4 all read back `pixelSize: 50`.
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
### TC-S81: Auto-resize columns to fit content ⚠️ destructive
|
|
760
|
+
|
|
761
|
+
**Prompt**
|
|
762
|
+
**Playwright: required**
|
|
763
|
+
> "Auto-fit the width of column B (0-based index 1) on the Sales sheet in {SPREADSHEET_ID} to its content"
|
|
764
|
+
|
|
765
|
+
**Setup:** Column B previously set to an oversized pixel width (e.g. TC-S79).
|
|
766
|
+
|
|
767
|
+
**Checks**
|
|
768
|
+
- `autoResizeDimensions` request sent with `dimensions.dimension: COLUMNS`, `startIndex: 1`, `endIndex: 2`
|
|
769
|
+
- Column B shrinks back to content-fit width in the Sheets UI
|
|
770
|
+
|
|
771
|
+
**Result (2026-07-15) ✅ PASS** `resize_columns(spreadsheet_id, sheet="Sales", start_column=1, auto_resize=True)` → `{"replies":[{}]}`, no error. Confirmed via `get_sheet_data(..., range="A1:E6", include_grid_data=True)`: column index 1's `pixelSize` read back `28` (content-fit for the short numeric values in column B), down from the `200` set by TC-S79 — confirms `autoResizeDimensions` fired and took effect.
|
|
772
|
+
|
|
773
|
+
---
|
|
774
|
+
|
|
775
|
+
### TC-S82: Resize columns — neither pixel_size nor auto_resize given returns error
|
|
776
|
+
|
|
777
|
+
**Prompt**
|
|
778
|
+
> "Resize column 0 on the Sales sheet in {SPREADSHEET_ID} without specifying a size or auto-fit"
|
|
779
|
+
|
|
780
|
+
**Checks**
|
|
781
|
+
- Response contains `error` field
|
|
782
|
+
- No `batchUpdate` call made
|
|
783
|
+
|
|
784
|
+
**Result (2026-07-15) ✅ PASS** `resize_columns(spreadsheet_id, sheet="Sales", start_column=0)` → `{"error":"Specify pixel_size or set auto_resize=True"}`. Returned before any batchUpdate call.
|
|
785
|
+
|
|
786
|
+
---
|
|
787
|
+
|
|
788
|
+
### TC-S83: Resize columns — both pixel_size and auto_resize given returns error
|
|
789
|
+
|
|
790
|
+
**Prompt**
|
|
791
|
+
> "Resize column 0 on the Sales sheet in {SPREADSHEET_ID} to 100 pixels and also auto-fit it"
|
|
792
|
+
|
|
793
|
+
**Checks**
|
|
794
|
+
- Response contains `error` field
|
|
795
|
+
- No `batchUpdate` call made
|
|
796
|
+
|
|
797
|
+
**Result (2026-07-15) ✅ PASS** `resize_columns(spreadsheet_id, sheet="Sales", start_column=0, pixel_size=100, auto_resize=True)` → `{"error":"Specify only one of pixel_size or auto_resize"}`. Returned before any batchUpdate call.
|
|
798
|
+
|
|
799
|
+
---
|
|
800
|
+
|
|
801
|
+
### TC-S84: Resize columns — sheet not found returns error
|
|
802
|
+
|
|
803
|
+
**Prompt**
|
|
804
|
+
> "Set column 0 to 100 pixels wide on a sheet called 'NoSuchSheet' in {SPREADSHEET_ID}"
|
|
805
|
+
|
|
806
|
+
**Checks**
|
|
807
|
+
- Response contains `error` field
|
|
808
|
+
|
|
809
|
+
**Result (2026-07-15) ✅ PASS** `resize_columns(spreadsheet_id, sheet="NoSuchSheet", start_column=0, pixel_size=100)` → `{"error":"Sheet 'NoSuchSheet' not found"}`.
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
645
813
|
## `format_cells`
|
|
646
814
|
|
|
647
815
|
### TC-S33: Apply bold and background color to a range ⚠️ destructive
|
|
@@ -761,6 +929,99 @@ Tests marked **⚠️ destructive** rename or delete sheets — reset fixtures a
|
|
|
761
929
|
|
|
762
930
|
---
|
|
763
931
|
|
|
932
|
+
## `update_borders`
|
|
933
|
+
|
|
934
|
+
### TC-S85: Apply a solid border around all four edges of a range ⚠️ destructive
|
|
935
|
+
|
|
936
|
+
**Prompt**
|
|
937
|
+
**Playwright: required**
|
|
938
|
+
> "Add a black solid border around all four edges of A1:D5 on the Sales sheet"
|
|
939
|
+
|
|
940
|
+
**Checks**
|
|
941
|
+
- `updateBorders` request sent with `top`, `bottom`, `left`, `right` all `style="SOLID"`
|
|
942
|
+
- `color` set to black on each edge
|
|
943
|
+
- `range` covers A1:D5
|
|
944
|
+
- No error in response
|
|
945
|
+
|
|
946
|
+
**Result (2026-07-16) ✅ PASS (API-verified, not visual)** The Sales sheet's A1:D5 region is fully covered by the known stacked-chart fixture pollution (see `run.md`'s "Chart-covered grid" entry), so a screenshot can't show the border. Used `get_sheet_data(range="A1:D5", include_grid_data=True)` instead: perimeter cells (row 1 all columns = top; row 5 all columns = bottom; column A all rows = left; column D all rows = right) all show `{"style": "SOLID", "color": {}}` (empty color = black), exactly matching the request. No error in the batchUpdate response.
|
|
947
|
+
|
|
948
|
+
---
|
|
949
|
+
|
|
950
|
+
### TC-S86: Apply dashed inner gridlines inside a range ⚠️ destructive
|
|
951
|
+
|
|
952
|
+
**Prompt**
|
|
953
|
+
**Playwright: required**
|
|
954
|
+
> "Add dashed inner horizontal and vertical borders inside A1:C3 on the Sales sheet"
|
|
955
|
+
|
|
956
|
+
**Checks**
|
|
957
|
+
- `updateBorders` request includes `innerHorizontal` and `innerVertical`, both `style="DASHED"`
|
|
958
|
+
- `top`/`bottom`/`left`/`right` are not set
|
|
959
|
+
- No error in response
|
|
960
|
+
|
|
961
|
+
**Result (2026-07-16) ✅ PASS (API-verified, not visual)** Same chart-coverage limitation as TC-S85 — verified via `get_sheet_data(range="A1:C3", include_grid_data=True)`. Interior cell edges between rows/columns show `DASHED`, while the pre-existing perimeter `SOLID` borders from TC-S85 (which this call's request did not include top/bottom/left/right keys for) were left completely untouched — confirming the tool only sends the edges it was given and doesn't clobber unspecified ones. No error in response.
|
|
962
|
+
|
|
963
|
+
---
|
|
964
|
+
|
|
965
|
+
### TC-S87: Clear an existing border edge with style NONE ⚠️ destructive
|
|
966
|
+
|
|
967
|
+
**Prompt**
|
|
968
|
+
**Playwright: required**
|
|
969
|
+
> "Remove the border from the right edge of A1:D5 on the Sales sheet"
|
|
970
|
+
|
|
971
|
+
**Checks**
|
|
972
|
+
- `updateBorders` request includes `right.style="NONE"`
|
|
973
|
+
- No error in response
|
|
974
|
+
|
|
975
|
+
**Result (2026-07-16) ✅ PASS (API-verified, not visual)** Same chart-coverage limitation. Verified via `get_sheet_data(range="D1:D5", include_grid_data=True)`: the `right` border is absent from every cell in column D after the call, while `top`/`bottom` borders from TC-S85 remain — confirming only the targeted edge was cleared. No error in response.
|
|
976
|
+
|
|
977
|
+
---
|
|
978
|
+
|
|
979
|
+
### TC-S88: update_borders — no border params returns error
|
|
980
|
+
|
|
981
|
+
**Checks (unit test)**
|
|
982
|
+
- Calling `update_borders` with no edge params returns `{"error": ...}`
|
|
983
|
+
- No batchUpdate API call made
|
|
984
|
+
|
|
985
|
+
**Result (2026-07-16) ✅ PASS** `test_no_params_returns_error` passes.
|
|
986
|
+
|
|
987
|
+
---
|
|
988
|
+
|
|
989
|
+
### TC-S89: update_borders — border spec missing style returns error
|
|
990
|
+
|
|
991
|
+
**Checks (unit test)**
|
|
992
|
+
- An edge dict without a `"style"` key returns `{"error": ...}` before any API call
|
|
993
|
+
|
|
994
|
+
**Result (2026-07-16) ✅ PASS** `test_missing_style_returns_error` passes.
|
|
995
|
+
|
|
996
|
+
---
|
|
997
|
+
|
|
998
|
+
### TC-S90: update_borders — invalid style value returns error
|
|
999
|
+
|
|
1000
|
+
**Checks (unit test)**
|
|
1001
|
+
- An unrecognized `style` value (e.g. `"SQUIGGLY"`) returns `{"error": ...}` listing valid styles, before any API call
|
|
1002
|
+
|
|
1003
|
+
**Result (2026-07-16) ✅ PASS** `test_invalid_style_returns_error` passes.
|
|
1004
|
+
|
|
1005
|
+
---
|
|
1006
|
+
|
|
1007
|
+
### TC-S91: update_borders — sheet not found returns error
|
|
1008
|
+
|
|
1009
|
+
**Checks (unit test)**
|
|
1010
|
+
- Sheet name not in spreadsheet → `{"error": "Sheet 'X' not found"}`
|
|
1011
|
+
|
|
1012
|
+
**Result (2026-07-16) ✅ PASS** `test_returns_error_when_sheet_not_found` passes.
|
|
1013
|
+
|
|
1014
|
+
---
|
|
1015
|
+
|
|
1016
|
+
### TC-S92: update_borders — non-string style value returns error, does not crash
|
|
1017
|
+
|
|
1018
|
+
**Checks (unit test)**
|
|
1019
|
+
- Calling `update_borders` with an edge dict whose `"style"` value is not a string (e.g. `top={"style": 5}` or `top={"style": None}`) returns `{"error": ...}`, same as the existing "invalid style" and "missing style" cases.
|
|
1020
|
+
|
|
1021
|
+
**Result (2026-07-16) ✅ PASS — fix verified** Originally failed: validation only checked `"style" not in border` (key presence) before calling `.upper()`, so a non-string style (e.g. `top={"style": 5}`) reached `border["style"].upper()` and crashed with an unhandled `AttributeError` instead of a clean error. Fixed by adding `if not isinstance(border["style"], str): return {"error": ...}` before the `.upper()` call. Verified three ways: unit test `test_non_string_style_returns_error` passes (covers both `5` and `None`); confirmed live against the real `update_borders` MCP tool with `top={"style": 5}` and `top={"style": null}` — both return `{"error": "Border spec for 'top' has a non-string 'style' value"}` with no crash. TC-S92 closed.
|
|
1022
|
+
|
|
1023
|
+
---
|
|
1024
|
+
|
|
764
1025
|
## `freeze`
|
|
765
1026
|
|
|
766
1027
|
### TC-S42: Freeze the header row ⚠️ destructive
|
|
@@ -105,7 +105,7 @@ No new tools. Stabilize on what Tier 1 shipped before starting Tier 2 feature wo
|
|
|
105
105
|
**Sheets**
|
|
106
106
|
- [ ] `update_borders` — border style, width, color on a range ([#122](https://github.com/khuisman/mcp-gee-sweet/issues/122)) _(freema/mcp-gsheets)_
|
|
107
107
|
- [x] `hide_rows` / `hide_columns` / unhide — toggle row or column visibility ([#123](https://github.com/khuisman/mcp-gee-sweet/issues/123)) _(freema/mcp-gsheets)_ (PR #311)
|
|
108
|
-
- [
|
|
108
|
+
- [x] `resize_rows` / `resize_columns` — set pixel height/width, or auto-fit to content (PR #321) ([#124](https://github.com/khuisman/mcp-gee-sweet/issues/124)) _(freema/mcp-gsheets)_
|
|
109
109
|
- [ ] `add_data_validation` / `get_data_validation` — dropdowns, checkboxes, value constraints ([#125](https://github.com/khuisman/mcp-gee-sweet/issues/125)) _(freema/mcp-gsheets)_
|
|
110
110
|
- [x] `update_sheet_properties` — tab color, hide/show gridlines (PR #301) ([#126](https://github.com/khuisman/mcp-gee-sweet/issues/126)) _(freema/mcp-gsheets)_
|
|
111
111
|
- [x] `duplicate_sheet` — copy a sheet within the same spreadsheet (PR #286) ([#127](https://github.com/khuisman/mcp-gee-sweet/issues/127))
|
|
@@ -119,7 +119,7 @@ No new tools. Stabilize on what Tier 1 shipped before starting Tier 2 feature wo
|
|
|
119
119
|
- [ ] `list_all_events` — query all subscribed calendars in parallel _(decision needed — also tracked in Decisions Needed)_ ([#194](https://github.com/khuisman/mcp-gee-sweet/issues/194))
|
|
120
120
|
|
|
121
121
|
**Docs**
|
|
122
|
-
- [
|
|
122
|
+
- [x] `insert_page_break` — explicit page break at an index ([#148](https://github.com/khuisman/mcp-gee-sweet/issues/148)) (PR #314)
|
|
123
123
|
- [x] `merge_table_cells` — merge cells in an existing table (distinct from write-time colspan) ([#150](https://github.com/khuisman/mcp-gee-sweet/issues/150)) (PR #310)
|
|
124
124
|
- [ ] Comments API — list, add, resolve doc comments ([#151](https://github.com/khuisman/mcp-gee-sweet/issues/151))
|
|
125
125
|
- [ ] `create_named_range` / `create_bookmark` — anchor points for internal links ([#152](https://github.com/khuisman/mcp-gee-sweet/issues/152))
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- Auto-generated by `scripts/gen_tool_docs.py` — do not edit by hand, your changes will be overwritten on the next commit. -->
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
103 tools. All tool names are lowercase with underscores — use these exact strings with `ENABLED_TOOLS` or `--include-tools`.
|
|
6
6
|
|
|
7
7
|
## Sheets — data
|
|
8
8
|
|
|
@@ -38,7 +38,10 @@
|
|
|
38
38
|
| `unhide_rows` | Unhide previously hidden rows in a sheet. | `spreadsheet_id`, `sheet`, `start_row`, `end_row?` |
|
|
39
39
|
| `hide_columns` | Hide columns in a sheet from view. | `spreadsheet_id`, `sheet`, `start_column`, `end_column?` |
|
|
40
40
|
| `unhide_columns` | Unhide previously hidden columns in a sheet. | `spreadsheet_id`, `sheet`, `start_column`, `end_column?` |
|
|
41
|
+
| `resize_rows` | Set an explicit pixel height for rows, or auto-fit them to content. | `spreadsheet_id`, `sheet`, `start_row`, `end_row?`, `pixel_size?`, `auto_resize?` |
|
|
42
|
+
| `resize_columns` | Set an explicit pixel width for columns, or auto-fit them to content. | `spreadsheet_id`, `sheet`, `start_column`, `end_column?`, `pixel_size?`, `auto_resize?` |
|
|
41
43
|
| `format_cells` | Apply formatting to a cell range (background color, font, alignment, number format). | `spreadsheet_id`, `sheet`, `range`, `bold?`, `italic?`, `strikethrough?`, `font_size?`, `font_color?`, `background_color?`, `horizontal_alignment?`, `vertical_alignment?`, `number_format_type?`, `number_format_pattern?` |
|
|
44
|
+
| `update_borders` | Apply or clear borders on the edges of a cell range. | `spreadsheet_id`, `sheet`, `range`, `top?`, `bottom?`, `left?`, `right?`, `inner_horizontal?`, `inner_vertical?` |
|
|
42
45
|
| `merge_cells` | Merge a range of cells into one cell. | `spreadsheet_id`, `sheet`, `range`, `merge_type?` |
|
|
43
46
|
| `unmerge_cells` | Unmerge all merged cells in a range. | `spreadsheet_id`, `sheet`, `range` |
|
|
44
47
|
| `freeze` | Freeze rows and/or columns in a sheet. | `spreadsheet_id`, `sheet`, `rows?`, `columns?` |
|