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