Skip to content

feat(mcp): automation-rule CRUD tools (#334)#211

Merged
ikrasovytskyi merged 1 commit into
mainfrom
mcp/334-automation-crud
Jul 25, 2026
Merged

feat(mcp): automation-rule CRUD tools (#334)#211
ikrasovytskyi merged 1 commit into
mainfrom
mcp/334-automation-crud

Conversation

@ikrasovytskyi

Copy link
Copy Markdown
Contributor

Exposes automation-rule CRUD over MCP so an agent can build a complete workflow end to end. Closes #334.

Tools added (all admin-scoped)

  • list_automations — a database's rules: trigger (field/relation names resolved), condition, actions, enabled state, failure streak, and last-run status.
  • get_automation — one rule's full editable definition, human-readable database/field/select names alongside stable ids.
  • create_automation — trigger (record_created / record_updated / record_linked / schedule / webhook_received) + optional condition (a query_records-style filter) + 1–10 actions (set_values, create_record, add_comment, notify_user, update_linked, send_slack_message, send_webhook, send_email, http_request, run_agent).
  • update_automation — rename, enable/disable, replace trigger/condition/actions; every field optional.
  • delete_automation — requires confirm=true; reports the removed rule's name, trigger and action summary.

Reuses the in-app validation (no parallel path)

Every tool calls the same endpoints the in-app RuleEditor uses (AutomationsControllerAutomationsServiceAutomationActionsService.validate()), so that validator stays the single source of truth. Human-readable trigger/action field references, select labels and qualified space/database refs resolve to ids client-side against describe_database; anything not resolvable there (connections, agents, relation targets) is validated by the API. An unresolvable/stale reference returns a structured error (client-side, naming the candidates) or the API's 422 — surfaced as an MCP isError result, never a 500. Reads inject a sibling *_name next to every id for a faithful round-trip; hookSecret/lastHookPayload are never surfaced.

Scope enforcement

AutomationsController is @RequiresScope('admin') and creator-gated, so all five tools are advertised only to an admin-scoped token (mirrored in TOOL_SCOPE). A read- or write-scoped PAT never sees them and therefore can never mutate — or even list — a rule. This mirrors the API's existing enforcement exactly; there is no lower-privilege read path to invent (automations are an admin surface).

No API/SDK changes

The automation endpoints already exist in the generated SDK client (list/create/patch/delete), so this is a pure MCP-consumer change — no openapi.json/SDK drift, no migration. The API has no single-GET automation endpoint, so get_automation/delete_automation read the database's rule list and pick the row out (404-ing clearly on an unknown id).

Verified

  • packages/mcp unit + integration tests (118 passing), incl. new automations.test.ts:
    • CRUD happy paths through a fake client whose POST body is parsed by the real createAutomationSchema (the controller's DTO) — proving the resolved body is API-valid.
    • a schedule-triggered rule and a state-change (record_updated by field name) rule with an action.
    • human-readable name resolution (field/relation/person names + select labels → ids) and read-side *_name annotation.
    • an invalid field reference → structured isError, not a throw/500, and no rule created.
    • authorization: read and write tokens get none of the automation tools registered; admin gets all five.
    • delete_automation confirmation gate.
  • pnpm --filter @storyos/mcp lint / typecheck / test / build all green; pnpm docs:check green (README + get_started updated with the build-a-workflow flow).

Docker (docker/mcp.Dockerfile) and the full monorepo lint/typecheck/test/build are left to CI (local disk was space-constrained); no new dependencies were added to packages/mcp, so the image build is unaffected.

Expose automation-rule CRUD over MCP so an agent can build a complete
workflow end to end: list_automations, get_automation, create_automation,
update_automation, delete_automation.

The tools call the SAME endpoints the in-app RuleEditor uses, so
AutomationActionsService.validate() stays the one validator — no parallel
path. Human-readable trigger/action field references, select labels and
qualified space/database refs resolve to ids server-side (against
describe_database); anything not resolvable client-side (connections,
agents, relation targets) is validated by the API, which returns a
structured 422 surfaced as an isError result, never a 500. Reads annotate
every id with a sibling *_name for a faithful round-trip.

Scope: the AutomationsController is @RequiresScope('admin') and
creator-gated, so all five tools are advertised only to an admin-scoped
token — a read- or write-scoped PAT never sees them and can never mutate a
rule. delete_automation requires confirm=true and reports what it removed.

No API/SDK changes: the automation endpoints already existed in the
generated client. Docs + get_started updated with the build-a-workflow flow.

Signed-off-by: ikrasovytskyi <i.krasovytskyi@owox.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
storyos-docs a4cbb33 Commit Preview URL

Branch Preview URL
Jul 25 2026, 08:46 PM

@ikrasovytskyi
ikrasovytskyi enabled auto-merge July 25, 2026 20:46
@ikrasovytskyi
ikrasovytskyi added this pull request to the merge queue Jul 25, 2026
Merged via the queue into main with commit a88506a Jul 25, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant