Skip to main content
Use this when a model should be able to fetch data or act through tools, while the graph, not the model, decides what runs. The model suggests a tool call; a node you wired runs it. Prerequisites: Call an LLM works for you. These are MCP client nodes (MCP_LIST_TOOLS, MCP_TOOL_CALL) that call other servers; to let an assistant drive Yukta itself, see Connect an assistant. Add -H "X-API-Key: $YUKTA_API_KEY" if your server has keys.

Steps

1

List the tools

MCP_LIST_TOOLS reads tool names, descriptions and input schemas from a server. allowedTools narrows the list the model sees.
The result is in payload.tools. The URL, tool names and token are placeholders for your own server; set YUKTA_SECRET_ISSUES_MCP on the Yukta server.
2

Let the model choose

LLM_CALL takes the tools in its tools option (an expression, so write payload.tools, not ${...}). If the model wants a tool, the call appears in payload.actions as {tool, arguments, id}. The node does not run it.
Each node replaces the payload with its own result, so carry what you still need in message metadata. A CONSTANT_SOURCE with "target": "METADATA" and "variables": {"task": "..."} at the start sets metadata.task, readable later as ${metadata.task}.
3

Branch on whether it asked

Port answer goes straight to a terminal; port tool continues.
4

Run the tool, with an allow-list

Map the first action into the tool call, then run it. allowedTools on this node means a model can never reach a tool you did not intend, even by choosing its name.
The result is payload = {success, tool, text, content, structured, error}. If the model asks for several tools, pick takes the first; split payload.actions with a SPLITTER node to run all of them.
5

Write the final answer

A second LLM_CALL quotes the task and the tool result: "prompt": "Task: ${metadata.task}\n\nTool result:\n${payload.text}". Edges: task → tools → plan → route, route -tool-> pick → act → finish → final-answer, route -answer-> direct-answer.
6

Hold risky tools for a person

On MCP_TOOL_CALL, requireApproval lists tools (or *) that wait for a human answer before they are called. The wait is an input request of risk HIGH, so only a person can answer; reject or no answer refuses the call.
approvalTimeoutSeconds is the longest wait (default 3600); approvalSignal names the request (default approve).

Verify it worked

Apply the session, start it with {"sessionId":"issue-agent","workflowId":"triage"} and read the node statuses:
When the model asked for a tool, tools, plan, route, pick, act and finish are SUCCESS and the result comes from final-answer. When it answered directly, pick, act and finish never ran and the result comes from direct-answer. GET /api/executions/$EXEC/nodes/act/output shows what the tool returned.

Variations

  • Several rounds. A graph has no cycles. Unroll a fixed number of rounds by repeating the LLM_CALL, BRANCH and MCP_TOOL_CALL group (a hard, readable upper bound), or move one round into its own workflow and call it with SUB_WORKFLOW (nesting is capped at 5): Run a sub-workflow. LOOP_PREDICATE repeats one processor, not a whole round.
  • Server-wide tool limits. yukta.mcp-client.allowed-tools, allowed-hosts, max-argument-bytes (65536) and deny-argument-patterns apply to every workflow: Configuration.
  • Cap the spend and the calls. Share one budget across the model and tool nodes: Set token budgets.
  • Let the model ask a person. allowAskInput: true on both nodes: Ask for input.
A model can be talked into asking for a tool you did not expect. Keep allowedTools narrow, put requireApproval on tools that change things, and never let a model-chosen name or argument reach something dangerous without a check.