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), clamped to a hard ceiling of 30000 (30 seconds). A value of 0, a negative number or anything that is not a number is treated as unset and the default applies (it never means “no timeout”); a value below 1000 is raised to 1000. |
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 afailureedge and nosuccessedge is rejected with a validation error. Asuccessedge on its own is allowed, but then a failed call still fails the run. - A node with neither label keeps today’s behavior exactly: every outgoing edge fires unconditionally, and an unhandled failure still fails the run.
- The same routing works on
action,webhook,llm,sub_agentandset_variablenodes (since 2026-10-15). See Failure routing for the full rules, including what never takes thefailureedge. - 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. - Every call made by a workflow run carries an
x-wetel-trace-idheader (live in production since 2026-10-13). Its value is the run’straceId, so you can match a request in your own logs to the run that sent it. It is the same for the paused and the continued half of anawait_replyorapprovalstep, and for asub_agentchild run. It is sent on the HTTP requests of the tool call itself (tools/call), not when the connector’s tool list is read. Ignore it if you do not need it. - The MCP server must not answer in Brotli (since 2026-10-23). Tool calls ask for
Accept-Encoding: gzip, deflateand refuse a Brotli-encoded response; gzip and deflate are fine. If your MCP server compresses with Brotli whatever the request says, disable Brotli for it. See Responses Wetel cannot read. timeoutMsdefaults to 10 seconds and is clamped to 30 seconds (a value below 1 second is raised to 1 second) — raise it for a connector you know is slow, but keep in mind a long-blocking tool call delays the whole conversation turn, and no value will wait longer than 30 seconds.- 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.
Retry policy
Section titled “Retry policy”Live in production since 2026-10-24. An optional retry field in config sets how many times a failed call is tried again, on which failure classes, and how long to wait. Only resends that are safe happen: a request that may already have reached the service is resent only when it is idempotent. See Retrying a failed call.
In a mock-mode test
Section titled “In a mock-mode test”Live in production since 2026-10-24. In a mock-mode draft test or regression replay, the node renders argsTemplate and looks up the connector exactly as live, then stops: no address check, no credential, no MCP call, no metering. renderedRequest in the trace row still shows the arguments it would have sent. outputVar gets the test’s draftMocks entry, else the node’s mockResponse (any JSON value up to 16 KB; an object is stored as its JSON text), else {"mocked": true, "nodeId": "<id>", "toolName": "<toolName>"}. A {"fail": "..."} mock takes the failure edge like a real failure. See Testing a Workflow Safely.
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.