Tool Node
The tool node invokes a tool exposed by a registered MCP connector — an external server implementing the Model Context Protocol. This is the standard way to call out to your own systems (looking up records, checking availability, fetching live data) once you’ve registered an MCP server as a connector.
For registering a connector, discovering its available tools, and known limitations of that side of the integration (schema drift, argument-splicing constraints, and so on), see MCP Connectors — this page covers only the workflow-node side: how to configure a tool node once a connector already exists.
See Workflows Overview for how this node type fits into the broader node/edge model.
Config fields
Section titled “Config fields”ToolNodeConfig:
| Field | Type | Required | Description |
|---|---|---|---|
connectorId | number | Yes | The ID of the MCP connector to call, scoped to your tenant. |
toolName | string | Yes | Which tool on that connector’s MCP server to invoke. |
argsTemplate | string | No | A Handlebars-templated JSON string providing the tool’s call arguments. Values can reference context variables set by earlier nodes (for example, an LLM node’s extracted outputVar). Rendered with the same escaping used for webhook body templates and action argument templates. |
outputVar | string | Yes | The context variable the tool’s result is stored in — the flattened, plain-text form of the tool’s response. See Workflow Best Practices for the structured (_{outputVar}Structured) form also written alongside it, and how to avoid leaking a raw JSON tool response into the chat reply. |
timeoutMs | number | No | Request timeout in milliseconds. Defaults to 10000 (10 seconds). |
Worked example
Section titled “Worked example”An appointment-scheduling workflow that has already extracted a requested date into requestedDate, then checks availability via an MCP-registered scheduling tool:
{ "id": "check_availability", "type": "tool", "label": "Check Appointment Availability", "config": { "connectorId": 12, "toolName": "check_availability", "argsTemplate": "{\"date\": \"{{requestedDate}}\", \"durationMinutes\": 30}", "outputVar": "availability_result", "timeoutMs": 8000 }, "position": { "x": 250, "y": 200 }}The result then feeds a response node:
{ "id": "respond_availability", "type": "response", "label": "Respond with Availability", "config": { "messageTemplate": "Here's what I found for {{requestedDate}}: {{availability_result}}", "mood": "helpful" }, "position": { "x": 350, "y": 200 }}Success/failure edges
Section titled “Success/failure edges”By default, every edge leaving a tool node fires unconditionally once the node finishes — regardless of whether the underlying MCP call succeeded. You can opt a tool node into a two-port routing pattern instead, exactly like a condition node’s true/false pair: draw one edge labeled success and one labeled failure out of the node (in the Workflow Editor UI, toggle “Add success/failure branch” on the node’s card toolbar to get the two colored handles).
- If a
failureedge exists and the tool call throws (a timeout, a connection failure, an MCP schema-drift error, a malformedargsTemplate), the runner routes to that edge instead of failing the whole run. The failure message is written to a_lastNodeErrorcontext variable a downstream node can reference. - A
failureedge requires a matchingsuccessedge on the same node — publishing a workflow with one but not the other is rejected with a validation error. - A node with neither label keeps today’s behavior exactly: every outgoing edge fires unconditionally, and an unhandled failure still fails the run.
- This is purely about “did the network call succeed” — it’s unrelated to and doesn’t affect any session-identity/authorization outcome an action node might separately expose via an
${outputVar}Statusvariable.
Gotchas
Section titled “Gotchas”argsTemplatehas zero schema awareness. Nothing validates it against the connector’s actual MCP tool schema, either at workflow-save time or at execution time. If the external MCP server later renames a field or adds a new required one, nothing here will notice — you have to updateargsTemplateyourself. See MCP Connectors for more on this.- Never point
argsTemplateat a bare"{{someVar}}"wheresomeVaralready holds a whole pre-serialized JSON string. The templating engine escapes every interpolated value as a plain scalar being inserted inside quotes you’ve already written — pointing it at a value that’s already JSON-shaped double-escapes the whole thing and failsJSON.parse()on every call. Have an extraction LLM node output plain scalar values into separate variables, and write the JSON structure yourself inargsTemplate, referencing each scalar inside quotes you’ve already placed. - There’s no way to splice a variable-length array into
argsTemplatetoday. If a tool argument accepts an array (for example, several selected categories) and the user’s message names more than one, there’s no supported templating construct for that — an LLM node’soutputVaris always raw text, never a parsed array. The practical workaround is branching to parallel single-valuetoolcalls (one per selected value) and merging the results in a downstream LLM node before phrasing the reply. - For “match this human value to an internal id” lookups, don’t use an LLM node — use the
findIdtemplate helper instead. Asking an LLM to find the matching entry in an array and output its id is unreliable in a specific, dangerous way: it tends to confuse a different field’s own digits (a code, an ISBN) for the real id.findIdperforms the lookup deterministically with no LLM involved:{{findId <arrayVariable> "<matchField>" <matchValueVariable> "<idField>"}}, referencing an earlier tool node’s structured result (exposed as_{outputVar}Structured). See Workflow Best Practices for the full pattern and worked example. timeoutMsdefaults to 10 seconds — raise it for a connector you know is slow, but keep in mind a long-blocking tool call delays the whole conversation turn.- The MCP connector’s
serverUrlis now re-validated on every call, not just when the connector is registered (as of 2026-09-08). Previously an MCP connector’s SSRF check only ran when it was created/updated — a hostname that resolved to a public address at registration time but was later repointed at a private one (DNS rebinding) was never re-checked. Everytoolnode call now re-validates before dispatching, and redirects on the underlying HTTP transport are re-validated hop-by-hop rather than followed blindly. - When a tool call fails, the real error is not shown to the user — the conversation gets a generic reply, but the underlying error (a JSON parse failure, an HTTP error from the connector) is available in the workflow run’s node trace and in a live
NodeFailedEventon the session subscription. - To see exactly what
argsTemplateactually rendered to, checkrenderedRequeston this node’snodeTraceentry — captured on both success and failure. See Observability & Debugging Your Agent for the fullnodeTraceshape.
Next steps
Section titled “Next steps”- MCP Connectors — registering connectors, discovering tools, and known limitations.
- Workflows Overview — the full node/edge model.
- Workflow Best Practices — templating rules, the
findIdhelper, and extraction-node framing. - Action Node and Webhook Node — alternatives for plain REST endpoints.
- LLM Node, Condition Node, Router Node, Response Node, Sub-Agent Node — other node types.
- Worked Examples & Demos — complete multi-node workflows.
- Workflows API Reference — the mutations for creating and publishing workflows.