ChanlChanl
Tools & MCP

How to Build an Agent That Writes Code Instead of Calling Tools

One JSON tool call per turn is slow, token-heavy, and brittle. A short script that orchestrates tools in a sandbox is faster and cheaper. Here's how it works.

DGDean GroverCo-founderFollow
August 1, 2026
12 min read
A slender android at a bare desk in a glass-walled studio at dusk writing one flowing branching script on a translucent pane, while behind the glass a wall of dozens of small labeled levers sits untouched (Ex Machina style, teal-copper palette)

The billing agent needed to do something simple. Pull the customer's last three invoices, find the ones that were overdue, sum the late fees, and tell the caller the total. Four steps. In the transcript, it took the agent nine model turns and about forty seconds.

Nine turns for four steps. Here's why. The agent called the invoice tool, got back three fat JSON objects, and those objects flooded its context. Then it called a date-comparison tool once per invoice to check overdue status, three more turns, each shuttling data back through the model. Then it added the fees in a fourth reasoning step, fumbled the arithmetic on the first pass, and corrected on the second. Every intermediate value made a full round trip through the model just to be handed back to the next tool.

A junior engineer would never solve this with nine API calls narrated one at a time. They'd write four lines of code. Fetch, filter, sum, return. So the team let the agent do that instead. One turn, one short script, about six seconds. The arithmetic bug vanished, because the arithmetic now runs in a Python interpreter instead of a language model.

That's code mode, and in 2026 it's quietly becoming the default way serious agents use tools.

What Is Code Mode for AI Agents?

Code mode lets an agent complete a task by writing a short program and executing it in a sandbox, rather than emitting one JSON tool call per turn. The agent gets a single execute_code tool. Inside that one call it can loop, branch, transform data, and chain several underlying tools together. Multi-step work that used to span many model turns collapses into one code-writing turn.

The idea has a lineage. The research line called CodeAct showed that letting a model act by writing executable code, instead of producing structured JSON actions, changed how well agents performed. The framing that spread through 2026, from smolagents to the Microsoft Agent Framework's CodeAct feature, is the same: give the model a place to write and run code, expose your tools inside that environment, and let the model orchestrate.

Contrast the two shapes directly. In the traditional model, a turn looks like this: the model emits a JSON object naming one tool and its arguments, the harness runs it, the result comes back, and the model decides what to do next. Every step is a separate round trip.

json-tool-call.json·json
{ "tool": "get_invoices", "args": { "customerId": "c_8812", "limit": 3 } }

In code mode, a turn looks like this instead. The model writes a script that does the whole job, and the script runs once.

code-mode-action.py·python
invoices = get_invoices(customer_id="c_8812", limit=3)
overdue = [i for i in invoices if i.due_date < today() and not i.paid]
total_late_fees = sum(i.late_fee for i in overdue)
print(f"You have {len(overdue)} overdue invoices totaling ${total_late_fees}")

Same tools underneath. But the loop, the filter, and the sum now happen in the sandbox, not across four model turns. The model wrote the recipe once and let a real interpreter cook.

Why Does Writing Code Beat Emitting JSON Tool Calls?

Writing code beats JSON tool calls for three reasons: models are far more fluent in real code than in chained JSON actions, control flow like loops and conditionals belongs in code, and orchestrating several tools in one script collapses many model turns into one. The result is higher task success and lower cost at the same time.

Take these one at a time, because each is doing real work.

Fluency. Language models trained on the open internet have seen a staggering amount of Python. They've seen comparatively little of your specific JSON tool schema. Ask a model to express "fetch these, filter to overdue, sum the fees" and it writes clean, idiomatic code because it has seen that exact pattern ten million times. Ask it to express the same logic as a sequence of discrete JSON tool calls with the intermediate state carried in its own reasoning, and it's improvising in a format it barely knows. The original CodeAct work put a number on this: acting in code lifted task success rates by up to roughly 20% on agent benchmarks versus JSON actions.

Control flow. Loops and conditionals are native to code and awkward in tool-call sequences. "For each overdue invoice, check if a payment plan exists, and if not, flag it" is one line of Python. As a JSON tool-call sequence it's a fragile dance: call a tool, reason about the result, call another, hope the model tracks which invoice it's on. Anything iterative or conditional gets dramatically easier when the control flow lives in an interpreter instead of in the model's working memory.

Turn collapse. This is the one that shows up on your bill. A five-step plan that used to be five model turns becomes one execute_code turn containing a short script. Five sets of prompt tokens, five latency round trips, and five chances to lose the thread become one. For agents that make ten to a hundred times more model calls than a chatbot, collapsing turns is the difference between an affordable agent and one that quietly bankrupts the feature. We walked through where those tokens hide in reasoning tokens: the shadow cost of agents.

There's a fourth benefit that's easy to miss: the intermediate data never touches the model's context. In JSON mode, those three fat invoice objects had to come back through the model so it could decide the next call. In code mode, the invoices live in a variable in the sandbox. The model sees only the final printed total. That single property is what unlocks the dramatic context savings, and it's the heart of MCP code mode.

What Is MCP Code Mode?

MCP code mode presents MCP tools to the agent as callable code instead of static JSON schemas loaded into context up front. The agent discovers and calls tools by writing code against them, and large intermediate results stay inside the sandbox. This avoids two expensive things at once: loading every tool definition into the window, and piping big payloads back through the model.

The problem MCP code mode solves is one every team with a growing tool set hits. In the standard setup, every tool's full JSON schema gets loaded into the model's context so it knows what's available. With ten tools that's manageable. With fifty it's a tax on every single turn, and we've written about how that plays out in tool explosion: managing 50 agent tools at scale. You're paying to describe tools the agent won't use on this turn.

Code mode flips it. Instead of front-loading every schema, the agent writes code against tools it discovers on demand, importing or calling only what the task needs. Cloudflare and Anthropic both published results on this pattern in the run-up to 2026, and the numbers are striking: token reductions reported as high as 98.7%, driven by on-demand tool loading and by processing large intermediate data inside the execution environment rather than shuttling it through context.

Two separate research threads back the same shift, one on task success and one on cost:

MetricReported gainSource
Task success rate on agent benchmarksup to ~20% higher than JSON tool callsCodeAct, ICML 2024
Context and token usageup to 98.7% lowerAnthropic and Cloudflare code-mode reports
Connected Integrations12 active
SalesforceSalesforce
SlackSlack
GoogleGoogle
StripeStripe
HubSpotHubSpot
IntercomIntercom
ZapierZapier
ShopifyShopify
GitHubGitHub
JiraJira
GmailGmail
PostgreSQLPostgreSQL

Think about what that means for a CX agent connected to a CRM, a billing system, a knowledge base, and a scheduling service, each exposing a dozen operations. Loading all of that into context on every turn is enormous overhead. Letting the agent write a script that calls exactly the two operations this turn needs, and keeps the bulky results in the sandbox, is a different cost structure entirely. This is why code mode and MCP are converging: MCP standardized how tools are described and reached, and code mode is the most efficient way to actually reach them. The broader protocol picture is in four protocols that power every AI agent.

If you're wiring this up, the MCP runtime is where the tool connections live, and the tools layer is where you decide which operations the agent can reach in code.

When Should You Not Use Code Mode?

Skip code mode for single-tool tasks, treat it carefully with untrusted input, and weigh it against auditability for high-stakes actions. Writing a script to make one tool call is pure overhead. Executing model-written code demands a real sandbox. And a single JSON tool call is easier to gate and review than a script, which matters when the action is irreversible.

Code mode is a sharp tool, not a universal upgrade. Here's where the tradeoffs bite.

Single-tool turns. If the task is "look up this order," a direct tool call is faster and simpler than spinning up a code execution round trip. Writing a script to make one call is the agent equivalent of opening an IDE to add two numbers. Code mode earns its keep when there's orchestration to do: multiple tools, a loop, a transformation. For a one-shot lookup, the sandbox is just latency you didn't need.

Untrusted input and security. This is the serious one. Executing code that a model wrote, based on input a stranger provided, is dangerous if you do it naively. The code must run in an isolated sandbox: no ambient production credentials, tight network limits, capped memory and CPU, and a hard timeout.

The frameworks shipping code mode pair it with a hardened execution environment for exactly this reason, and the Microsoft Agent Framework's CodeAct runs against an isolated sandbox rather than your process. The rule is absolute: never execute agent-written code with your real credentials in scope. The sandbox is the security boundary, and if you don't have one, you don't have code mode, you have a remote code execution vulnerability with extra steps.

Auditability and high-stakes actions. A JSON tool call is a discrete, inspectable event: this tool, these arguments, gate it or log it cleanly. A script that does five things is harder to review at the moment of approval and harder to reason about after the fact. For irreversible, high-consequence actions, refunds, cancellations, data deletion, you may still want those specific operations exposed as explicit gated tool calls even inside a code-mode agent, so a human or a policy can approve the exact action.

The pragmatic stance most teams land on is hybrid. Use code mode for the read-heavy, multi-step, orchestration-shaped work where it shines. Keep the handful of dangerous write actions as explicit, individually gated tool calls. That gives you the efficiency of code mode for the 90% of turns that are lookups and transformations, and the control of discrete tool calls for the 10% that can hurt.

Building and Testing a Code-Mode Agent

Building a code-mode agent means three things: expose your tools inside a sandbox the agent can write code against, keep dangerous write operations as separate gated calls, and test the generated code the way you'd test any other agent behavior. The code the model writes is agent output, and agent output needs evaluation before it reaches production.

The exposure step is where MCP and a sandbox meet. Your tools, whether MCP servers or internal functions, become callable inside the execution environment. The agent writes against them; the sandbox runs the code with no production credentials in scope. Expose the read-heavy operations as callable functions, and leave the dangerous writes out of the sandbox entirely so they stay explicit, individually gated calls.

The testing step is the one teams skip and regret. In JSON mode, you test which tools the agent chose. In code mode, you also have to test the code it wrote, because a subtly wrong script fails in ways a single tool call can't. An off-by-one in a loop, a filter with inverted logic, a sum over the wrong field: these produce confident, plausible, wrong answers. This is exactly the failure class that output-only evaluation misses, the argument we made in trajectory evals: catch agent bugs output scoring misses.

Run realistic scenarios against the agent and check both the outcome and the path. Did the generated code call the right tools, in the right order, with the right arguments? Chanl's scenario testing drives those runs, and the monitoring layer traces what the code actually did in production, so a script that starts summing the wrong field shows up as a divergence rather than a mystery in your callback queue.

test-code-mode-agent.ts·typescript
// Run a realistic billing scenario, then check the trajectory
// (which tools ran, in what order) instead of just the final number.
const { data: run } = await chanl.scenarios.run(scenarioId, { agentId });
 
const { data: execution } = await chanl.scenarios.getExecution(run.executionId!);
console.log(execution.overallScore, execution.stepResults?.[0]?.toolCalls);

The point isn't that code mode is dangerous and needs babysitting. It's that code mode moves more of the agent's logic into a place you can and should verify. That's a feature. The four lines of Python that replaced nine model turns are also four lines you can test deterministically, which is more than you could ever say for nine turns of model reasoning.

Four Lines Instead of Nine Turns

The billing agent runs in code mode now. The overdue-fees task that took nine turns and forty seconds takes one turn and six. The arithmetic is correct every time, because a Python interpreter does the arithmetic. The invoice payloads that used to flood the context stay in a sandbox variable, and the model sees only the total it needs to report.

Nothing about the underlying tools changed. The invoice API is the same, the date logic is the same, the tools are the same MCP servers they always were. What changed is that the agent stopped narrating one API call at a time and started doing what any engineer would do with the same problem: it wrote a short script and ran it.

That's the whole shift. For years we asked models to act like a person clicking through an API one call at a time, then wondered why multi-step tasks were slow and brittle. Give the model a keyboard and a sandbox instead, keep the dangerous buttons behind explicit gates, and test the code it writes. Your agent gets faster, cheaper, and more correct on the same tools it already had.

Ship a code-mode agent you can trust

Chanl connects your tools through an MCP runtime, runs realistic scenarios against the code your agent writes, and monitors what it actually does in production. Build, connect, and monitor agents that orchestrate tools in one turn instead of ten.

Start free
DG

Co-founder

Building the platform for AI agents at Chanl — tools, testing, and observability for customer experience.

Learn Agentic AI

Weekly. Patterns for shipping agents that work. MCP, scorecards, regression tests, prompts, model comparisons.

500+ builders subscribed

Frequently Asked Questions