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. 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 https://harness-router.vercel.app/ · Architecture architecture · Integrations integrations · MCP mcp · Python API 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 .claude/README.md . 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 .omp/README.md . 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 .antigravity/README.md . 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 https://protocol-lattice.github.io/harness-router/hooks/deepseek/ . 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.