<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>MCP on David Parry</title>
    <link>https://davidparry.com/tags/mcp/</link>
    <description>Recent content in MCP on David Parry</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Sat, 03 Oct 2026 11:30:00 -0500</lastBuildDate>
    <atom:link href="https://davidparry.com/tags/mcp/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>📏 Generative Agents Create. Decision Models Judge.</title>
      <link>https://davidparry.com/blog/2026/10/03/generative-agents-create-decision-models-judge/</link>
      <pubDate>Sat, 03 Oct 2026 11:30:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/10/03/generative-agents-create-decision-models-judge/</guid>
      <description>&lt;img src=&#34;https://davidparry.com/images/generative-agents-create-decision-models-judge-linkedin.jpg&#34; alt=&#34;A machined part on a tooling plate beside a two-ended go/no-go plug gauge labeled DECISION MODEL with green GO and red NO-GO ends, a dial indicator labeled THRESHOLD, and a steel diverter routing parts down three channels labeled CONTINUE, REWORK, and ESCALATE&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;Just when I thought I might not write code today, Ollama added support for decision models.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://ollama.com/blog/ollama-now-supports-jev-style-decision-models&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Ollama 0.35&lt;/a&gt;&#xA; exposes a new &lt;code&gt;/v1/systemone&lt;/code&gt; endpoint that follows TypeSafe&amp;rsquo;s Jev API. You send text as &lt;code&gt;state&lt;/code&gt; and a set of named questions. One pass returns a typed answer for each, with a probability for every allowed option. &lt;a href=&#34;https://ollama.com/library/nimble&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;nimble&lt;/code&gt;&lt;/a&gt;&#xA; is a 9B model from Bespoke Labs, fine-tuned from Qwen3.5-9B and released under Apache 2.0. There is no reasoning step, which is why a decision comes back in roughly 90 milliseconds on a laptop.&lt;/p&gt;</description>
      <content:encoded>&lt;img src=&#34;https://davidparry.com/images/generative-agents-create-decision-models-judge-linkedin.jpg&#34; alt=&#34;A machined part on a tooling plate beside a two-ended go/no-go plug gauge labeled DECISION MODEL with green GO and red NO-GO ends, a dial indicator labeled THRESHOLD, and a steel diverter routing parts down three channels labeled CONTINUE, REWORK, and ESCALATE&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;Just when I thought I might not write code today, Ollama added support for decision models.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://ollama.com/blog/ollama-now-supports-jev-style-decision-models&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Ollama 0.35&lt;/a&gt;&#xA; exposes a new &lt;code&gt;/v1/systemone&lt;/code&gt; endpoint that follows TypeSafe&amp;rsquo;s Jev API. You send text as &lt;code&gt;state&lt;/code&gt; and a set of named questions. One pass returns a typed answer for each, with a probability for every allowed option. &lt;a href=&#34;https://ollama.com/library/nimble&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;nimble&lt;/code&gt;&lt;/a&gt;&#xA; is a 9B model from Bespoke Labs, fine-tuned from Qwen3.5-9B and released under Apache 2.0. There is no reasoning step, which is why a decision comes back in roughly 90 milliseconds on a laptop.&lt;/p&gt;&#xA;&lt;p&gt;So my daily driver and &lt;a href=&#34;https://davidparry.com/blog/2026/09/25/the-jig/&#34;&gt;the Jig&lt;/a&gt;&#xA; both just got more interesting, because this is a primitive I did not have:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Generative agents create. Decision models judge. Deterministic code controls the workflow.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;a-gauge-does-not-decide-what-happens-to-the-part&#34;&gt;A gauge does not decide what happens to the part&lt;/h2&gt;&#xA;&lt;p&gt;A go/no-go gauge does not measure anything. It answers one bounded question about one part: does this seat, or does it not. Somebody else set the tolerance before the gauge was ground, and the shop&amp;rsquo;s routing decides whether a part that fails goes back to the machine, gets scrapped, or lands on an engineer&amp;rsquo;s desk.&lt;/p&gt;&#xA;&lt;p&gt;That division is the entire idea. The gauge supplies the verdict. It does not own the policy.&lt;/p&gt;&#xA;&lt;p&gt;When I want a judgment out of a model inside the harness today, I ask a generative model and get back prose, or JSON I have to parse, validate, and then second-guess. The judgment and the consequence arrive fused together in the same blob of text, and pulling them apart is my problem. A decision model separates them at the source. The allowed answers are declared up front, the model picks one, and the probability distribution comes back with it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-my-refiner-actually-does-today&#34;&gt;What my refiner actually does today&lt;/h2&gt;&#xA;&lt;p&gt;In &lt;a href=&#34;https://github.com/davidparry/spec-driven-agentic/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;spec-driven-agentic&lt;/code&gt;&lt;/a&gt;&#xA;, a requirement is validated and critiqued before a scenario or a line of production code exists. Neither step calls a model. &lt;code&gt;SpecValidator::validate&lt;/code&gt; is structural. &lt;a href=&#34;https://github.com/davidparry/spec-driven-agentic/blob/trunk/harness/src/domain/refiner.rs&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;RequirementRefiner::review&lt;/code&gt;&lt;/a&gt;&#xA; is a stack of regexes:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;AMBIGUOUS&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;LazyLock&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Regex&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; LazyLock::new(&lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Regex::new(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(?i)\b(should|could|might|handles?|properly|appropriately|quickly|easily|robust|user-friendly|etc)\b&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .expect(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;valid regex&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When I wrote in the Jig post that a useful tool tells you &amp;ldquo;quickly&amp;rdquo; is not measurable, that is this list. I would keep it. It is fast, it costs nothing, it never flakes, and it is the same answer every time.&lt;/p&gt;&#xA;&lt;p&gt;It also only catches vagueness spelled one of eleven ways. &amp;ldquo;The response should feel snappy&amp;rdquo; trips on &lt;code&gt;should&lt;/code&gt;. &amp;ldquo;The response completes before the user notices&amp;rdquo; trips nothing at all, and it is exactly as unmeasurable. The regex matches words. The thing I actually care about is a property.&lt;/p&gt;&#xA;&lt;p&gt;That is the gap. Not &amp;ldquo;rewrite this requirement,&amp;rdquo; which is generative work the coding model already does well. One bounded question: is this acceptance criterion measurable? True or false, with a probability.&lt;/p&gt;&#xA;&lt;p&gt;Both the validator and the refiner report plain English strings. &lt;code&gt;validate_spec&lt;/code&gt; hands back a &lt;code&gt;Vec&amp;lt;String&amp;gt;&lt;/code&gt; and &lt;code&gt;refine_requirement&lt;/code&gt; hands back findings, with no typed codes anywhere in the MCP or CLI contract. Every consumer downstream is pattern-matching on sentences I wrote. A typed decision plane is the thing that lets a finding become a value instead of a paragraph.&lt;/p&gt;&#xA;&lt;h2 id=&#34;where-the-decision-plane-goes-in&#34;&gt;Where the decision plane goes in&lt;/h2&gt;&#xA;&lt;p&gt;The change is not that there is another local model to run. It is that a &lt;strong&gt;decision plane&lt;/strong&gt; now sits between agent reasoning and workflow execution, and the responsibilities split four ways:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;The specification defines the contract&lt;/strong&gt;: requirements, acceptance criteria, permitted outcomes, thresholds, and escalation rules.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Generative agents implement&lt;/strong&gt;: the model writes and modifies code to satisfy the specification.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Decision models evaluate&lt;/strong&gt;: bounded questions decide whether a requirement is satisfied, whether the risk is acceptable, whether a human needs to look.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Deterministic code transitions state&lt;/strong&gt;: the harness decides exactly what happens for each permitted outcome.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The gates I want are the ones I already check by hand or by regex: &lt;code&gt;SPEC_SATISFIED&lt;/code&gt;, &lt;code&gt;TESTS_SUFFICIENT&lt;/code&gt;, &lt;code&gt;SECURITY_REVIEW_REQUIRED&lt;/code&gt;, &lt;code&gt;IMPLEMENTATION_COMPLETE&lt;/code&gt;, &lt;code&gt;HUMAN_REVIEW_REQUIRED&lt;/code&gt;. Each maps to a transition the harness owns: &lt;code&gt;CONTINUE&lt;/code&gt;, &lt;code&gt;REWORK&lt;/code&gt;, &lt;code&gt;ESCALATE&lt;/code&gt;, &lt;code&gt;STOP&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Three places in the harness are waiting for this.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;strong&gt;advice paths&lt;/strong&gt; are the easiest. &lt;code&gt;spec status&lt;/code&gt; and the &lt;code&gt;spec implement&lt;/code&gt; preflight both ask a generative model what to do next, strip the code fences, and check that the reply is not empty. Non-empty is not a verdict. A bounded question about whether the project is ready to implement is one, and it is a question with three or four legitimate answers rather than a paragraph.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;strong&gt;refactor loop&lt;/strong&gt; accepts a round when the suite passes and the test count has not moved. That catches a refactor that broke something. It does not catch a refactor that changed nothing worth changing, and it burns up to ten attempts finding out.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Tool-call moderation&lt;/strong&gt; is the one I am most interested in, and Nimble&amp;rsquo;s own documentation leads with it: send &lt;code&gt;run_shell(command=&amp;quot;rm -rf ~&amp;quot;)&lt;/code&gt; as state, ask whether the call could cause harm, get back a probability. The harness already has the hook. &lt;code&gt;[tools] confirm&lt;/code&gt; defaults to &lt;code&gt;command_run&lt;/code&gt; and nothing else, which is a static list guarded by a human prompt. That works while a person is sitting there. &lt;code&gt;spec deliver&lt;/code&gt; runs unattended and auto-accepts its gates by design, and unattended is the case &lt;a href=&#34;https://davidparry.com/blog/2026/09/25/the-jig/&#34;&gt;the Jig was built for&lt;/a&gt;&#xA;. A decision plane gives that mode something to escalate on instead of nothing.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-threshold-is-mine&#34;&gt;The threshold is mine&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;confidence&lt;/code&gt; in the response is not what the word suggests, and Ollama says so directly: it &amp;ldquo;runs from 0 to 1 and shows how concentrated the probabilities are. It isn&amp;rsquo;t the chance that the answer is right.&amp;rdquo; The documentation is blunter still a few lines down. A probability of 0.9 does not mean the answer is right 90% of the time on your data, and any threshold has to be tested on your own data before you lean on it.&lt;/p&gt;&#xA;&lt;p&gt;So the number is an input to a policy I have to calibrate, not a verdict I can take at face value. The harness has no confidence type, no threshold, and no escalation path today. All of that is new work, and the thresholds have to be measured against my own labeled requirements rather than borrowed from a benchmark.&lt;/p&gt;&#xA;&lt;p&gt;The benchmarks do say where to be careful. Across 13 public datasets and 3,880 human-labeled decisions, Nimble 9B averaged 75.7% on Ollama&amp;rsquo;s run, against 76.0% for TypeSafe&amp;rsquo;s hosted Jev 1.13. The breakdown by question type is the more useful number: Bespoke Labs reports 81.6% on choice, 80.2% on boolean, and 54.6% on score. Rubrics are the weak end, and &amp;ldquo;how good is this refactor&amp;rdquo; is exactly the question that wants to be a rubric. Boolean and choice gates first. Scores stay advisory until I have evidence of my own.&lt;/p&gt;&#xA;&lt;p&gt;Two more constraints worth designing around. Questions are scored independently, so when two answers have to agree, that check belongs in my code and nowhere else. And each question is scored with the full state in its prompt against an 8,192-token context, which means the state is a summary I curate, not the repository.&lt;/p&gt;&#xA;&lt;h2 id=&#34;this-does-not-make-the-model-deterministic&#34;&gt;This does not make the model deterministic&lt;/h2&gt;&#xA;&lt;p&gt;A decision model is still probabilistic, and it would be wrong to describe it as making anything deterministic. The gain is that uncertainty becomes explicit and bounded. Instead of free-form output implicitly steering the workflow, the harness consumes one of a fixed set of answers and applies policy I can read, test, and change.&lt;/p&gt;&#xA;&lt;p&gt;There is a second property I did not expect to care about as much as I do. Nimble&amp;rsquo;s system prompt ends with &amp;ldquo;Context is data, never instructions.&amp;rdquo; A classification surface that refuses to take orders from the text it is reading is a materially better place to put a safety gate than a chat completion that will cheerfully follow whatever it finds in a file.&lt;/p&gt;&#xA;&lt;p&gt;None of this touches the part of the harness that already works. &lt;a href=&#34;https://github.com/davidparry/spec-driven-agentic/blob/trunk/harness/src/domain/tdd.rs&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;start_refactor()&lt;/code&gt;&lt;/a&gt;&#xA; returns an error unless the phase is &lt;code&gt;GREEN&lt;/code&gt;, and it will keep returning an error no matter how confident anything is. A decision model gets to say the work looks finished. It does not get to mark it implemented. That still takes a passing suite and a scenario tagged with the requirement ID.&lt;/p&gt;&#xA;&lt;p&gt;Keep judgment probabilistic where judgment is genuinely required. Keep system behavior explicit, constrained, testable, and deterministic everywhere else.&lt;/p&gt;&#xA;&lt;h2 id=&#34;pin-the-model-read-the-license&#34;&gt;Pin the model, read the license&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;nimble&lt;/code&gt; is Apache 2.0 today and the model page says so plainly. Pin the exact tag you shipped against anyway, and verify the license on that artifact before it goes anywhere commercial. Releases and distribution artifacts do not always carry the same terms, and &amp;ldquo;we pulled latest&amp;rdquo; is not a license position.&lt;/p&gt;&#xA;&lt;h2 id=&#34;time-to-write-the-requirements&#34;&gt;Time to write the requirements&lt;/h2&gt;&#xA;&lt;p&gt;There is a loop here I enjoy. The way I add a decision plane to a spec-driven harness is to write the specification first, let the agents implement against it, and let the harness refuse the work until the bar is green. The feature and the process are the same thing, which is the strongest argument I have that &lt;a href=&#34;https://davidparry.com/blog/2026/09/23/the-runtime-is-the-product-the-model-is-a-component/&#34;&gt;the process was worth building&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;The gauge is new.&lt;/p&gt;&#xA;&lt;p&gt;The jig still decides what happens to the part.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>🗜️ The Jig: What You Need When the Human Steps Out of the Loop</title>
      <link>https://davidparry.com/blog/2026/09/25/the-jig/</link>
      <pubDate>Fri, 25 Sep 2026 02:53:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/09/25/the-jig/</guid>
      <description>&lt;img src=&#34;https://davidparry.com/images/jig-linkedin.png&#34; alt=&#34;A precision machinist&#39;s jig on a workbench, where a glowing cutting tool labeled THE MODEL is held to one path by a hardened bushing labeled GUIDE as it cuts a clamped metal part labeled THE WORK, with a red toggle clamp labeled CONSTRAIN and two dial indicators labeled VERIFY and FEEDBACK&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;A jig is a purpose-built harness around a model.&lt;/strong&gt; It gives the model the context, tools, constraints, workflow, and verification needed to do one class of work well. The model stays adaptable and can still reason its way through an unfamiliar problem, but the system around it makes the path to a useful outcome explicit.&lt;/p&gt;</description>
      <content:encoded>&lt;img src=&#34;https://davidparry.com/images/jig-linkedin.png&#34; alt=&#34;A precision machinist&#39;s jig on a workbench, where a glowing cutting tool labeled THE MODEL is held to one path by a hardened bushing labeled GUIDE as it cuts a clamped metal part labeled THE WORK, with a red toggle clamp labeled CONSTRAIN and two dial indicators labeled VERIFY and FEEDBACK&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;A jig is a purpose-built harness around a model.&lt;/strong&gt; It gives the model the context, tools, constraints, workflow, and verification needed to do one class of work well. The model stays adaptable and can still reason its way through an unfamiliar problem, but the system around it makes the path to a useful outcome explicit.&lt;/p&gt;&#xA;&lt;p&gt;I started uncovering this idea in &lt;a href=&#34;https://davidparry.com/blog/2026/09/23/the-runtime-is-the-product-the-model-is-a-component/&#34;&gt;The Runtime Is the Product. The Model Is a Component.&lt;/a&gt;&#xA;, where the argument was that the runtime should own the workflow, the guardrails, the tool boundaries, and the verification. That is still the argument. This post is where I put a name to it.&lt;/p&gt;&#xA;&lt;p&gt;The name is &lt;strong&gt;Jig&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Large language models are powerful, and power is not the same thing as reliability. Give a model an open-ended brief, filesystem access, a shell, and a blank workspace, and it may produce something remarkable. It may also take an appealing shortcut, skip a constraint, declare victory too early, or solve a problem adjacent to the one you actually asked about.&lt;/p&gt;&#xA;&lt;p&gt;A better prompt does not fix that.&lt;/p&gt;&#xA;&lt;h2 id=&#34;a-harness-is-worn-a-jig-is-clamped-down&#34;&gt;A harness is worn. A jig is clamped down.&lt;/h2&gt;&#xA;&lt;p&gt;I called that a purpose-built harness, which is the phrase most of us reach for. One word in it is wrong, and the reason it is wrong is the reason for this post.&lt;/p&gt;&#xA;&lt;p&gt;A harness goes on a person. A climber wears one, an arborist wears one, a roofer wears one. It does not do the work and it does not decide how the work gets done. It keeps someone attached while they make one move at a time, and it catches them when a move goes wrong. That is a genuinely good picture of AI with a human in the loop. You are still driving, still choosing each next step, and the harness is there so your mistakes stay survivable.&lt;/p&gt;&#xA;&lt;p&gt;Nobody wears a jig. It is clamped to the bench, and protecting anyone is not its purpose. In manufacturing, a jig paves the path the tool has to travel, holds the work in the one position that produces the right cut, and refuses the piece that does not seat properly. It does not replace the tool, it makes the tool worth pointing at production work, and it needs nobody watching to do any of that.&lt;/p&gt;&#xA;&lt;p&gt;Which word you want depends on who is present. While a human approves every step, protection is what you are buying, and harness is the honest term for it. Once an agent is expected to carry work all the way to delivery with no person at each move, protection is not the thing that is missing. What is missing is the path, the test, and the feedback that catch the work going wrong while it is still going wrong.&lt;/p&gt;&#xA;&lt;p&gt;So keep purpose-built and drop harness. The thing I have been describing, and the thing my own repository still calls a harness, is a jig.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-model-is-not-the-system&#34;&gt;The model is not the system&lt;/h2&gt;&#xA;&lt;p&gt;We often talk about an LLM as though it is the product. Pick the model, write the prompt, add a few tools, and hope the intelligence carries the rest. That framing asks a probabilistic system to guarantee an outcome.&lt;/p&gt;&#xA;&lt;p&gt;A model can interpret language, generate alternatives, recognize patterns, and make useful leaps. Those are extraordinary capabilities. What it does not have is your domain: which constraints matter most, which actions are safe to take, what has to be verified before anything is called finished, and what &amp;ldquo;done&amp;rdquo; means for this particular kind of work. None of that is in the weights, and restating it at the top of a conversation does not make it binding.&lt;/p&gt;&#xA;&lt;p&gt;A Jig makes those decisions first-class. It turns a vague instruction such as &amp;ldquo;build this feature properly&amp;rdquo; into a defined sequence of valid operations, observable evidence, and meaningful gates.&lt;/p&gt;&#xA;&lt;p&gt;The goal is not to make the model deterministic. Its reasoning is probabilistic, and part of its value comes from exactly that. The goal is to make the outcome dependable even when the reasoning wanders.&lt;/p&gt;&#xA;&lt;h2 id=&#34;a-reference-implementation&#34;&gt;A reference implementation&lt;/h2&gt;&#xA;&lt;p&gt;I have been building one example: &lt;a href=&#34;https://github.com/davidparry/spec-driven-agentic/tree/trunk/harness&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;spec&lt;/code&gt;&lt;/a&gt;&#xA;, a spec-driven BDD and TDD harness for software delivery. It is built around a single problem, getting from a requirement to working, tested software without letting the process quietly lose its way, which is what makes it one Jig rather than the shape every Jig should take.&lt;/p&gt;&#xA;&lt;p&gt;Here the requirements specification is the source of truth. It is structurally validated, then checked for vague wording, before a scenario or a line of production code exists. Behavior flows from the approved requirement into Gherkin scenarios, step definitions, tests, and implementation, in that order.&lt;/p&gt;&#xA;&lt;p&gt;Red, Green, and Refactor are enforced as states. A refactor cannot begin while the tests are red. A requirement cannot be marked complete without the evidence that it passed. Every change is staged for review. The model gets no unrestricted filesystem, shell, or dependency-installation access; it reaches the work through a fixed set of tools and nothing else.&lt;/p&gt;&#xA;&lt;p&gt;That is the difference between a Jig and an agent holding a detailed instruction sheet. The commands do not matter, nor the language it is written in, nor even that the domain is software delivery. What matters is that the Jig knows the shape of good work in its domain, and can tell when it is not getting it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;prompts-describe-jigs-enforce&#34;&gt;Prompts describe, Jigs enforce&lt;/h2&gt;&#xA;&lt;p&gt;Prompts are still useful. They carry intent and leave the model room to think. They are a weak place to keep anything non-negotiable.&lt;/p&gt;&#xA;&lt;p&gt;If a requirement must be clear before work starts, validate it. If an action is only valid in a particular state, make the state explicit and refuse the transition. If a change must be reviewed before it becomes real, stage it. If success needs proof, collect the proof rather than accepting the model&amp;rsquo;s confidence in its place.&lt;/p&gt;&#xA;&lt;p&gt;The test for what belongs in the Jig is whether the model can skip it. Anything a model can talk its way past is a preference, not a rail.&lt;/p&gt;&#xA;&lt;p&gt;So the question stops being how we get the model to follow our process, and becomes which parts of that process should never have depended on its discretion. The model spends its intelligence on interpreting intent, proposing solutions, writing code, diagnosing failures, and adapting to what it observes. The Jig holds the allowed actions, the required sequence, the domain context, the checkpoints, and the record.&lt;/p&gt;&#xA;&lt;h2 id=&#34;a-skill-is-not-a-jig&#34;&gt;A Skill is not a Jig&lt;/h2&gt;&#xA;&lt;p&gt;The objection I expect here is that all of this already exists. Skills package the context, the instructions, the reference material, and sometimes the scripts for a class of work. Hand one to a general-purpose agent and surely you have a Jig.&lt;/p&gt;&#xA;&lt;p&gt;You have a briefing. The &lt;a href=&#34;https://agentskills.io/specification&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Agent Skills specification&lt;/a&gt;&#xA; loads those instructions once the agent has decided the skill is relevant, so the model chooses when the guidance applies and how much of it survives contact with a hard task. Apply the test from the previous section and a Skill fails it by construction, because activation and compliance are both the model&amp;rsquo;s call. The cost side told me the same thing when I measured it, and what mattered was never the packaging: it was &lt;a href=&#34;https://davidparry.com/blog/2026/07/20/skills-vs-mcp-what-the-token-bill-actually-measures/&#34;&gt;whether the rule ran inside the model or in code&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;This is why a generic agent does not turn into a Jig when you attach a better Skill to it. The package can describe good work in exhaustive detail. It cannot hold the state, block a step that should not happen yet, or notice a step that never happened at all. Nobody inside a Skill has the job of saying no.&lt;/p&gt;&#xA;&lt;h2 id=&#34;tools-are-the-grip&#34;&gt;Tools are the grip&lt;/h2&gt;&#xA;&lt;p&gt;Something has to hold instead, and in a Jig it is the tool surface. MCP is usually sold as reach, a standard way to give a model more capability. Inside a Jig the valuable property is the inverse of that. When tools are the only way to touch the work, the set of things the model can attempt is finite, typed, and known before it starts, and every attempt arrives as a call the Jig can permit, refuse, or record. A narrow tool list is a clamp. Because MCP is a standard rather than a private interface, the same grip holds whether a general-purpose agent is driving or the Jig is sequencing the work itself.&lt;/p&gt;&#xA;&lt;p&gt;The grip is also how truth reaches the model. Left to its own account of what it did, a model reports the version of events it finds most plausible, and it is fluent enough to make that version sound like a result. A tool returns what actually happened: the validator&amp;rsquo;s verdict, the color of the bar, the state the workflow is genuinely in.&lt;/p&gt;&#xA;&lt;p&gt;Which makes the return value as important as the permission. A tool that answers &amp;ldquo;ok&amp;rdquo; leaves the model exactly where it was. A tool that answers that an acceptance criterion has no Then clause, and that &amp;ldquo;quickly&amp;rdquo; is not measurable, has handed it something to work with. Restraint and evidence come through the same channel, and that is what makes the grip tight without making it stiff.&lt;/p&gt;&#xA;&lt;p&gt;The model still reasons however it reasons, and it is free to be wrong along the way. What it cannot do is carry a wrong claim about the work past a call that is able to contradict it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;build-jigs-not-generic-agents&#34;&gt;Build Jigs, not generic agents&lt;/h2&gt;&#xA;&lt;p&gt;General-purpose agents earn their flexibility on work with no settled shape, which describes most exploration. A great deal of engineering work is not like that. Shipping a feature, investigating an incident, preparing a release, reviewing a change, running a security assessment, migrating a system: each one has known constraints, known failure modes, and evidence that ought to exist before anybody calls it complete. That is where a Jig repays what it costs, and it does cost more than writing a prompt. Repetition is what justifies the build.&lt;/p&gt;&#xA;&lt;p&gt;The opposite failure is worth naming too. A Jig that tries to script every decision is not guiding anything, it is ordinary automation with an expensive model sitting inside it. Narrow enough to understand the work deeply, open enough to leave the model everything that cannot be reduced to a fixed script. It should not try to predict the answer. It should create the conditions in which a good answer becomes safe, testable, repeatable work.&lt;/p&gt;&#xA;&lt;p&gt;So we are not choosing between rigid automation and unconstrained intelligence. We can build systems that use everything the model is good at and still keep the outcome in human hands, because the jig we built is what decides when the work is finished.&lt;/p&gt;&#xA;&lt;p&gt;That is the Jig.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>⚙️ The Runtime Is the Product. The Model Is a Component.</title>
      <link>https://davidparry.com/blog/2026/09/23/the-runtime-is-the-product-the-model-is-a-component/</link>
      <pubDate>Wed, 23 Sep 2026 10:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/09/23/the-runtime-is-the-product-the-model-is-a-component/</guid>
      <description>&lt;img src=&#34;https://davidparry.com/images/the-runtime-is-the-product-linkedin.jpg&#34; alt=&#34;A machined runtime on a desk, gears and a red-to-green gate around a small glowing cube, beside a laptop showing a workflow and a local machine&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;For the last year, the industry has largely treated the model as the product.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Which model should we buy? Which benchmark did it win? How many agent skills should we load into its context? How much can it do with one enormous prompt?&lt;/p&gt;</description>
      <content:encoded>&lt;img src=&#34;https://davidparry.com/images/the-runtime-is-the-product-linkedin.jpg&#34; alt=&#34;A machined runtime on a desk, gears and a red-to-green gate around a small glowing cube, beside a laptop showing a workflow and a local machine&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;For the last year, the industry has largely treated the model as the product.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Which model should we buy? Which benchmark did it win? How many agent skills should we load into its context? How much can it do with one enormous prompt?&lt;/p&gt;&#xA;&lt;p&gt;Those are reasonable questions, but they miss a more useful one:&lt;/p&gt;&#xA;&lt;p&gt;What runtime have we built around the model?&lt;/p&gt;&#xA;&lt;p&gt;Tokens are not free. Neither is model attention. Every skill, tool description, policy document, repository summary, and repeated instruction consumes context that the model must interpret before it can do useful work. General-purpose coding agents need that flexibility, because they have to operate in almost any repository and almost any workflow. That flexibility is overhead once the workflow is known.&lt;/p&gt;&#xA;&lt;p&gt;My &lt;a href=&#34;https://github.com/davidparry/spec-driven-agentic/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Spec-Driven with Harness project&lt;/a&gt;&#xA; puts the workflow, the guardrails, and the verification in the harness. The model still drafts, refines, and attempts the implementation. It does not have to remember the process, or police its own compliance.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-model-should-not-be-the-process&#34;&gt;The model should not be the process&lt;/h2&gt;&#xA;&lt;p&gt;The workflow is the one in &lt;a href=&#34;https://davidparry.com/blog/2026/08/07/spec-first-was-always-right-agents-just-made-it-fast/&#34;&gt;Spec-First Was Always Right&lt;/a&gt;&#xA;: a versioned requirement, executable scenarios, and a test bar.&lt;/p&gt;&#xA;&lt;p&gt;The harness exposes 25 MCP tools when a general agent connects to it. A model-backed command gets a smaller profile: three tools to reword a requirement or generate a scenario, four to draft or to generate steps and unit tests, five for refactor and for implement advice, seven to implement or to report status. &lt;code&gt;ask&lt;/code&gt; is the exception, twelve read-only tools. The defaults are in &lt;a href=&#34;https://github.com/davidparry/spec-driven-agentic/blob/trunk/harness/src/domain/tool_profile.rs&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;tool_profile.rs&lt;/code&gt;&lt;/a&gt;&#xA;. Running tests, reading state, validating a spec, and enforcing the phase transition do not call a model. The &lt;a href=&#34;https://github.com/davidparry/spec-driven-agentic/blob/trunk/README.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;command surface&lt;/a&gt;&#xA; is documented with the project.&lt;/p&gt;&#xA;&lt;p&gt;A skill can tell a model, “do not refactor on red.” A runtime can make the invalid transition impossible.&lt;/p&gt;&#xA;&lt;h2 id=&#34;a-small-experiment-with-real-clocks&#34;&gt;A small experiment, with real clocks&lt;/h2&gt;&#xA;&lt;p&gt;I drove the same workshop exercises on the same laptop, from the same commit, with the same local Ollama model, &lt;code&gt;qwen3.8-flash-next:125b-mlx&lt;/code&gt;. One path was pi 0.85.1, connected to &lt;code&gt;spec mcp serve&lt;/code&gt;. The other was the &lt;code&gt;spec&lt;/code&gt; binary, 0.5.5, sequencing the workflow itself. Five phases: draft REQ-007, generate the scenario and unit test, commit and show RED, implement, then refactor and mark implemented.&lt;/p&gt;&#xA;&lt;p&gt;Both finished with the workshop verifier at 7 of 7. The runtime took 176.7 seconds. The agent took 642.6 seconds, 3.6× on this single run. The &lt;a href=&#34;https://github.com/davidparry/spec-driven-agentic/blob/trunk/notes/wall-clock-pi-vs-spec.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;wall-clock record&lt;/a&gt;&#xA; keeps two costs that belong to the agent path: 145.7 seconds of scenarios rejected at review, and 38.4 seconds repairing an invalid import. Without them the agent path is 458.5 seconds, still 2.6×.&lt;/p&gt;&#xA;&lt;p&gt;The last phase went the other way. pi finished it in 32.6 seconds, &lt;code&gt;spec refactor&lt;/code&gt; in 59.7, because the runtime spends a model call and a test run on every round and then judges the result. On the pi path, &lt;code&gt;start_refactor&lt;/code&gt; only flips the phase.&lt;/p&gt;&#xA;&lt;p&gt;One laptop, one kata, and no token measurement. The graded outcome is a separate pair of full walks, re-walked on 0.5.4 and written up in the &lt;a href=&#34;https://github.com/davidparry/spec-driven-agentic/blob/trunk/notes/workshop-validation-runs.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;workshop validation notes&lt;/a&gt;&#xA;: all seven requirements implemented, and 29 of 29 kata tests passing, on both paths.&lt;/p&gt;&#xA;&lt;h2 id=&#34;you-do-not-need-a-frontier-model&#34;&gt;You do not need a frontier model&lt;/h2&gt;&#xA;&lt;p&gt;The spec half of that argument is the &lt;a href=&#34;https://davidparry.com/blog/2026/08/14/%EF%B8%8F-you-dont-need-a-frontier-model.-you-need-a-spec./&#34;&gt;August post&lt;/a&gt;&#xA;. This run is the other half. The harness path made no cloud call.&lt;/p&gt;&#xA;&lt;p&gt;A smaller model can still finish at the same bar. It spends the difference on iteration and validation. &lt;a href=&#34;https://aclanthology.org/2025.findings-emnlp.865/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;S*&lt;/a&gt;&#xA; revises code against execution feedback, and with that loop a 3B model outperforms GPT-4o mini. Qwen2.5-Coder 7B Instruct with the loop outperforms the 32B Instruct model without it by 10.1% on LiveCodeBench. On math problems, where a smaller model already succeeds some of the time, Snell and colleagues found that more test-time compute can beat a model fourteen times larger (&lt;a href=&#34;https://arxiv.org/abs/2408.03314&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ICLR 2025&lt;/a&gt;&#xA;). I have not timed a smaller model on this kata. The 125B run is the one on the clock. The harness is what makes the trade possible. A scenario that does not match the requirement is rejected, and refactor waits until the tests are green. A weaker model takes more trips through those gates. The outcome it is allowed to declare stays the same.&lt;/p&gt;&#xA;&lt;p&gt;A skill does not hold that gate. The &lt;a href=&#34;https://agentskills.io/specification&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Agent Skills specification&lt;/a&gt;&#xA; loads the instructions only after the agent has decided to activate the skill, and following them remains the model&amp;rsquo;s choice. On the τ-bench airline tasks, a &lt;a href=&#34;https://aclanthology.org/2025.emnlp-industry.41/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;2025 EMNLP industry paper&lt;/a&gt;&#xA; reports that agents struggle to follow complex company policy written as instructions. The approach they test is to compile the policy into guard code and check it before each action. S* finds the same limit in code: a model asked to judge which program is correct is a weak judge of what the program will do. Execution is what selects.&lt;/p&gt;&#xA;&lt;h2 id=&#34;one-use-case-outside-the-delivery-loop&#34;&gt;One use case, outside the delivery loop&lt;/h2&gt;&#xA;&lt;p&gt;The kata is software delivery. A test suite makes that an easy place to prove the loop. The same harness can be built wherever the outcome can be checked without asking the model: a spec, a bounded action, a check, an approval, and a record.&lt;/p&gt;&#xA;&lt;p&gt;That harness is what you can have confidence in. The model may draft the step. The result on record is the one that passed the check. Once the harness is built, a failed match does not post, a failed limit does not pay, and a failed reconciliation does not close, for the same reason a red bar does not refactor. The confidence is that refusal.&lt;/p&gt;&#xA;&lt;p&gt;It stops where nothing independent can fail the work. Strategy, negotiation, and diagnosis are that kind of work. I have not built or timed a harness outside this workshop. The workshop is the case with a clock on it.&lt;/p&gt;&#xA;&lt;p&gt;The model will keep improving. It cannot release the payment. With the harness, the process can complete.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>❓ Stop Guessing the Questions</title>
      <link>https://davidparry.com/blog/2026/08/31/stop-guessing-the-questions/</link>
      <pubDate>Mon, 31 Aug 2026 10:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/08/31/stop-guessing-the-questions/</guid>
      <description>&lt;img src=&#34;https://davidparry.com/images/stop-guessing-the-questions-linkedin.jpg&#34; alt=&#34;A business user stands at a developer&#39;s shoulder, pointing at a dashboard of charts while the other monitor shows analytics code in an IDE&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;In my last post, I argued that &lt;a href=&#34;https://davidparry.com/blog/2026/08/18/data-is-king.-ai-just-removed-the-middleman./&#34;&gt;data is king&lt;/a&gt;&#xA;. If that is true, why are we still building analytics as if we have to predict every question a customer will ever ask? For decades that was the job: gather requirements, decide what &amp;ldquo;usage&amp;rdquo; meant, pick the charts, ship the report, then open another ticket when someone asked a question we had not guessed. That was not bad engineering. We simply had to know the question before we could build software to answer it. &lt;strong&gt;That constraint is starting to disappear.&lt;/strong&gt;&lt;/p&gt;</description>
      <content:encoded>&lt;img src=&#34;https://davidparry.com/images/stop-guessing-the-questions-linkedin.jpg&#34; alt=&#34;A business user stands at a developer&#39;s shoulder, pointing at a dashboard of charts while the other monitor shows analytics code in an IDE&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;In my last post, I argued that &lt;a href=&#34;https://davidparry.com/blog/2026/08/18/data-is-king.-ai-just-removed-the-middleman./&#34;&gt;data is king&lt;/a&gt;&#xA;. If that is true, why are we still building analytics as if we have to predict every question a customer will ever ask? For decades that was the job: gather requirements, decide what &amp;ldquo;usage&amp;rdquo; meant, pick the charts, ship the report, then open another ticket when someone asked a question we had not guessed. That was not bad engineering. We simply had to know the question before we could build software to answer it. &lt;strong&gt;That constraint is starting to disappear.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;we-dont-need-to-predict-every-question-anymore&#34;&gt;We Don&amp;rsquo;t Need to Predict Every Question Anymore&lt;/h2&gt;&#xA;&lt;p&gt;Imagine a SaaS product with years of usage, billing, customer, feature, session and performance data. Instead of building every possible report, a customer could ask which business units increased AI-assisted development usage over the last six months without a corresponding increase in pull requests reaching production. Nobody had to anticipate that exact question.&lt;/p&gt;&#xA;&lt;p&gt;The more interesting cases are questions nobody would ever put on the roadmap.&lt;/p&gt;&#xA;&lt;p&gt;Consider a building management system with years of elevator trips, stair-door events, badge swipes, occupancy data, access control and sensor readings. Then someone asks:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&amp;ldquo;We ran a Use the Stairs campaign. Are employees actually taking the stairs more often, and does it vary by building?&amp;rdquo;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Nobody built that product to answer a wellness campaign. The evidence may already exist. The hard part is knowing what it &lt;strong&gt;means&lt;/strong&gt;. Does a stair-door opening represent an employee taking the stairs? Do cleaners count? Visitors? Freight elevators? Can badge events distinguish employees from contractors? Are the sensors equally reliable across buildings?&lt;/p&gt;&#xA;&lt;p&gt;The application no longer has to encode every possible &lt;strong&gt;question&lt;/strong&gt;. It has to encode the &lt;strong&gt;meaning of the data and the operations that are safe to perform against it&lt;/strong&gt;. That is a different architecture.&lt;/p&gt;&#xA;&lt;h2 id=&#34;duckdb-is-the-engine-not-the-meaning&#34;&gt;DuckDB Is the Engine, Not the Meaning&lt;/h2&gt;&#xA;&lt;p&gt;This is one reason I keep coming back to DuckDB. I want an &lt;a href=&#34;https://duckdb.org/docs/stable/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;analytical engine&lt;/a&gt;&#xA; that can sit next to the data, query formats such as Parquet directly, and &lt;a href=&#34;https://duckdb.org/docs/current/data/parquet/overview&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;push projections and filters into those reads&lt;/a&gt;&#xA;. That is the compute. It is not the business. DuckDB knows SQL, columns, types, and the relationships you give it. It does not know what an active customer is, whether ARR includes trials, or whether a stair-door event counts as an employee choosing the stairs. That meaning still has to come from humans.&lt;/p&gt;&#xA;&lt;p&gt;This is where a &lt;strong&gt;semantic layer&lt;/strong&gt;, or more broadly a &lt;strong&gt;context layer&lt;/strong&gt;, belongs. It is the place you define entities, metrics, dimensions, relationships, filters and rules. &lt;a href=&#34;https://github.com/Canner/WrenAI&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Wren&lt;/a&gt;&#xA; is the open-source version of that idea I would start with: a Modeling Definition Language for business meaning, plus a &lt;a href=&#34;https://crates.io/crates/wren-semantic-core&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Rust semantic engine&lt;/a&gt;&#xA; that plans SQL through those definitions. I would not let an agent improvise against raw tables and call that governance.&lt;/p&gt;&#xA;&lt;p&gt;Human expertise belongs there: not predicting every future question, but defining what the business actually means.&lt;/p&gt;&#xA;&lt;h2 id=&#34;dont-just-hand-the-llm-a-database&#34;&gt;Don&amp;rsquo;t Just Hand the LLM a Database&lt;/h2&gt;&#xA;&lt;p&gt;The obvious shortcut is to give an LLM your schema and let it generate SQL. That makes a great demo. It is a much weaker production boundary. The model can choose the wrong join, misunderstand the grain, apply the wrong definition, or generate perfectly valid SQL that answers the wrong business question. There is also a security issue. DuckDB &lt;a href=&#34;https://duckdb.org/docs/current/operations_manual/securing_duckdb/overview&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;warns that untrusted SQL should be treated like untrusted Bash or Python&lt;/a&gt;&#xA;, because it can reach files, extensions, networking and other system resources depending on configuration.&lt;/p&gt;&#xA;&lt;h2 id=&#34;a-design-proposal&#34;&gt;A Design Proposal&lt;/h2&gt;&#xA;&lt;p&gt;The architecture I want is a runtime harness, not a chat window bolted onto a warehouse.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;User&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ↓&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Runtime harness + agent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ↓&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Governed MCP tools&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ↓&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Semantic / context layer (Wren MDL, Rust engine)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ↓&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DuckDB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ↓&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ↓&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Answer + receipt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I have not had the chance yet to put this combination into practice. DuckDB, Wren, and MCP are real. The runtime harness, the governed tools, and the receipt are the architecture I am proposing. From building MCP servers, and from measuring &lt;a href=&#34;https://davidparry.com/blog/2026/07/20/skills-vs-mcp-what-the-token-bill-actually-measures/&#34;&gt;what happens when business rules live in the model instead of in code&lt;/a&gt;&#xA;, this is the path I would take.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://duckdb.org/docs/stable/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;DuckDB&lt;/a&gt;&#xA; is the engine I would put under it. &lt;a href=&#34;https://github.com/Canner/WrenAI&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Wren&lt;/a&gt;&#xA; is where I would put the meaning: an Apache-2.0 Modeling Definition Language and a &lt;a href=&#34;https://crates.io/crates/wren-semantic-core&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Rust semantic engine&lt;/a&gt;&#xA; that plans SQL through those definitions. MCP is the tool boundary. In this design, the agent never gets the database. It gets a small set of governed operations: list metrics, describe one, query it, compare periods, and so on. MCP does not make those tools safe by itself. Authorization, tenant isolation, validation and auditing still belong to the software that implements them.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Let the model decide which governed operation it needs. Let software enforce what that operation is allowed to do.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Natural language is the right interface for the first ask. It is the wrong interface for the tenth. Once the facilities lead has asked whether employees are taking the stairs more than the elevators, they will want the trend next month. That should not cost another agent loop, another pile of tokens, and another chance for the model to assemble the question slightly differently.&lt;/p&gt;&#xA;&lt;p&gt;So the runtime produces a &lt;strong&gt;receipt&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Not a transcript. An executable artifact: Python plus the data it is allowed to see plus the MCP tools it is allowed to call. That receipt is the proof of work for the current answer, and it is all that is needed to ask the same question again.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Receipt: stairs_vs_elevator_campaign&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Question: Are employees taking the stairs more than the elevators&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          since the Use the Stairs campaign launched?&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Re-run without an LLM: python stairs_vs_elevator_campaign.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; analytics_runtime &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; tools&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CAMPAIGN_START &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2026-01-15&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EMPLOYEE_BADGE_TYPES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;employee&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contractor&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EXCLUDE_ELEVATORS &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;freight&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;service&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stair_vs_elevator&lt;/span&gt;(as_of: str) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; dict:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buildings &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tools&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;list_buildings(campaign&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;use_the_stairs&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stairs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tools&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query_stair_entries(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        buildings&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;buildings,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        since&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;CAMPAIGN_START,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        until&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;as_of,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        badge_types&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;EMPLOYEE_BADGE_TYPES,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        exclude_after_hours_service&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    elevators &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tools&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query_elevator_trips(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        buildings&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;buildings,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        since&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;CAMPAIGN_START,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        until&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;as_of,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        badge_types&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;EMPLOYEE_BADGE_TYPES,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        exclude&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;EXCLUDE_ELEVATORS,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; tools&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;explain_result(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        question&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;employee stairs vs elevators by building&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stairs&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;stairs,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        elevators&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;elevators,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        baseline&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;tools&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;compare_periods(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            metric&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vertical_circulation&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            current&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;(CAMPAIGN_START, as_of),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            baseline&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;prior_equal_window&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dimensions&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_id&amp;#34;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(stair_vs_elevator(as_of&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;today&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first run still uses the agent. It has to interpret the question, pick the tools, and apply the semantic definitions. What comes back is the answer &lt;strong&gt;and&lt;/strong&gt; this receipt. A human can see which buildings were included, which sensors counted, which badge types counted as employees, and which elevators were excluded.&lt;/p&gt;&#xA;&lt;p&gt;The output is all you keep:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Question → business interpretation → governed tools → verified result&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                      ↓&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                   receipt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                      ↓&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                              run again, no LLM&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next month the facilities lead does not spend tokens to find out whether the campaign is still working. They run the receipt against newer data. The trend is software, not a second interpretation by a model. If &amp;ldquo;taking the stairs&amp;rdquo; needs to change, a human changes the receipt or the metric underneath it. The model does not quietly drift.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Once we know how to answer a question correctly, we should not require an LLM to invent the answer path again.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;i-have-not-wired-this-together-yet&#34;&gt;I Have Not Wired This Together Yet&lt;/h2&gt;&#xA;&lt;p&gt;I am not going to pretend MotherDuck or Wren already is this architecture. I have not wired the combination end to end. What I have seen is close enough that, from my experience, this is the right path.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://motherduck.com/product/mcp-server/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;MotherDuck&lt;/a&gt;&#xA; already exposes DuckDB-based analytics to agents through MCP: inspect catalogs, run SQL, isolated compute, and the SQL itself available for inspection. That is useful. It is not my receipt, and it is not a set of governed metric tools. The agent still gets SQL. What I would steal from it is the permission split: a read-oriented &lt;code&gt;query&lt;/code&gt; tool is a different boundary than &lt;code&gt;query_rw&lt;/code&gt;. That is the lesson. The protocol should make the dangerous operation a different tool, not a different prompt.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/Canner/WrenAI&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Wren&lt;/a&gt;&#xA; is the closest open-source version of the semantic-plus-memory side I would use. The engine is Apache-2.0, written in Rust, sits on Apache DataFusion, and compiles modeled metrics and relationships into SQL. Successful questions can be kept as versioned examples and retrieved when a similar ask shows up later. That is still an agent pulling an example so it can try again. My proposal is stricter. Once we know the path, we should not need the model to invent it a second time. &lt;strong&gt;Discover a question with AI, then preserve what worked as software.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-questions-become-data-too&#34;&gt;The Questions Become Data Too&lt;/h2&gt;&#xA;&lt;p&gt;Once customers ask in natural language, &lt;strong&gt;the questions themselves become data&lt;/strong&gt;. If hundreds of customers start asking which teams have high AI usage but low acceptance of generated changes, you did not need six months of interviews to learn that the concept mattered. The same thing happens with the stairs example. If facilities teams keep asking about stair usage versus elevators, perhaps vertical circulation mix deserves to become a governed metric.&lt;/p&gt;&#xA;&lt;p&gt;Wren&amp;rsquo;s &lt;a href=&#34;https://docs.getwren.ai/oss/concepts/what_is_mdl&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;MDL&lt;/a&gt;&#xA; and &lt;a href=&#34;https://docs.getwren.ai/oss/concepts/memory_system&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;memory layer&lt;/a&gt;&#xA; are built for a version of that loop: modeled meaning in version-controlled definitions, plus stored examples the agent can retrieve. I would still want a human to decide which of those patterns become part of the governed understanding of the business. AI can surface how people are actually trying to use the data. It should not silently rewrite what the business means.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>🔺 Spec-First Was Always Right — Agents Just Made It Fast</title>
      <link>https://davidparry.com/blog/2026/08/07/spec-first-was-always-right-agents-just-made-it-fast/</link>
      <pubDate>Fri, 07 Aug 2026 16:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/08/07/spec-first-was-always-right-agents-just-made-it-fast/</guid>
      <description>&lt;img src=&#34;https://davidparry.com/images/tdd-in-the-agentic-era-spec-driven-convert-linkedin.png&#34; alt=&#34;The Specs, Code, Tests triangle kept in sync — an AI agent turns the crank while a human steers, and the test bar goes from red to green&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;For twenty years I have argued that requirements, tests, and code are the same information at three altitudes, and that the job is to keep them in sync. The argument has not changed — the economics finally have.&lt;/strong&gt; The correct way was always to nail the spec first, but that was the slower way, and when a human sits down at a keyboard there has always been an unspoken rule that whatever they type had better be code. Spec-writing looked like stalling. So teams typed the code, and the spec — if it ever existed — drifted into fiction. That trade-off is now dead. With an agent doing the transformation from requirement to test to implementation, the spec-first path is no longer the slow path. Getting it right is now &lt;em&gt;faster&lt;/em&gt; than winging it, because a well-captured requirement is the thing an agent can actually execute against, and a vague one is the thing you pay for in review, rework, and slop.&lt;/p&gt;</description>
      <content:encoded>&lt;img src=&#34;https://davidparry.com/images/tdd-in-the-agentic-era-spec-driven-convert-linkedin.png&#34; alt=&#34;The Specs, Code, Tests triangle kept in sync — an AI agent turns the crank while a human steers, and the test bar goes from red to green&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;For twenty years I have argued that requirements, tests, and code are the same information at three altitudes, and that the job is to keep them in sync. The argument has not changed — the economics finally have.&lt;/strong&gt; The correct way was always to nail the spec first, but that was the slower way, and when a human sits down at a keyboard there has always been an unspoken rule that whatever they type had better be code. Spec-writing looked like stalling. So teams typed the code, and the spec — if it ever existed — drifted into fiction. That trade-off is now dead. With an agent doing the transformation from requirement to test to implementation, the spec-first path is no longer the slow path. Getting it right is now &lt;em&gt;faster&lt;/em&gt; than winging it, because a well-captured requirement is the thing an agent can actually execute against, and a vague one is the thing you pay for in review, rework, and slop.&lt;/p&gt;&#xA;&lt;p&gt;I recently decided to polish my TDD talk, &lt;em&gt;TDD in the Agentic Era&lt;/em&gt;, with exactly this in mind. The talk always rested on having the spec; the revision makes the spec &lt;strong&gt;mandatory&lt;/strong&gt;. The workflow now begins at the requirement and refuses to move without one — the agent cannot write a test or a line of implementation until it has pulled the spec and turned its acceptance criteria into an executable scenario. In the 60-minute hands-on version, every artifact is real, runnable code — an MCP server, a client agent, a requirements backlog, Gherkin scenarios, and a Red/Green/Refactor state machine that refuses to let anyone, human or AI, refactor on a red bar. It is public in the &lt;a href=&#34;https://github.com/davidparry/tdd-bdd-agentic&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;tdd-bdd-agentic repository&lt;/a&gt;&#xA;, and I use it through this post as the working proof of the argument. But the workshop is the demonstration, not the point. The point is that my old conviction is holding true, and the time to do this — and get it right — is now: why this style finally pays for itself, and why this exact idea, spec-driven development as a triangle, is the reason I joined CodiumAI. I chose them for Itamar&amp;rsquo;s vision — a vision that was not completely deliverable at the time, given where the models were, but was spot on about where this was all going. That company is the one you now know as &lt;a href=&#34;https://www.qodo.ai&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Qodo.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-we-accomplish-in-the-hour&#34;&gt;What we accomplish in the hour&lt;/h2&gt;&#xA;&lt;p&gt;Everyone has watched an agent demo. Almost nobody has driven the workflow underneath one. That is the gap the workshop closes — and deliberately not by building plumbing. The MCP server and client come completed in the repo (Java, stdio transport, tested to 100% coverage); MCP gets about seven minutes as the standard way to feed your tools into whatever agent you use — Cursor, Claude Desktop, or the bundled CLI client, which narrates the handshake, discovery, and invocation that every IDE does under the hood. The rest of the hour, with 35 of the 60 minutes at your keyboard instead of looking at mine, every attendee:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Drafts and refines a requirement with an agent.&lt;/strong&gt; You describe the intent in a sentence; the agent writes the requirement into the backlog; and then the spec iterates through two server feedback loops. First structure: &lt;code&gt;validate_spec&lt;/code&gt; reports every issue — a criterion missing its Then, a duplicate id, broken JSON — and the agent fixes and re-validates until the spec is valid. Then wording: &lt;code&gt;refine_requirement&lt;/code&gt; critiques the draft — &amp;ldquo;&amp;lsquo;quickly&amp;rsquo; is ambiguous,&amp;rdquo; &amp;ldquo;the story is missing its why,&amp;rdquo; &amp;ldquo;only happy paths, add an edge case&amp;rdquo; — and the LLM rewords against that deterministic feedback until the critique comes back clean. The agent drafts, the server arbitrates, the human approves the final wording.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Drives the full spec-to-green loop.&lt;/strong&gt; A real LLM agent validates the spec, picks up a pending requirement through the seven workflow tools (&lt;code&gt;list_requirements&lt;/code&gt;, &lt;code&gt;get_requirement&lt;/code&gt;, &lt;code&gt;validate_spec&lt;/code&gt;, &lt;code&gt;refine_requirement&lt;/code&gt;, &lt;code&gt;run_tests&lt;/code&gt;, &lt;code&gt;get_tdd_state&lt;/code&gt;, &lt;code&gt;start_refactor&lt;/code&gt;), writes the Gherkin scenario for its acceptance criteria, runs the tests to show RED, implements the simplest code to reach GREEN, and then — only then — refactors.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The kata under all of this is deliberately boring: the String Calculator. The point was never the calculator. The point is the workflow wrapped around it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;three-altitudes-one-discipline&#34;&gt;Three altitudes, one discipline&lt;/h2&gt;&#xA;&lt;p&gt;The center of the talk is a slide I call the three altitudes. It is not &amp;ldquo;just TDD.&amp;rdquo; It composes the three spec-first methodologies, each pinning the system at a different height, and the agent works across all of them:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Methodology&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Pins&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Canonical artifact&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;SDD&lt;/strong&gt; (spec-driven)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;the feature&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;a versioned spec with acceptance criteria — in the repo, &lt;code&gt;requirements/requirements.json&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;BDD&lt;/strong&gt; (behavior-driven)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;one behavior&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;a Gherkin scenario, executed by Cucumber&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;TDD&lt;/strong&gt; (test-driven)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;one unit&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;a failing JUnit test&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The flow is spec-down. The agent reads a requirement (SDD), turns its acceptance criteria into a tagged Gherkin scenario (BDD), adds unit tests where useful (TDD), and the &lt;code&gt;run_tests&lt;/code&gt; tool runs Cucumber and JUnit together — one bar, one color. Tests are generated &lt;em&gt;from&lt;/em&gt; the spec, not reverse-engineered from the code afterward. That direction is the entire spec-driven claim.&lt;/p&gt;&#xA;&lt;p&gt;Here is what the agent actually produces during the spec-to-green exercise, for a requirement whose acceptance criteria were already phrased Given/When/Then in the spec:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-gherkin&#34; data-lang=&#34;gherkin&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;@REQ-003&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Scenario:&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt; Two numbers separated by a comma are summed&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;    Given &lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;a string calculator&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;When &lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;I add &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;1,2&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Then &lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;the result is &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;3&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here is the prompt the audience pastes into their agent — notice how much of it is workflow, not code:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Using the tdd-workflow tools: validate the spec first, then find the next&#xA;pending requirement, add a Gherkin scenario for its acceptance criteria to&#xA;the feature file (tag it with the requirement id), reuse or add step&#xA;definitions, run the tests to show RED, then implement the simplest code to&#xA;reach GREEN, then refactor. Ask me before each phase change.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;There are two hard human checkpoints in the loop. The first is a spec review: when the agent writes the scenario, the room stops and reads it aloud — &lt;em&gt;is this the behavior we want?&lt;/em&gt; The second is diff approval after GREEN. The agent turns the crank; the human steers. And the discipline is not a suggestion living in a prompt — it lives in the tool. Ask the server to &lt;code&gt;start_refactor&lt;/code&gt; while the bar is red and it refuses. Guardrails belong in code, not in vibes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;twenty-years-a-convert&#34;&gt;Twenty years a convert&lt;/h2&gt;&#xA;&lt;p&gt;I have been a convert to this style for twenty years. Test-first, behavior-first, and above all &lt;strong&gt;requirements-first&lt;/strong&gt; — the conviction that you cannot build the right thing until someone has written down, in a form that can be checked, what the right thing is. I lived through the era when the spec was a Word document that was obsolete before the first sprint ended. Cucumber and Gherkin were the first tools that let the requirement itself become executable, and I never went back.&lt;/p&gt;&#xA;&lt;p&gt;But I should be honest: being a convert and getting to practice it were two different things. The conviction was mostly private, because in most places I worked the requirement either did not exist at all or arrived from someone outside engineering — a product owner, a stakeholder, a business analyst — who was never equipped to turn what they wanted into a technical requirement or a PRD an engineer could build against. What came across was rarely close. That was not their failing. They knew the business cold; they simply were not trained to interrogate a request the way it needs to be interrogated — to dig for the edge cases and the unstated assumptions, to ask the questions that actually need asking, and to pin the answers down in a form that can be checked. So I spent twenty years believing in requirements-first while working in organizations that rarely produced a requirement worth the name.&lt;/p&gt;&#xA;&lt;p&gt;Which is the whole point, because it tells you where the scarce skill always lived. It was never typing the code. It was extracting what is actually needed — from the ticket, from the stakeholder, from the silence between what they said and what they meant — and pinning it down in a testable form. That is why the agentic era feels like vindication rather than disruption to me. An agent is a phenomenal crank-turner, but it can only turn the crank on requirements someone captured well — and if that skill was scarce when a human wrote the code, it is scarcer and more valuable now that an agent will faithfully execute whatever you hand it, good spec or bad. Which leads me to believe the developer&amp;rsquo;s real emerging role is &lt;em&gt;requirements gatherer for the agent&lt;/em&gt; — but that is another post, and I intend to write it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-triangle-that-made-me-join&#34;&gt;The triangle that made me join&lt;/h2&gt;&#xA;&lt;p&gt;In July 2024 I was deciding whether to join a startup called CodiumAI. Plenty of companies were pitching AI code generation; what convinced me was that the CEO, &lt;a href=&#34;https://www.linkedin.com/in/itamarf&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Itamar Friedman&lt;/a&gt;&#xA;, was saying the same thing I had spent twenty years believing — and he had drawn it as a triangle: &lt;strong&gt;spec, tests, code, kept in sync&lt;/strong&gt;. He lays it out most explicitly in his &lt;a href=&#34;https://www.linkedin.com/posts/itamarf_brownfield-activity-7404141226960060416-lhI6&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Gen 3.5 framing&lt;/a&gt;&#xA;, where agents collaborate around three core artifacts, &lt;strong&gt;Specs → Code → Tests&lt;/strong&gt;, improving the spec, transforming it into code, generating and executing tests, and keeping all three tightly aligned as each evolves. I was not joining a company that bolted testing onto generation as an afterthought; I was joining one whose founding thesis was that the three corners are the same information at different altitudes and the job of AI is to keep them aligned. When I joined, that vision was ahead of what the models could deliver. Reading it now, with the workshop in this post running exactly that loop on my laptop, we are close — if not already there.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-workshop-is-the-triangle-running&#34;&gt;The workshop is the triangle, running&lt;/h2&gt;&#xA;&lt;p&gt;Look back at the workshop with the triangle in mind and the mapping is one-to-one:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Spec corner:&lt;/strong&gt; &lt;code&gt;requirements.json&lt;/code&gt;, versioned, with acceptance criteria — drafted with the agent, held structurally valid by &lt;code&gt;validate_spec&lt;/code&gt; and iterated to clean wording by &lt;code&gt;refine_requirement&lt;/code&gt;, and the source of truth the agent implements from.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Tests corner:&lt;/strong&gt; the Gherkin feature file and the JUnit tests, generated from the spec, executed together as one bar.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code corner:&lt;/strong&gt; &lt;code&gt;StringCalculator.java&lt;/code&gt;, written last, as the simplest thing that makes the bar green.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The MCP server is the connective tissue — the standard way for an agent to discover your workflow tools and respect your discipline. The human sits at the two checkpoints where judgment lives: &lt;em&gt;is this the right spec?&lt;/em&gt; and &lt;em&gt;is this the right code?&lt;/em&gt; Everything between those checkpoints is crank-turning, and the crank no longer needs to be turned by hand.&lt;/p&gt;&#xA;&lt;p&gt;Twenty years ago, keeping the three corners in sync was a manual discipline that most teams abandoned under deadline pressure. The spec drifted, the tests decayed, and the code became the only truth — unreadable, unverifiable truth. What the agentic era changes is the cost of the discipline. The alignment work that teams always skipped is exactly the work agents are good at.&lt;/p&gt;&#xA;&lt;p&gt;The talk ends with homework: on &lt;code&gt;trunk&lt;/code&gt; — the exact starting point the class clones — requirements REQ-004 through REQ-006 are still pending, and the requirement the room drafted in Exercise 1 is waiting to be taken to green. Clone the &lt;a href=&#34;https://github.com/davidparry/tdd-bdd-agentic&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;repo&lt;/a&gt;&#xA;, point your agent at the tools, and finish the kata on the plane home. Watch the bar go RED, then GREEN. And if you want the answer key, the &lt;code&gt;complete&lt;/code&gt; branch is the kata fully driven through the loop — every requirement implemented, every scenario tagged and green — with CI telling the story on both branches: trunk deliberately fails a class-completeness check because the work is still ahead of you; complete passes everything. Then ask yourself who really wrote the requirement — because that person, not the agent, decided what got built. That&amp;rsquo;s the next post.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>🔍 MCP: Enterprise Trust Through Traceability</title>
      <link>https://davidparry.com/blog/2026/07/22/mcp-enterprise-trust-through-traceability/</link>
      <pubDate>Wed, 22 Jul 2026 09:30:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/07/22/mcp-enterprise-trust-through-traceability/</guid>
      <description>&lt;p&gt;The headline everyone pulled out of the &lt;a href=&#34;https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Model Context Protocol (MCP) 2026-07-28 Release Candidate&lt;/a&gt;&#xA; is that MCP is going &lt;strong&gt;stateless&lt;/strong&gt;. Fair enough — it&amp;rsquo;s a genuine architectural improvement. Stateless protocols scale horizontally without sticky sessions, they cut operational complexity, and they make the infrastructure easier to deploy and reason about.&lt;/p&gt;&#xA;&lt;p&gt;But I don&amp;rsquo;t think statelessness is the change that will matter most for enterprise adoption. I think &lt;strong&gt;traceability&lt;/strong&gt; is.&lt;/p&gt;&#xA;&lt;p&gt;When I gave a conference talk on building MCP servers earlier this year, the hallway questions afterward were rarely about what a server could do. They were about what it takes to run one: how the traffic shows up in gateway logs, what a trace looks like when a tool call fails, whether any of it can be audited later. So when the release candidate landed, I read it with those questions in mind.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;The headline everyone pulled out of the &lt;a href=&#34;https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Model Context Protocol (MCP) 2026-07-28 Release Candidate&lt;/a&gt;&#xA; is that MCP is going &lt;strong&gt;stateless&lt;/strong&gt;. Fair enough — it&amp;rsquo;s a genuine architectural improvement. Stateless protocols scale horizontally without sticky sessions, they cut operational complexity, and they make the infrastructure easier to deploy and reason about.&lt;/p&gt;&#xA;&lt;p&gt;But I don&amp;rsquo;t think statelessness is the change that will matter most for enterprise adoption. I think &lt;strong&gt;traceability&lt;/strong&gt; is.&lt;/p&gt;&#xA;&lt;p&gt;When I gave a conference talk on building MCP servers earlier this year, the hallway questions afterward were rarely about what a server could do. They were about what it takes to run one: how the traffic shows up in gateway logs, what a trace looks like when a tool call fails, whether any of it can be audited later. So when the release candidate landed, I read it with those questions in mind.&lt;/p&gt;&#xA;&lt;p&gt;Two changes stood out to me, and they work as a pair.&lt;/p&gt;&#xA;&lt;p&gt;The first is &lt;a href=&#34;https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;SEP-2243&lt;/a&gt;&#xA;, which makes two headers, &lt;code&gt;Mcp-Method&lt;/code&gt; and &lt;code&gt;Mcp-Name&lt;/code&gt;, required on the Streamable HTTP transport. The spec is clear about their job: they exist so load balancers, gateways, and rate limiters can route on the operation without inspecting the JSON-RPC body. What interests me is the side effect. Every layer of the stack now gets a piece of standardized, protocol-level metadata — and standardized metadata is exactly the raw material enterprise observability and audit tooling is built on.&lt;/p&gt;&#xA;&lt;p&gt;The second is &lt;a href=&#34;https://modelcontextprotocol.io/seps/414-request-meta&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;SEP-414&lt;/a&gt;&#xA;, which locks the &lt;strong&gt;W3C Trace Context&lt;/strong&gt; keys — &lt;code&gt;traceparent&lt;/code&gt;, &lt;code&gt;tracestate&lt;/code&gt;, and &lt;code&gt;baggage&lt;/code&gt; — into the spec as reserved keys inside &lt;code&gt;params._meta&lt;/code&gt; of every JSON-RPC request. Note where they live: in the message, not in HTTP headers. That&amp;rsquo;s deliberate. MCP is transport-agnostic and stdio has no headers, and a single Streamable HTTP connection can multiplex many JSON-RPC messages, so trace context has to belong to the individual request rather than the connection. A trace that starts in the host application can now follow a tool call through the client SDK, the MCP server, and whatever the server calls downstream, and land in an OpenTelemetry-compatible backend as one span tree:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;jsonrpc&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2.0&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;method&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tools/call&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;params&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;arguments&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;location&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;New York&amp;#34;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;_meta&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;traceparent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Several SDKs were already doing this — the release candidate fixes the key names so traces correlate across implementations instead of by convention. And in the same release, MCP&amp;rsquo;s own logging capability is deprecated, with OpenTelemetry named as the path for structured observability. Read those together and the direction is unambiguous: the protocol didn&amp;rsquo;t invent its own tracing story, it adopted the one enterprises already run.&lt;/p&gt;&#xA;&lt;p&gt;That matters because enterprises are not building greenfield. They already have API gateways, OpenTelemetry, centralized logging, SIEM platforms, operational dashboards, and distributed tracing — and years of trust invested in all of it. A new protocol gets adopted when it slots into that existing machinery, not when it asks to be operated as a special case beside it. Routing headers at the transport layer, W3C Trace Context in the message — small design decisions with a large consequence, because together they mean MCP can be operated alongside everything else instead of babysat beside it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-enterprise-traceability-looks-like&#34;&gt;What Enterprise Traceability Looks Like&lt;/h2&gt;&#xA;&lt;p&gt;The important property is that the new metadata enriches an existing trace without changing how distributed tracing works.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;                    End User&#xA;                       │&#xA;                       ▼&#xA;                  API Gateway ────────────────┐&#xA;                       │                      │&#xA;                       ▼                      │&#xA;            Spring Boot / Spring AI           │&#xA;                       │                      │&#xA;                       ▼                      ▼&#xA;                  MCP Client         Logs · Metrics · SIEM&#xA;                       │                      ▲&#xA;    Mcp-Method         │                      │&#xA;    Mcp-Name    ──────►│ ─────────────────────┤&#xA;    (HTTP headers)     │                      │&#xA;                       │                      │&#xA;    traceparent        │                      │&#xA;    tracestate  ──────►│                      │&#xA;    (in _meta)         │                      │&#xA;                       ▼                      │&#xA;                  MCP Server ─────────────────┤&#xA;                       │                      │&#xA;                       ▼                      │&#xA;                      Tool ───────────────────┘&#xA;&#xA;  W3C Trace Context propagates through every hop above — over HTTP&#xA;  headers on ordinary hops, inside _meta on the MCP hop. The routing&#xA;  headers add protocol-specific metadata to that same trace.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The distributed trace flows exactly as it does today. The routing headers give the infrastructure something to key on, and the &lt;code&gt;_meta&lt;/code&gt; trace keys keep the MCP hop stitched into the trace you&amp;rsquo;re already collecting.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-questions-a-platform-team-asks-first&#34;&gt;The Questions a Platform Team Asks First&lt;/h2&gt;&#xA;&lt;p&gt;Enterprise engineering organizations rarely get stuck because they can&amp;rsquo;t scale a service. They get stuck because they can&amp;rsquo;t answer questions after the fact:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Which MCP method executed, on which server, running which tool?&lt;/li&gt;&#xA;&lt;li&gt;Which distributed trace contains this interaction, and how does it line up with the gateway logs?&lt;/li&gt;&#xA;&lt;li&gt;Which agent initiated the request, and why did it fail?&lt;/li&gt;&#xA;&lt;li&gt;Can we reproduce it — and can we audit it six months from now?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These aren&amp;rsquo;t exotic questions. They&amp;rsquo;re the ones a platform team asks before it agrees to support new infrastructure in production, and standardized protocol metadata is what makes them answerable. With &lt;code&gt;Mcp-Method&lt;/code&gt; and &lt;code&gt;Mcp-Name&lt;/code&gt; riding alongside the trace context, requests correlate across services and gateways without custom parsing, dashboards and rate limits key off the operation instead of the payload, and the telemetry looks the same across different MCP implementations. That&amp;rsquo;s the difference between something a platform team can confidently run in production and something that stays a proof of concept.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fan-out-is-where-this-gets-expensive&#34;&gt;Fan-Out Is Where This Gets Expensive&lt;/h2&gt;&#xA;&lt;p&gt;The stakes go up as organizations move toward agentic software. A single user request can fan out across multiple agents, several MCP servers, and dozens of tool invocations before anything comes back. Without standardized metadata, reconstructing what actually happened in that fan-out is slow and often guesswork.&lt;/p&gt;&#xA;&lt;p&gt;With the routing headers and trace-context keys in place, each MCP interaction correlates cleanly with the telemetry you already collect, and the whole system gets easier to operate and troubleshoot. This is the same argument I keep coming back to: agentic systems only become &lt;em&gt;enterprise&lt;/em&gt; systems when they&amp;rsquo;re as observable, governable, and auditable as every other critical application in the estate. This release moves MCP another step toward that bar.&lt;/p&gt;&#xA;&lt;h2 id=&#34;more-than-stateless&#34;&gt;More Than Stateless&lt;/h2&gt;&#xA;&lt;p&gt;Statelessness earns its headlines. Simpler deployments, better scalability, and lower operational complexity are real wins, and the &lt;a href=&#34;https://modelcontextprotocol.io/specification/draft/changelog&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;changelog&lt;/a&gt;&#xA; is worth reading in full. But the larger enterprise story is the standardized metadata that integrates naturally with the observability and tracing ecosystems organizations already trust.&lt;/p&gt;&#xA;&lt;p&gt;Platform teams want a protocol that fits the infrastructure they already run, and the technologies that win in the enterprise are rarely the ones with the longest feature lists.&lt;/p&gt;&#xA;&lt;p&gt;The claim I&amp;rsquo;ll defend is narrower than &amp;ldquo;MCP is enterprise-ready&amp;rdquo;: two required routing headers and three reserved trace-context keys quietly gave every gateway, trace, and audit log a common vocabulary for MCP traffic. In my experience, that&amp;rsquo;s the kind of unglamorous change that decides whether a protocol makes it out of the proof-of-concept stage.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>💰 Skills vs MCP: What the Token Bill Actually Measures</title>
      <link>https://davidparry.com/blog/2026/07/20/skills-vs-mcp-what-the-token-bill-actually-measures/</link>
      <pubDate>Mon, 20 Jul 2026 09:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/07/20/skills-vs-mcp-what-the-token-bill-actually-measures/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Move deterministic business rules out of the model and into code.&lt;/strong&gt; For the one workload I benchmarked, that single decision cut hosted-model cost by 32% on Claude Opus 4.8 and 76% on GPT-5, ran two to five times faster, and eliminated a silent age-calculation error because an authoritative clock replaced the model&amp;rsquo;s guess. That is the conclusion. Everything below is how I measured it, why the popular “Skills versus MCP” framing is the wrong dividing line, and where the result stops being defensible.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Move deterministic business rules out of the model and into code.&lt;/strong&gt; For the one workload I benchmarked, that single decision cut hosted-model cost by 32% on Claude Opus 4.8 and 76% on GPT-5, ran two to five times faster, and eliminated a silent age-calculation error because an authoritative clock replaced the model&amp;rsquo;s guess. That is the conclusion. Everything below is how I measured it, why the popular “Skills versus MCP” framing is the wrong dividing line, and where the result stops being defensible.&lt;/p&gt;&#xA;&lt;p&gt;The question that sent me down this path came after a conference talk I gave about building MCP servers. Another engineer asked me something blunt: &lt;em&gt;Why are you still talking about MCP when Skills can do all of this?&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;My first reaction was equally blunt. If I worked for a model provider, I might prefer the design that keeps more work inside the model. I work for the organization paying the bill, so I want deterministic business rules to run in ordinary code whenever that is practical.&lt;/p&gt;&#xA;&lt;p&gt;That is my opinion, not evidence of a vendor conspiracy. Providers have good reasons to make models more capable, and customers are free to choose the architecture. Still, the incentive is worth noticing: when a model does more work, customers usually buy more inference. The two hosted models in this benchmark also price output tokens at five to eight times their input-token rate.&lt;/p&gt;&#xA;&lt;p&gt;So I built a small benchmark to see how much that choice mattered. The result supported my architectural instinct, but it also exposed a problem with the original framing of this article. “Skills versus MCP” is not the real dividing line. The real one is &lt;strong&gt;model-executed rules versus code-executed rules&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-i-actually-compared&#34;&gt;What I actually compared&lt;/h2&gt;&#xA;&lt;p&gt;A Skill is a package, not an execution environment. The &lt;a href=&#34;https://agentskills.io/home&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;open Agent Skills specification&lt;/a&gt;&#xA; allows a Skill to contain instructions, reference material, assets, &lt;strong&gt;and executable scripts&lt;/strong&gt;. &lt;a href=&#34;https://help.openai.com/en/articles/20001066&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;OpenAI’s description of Skills&lt;/a&gt;&#xA; likewise says a Skill can include code.&lt;/p&gt;&#xA;&lt;p&gt;MCP is a protocol through which a model-facing client can discover and call tools. An MCP server can contain business logic, but it can just as easily wrap a database, an API, a clock, or a bad nondeterministic service. The protocol itself does not make the result correct.&lt;/p&gt;&#xA;&lt;p&gt;The benchmark therefore compared these specific implementations:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Instruction-only Skill:&lt;/strong&gt; the Skill contains the rules and catalog in Markdown. The model performs the calculations.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;MCP-backed tool:&lt;/strong&gt; a Rust server performs the calculations and returns structured data. The model calls it and formats the response.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Prompt-only baseline:&lt;/strong&gt; the same rules are placed directly in the system prompt and the model performs the calculations.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That is a narrower and more useful comparison: &lt;strong&gt;model-executed rules versus code-executed rules&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;An executable Skill would make a legitimate fourth configuration, but “the Skill contains code” is not enough information. There are two very different ways an agent can use that code. It can run the script in a real software runtime, or it can read the source and attempt to follow the logic itself. The second option is still model-executed business logic. In fact, it may be worse than giving the model concise rules: the source consumes more context, while the model remains free to miss a branch, mishandle a boundary, or improvise around the implementation.&lt;/p&gt;&#xA;&lt;p&gt;Running the script is different. Once ordinary software receives the same validated inputs, it can produce the same authoritative output as the Rust server. The remaining uncertainty sits in the orchestration around it. Will the model load the Skill, notice the script, invoke it instead of doing the calculation itself, construct the command and arguments correctly, and present the returned value without “correcting” it? If script execution is merely suggested in prose, the architecture still depends on the model choosing the deterministic path on every request.&lt;/p&gt;&#xA;&lt;p&gt;If the agent runtime exposes the bundled script as a required, typed capability and reliably routes the request through it, I would expect the token and latency profile to resemble the MCP-backed path. At that point, however, the important win comes from executable code being treated as authoritative, not from the Skill label. If the model is expected to read and mentally execute the bundled source, I would expect more tokens without gaining the guarantee that made the code worth writing.&lt;/p&gt;&#xA;&lt;p&gt;I have not tested either version of that fourth configuration yet. A useful follow-up would measure them separately: &lt;strong&gt;Skill plus enforced script execution&lt;/strong&gt; and &lt;strong&gt;Skill plus source code for the model to interpret&lt;/strong&gt;. Combining those into one “executable Skill” result would hide the architectural difference, but both versions still hit the core economic question: how much material must the model read, reason about, and generate before the code runs? Loading Skill instructions or source, deciding to invoke a script, constructing the command and arguments, and then consuming the result all use tokens. Executable code makes the calculation authoritative; it does not make the orchestration free. My expectation is that asking the model to interpret source will be the most expensive version, while enforced script execution should be closer to MCP. Whether it is cheaper or more expensive than a compact, typed MCP tool call is something the fourth benchmark must measure rather than assume.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-workload&#34;&gt;The workload&lt;/h2&gt;&#xA;&lt;p&gt;The customer record contains a birth date, ZIP code, ordered interests, and a maximum budget. The system must calculate age, classify the customer, filter a small activity catalog, apply discounts, rank eligible activities, and return the best matches.&lt;/p&gt;&#xA;&lt;p&gt;The harness ran all three configurations 15 times on each of three models:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Claude Opus 4.8&lt;/li&gt;&#xA;&lt;li&gt;GPT-5&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;qwen3:30b&lt;/code&gt;, served locally through Ollama&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That produced 135 responses. The harness recorded provider-reported input, output, and total tokens; model and tool calls; end-to-end latency; estimated API cost; and the final text. The Rust server, Skill, prompts, harness, tests, and raw JSON are in the &lt;a href=&#34;https://github.com/davidparry/skill-vs-mcp&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;public benchmark repository&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;There is one important wrinkle. The customer request did &lt;strong&gt;not&lt;/strong&gt; include the current date. The MCP server read the host’s clock, while the instruction-only configurations had no equivalent clock tool. An earlier draft of this article said the date was part of the scenario. It was not.&lt;/p&gt;&#xA;&lt;p&gt;That means the cost and latency comparison uses the same user request, but the later age comparison is not a controlled test of code versus model arithmetic. It also tests access to current state. I discuss those results separately rather than pretending the difference does not exist.&lt;/p&gt;&#xA;&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;&#xA;&lt;p&gt;Every number below is the mean of 15 successful runs. Prices are standard, non-cached API prices at the time of the test. GPT-5 was &lt;a href=&#34;https://developers.openai.com/api/docs/models/gpt-5&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;$1.25 per million input tokens and $10 per million output tokens&lt;/a&gt;&#xA;. Claude Opus 4.8 was &lt;a href=&#34;https://www.anthropic.com/claude/opus&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;$5 and $25&lt;/a&gt;&#xA;, respectively.&lt;/p&gt;&#xA;&lt;p&gt;The “text variation” column is the mean pairwise character-sequence difference among the 15 final answers, calculated with Python’s &lt;code&gt;SequenceMatcher&lt;/code&gt;. It measures how different the rendered responses were. It does &lt;strong&gt;not&lt;/strong&gt; measure semantic correctness, business-rule determinism, or auditability.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Claude Opus 4.8&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Config&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Model calls&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Latency (s)&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Text variation&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Instruction-only Skill&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,210&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,193&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,403&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00605&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.02983&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.03588&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;12.5&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;48.5%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;MCP-backed tool&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,071&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;561&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,632&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.01036&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.01403&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.02438&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;9.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;38.8%&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Prompt only&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,131&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,231&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,362&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00566&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.03078&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.03643&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;12.9&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;61.9%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;GPT-5&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Config&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Model calls&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Latency (s)&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Text variation&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Instruction-only Skill&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;802&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,089&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,891&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00100&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.02089&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.02189&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;16.1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;76.8%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;MCP-backed tool&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;963&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;408&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,371&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00120&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00408&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.00529&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;4.8&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;38.7%&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Prompt only&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;783&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,320&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;3,103&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00098&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.02320&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.02418&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;20.8&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;67.8%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;qwen3:30b (local via Ollama on an Apple M4 Max with 128 GB RAM)&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Config&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Model calls&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Latency (s)&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Text variation&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Instruction-only Skill&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;898&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;4,192&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;5,090&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.00&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;43.4&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;76.7%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;MCP-backed tool&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,480&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,086&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,565&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.00&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;10.8&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;58.9%&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Prompt only&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;882&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;5,112&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;5,994&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.00&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;54.4&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;73.2%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;Cost per 1,000 requests&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Model&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;MCP-backed tool&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Instruction-only Skill&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Prompt only&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Tool vs Skill&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Tool vs Prompt&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Claude Opus 4.8&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$24.38&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$35.88&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$36.43&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;32% less&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;33% less&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;GPT-5&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$5.29&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$21.89&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$24.18&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;76% less&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;78% less&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;what-those-numbers-support&#34;&gt;What those numbers support&lt;/h2&gt;&#xA;&lt;p&gt;The tool-backed path sent more input because the request included a tool schema and required a second model call. It still cost less because it used far fewer completion tokens. On these models, for this task, the price difference between input and output was large enough to overwhelm the extra round trip.&lt;/p&gt;&#xA;&lt;p&gt;The same implementation was also faster in this sweep. It reduced mean latency from 12.5 to 9.0 seconds against the instruction-only Skill on Opus, from 16.1 to 4.8 seconds on GPT-5, and from 43.4 to 10.8 seconds on the local model. The local comparison matters because there is no per-token invoice to blame. Generating fewer tokens still consumed less wall-clock time on my hardware.&lt;/p&gt;&#xA;&lt;p&gt;The provider APIs reported completion-token usage; those counts are what I priced. Depending on the provider and model, that usage can include billed reasoning tokens that never appear in the visible answer. It would be inaccurate to say every calculation was literally printed to the user.&lt;/p&gt;&#xA;&lt;p&gt;The text-variation result is interesting but weaker. Tool-backed answers were more alike in all three sets, which makes sense because the model received the same structured result each time. A character-level similarity score is sensitive to headings, wording, and answer length, though. I would not use it as evidence that the business logic is deterministic. For that, I would test the function outputs directly.&lt;/p&gt;&#xA;&lt;p&gt;Most important, this is one synthetic workload with one customer and one catalog. It shows that moving this set of rules into code reduced tokens, latency, and hosted-model cost. It does not prove that an MCP call is always cheaper. A large tool schema, a chatty tool response, network latency, retries, or a tiny calculation could reverse the result.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-clock-result-useful-but-not-a-fair-arithmetic-contest&#34;&gt;The clock result: useful, but not a fair arithmetic contest&lt;/h2&gt;&#xA;&lt;p&gt;All configurations returned the same three activities in the same order:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Guided Nature Walk — $20&lt;/li&gt;&#xA;&lt;li&gt;Mountain Hiking Tour — $45&lt;/li&gt;&#xA;&lt;li&gt;Jazz Club Evening — $60&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The reported age differed:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Config&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Claude Opus 4.8&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;GPT-5&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;qwen3:30b&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;MCP-backed tool&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Instruction-only Skill&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;35 (14/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;33 (12/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Prompt only&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;35 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;33 (14/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The benchmark ran on July 19, 2026, and the customer was born on May 15, 1990, so 36 was correct. The Rust service read that date from the system clock. Opus often behaved as if the year were 2025, and qwen often behaved as if it were 2023. GPT-5 returned the exact benchmark date even though the user request and harness system prompt did not supply it. The data does not tell me whether that date came from model behavior or provider-side context, so calling it a “guess” would go beyond the evidence.&lt;/p&gt;&#xA;&lt;p&gt;This result demonstrates a real production lesson: if an answer depends on current state, give the system an authoritative source for that state. It does &lt;strong&gt;not&lt;/strong&gt; demonstrate that MCP is uniquely able to provide one. A bundled Skill script, a local command, a conventional API, or an MCP server could all read a clock.&lt;/p&gt;&#xA;&lt;p&gt;It also explains why the bad ages did not change the recommendations. Ages 33, 35, and 36 fall into the same age band, generation, and discount tier in this catalog. Near a boundary, the error could matter. A 65-year-old calculated as 62 would miss the senior discount; an 18-year-old calculated as 17 could cross discount and eligibility rules. Those are examples of what the defect &lt;em&gt;could&lt;/em&gt; cause, not outcomes observed in this run.&lt;/p&gt;&#xA;&lt;p&gt;For repeatable testing, the server already has a &lt;code&gt;FixedClock&lt;/code&gt; implementation. The benchmark should use it, or pass the same explicit date to all three configurations. I plan to add that controlled case before making broader correctness claims.&lt;/p&gt;&#xA;&lt;h2 id=&#34;where-i-draw-the-boundary&#34;&gt;Where I draw the boundary&lt;/h2&gt;&#xA;&lt;p&gt;This experiment reminded me of reading a SQL execution plan. Returning the right rows is necessary, but it is not the end of the engineering work. At scale, I also care about the cost of getting those rows, the behavior under failure, and whether I can test the logic without asking a probabilistic model to repeat it.&lt;/p&gt;&#xA;&lt;p&gt;My rule of thumb is now:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Use Skill instructions for judgment, workflow, and reusable operating procedures.&lt;/li&gt;&#xA;&lt;li&gt;Use executable code for calculations, eligibility, prices, policy rules, and access to authoritative state.&lt;/li&gt;&#xA;&lt;li&gt;Use MCP when that executable capability should be a shared, discoverable tool with a stable interface. A bundled Skill script or ordinary service may be simpler when it should not.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That is also what I look for when interviewing engineers who use AI. This is a personal hiring preference, not a universal standard. I care less about whether a candidate can make a model produce working code and more about whether they can explain which parts belong in model reasoning, which parts need a deterministic boundary, and what the choice costs at production volume.&lt;/p&gt;&#xA;&lt;p&gt;The benchmark changed my wording, not my conclusion. “MCP beats Skills” is too broad. The claim I can defend is this: &lt;strong&gt;for this workload, code-executed business rules beat model-executed rules on cost and latency, and an authoritative clock prevented a silent data error.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;That is enough to influence an architecture. It is not enough to declare a universal winner.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;em&gt;Reproduce the experiment or challenge it in the &lt;a href=&#34;https://github.com/davidparry/skill-vs-mcp&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;skill-vs-mcp repository&lt;/a&gt;&#xA;. The raw 15-run result set used here is included.&lt;/em&gt;&lt;/p&gt;&#xA;</content:encoded>
    </item>
  </channel>
</rss>
