Serve move and copy that wait for Basecamp in basecamp mcp
Merged
RO
5h ago+1157+1157+0123456789012345678901234567890123456789-34-34-01234567890123456789
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 moveandcopytake a recording id and type and a destination. The destination isproject_id(its one enabled tool of the recording's kind, or its one to-do list) orcontainer_id, withfolder_idfor Docs & Files andinclude_commentsfor a copy. Each call starts the filing once, waits up to a minute, and returns the new recording withsource_id.- A failure carries
error.typeanderror.retry_safe. A project with the tool turned off names thetool_idto enable. A project with several candidates lists them forcontainer_id. A filing still running when the wait ends carries thefiling_idto check withget_filing. An id that now redirects to a moved recording is refused asrecording_moved, so a retried move never files the recording twice. move_recordingandcopy_recordingare removed.get_filingmoves frombasecamp_automationtobasecamp_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_typeis 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/cigreen on this head.TestFilingServesEveryTypeTheSDKFilesasksFilingContainerForabout 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 withInbox::Forwardremoved 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. Afiling_unsettledanswer 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.