Skip to content

slack_update_canvas: action=replace with section_id inserts sibling instead of replacing; full-canvas replace also auto-injects sticky canvas title H1 #30

@andrewacorn

Description

@andrewacorn

Two related bugs in slack_update_canvas on the hosted Slack MCP at https://mcp.slack.com/mcp, both re-verified on 2026-04-11 against a real Slack canvas.

Environment

  • MCP server: https://mcp.slack.com/mcp (HTTP MCP, hosted, accessed via the Cowork slack-by-salesforce plugin)
  • Plugin: slackapi/slack-mcp-plugin (manifest wrapper, no local source to patch)
  • Slack workspace: Acorn Rentals (private)
  • Test canvas: a daily ops canvas I rebuild every morning from a local markdown file, safe to destructively test against

Bug 1: action=replace + section_id inserts a sibling instead of replacing

Steps to reproduce

  1. slack_read_canvas(canvas_id=<canvas>) to capture a section_id_mapping.
  2. Pick any paragraph section ID from the mapping.
  3. slack_update_canvas(canvas_id=<canvas>, action="replace", section_id=<that id>, content="TEST REPLACEMENT CONTENT PARAGRAPH").
  4. slack_read_canvas(canvas_id=<canvas>) again.

Expected

The targeted section is replaced by the new content. The original section is gone.

Actual

The original section is still present. A NEW sibling element is inserted immediately after it with the replacement content. Confirmed on 2026-04-11 with fresh reproduction IDs against my test canvas:

  • Paragraph test: targeted temp:C:YHQ7c017b835b2490b568139c241, replacement appeared as new sibling temp:C:YHQ67bdbd6b8fa2a99a9f975e46b immediately after the still-present original.
  • Header test: targeted temp:C:YHQ1074388272fe8bc877cee32da (an H1 section), replacement appeared as new sibling temp:C:YHQ0ea46e83931897be9ea6f11a0 immediately after the still-present original.

Confirmed on both paragraph and header section types, so it is not type-specific.

Root cause hypothesis

Slack's canvases.edit API documents six operations: insert_after, insert_before, insert_at_start, insert_at_end, replace, delete. The observed behavior matches insert_after with the supplied section_id as the anchor, not replace. Most likely the MCP server maps action=replace + section_id to Slack API operation=insert_after (either a mapping typo or a deliberate but incorrect workaround for a different Slack issue).

Workaround

Omit section_id entirely and use full-canvas replace. slack_update_canvas(canvas_id=<canvas>, action="replace", content=<full markdown body>) atomically overwrites the entire canvas body and works correctly, with the caveat from Bug 2 below.

Bug 2: full-canvas replace auto-injects a sticky canvas title H1

Steps to reproduce

  1. slack_update_canvas(canvas_id=<canvas>, action="replace", content="# Acorn Daily Operations\n\nTest body content after leading H1.") (body markdown starts with the same H1 as the canvas title).
  2. slack_read_canvas(canvas_id=<canvas>).
  3. Then slack_update_canvas(canvas_id=<canvas>, action="replace", content="Test body content only, no leading H1.").
  4. slack_read_canvas(canvas_id=<canvas>) again.

Expected

Step 2: one H1 at the top. Step 4: no H1, just the body content (since none was sent).

Actual

Step 2 (leading H1 in body): read-back returns TWO H1s with identical text "# Acorn Daily Operations". The first is a sticky section with ID temp:C:YHQ8336071669ed64f982f5bd788, the second is a new body-sourced H1 with a content-hashed ID (confirmed with temp:C:YHQce72d96e64c9c257bd6d70bdd on my 2026-04-11 reproduction).

Step 4 (no leading H1 in body): read-back returns exactly ONE H1 at the top, the sticky temp:C:YHQ8336071669ed64f982f5bd788 H1 with text "# Acorn Daily Operations". The body content follows.

Root cause hypothesis

The sticky H1's section ID is invariant across writes, which confirms it is NOT content-derived. It is almost certainly the canvas title metadata being rendered into the markdown representation by either the Slack Canvas API on read, or the MCP server on write and read. Whatever is sending the body to Slack is not stripping or deduping an H1 at position 0 when that H1 matches the canvas title.

Workaround

Build the full canvas body so that the first non-empty line is not an H1. Let the MCP auto-inject the canvas title H1 above the body. This works reliably but forces every client to know about this behavior, and it prevents legitimate use of the canvas title as a body H1.

Impact

Bug 1 makes surgical section updates impossible, which forces every canvas-editing client into destructive full-canvas replace. That is acceptable for canvases that are fully rebuildable from a source file (my use case), but it is not acceptable for canvases where multiple authors or automations edit different sections independently. Bug 2 forces every client that uses full-canvas replace to know about the auto-injection behavior and build body markdown accordingly. Neither bug is documented in the Slack Canvas API docs I have been able to find.

Repro environment

I can share the exact MCP call transcripts and section_id_mappings from my 2026-04-11 test run on request. The test canvas itself is in a private workspace so I cannot link it directly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions