Issue #233 closed: scheduled_tasks: add a run_once action (one-off command execution with output, safe cleanup)
Motivation (real war story, 2026-07-02)
Needed to run three one-off artisan/SQL commands inside the crunch app container (apply a missed migration). The only path today is the documented dance: create a task with frequency: "* * * * *", wait for the minute tick, read list_executions, delete the task. Sharp edges hit in practice:
- The task fires every minute until deleted — a non-idempotent SQL statement ran twice before the delete landed (and the "cleanup" task then also ran twice, deleting both rows it was meant to dedupe). Recovering required a third, carefully idempotent (
where not exists) task. - Timing the tick manually (create → wait ~60-70s → list_executions → delete) is boilerplate every caller reimplements badly.
Verified there is no upstream trigger/run-now endpoint: the bundled docs/coolify-openapi.yaml has no scheduled-task trigger (the only trigger in the spec is database backups), and src/lib/coolify-client.ts has no such method. So this must be a composite in the MCP layer.
Proposed: action: "run_once"
Params: resource, uuid, command, container, optional timeout (execution), optional wait_seconds (poll budget, default ~90).
Behaviour:
- Create task: generated name (
oneoff-<rand>),frequency: "* * * * *", enabled. - Poll
list_executions(existing endpoints:coolify-client.ts:1460app /:1637service) every ~5s until the first execution appears with a terminal status, orwait_secondsexpires. - Delete the task in a finally — cleanup must happen on success, timeout, and error.
- Return the execution's
status+message(themessagefield carries the command's stdout — worth also documenting onlist_executions, sincetypes/coolify.ts:1188doesn't model it).
Tool description must carry an idempotency warning: "the underlying cron may fire more than once before cleanup completes — make the command idempotent or tolerate re-execution." (Deleting immediately after the first observed execution shrinks but does not close the window.)
Where
- Tool shape/handler:
src/lib/mcp-server.ts:1656(scheduled_tasks). - Client: reuse existing create/delete/list_executions methods; no new endpoints needed.
Acceptance
- One call runs a command in a container and returns its output, with the task gone afterwards in all paths.
- Timeout path returns a clear message including the task UUID it cleaned up (or failed to — say so loudly).
- Docs:
site/concepts/coolify-api-gotchas.mdgains the every-minute/idempotency gotcha for anyone still doing it manually.