Skip to main content
Glama

update_doc_section

Replace a single section of a workspace's doc body, identified by its heading text. The targeted edit complement to update_doc (full replacement) and append_doc_section (append-only at the end). Use this when the agent maintains a recurring section (e.g., a 'Status' block in a launch-prep doc, an 'Outcomes' block in a meeting note) and only needs to refresh that one piece. Without it, agents are forced into 'GET → splice → PUT' which costs tokens, costs latency, and races against any concurrent human edit elsewhere in the doc (last-write-wins clobbers). Section semantics: the FIRST heading whose plain text matches heading exactly (case-sensitive on trimmed text) is found, and everything from that heading up to the next heading at the same OR shallower level is replaced. So a ## Outcomes section ends at the next ## … or # …; nested ### … subsections stay part of the replaced range. Returns 404 when no matching heading exists; strict by design so a misremembered heading fails loudly. markdown is the FULL replacement, INCLUDING the heading line: pass it back as-is to keep the heading, change it to rename or rewrite the heading, change the heading level, or omit the heading entirely (collapses the section into the prior one). Empty markdown deletes the section. Same markdown surface as update_doc / append_doc_section (CommonMark + GFM + ![alt](url) images + lone-URL videos (mp4/webm/mov/mkv/m4v) + Mermaid + KaTeX + callouts + SVG + details + cross-refs + @-mentions + URL embeds). Identity / attribution / events / doc-guard all flow through the same writeDocBody path as the other doc endpoints, so @-mentions in the new section fire doc.mention_added for newly-added mentions just like update_doc does. Requires editor role. Multi-surface workspaces optionally accept surface_slug to target a specific doc surface. WARNING — a section runs to the next heading of the SAME OR SHALLOWER level, so targeting the LAST heading (or a lone H1) means its section extends to the END OF THE DOCUMENT and this call replaces everything below it. That has silently destroyed a doc twice (#8196), both times returning success. Call get_doc first and check which headings follow the one you are targeting. Agent writes that would drop most of the doc's blocks are now REFUSED with both block counts. So are writes that would delete a heading inside the replaced range (a nested subsection, content below a trailing heading, or the target heading itself when you send only body text), drop a callout / quote / code block / table / image / embed in the range, or most of the section's own blocks; the refusal lists removedHeadings and removedBlocks. A block counts as carried back when one top-level block of your markdown has exactly its text (whitespace aside), or when you edit it in place (same number of blocks, same type and shape at the same position, and not emptied); rewording a callout into a paragraph, or folding its text into a larger block, counts as removing it. Carry back what you mean to keep, or pass allowBlockLoss: true to confirm an intended deletion. Edited or deleted paragraphs and lists are allowed but reported: replacedSection.removedBlockCount and removedBlocks. CONCURRENCY: concurrent writes to other sections are merged, never overwritten. But if you rebuild a section from an earlier read (someone may have written since), pass the updatedAt from your get_doc as if_unmodified_since, or pass the sectionHash from your previous update_doc_section response as if_section_hash (guards only this section). Without a precondition the write wins even over an edit you never saw. Every success returns prior (the replaced range's updatedBy, updatedAt, blockCount, contentHash: who and what you overwrote), sectionHash (the section as it now stands, the value for your next if_section_hash; null if your markdown left no heading), and the new updatedAt to chain into your next if_unmodified_since. Heed warning (section_recently_changed_by_other): your unguarded write landed right after another principal's; updatedBy on the doc names only the latest writer, so re-read and restore their edit if your body predates it.

Input Schema

NameRequiredDescriptionDefault
slugYesThe workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace').
headingYesPlain text of the heading to find (case-sensitive, trimmed). For `## Outcomes`, pass `Outcomes`. Hash marks and surrounding whitespace are stripped from the comparison automatically by the markdown converter. Use `get_doc` first if you need to enumerate the headings actually present.
markdownYesFULL replacement markdown for the section, including the heading line if you want to keep / rename / restructure it. Empty string deletes the section.
surface_slugNoOptional doc surface slug for multi-doc workspaces. Omit to target the primary doc surface.
allowBlockLossNoConfirm a replacement that removes content. Omit it normally: the call is refused (with block counts, any `removedHeadings` and any `removedBlocks`) when it would destroy the bulk of the doc or of the section, delete a heading inside the replaced range that your markdown does not carry back, or drop a callout, quote, code block, table, image, embed, diagram, math or details block in the range. Set true only when the deletion is what you intend.
if_section_hashNoOptional precondition scoped to THIS section: the `sectionHash` from your last update_doc_section response for this section (the hash AFTER that write; `prior.contentHash` is the hash before it and would always conflict). When the section has changed since, the write is rejected with `code: -32602` and `data: { conflict: true, currentSectionHash }`; edits to other sections don't trip it. Never retried; re-read, merge, and resend with the new hash.
if_unmodified_sinceNoOptional precondition. ISO 8601 timestamp (typically the `updatedAt` you read via `get_doc`, or the `updatedAt` of your last write). When set and the doc has changed since, the write is rejected with `code: -32602` and `data: { conflict: true, currentUpdatedAt, precondition }` so you can refetch and rebuild the section instead of overwriting someone else's edit with a stale copy. Compared to the millisecond when the value carries milliseconds. Concurrent writes to OTHER sections are always safe without it; pass it whenever your replacement was built from content you read earlier.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed3 schema fields changed
    • changedInput schema / properties / allowBlockLoss / description
      Previous value: -"Confirm a replacement that removes content. Omit it normally: the call is refused (with block counts and any `removedHeadings`) when it would destroy the bulk of the doc or of the section, or delete a heading inside the replaced range that your markdown does not carry back. Set true only when the deletion is what you intend."New value: +"Confirm a replacement that removes content. Omit it normally: the call is refused (with block counts, any `removedHeadings` and any `removedBlocks`) when it would destroy the bulk of the doc or of the section, delete a heading inside the replaced range that your markdown does not carry back, or drop a callout, quote, code block, table, image, embed, diagram, math or details block in the range. Set true only when the deletion is what you intend."
    • addedInput schema / properties / if_section_hash
      Added value: +{
      +  "description": "Optional precondition scoped to THIS section: the `sectionHash` from your last update_doc_section response for this section (the hash AFTER that write; `prior.contentHash` is the hash before it and would always conflict). When the section has changed since, the write is rejected with `code: -32602` and `data: { conflict: true, currentSectionHash }`; edits to other sections don't trip it. Never retried; re-read, merge, and resend with the new hash.",
      +  "type": "string"
      +}
    • addedInput schema / properties / if_unmodified_since
      Added value: +{
      +  "description": "Optional precondition. ISO 8601 timestamp (typically the `updatedAt` you read via `get_doc`, or the `updatedAt` of your last write). When set and the doc has changed since, the write is rejected with `code: -32602` and `data: { conflict: true, currentUpdatedAt, precondition }` so you can refetch and rebuild the section instead of overwriting someone else's edit with a stale copy. Compared to the millisecond when the value carries milliseconds. Concurrent writes to OTHER sections are always safe without it; pass it whenever your replacement was built from content you read earlier.",
      +  "type": "string"
      +}
  2. Changed1 schema field changed
    • changedInput schema / properties / allowBlockLoss / description
      Previous value: -"Confirm a replacement that removes most of the document. Omit it normally: the call is refused (with the previous and new block counts) when it would destroy the bulk of the doc, which is what a trailing-heading target does. Set true only when the deletion is what you intend."New value: +"Confirm a replacement that removes content. Omit it normally: the call is refused (with block counts and any `removedHeadings`) when it would destroy the bulk of the doc or of the section, or delete a heading inside the replaced range that your markdown does not carry back. Set true only when the deletion is what you intend."
  3. Changed1 schema field changed
    • addedInput schema / properties / allowBlockLoss
      Added value: +{
      +  "description": "Confirm a replacement that removes most of the document. Omit it normally: the call is refused (with the previous and new block counts) when it would destroy the bulk of the doc, which is what a trailing-heading target does. Set true only when the deletion is what you intend.",
      +  "type": "boolean"
      +}
  4. First observed

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so thoroughly: it discloses destructive risk from section-range semantics, the guard/refusal behavior, `allowBlockLoss` confirmation, concurrency merging, precondition conflicts, return fields (`prior`, `sectionHash`, `updatedAt`), warning signals, and the required editor role. This is far beyond what annotations would provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded and every major paragraph carries important safety or selection information, but it is very long and repeats some parameter details already present in the schema (e.g., `allowBlockLoss`, `if_section_hash`, `if_unmodified_since`). It remains readable and justified for a dangerous mutation tool, but could be tightened.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description explains success return fields, conflict errors, refusals, and warning behavior. Given the complexity of section replacement and concurrency guards, an agent has enough information to call this tool correctly and safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although the schema has 100% description coverage, the description adds substantial semantics beyond it: exact heading matching and section-boundary rules, `markdown` being the FULL replacement including the heading line, empty markdown deleting the section, `allowBlockLoss` acting as an explicit deletion confirmation, and the difference between `if_section_hash` and `if_unmodified_since` preconditions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Replace a single section of a workspace's doc body.' It immediately distinguishes this tool from `update_doc` (full replacement) and `append_doc_section` (append-only), so an agent can select it without opening sibling schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use this tool ('when the agent maintains a recurring section') and names the alternatives it complements. It also provides the critical prerequisite to call `get_doc` first and check headings, with a clear reason: avoiding accidental deletion below the target section.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.