Blog • AI / Agent
AI Coding Agent Race 2026: What Are Claude Code, Codex, OpenCode, and DeepSeek Agent Actually Competing For? Agent Harness Architecture Through JSON Tool Calling
Leaderboards still ask who scores higher. Open a terminal and what actually edits files, runs tests, and talks to MCP is not that natural-language reply.
Claude Code, Codex, OpenCode, and DeepSeek Agent all appear to sell a Coding Agent that can do the work. The real gap is the Agent Harness: who owns a JSON Tool Calling hop from arguments to side effects.
This article covers:
- Which extra layer a Coding Agent has over autocomplete
- What the Agent Harness owns, and what the model does not
- Why JSON Tool Calling is the shared wire across all four
- Seven hops in one tool call: where each product puts the gate
- How to inspect arguments and schema locally in JSONNote
Keep this in mind: The 2026 race is not “who autocompletes the next line more accurately.” The model emits a tool call; the arguments are JSON. The harness decides whether it can parse, whether to ask, which sandbox runs it, and how the result is written back. Claude Code pins hooks around every call. Codex makes sandbox and approval the default boundary. OpenCode makes models and permissions swappable config. DeepSeek Agent splits the whole pipeline into plugins.
The real race is not autocomplete
Autocomplete answers “what goes on the next line.” A Coding Agent answers “how this goal gets finished in a real repo”: read context, pick a tool, edit code, run a command, read the result, decide again.
All four UIs look like chat. The loop underneath is almost the same:
User goal
→ Agent loop
→ LLM emits tool_call (JSON arguments)
→ Harness parses / validates / gates / executes
→ Tool result returns to context
→ Agent decides again
The model reasons and picks “who to call next.” The harness decides which tools the model can see, whether arguments count as valid, whether a risky action needs a human, what the tool actually does, how the session is recorded, and what counts as done.
Feature lists will keep converging. Everyone has Skills, MCP, subagents, and permission switches. The difference is where control sits. For product positioning, see AI Coding Agents Are More Than Autocomplete: What Are Codex, Claude Code, OpenCode, and DeepSeek Harness Competing On?. For how the loop turns, see How Do AI Coding Agents Work?. This article only splits one hop: what the harness does after JSON Tool Calling leaves the model.
What an Agent Harness is
By 2026 the layer has a name. A source-code anatomy of eleven production coding agents calls it Harness Engineering: an agent is a model plus a harness—the runtime that couples an LLM to the world through a loop, tools, context, safety controls, orchestration, and extension surfaces.
Another paired experiment asks it more bluntly: does swapping the harness let the same model solve more tasks? Average scores often stay close; repository tasks and contest tasks can move in opposite directions; cost and cancel rates change. The takeaway is not “the vendor-native harness always wins.” It is “you are buying a completion path, not a leaderboard point.”
So the four are not competing over who shipped one more button. They are competing over who organizes the tool contract, the execution boundary, and the human veto more clearly.
| Layer | Owns | Does not own |
|---|---|---|
| Model | Pick a tool, fill JSON arguments, read the result, decide again | Write disk, run the shell, reach the network, authorize |
| JSON contract | What arguments and results look like | Whether this call should happen |
| Agent Harness | Visible tools, validation, approval, sandbox, write-back, session | Inventing the business goal for you |
For how the stack is layered, see 2026 AI Agent Stack: How Do LLM, MCP, Function Calling, and JSON Schema Fit Together?.
The shared wire: JSON Tool Calling
Tool Calling (also called Function Calling) is not another name for “the model can write code.” It is a structured call: the model picks a name from the tools you declared, then generates JSON that should match the parameter contract.
On an OpenAI-compatible API, the tool definition’s parameters field is itself JSON Schema:
{
"type": "function",
"function": {
"name": "run_tests",
"description": "Run the project test suite and return a structured summary.",
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"suite": { "type": "string", "enum": ["unit", "integration", "e2e"] },
"path": { "type": "string", "minLength": 1 }
},
"required": ["suite"]
}
}
}
When the model returns, arguments is often a string, not an already-parsed object:
{
"id": "call_7f21",
"type": "function",
"function": {
"name": "run_tests",
"arguments": "{\"suite\":\"e2e\",\"path\":\"tests/checkout.spec.ts\"}"
}
}
So the harness does at least two things: parse the string into valid JSON, then check fields, types, and enums against the schema you declared. Missing quotes, extra commas, and undeclared keys all happen on this hop.
MCP plugs the same wire into an external Server: inputSchema / outputSchema is still JSON Schema, and tools/call is still one structured call. More Servers mean more descriptions and schemas in the model context, and more gates the harness must hold. For how safety is split, see MCP Security Vulnerabilities Explained.
The battlefield: seven hops in one tool call
Spread the four products out and the feature names differ. The pipe looks the same. The race is who decides each hop:
-
1
Present
Which tools and which slice of schema enter this request. A tool the model cannot see cannot be called.
-
2
Emit
The model picks a name and generates an arguments string. This hop only means it intends to call.
-
3
Parse
The string becomes an object. Invalid JSON must fail before it reaches the execution layer.
-
4
Validate
Check required fields, types, enums, and additionalProperties against JSON Schema.
-
5
Gate
allow / deny / ask. A valid shape is not an authorization.
-
6
Execute
Run for real in a sandbox, a workspace, or full host privileges. Side effects start here.
-
7
Write back
The result becomes text or JSON, is recorded in the session, and is fed into the next turn. Failures need structure too—not just “something went wrong.”
All four walk these seven hops. The difference is where hooks land, how tight the default sandbox is, whether the model can be swapped, and whether the pipe itself can be replaced.
Claude Code: hooks before and after the call
Claude Code makes the main agent stronger: Skills, MCP, subagents, and CLAUDE.md all wrap the same Claude Agent. What actually bites JSON Tool Calling is the deterministic hook around every call.
In the official lifecycle, every tool call in the loop goes through PreToolUse and PostToolUse. You can also intercept when a permission prompt appears (PermissionRequest) or decide after a failure whether a retry is allowed (PermissionDenied / PostToolUseFailure).
The hook reads event JSON, not prose. It can rewrite inputs, deny the call, ask for confirmation, or after success add logs, run a formatter, and push new context back. It cannot undo a side effect that already happened—PostToolUse arrives too late.
Permission rules match deny → ask → allow, and deny wins. A hook that returns allow only skips the interactive prompt; it cannot override an enterprise-managed deny list. Even with bypassPermissions, a PreToolUse that returns deny still blocks the call.
The engineering trade-off is clear: a probabilistic agent plus a deterministic gate. Run tests after edits, block dangerous commands, require approval on protected paths—the model should not have to “remember” those every time.
Best when workflows are stable and you want to pin Skills, subagents, and hooks on the main loop, packaging experience as reusable units.
Codex: sandbox and approval before execution
Codex (including Codex CLI) pushes the question to the execution layer. Once an agent edits files, runs the shell, installs dependencies, and touches the network and credentials, it must answer two different things at once: the capability boundary, and whether this action is allowed now.
| Mechanism | Question it answers | Typical knobs |
|---|---|---|
| Sandbox | What can it technically touch or change? | read-only / workspace-write / danger-full-access |
| Approval | Is this action allowed right now? | untrusted / on-request / never |
The default story is least privilege: tighten the environment first, open it when needed. An untrusted directory starts read-only. After you trust the workspace, the common preset is workspace-write plus on-request—reads, writes, and routine commands inside the workspace run automatically; leaving the workspace or touching the network needs approval. Network is off by default and must be turned on explicitly.
Newer versions describe filesystem and network with a permission profile. Do not mix that with the older sandbox_mode. The names change. The split does not: one dial is “what can it touch,” the other is “may it do this now.”
Best when side effects are large, the execution trail must be auditable, and isolation plus approval are first-class. Open-source repo: openai/codex.
OpenCode: models are swappable, permissions are config
OpenCode’s differentiation is not “yet another Skills syntax.” The default stance is: models are swappable. MIT-licensed, terminal-first, many providers and local models. Flexibility lives mainly in configuration—provider, model, permission, agent—not in exploding the harness core into a plugin bus.
Permissions have moved from the early boolean tools into permission: each action is allow, ask, or deny. You can write rules by tool name, command pattern, or whether the call leaves the workspace (external_directory). The last match wins. A subagent can be stricter than the main agent—for example a review role that denies edit outright.
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"bash": {
"*": "ask",
"git *": "allow",
"git push *": "deny"
},
"edit": "allow",
"external_directory": "deny"
}
}
It typically wins on: multi-model as a default, not a vendor extra; an open-source license and community; “switch the model” as a first-class operation. The cost is clear: execution boundaries and plugin depth are not automatically the number-one selling point. You are buying a model-swappable harness, not the heaviest sandbox product, and not an “Everything is a plugin” platform core.
Best when you distrust a single vendor, or you need to switch models—including local weights—inside the same loop.
DeepSeek Agent: the pipeline itself is a plugin
DeepSeek pushes the question one layer down. The public preview product is DeepSeek Harness (dsh). The external contract is the Agent interface; the default implementation is a replaceable agent-loop. The slogan is Everything is a plugin—model, tools, Skills, session, sandbox, storage, loop, scheduling, and UI can all be swapped.
What lines up with Tool Calling is the tool pipeline, not another chat box. A ToolDefinition in the registry carries typed parameters and outputs. The model only sees name, description, and parameters. execute, timeouts, concurrency flags, and UI presenters must not leak onto the request.
Every call walks a fixed waterfall:
tools/pre-execute → allow / deny / ask
monotonic guards → 只收紧,不能再放行
tools/execute → 真正派发(可包超时 / 重试)
tools/post-execute → 检查或替换结果
finalizeContent → 定义自己的收尾
tools/result → 冻结后的权威结果
It can use native Function Calling, or PTC (the reserved run_code transport; subcalls still enter the same pipeline). Concurrency is classified per call: exclusive calls form a barrier, parallel-safe calls enter a bounded pool; events are still committed in model order.
Best when you want the runtime itself as a platform, not just a stronger assistant. For the model layer, see What Is DeepSeek V4-Pro?.
Same JSON, four gates
The same tool-call JSON hits a different gate in each product:
| This hop | Claude Code | Codex | OpenCode | DeepSeek Agent |
|---|---|---|---|---|
| Tool surface shown to the model | Built-in tools + Skills + MCP; schemas can load lazily | Session toolbox + project notes (AGENTS.md) | Built-ins + MCP + per-agent trim | A scoped registry projects ToolSchema through an allowlist |
| Before arguments reach execution | Hooks can read the full tool-event JSON | Sandbox capability first, then approval policy | permission rules match tool name and input | After parse, the pre-execute waterfall and monotonic guards |
| How a human vetoes | PermissionRequest; deny beats a hook allow | on-request / untrusted; can go to a reviewer | ask; a subagent can be stricter | An ask decision is a first-class pipeline result |
| Where side effects happen | Local tools + post-hooks | OS-level sandbox; no network by default, workspace writable | Local execution, git snapshots, undo | A replaceable sandbox plugin |
| How results are written back | PostToolUse / failure hooks add context | JSONL events, easy to audit in CI | LSP diagnostics can re-enter the loop | tools/result freezes, then the session log |
The last row is not a ranking. You are buying a more usable main agent, a more controllable execution environment, a model-swappable config layer, or a composable runtime.
For the product view of the control map, see the four architecture centers. For who owns safety after MCP widens the tool surface, see When AI Agents Start Attacking the Internet: How JSON Becomes a Security Boundary.
Why you still inspect JSON Schema
No matter how strong the four harnesses are, dirty parameters in the execution layer become incidents. Model-filled arguments are a soft constraint: arguments may be broken JSON, miss fields, or add undeclared keys.
A valid schema is not an authorization. Both snippets below pass a loose schema that only requires a sql field. Only the tight side turns the operation into an enum and writes the identifier as a pattern:
{
"loose": {
"type": "object",
"properties": { "sql": { "type": "string" } },
"required": ["sql"]
},
"tight": {
"type": "object",
"additionalProperties": false,
"properties": {
"op": { "type": "string", "enum": ["get_user_by_id"] },
"user_id": { "type": "string", "pattern": "^[a-z0-9-]{8,36}$" }
},
"required": ["op", "user_id"]
}
}
For how to write the contract, and how JSON Mode differs from Strict Schema, see Why Does AI Need JSON Schema? Structured Output, Function Calling, and JSON Schema Explained, and Why Do AI Agents Need JSON Schema? Tool Calling to Structured Output Explained.
Split a tool call in JSONNote
The most useful debug material is usually a slice of JSON: the tool definition, model arguments, hook events, the deny reason. You can split all of that locally in the browser. You do not need to upload the repo.
-
1
First check whether arguments is valid JSON
Unescape the string and drop it into JSON Format. Missing commas, trailing commas, and single quotes blow up here.
-
2
Validate against the declared schema
Put the tool’s parameters / inputSchema and the parsed object into JSON Schema. See whether a field is missing, a type is wrong, or an undeclared key was added.
-
3
Diff “what you approved” against “what it is now”
Drop last week’s tool list and today’s list into JSON Diff and look specifically for silent description and schema edits.
-
4
Need a colleague to see it? Use hash share
The data stays in the URL fragment and never hits the server. See Share JSON with a URL Hash.
FAQ
What are Claude Code, Codex, OpenCode, and DeepSeek Agent actually competing for?
Not a feature list, and not who autocompletes the next line more accurately. The fight is the Agent Harness: who owns a JSON Tool Calling hop from parse and contract checks through approval, sandboxed execution, and writing the result back.
Which matters more, the Agent Harness or the model?
The model reasons and picks the next step. The harness decides which tools are visible, whether arguments are valid, whether a risky action needs a human, and where side effects happen. Swapping the harness does not guarantee a higher average score, but the completion path, cost, and cancel rate change. You are choosing control, not a score.
Which of the four should I pick?
There is no single winner. Pin deterministic hooks and permissions on a main agent → Claude Code. Treat sandbox and approvals as the default execution boundary → Codex. Switch models and avoid lock-in → OpenCode. Make the loop, tool pipeline, and sandbox replaceable plugins → DeepSeek Agent (DeepSeek Harness).
Does JSON Tool Calling include authorization?
No. The model picking a tool name and filling arguments only means it intends to call. Whether the call is blocked, which sandbox it runs in, and whether the result returns to the model are harness decisions. Without a tool, if permission is denied, or if JSON validation fails, nothing happens on disk.
Why still validate JSON Schema yourself?
Model-filled arguments are a soft constraint. arguments is often a string and may miss fields, use the wrong type, or add undeclared keys. A valid schema is not an authorization. Production still needs parse plus validation, then permissions and the sandbox. No matter how strong the four harnesses are, dirty parameters in the execution layer become incidents.
Conclusion
The 2026 Coding Agent race looks like a model war. Inside a repo, it is a harness war.
The model emits JSON. The harness decides whether that JSON can become a side effect. The four compete over control of the same pipe: Claude Code pins hooks, Codex tightens the execution boundary, OpenCode unlocks the model, and DeepSeek Agent turns the runtime itself into plugins.
Features will keep converging. What you should actually watch is who decides after a tool call leaves the model.
Next: paste one tool call’s arguments into JSONNote