cd /news/ai-agents/how-hn-harness-router Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-142455] src=protocol-lattice.github.io β†— pub= topic=ai-agents verified=true sentiment=Β· neutral

How HN: Harness Router

Protocol-Lattice released Harness Router, a decision layer that intercepts an agent harness's tool-selection loop and routes the next tool call through a route cache, a fast path called Jev, and optional bounded MCTS search. Harness Router is framework-agnostic and integrates via each host's native control point, using SessionStart for tool discovery and PreToolUse for routing in Codex and PreToolUse in Claude Code, where a different confident tool proposal is denied to trigger re-planning. MCTS runs against a side-effect-free simulator so real tools are not executed during search, and the hook does not execute tools or rewrite their arguments.

read7 min views2 publishedSep 30, 2026

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

Agent harnesses already have a tool-selection loop:

goal
  β”‚
  β–Ό
model reasoning
  β”‚
  β–Ό
tool selection
  β”‚
  β–Ό
tool execution
  β”‚
  β–Ό
result
  β”‚
  └──────────────► next iteration

Harness Router inserts a decision layer inside that loop:

model proposes / harness reaches tool-selection point
                         β”‚
                         β–Ό
                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚ Harness Router  β”‚
                 β”‚                 β”‚
                 β”‚ cache β†’ Jev     β”‚
                 β”‚        β†’ MCTS   β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
                    routing decision
                          β”‚
                          β–Ό
                    next tool call

The host harness still owns the agent.

Harness Router owns the tool-selection decision.

It does not replace the harness’s:

This separation is intentional.

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 decision

Repeated compact decisions can be served without invoking a decision provider.

Jev is the normal decision path for tool-selection choices.

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.

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 uses:

SessionStart for tool discovery,PreToolUse for routing,

Codex
  β”‚
  β–Ό
tool proposal
  β”‚
  β–Ό
PreToolUse
  β”‚
  β–Ό
Harness Router
  β”‚
  β”œβ”€β”€ cache
  β”œβ”€β”€ Jev
  └── optional MCTS
  β”‚
  β”œβ”€β”€ same / fallback / error ──► allow
  β”‚
  └── different confident tool ─► deny
                                      β”‚
                                      β–Ό
                                  re-plan
                                      β”‚
                                      β–Ό
                                  next call

Install:

curl -fsSL https://raw.githubusercontent.com/Protocol-Lattice/harness-router/main/scripts/install_hook.py \
  | python3 - --provider codex

Claude Code uses:

Claude Code
    β”‚
    β–Ό
tool proposal
    β”‚
    β–Ό
PreToolUse
    β”‚
    β–Ό
Harness Router
    β”‚
    β”œβ”€β”€ cache β†’ Jev β†’ optional MCTS
    β”‚
    β”œβ”€β”€ same / fallback ──────► normal flow
    β”‚
    └── different tool ───────► deny + re-plan
                                  β”‚
                                  β–Ό
                              next call

The hook does not execute tools or rewrite their arguments.

See the Claude integration guide.

curl -fsSL https://raw.githubusercontent.com/Protocol-Lattice/harness-router/main/scripts/install_hook.py \
  | python3 - --provider claude

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 decision

The active tool set is restored on fallback, timeout, low confidence, malformed output, or router failure.

See the ohmypi integration guide.

curl -fsSL https://raw.githubusercontent.com/Protocol-Lattice/harness-router/main/scripts/install_hook.py \
  | python3 - --provider ohmypi

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-plan

The adapter uses the live conversation tool inventory and does not invent a fallback catalog when discovery fails.

See the Antigravity integration guide.

curl -fsSL https://raw.githubusercontent.com/Protocol-Lattice/harness-router/main/scripts/install_hook.py \
  | python3 - --provider antigravity

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-plan

The 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.

python3 hooks/deepseek/install.py
dsh --patch .dsh/harness-router.patch.yml

See the DeepSeek integration guide.

Harness Router is also available as a native MCP server.

Start it with:

harness-router-mcp

Available 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.

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

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
import asyncio

from 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())

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 action
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.

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

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/lint

Common categories include:

inspect Β· mutate Β· execute Β· verify Β· git Β· browser Β· memory Β· network Β· finish

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.

For agents without automatic hook integration:

skills/harness-router/SKILL.md

Skill mode is explicit:

agent
  β”‚
  β–Ό
invoke routing skill
  β”‚
  β–Ό
Harness Router
  β”‚
  β–Ό
next tool

Use hooks when routing should be embedded in the host lifecycle. Use the skill when routing should be invoked selectively.

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:

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

Harness Router is alpha.

The project focuses on one boundary in an agentic system:

current harness state
        β”‚
        β–Ό
  tool-selection point
        β”‚
        β–Ό
  Harness Router
        β”‚
        β–Ό
     next tool

The 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

MIT.

── more in #ai-agents 4 stories Β· sorted by recency
── more on @protocol-lattice 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/how-hn-harness-route…] indexed:0 read:7min 2026-09-30 Β· β€”