Mindket

What is an agent harness? The loop, tools and checks around a model

A black banner titled "Harness engineering" over the line "The loop, tools and guardrails around an agent".

An agent harness is the code that runs a model as an agent. It calls the model in a loop, runs the tools the model asks for, decides what goes into its context on each turn and checks every action against rules such as permissions and turn limits. The same model acts differently in Claude Code, Codex or your own script because their harnesses differ.

In this lesson

  • Draw your agent as a model plus its harness, and list what your code adds to every request.
  • Keep the tool list and the opening messages fixed for a whole conversation so the cached prefix survives.
  • Turn rules your reviewers repeat into lints, tests and approval gates the agent cannot skip.
  • When a new model ships, take your harness apart one piece at a time and keep only what still earns its place.
All courses
On this page
  1. A harness in under 80 lines of Python
  2. What Codex puts in front of the model before your first word
  3. Harness engineering: OpenAI's product with no hand-written code
  4. What the Claude Agent SDK and OpenAI Agents SDK build for you
  5. Every part of a harness is a bet about the model
  6. Ryan Lopopolo, who wrote OpenAI's post on harness engineering, on context and plumbing
  7. Common mistakes
  8. Try it yourself
  9. Check yourself
  10. Frequently asked questions
  11. Sources

A harness in under 80 lines of Python

Put one model in Claude Code, in Codex and in a script of your own, and you get three different agents. The weights are the same. What changes is the code around them. OpenAI's Codex team and Anthropic both call it the harness: the loop that calls the model, the tools it may use, what it sees on each turn and the checks around every action.

The sketch below is about as small as an agent harness gets while still holding all of those parts. It is the lesson's own code, wrapped around two tool definitions copied from Anthropic's customer service cookbook, and it was not run for this lesson. The comments name the part each line belongs to. The Claude API agent lesson builds the loop itself step by step, so here it is only labelled.

import anthropic

from orders import cancel_order, get_order_details  # the cookbook's stand-in functions

client = anthropic.Anthropic()
MODEL = "claude-opus-5-5"
MAX_TURNS = 10                     # check: end a run that never stops
NEEDS_APPROVAL = {"cancel_order"}  # permission: tools that change something

# tools: two of the cookbook's three definitions, as written there
TOOLS = [
    {
        "name": "get_order_details",
        "description": "Retrieves the details of a specific order based on the order ID. Returns the order ID, product name, quantity, price, and order status.",
        "input_schema": {
            "type": "object",
            "properties": {
                "order_id": {
                    "type": "string",
                    "description": "The unique identifier for the order.",
                }
            },
            "required": ["order_id"],
        },
    },
    {
        "name": "cancel_order",
        "description": "Cancels an order based on the provided order ID. Returns a confirmation message if the cancellation is successful.",
        "input_schema": {
            "type": "object",
            "properties": {
                "order_id": {
                    "type": "string",
                    "description": "The unique identifier for the order to be cancelled.",
                }
            },
            "required": ["order_id"],
        },
    },
]
FUNCTIONS = {"get_order_details": get_order_details, "cancel_order": cancel_order}


def allowed(name, args):
    if name not in NEEDS_APPROVAL:
        return True
    return input(f"Allow {name} {args}? (y/n) ").strip().lower() == "y"


def run_agent(task):
    messages = [{"role": "user", "content": task}]  # context: all the model sees next turn
    for _ in range(MAX_TURNS):  # the loop
        response = client.messages.create(
            model=MODEL, max_tokens=2048, tools=TOOLS, messages=messages
        )
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason != "tool_use":
            return "".join(b.text for b in response.content if b.type == "text")
        results = []
        for block in response.content:
            if block.type != "tool_use":
                continue
            if allowed(block.name, block.input):
                fn = FUNCTIONS[block.name]
                output, failed = fn(**block.input), False
            else:
                output, failed = "The user declined this action.", True
            results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": str(output),
                "is_error": failed,
            })
        messages.append({"role": "user", "content": results})
    return "Stopped: the turn limit was reached."


print(run_agent("Please cancel order O1 for me."))
  1. Tools. The two definitions tell the model what it can ask for. The model never runs anything itself: your code looks the name up in FUNCTIONS and calls it.
  2. Context. messages holds everything the model sees on the next call. Each turn appends the reply and the tool results, so every request is larger than the one before.
  3. Permissions. allowed() holds cancel_order until a person types y. A refusal goes back as a tool result with is_error set, so the model can tell the user rather than retry blindly.
  4. Checks. MAX_TURNS ends a run where the model keeps asking for tools. Anthropic's Building effective agents says it is common to add stopping conditions, such as a maximum number of iterations, to keep control, and the API leaves that count to you.
  5. The loop. Call the model, run what it asked for, append the results and call again, until a reply arrives with no tool call in it.

What Codex puts in front of the model before your first word

Michael Bolin of OpenAI took the Codex CLI apart in Unrolling the Codex agent loop, published on 23 January 2026, and called the agent the harness that makes use of the model. Before your message reaches the model, Codex has already assembled six other parts of the request. The table lists them in order.

One line in that build is a permissions fact worth knowing before you add tools. The sandbox that the permissions message describes applies only to the shell tool Codex provides. Tools from MCP servers are not sandboxed by Codex and have to enforce their own guardrails.

The order matters for speed and cost. Each request resends the whole conversation, and the old prompt is an exact prefix of the new one, which is what lets prompt caching reuse earlier work. Change the tools, the model, the sandbox, the approval mode or the working directory mid-conversation and the cache misses. So Codex appends a new message to record a changed setting instead of editing an early one. An early version of its MCP support listed tools in an unstable order and caused misses until it was fixed. The prompt caching lesson shows the same prefix rule on Claude.

Context is the other budget. When a conversation passes auto_compact_limit, Codex calls the Responses API's /responses/compact endpoint and continues from the shorter list of items it returns.

How Codex's agent harness builds the first request, from OpenAI's Unrolling the Codex agent loop (23 January 2026)
PartRoleWhat it holds
instructionssystem or developerThe model_instructions_file from ~/.codex/config.toml when set, else the base instructions bundled for the model
toolsNot a messageCodex's own tools such as shell and update_plan, the Responses API's web_search, and tools from your MCP servers
Permissions instructionsdeveloperThe sandbox for the shell tool, when to ask before running a command and which folders Codex may write
Developer instructionsdeveloperThe optional developer_instructions value from config.toml
User instructionsuserAGENTS.override.md and AGENTS.md from $CODEX_HOME and from each folder between the project root and the working directory, up to 32 KiB by default, plus skill metadata
Environment contextuserThe working directory and the shell
Your messageuserThe task you typed

Harness engineering: OpenAI's product with no hand-written code

In Harness engineering, published on 11 February 2026, Ryan Lopopolo of OpenAI described five months of building an internal product in which no person typed a line of code. Codex CLI generated the first scaffold on GPT-5 in late August 2025, and even the first AGENTS.md was written by Codex. The figures are the team's own.

Progress was slow at first, and the post puts that down to an underspecified environment, not a weak model. So when something failed, the engineers asked "what capability is missing, and how do we make it both legible and enforceable for the agent?" One early answer was about instructions. A single big AGENTS.md crowded out the task, rotted and could not be checked by a machine, so it became a table of contents of roughly 100 lines pointing into a docs/ folder. Linters and CI jobs check that folder for freshness and cross-links, and a doc-gardening agent opens fix-ups for stale pages. The post shows the layout:

OpenAI's harness engineering experiment, as its team reported it

1M

lines of code in the repository after five months, none typed by a person

Source: OpenAI: Harness engineering (read November 6, 2026)

1,500

pull requests opened and merged, with three engineers driving Codex at first

Source: OpenAI: Harness engineering (read November 6, 2026)

3.5

pull requests per engineer per day on average

Source: OpenAI: Harness engineering (read November 6, 2026)

6+

hours a single Codex run worked on one task, often overnight

Source: OpenAI: Harness engineering (read November 6, 2026)

AGENTS.md
ARCHITECTURE.md
docs/
├── design-docs/
│   ├── index.md
│   ├── core-beliefs.md
│   └── ...
├── exec-plans/
│   ├── active/
│   ├── completed/
│   └── tech-debt-tracker.md
├── generated/
│   └── db-schema.md
├── product-specs/
│   ├── index.md
│   ├── new-user-onboarding.md
│   └── ...
├── references/
│   ├── design-system-reference-llms.txt
│   ├── nixpacks-llms.txt
│   ├── uv-llms.txt
│   └── ...
├── DESIGN.md
├── FRONTEND.md
├── PLANS.md
├── PRODUCT_SENSE.md
├── QUALITY_SCORE.md
├── RELIABILITY.md
└── SECURITY.md
  • Rules became code. Custom linters and structural tests hold each business domain to fixed layers (Types, Config, Repo, Service, Runtime, UI) and enforce structured logging, naming and file size limits. The lint messages are written to tell the agent how to fix what it broke.
  • The agent got eyes on the app. Each git worktree boots its own copy of the app, wired to the Chrome DevTools Protocol, with logs and metrics the agent can query in LogQL and PromQL.
  • Cleanup went on a schedule. The team used to spend every Friday cleaning up after the agents. Now "golden principles" live in the repository, and background Codex tasks open small refactoring pull requests against them.

The post says this way of working depends on that repository's structure and tooling and should not be assumed to generalize without similar investment.

What the Claude Agent SDK and OpenAI Agents SDK build for you

Nothing above needs a framework. Anthropic's Building effective agents, from December 2024, advises starting with the model API directly, since many patterns take a few lines. If you do use a framework, understand the code underneath, because wrong assumptions about it are a common source of customer error. A note at the top of the post now says much of the tooling it describes has changed since then.

Each of the two large SDKs is a harness you configure instead of writing. Anthropic renamed the Claude Code SDK to the Claude Agent SDK in September 2025 because the agent harness that powers Claude Code can power many other kinds of agents. Its Python package bundles the Claude Code CLI and gives Claude the full Claude Code toolset by default. OpenAI's Agents SDK is provider-agnostic and names agents, sandbox agents, tools, guardrails, human in the loop, sessions and tracing as its core parts. The matrix sorts both by part of the harness, from their READMEs.

The Claude Agent SDK lesson and the OpenAI Agents SDK lesson set each one up. A sensible split: your own loop for one narrow task with a tool or two, an SDK once you need file tools, approvals and hooks.

Which parts of the harness you write and which an SDK ships
Your own loopClaude Agent SDKOpenAI Agents SDK
Runs the loopNoYesYes
File and shell tools built inNoYesWith a SandboxAgent
Approval before a tool runsYou write itYesYes
Checks around each stepYou write themHooksGuardrails
Models it drivesWhatever API you callClaudeOpenAI models and 100+ others

Sources: anthropics/claude-agent-sdk-python README (read November 6, 2026), openai/openai-agents-python README (read November 6, 2026)

Every part of a harness is a bet about the model

Prithvi Rajasekaran of Anthropic's Labs team put the rule plainly in Harness design for long-running application development, in March 2026. Every component in a harness encodes an assumption about what the model can't do on its own, and those assumptions go stale as models improve. His own harness shows it. Claude Sonnet 4.5 wrapped up work early as its context filled, a habit the post calls context anxiety, so the harness cleared the context and handed state over in files. Opus 4.5 largely stopped doing that, and the resets came out. With Opus 4.6, the sprint structure that split the work into chunks came out too.

The method is the part to copy. Cutting the harness back hard in one pass lost quality and left it unclear which pieces were load-bearing. Removing one component at a time and reviewing the result did work. The long-running agents lesson builds a harness of that kind, one that spans many sessions.

Ryan Lopopolo, who wrote OpenAI's post on harness engineering, on context and plumbing

Ryan Lopopolo wrote the harness engineering post and has since moved to Google Cloud as a principal engineer, by his X bio. On 7 March 2026, replying to a question on X, he split the work into two jobs. One is to get every nonfunctional requirement into a place the agent can pull into context. The other is to keep the demands on that context small. In this lesson's terms, the docs folder and the lints do the first job. The short AGENTS.md and quiet tool output do the second.

On 15 April he posted that the new Agents SDK was what he had wished for in September 2025, and singled out its bring-your-own-sandbox design. His advice was to let the SDK do the plumbing and spend the effort on your users. That fits the matrix: take the loop, tools and approvals ready-made, and keep your time for the context and checks only your product knows.

His keynote on the experiment, with a Q&A after it, is on the AI Engineer channel. His harness engineering repository collects his posts and notes for agents to read.

Harness engineering is two parts: put all the nonfunctional requirements into a space where they can be pulled into context + minimize the demands on context

Ryan Lopopolo, Author of OpenAI's harness engineering post, now a principal engineer at Google Cloud · X (read November 6, 2026)
Ryan Lopopolo (@_lopopolo) on X, 7 March 2026, replying to @_buggles: based on current model architecture, context is a hard constraint and the scarce resource. Harness engineering is two parts: put the nonfunctional requirements where they can be pulled into context, and minimize the demands on context.
Ryan Lopopolo on X, 7 March 2026
Ryan Lopopolo (@_lopopolo) on X, 15 April 2026: the new Agents SDK is what he wished he had in September 2025. It leans into the truth that all agents are coding agents, with a bring your own sandbox design. Let the SDK do the plumbing and focus your energy on your users.
Ryan Lopopolo on X, 15 April 2026
Ryan Lopopolo's keynote on harness engineering, with a Q&A after it, on the AI Engineer channel on YouTube.

Screenshots of X's own embeds of the posts, taken on 6 November 2026, with the time in UTC. X cuts the first post short with Show more. Select one to open the post on X.

Common mistakes

  • Comparing two models while the harness changes too. A model in Codex and the same model in your script see different instructions, tools and context.
  • Growing one long instruction file until it crowds out the task and nobody can tell which rules still hold.
  • Adding or reordering tools in the middle of a conversation, which breaks the cached prefix for every later call.
  • Assuming one sandbox covers every tool. In Codex it covers the built-in shell tool, and MCP servers police themselves.
  • Leaving a rule in prose when a lint or a test could enforce it on every change.
  • Keeping a workaround long after the model that needed it has been replaced.

Try it yourself

The tools in the sketch were written for people reading a cookbook. Tools for AI agents, the next lesson, shows how to write them for a model.

  • Run the sketch with its own task, answer n at the prompt and read what the model tells the user after the refused tool result.
  • Set MAX_TURNS to 1, ask for the status of order O2 and then its cancellation, and note which part of the harness ended the run.
  • Open your ~/.codex/config.toml or your project's AGENTS.md and mark which rows of the Codex table you already control.
  • Pick one rule your team repeats in code reviews and write it as a lint whose error message tells the agent how to fix the code.

Check yourself

  1. In Codex's agent harness, which tools does the sandbox in the permissions message cover?

    Show answer

    B. Only the shell tool that Codex provides The sandbox applies to the Codex-provided shell tool, and tools from MCP servers have to enforce their own guardrails.

  2. What did OpenAI's team put in place of one big AGENTS.md?

    Show answer

    A. A short AGENTS.md of about 100 lines that points into a docs folder The big file crowded out the task and went stale, so it became a table of contents for a docs folder that lints keep current.

  3. Which change in the middle of a Codex conversation causes a prompt cache miss?

    Show answer

    C. Changing the tools available to the model A cache hit needs an exact prefix, and the tool list sits near the start of every request.

  4. A stronger model ships. What does Anthropic's harness design post suggest you do with your harness?

    Show answer

    D. Remove one component at a time and check the result Each component encodes an assumption about what the model cannot do, and removing them one at a time shows which still carry weight.

Frequently asked questions

What is harness engineering?

It is the work of shaping everything around a coding agent so the agent can do reliable work, a term Ryan Lopopolo of OpenAI used in his February 2026 post. The engineers on his team did not type the code. They built the docs, lint rules and feedback loops the agent worked inside.

Is Claude Code an agent harness?

Yes. Claude is the model inside, and everything else you meet in a session, from permission prompts to the CLAUDE.md file it reads at the start, belongs to the harness. That is why a Claude Code session and a plain API call to the same model behave so differently.

Is an agent harness the same as an agent framework?

Not quite. A framework or SDK is a harness someone else wrote and you configure. A loop of your own around a model API is a harness too, just a smaller one that you maintain.

Why does the same model act differently in two coding agents?

Each harness builds a different request around it. Codex, for one, adds its own instructions, a permissions message, AGENTS.md files and the working directory before your message, and each agent offers a different set of tools.

Where do guardrails fit in a harness?

They are the checks around each action: sandboxes, permission rules, approval prompts and hooks. The approval prompt before cancel_order in this lesson is the smallest kind, and the agent guardrails lesson covers the rest.

Sources

  1. OpenAI: Harness engineering, leveraging Codex in an agent-first world (read November 6, 2026)https://openai.com/index/harness-engineering/
  2. OpenAI: Unrolling the Codex agent loop (read November 6, 2026)https://openai.com/index/unrolling-the-codex-agent-loop/
  3. Anthropic Engineering: Building effective agents (read November 6, 2026)https://www.anthropic.com/engineering/building-effective-agents
  4. Anthropic Engineering: Building agents with the Claude Agent SDK (read November 6, 2026)https://www.anthropic.com/engineering/building-agents-with-the-claude-agent-sdk
  5. Anthropic Engineering: Harness design for long-running application development (read November 6, 2026)https://www.anthropic.com/engineering/harness-design-long-running-apps
  6. Claude Cookbooks: Creating a customer service agent with client-side tools (read November 6, 2026)https://github.com/anthropics/claude-cookbooks/blob/main/tool_use/customer_service_agent.ipynb
  7. anthropic-sdk-python: ToolResultBlockParam (is_error) (read November 6, 2026)https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/types/tool_result_block_param.py
  8. anthropics/claude-agent-sdk-python README (read November 6, 2026)https://github.com/anthropics/claude-agent-sdk-python
  9. openai/openai-agents-python README (read November 6, 2026)https://github.com/openai/openai-agents-python
  10. lopopolo/harness-engineering on GitHub (read November 6, 2026)https://github.com/lopopolo/harness-engineering
  11. Ryan Lopopolo on X: harness engineering is two parts (read November 6, 2026)https://x.com/_lopopolo/status/2030356581792293212
  12. Ryan Lopopolo on X: let the SDK do the plumbing (read November 6, 2026)https://x.com/_lopopolo/status/2044507506949689726
  13. AI Engineer on YouTube: Harness Engineering, Ryan Lopopolo (read November 6, 2026)https://www.youtube.com/watch?v=am_oeAoUhew

Share this lesson

Agents built on a harness

Browse the agents listed on Mindket, each with a preview, and see what a model with good tools and rules can do.

Browse agents

This lesson as Markdown · Harness engineering: build the code around an AI agent · All courses