gdocs-patch
Efficient Google Doc editing for agents
Overview
When you ask an agent to edit a Google Doc, they would typically need to assemble Google Docs API requests manually. This involves finding the right order of operations, managing document indices, and keeping track of styles. It is all quite error-prone and could lead to data loss if the agent makes a mistake.
gdocs-patch is a CLI and MCP that allows agents to efficiently edit Google documents by representing documents as XHTML and allowing the agent to make targeted edits, just like editing a normal file locally.
Install
Install the CLI with uv:
uv tool install gdocs-patch
Alternatively, install it with pip:
pip install gdocs-patch
gdocs-patch supports Python 3.10 and newer.
How it works
gdocs-patch exposes a few commands for agents to use:
read Read a Google document as canonical XHTML.
edit Edit exact text in canonical XHTML.
write Write canonical XHTML to a Google document.
syntax Explore the XHTML document syntax.
skill Show best practices for coding agents using gdocs-patch.
auth Manage Google authentication.
The editing commands (read, edit, and write) all operate using a custom
XHTML dialect that represents every Google doc element as a tag or attribute.
The agents can learn the dialect by reading documents, or through the syntax and
skill commands.
gdocs-patch at its core is a source doc + target doc = google doc batch update request compiler. The agent reads the source document, edits the XHTML
locally, and calls write to apply the edits to the Google Doc. Alternatively,
agents can call edit to make targeted replacements without a local copy.
Authenticating with Google
You need to give the tool access to the Google Docs API. To do this, create a
Google Cloud project, enable the Google Docs API in the project, configure its
OAuth consent screen, and create an OAuth client with application type Desktop
app. Download its client JSON and save it in
~/.config/gdocs-patch/client_secret.json:
mkdir -p ~/.config/gdocs-patch
cp ~/Downloads/client_secret.json ~/.config/gdocs-patch/client_secret.json
The client_secret.json file allows gdocs-patch to log you in via OAuth.
gdocs-patch auth login
This command opens the OAuth authorization screen. Authorize the app. Once done,
gdocs-patch should say you're good to go.
If you need to run the tool non-interactively, you can copy the OAuth authorization URL from the terminal and paste it into your local browser. After finishing authorization, copy the complete callback URL from your browser's address bar and paste it back into the terminal.
MCP server
A Streamable HTTP MCP server is also available that exposes the read, edit,
and write commands over MCP. To use it, install gdocs-patch[mcp]:
uv tool install 'gdocs-patch[mcp]'
The server uses the same Google credentials as the CLI.
The MCP server is secured via a static Bearer token. Generate a token and set it
to the GDOCS_PATCH_MCP_TOKEN environment variable. Then start the server with
gdocs-patch-mcp:
export GDOCS_PATCH_MCP_TOKEN="$(openssl rand -hex 32)"
gdocs-patch-mcp --host 127.0.0.1 --port 8000
Every request must include Authorization: Bearer with the value of
GDOCS_PATCH_MCP_TOKEN. There's no built-in TLS, though, so you'd still need
your own reverse proxy and potentially more security if you want to expose the
MCP server over the open internet.
Google Docs support
This table shows what you can currently change with gdocs-patch and whether the
Google Docs batchUpdate API provides enough support for the feature.
- ✅ Supported
- ⚠️ Supported with limitations
- ❌ Unsupported
| Google Docs feature | Can add | Can edit | Can delete | batchUpdate supports it |
Notes |
|---|---|---|---|---|---|
| Text and formatting | ✅ | ✅ | ✅ | ✅ | Includes links and common character formatting. |
| Paragraphs and headings | ✅ | ✅ | ✅ | ✅ | Includes common paragraph styling. Heading IDs and tab stops are preserved but not editable. |
| Bulleted, numbered, and checklist items | ✅ | ✅ | ✅ | ✅ | Includes nesting and Google's standard presets. |
| Custom list appearance | ❌ | ⚠️ | ✅ | ❌ | Editing requires opt-in conversion to the closest Google preset, which may change its appearance. |
| Page breaks | ✅ | ✅ | ✅ | ✅ | New page breaks can only be inserted in the document body. |
| Sections | ✅ | ⚠️ | ✅ | ✅ | Most section formatting is editable, but some existing settings cannot be changed or cleared. |
| Tables | ✅ | ✅ | ✅ | ✅ | Includes rows, columns, merged cells, nested content, and cell styling. |
| Repeating table header rows | ❌ | ❌ | ❌ | ✅ | Existing settings are preserved, but gdocs-patch cannot change them yet. |
| Headers and footers | ❌ | ✅ | ❌ | ✅ | Content in existing headers and footers is editable. |
| Footnotes | ❌ | ✅ | ✅ | ✅ | Existing footnote content is editable. |
| Document tabs | ❌ | ⚠️ | ❌ | ✅ | Content in existing tabs is editable, but tabs cannot be created, moved, renamed, or deleted yet. |
| Images, drawings, and other embedded objects | ❌ | ❌ | ✅ | ⚠️ | Existing objects are preserved. Google supports some image operations, but not every kind of embedded object. |
| Dates, people, and rich links | ❌ | ❌ | ✅ | ✅ | Existing elements are preserved. Google supports inserting these, but gdocs-patch does not yet expose it. |
| Equations | ❌ | ❌ | ✅ | ❌ | Existing equations are preserved. Google does not expose their contents or provide requests to create or edit them. |
| Table of contents | ❌ | ❌ | ✅ | ❌ | Existing tables of contents are preserved. Google does not provide requests to create or update them. |
| Column breaks, horizontal rules, and auto text | ❌ | ❌ | ✅ | ❌ | Existing elements are preserved. Google does not provide requests to create them. |
| Document-wide and named style definitions | ❌ | ❌ | ❌ | ✅ | Existing definitions are preserved. Applying a named style to a paragraph is supported. |
| Named ranges | ❌ | ❌ | ❌ | ✅ | Named ranges are currently ignored by gdocs-patch. |
| Comments | ❌ | ❌ | ❌ | ❌ | Text edits try to preserve comment anchors. Comments are managed through the Google Drive API instead. |
| Suggestions | ❌ | ❌ | ❌ | ❌ | Suggested changes are not currently modeled. |
| Document metadata | ❌ | ❌ | ❌ | ⚠️ | IDs and revision information are preserved. Google does not provide Docs requests for changing all metadata. |
Development
Install Python 3.14 and synchronize all development dependencies:
uv sync --dev --all-extras
Run the test and static-analysis tools:
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run fixit lint .
uv run pyright
Install and run the Git hooks:
uv run pre-commit install
uv run pre-commit run --all-files
See RELEASING.md for package build, versioning, and PyPI publication instructions.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gdocs_patch-0.2.0.tar.gz.
File metadata
- Download URL: gdocs_patch-0.2.0.tar.gz
- Upload date:
- Size: 78.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52430d08cb0a1a8558ea7e7e835e005d7dc3129f212cdaf915e94c9321150bce
|
|
| MD5 |
155606e61c458a20cab78056e65aa2ea
|
|
| BLAKE2b-256 |
2c34916cc56f77b3cec68d77da2b72227244babd4d77c593088b6153c6881e18
|
Provenance
The following attestation bundles were made for gdocs_patch-0.2.0.tar.gz:
Publisher:
release.yml on mixxorz/gdocs-patch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gdocs_patch-0.2.0.tar.gz -
Subject digest:
52430d08cb0a1a8558ea7e7e835e005d7dc3129f212cdaf915e94c9321150bce - Sigstore transparency entry: 2541866374
- Sigstore integration time:
-
Permalink:
mixxorz/gdocs-patch@af8cf9601bf1ad6fcdb4a8e4e30e83bb097e9af7 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/mixxorz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@af8cf9601bf1ad6fcdb4a8e4e30e83bb097e9af7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file gdocs_patch-0.2.0-py3-none-any.whl.
File metadata
- Download URL: gdocs_patch-0.2.0-py3-none-any.whl
- Upload date:
- Size: 99.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1487a59e76e0be32cffcd8bd6d56e185d63f8237affbf5e2c8549e3598cc48a
|
|
| MD5 |
0b478ebf900f0e35ee66cf1ebd0ba391
|
|
| BLAKE2b-256 |
1131c93cca6364a26946be455c56ecc540792291701578f11383d574ae92ee6d
|
Provenance
The following attestation bundles were made for gdocs_patch-0.2.0-py3-none-any.whl:
Publisher:
release.yml on mixxorz/gdocs-patch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gdocs_patch-0.2.0-py3-none-any.whl -
Subject digest:
a1487a59e76e0be32cffcd8bd6d56e185d63f8237affbf5e2c8549e3598cc48a - Sigstore transparency entry: 2541866514
- Sigstore integration time:
-
Permalink:
mixxorz/gdocs-patch@af8cf9601bf1ad6fcdb4a8e4e30e83bb097e9af7 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/mixxorz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@af8cf9601bf1ad6fcdb4a8e4e30e83bb097e9af7 -
Trigger Event:
release
-
Statement type: