Request a callbackBook a call
← All posts

Build an AI Agent That Does Real Work: Orchestrator, Specialist Agents and One MCP Harness

TL;DR
  • An ops agent that reads a refund ticket, checks the order in Shopify, refunds up to $50 and drafts the reply costs about 3.4 cents a ticket in models and durable execution at September 2026 prices, against about $2.20 of a person's time. Refunds above $50 go to a lead and cost about 58 cents, almost all of it their 90 seconds.
  • One orchestrator owns the plan and the state, every specialist agent is stateless, and every tool call passes through one MCP harness that enforces scopes, the $50 limit, approvals, idempotency keys, timeouts, logs and a $0.25 per-task cost cap. A limit written in a prompt is a suggestion; a limit checked on the arguments is a control.
  • Choose the orchestrator runtime by how long a run waits: your own state machine for seconds, LangGraph or Google ADK 2.x for minutes, Temporal underneath for hours or days. Below roughly 410 refund tickets a month, a $10,000 build does not pay back within a year on these assumptions, so do not build it.
The refund ops agent, layer by layer
1 · Trigger and run record

A helpdesk webhook creates one run row with a stable id, the ticket, the customer and the store. Nothing happens that is not attached to that id.

$0 · fails: duplicate webhooks
2 · Orchestrator

Owns the plan and the state as a typed state machine: triaged, order checked, refund proposed, gated, refunded, reply drafted, done. For a known intent, no model decides what runs next.

code, not a model
3 · Specialist agents

Triage and a reply checker on Claude Haiku 4.5, an order agent and a reply agent on Claude Sonnet 5. Each sees only the context and the one or two tools its step needs, and returns structured output.

$0.032 per ticket
4 · MCP harness

The only path to a tool: pinned registry, per-step allowlists, credentials it holds, argument limits, approvals, idempotency keys, timeouts, retries and cost caps.

fails closed
5 · Systems of record

The Shopify Admin API for orders and refundCreate, the helpdesk API for the reply draft. Both wrapped as MCP tools on a server you own.

store-scoped tokens
6 · Journal, evals and cost

One row per model call and tool call with an arguments hash, a bounded result, latency and cost. The same rows feed replay tests, dashboards and the audit trail.

one query per incident
The model layer is the cheapest and the least trusted. Everything that decides whether money moves lives in ordinary code: the orchestrator's state machine and the harness policy.

What does an AI ops agent that issues refunds actually do?

It reads a customer's refund request, finds the order in Shopify, checks your refund policy, refunds up to $50 on its own, asks a person to approve anything larger, and drafts a reply with the amount and timeline. On the arithmetic in this post it costs about 3.4 cents a ticket, against roughly $2.20 of a person's time.

The buyer is the support or operations lead at an online store who handles a daily stream of 'it arrived broken' and 'where is my refund' tickets. The product is not a chatbot. It is a colleague with a narrow job description: one intent, four tools, one spending limit. A ticket saying the mug in order 1042 arrived cracked becomes an order lookup in Shopify, a policy check (inside 30 days, item damaged, no earlier refund), a refundCreate call for that line item, and a reply waiting in the helpdesk before anyone on the team has opened the ticket.

What it is worth is arithmetic. A person handling the same ticket (open it, find the order, check the policy, refund, write the reply) takes about six minutes, which is $2.20 at $22 an hour fully loaded. The agent closes the under-$50 cases for about 3.4 cents and prepares the rest so a lead approves in about 90 seconds, roughly 58 cents. With one ticket in four needing approval, the blended cost is about 17 cents, so 4,000 refund tickets a month come to roughly $685 against $8,800 handled by hand. These are modelled figures with every input printed, not measurements from a client system.

This is the architecture I start from at Axionry for any agent that writes to another system: one orchestrator that owns the plan and the state, specialist agents that own nothing, and one MCP harness that every tool call passes through. The rest of this post covers why each choice exists, what it costs and where the frameworks fit. The loop mechanics underneath (termination, retries, budgets) are in the agent loop in production.

The four numbers for the business case
$0.034
modelled cost of a refund ticket the agent closes alone, models plus durable execution
$0.58
modelled cost when a lead approves a refund over $50, mostly 90 seconds of their time
$2.20
a fully manual refund ticket at six minutes and $22 an hour
→ baseline
~410
refund tickets a month for a $10,000 build to pay back within a year on these assumptions
The fourth number is the one to argue about. If your store sees fewer refund tickets than that, the right product is a better helpdesk macro.

Why do simple AI agents break in production?

Because a prompt-chained agent keeps its plan, its permissions and its memory inside the model's context, where nothing enforces them. It loops when a tool result is ambiguous, repeats a side effect when the chain is retried, can call any tool its API key reaches, and leaves a transcript, not an audit trail. These are design gaps, not model flaws.

The published evidence points the same way from three directions. The τ-bench paper found that gpt-4o, a leading function-calling agent in 2024, succeeded on under half of its customer-service tasks, and in the retail domain got a task right on all eight of eight runs less than 25% of the time, which is why it introduced the pass^k metric (arXiv 2406.12045). The MAST study of 1,600+ traces across seven multi-agent frameworks catalogued 14 failure modes in three groups: system design, inter-agent misalignment and task verification (arXiv 2503.13657). And Gartner expects over 40% of agentic AI projects to be cancelled by the end of 2027, citing cost, unclear value and inadequate risk controls.

In the refund agent each gap has a price. A loop between order and policy lookups, 40 turns with the history growing about 500 tokens a turn, is roughly 650,000 input tokens or $1.15 on Sonnet 5 before anyone notices. A retried chain that re-sends refundCreate is a second refund and a reconciliation task. A system prompt that says never refund more than $50 is one injected sentence away from refunding $400, the failure OWASP calls excessive agency: too much functionality, permission or autonomy. And a transcript cannot tell you which run refunded order 1042, with whose approval, at what cost.

The fix is to move control out of the context window. The plan becomes data owned by code, permissions become a property of each tool call rather than a line in a prompt, and every side effect gets a key before it happens. The models stay exactly as capable as they were. They stop being the only thing between an angry email and your refunds ledger.

Same models, different place for control
Prompt-chained agent
Who owns the plan
The model, inside its context
Tool access
Every tool the API key can reach
$50 refund limit
A sentence in the system prompt
Retry after a timeout
Re-run the chain and refund again
Stuck run
About $1.15 for a 40-turn loop, then a timeout
Audit
A chat transcript
Orchestrator plus MCP harness
Who owns the plan
A typed state machine in code
Tool access
One or two tools per step, credentials held by the harness
$50 refund limit
Checked on the arguments before dispatch
Retry after a timeout
Resume at the failed step with the same idempotency key
Stuck run
Six-turn cap, $0.25 cost cap, then a person
Audit
One journal row per call, queryable
Nothing on the right needs a better model
On my estimate the right-hand column is about two engineer-weeks of ordinary backend work, which is why teams that skip it are usually tuning the wrong layer.

What is an agent orchestrator, and how does it work with an MCP harness?

An orchestrator is the code that owns a task's plan and state: which step runs next, what each step received and returned, and when to stop. Specialist agents each do one step. The MCP harness sits between every agent and every tool and decides whether a call may run, with which credential, and at what cost.

For a known intent the plan is a template, not a model output: triage, check the order, propose a refund, gate, refund, draft the reply, check the reply. The orchestrator persists each transition, so a crash resumes at the right step rather than at the top. A planner model appears only for intents no template covers, and its plan is validated against the same state machine before any tool runs. This is Anthropic's distinction between workflows and agents applied on purpose: predefined code paths where the path is known, model-directed steps only where it is not.

Specialists are stateless and narrow. The triage agent on Claude Haiku 4.5 extracts intent, order number and risk flags into a schema. The order agent on Claude Sonnet 5 sees the ticket, the order and the policy, may call get_order and get_refund_history, and returns a structured proposal with line items and an amount. The reply agent writes from that proposal and the refund receipt, and a Haiku checker confirms the amount and order number in the reply match the receipt. None of them can call refund_create. Only the orchestrator's refund step can, and only through the harness.

The harness is one MCP client and gateway for every tool call. It wraps the Shopify Admin API and the helpdesk API as MCP tools on a server you own, holds the credentials so no agent ever sees a token, knows which step is calling so an allowlist can say the reply agent may post a draft and nothing else, and writes the journal so every refund traces to a run, a step, an approval and a cost in one query. The gateway mechanics (routing, catalogue caching, schema pinning) are in MCP in production.

Orchestrator and MCP harness
Architecture diagram of an AI ops agent for refund tickets. A Support ticket box sends a new ticket to the Orchestrator, which owns plan and state. The Orchestrator calls three specialist agents: Triage agent on Haiku 4.5, Order agent on Sonnet 5 and Reply agent on Sonnet 5. The Order agent sends get_order, the Reply agent sends post_draft and the Orchestrator sends refund_create, all into the MCP harness, which holds scopes, caps, approvals and logs. The MCP harness calls the Shopify Admin API and the Helpdesk API with scoped tokens and sends refunds over $50 to a Human approval box that returns approve or deny. The Orchestrator and the MCP harness both write to a Run journal.
Only the orchestrator's refund step can reach refund_create, and only through the harness, so an injected instruction can change what the order agent proposes but not what money moves.
One refund ticket through the orchestrator's states
  1. 1
    Triaged$0.0024

    Haiku 4.5 returns intent refund_request, order 1042, item damaged, no risk flags. Unknown intents leave the automated path here.

  2. 2
    Order checkedtool call 1

    The order agent calls get_order through the harness: fulfilled nine days ago, one mug at $18.00, no earlier refunds.

  3. 3
    Refund proposed$0.0193 for two turns

    A structured proposal with line item, quantity, amount and reason, validated against a schema and never parsed from prose.

  4. 4
    Gated$0

    Code, not a model: at or under $50, inside the 30-day window, fewer than two refunds in 90 days. Anything else becomes an approval request with the evidence attached.

  5. 5
    Refundedtool call 2

    refund_create through the harness, with the idempotency key in the journal before the request leaves. A timeout retries with the same key.

  6. 6
    Reply drafted and checked$0.0107, tool call 3

    Sonnet 5 drafts from the receipt; Haiku 4.5 and a string match confirm amount and order number before post_draft runs.

  7. 7
    Done$0.0336 total

    Run closed with total cost in micros, a journal row per step, and the approval id when there was one.

Seven states, and only one of them holds a model's judgement about money: the proposal. Whether it executes is decided by ordinary code with a unit test.

What should an MCP harness enforce on every tool call?

Seven things: a registry of allowed tools with pinned versions, per-step permissions with credentials the harness holds, argument limits checked in code, human approval for risky calls, timeouts and bounded retries, an idempotency key on every write, and a journal with cost caps. If any check cannot run, the call fails closed and the task goes to a person.

The registry pins every tool to a server version and a schema hash, which is not paranoia. In September 2025 an npm package impersonating Postmark's MCP server added one line in version 1.0.16 that BCC'd every email to an outside address. A pinned hash turns that kind of change into a failed call instead of a quiet leak. Permissions are per step rather than per agent, and the harness keeps its own risk labels, because the spec tells clients to treat tool annotations such as destructiveHint as untrusted unless they come from trusted servers.

Approvals, retries and idempotency are one mechanism seen three ways. The harness writes an idempotency key (run, step, order, amount) to the journal before dispatch and passes it to Shopify, whose @idempotent directive is mandatory on refundCreate from API version 2026-04. A timeout retries with the same key. An approval binds to a hash of the exact arguments and expires, so an approved $62 cannot be replayed as $620. And because approvals can wait hours, the harness keeps its own dedupe record: Stripe, for comparison, may prune idempotency keys after 24 hours, which is shorter than a weekend queue.

Logs and cost caps close the loop. Every call is written with its step, an arguments hash, a bounded result, latency and cost, which is what makes replay testing possible later. The per-task cap is $0.25, about seven times a clean run, and a six-turn ceiling per specialist stops the 40-turn loop at about 7 cents instead of $1.15. The MCP spec tells clients to confirm sensitive operations, time out tool calls and log them for audit; the harness is where those recommendations become code, and it is the first part of an agent I would put through a security review.

harness/policies/refund_create.yaml
tool: refund_create
server: shopify-admin              # our MCP server wrapping the Admin API
server_version: 1.4.2              # pinned
schema_hash: "sha256:3b1e...9c07"  # the call fails if the schema changes
callable_from: [orchestrator.refund_step]   # never a model-driven agent

arguments:
  order_id: { must_equal: run.order_id }        # the model cannot pick another order
  amount_usd: { max: order.refundable_total }   # never more than was paid

auto_execute:
  when: "amount_usd <= 50 and customer.refunds_90d < 2"

approval:                          # everything auto_execute does not cover
  approver_role: support_lead
  bind_to: arguments_hash          # an approved $62 cannot become $620
  expires_after_hours: 24

limits:
  max_calls_per_run: 1
  timeout_ms: 8000
  retry: { attempts: 2, on: [timeout, 502, 503], backoff_ms: [500, 2000] }

idempotency:
  key: "{run_id}:{step}:{order_id}:{amount_minor}"
  persist_before_dispatch: true
  forward_as: shopify_idempotent_directive

budget:
  run_cost_cap_usd: 0.25
  tenant_daily_cap_usd: 40

log:
  arguments: hashed
  result: bounded_800_tokens
  replayable: true
The whole policy for the one tool that moves money. Two lines do most of the work: callable_from, which means no model-driven agent can reach refund_create, and bind_to, which ties an approval to the exact arguments a lead saw. The values are this reference design's, not defaults.
Seven harness duties
What the harness checks before any tool runs
  • Registry with pinned server versions and schema hashesStops a changed or impersonated server from changing what your agent can do.
  • Per-step allowlists, with credentials held by the harnessStops the reply agent from refunding, and keeps tokens out of every prompt.
  • Argument checks in code: the order must match the ticket, the amount must be within limitsStops injected instructions from moving more money than policy allows.
  • Human approval bound to an arguments hash, with an expiryStops an approval for one amount being replayed for another.
  • Timeouts and bounded retries per toolRetries only on timeouts and 5xx responses, so a slow Shopify call cannot hold a run open.
  • Idempotency key persisted before every writeStops a retry or a resumed run from refunding twice.
  • Journal row per call, plus per-task and per-tenant cost capsStops runaway spend and answers who refunded what, when, with whose approval.
Seven duties and one rule: if a check cannot run, the call does not run.

What changed in the MCP spec in 2026, and what does it mean for your harness?

The current revision is 2026-07-28 and it made MCP stateless: no initialize handshake, no session header, and every request carries its own protocol version and capabilities. For a harness that means ordinary load balancing, routing on Mcp-Method and Mcp-Name headers, cacheable tool lists, and mid-call questions handled by retrying the request instead of holding a stream open.

Two transports are standard: stdio, for a server the client launches as a subprocess, and Streamable HTTP, where each message is a POST to one endpoint (transports). The older HTTP+SSE transport is formally deprecated. Authorization is optional and defined for HTTP: a protected server acts as an OAuth 2.1 resource server and must publish Protected Resource Metadata (RFC 9728), clients must send a resource indicator (RFC 8707) so a token is bound to one server, Client ID Metadata Documents are preferred over the now deprecated Dynamic Client Registration, and clients must check a returned iss parameter (RFC 9207) before redeeming a code (authorization). Servers must not accept or pass through tokens issued for anyone else, the rule that stops a harness becoming a confused deputy.

Three more changes shape the harness directly. Multi Round-Trip Requests replace server-initiated elicitation and sampling: the server returns input_required with its questions and the client retries with answers. Long-running work moved to the io.modelcontextprotocol/tasks extension with poll-based tasks/get. And a formal deprecation policy now sets a twelve-month minimum window, with a narrow expedited exception, and Roots, Sampling and Logging are already deprecated (changelog). I still keep business approvals in the orchestrator rather than in MRTR, because the approver is a support lead in your tool, not the user of the MCP client.

The ecosystem has settled around it. MCP moved to the Linux Foundation's Agentic AI Foundation in December 2025, and the maintainers report close to half a billion Tier 1 SDK downloads a month across TypeScript, Python, Go and C#. A2A reached a 1.0 specification in April 2026 for agents from different organisations to talk to each other. The split is clean: MCP for an agent reaching tools and data, A2A across organisational boundaries. Inside one product the orchestrator is a function call, not a protocol.

MCP 2026-07-28, read as a harness checklist
 What changedWhat the harness does about itBreaking?
Sessions and the initialize handshakeRemoved; each request carries version and capabilities in _metaNo sticky routing: any harness instance serves any callYes
Mcp-Method and Mcp-Name headersRequired on Streamable HTTP POSTsAllowlists and rate limits run on headers before the body is parsedYes
ttlMs and cacheScope on list resultsTool lists are cacheable and should come back in deterministic orderCache the catalogue per server; stable order keeps prompt caches warmNo
Multi Round-Trip Requestsinput_required replaces server-initiated elicitation and samplingHandle the retry; keep business approvals in the orchestratorYes, for those flows
Tasks extensionio.modelcontextprotocol/tasks with tasks/get pollingLong tool calls become a durable wait, not a held connectionMoved out of core
AuthorizationRFC 9728 metadata, RFC 8707 resource indicators, CIMD over DCR, RFC 9207 iss checksTokens bound to one server; no user token is forwarded downstreamYes, for clients
Deprecation policyTwelve-month minimum; Roots, Sampling, Logging and HTTP+SSE deprecatedMigrations go on a calendar instead of into an incidentNo
Most of the list makes a harness cheaper to run. The change that breaks code is the loss of sessions: anything that kept per-connection state now passes an explicit handle as a tool argument.

How much does an AI agent cost per task?

For this refund agent, about 3.2 cents in models and a tenth of a cent in durable execution when it closes the ticket alone, and about 58 cents when a lead approves, nearly all of it their time. The number that moves the business case is the approval rate, not the model price.

Line by line: triage on Haiku 4.5 is 1,800 tokens in and 120 out, $0.0024. The order agent takes two Sonnet 5 turns of 6,000 and 7,200 input tokens with a 3,500-token cached prefix (system prompt, two tool schemas and the 354-token tool-use prompt Anthropic adds on Sonnet 5), for $0.0082 and $0.0111. The reply agent is $0.0078 and the Haiku checker $0.0029, so models total $0.0324. Temporal Cloud at $50 per million actions adds about $0.0013 for 25 actions. Rates are from Anthropic's pricing page, checked 23 September 2026.

The approval path is the expensive one: 90 seconds of a lead at $22 an hour is $0.55. At one approval in four the blended cost is about 17 cents a ticket, and 4,000 refund tickets a month come to roughly $685 against $8,800 handled by hand. The payback line for a $10,000 build sits near 410 refund tickets a month, excluding maintenance. Below it, a helpdesk macro and a person are the better product, and I would tell anyone asking me to build this the same.

Two things move the model line more than model choice does. A runaway loop costs about $1.15 per stuck run, so the six-turn cap is a cost control as much as a safety one. And prompt caching only pays when the cached prefix is stable: tool lists in deterministic order, which the 2026-07-28 spec now asks servers to return, and a system prompt that never embeds the ticket. The routing and caching machinery is in LLM routing and caching, and you can price your own version in the AI product cost estimator.

Line itemModel or rateTokens or unitsAuto pathApproval path
TriageHaiku 4.5 at $1 in, $5 out per 1M1,800 in / 120 out$0.0024$0.0024
Order agent, turn 1Sonnet 5 at $2 in, $10 out, $0.20 cached6,000 in (3,500 cached) / 250 out$0.0082$0.0082
Order agent, turn 2Sonnet 57,200 in (3,500 cached) / 300 out$0.0111$0.0111
Policy gateCode0 tokens$0$0
Reply agentSonnet 53,500 in (1,500 cached) / 350 out$0.0078$0.0078
Reply checkHaiku 4.5 plus a string match2,500 in / 80 out$0.0029$0.0029
Durable executionTemporal Cloud at $50 per 1M actionsabout 25 actions$0.0013$0.0013
Lead approval$22 an hour, modelled90 seconds$0$0.55
Total per ticketBlended at 25% approvals: $0.1715 model calls, 3 tool calls$0.034$0.584
The auto path
One refund ticket, end to end, with tokens and cost on every model callHelpdeskOrchestratorTriageOrder agentMCP harnessShopifyReply agentJournal
ticket.created webhook
INSERT run (received)
before any model call
classify: 1,800 in / 120 out
$0.0024, Haiku 4.5
refund_request, order 1042, damaged
propose refund: ticket, policy, order id
tools/call get_order
allowlisted for this step
orders query with a store-scoped token
fulfilled 9 days ago, 1 item, $18.00
result bounded to 900 tokens
proposal: 1 item, $18.00, damaged
2 turns, $0.0193, Sonnet 5
UPDATE run (proposed), write idempotency key
before the call leaves
tools/call refund_create
gate passed: under $50
refundCreate @idempotent(key)
refund created, $18.00
draft reply from the refund receipt
$0.0078 plus $0.0029 check
tools/call post_draft
UPDATE run (done, 33,600 micros)
Seventeen messages, five model calls, three tool calls. The one call that moves money is issued by the orchestrator rather than a model, and its key is written before the request leaves.
Where 3.4 cents goes
$ per refund ticket closed without approvallower is better
Order agent, two turns57%. Two Sonnet 5 turns with growing history$0.0193
Reply agent23%$0.0078
Reply check9%$0.0029
Triage7%$0.0024
Durable execution4%. About 25 Temporal actions$0.0013
Total$0.0336
The machine bill is small enough to be the wrong thing to optimise first. A 25% approval rate adds $0.1375 a ticket in human time, about four times the entire machine line.

Which agent framework should you use: LangGraph, OpenAI Agents SDK, Claude Agent SDK, Google ADK or Temporal?

Choose by how long a run waits and what the agent touches. LangGraph and Google ADK 2.x suit plans you want as an explicit graph, the OpenAI Agents SDK suits handoff-shaped flows, the Claude Agent SDK suits agents that need files and a shell, and Temporal sits underneath any of them when waits last hours or days.

LangGraph reached 1.0 in October 2025 and is at 1.2.12 on PyPI this week. Its checkpointer and interrupt() give you approvals that pause a graph, with one sharp edge its docs state plainly: on resume, the node re-runs from its beginning, so any side effect before the interrupt must be idempotent. Google's ADK 2.0 reached general availability for Python on 19 May 2026, Go on 30 June and TypeScript on 21 August, adding graph workflows, human-in-the-loop and collaborative agents, with breaking event-schema changes if you stored 1.x sessions yourself.

The OpenAI Agents SDK is still pre-1.0 (0.22.3 in Python) and a short route to handoffs, guardrails, tracing and MCP in a few files; tools marked needs_approval pause the run into a RunState you can serialise, store and resume after a person decides (human-in-the-loop docs). Non-OpenAI models work through OpenAI-compatible endpoints or LiteLLM and any-llm adapters that the SDK labels best-effort. The Claude Agent SDK is the Claude Code harness as a library: file, shell and web tools, subagents, hooks, MCP, and a permission check that runs hooks, deny rules, ask rules, the permission mode, allow rules and then your canUseTool callback (permissions). It is the right runtime for a specialist that needs a computer, and more machinery than a refund step needs.

Temporal is not an agent framework, and that is its value. It makes the run itself durable: activities retry with their own timeouts, a signal can wait days for an approval, and a crashed worker resumes where it stopped. Its OpenAI Agents SDK integration now ships as a standalone 1.0 package, and its LangGraph plugin entered public preview on 16 July 2026 with a blunt summary of the gap: checkpoints preserve data, not execution. At $50 per million actions, durability for this refund run costs about a tenth of a cent.

OptionVersion on 23 Sep 2026What it gives youWhat you still buildUse it whenSkip it when
LangGraph1.2.12 Python; 1.0 GA October 2025A graph of nodes and edges, checkpointers, interrupt() and resume, streamingHarness policies, idempotent side effects, cost caps, your own journalThe plan is a graph you want in code and version controlRuns must survive a dead process for days without extra infrastructure
OpenAI Agents SDK0.22.3 Python; 0.18.0 JS; pre-1.0Agents, handoffs, guardrails, sessions, tracing, MCP, needs_approval with a resumable RunStateDurable waits, harness policies, multi-provider routingMostly OpenAI models and a handoff-shaped flowYou need a stable 1.0 API or first-class non-OpenAI models
Claude Agent SDK0.2.158 Python; 0.3.280 TypeScriptClaude Code's loop with file, shell and web tools, subagents, hooks, permission modes, MCP, sessionsThe orchestrator around it, business state, the approval UIA specialist needs files, code or a shellEach step is a short stateless call; it runs a CLI process per session
Google ADK2.9.2 Python; 2.0 GA May 2026Graph workflows, Sequential, Parallel and Loop agents, human-in-the-loop, MCP tools, A2A, evals with user simulationHarness policies, idempotency, cost capsYou deploy on Google Cloud or need A2AYou cannot absorb the 2.0 event-schema changes yet
TemporalPython SDK 1.33.0; Cloud at $50 per 1M actionsDurable execution, per-activity retries and timeouts, signals that wait days, replayEverything agent-specific; it wraps the othersMoney moves or approvals wait hoursTasks finish in seconds and a queue plus Postgres is enough
Your ownA Postgres journal, a queue and a typed state machineExact control of state, idempotency and auditAll of it, including timersThe plan is mostly fixed and there are 3 to 10 toolsYou need parallel sub-agents or durable timers
Build versus framework
What should run your orchestrator?
Fixed sequence, a few model calls, finishes in seconds
Your own state machine and a Postgres journal

The refund agent without approvals fits here, with the least code you do not own.

Branches, loops or pauses of minutes for review
LangGraph or Google ADK 2.x

An explicit graph with interrupts. Make every side effect before an interrupt idempotent.

Approvals that wait hours or days, or money moves
Temporal underneath, agent steps as activities

The run survives crashes and deploys. Works with the OpenAI Agents SDK and, in preview, LangGraph.

A step needs files, a shell or code execution
Claude Agent SDK as that step's runtime

Keep it a specialist inside the plan, not the orchestrator.

Nobody on the team will own the harness
Buy a vertical product instead

A framework does not remove the harness work. It only moves where you write it.

Every branch ends at the same harness. Frameworks change where the plan and state live; none of them decides your refund limit, your idempotency key or your approval rules.

How do you test an AI agent before it touches real money?

Test reliability, not one lucky run: replay each labelled ticket several times and require every attempt to pass, assert on the sequence of tool calls as well as the reply, attack it with injected instructions, and run it in shadow mode on live tickets before it may refund anything. After launch, watch rates rather than individual tickets.

Offline, the golden set is 200 real refund tickets with the correct outcome labelled by the support team: refund or not, amount, approval or not. Score pass^k, where all k runs must be right, because single-run accuracy hides inconsistency. Assert the trajectory, not just the text: get_order before any refund, never two refund_create calls in one run, an approval request whenever the amount is above $50. Because the harness logged every tool request and response, those logs replay as fixtures, so a new model or prompt is tested against Shopify's real answers without calling Shopify.

Adversarially, feed it tickets that carry instructions: a customer who writes that the system already approved a $400 refund, an order number that belongs to someone else, a message asking the agent to email a different address. The pass condition is not that the model resists. It is that the harness refuses, whatever the model proposed. Then run two weeks of shadow mode, where the agent proposes and a person decides, and switch on auto-execution only for ticket classes a lead accepts unchanged at least 98% of the time.

Online, four rates tell you most of what matters: approval override rate, refund reversal or chargeback rate on agent refunds, reopen rate within 72 hours, and cost per closed ticket. Alert when a rate moves, not when one ticket goes wrong. The answer-quality side of support automation (confidence gates, citations, escalation briefs) is covered in the AI customer support agent design.

Failure modeWhat you seeWhere it is stoppedWhat catches it
Loop between lookupsNothing for a minute, then a timeout, about $1.15 spentSix-turn cap per specialist and a repeated-call detector; the run moves to stalledTurns-per-run distribution, stalled-run rate
Duplicate refund on retryTwo refunds for one orderIdempotency key persisted before dispatch; Shopify @idempotentDuplicate-key attempts in the journal, finance reconciliation
Injected instruction raises the amountA $400 proposal on an $18 itemArgument check against the refundable total and the $50 auto limitAdversarial suite in CI, override rate
Wrong order refundedA refund on another customer's orderorder_id must equal the order bound to the runCross-customer assertions in tests, audit anomalies
Approval replayedAn approved $62 becomes a $620 callApproval bound to the arguments hash, 24-hour expiryApproval id and hash on every write row
Tool server changed underneath youNew behaviour with no deploy on your sidePinned version and schema hash in the registryHash-mismatch alerts
Refund succeeded, reply failedCustomer refunded but not toldOrchestrator resumes at the reply step and never re-runs the refundRuns stuck in the refunded state for over 10 minutes
Cost blow-out after a deploySpend doubles in an afternoonPer-task $0.25 cap, per-tenant daily cap, circuit breaker on tool errorsCost per closed ticket, hourly
Pre-launch tests
Six tests before the agent may refund anything
  • pass^k on 200 labelled tickets, with k of 4 or moreAll k runs must be right. A single-run pass rate hides the inconsistency τ-bench was built to expose.
  • Trajectory assertions on tool order and countsget_order before refund_create, one refund per run, an approval request above $50.
  • Replay from production logsRecorded tool responses become fixtures, so new models meet real Shopify answers without touching Shopify.
  • Injection suiteTickets that claim approvals, name other orders or redirect the reply. Pass means the harness refused.
  • Failure injection on toolsTimeouts, throttling and 5xx responses mid-run. Pass means no duplicate refund and a resumed run.
  • Two weeks of shadow modeThe agent proposes and a person decides. Auto-execution switches on per ticket class only above 98% accepted unchanged.
The first three are ordinary CI. The fourth and fifth decide whether an agent may move money, and the sixth is the one teams skip because it delays launch by two weeks.

Should you build your own agent orchestrator or use a framework?

Build the harness yourself in every case, because it encodes your policies. For the orchestrator, use your own state machine when the plan is short and fixed, and a framework when you need graphs, long waits or a computer. On my estimate the refund agent is 5 to 6 engineer-weeks, and most of that is harness, evals and rollout.

Frameworks are good at control flow and know nothing about your business rules. None of them knows that refunds over $50 need a lead, that an approval must bind to its arguments, or that your journal must answer which run refunded order 1042. That code is small (the policy file above is most of it), and it is yours whichever framework runs the plan. A team of agents rarely helps either: a refund is short, sequential and single-domain, the shape where one agent per step beats a multi-agent design.

Sequenced, the build runs journal and harness first, then tools and policies, then the orchestrator and specialists, then approvals and evals, then shadow mode. Axionry's published pricing puts an AI feature inside an existing product from $10,000 and a standalone AI-first product from $22,000. A working demo of the happy path starts at $1,000, which is the cheapest way to learn whether your approval rate is 10% or 60% before committing. If refunds are one of several agents on your list, the AI agents worth building in 2026 ranks the others.

If you want this built, Axionry builds it at $0: the work is split into checkpoints with acceptance criteria agreed before work starts, and each checkpoint is invoiced only after you have seen it and accepted it. The service is AI product development.

Five to six engineer-weeks, sequenced
  1. Week 1
    Journal and harness skeleton

    Run and step tables, cost in micros, and the MCP harness with registry, allowlists and logging. No agent yet.

  2. Week 2
    Tools and policies

    get_order, get_refund_history, refund_create and post_draft on an MCP server you own, with argument checks, idempotency, timeouts and the $50 rule.

  3. Week 3
    Orchestrator and specialists

    The state machine, triage, order agent, reply agent and checker, each with a schema for its output.

  4. Week 4
    Approvals and evals

    An approval queue bound to argument hashes; the golden set, pass^k, trajectory assertions and the injection suite.

  5. Weeks 5 to 6
    Shadow mode and rollout

    The agent proposes and people decide; auto-execution switches on per ticket class once acceptance clears 98%.

An estimate for one intent and four tools with one engineer, not a quote. The agent itself arrives in week three, which is the ordering that finishes on time.

Agent orchestrators and MCP harnesses: common questions

→What is an MCP harness?

An MCP harness is the single layer every tool call from an agent passes through. It keeps a registry of allowed tools with pinned versions, checks per-step permissions, holds the credentials, enforces argument limits such as a $50 refund cap, requests human approval for risky calls, writes idempotency keys, applies timeouts and cost caps, and logs every call so runs can be audited and replayed.

→How much does an AI agent that issues refunds cost to run?

On September 2026 list prices, about 3.4 cents per ticket when the agent closes it alone: five model calls on Claude Haiku 4.5 and Claude Sonnet 5 plus about 25 Temporal actions. When a lead approves a refund over $50, the modelled cost is about 58 cents, almost all of it 90 seconds of their time at $22 an hour.

→Do I need LangGraph or another framework to build an agent orchestrator?

No. A short, fixed plan runs well on your own state machine with a Postgres journal. Use LangGraph or Google ADK 2.x when you want an explicit graph with pauses, Temporal when approvals wait hours or days, and the Claude Agent SDK when a step needs files or a shell. You build the harness and its policies either way.

→What changed in MCP in 2026?

The 2026-07-28 revision made MCP stateless. The initialize handshake and session header are gone, requests carry their own version and capabilities, Streamable HTTP requires Mcp-Method and Mcp-Name headers, list results are cacheable, and Multi Round-Trip Requests replace server-initiated elicitation and sampling. Authorization now prefers Client ID Metadata Documents over Dynamic Client Registration and requires RFC 9207 issuer checks.

→How do you stop an AI agent from refunding the wrong amount?

Do not rely on the prompt. Check the arguments in code before the call leaves the harness: the order must be the one bound to the run, the amount cannot exceed what was paid, and anything above the auto limit needs a person whose approval is bound to the exact arguments. An idempotency key written before the call stops a retry from refunding twice.

→When should you not build an agent like this?

When volume is low or the policy is unclear. On the modelled numbers, a $10,000 build pays back within a year only above roughly 410 refund tickets a month. Below that, a helpdesk macro and a person are cheaper. And if your refund rules live in people's heads, write them down first, because the agent needs them as code.

Take this into your own chat

Open the article in your assistant with one click and ask it how this applies to your product.

Ready to talk numbers?

Twenty minutes, straight to the engineer. No sales rep, no deck.