harness-router
harness-router
Harness Router
A decision layer embedded into the harness tool-selection loop.
Harness Router intercepts the host harness at its native control point, evaluates the available tools, and routes the next tool with cache, Jev, and optional MCTS.
Website· Architecture· Integrations· MCP· Python API
---
The idea
Agent harnesses already have a tool-selection loop:
goal
│
▼
model reasoning
│
▼
tool selection
│
▼
tool execution
│
▼
result
│
└──────────────► next iterationHarness Router inserts a decision layer inside that loop:
model proposes / harness reaches tool-selection point
│
▼
┌─────────────────┐
│ Harness Router │
│ │
│ cache → Jev │
│ → MCTS │
└────────┬────────┘
│
routing decision
│
▼
next tool callThe host harness still owns the agent.
Harness Router owns the tool-selection decision.
It does not replace the harness’s:
- reasoning,
- user intent handling,
- argument generation,
- permissions,
- approvals,
- sandboxing,
- credentials,
- execution,
- result handling.
This separation is intentional.
---
Architecture
Decision path
Every routing request follows the cheapest applicable path first:
routing request
│
▼
┌─────────────┐
│ Route cache │
└──────┬──────┘
│ miss
▼
┌─────────────┐
│ Jev │
│ fast route │
└──────┬──────┘
│
ambiguous /
downstream-dependent
│
▼
┌─────────────┐
│ MCTS │
│ bounded │
│ local search│
└──────┬──────┘
│
▼
routing decisionCache
Repeated compact decisions can be served without invoking a decision provider.
Jev
Jev is the normal decision path for tool-selection choices.
MCTS
MCTS is an optional escalation path for decisions where the best immediate action depends on possible downstream state.
MCTS operates against a side-effect-free simulator. Real tools are not executed during search.
---
Integrations
Harness Router is framework-agnostic, but integrations use the native control point available in each host.
The control point determines how directly Router can influence the next tool.
Codex
Codex uses:
Codex
│
▼
tool proposal
│
▼
PreToolUse
│
▼
Harness Router
│
├── cache
├── Jev
└── optional MCTS
│
├── same / fallback / error ──► allow
│
└── different confident tool ─► deny
│
▼
re-plan
│
▼
next callInstall:
curl -fsSL https://raw.githubusercontent.com/Protocol-Lattice/harness-router/main/scripts/install_hook.py \
| python3 - --provider codex---
Claude Code
Claude Code uses:
Claude Code
│
▼
tool proposal
│
▼
PreToolUse
│
▼
Harness Router
│
├── cache → Jev → optional MCTS
│
├── same / fallback ──────► normal flow
│
└── different tool ───────► deny + re-plan
│
▼
next callThe hook does not execute tools or rewrite their arguments.
See the Claude integration guide.
Install:
curl -fsSL https://raw.githubusercontent.com/Protocol-Lattice/harness-router/main/scripts/install_hook.py \
| python3 - --provider claude---
ohmypi
ohmypi exposes a stronger control point through its active tool surface.
The integration can call `setActiveTools()` before the next provider request:
agent turn
│
▼
before_agent_start
│
▼
Harness Router
│
├── cache
├── Jev
└── optional MCTS
│
▼
setActiveTools([selected])
│
▼
next provider request
│
▼
model selects from controlled tools
│
▼
tool execution
│
▼
next routing decisionThe active tool set is restored on fallback, timeout, low confidence, malformed output, or router failure.
See the ohmypi integration guide.
Install:
curl -fsSL https://raw.githubusercontent.com/Protocol-Lattice/harness-router/main/scripts/install_hook.py \
| python3 - --provider ohmypi---
Antigravity
Antigravity uses its native `PreToolUse` hook.
Antigravity
│
▼
tool proposal
│
▼
PreToolUse
│
▼
live tool inventory
│
▼
Harness Router
│
├── cache → Jev → optional MCTS
│
├── same / fallback ──────► allow
│
└── different tool ───────► deny + re-planThe adapter uses the live conversation tool inventory and does not invent a fallback catalog when discovery fails.
See the Antigravity integration guide.
Install:
curl -fsSL https://raw.githubusercontent.com/Protocol-Lattice/harness-router/main/scripts/install_hook.py \
| python3 - --provider antigravity---
DeepSeek Harness
DeepSeek Harness reaches Router through its supported Codex hook bridge.
DeepSeek Harness
│
▼
tool proposal
│
▼
tools/pre-execute
│
▼
dsh-hooks-codex
│
▼
Harness Router
│
├── same / fallback / error ──► allow
│
└── different tool ───────────► deny + re-planThe command-hook bridge does not provide a faithful live tool registry, so this integration requires a supplied catalog and fails open when a valid routing context is unavailable.
Install:
python3 hooks/deepseek/install.py
dsh --patch .dsh/harness-router.patch.ymlSee the DeepSeek integration guide.
---
MCP
Harness Router is also available as a native MCP server.
Start it with:
harness-router-mcpAvailable tools:
| Tool | Purpose | | ----------- | -------------------------- | | route | Fast next-tool routing | | route\_mcts | Bounded multi-step routing |
Example:
[mcp_servers.harness-router]
command = "harness-router-mcp"MCP provides an explicit routing interface for hosts that do not use automatic hook integration.
---
Installation
Requires Python 3.11+.
uv tool install --force --with 'mcp>=2,<3' \
'git+https://github.com/Protocol-Lattice/harness-router.git@main'Set the OpenRouter key:
export OPENROUTER_API_KEY="your-key"Default decision model:
typesafe/jev-1.13---
CLI
Route a tool-selection decision directly:
har route \
--goal "Fix the failing parser test" \
--observation "Failure points to src/parser.py" \
--tools-json '[
{
"name": "read_file",
"description": "Read a repository file",
"category": "inspect",
"risk": "low"
},
{
"name": "search_code",
"description": "Search repository source",
"category": "inspect",
"risk": "low"
},
{
"name": "run_tests",
"description": "Run tests",
"category": "verify",
"risk": "low"
}
]'Example result:
{
"tool": "read_file",
"confidence": 0.93,
"fallback": false
}---
Python API
import asynciofrom harness_router import (
HarnessState,
JevToolRouter,
OpenRouterConfig,
OpenRouterJevProvider,
RiskLevel,
RoutingConfig,
ToolDescriptor,
)
async def main():
provider = OpenRouterJevProvider.from_config(OpenRouterConfig())
router = JevToolRouter(provider, RoutingConfig())
tools = [
ToolDescriptor(
name="read_file",
description="Read a repository file",
category="inspect",
risk=RiskLevel.LOW,
),
ToolDescriptor(
name="search_code",
description="Search repository source",
category="inspect",
risk=RiskLevel.LOW,
),
]
try:
decision = await router.route(
HarnessState(
goal="Fix the failing parser test",
observation="Failure points to src/parser.py",
),
tools,
)
print(decision.tool)
print(decision.confidence)
finally:
await provider.aclose()
asyncio.run(main())
---
MCTS
Use `route_mcts` when the immediate choice depends on possible downstream outcomes.
current state
│
▼
policy prior
│
▼
local simulator
│
├── state
├── candidate tools
├── transition
├── reward
└── terminal condition
│
▼
best first actionExample:
mcts = MCTSToolRouter(
simulator,
policy_router=router,
config=MCTSConfig(
simulations=64,
max_depth=4,
max_policy_evaluations=1,
),
)The simulator should model possible transitions rather than execute real tools.
---
What Router decides
Harness Router is designed for known alternatives at a tool-selection point.
Typical decisions:
Keep these in the main planner:
The intended division is:
harness / planner
│
goal + state
│
▼
Harness Router
│
next tool
│
▼
argument generation
│
▼
permissions
│
▼
execution
│
▼
result
│
└────────► next iteration---
Large tool registries
Routing is particularly useful when many tools overlap semantically.
Hierarchical routing can reduce the decision surface:
current state
│
▼
category route
│
┌─────────────┼─────────────┐
▼ ▼ ▼
inspect mutate verify
│ │ │
read/search write/patch test/lintCommon categories include:
`inspect` · `mutate` · `execute` · `verify` · `git` · `browser` · `memory` · `network` · `finish`
---
Safety and failure behavior
Routing is not authorization.
A high-confidence routing decision means:
This is the strongest candidate from the supplied tool set.
It does not mean:
This action is authorized.
The host harness remains responsible for:
Harness Router does not bypass those controls.
Where the host integration supports it, routing failures fail open: if discovery, the router, or the decision provider is unavailable, the original host behavior continues.
---
Skill mode
For agents without automatic hook integration:
skills/harness-router/SKILL.mdSkill mode is explicit:
agent
│
▼
invoke routing skill
│
▼
Harness Router
│
▼
next toolUse hooks when routing should be embedded in the host lifecycle. Use the skill when routing should be invoked selectively.
---
When to use Harness Router
Harness Router is useful when:
If there are only a few obvious tools, routing can add unnecessary overhead.
Evaluate the whole agent loop, not router latency in isolation:
---
Development
git clone https://github.com/Protocol-Lattice/harness-router.git
cd harness-router
python -m pip install -e ".[dev]"Run checks:
pytest
ruff check .
mypy---
Project status
Harness Router is alpha.
The project focuses on one boundary in an agentic system:
current harness state
│
▼
tool-selection point
│
▼
Harness Router
│
▼
next toolThe host harness remains in control of the rest of the agent lifecycle.
---
A decision layer embedded into the harness tool-selection loop. Cache → Jev → MCTS
License
MIT.