OpenHarness: The Python Harness That Turns Any LLM Into an Agent

A deep dive into the control plane behind tool use, memory, permissions, and multi-agent coordination.

9 min read • View on GitHub • More from HKUDS

A tiny engine sits inside a larger mechanical frame of levers, valves, ledgers, and routed lines. The image explains that OpenHarness treats the model as a replaceable component and the harness as the real system boundary.
OpenHarness puts the control plane around the model.
Key Takeaways

OpenHarness is interesting because it refuses the usual agent story. The model is not the product here. The harness is. That means permissions, tool calls, memory, continuation logic, and swarm coordination are first-class, not bolt-ons.

OpenHarness (oh) is an ultra-lightweight alternative to Claude Code with pure Python implementation

HKUDS/OpenHarness README, Project Documentation · OpenHarness README

That design choice makes sense for a repo coming out of HKUDS, the University of Hong Kong Data Science group. The code reads like an attempt to compress a messy agent stack into something inspectable, portable, and easy to swap underneath a different model provider.

港大刚刚最新开源了一款比Claude Code轻44倍、兼容Claude Code skills和plugins生态的智能体框架:OpenHarness核心代码1万多行,砍掉了遥测、OAuth、重型React UI,只保留了核心Harness架构模型负责思考,Harness给模型提供手、眼、记忆和安全边界。

AIGCLINK, AI Blogger · Sina coverage

What OpenHarness actually ships

The repository is split by responsibility, not by hype. engine owns the loop, coordinator defines agent behavior, mcp standardizes external tools, swarm keeps long-lived teams, and frontend/terminal gives the whole thing a console face.

src/openharness/
  api/
  engine/
  coordinator/
  mcp/
  swarm/
frontend/terminal/
ohmo/
.claude/skills/

That layout is the point. OpenHarness keeps the state machine visible, so you can see where a message becomes a tool call, where a tool call becomes a result, and where a result becomes another turn. The engine does not hide the harness behind a single opaque prompt.

Inside the agent loop

The loop is a state machine. The harness decides when to continue, when to stop, and when to deny.

The engine is where the harness earns its name. QueryEngine tracks conversation history and cost, while ConversationMessage and its content blocks normalize provider-specific payloads into one local language. has_pending_continuation then decides whether the last tool result should flow back into the model for another pass.

A close-up shows a locked gate opening just enough for a tool to pass through, while a second gate remains shut behind it. The image explains that approvals are a control point, not an afterthought, and that tool execution happens only after governance checks.
Approval sits in front of action, not beside it.

That is why this does not feel like a dumb wrapper. It is a governed loop that keeps enough structure to be inspectable and enough freedom to keep going when the work is not done.

How it keeps agents on a leash

OpenHarness puts approval before action. Tool breadth matters less than the place where the system stops and asks a human, because that is where autonomy becomes usable instead of reckless. Memory persistence and continuation control are part of the same bargain: the harness remembers enough to continue, but not so much that it forgets who is in charge.

Why the terminal is part of the product

A terminal window sits on a desk like a miniature stage, with command cards, status lights, and branching paths rising from it. The image explains that the terminal UI is part of the control plane, giving the user visibility into what the agent is doing.
The terminal is where trust becomes visible.

The React plus Ink terminal is not decoration. It gives the agent loop a visible surface, which matters when the system is making decisions over several turns and the user needs to know what just happened. In a terminal, status, prompts, approvals, and task handoffs share the same space, so the control plane stays close to the operator.

MCP, skills, and the long tail of tool use

A central hub feeds several distinct tool modules through clean sockets and rails, while a tangled bundle of cables sits off to one side. The image explains the split between standardized integration paths and lightweight skills-based extension.
Capability is organized, not just registered.

OpenHarness's tool story is deliberately hybrid. MCP gives it a standardized way to reach out to external systems, while markdown skills make extension feel lightweight instead of plugin-heavy. The result is not just more tools, but a cleaner way to organize capability.

OpenHarness vs Claude Code and agent SDKs

This is where the trade-off becomes obvious. Claude Code is the finished product experience, agent SDKs are building blocks, and OpenHarness is the harness layer you can inspect, trim, and extend without rebuilding the whole control plane.

AxisOpenHarnessClaude CodeAgent SDKs
Primary unitHarness first. The model is swappable.Product first. The model family is embedded.Library first. You assemble the experience.
ExtensibilityMCP, skills, swarm, and permissions.Constrained by the product architecture.Anything you can build on top.
GovernanceExplicit permission checks and continuation control.Managed inside the app experience.Mostly your responsibility.
UI postureTerminal-native with a React plus Ink surface.Polished first-party CLI experience.No UI opinion unless you add one.
Best fitTeams that want a transparent control plane.Users who want a finished agent tool.Developers who want primitives, not opinions.

The trade-off is clear. You give up some product polish, but you get less coupling, more visibility, and a control layer that can survive model churn. That is a good deal if your real problem is not chatting with a model, but running one inside a governed workflow.

What this design says about agents

The larger lesson is simple. As models get easier to swap and cheaper to improve, the harness becomes the durable layer. OpenHarness is interesting because it makes that layer explicit, legible, and small enough to study.