mcp-gee-sweet 0.7.0.dev72__tar.gz → 0.7.0.dev74__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.
Files changed (137) hide show
  1. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.github/workflows/ci.yml +1 -1
  2. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.github/workflows/docs.yml +1 -1
  3. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.github/workflows/publish-dev.yml +2 -2
  4. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.github/workflows/release.yml +2 -2
  5. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/PKG-INFO +1 -1
  6. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/design/docs-ast-pipeline.md +45 -7
  7. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/tests/docs.md +81 -0
  8. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/docs/ast.py +1 -2
  9. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/docs/emitter.py +309 -136
  10. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/docs/html_parser.py +25 -7
  11. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_docs_core.py +36 -13
  12. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_docs_style.py +7 -4
  13. mcp_gee_sweet-0.7.0.dev74/tests/test_docs_tables.py +721 -0
  14. mcp_gee_sweet-0.7.0.dev72/tests/test_docs_tables.py +0 -433
  15. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.claude/commands/prep-for-pr.md +0 -0
  16. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.dockerignore +0 -0
  17. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  18. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  19. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  20. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.github/dependabot.yml +0 -0
  21. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.gitignore +0 -0
  22. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.pre-commit-config.yaml +0 -0
  23. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/.python-version +0 -0
  24. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/CHANGELOG.md +0 -0
  25. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/CLAUDE.md +0 -0
  26. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/CODE_OF_CONDUCT.md +0 -0
  27. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/CONTRIBUTING.md +0 -0
  28. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/Dockerfile +0 -0
  29. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/LICENSE +0 -0
  30. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/Makefile +0 -0
  31. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/README.md +0 -0
  32. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/SECURITY.md +0 -0
  33. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docker-compose.yml +0 -0
  34. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/auth.md +0 -0
  35. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/client-setup.md +0 -0
  36. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/configuration.md +0 -0
  37. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/decisions/decision-chart-theming.md +0 -0
  38. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/decisions/decision-composite-tools.md +0 -0
  39. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/decisions/decision-docs-formatting.md +0 -0
  40. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/decisions/decision-fork.md +0 -0
  41. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/decisions/decision-grid-data-size-cap.md +0 -0
  42. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/decisions/decision-publishing.md +0 -0
  43. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/decisions/decision-response-size-cap-generalization.md +0 -0
  44. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/decisions/decision-testing.md +0 -0
  45. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/decisions/index.md +0 -0
  46. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/design/effectiveformat-spike.md +0 -0
  47. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/design/index.md +0 -0
  48. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/design/markdown-support.md +0 -0
  49. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/design/phase3-theme-system.md +0 -0
  50. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/design.md +0 -0
  51. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/index.md +0 -0
  52. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/known-limitations.md +0 -0
  53. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/notes-read.md +0 -0
  54. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/README.md +0 -0
  55. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/fixtures/tc-d195-create-doc.md +0 -0
  56. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/fixtures/tc-d196-create-doc.html +0 -0
  57. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/fixtures/tc-d213-dollar-escape.md +0 -0
  58. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/fixtures/tc-d226-heading-table.md +0 -0
  59. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/fixtures.template.md +0 -0
  60. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/operations.yaml +0 -0
  61. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/playwright_oauth.md +0 -0
  62. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/.gitkeep +0 -0
  63. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-06-02.md +0 -0
  64. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-06-14.md +0 -0
  65. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-06-17.md +0 -0
  66. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-06-18.md +0 -0
  67. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-06-21-drive-discovery.md +0 -0
  68. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-06-21-sheets-delete-clear.md +0 -0
  69. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-06-28.md +0 -0
  70. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-07-04-docs.md +0 -0
  71. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-07-04-drive-smoke.md +0 -0
  72. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-07-04-infra.md +0 -0
  73. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-07-04-sheets_read.md +0 -0
  74. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/results/2026-07-04.md +0 -0
  75. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/retro-v0.8.0.md +0 -0
  76. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/run.md +0 -0
  77. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/runs/README.md +0 -0
  78. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/runs/v0.8.0.md +0 -0
  79. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/runs/v0.8.1.md +0 -0
  80. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/setup.md +0 -0
  81. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/tests/calendar.md +0 -0
  82. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/tests/drive.md +0 -0
  83. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/tests/infra.md +0 -0
  84. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/tests/sheets_charts.md +0 -0
  85. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/tests/sheets_mgmt.md +0 -0
  86. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/tests/sheets_read.md +0 -0
  87. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa/tests/sheets_write.md +0 -0
  88. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/qa-checklist.md +0 -0
  89. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/roadmap.md +0 -0
  90. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/docs/tools.md +0 -0
  91. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/mkdocs.yml +0 -0
  92. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/pyproject.toml +0 -0
  93. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/pyrightconfig.json +0 -0
  94. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/scripts/gen_tool_docs.py +0 -0
  95. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/scripts/oauth_setup.py +0 -0
  96. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/.env.template +0 -0
  97. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/__init__.py +0 -0
  98. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/auth.py +0 -0
  99. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/cache.py +0 -0
  100. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/server.py +0 -0
  101. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/__init__.py +0 -0
  102. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/cache.py +0 -0
  103. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/calendar.py +0 -0
  104. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/docs/__init__.py +0 -0
  105. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/docs/content.py +0 -0
  106. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/docs/layout.py +0 -0
  107. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/docs/style.py +0 -0
  108. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/docs/tables.py +0 -0
  109. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/drive/__init__.py +0 -0
  110. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/drive/activity.py +0 -0
  111. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/drive/files.py +0 -0
  112. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/drive/sharing.py +0 -0
  113. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/drive/transfer.py +0 -0
  114. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/response_limits.py +0 -0
  115. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/sheets/__init__.py +0 -0
  116. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/sheets/data.py +0 -0
  117. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/sheets/helpers.py +0 -0
  118. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/src/mcp_gee_sweet/tools/sheets/structure.py +0 -0
  119. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/__init__.py +0 -0
  120. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/drive/__init__.py +0 -0
  121. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/drive/test_activity.py +0 -0
  122. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/drive/test_files.py +0 -0
  123. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/drive/test_sharing.py +0 -0
  124. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/drive/test_transfer.py +0 -0
  125. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/sheets/__init__.py +0 -0
  126. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/sheets/test_data.py +0 -0
  127. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/sheets/test_helpers.py +0 -0
  128. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/sheets/test_structure.py +0 -0
  129. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_auth.py +0 -0
  130. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_cache.py +0 -0
  131. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_calendar.py +0 -0
  132. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_docs_content.py +0 -0
  133. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_docs_layout.py +0 -0
  134. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_gen_tool_docs.py +0 -0
  135. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_response_limits.py +0 -0
  136. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/tests/test_server.py +0 -0
  137. {mcp_gee_sweet-0.7.0.dev72 → mcp_gee_sweet-0.7.0.dev74}/uv.lock +0 -0
@@ -17,7 +17,7 @@ jobs:
17
17
  - uses: actions/checkout@v7
18
18
 
19
19
  - name: Install uv
20
- uses: astral-sh/setup-uv@v8.2.0
20
+ uses: astral-sh/setup-uv@v8.3.0
21
21
 
22
22
  - name: Set up Python 3.12
23
23
  run: uv python install 3.12
@@ -18,7 +18,7 @@ jobs:
18
18
  fetch-depth: 0 # full history so mike can see existing gh-pages versions
19
19
 
20
20
  - name: Install uv
21
- uses: astral-sh/setup-uv@v8.2.0
21
+ uses: astral-sh/setup-uv@v8.3.0
22
22
 
23
23
  - name: Set up Python 3.12
24
24
  run: uv python install 3.12
@@ -23,7 +23,7 @@ jobs:
23
23
  fetch-depth: 0 # full history so uv-dynamic-versioning can read tags
24
24
 
25
25
  - name: Install uv
26
- uses: astral-sh/setup-uv@v8.2.0
26
+ uses: astral-sh/setup-uv@v8.3.0
27
27
 
28
28
  - name: Set up Python 3.12
29
29
  run: uv python install 3.12
@@ -45,7 +45,7 @@ jobs:
45
45
  fetch-depth: 0 # full history so uv-dynamic-versioning can read tags
46
46
 
47
47
  - name: Install uv
48
- uses: astral-sh/setup-uv@v8.2.0
48
+ uses: astral-sh/setup-uv@v8.3.0
49
49
 
50
50
  - name: Set up Python 3.12
51
51
  run: uv python install 3.12
@@ -15,7 +15,7 @@ jobs:
15
15
  - uses: actions/checkout@v7
16
16
 
17
17
  - name: Install uv
18
- uses: astral-sh/setup-uv@v8.2.0
18
+ uses: astral-sh/setup-uv@v8.3.0
19
19
 
20
20
  - name: Set up Python 3.12
21
21
  run: uv python install 3.12
@@ -36,7 +36,7 @@ jobs:
36
36
  fetch-depth: 0
37
37
 
38
38
  - name: Install uv
39
- uses: astral-sh/setup-uv@v8.2.0
39
+ uses: astral-sh/setup-uv@v8.3.0
40
40
 
41
41
  - name: Set up Python 3.12
42
42
  run: uv python install 3.12
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-gee-sweet
3
- Version: 0.7.0.dev72
3
+ Version: 0.7.0.dev74
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
@@ -266,13 +266,13 @@ New tests to add:
266
266
  | Column widths | Yes | #66 |
267
267
  | Colspan | Yes | #67 partial |
268
268
  | Rowspan | Yes | `Cell.rowspan` in AST; #91 added `mergeTableCells` in emitter |
269
- | Nested tables | Yes | `Cell.nested_table: Table \| None`; #92 added stages 3+5 in `fill_tables` |
269
+ | Nested tables | Yes | `Cell.nested_table: Table \| None`; #92 added stages 3+5 in `fill_tables`. Superseded 2026-07-06 by `Cell.children: list[Run \| Table]` — see "Nested Table Rewrite" below |
270
270
  | Phase 3 styling fields (colors, fonts) | No | In AST as `None`; emitter ignores them |
271
271
  | `<ol>` ordered bullets | Yes | `BulletItem.ordered` → different bullet preset |
272
272
 
273
273
  ---
274
274
 
275
- ## Nested Table Design (#92)
275
+ ## Nested Table Design (#92, superseded 2026-07-06 by #108/#275 — see below)
276
276
 
277
277
  **Date:** 2026-06-19
278
278
 
@@ -294,9 +294,47 @@ A `<table>` inside a `<td>` is now supported (one level of nesting).
294
294
 
295
295
  **Phase 5 — fill nested cells**: after outer cell text is filled (phase 4), indices shift. A second re-fetch is performed, then `_collect_nested_table_pairs(doc_tables, ast_tables)` traverses cell content to find the nested `table` element and pairs it with the AST `Table`. `_build_fill_requests` reuses the same logic as outer cells.
296
296
 
297
- ### Known limitations (first pass)
297
+ ### Known limitations (first pass, now historical — see rewrite below)
298
298
 
299
- - One level of nesting only (table-in-table-in-table not supported)
300
- - Cells with `nested_table` should not also contain text runs (runs are dropped silently)
301
- - No `colspan`/`rowspan` inside nested tables
302
- - No `col_widths` on nested tables
299
+ - Cells with `nested_table` should not also contain text runs (runs are dropped silently) — **fixed and superseded**, see below
300
+ - One level of nesting only (table-in-table-in-table not supported) **fixed and superseded**, see below
301
+ - No `colspan`/`rowspan` inside nested tables — still true
302
+ - No `col_widths` on nested tables — still true
303
+
304
+ ---
305
+
306
+ ## Nested Table Rewrite: `Cell.children` + recursive fill (#108, #275)
307
+
308
+ **Date:** 2026-07-06
309
+
310
+ The single-`nested_table`-field design above had two real defects, both found while investigating #108:
311
+
312
+ 1. Text sharing a cell with a nested table was silently corrupted. Root cause was in the parser, not the emitter: `_AstParser._run_buf`/`_pending_runs` are shared, unscoped state across nesting levels — opening a nested `<table>` never flushed them, so text typed before the nested table bled into the *nested* table's own first cell instead of staying with the outer cell.
313
+ 2. Even once the parser stopped corrupting it, the single `Cell.runs` field had nowhere to put "text after the table" separately from "text before the table" — `fill_tables` always inserted a cell's text at its first paragraph, i.e. always immediately *before* any nested table, regardless of where the text actually appeared in the source HTML. Filed as follow-up #275 rather than silently expanding #108's scope; then, per explicit direction, fixed in the same pass ("do the fuller version").
314
+
315
+ ### Schema change (`ast.py`)
316
+
317
+ `Cell.runs: list[Run]` + `Cell.nested_table: Table | None` → **`Cell.children: list[Run | Table]`** — one ordered list holding exactly what appeared in the source, in order. This is the `Union[Run, Table]` design #92 originally proposed and explicitly didn't ship; the simpler two-field version turned out to be insufficient once #275 surfaced.
318
+
319
+ ### Parser changes (`html_parser.py`)
320
+
321
+ `_TableBuilder` replaces `_current_nested_table`/`_pending_leading_runs` with a single `_current_children: list[Run | Table]`, plus `append_runs()`/`append_nested_table()` methods that append to it in call order. `_AstParser.handle_starttag`'s `table` branch flushes pending runs onto the *outer* builder via `append_runs()` before pushing a new builder for the nested table (fixing defect 1 above); `handle_endtag`'s `table` branch calls `append_nested_table()` on the parent builder instead of setting a single field. `end_cell` builds `Cell(children=self._current_children + runs, ...)`. This works at arbitrary depth and for arbitrarily many nested tables per cell — no explicit depth/count limit needed.
322
+
323
+ ### Emitter changes (`emitter.py`)
324
+
325
+ Cells with no `Table` in `children` are still filled in one bulk batch by `_build_fill_requests` (unchanged, fast path — most cells). Cells with at least one `Table` child go through a new recursive, round-based algorithm:
326
+
327
+ - **`_fill_children_recursive(docs_service, doc_id, resolve, ast_tables)`** — `resolve(live_doc) -> doc_table dicts` positionally matching `ast_tables`. Tracks a per-cell cursor into `children`. Each round: for every still-pending cell, emit **one** contiguous segment — either a run of text (`insertText` + styles) or one nested table's shell (`insertTable`) — at that cell's next open position, sorted high→low within the round (same pattern as the old phase-3/5 code), then re-fetch before the next round.
328
+ - **`_cell_para_start_for_cursor`**: a cell's `content` alternates paragraph/table/paragraph/table/...; the Nth paragraph (0-indexed) is the one after the Nth already-inserted table, where N = count of `Table` children before `cursor`.
329
+ - **`_text_offset_since_last_table`**: a table's insertion point is its paragraph's `startIndex` *plus* this — the length of any text already inserted into that same paragraph earlier in this round-chain (the paragraph's own `startIndex` doesn't change when text is inserted into it, but the table must land after that text, not at the start).
330
+ - When a table shell is inserted, **`_fill_table_fully`** is called for it: re-fetches, bulk-fills its own plain cells via `_build_fill_requests`, then recurses via `_fill_children_recursive` for any of *its* cells that themselves contain a further nested table. This is what makes nesting depth unbounded — a nested table is filled exactly the same way a top-level one is.
331
+ - After recursing into any nested tables inserted in a round (which run their own `batchUpdate`s and shift subsequent document indices), the outer loop re-fetches again before its own next round — recursion's mutations must be visible before the outer cell's next segment (e.g. trailing text) is positioned.
332
+ - `_ast_cell_to_doc_cell` / `_find_nth_table_in_cell` locate a specific physical doc cell / the Nth nested-table content element inside a cell, reusing the existing `_physical_to_ast_indices`/`_build_phantom_set` colspan/rowspan-aware mapping.
333
+
334
+ `_build_nested_table_inserts` and `_collect_nested_table_pairs` (the old phase-3/5 helpers) are removed — fully superseded by the recursive algorithm.
335
+
336
+ ### Result
337
+
338
+ - Text before, between, and after any number of nested tables in one cell renders in true source order, at any nesting depth.
339
+ - The "one level of nesting only" and "no colspan/rowspan/col_widths inside nested tables" limitations from the original #92 design are otherwise unchanged — this rewrite only touches content ordering, not those structural features.
340
+ - Tested with `tests/test_docs_tables.py`'s `TestFillNestedCellContent`, which drives the real algorithm against a small in-memory Docs-API simulator (`FakeDoc`/`Para`/`TableNode` in that file) that actually applies `insertText`/`insertTable` mutations and re-derives indices — chosen over hand-computed index fixtures because this much re-fetch/recursion logic is easy to get subtly wrong in a way a fixture can't catch (and did, twice, during development).
@@ -939,6 +939,87 @@ These test the HTML→AST→Docs API pipeline introduced in Phase 2 (#87). All u
939
939
 
940
940
  ---
941
941
 
942
+ ### TC-DOC84: Text sharing a cell with a nested table is no longer dropped (issue #108) ⚠️ destructive
943
+ **Prompt**
944
+ **Playwright: required**
945
+ > "Write this HTML to doc {DOC_ID}: `<table><tr><td>Some label <table><tr><td>Inner</td></tr></table></td></tr></table>`"
946
+
947
+ **Checks**
948
+ - Call succeeds with no API error
949
+ - `get_doc_structure` shows the outer table (1 row × 1 col) with a non-empty text run in cell [0,0] (previously this cell's text was silently dropped — bled into the nested table's own cell instead)
950
+ - 🔍 Visual check: "Some label" appears above/before the nested table inside the outer cell, and the nested table's own cell reads "Inner" (not "Some label Inner" merged together)
951
+
952
+ **Cleanup:** write fixture content back
953
+
954
+ **Result (2026-07-06) ✅ PASS** `get_doc_structure` shows outer cell [0,0] `text: "Some label"` (previously empty per TC-DOC48's bug pattern). Playwright screenshot confirms "Some label" renders above the nested table, whose own cell reads exactly "Inner" — no merging.
955
+
956
+ ---
957
+
958
+ ### TC-DOC85: Text after a nested table in the same cell, correctly positioned (issue #275) ⚠️ destructive
959
+ **Prompt**
960
+ **Playwright: required**
961
+ > "Write this HTML to doc {DOC_ID}: `<table><tr><td><table><tr><td>Inner</td></tr></table>After</td></tr></table>`"
962
+
963
+ **Checks**
964
+ - Call succeeds with no API error
965
+ - `get_doc_structure` shows the outer cell's text as "After" — content intact, not merged with "Inner"
966
+ - 🔍 Visual check: "After" renders *below* the nested table, not above it
967
+
968
+ **Cleanup:** write fixture content back
969
+
970
+ **Result (2026-07-07) ✅ PASS** `get_doc_structure` cell [0,0] `text: "After"`. Playwright confirms "After" renders below the nested table ("Inner"). Note: an earlier pass of this test case (2026-07-06) incorrectly expected "After" to render *above* the table — that was the pre-#275-fix limitation (a cell's text always rendered as one block before any nested table, regardless of source order). #275 fixed the emitter to place text on the correct side of each nested table; this test case's expectation and prompt were updated to match.
971
+
972
+ ---
973
+
974
+ ### TC-DOC86: Text before AND after one nested table, both correctly positioned (issue #275) ⚠️ destructive
975
+ **Prompt**
976
+ **Playwright: required**
977
+ > "Write this HTML to doc {DOC_ID}: `<table><tr><td>Before <table><tr><td>Inner</td></tr></table> After</td></tr></table>`"
978
+
979
+ **Checks**
980
+ - Call succeeds with no API error
981
+ - `get_doc_structure` shows outer cell [0,0] text as `"Before \n After"` (the `\n` confirms two distinct paragraphs — before and after the table — not one merged block)
982
+ - 🔍 Visual check: "Before" above the nested table, "Inner" inside it, "After" below it
983
+
984
+ **Cleanup:** write fixture content back
985
+
986
+ **Result (2026-07-07) ✅ PASS** `get_doc_structure` shows `text: "Before \n After"`. Playwright screenshot confirms all three pieces render in the correct order and position.
987
+
988
+ ---
989
+
990
+ ### TC-DOC87: Multiple nested tables in one cell with text between them (issue #275) ⚠️ destructive
991
+ **Prompt**
992
+ **Playwright: required**
993
+ > "Write this HTML to doc {DOC_ID}: `<table><tr><td>A<table><tr><td>1</td></tr></table>B<table><tr><td>2</td></tr></table>C</td></tr></table>`"
994
+
995
+ **Checks**
996
+ - Call succeeds with no API error
997
+ - `get_doc_structure` shows outer cell [0,0] text as `"A\nB\nC"` (three separate paragraphs — one per text segment between/around the two nested tables)
998
+ - 🔍 Visual check: A, then a table containing "1", then B, then a second table containing "2", then C — all in that order
999
+
1000
+ **Cleanup:** write fixture content back
1001
+
1002
+ **Result (2026-07-07) ✅ PASS** `get_doc_structure` shows `text: "A\nB\nC"`. Playwright screenshot confirms both nested tables render in the correct positions with "1" and "2" filled in, and A/B/C text correctly interleaved — a capability that didn't exist before #275 (previously only one nested table per cell was supported at all).
1003
+
1004
+ ---
1005
+
1006
+ ### TC-DOC88: `colspan="0"` clamps to 1 instead of producing a degenerate zero-column cell
1007
+
1008
+ **Background:** Found via code review of PR #276 — `int(attr_dict.get("colspan") or 1)` only covers a *missing* colspan attribute; an explicit `colspan="0"` is a non-empty (truthy) string, so it survives the `or` and parses to the literal integer `0`. A cell that spans zero columns breaks downstream `num_cols` calculations used by the nested-table fill algorithm. Fixed by clamping colspan/rowspan to a minimum of 1 in `html_parser.py`.
1009
+
1010
+ **Prompt**
1011
+ > "Write this HTML to doc {DOC_ID}: `<table><tr><td colspan="0">Wide</td><td>Next</td></tr></table>`"
1012
+
1013
+ **Checks**
1014
+ - Call succeeds with no API error
1015
+ - `get_doc_structure` shows the table with `columns: 2` (not 1 or a broken/merged layout) — cell [0,0] text "Wide", cell [0,1] text "Next"
1016
+
1017
+ **Cleanup:** write fixture content back
1018
+
1019
+ **Result (2026-07-07) ✅ PASS** `get_doc_structure` shows `columns: 2`, cell [0,0] `text: "Wide"`, cell [0,1] `text: "Next"` — `colspan="0"` clamped to 1 and rendered as two normal side-by-side cells.
1020
+
1021
+ ---
1022
+
942
1023
  ### TC-DOC52: `get_doc_theme` scans body paragraph styles
943
1024
  **Note:** `get_doc_theme` reads explicit per-paragraph and per-run styles from the document body. It returns data for AI-generated docs (where styles are set explicitly on runs); for standard docs whose styles are fully inherited from named style defaults it returns an empty dict.
944
1025
 
@@ -43,8 +43,7 @@ class Run:
43
43
 
44
44
  @dataclass
45
45
  class Cell:
46
- runs: list[Run]
47
- nested_table: Table | None = None # one nested table rendered inside this cell
46
+ children: list[Run | Table] # ordered text runs and nested tables, in source order
48
47
  colspan: int = 1
49
48
  rowspan: int = 1
50
49
  is_header: bool = False