Skip to content
Use Agent Tools in Prompts

How to Use Agent Tools in Prompts

Where an agent kind can reach them, Sortie registers its tools via MCP and advertises them in the first-turn prompt: the agent already knows each tool’s name, input schema, and response format. This guide shows you how to add prompt instructions that make agents use those tools at the right moments: checking their turn budget, watching the token budget, reviewing prior run history, querying the tracker, escalating to a human when a decision needs one, and signaling when they’re stuck.

Prerequisites

Pick a kind that has the tools

Not every session can call Sortie’s tools. For three agent kinds it depends on where the session runs, and for Kiro CLI in ACP mode on how it signs in. Decide this before you write a line of tool guidance into a prompt.

agent.kindLocal dispatchDispatch over SSH
claude-codeTools availableTools available
copilot-cliTools availableTools available
codexTools availableNo tools
opencodeTools availableNo tools
agent-client-protocolTools available, subject to the runtime’s own workspace-trust and approval configurationNo tools

Kiro CLI runs on agent-client-protocol, and there Sortie’s tools arrive only under a stored device login; an API key starts sessions but delivers none. See Kiro CLI on the Agent Client Protocol for how to confirm which credential a run uses.

A session with no tools is not told about them either: Sortie withholds the first-turn advertisement rather than name a tool the agent cannot call. Nothing fails. The agent simply works without them.

Two consequences for the way you write prompts:

  • If you run agents over SSH and want tools, keep those workflows on claude-code or copilot-cli. Moving a codex, opencode, or agent-client-protocol workflow onto a host pool silently removes the tools from every session it dispatches.
  • Instructions you write yourself are not withheld. If a workflow can dispatch to a kind with no channel, phrase them conditionally (“If the cost_budget tool is available”) the way the notify_operator examples below do, so an agent without the tool is not left chasing one.

Run sortie validate to see where a workflow stands. A kind with no channel anywhere draws an agent.kind.no_tool_channel warning; the file stays valid and the exit code stays 0.

Guide the agent to check its own status

Add a block near the top of your prompt template that tells the agent to check sortie_status before diving into work:

Before starting, call the sortie_status tool to check your turn budget.
If turns_remaining is 3 or fewer, focus on completing the most important
change and skip nice-to-haves.

Without this, agents treat every turn as if the budget is unlimited. They start low-priority refactors on their second-to-last turn, then get cut off mid-change. A single status check at the start lets the agent prioritize.

Guide the agent to watch the token budget

When agent.max_tokens is set, the orchestrator cancels the session in progress and stops dispatching new ones once the issue’s cumulative token spend reaches the budget. An agent that runs into it is cut off mid-turn rather than allowed to wrap up. The cost_budget tool lets the agent see that ceiling coming:

Before starting expensive work, call the cost_budget tool.
If remaining_tokens is null, there is no token budget; proceed normally.
If remaining_tokens is less than 100000, finish the most important
change, commit what works, and summarize what remains instead of
starting anything new.

Tune the threshold to your budget; 100,000 tokens is a sensible reserve when max_tokens is in the low millions. Unlike sortie_status, which covers the current session only, cost_budget reports spend across all of the issue’s sessions, including the one in flight. The in-flight part of that figure refreshes at most every two seconds, so it trails what the orchestrator enforces rather than leading it: an agent acting on the reading acts early, never late. A used_tokens_complete of false means used_tokens is a lower bound and remaining_tokens is optimistic; this happens for sessions whose agent reported no usage at all, for a turn that reached the model without a figure covering it, and for the running session itself before its own first token report lands, so seeing false early in a session is expected, not a sign of missing history. See the cost_budget tool for the exact conditions.

The null case earns its line in the prompt. remaining_tokens: null means the budget is unlimited, not exhausted; an instruction that says “stop when remaining_tokens is low” without it makes the agent wind down on issues that have no token budget at all.

Setting agent.token_warning_percent does the same job without a hand-picked token threshold: cost_budget then also returns warning_tokens and warning_reached, so the prompt can act on a threshold Sortie computes from the ceiling instead of one you guess at.

Call the cost_budget tool. If warning_reached is true, finish the most
important change, commit what works, and summarize what remains instead
of starting anything new.

warning_reached and a hand-picked remaining_tokens threshold answer the same question two ways; use whichever fits how you think about the budget, or both. See how to control agent costs for choosing the percentage.

Guide the agent to review prior history

On continuation and retry runs, the agent has no memory of what happened before. Tell it to check:

{{if .run.is_continuation}}
Call the workspace_history tool to review prior run outcomes. If the last
run failed, read the error message before retrying the same approach.
Do not repeat a failed strategy without a different plan.
{{end}}

The {{if .run.is_continuation}} guard keeps this out of first runs, where there is no history to review. Without it, the agent wastes a tool call that returns empty results.

On retry runs (.attempt >= 1), you can add a stronger instruction:

{{if and .attempt (not .run.is_continuation)}}
This is retry attempt {{ .attempt }}. Call workspace_history to understand
what went wrong. The previous approach failed. Changing your strategy is
mandatory, not optional.
{{end}}

Agents on retry runs that skip history tend to repeat the exact same failing approach. Forcing a history check before any code changes breaks that loop.

Guide the agent to use tracker_api

The tracker_api tool gives the agent read and write access to your issue tracker. Three scenarios come up most often.

Check related issues before starting

Call the tracker_api tool with the search_issues operation to find
other active issues. Note any that are related to your task. Avoid
duplicating work or introducing conflicts with in-progress changes.

This is useful in projects with many concurrent issues. The agent sees what else is in flight and can avoid, for example, refactoring a module that another issue is actively rewriting.

Read comments for human feedback

Call the tracker_api tool with fetch_comments to check for human
feedback or clarifications added since the last run.

Pair this with the continuation guard when feedback arrives between runs:

{{if .run.is_continuation}}
Call the tracker_api tool with fetch_comments to check for new
reviewer feedback. If a human left comments, address them before
continuing with the original plan.
{{end}}

Transition the issue when done

When your changes are committed and pushed, call the tracker_api
tool with the transition_issue operation to move the issue to
"In Review". Do not transition until the CI checks pass.

This closes the loop. The agent moves the issue forward without human intervention. The target state must match a valid state in your tracker’s workflow. For the full list of tracker_api operations and their input schemas, see the agent extensions reference.

Guide the agent to escalate and report progress

When a notifications entry in WORKFLOW.md lists agent.message in its events, agents can call notify_operator to reach a human on a real-time channel without ending the session:

If the notify_operator tool is available: when you hit a decision you
should not make alone (architecture changes, destructive migrations,
ambiguous requirements), call it with severity "warning" and category
"decision_needed" before proceeding. On long tasks, send a short update
with severity "info" and category "progress" at meaningful milestones.
Do not notify on every turn.

The conditional phrasing matters: the tool is registered only when some entry receives agent.message, so an unconditional instruction confuses agents in setups without one. The cap matters too: notifications are capped per agent run (default 20), shared across every turn of that run, and calls past the cap return rate_limited errors, so instruct meaningful moments rather than a running commentary.

A notification does not stop the session or the retry loop. An agent that is genuinely blocked must still write .sortie/status. The right order is notify first, then write the file, so the human hears about the blocker and the orchestrator stops retrying.

Guide the agent to signal blocked status

When an agent can’t complete a task (missing credentials, ambiguous requirements, a dependency on another issue), it should tell the orchestrator to stop retrying. The .sortie/status file is the mechanism:

If you determine you cannot complete this task because of missing
credentials, ambiguous requirements, or a dependency on another
issue, signal the orchestrator:

    mkdir -p .sortie && echo "blocked" > .sortie/status

If the work is complete but needs human review before merging:

    mkdir -p .sortie && echo "needs-human-review" > .sortie/status

If the requested outcome already held and you changed nothing to
reach it:

    mkdir -p .sortie && echo "no-change-needed" > .sortie/status

DO NOT write this file during normal productive work.

Sortie auto-injects similar instructions on the first turn, so including your own version is harmless. Custom instructions are useful when you want to be more specific (for example, listing the exact conditions that count as “blocked” in your project).

The orchestrator reads .sortie/status after each turn. Unrecognized values are silently ignored, so only blocked, needs-human-review, and no-change-needed have any effect. For background on why this is a file rather than a tool call, see agent communication model.

Ask for a reason

The first line of the file is the value. Lines after it are a stop statement that Sortie can publish with the stop, so the person who finds the parked issue learns what the agent needs. Sortie’s own first-turn text already asks for one. Add project-specific wording when you want it to be more useful:

When you signal blocked, write the reason after the value: the one thing
you need from a person, as a question they can answer in a sentence. When
you signal needs-human-review, say what the reviewer should check first.
Write for the people who read the issue. Name files by their path in the
repository. Never put credentials or tokens in the file, and keep the
whole file under 1024 bytes.

    mkdir -p .sortie && printf '%s\n' "blocked" "Which invoice delete should the API expose: soft or hard?" > .sortie/status

The reason reaches a destination only when that destination lists session.stopped in its events. To put it on the issue, add a tracker_comment entry; see how to route notifications. Sortie masks the secrets it knows and shows the text as literal text, but it can only mask values it knows about.

Combine tools in a complete workflow

Here is a full WORKFLOW.md prompt body that ties all four patterns together:

---
tracker:
  kind: jira
  project: PROJ
  active_states: [To Do, In Progress]
  terminal_states: [Done]
agent:
  kind: claude-code
  command: claude
  max_turns: 10
notifications:
  - kind: slack
    webhook_url: $SORTIE_SLACK_WEBHOOK_URL
    events: [agent.message]
---

You are a senior engineer. Your work is tracked by Sortie.

## Task

**{{ .issue.identifier }}**: {{ .issue.title }}
{{ if .issue.description }}

### Description

{{ .issue.description }}
{{ end }}

## Budget

Call the sortie_status tool to check your turn budget. If turns_remaining
is 3 or fewer, focus on the most critical change and skip cleanup tasks.

Call the cost_budget tool to check your token budget. If remaining_tokens
is null, there is no token budget. If it is below 100000, wrap up: commit
what works and summarize what remains.

{{ if not .run.is_continuation }}
## First run

Check for related issues: call tracker_api with search_issues. Note any
that overlap with your task.

Read the specification and existing code before writing anything.
Write tests first, then implement.
{{ end }}
{{ if .run.is_continuation }}
## Continuation (turn {{ .run.turn_number }}/{{ .run.max_turns }})

Call workspace_history to review what happened in prior turns.
Call tracker_api with fetch_comments to check for new reviewer feedback.

If the previous turn failed, do not repeat the same approach. Diagnose
the root cause before making changes.
{{ end }}
{{ if and .attempt (not .run.is_continuation) }}
## Retry (attempt {{ .attempt }})

Call workspace_history to understand what the previous attempt did wrong.
A different strategy is required. Do not retry the same approach.
{{ end }}

## When You Finish

1. Run `make lint && make test`. All checks must pass.
2. Commit and push your changes.
3. Call tracker_api with transition_issue to move {{ .issue.identifier }}
   to "In Review".

## If You Get Stuck

If you cannot complete this task because of missing credentials,
ambiguous requirements, or a dependency on another issue:

1. Call notify_operator with severity "critical" and category "blocked",
   describing what you need.
2. Write the status file, with your reason on the second line:

    mkdir -p .sortie && printf '%s\n' "blocked" "<what you need>" > .sortie/status

Do not write this file during normal productive work.

The flow: the agent checks its budget, gathers context (related issues on first run, history and comments on continuations), does the work, transitions the issue, and signals if stuck. Each tool call happens at the moment its output is most useful.

Common mistakes

Calling sortie_status on every turn. Once at the start is enough. Calling it every turn wastes tokens on redundant information: the budget changes by one each turn, and the agent can track that from the first response.

Treating remaining_tokens: null as zero. null means no token budget is configured; 0 means the budget is spent. A prompt that tells the agent to stop when remaining_tokens is low, without naming the null case, makes it wind down on issues with unlimited budget. Spell out both cases in the prompt.

Including tool schemas or JSON call syntax in the prompt. Sortie already advertises tools via MCP and the first-turn prompt injection. Repeating the schema wastes context window, and writing {"operation": "search_issues"} in the prompt is not how agents invoke MCP tools. Use natural language: “Call tracker_api with the search_issues operation.”

Forgetting {{if .run.is_continuation}} guards. A workspace_history call on the first run returns nothing. There is no prior history. Wrap history-related instructions in a continuation or retry guard so the agent skips them when they’re useless.

Treating notify_operator as a stop signal. It notifies a human and changes nothing in orchestration: retries continue, the tracker state stays put, the claim stays held. Only .sortie/status stops the retry loop. Pair them: notify, then write the file.

Notifying on every turn. Notifications are capped per agent run (default 20), shared across every turn; past the cap, calls return rate_limited errors. Reserve notify_operator for decisions, blockers, and meaningful milestones, not a running commentary.

Writing .sortie/status with unrecognized values. Only blocked, needs-human-review, and no-change-needed are recognized. Values like done, error, or waiting are silently ignored. The agent writes the file thinking it communicated something, but the orchestrator sees nothing.

Related guides

Was this page helpful?