← Blog
Don't Make the Model Your Loop: Scripts vs MCP in Agent Skills

Don't Make the Model Your Loop: Scripts vs MCP in Agent Skills

AIClaude CodeMCPAgent Skills

Background

At work I package configuration builds as Claude Code skills. A build is a fixed set of platform objects, such as fields, dashboards, automation flows and scheduled rules, and the skill installs that set into a customer tenancy. Two of these builds were large: a reporting build of about 460 entities, and a credit-risk module of about 415 entities that includes 260 KB of flow definitions.

The first version of both used the obvious design. We already had an MCP server wrapping the platform API, so each skill was a long prompt plus the pack's JSON files. The model read what the tenancy already had, detected drift against the pack, worked out which existing fields and lookups matched, then wrote every object through MCP tools, one call at a time. It worked. It also took most of a working day per tenancy.

Where the hours went

Before rewriting anything, I measured the credit-risk install. Before its first write, the prompt asked the model to cross-check 573 KB of config JSON against 130 KB of instructions. That is about 175k tokens, more than fits in one context window, so the run compacted and had to re-read the files. The install itself was roughly 400 writes and 200 reads. Each one was a model turn.

Working from those counts, I estimated three hours in the best case and ten in a conservative one. In practice a full install took seven to eight hours. Most of that time went into moving data through the context, not into any real decisions.

Why "write it to a file first" doesn't help

The first fix I tried was a prompt rule: save the MCP response to a file and analyse it from there.

That rule makes things worse. An MCP result lands in the conversation no matter what the prompt says. For the model to save it, it first reads the whole response as input tokens. Then it writes the same content back out as output tokens, which are the slowest and most expensive kind. So the data is paid for twice, and it stays in the history afterwards, where it is sent again on every later turn.

Writes had the same problem in reverse. Every payload in the pack was already a JSON file on disk, but an MCP tool only receives what the model types. The largest flow was 89 KB, so the model retyped about 22k tokens in a single call. Retyping is also a correctness risk: one node copied wrong still saves, and nothing flags it.

Prompt caching reduces what the history costs, but the history still uses up the context window. And as the context grows, the model gets worse. It is at its least reliable late in the run, when a half-written build is most expensive to recover from.

The rewrite

I kept the pack content byte-for-byte and changed only the executor. Each skill now ships a stdlib-only Python script. The script calls the same REST endpoints the MCP tools use and authenticates with a credentials file the user points it at. The skill tells the model never to open that file.

The model's job is now short. It runs discover and reads the result: a summary of what is missing, what differs and what a person needs to decide. The full JSON report goes to a temp file the model is told not to read. After a person approves the plan, the model runs deploy with that plan's hash. The deploy refuses to run if anything changed since the plan was reviewed.

On a QA tenancy, the whole install now takes under five minutes of machine time. Fields, forms, workspaces, 179 insights and views took 229 API calls in 2 minutes 10 seconds. Flows and rules took another 12 seconds. The model reads about 20k tokens across the whole run: the skill file and one short report per phase. That is roughly 30 times less than before, and the run fits in one context window with no compaction.

Moving to code also brought three improvements I hadn't planned for:

  • The same pack and tenancy always produce the same plan hash.

  • Safety rules such as "stop on the first failed write" and "never touch production without a flag" are now enforced in code. Before, they were sentences the model was asked to follow.

  • Every entity the script writes carries a stamp, so the build can be uninstalled cleanly. The old prompt claimed this was impossible because MCP can't delete a field. That limit was in the MCP tool surface, not in the platform API.

Takeaway: data stays on disk, and only conclusions enter the context.

This isn't "scripts good, MCP bad"

The real question is where the data flows. A script that prints 10 MB of JSON to stdout has the same problem the MCP tool had. In the other direction, an MCP tool that pages, filters and returns a summary instead of the raw API response is fine. Most of the trouble comes from tools that pass the full upstream response straight through.

The script path has costs too. It needs Python, an API user and a credentials file, and it only runs where it can reach the API. Its mapping layer is a second copy of what the MCP server already does, and nothing keeps the two in sync. MCP follows platform changes for free, because the tool schemas are the contract. It also handles odd cases better, since the model can reason about an unexpected tenancy instead of refusing it.

For a build of a few dozen objects, or an exploratory question like "what does this flow do?", I would still use MCP. A per-skill script is a workaround. The better long-term fix is for the platform to own the executor, through batch MCP tools or a flow upload that takes a file. That would bring roughly 400 calls down to about 30, and the model would no longer type payloads by hand.

Reflection

Looking back, the MCP version wasn't really a bad first draft. It was a specification that happened to be executable. The prose described what a correct result looks like, and the JSON was already machine-shaped. The one thing missing was an executor, so the model played that role. Model time is the most expensive way to run a loop, and model output is the least reliable way to copy a file.

The model still matters in the new version, but its job has changed. It reads the drift report, explains it, and asks the reviewer the right questions. That is work only a model can do. Copying JSON never needed a model.

Don't Make the Model Your Loop: Scripts vs MCP in Agent Skills | WeiTanZzz