Releases: dasasian/gdocs-mcp
Release list
v0.6.0 — Drive as a filesystem
[0.6.0] — 2026-08-21
Changed
Breaking: five Drive tools became one.
list_folder,search_drive,create_folder,copy_docandupdate_docare removed, replaced bydrive({ cmd, args }). Anything calling them by name must be updated.
- Drive navigation is now a filesystem (#44). The five Drive tools became
drive({ cmd, args })speakingls/find/mkdir/cp/mv— 36 tools down to 32. Paths are/or~(My Drive),/shared/<drive name>,/shared-with-meand/lost+found; anything else is read as a Drive id or URL, so ids from other tools paste straight in. A whole path resolves in a singlefiles.listcall. - Drive is not a filesystem in three ways, and each refuses rather than half-working (#44). Two files may share a name in one folder and matching folds case, so an ambiguous path is refused with the candidates listed — and
cp/mvrefuse to create that state.cp -rdoes not exist: Drive'sfiles.copyrejects folders outright.mvinto/shared/…transfers ownership irreversibly, so it requiresacceptOwnershipTransfer. mvkeepsupdate_doc's guard asexpectName, and covers rename and move together the way a filesystem does: an existing folder as the destination means "into it", anything else means "to that name" (#44).- Flags and operands parse in any order, as a terminal accepts them:
cp -r a b,cp a b -randcp a -r bare one command, and--ends the options. What an unrecognised-tokenmeans is set per command, because the commands disagree —ls -la /Workwants the flag ignored,find -2026wants-2026searched for (#44). cpkeeps the source name. Drive'sfiles.copydefaults to "Copy of …", which is its UI convention;cp file /diron a filesystem producesfile(#44).ls /lost+foundreplaceslist_folder({ folder: "orphaned" })— Unix's name for exactly this (#44, #46).- No destructive command ships. There is none in the surface to collapse, and host permissions are granted per tool name: allowlisting
drivesolsstops prompting would allowlistrmtoo. Conditions that would change that are in #47 (#44).
Added
- Files in no folder are listable —
ls /lost+found. A parentless file opens and turns up in a search, but nothing that browses the tree will ever show it, so it is found only by someone who already remembers it exists. Drive has no query operator for "has no parent", so the scan pages the files you own and reportsscanned/completerather than passing a silent cap off as the whole answer. Re-home one withmv(#46). - The README shows what to ask for. Two chained examples — filling a template without rebuilding it, and a whole review pass in one instruction — now sit above the tool table, where the only pointer to
docs/recipes.mdused to be a blockquote below it.docs/recipes.mdgains a Whole jobs section above the single-tool recipes (#44).
v0.5.0 — sharing you can actually see and revoke
Changed
Breaking:
unshare_docnow requiresexpectRole. A call passing onlydocumentIdandlist_permissionsfirst and echo the role back.
unshare_docnow requiresexpectRole. Revoking access is the one operation in the surface with no undo — verified live, a Drive revision carries no permission data at all, so version history restores content and never sharing. Echo back the rolelist_permissionsreported and the call proceeds only if the grant is still that; otherwise nothing is removed. An optionalexpectTitlerefuses a wrong document id as well.
Fixed
- Two tool descriptions pointed at tools that do not exist.
set_styletold callers to runinspect_style, which was renamedget_style;list_suggestionspointed atapply_suggestion, whose singular form was deleted. Both sent the model somewhere there was nothing. A test now reads the descriptions and fails on any name that is not a registered tool (#45). set_styleandedit_docdid not say which to use. Since 0.3.0 both can style existing text, butedit_docrequires restating the whole run, and retyping is how text gets silently dropped. Each now names the other and says when it is the wrong choice (#45).- A domain or group grant could be seen but never revoked.
unshare_docmatched on email, and a domain or anyone-with-link grant has none — so the domain-wide grant every doc gets under a Workspace was permanent as far as the tool surface was concerned. It now also accepts apermissionIdfromlist_permissions, which covers every grant type, echoes back what it removed, refuses the owner, and lists what is present when nothing matches (#41). list_permissionscould not name a domain or link grant. It never asked Drive fordomainorallowFileDiscovery, so a domain-wide grant came back with a null email and no way to tell which domain it covered — rendering asnull:reader. Every entry now carries asubjectnaming its audience (alice@x.com,x.com (domain),anyone with the link), and domain/link grants report whether the file is discoverable in search rather than only reachable by link (#42).
Install: npx -y @dasasian/gdocs-mcp · npm · docs
Full diff: v0.4.0...v0.5.0
v0.4.0 — get_table_style
Added
get_table_style— reads a table's column widths, pinned header rows, and the matched cell's padding, background and per-side borders, located by cell text like its setter.set_table_stylewas the only setter with no getter, so everything it wrote was invisible on read.columnWidthscomes back in the shape the setter accepts, so one table's layout can be copied onto another (#33).
No behaviour changes to existing tools, so nothing to check on upgrade.
Install: npx -y @dasasian/gdocs-mcp · npm · docs
Full diff: v0.3.0...v0.4.0
v0.3.0 — six correctness fixes, and the cleanup that found them
Six correctness fixes and the cleanup pass that uncovered them. Nearly all of it
is one bug wearing different clothes: a write whose effect the next read could
not see, so an agent could not verify its own work. DESIGN.md §2 now states
the rule — a construct the writer accepts must be a construct the reader emits.
Changed
read_docoutput format. Anything that parses it should be checked.
read_docnow shows text color, size and font. A run carrying any of them comes back wrapped in<span style="color:…;font-size:…pt;font-family:…">— the exact spelling the writer already parsed, so it round-trips.DESIGN.md§2 made inline HTML the escape hatch for formatting markdown can't express and required it be "visible in the read", but the reader never emitted these, so aset_stylecolour change was invisible on the next read and an agent could not verify its own edit or preserve styling it was rewriting around. Emission is quiet by default: Google only populates these fields on runs that override them, so inherited text (including headings) is untouched (#30).- Embedded images read back with their size.
read_docnow emits<img src="image:<objectId>" width="…" height="…">(points) instead of a bare, and the writer accepts an<img>line — so image dimensions survive a round-trip.DESIGN.md§2 named<img width="400">as the mechanism; it existed on neither side. The plainform still works unchanged for authoring. Writing animage:<objectId>marker back is now refused with an explanation, rather than failing as a missing local file, because Docs stores the embedded bytes and not a re-fetchable URL (#30).
Added
segment/pageon the table and suggestion tools —insert_table,edit_table,set_table_style,list_suggestionsandapply_suggestionscan now target a header or footer, the same way the text tools already could. A letterhead table or a tracked change on a footer disclaimer was previously unreachable, and failed silently: the cell text simply never matched, soedit_tablereported "no table cell containing …" as though the table did not exist. These now reportno_segmentwith the list of segments the doc actually has.insert_tablealso takescreateSegment(asinsert_imagedoes), so a table can be placed in a header the doc doesn't have yet (#28).- Inline code round-trips. Docs has no code style, so the writer maps
`x`to a monospace font; the reader now maps it back. Previously the backticks were dropped on read (#30). insert_tablecells accept inline markdown, and take per-columnalign. Cell text now goes through the same renderer the markdown path uses, so**bold**,`code`and[links](url)work;align: ["center","right"]sets column alignment at creation (#29).insert_imageaccepts a local file path, not just a public URL — it uploads to Drive, embeds, and removes the temp upload, the same wayin pushed markdown already did. Relative paths resolve againstbaseDir(#29).
Fixed
overwrite_docandinsert_contentinherited the styling of the text they replaced. Google'sinsertTextpicks up the character formatting at the insertion point, and when a delete and an insert share onebatchUpdate— which is exactly what an overwrite is — the new text inherits the formatting of the text just deleted. So pushing plain markdown into a bold, coloured or hyperlinked document produced bold, coloured, hyperlinked output that nothing in the markdown asked for. The rendered range now has its direct character styling cleared before the markdown's own styling is applied. Named styles still inherit, so a document'sNORMAL_TEXTfont is unaffected (#32).read_docwrapped every hyperlink in a redundant colour span. Docs writes its link blue in as a direct run colour, which the new colour rendering then surfaced. The default is now suppressed on links (as underline already was), while a deliberately coloured link still shows (#32).insert_tablewrote literal markdown into cells.data: [["**Bold**"]]inserted the asterisks as text. Becauseread_docrenders genuinely-bold text as**Bold**too, a read-back looked correct while the document held corrupt text, so callers had no way to notice (#29).- A read→write round-trip corrupted nested inline styles. The reader emits styles in layers (
<u>**AAA**</u>for a bold+underlined run), but the writer's inline parser was one level only: it took a container's contents verbatim, so the inner style was dropped and its markers were baked into the text as literal characters. Each cycle added another layer (<u>****AAA****</u>), so a document degraded every time it was read and written back. Containers now re-parse their contents and layer their own styles on. Same-tag nesting remains unsupported and is documented indocs/limitations.md(#31). edit_doccould not match text containing__.old_stringis resolved by exact match first, then by a markup-stripped retry; the strip step ran its own copy of the markdown grammar, which had drifted from the writer's. The writer guards underscore-bold with CommonMark word boundaries, the strip step did not — so a signature rule (____ ____) or an intraworda__b__ccopied out ofread_docwas mangled into something the document never contained, and the edit was refused. The strip step now derives its plain text from the writer's own parser (#27).
Install: npx -y @dasasian/gdocs-mcp · npm · docs
Full diff: v0.2.0...v0.3.0
v0.2.0 — copy_doc, create_folder, table borders and pinned headers
Added
-
copy_doc— duplicate a Doc via Drivefiles.copy, with an optional new name and target folder. Copying preserves what a markdown round-trip cannot rebuild (headers/footers, image sizing, exact formatting), so a template can be reused instead of recreated. Kept as its own tool rather than anupdate_docmode: it creates a file rather than mutating one (#24). -
create_folder— create a Drive folder, optionally inside a parent (URL or id). Previously the only way to make a folder was the Drive UI (#25). -
set_table_style({ border })— cell border width (pt), color (hex), dash style, and which sides, over the samescopeas padding/background.border: { width: 0 }makes a table borderless (#21). -
set_table_style({ headerRows })— repeat the top N rows on every page (Docs' "pin header rows");0unpins (#19). -
insert_content— insert new markdown-rendered content at a structural position:at: "end"(default),"top", or a unique text anchor to insert right after. This is the only path to content thatedit_doccannot anchor: a paragraph after a table that ends the doc (a table cell can't anchor an insert outside the table, and Docs' mandatory trailing empty paragraph has no text to match). Kept as its own tool rather than anedit_docmode soedit_docstays "replace this exact text" (#20). -
export_doc— export a Doc to a local file: pdf (default), docx, odt, rtf, txt, html, epub, or md, via Drivefiles.export. Google renders server-side, so pagination and page setup match the editor. Note Drive refuses exports over 10 MB (#22).
Changed
- Headers and footers are reachable everywhere text is (#23).
read_doc,edit_doc,set_style,get_style,insert_contentandinsert_imageall takesegment: "body" | "header" | "footer"(pluspagefor first-/even-page variants);read_docalso takessegment: "all". Writes to a header/footer that doesn't exist returnno_segmentlisting what does, andcreateSegment: truecreates it (default header/footer only — the API cannot create first-/even-page ones). Implemented by threadingsegmentIdthrough the existing request builders, not a parallel set of tools. - A body read no longer looks empty when it isn't (#23).
read_docnow reports the headers/footers it did not render, with paragraph and image counts. This was a wrong answer, not a missing one: a letterhead's logo lives in the page header, soread_docreturned markdown with no image at all and the doc read as having no logo. search_drive/list_folderresults now carryparents— each entry lists its parent folder(s) as{ id, name }, so a hit can be traced upward (e.g. to create a sibling folder). Parent names are resolved once per distinct id, and degrade to the bare id if a lookup fails (#26).
Install: npx -y @dasasian/gdocs-mcp@0.2.0 · npm · docs
Full diff: v0.1.1...v0.2.0
v0.1.1
Added
- MCP Registry metadata — a
server.json(registry schema) plus anmcpNamefield inpackage.json, so the server can be published to the official MCP Registry asio.github.dasasian/gdocs-mcp. No functional or API changes.
v0.1.0
First public release — a Model Context Protocol server that lets an AI agent treat a
Google Doc like a local file. The full tool surface is implemented and validated
against the live Docs/Drive API. As a 0.x release the tool surface may still change
between minor versions.
Reading & editing
- read_doc — markdown + inline HTML, in
clean/tracked(<ins>/<del>) /accepted/rejectedmodes. - edit_doc — string-anchored, markup-tolerant edits (no indices);
new_stringrenders inline markdown + HTML. - overwrite_doc — guarded wholesale replace (refuses to orphan comments/suggestions); accepts inline
contentor acontentFilepath read server-side. - create_doc — render a markdown doc (inline
contentorcontentFile), optionally into a Drive folder. - update_doc — rename and/or move a doc, with a title-verification guard on move.
Styling
- set_style — style existing text the way you select in Docs: a single
fromsnippet, afrom/toselection, or thewhole_document; bold/italic/underline/strikethrough, color, font size/family, link, alignment, and paragraph spacing. Bold survives a whole-document font change (works around a Docs API quirk that otherwise drops it). - get_style — read the effective (inherited-resolved) style at a text anchor.
- set_page_setup / get_page_setup — document margins, page size (preset or explicit), and orientation.
Suggestions (tracked changes)
- list_suggestions — pending changes as
before → afterdiffs. - apply_suggestions — accept/reject one or more in a single atomic update; resolves overlapping/adjacent clusters safely (resolving them one at a time corrupts neighbours) and surfaces genuine
conflictsinstead of reporting a clean merge.
Comments
- list_comments / add_comment (replies via
replyTo) / resolve_comment — with a quote-verification guard on resolve.
Tables & images
- Markdown tables render on create/overwrite and round-trip via read_doc (inline formatting + column alignment).
- insert_table, edit_table (insert/delete a row or column, located by cell text), set_table_style (padding, background, column widths).
- insert_image (position/size/align); markdown images render on create/overwrite; download_images pulls embedded images to disk with an id→file map + sha256.
Tabs, Drive & accounts
- list_tabs / add_tab / rename_tab / delete_tab, plus tab-targeting on read/edit/suggestion tools.
- list_folder / search_drive; list_permissions / share_doc (a person or anyone-with-link) / unshare_doc.
- list_accounts,
add-accountCLI, per-project defaults via.gdocs-mcp.jsonandGDOCS_DEFAULT_ACCOUNT.
Safety
- Confirmation guards on destructive / opaque-id tools: a human-readable label (
expectTitle/expectQuote) is shown in the permission prompt and verified against live state before mutating — a mismatch refuses without changing anything. - Every write is a direct (live-text) edit, not a tracked suggestion — tools say so in their descriptions and results.
Known limitations (Google-API constraints, not bugs)
See docs/limitations.md. Highlights: suggestion attribution
(author/time) is unavailable via any Google API; comment author email isn't returned
by Drive; images are inline-only and don't read back to a stable URL; embedded code
blocks aren't rendered yet (Tier-2 roadmap).