Skip to content

feat(mcp): build complete workflows through the MCP server - #518

Merged
driaug merged 1 commit into
useplunk:nextfrom
felipefontoura:feat/mcp-workflow-builder
Oct 8, 2026
Merged

driaug merged 1 commit into
useplunk:nextfrom
felipefontoura:feat/mcp-workflow-builder

Conversation

@felipefontoura

Copy link
Copy Markdown

Description

Follow-up to #497. The MCP workflow tools could build a linear SEND_EMAIL / DELAY sequence; conditions, waits, contact updates, webhooks and exits had to be added in the dashboard builder. This covers the rest of the graph through the existing /workflows API, so an agent can build and maintain a branching workflow end to end. No API changes.

New read-only tools

Tool Route
plunk_check_workflow GET /workflows/:id; a numbered outline in walk order plus the problems listed below
plunk_list_workflow_fields GET /workflows/fields
plunk_get_workflow_execution GET /workflows/:id/executions/:executionId

New writing tools

Tool Route
plunk_add_workflow_steps Several steps in one call, each placed after the previous one unless it names after / branch
plunk_update_workflow_step PATCH /workflows/:id/steps/:stepId, overlaying only the arguments passed
plunk_delete_workflow_step DELETE /workflows/:id/steps/:stepId, see downstream below
plunk_connect_workflow_steps POST /workflows/:id/transitions
plunk_disconnect_workflow_steps DELETE /workflows/:id/transitions/:transitionId
plunk_update_workflow PATCH /workflows/:id (name, description, triggerConfig.eventName, allowReentry)
plunk_duplicate_workflow POST /workflows/:id/duplicate, optionally renaming / re-triggering the copy
plunk_delete_workflow DELETE /workflows/:id, disabled workflows only
plunk_start_workflow_execution POST /workflows/:id/executions, by contact ID or email
plunk_cancel_workflow_executions DELETE .../executions/:executionId or POST .../executions/cancel-all

plunk_add_workflow_step now takes every step type (WAIT_FOR_EVENT, CONDITION yes/no and multi-branch, UPDATE_CONTACT, WEBHOOK, EXIT), placed with after + branch. If that position already leads to a step, the new one goes in between via insert-step.

Notes

  • Settings are validated before they are saved. POST/PATCH .../steps store config as-is and only the executor parses it, so a bad config surfaces as a FAILED execution for a real contact. Arguments are checked against a copy of WorkflowStepConfigSchemas (the package is published standalone, so it does not import @plunk/shared), plus the dashboard's rule that comparison operators need a value, plus the paths resolveField can reach (contact.email, contact.subscribed, data.* / contact.data.*, event.*, workflow.*). Any other field always resolves to undefined.
  • Deleting a step is explicit about what happens below it. The plain step DELETE removes everything reachable from the step, so downstream is reconnect (default, ?splice=true), detach (drops the step's outgoing transitions first, then the step) or delete (the cascade, listing every step it removes). A CONDITION with steps below it requires detach or delete.
  • Confirmation. The existing elicitation gate now also covers changing the trigger event or allowing re-entry on an enabled workflow with email steps, and starting a workflow for a contact. Renames and other edits are not gated.
  • Inserting a multi-branch CONDITION (or using continueOn) needs the downstream path moved off the yes branch the API attaches, which takes two more calls; that is refused while the workflow is enabled.
  • plunk_update_workflow_step refuses to drop a condition branch that still has a transition. plunk_cancel_workflow_executions refuses executions that are not RUNNING or WAITING.
  • resolveContact in contacts.ts is exported for plunk_start_workflow_execution. README, mcp-server.mdx and the server instructions are updated.

Changes to the #497 tools

  • plunk_add_workflow_step creates the step with autoConnect: false and then the transition, removing the step again if the transition is rejected. addStep's autoConnect links from the most recent step with no outgoing transition, which can be an EXIT step or a CONDITION (giving a branch-less transition that is followed for every contact). Without after, the step goes after the single step that leads nowhere; if there are several, the tool lists the open positions instead of guessing. autoConnect: false behaves as before, and the default step names (Send email, Wait N days) are unchanged.
  • plunk_set_workflow_enabled refuses to enable while plunk_check_workflow reports errors: invalid configs, SEND_EMAIL without a template, transitions on branches the condition does not produce, or more than one exit from a non-condition step. Warnings (steps unreachable from the trigger) do not block.
  • plunk_get_workflow prepends the outline to its summary; the JSON payload is unchanged.

Observed in the API, not changed here

  • cancelExecution does not check the current status, so cancelling a finished execution overwrites its outcome (the tool guards against it).
  • A cancelled execution parked on WAIT_FOR_EVENT keeps its step execution WAITING, so handleEvent / processTimeout resume it. The cancel tool's description says so.

Happy to open issues or follow-up PRs for either.

Type of Change

  • feat: New feature (MINOR version bump)

Testing

  • New apps/mcp/src/__tests__/workflow-builder.test.ts drives the tools through a real MCP client against an in-memory fake of the /workflows routes. The fake keeps steps and transitions and applies createTransition's rules (one exit per non-condition step, one transition per branch, no loops, nothing out of EXIT) and deleteStep's cascade, so assertions are on the resulting graph. The rollback, detach, confirmation, cancel and live-insert guards were each checked by disabling them and seeing their test fail.
  • workflows.test.ts and server.test.ts are updated for the new tools, destructive annotations and fixtures with valid configs.
  • vitest run --project mcp: 90 passed. eslint . and tsc --noEmit in apps/mcp: clean.
  • End to end against a self-hosted instance: created a disabled workflow; built a yes/no condition, a multi-branch condition, UPDATE_CONTACT, DELAY, SEND_EMAIL, WAIT_FOR_EVENT, WEBHOOK and EXIT in one plunk_add_workflow_steps call; inserted a step mid-flow; edited a delay; deleted a leaf (reconnect) and detached a condition; checked the result; deleted the workflow.

Checklist

  • PR title follows conventional commits format
  • Code builds successfully
  • Tests pass locally
  • Documentation updated (if needed)

Related Issues

Follow-up to #497.

Extend the workflow tools from useplunk#497 to the whole graph: every step type
(WAIT_FOR_EVENT, yes/no and multi-branch CONDITION, UPDATE_CONTACT,
WEBHOOK, EXIT), placement after a step or branch or between two steps,
connecting and disconnecting steps, editing and deleting steps, and
updating, duplicating and deleting workflows.

Add plunk_check_workflow, which outlines a workflow in walk order and
lists what would fail at run time, and refuse to enable a workflow while
it reports errors. Step settings are validated against the executor's
schemas before they are saved, since the API stores them unvalidated.

Steps are now connected explicitly instead of through addStep's
autoConnect, which can link from an EXIT step or without a branch from a
CONDITION. Deleting a step states what happens below it, because the
plain step DELETE cascades.

Also add plunk_list_workflow_fields, plunk_get_workflow_execution,
plunk_start_workflow_execution and plunk_cancel_workflow_executions.
@driaug
driaug merged commit 70c97b8 into useplunk:next Oct 8, 2026
2 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.

2 participants