Serve move and copy that wait for Basecamp in basecamp mcp

Merged
RO
5h ago
+1157-34

Why

Agents on basecamp mcp could only start a move or copy with move_recording or copy_recording. They then had to poll get_filing and look up the new recording themselves. The CLI's own move and copy commands already wait and answer with the new recording. This change gives the MCP server the same behavior through basecamp-sdk v0.32.0's MoveAndWait and CopyAndWait.

What changed

  • basecamp_recordings move and copy take a recording id and type and a destination. The destination is project_id (its one enabled tool of the recording's kind, or its one to-do list) or container_id, with folder_id for Docs & Files and include_comments for a copy. Each call starts the filing once, waits up to a minute, and returns the new recording with source_id.
  • A failure carries error.type and error.retry_safe. A project with the tool turned off names the tool_id to enable. A project with several candidates lists them for container_id. A filing still running when the wait ends carries the filing_id to check with get_filing. An id that now redirects to a moved recording is refused as recording_moved, so a retried move never files the recording twice.
  • move_recording and copy_recording are removed. get_filing moves from basecamp_automation to basecamp_recordings, so the check is always next to the actions that need it.
  • The SDK is bumped to v0.32.0 and the vendored MCP model is synced to it.

Scope

  • Covers the 14 kinds the web's Move… and Copy… dialogs file: cards, card columns, messages, documents, uploads, cloud files, Google documents, folders, schedule entries, check-in questions, email forwards, to-dos, to-do lists and to-do list groups.
  • Leaves out whole dock tools (a project's Docs & Files or card table), as the CLI does. basecamp_cards move_card (column moves and wormholes) uses a different endpoint and is unchanged.
  • A recurring event files as its whole series, as in the CLI. The answer names the series and keeps the occurrence Basecamp read as occurrence_id.
  • One recording per call. No bulk form.

Tradeoffs

  • recording_type is required. Basecamp has no generic recording read, and the type selects the typed read that reports the parent, which tells a to-do list from a group and a folder from a whole Docs & Files.
  • No start-only twin stays beside the waiting actions. The gateway does not need one, and an agent shown both would have to pick.

Blast Radius

A client that calls move_recording, copy_recording or basecamp_automation get_filing now gets an unknown-action error and must use basecamp_recordings. basecamp_recordings now has write actions, so its tool-level read-only hint is off. --read-only drops move and copy and keeps summarize and get_filing.

Verification

  • bin/ci green on this head.
  • TestFilingServesEveryTypeTheSDKFiles asks FilingContainerFor about every recordable type (bc3's models, the SDK's constants and every type name in the vendored model, with each as a parent) and drives a move and a copy through the MCP wire for each of the 14 routes it accepts. It fails when the SDK files a type this server cannot read or a container kind it cannot resolve. It fails with Inbox::Forward removed from the read table, which was checked.
  • Live matrix: 64 of 64 rows passed through the built binary over stdio (initialize, then tools/call basecamp_recordings) against a dev bc3 with a job worker. Each kind ran move within its project, move to another project, copy in place and copy to another project. Extra rows covered folder placement in another project, include_comments, three refusals (tool turned off, two candidate message boards, a folder passed as the container), a recurring event moved and copied to another project, and a retried move refused. An independent API read proved each row: the new id is in the expected project and container, a moved id answers 302, and a copied source is unchanged. A filing_unsettled answer was not provoked live, because bc3 finishes in under 2 seconds and the wait is a minute; unit tests cover it. The driver is in the first comment below.