ANALYSIS · AGENT TOOLING 15 min read

The harness is not the model. What you actually pick when you install an agentic CLI

Installing an agentic CLI is two choices at once. The first is the harness, the machinery around the model. The second is the model family. On 1 September 2026 I checked who actually welds those two into one decision. The answer runs against the common version. Anthropic and Google do. OpenAI, DeepSeek, Cline, Aider, OpenCode and Pi leave the provider open.

Two layouts side by side. On the left, stitched: a harness block welded to a single model family block, captioned one decision. On the right, separated: the same harness block above one interface and three provider blocks, captioned two decisions, the second reversible

A harness is the scaffolding around a model. The agent loop, the tool set, context management, permissions, sessions. It turns a model that answers messages into something that reads a repository and runs the tests on its own. The model is the engine inside. The harness calls it and is not the same thing as it.

That distinction sounds academic right up to the day you want to change models. Then it turns out the decision was made months earlier, when you picked a tool, and that nobody wrote it down at the time. Every number and quote below comes from a source you can open, and I checked all of them on 1 September 2026. Where I could not confirm something, I say so.

Four things we call by one name

Start with the word, because "harness" is not my coinage. Anthropic uses it and defines it. The Claude Code glossary settles the whole argument in one line: "Claude Code is the harness; Claude is the model inside it." The company blog adds the definition: a harness is "the software scaffolding around a model: the loop, tools, context management, and guardrails that turn raw intelligence into a working agent."

So a harness is a category, not a product. The products are four, and Anthropic's own docs line them up in one table: "The Agent SDK, the CLI, the Client SDK, and Managed Agents each fit different needs." They are worth separating, because in conversation they travel as a single word.

Layer What you get What you write yourself
Client SDK An API client for seven languages. You send a request, you handle a response. The whole tool loop. The docs put it plainly: "implementing the tool loop yourself".
Agent SDK A ready harness as a library: "the same tools, agent loop, and context management that power Claude Code". Plus hooks, subagents, MCP, permissions and sessions. Your application logic. The loop runs in your own process.
CLI The same harness as a terminal program, built for interactive work. Nothing. From another language you run it as a subprocess with the -p flag.
Managed Agents A "pre-built, configurable agent harness that runs in managed infrastructure". Orchestration stays on Anthropic's side. Configuration. You can host the sandbox yourself. The loop, no.

Four entries, four answers to one question: how much machinery do you take ready-made. None of them says anything about which model sits at the far end. That is a separate axis, and it is where this gets interesting.

Who actually welds the harness to the provider

The received view says that tools built by model vendors are closed to their own models by nature. I checked that against the documentation of eight tools rather than against writeups about them. In that form it does not hold.

Tool Licence Model providers per its own docs
Claude Code Closed. LICENSE.md: "© Anthropic PBC. All rights reserved." Claude only. All six deployment routes serve the same family.
Gemini CLI Apache 2.0 Gemini only. Three sign-in paths, all of them Google's.
Codex CLI Apache 2.0 Built-in provider ids openai, ollama and lmstudio, plus a custom endpoint.
DeepSeek Harness MIT Its model layer is described as "provider-neutral". Anthropic and OpenAI are added from config.
Cline Apache 2.0 README: "Cline is not locked to a single AI provider".
Aider Apache 2.0 "Aider can connect to most LLMs". Last commit 22 May 2026.
OpenCode MIT Docs: "75+ LLM providers", local models included.
Pi MIT The pi-ai README: "Unified multi-provider LLM API (OpenAI, Anthropic, Google, etc.)".

Read on 1 September 2026. What comes out of it is not what the thesis promised. Anthropic and Google are the exceptions here, not the rule. OpenAI built its own CLI and left a ready socket in it for Ollama and LM Studio. DeepSeek built its own harness and described the model layer as neutral toward the provider. Welding a harness to one model family is a call two specific companies made. It is not a property of tools built by model vendors.

What exactly is closed on Anthropic's side

Precision matters here, because a short and false version circulates. It goes: "the Claude Agent SDK only works with Anthropic's API." That is untrue. The comparison table on the deployment page lines up six options: Claude for Teams and Enterprise, the Anthropic Console, Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform and Microsoft Foundry. Below the table sits a seventh entry, a self-hosted gateway that stands in front of the other routes.

So you have full freedom on infrastructure. You pick who invoices you, which region serves the inference and which credential you hold. That same table has no row for the model. All six routes lead to the Claude family. The opus and sonnet aliases resolve to Opus and Sonnet builds on every one of those providers. The lock is on the model family, not on the endpoint and not on the cloud. That is the whole difference and it is worth stating exactly.

There is one more door, and it deserves an honest description, because the first comment under any piece like this reads "I run Claude Code on GLM every day". You can. The model configuration docs say that "your provider or gateway defines the model names, so Claude Code passes any string through without checking it". Anthropic raises the case itself on its gateway page and withholds support for it: it "doesn't support routing Claude Code to non-Claude models through any gateway". That is a support boundary, not a technical block. It works exactly as long as any unsupported configuration works, which is until the next change on the other side.

Two more facts complete the picture. The library exists in two languages, and the docs state it flatly: "The SDK is available as a library for Python and TypeScript only." From another language you run the CLI as a subprocess. On licensing, the whole thing sits under Anthropic's commercial terms rather than an open licence. If you are rolling this out inside a company, it is worth reviewing what activating Claude Code actually covers, because that is where product documentation ends and the deployment work starts.

Pi splits it into packages

Pi is an open agent toolkit under the MIT licence. The repository earendil-works/pi was created in August 2025 and had 100,481 stars when I queried the GitHub API on 1 September 2026. Its LICENSE file, the only one in the repository, opens with "Copyright (c) 2025 Mario Zechner".

Instead of one stitched product, Pi ships packages. The README lists five: pi-ai as a unified API across providers, pi-agent-core as the runtime with the loop and tool calling, pi-coding-agent as a ready CLI, pi-tui as the terminal layer and pi-telemetry as vendor-neutral telemetry contracts. Worth adding that this is the README's list, not the contents of the directory. There are ten directories under packages/, and three the README leaves out, pi-client, pi-protocol and pi-server, are published on npm.

Mario Zechner wrote it, the same person who made libGDX. How he describes ownership today matters, because the shorthand "Mario Zechner's Pi" has already drifted from the facts. Pi belongs to Earendil, the company Armin Ronacher, the author of Flask, founded with Colin Daymond Hanna. Zechner is a partner there and writes that he is in charge of Pi decisions together with Armin and Colin. The move was announced on 8 April 2026, and the old badlogic/pi-mono path still redirects to the new repository.

WORK WITH ME

This is what I do hands-on: advising on AI strategy and building agents that survive the demo.

The proof that it works came from DeepSeek

The strongest case for splitting these layers is not an argument. It is a commit in somebody else's repository.

DeepSeek opened its own harness on 13 August 2026. It is called DeepSeek Harness, it is MIT-licensed, and it is built on an architecture where everything is a plugin. In its monorepo, under packages/llm/, next to the adapter for DeepSeek's own models sits a second package: @deepseek-ai/dsh-llm-pi-ai. Its README describes it as a multi-provider adapter in which every route is served "through @earendil-works/pi-ai". Open the package.json and the dependency is right there, at version ^0.84.2.

A company that makes its own models built its own harness, and for the multi-provider layer it took somebody else's library instead of writing one. That package's README explains why: a route the pi-ai catalog does not describe gets declared by hand, so "an OpenAI-compatible gateway or self-hosted server is configuration, not a code change". That is exactly the benefit this whole separation is for, written down by a model vendor.

The scope of that observation belongs next to it. The package is at version 0.1.2-alpha.4 and its own description calls it a design-verification twin of the DeepSeek adapter. It is not that harness's default production route. It is evidence that the seam exists and that somebody used it. While I was there I checked the circulating claim that OpenClaw embeds an agent session through createAgentSession() from pi-agent-core, and it does not hold. OpenClaw has its own function by that name in its own tree, and the only Earendil package it ships is pi-tui. The link to Pi is real but of a different kind: its THIRD_PARTY_NOTICES file says portions of OpenClaw were adapted from Pi under the MIT licence.

Five layers you rewrite when you change provider

Here the theory ends. If your harness is stitched to one provider, moving touches five layers. What follows are specifics from three native APIs: Anthropic's, OpenAI's and Google's.

The call layer. The three APIs do not even agree on where the request lives. Anthropic POSTs to a fixed /v1/messages, OpenAI to /v1/chat/completions or /v1/responses, and Gemini puts the model name in the URL path and has a separate method for streaming. The cap on response length is called max_tokens, max_completion_tokens, max_output_tokens or generationConfig.maxOutputTokens, depending on who you are calling.

The tool format. The same tool is spelled four ways. Anthropic puts the JSON Schema in input_schema, flat beside the name. OpenAI's Chat Completions wraps it in a function object with a parameters field. OpenAI's own Responses API flattens that back out. Gemini nests the whole thing at tools[].functionDeclarations[].parameters and accepts a selected subset of OpenAPI there rather than full JSON Schema.

The return trip is worse, meaning the part where you hand the model a tool's output. Anthropic expects a tool_result block with tool_use_id inside a user message. OpenAI's Chat Completions wants a whole message with role tool and a tool_call_id. The Responses API wants a function_call_output item with a call_id. Gemini has no tool role and expects a functionResponse part carrying the function name. Four different operations for one thing.

Context. The system prompt alone is spelled at least four ways: a system parameter at Anthropic, a system or developer message in Chat Completions, an instructions field in Responses and a systemInstruction object at Google. Reasoning is sharper still. What comes back is an opaque seal from whoever issued it: signature at Anthropic, encrypted_content at OpenAI, thoughtSignature at Google. You replay it byte for byte to the vendor that issued it. Nobody else can read it.

Permissions. "Force a tool call" is tool_choice: {"type": "any"} at Anthropic, tool_choice: "required" at OpenAI and toolConfig.functionCallingConfig.mode: "ANY" at Google. At the edges the mapping stops being one to one. Gemini has a VALIDATED mode nobody else has, and per-request safety settings that Anthropic does not expose at all. The policy itself, meaning the rules that say what an agent may not do without a human, carries over unchanged. How that policy is written into an API call has to be rewritten.

Telemetry. Renaming prompt_tokens to input_tokens is the easy half. The hard half is where the number changes meaning. Anthropic keeps cache traffic out of the input total and asks you to add input_tokens, cache_creation_input_tokens and cache_read_input_tokens. Google and OpenAI fold the cache hit into the prompt total. A mapping built on field names reports a different volume, and a different cost, for an identical request. A cost dashboard survives the move only in appearance, and a fault like that looks exactly like a retry that returns OK and hides the problem.

What a shared interface will not absorb

A shared library such as pi-ai or LiteLLM takes most of that work off you. LiteLLM's docs promise that "every response follows the OpenAI Chat Completions format, regardless of provider", and swaps like input_schema to function.parameters really are mechanical. The absorption is not total, though, and the seams show in the library's own issue tracker. A tool schema using $ref or anyOf has to be rewritten before Gemini will accept it. Flattening a reasoning turn into OpenAI's shape has repeatedly lost the blocks the next request needs.

There is also a part no adapter carries across, because the format has no place for it. A specification describes the shape of a request and nothing beyond that. Whether your prompts still behave, whether tools get called when they should and whether your evals come out the same are questions a schema does not answer. You find out by running your own suite against the new model. Keep that in mind when you estimate what a move costs, especially since the model is the cheapest line in an agent quote and rewriting the integration is not.

When stitching is the right call

Separation has a price and it needs saying out loud, or this whole piece reads as an advert for Pi. A team with no intention of changing model family pays for separation in complexity and gets nothing back. A shared interface is one more dependency, one more layer to debug and one more place where a vendor's new feature lands later than it does in that vendor's own SDK.

A ready harness from a vendor gives you more out of the box. Hooks, subagents, permissions and sessions are tested against that vendor's own model rather than against an average of all of them. If you are building on one family and you know it, that is the right choice. I do the same where it makes sense: a dashboard that reads the artifacts a multi-agent run leaves behind is built directly on one harness and does not pretend to be portable.

The difference is whether the decision was made or merely inherited from a tool. Deliberately choosing one model family is a strategy. The same state of affairs with no decision behind it is debt, and you learn about it in the worst possible month.

A question worth asking once a year

I am not arguing for a switch. I am arguing for separating these two decisions in your head, because while they are stuck together every model change looks like a migration and nobody even prices it.

Test it on yourself with one question. How much work would it take tomorrow to run the same task on a different model? An answer of "we would have to rewrite the integration" means you did not choose the model. The harness chose it for you on the day you installed it.

Turn that into one page in your project docs. List the five layers from this piece and note beside each how many days of work a change would cost. Come back to that page in a year. If the numbers grew, you already know which layer to separate first.

Frequently asked questions

How does a harness differ from a model?

The model answers messages. The harness is the machinery around it: the agent loop, tools for reading and writing files, context management, permissions and sessions. Anthropic records the split in the Claude Code glossary in one sentence: "Claude Code is the harness; Claude is the model inside it." A harness is a category rather than a specific product. The same harness can in principle call different models, if it was built that way.

Does the Claude Agent SDK only work with Anthropic's API?

No. The comparison table on Anthropic's deployment page lines up six options, including Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform and Microsoft Foundry. So you do get a choice of infrastructure, region and billing. Every one of those routes serves models from the Claude family, though. The lock is on the model family rather than on the endpoint or the cloud vendor, and that difference is worth remembering.

Can you run Claude Code on a model outside the Claude family?

Technically yes, through your own gateway and a base URL variable. The model configuration docs say the model names are defined by your provider or gateway, and that Claude Code passes any string through without checking it. Anthropic raises the case itself and withholds support, stating that it does not support routing Claude Code to non-Claude models through any gateway. Treat it as an unsupported configuration that works until the next change on the other side.

What is Pi and who is behind it?

Pi is an open agent toolkit under the MIT licence, developed as a monorepo in the repository earendil-works/pi. Instead of one product it ships packages: a unified API across providers, an agent runtime with the loop and tool calling, a terminal UI library, a ready coding CLI and telemetry contracts. Mario Zechner, the creator of libGDX, wrote it. Pi now belongs to Earendil, the company founded by Armin Ronacher with Colin Daymond Hanna, where Zechner is a partner and shares the decisions about the project.

Is separating harness from model worth it in a small team?

Not always. If a team has no intention of changing model family, separation costs complexity and returns nothing. A ready harness from a vendor is tested against that vendor's own model and gets new features sooner. What matters is that choosing one family is a decision rather than a side effect of installing a tool. Write it down and revisit it when pricing changes or a model appears that is clearly better at your task.

SP

Szymon Paluch

ex-CTO · AI Strategy

Not sure what changing models would cost you?

We will go through your stack layer by layer and you will leave with a number in days of work rather than a hunch.

Book a call
Related posts
The AI GTM stack: five stages you can buy, four handoffs you define
A public report written with AI: nine errors you can see in the file
What an AI agent costs and what actually drives the quote