feat: add create workflow tool in MCP

This commit is contained in:
Abhishek Kumar 2026-04-25 17:38:38 +05:30
parent d6567eef28
commit 3e3773f400
13 changed files with 245 additions and 94 deletions

View file

@ -12,27 +12,26 @@ mistake the system has seen at least once.
"""
DOGRAH_MCP_INSTRUCTIONS = """\
You build and edit Dograh voice-AI workflows by emitting TypeScript that
uses the `@dograh/sdk` package. Workflows are stored as JSON; this server
projects them to TypeScript for editing and parses them back on save.
You build and edit Dograh voice-AI workflows by emitting TypeScript that uses the `@dograh/sdk` package. Workflows are stored as JSON; this server projects them to TypeScript for editing and parses them back on save.
## Call order
### Editing an existing workflow
1. `list_workflows` locate the target workflow.
2. `get_workflow_code(workflow_id)` fetch the current source (draft if
one exists, otherwise published).
3. (optional) `list_node_types` / `get_node_type(name)` consult before
adding or editing a node type whose fields aren't already visible in
the current code.
4. Mutate the code in place. Preserve existing nodes, edges, and variable
names unless the task requires removing or renaming them.
5. `save_workflow(workflow_id, code)` persist as a new draft. The
published version is untouched.
2. `get_workflow_code(workflow_id)` fetch the current source.
3. (optional) `list_node_types` / `get_node_type(name)` consult before adding or editing a node type whose fields aren't already visible in the current code.
4. Mutate the code in place. Preserve existing nodes, edges, and variable names unless the task requires removing or renaming them.
5. `save_workflow(workflow_id, code)` persist as a new draft. The published version is untouched.
### Creating a new workflow
1. Create a simple 1-node workflow with only `startCall`. The user can iteratively add complexity by editing it.
2. `list_node_types` / `get_node_type(name)` consult to learn the fields available on the node types you intend to use.
3. Author SDK TypeScript from scratch. The `new Workflow({ name: "..." })` call is required `name` becomes the workflow's display name.
4. `create_workflow(code)` persists a new workflow as version 1 (published). Returns the new `workflow_id`. For subsequent edits use `save_workflow(workflow_id, code)` (which writes a draft).
## Allowed source shape
The parser is AST-only and rejects anything outside this grammar. At the
top level, only three statement forms are accepted:
The parser is AST-only and rejects anything outside this grammar. At the top level, only three statement forms are accepted:
import ... from "..."; // any import
const <var> = <initializer>; // bindings (see below)
@ -43,26 +42,17 @@ top level, only three statement forms are accepted:
wf.addTyped(<factory>({ ...fields }) [, { position: [x, y] }])
wf.add({ type: "<nodeType>", ...fields [, position: [x, y]] })
No functions, arrow fns, loops, conditionals, ternaries, spreads,
destructuring, template interpolation, `export`, or `.map`/`.forEach`.
Data-position values must be plain literals (strings, numbers, booleans,
null, arrays/objects of same). A single `new Workflow(...)` per file
the `name` you pass there is the workflow's display name and is applied
on save (renames propagate immediately; definition changes go to draft).
No functions, arrow fns, loops, conditionals, ternaries, spreads, destructuring, template interpolation, `export`, or `.map`/`.forEach`.
Data-position values must be plain literals (strings, numbers, booleans, null, arrays/objects of same). A single `new Workflow(...)` per file the `name` you pass there is the workflow's display name and is applied on save (renames propagate immediately; definition changes go to draft).
## Adding edges — explicit syntax
wf.edge(source, target, { label: "...", condition: "..." });
Rules:
- `source` and `target` are the **bare variable identifiers** bound by
`wf.addTyped(...)` / `wf.add(...)` not strings, not `.id`, not inline
factories. Both must be declared earlier in the file.
- `label` is a short tag (4 words) shown in call logs to identify the
branch: `"qualified"`, `"wrap up"`, `"retry"`.
- `condition` is a full natural-language predicate the runtime evaluates
against the live conversation: `"caller confirmed interest in a demo"`,
not `"interested"`. Condition clarity determines routing accuracy.
- `source` and `target` are the **bare variable identifiers** bound by `wf.addTyped(...)` / `wf.add(...)` not strings, not `.id`, not inline factories. Both must be declared earlier in the file.
- `label` is a short tag (4 words) shown in call logs to identify the branch: `"qualified"`, `"wrap up"`, `"retry"`.
- `condition` is a full natural-language predicate the runtime evaluates against the live conversation: `"caller confirmed interest in a demo"`, not `"interested"`. Condition clarity determines routing accuracy.
- Both fields are required and must be non-empty strings.
- Edges are directional; emit one `wf.edge(...)` per outgoing branch.
- Place all edges after all node bindings; group by source node.
@ -76,49 +66,30 @@ Example:
condition: "user acknowledged the greeting and is ready to end"
});
## Hard graph constraints
- Exactly one `startCall` node per workflow; no incoming edges.
- `endCall` nodes have no outgoing edges.
- `globalNode` has no incoming or outgoing edges; its prompt is prepended
to every other node's prompt at runtime when that node sets
`add_global_prompt=true`.
- Every non-global node must be reachable from `startCall`.
## Iterating on errors
`save_workflow` returns one of:
- `parse_error` Disallowed construct (see grammar above) or
malformed TypeScript.
- `validation_error` Node data failed spec validation (unknown field,
missing required, wrong type, bad `options` value).
- `graph_validation` Structural rule broken (missing startCall,
unreachable node, edge to/from wrong node type).
- `bridge_error` Internal retry once, then surface to the user.
`save_workflow` and `create_workflow` return one of:
- `parse_error` Disallowed construct (see grammar above) or malformed TypeScript.
- `validation_error` Node data failed spec validation (unknown field, missing required, wrong type, bad `options` value).
- `graph_validation` Structural rule broken (missing startCall, unreachable node, edge to/from wrong node type).
- `missing_name` (`create_workflow` only) `new Workflow({ name })` is absent or empty.
- `bridge_error` Internal; retry once, then surface to the user.
Every error carries `line` and `column`. Fix at that location and
resubmit the **complete source** this tool does not accept patches.
Every error carries `line` and `column`. Fix at that location and resubmit the **complete source** this tool does not accept patches.
## Field conventions
- `data.name` is the canonical identifier. Pick a descriptive name
(`"Qualify Budget"`, not `"Node1"`) the generated code uses it as
the variable name and call logs reference it.
- `data.name` is the canonical identifier. Pick a descriptive name (`"Qualify Budget"`, not `"Node1"`) the generated code uses it as the variable name and call logs reference it.
- Reference fields take UUIDs, not human names:
`tool_refs`, `document_refs` from `list_tools`, `list_documents`
`credential_ref` from `list_credentials`
`recording_ref` from `list_recordings`
- `mention_textarea` fields (prompts, greetings, etc.) accept
`{{template_variables}}` values resolved at runtime from
`pre_call_fetch`, caller context, or earlier extraction passes.
- `tool_refs`, `document_refs` from `list_tools`, `list_documents`
- `credential_ref` from `list_credentials`
- `recording_ref` from `list_recordings`
- `mention_textarea` fields (prompts, greetings, etc.) accept `{{template_variables}}` values resolved at runtime from `pre_call_fetch`, caller context, or earlier extraction passes.
## Style
- Prefer `wf.addTyped(factory({ ... }))` over `wf.add({ type, ... })`.
- Only include fields whose values differ from the spec default the
parser re-applies defaults on save, so extras are noise.
- Omit `position`; the server reconciles positions against the previous
saved workflow and lays out new nodes automatically.
- Add nodes in call-flow order (start intermediate end) so the
generated code reads top-to-bottom, with all edges after all nodes.
- Only include fields whose values differ from the spec default the parser re-applies defaults on save, so extras are noise.
- Omit `position`; the server reconciles positions against the previous saved workflow and lays out new nodes automatically.
- Add nodes in call-flow order (start intermediate end) so the generated code reads top-to-bottom, with all edges after all nodes.
"""