Skip to content

MCP: expose block-node deletion (blocks.delete / blocks.deleteRange) via superdoc_edit #3586

@jacobjove

Description

@jacobjove

What problem does this solve?

The apps/mcp server can delete text (superdoc_edit action:"delete", superdoc_mutations text.delete) but has no way to remove an entire block node. Deleting a heading's or paragraph's text leaves the empty <w:p> container behind — it still renders as block/heading spacing — so an MCP client (e.g. an AI agent running a redline/cleanup pass) can't remove a clause, heading, or stray empty paragraph.

The capability already exists in document-api: blocks.delete and blocks.deleteRange are implemented, tested, and wired through the super-editor adapter (they physically remove the container via deleteBlockNodeById / tr.delete()), merged in #3444. They're just not exposed on the MCP tool surface: their operation-definitions.ts entries carry no intentGroup/intentAction, so codegen skips them.

Proposed solution

Expose both as actions on the existing superdoc_edit tool (intentGroup: 'edit'):

  • blocks.deletedelete_block, input { target: { kind:'block', nodeType, nodeId } }
  • blocks.deleteRangedelete_block_range, input { start, end }

This is the smallest additive change: two field-pairs on the contract plus regenerated catalog/dispatch, no new tool, no plan-engine work. The delete_block name is forced by a collision, since the existing text-delete op already owns intentAction:'delete' in the edit group.

A draft PR implementing exactly this is up (#3587): regenerated MCP catalog plus Node/browser/Python dispatch, and end-to-end apps/mcp protocol tests proving physical block removal (block-count decrement + nodeId absence). Opened as draft to confirm the surface choice before marking ready.

Alternatives considered

  • Dedicated superdoc_blocks tool (action:"delete" / "delete_range"). Matches the structural-delete precedent (lists.deletesuperdoc_list action:"delete", tables.deletesuperdoc_table action:"delete") and avoids the delete collision, but blocks.list already lives under superdoc_get_content, so this yields a 2-op tool that can't list; a whole new tool for two ops is heavier.
  • superdoc_mutations block.delete step. Enables atomic batching of block removal within a multi-step redline, but requires extending the plan-engine step framework. A sound follow-up, not v1.

Happy to rework toward either if you'd prefer.

Additional context / limitations

  • blocks.delete / blocks.deleteRange have supportsTrackedMode: false, so block-node removal can't currently be recorded as a tracked change; the "AI suggests a clause deletion as a redline" workflow isn't covered. Tracked structural deletion is a separate effort (cf. feat(track-changes): block-level structural tracked changes for tables #3343 for tables).
  • blocks.delete covers a superset of node types (paragraph, heading, listItem, table, sdt), overlapping tables.delete / lists.delete. It's the type-agnostic primitive alongside the type-specific tools.

Environment: superdoc-dev/superdoc @ current main.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions