Skip to main content
Glama

Choose the next tool inside an existing agent loop.

Harness Router takes a goal, the current observation, and a set of available tools, then returns a tool choice, confidence, or a fallback to the host planner. It uses Jev through OpenRouter for discrete decisions, caches reusable choices, and supports optional Monte Carlo Tree Search (MCTS) over simulated outcomes.

The host harness generates arguments, checks permissions, executes the tool, and processes its result. Router supplies the next-tool decision at the lifecycle boundary the host exposes.

Status: alpha · Python: 3.11+ · License: MIT

Website · Quick start · Integrations · MCP · Python API · MCTS

Quick start

Install the CLI and optional MCP support with uv:

uv tool install --force --with 'mcp>=2,<3' \
  'git+https://github.com/Protocol-Lattice/harness-router.git@main'

export OPENROUTER_API_KEY="your-key"

The package exposes har, harness-router, and harness-router-mcp. har and harness-router are aliases for the same CLI. The default provider model is typesafe/jev-1.13.

Route a choice among tools:

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"}
  ]'

An illustrative response:

{
  "tool": "read_file",
  "category": "inspect",
  "confidence": 0.93,
  "fallback": false,
  "fallback_reason": null,
  "provider": "openrouter",
  "provider_requests": 1
}

Use the selected tool through your harness's normal execution path. When fallback is true, return control to the planner and inspect fallback_reason. The CLI example supplies descriptors only; Router does not implement those tools.

Related MCP server: TypeSafe MCP

How it works

goal + observation + last action + available tools
                       │
                       ▼
               cached decision?
                       │ miss
                       ▼
                   Jev choice
                       │
                       ├── confident choice ──► next tool
                       ├── uncertain choice ──► planner fallback
                       └── optional simulator ─► MCTS ─► next tool
                                                      │
                                                      ▼
                                    host arguments, permissions, execution
                                                      │
                                                      ▼
                                             new observation

For large registries, Jev can choose a category first and then a tool within that category. The default library configuration considers this above 24 tools and uses an input-size estimate to decide whether hierarchy is worthwhile.

JevToolRouter.route() performs cached Jev routing. MCTS is a separate search path: invoke it explicitly, or configure a graph provider for the shared hook bridge. Search predicts tool outcomes through a simulator and returns the first action.

Integrations

The adapters connect Router to each host's available lifecycle callbacks. Current hook integrations precompute a next-tool decision before a model request or after a tool result. PreToolUse validates that stored decision locally, without making another Jev request.

Harness

Installer provider

Decision boundary and behavior

Codex

codex

Discovers tools at SessionStart; precomputes at UserPromptSubmit and PostToolUse; a confident mismatch at PreToolUse requests a re-plan.

Claude Code

claude

Uses the same discovery, prompt, result, and validation callbacks; a confident mismatch requests a re-plan.

ohmypi

ohmypi

Reads the runtime tool registry, routes at before_agent_start and tool_result, and uses setActiveTools() to expose the selected tool.

Antigravity

antigravity

Uses PreInvocation to expose or seed a decision, PostToolUse to update it, and PreToolUse to validate it.

DeepSeek Harness

deepseek

Uses the dsh-hooks-codex pre/post execution bridge with a supplied catalog and goal context.

The shared Stop hook can request another step when a fresh, confident next-tool decision remains. Its default decision age limit is 15 seconds.

Install project hooks

Install the package and set OPENROUTER_API_KEY first. The executable and key must be available in the environment of the harness process.

From a local checkout, install into a target project:

git clone https://github.com/Protocol-Lattice/harness-router.git
cd harness-router

python3 scripts/install_hook.py \
  --provider claude \
  --project /path/to/project \
  --source .

# Complete the current Claude Code adapter installation.
cp .claude/hooks/pre_decision.py .claude/hooks/stop.py \
  /path/to/project/.claude/hooks/

Replace claude with the provider from the table. Only Codex and Claude Code need the extra copy step. For Codex, use .codex in the copy command and install at the target Git repository root. The current installer copies the shared hooks but omits the Codex and Claude Code pre_decision.py and stop.py adapter scripts; the copy step supplies those registered callbacks.

The installer merges hook configuration, backs up changed files as *.harness-router.bak, and adds provider-specific generated files to .gitignore. Also ignore .harness-router/, which stores shared session state. Start a new harness session after installation.

--provider both selects Codex and Claude Code. --provider all selects every supported harness. Supply the two adapter scripts for each selected Codex or Claude Code integration.

ohmypi installation requires Bun to install its development dependencies and run the extension type check. --skip-ohmypi-deps copies the files without that setup. Start ohmypi from the project root so it discovers .omp/extensions.

For DeepSeek Harness, supply the actual tool descriptors in .dsh/harness-router-tools.json and start with:

dsh --patch .dsh/harness-router.patch.yml

The bridge needs a goal from the hook payload or an existing session decision to precompute the next choice. Missing catalog or goal context leaves normal host behavior in place. Adapter READMEs provide inventory and host setup details; their older descriptions of routing inside PreToolUse predate the shared precomputation flow described here.

Hook configuration and session state

The shared bridge uses these environment variables:

Variable

Default

Purpose

HARNESS_ROUTER_BIN

harness-router on PATH

CLI executable for routing in installed projects.

HARNESS_ROUTER_PREDECISION_TIMEOUT

4

Precomputation deadline in seconds.

HARNESS_ROUTER_POSTTOOL_TIMEOUT

4

Post-tool bridge deadline in seconds.

HARNESS_ROUTER_PREDECISION_THRESHOLD

0.85

Minimum confidence for enforcing a stored choice.

HARNESS_ROUTER_PREDECISION_TTL

30

Stored-choice age limit in seconds for validation hooks.

HARNESS_ROUTER_PREDECISION_CACHE_TTL

300

Shared state-cache lifetime in seconds.

HARNESS_ROUTER_PREDECISION_CACHE_SIZE

512

Maximum shared state-cache entries per session.

HARNESS_ROUTER_PREDECISION_BYPASS_CACHE

unset

Set to 1 to bypass the shared state-cache lookup.

HARNESS_ROUTER_PRETOOL_MCTS_GRAPH_CMD

unset

Command that supplies a simulated graph for optional MCTS.

HARNESS_ROUTER_PRETOOL_MCTS_THRESHOLD

0.80

Confidence threshold considered for MCTS escalation.

HARNESS_ROUTER_STOP_DECISION_TTL

15

Stored-choice age limit for stop continuation.

HARNESS_ROUTER_STOP_CONFIDENCE

0.85

Minimum confidence for requesting continuation.

Hook precomputations use a state key covering the goal, observation, last action, and compact tool catalog. Decisions and traces are stored under:

.harness-router/sessions/<session_id>.decision.json
.harness-router/sessions/<session_id>.decisions.jsonl
.harness-router/sessions/<session_id>.predecision-cache.json

The decision records include goal and observation text. Routing sends compact state and tool descriptions to the configured provider; keep secrets out of those inputs.

MCP

harness-router-mcp runs a stdio MCP server with two tools:

Tool

Inputs

Result

route

Goal, tool descriptors, optional observation and last action.

Next tool, confidence, fallback, and reason.

route_mcts

Root state, simulated states and transitions, search budget, optional Jev prior.

First tool, confidence, fallback, principal variation, and search diagnostics.

For a host using TOML MCP configuration:

[mcp_servers.harness-router]
command = "harness-router-mcp"

Pass OPENROUTER_API_KEY through the host's environment configuration. Server initialization and tool discovery work without it; route returns a missing_openrouter_api_key fallback when the key is absent. MCTS can run locally with use_jev_prior=false.

Use MCP for explicit routing calls. For agents that load skills, the Harness Router skill describes selective use for ambiguous choices.

CLI

har route --help
har route-mcts --help

route requires --goal and a non-empty --tools-json array. Each descriptor needs a unique tool name; descriptions, categories, and risk levels help the router distinguish candidates. Risk values are low, medium, high, and critical.

Option

Default

Effect

--mode

hybrid

Select hybrid, jev_only, or planner_only.

--direct-threshold

0.85

Confidence required to accept a choice in hybrid mode.

--fallback-threshold

0.60

Confidence below which a choice falls back to the planner.

--hierarchical-threshold

24

Tool count above which category-first routing is considered.

--last-action

unset

Include the previous tool name in routing state.

--verbose

off

Pretty-print the JSON response.

--no-daemon

off

Route in the current process.

--no-cache

off

Disable the library router's decision caches for this request.

On Unix-like systems, repeated CLI requests can reuse a local daemon's HTTP connections and in-memory caches. Daemon responses also include daemon and cache_hit. Set HARNESS_ROUTER_NO_DAEMON=1 to disable CLI daemon use through the environment.

The route command exits with 0 when a provider request was attempted and 2 when none was attempted, including cache hits. Read the JSON fallback field to determine whether routing produced a usable selection.

Python API

Install the library into your application's Python environment from a checkout:

python -m pip install -e .

Basic Jev routing needs only the core dependencies. Add .[mcp] if the application also uses the MCP server or the graph-based CLI command.

import asyncio

from harness_router import (
    HarnessState,
    JevToolRouter,
    OpenRouterConfig,
    OpenRouterJevProvider,
    RiskLevel,
    RoutingConfig,
    ToolDescriptor,
)


async def main():
    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,
        ),
    ]

    async with OpenRouterJevProvider.from_config(OpenRouterConfig()) as provider:
        router = JevToolRouter(provider, RoutingConfig())
        decision = await router.route(
            HarnessState(
                goal="Fix the failing parser test",
                observation="Failure points to src/parser.py",
            ),
            tools,
        )

        if decision.fallback:
            print("Planner fallback:", decision.fallback_reason)
        else:
            print(decision.tool, decision.confidence)


asyncio.run(main())

RoutingConfig controls confidence thresholds, hierarchy, context limits, and caches. Its modes behave as follows:

Mode

Behavior

hybrid

Accept choices at or above the direct threshold; return planner fallbacks for uncertainty and handled provider/router errors.

jev_only

Accept choices at or above the fallback threshold; propagate provider/router errors.

planner_only

Return a planner fallback without asking Jev.

The library includes a short-lived cache that reuses high-confidence choices for an unchanged goal and tool set, even when observations change. Set RoutingConfig(obvious_cache_size=0) to disable that reuse; the shared hook bridge uses a separate cache that includes observations in its key.

MCTS

Use MCTS when the best first action depends on possible downstream outcomes. Supply predicted states and transitions; the search does not execute real tools.

This complete CLI example runs locally without an API key. It compares inspecting source with repeating a failing test in a small illustrative graph:

har route-mcts <<'JSON'
{
  "root_state": "start",
  "use_jev_prior": false,
  "simulations": 64,
  "max_depth": 3,
  "states": [
    {
      "id": "start",
      "goal": "Locate the parser failure",
      "tools": [
        {"name": "read_file", "description": "Inspect the failing source", "risk": "low"},
        {"name": "run_tests", "description": "Repeat the failing test", "risk": "low"}
      ]
    },
    {"id": "located", "goal": "Locate the parser failure"},
    {"id": "unchanged", "goal": "Locate the parser failure"}
  ],
  "transitions": [
    {"from_state": "start", "tool": "read_file", "to_state": "located", "reward": 1.0, "terminal": true},
    {"from_state": "start", "tool": "run_tests", "to_state": "unchanged", "reward": 0.0, "terminal": true}
  ]
}
JSON

The graph-based CLI requires the MCP extra. It accepts JSON on stdin or through --graph-json. The MCP route_mcts tool accepts the same graph fields and budgets from 1 to 4,096 simulations and depths from 1 to 8.

For custom Python simulators, implement the async SearchEnvironment methods tools, transition, and evaluate, then use MCTSToolRouter. A transition returns a SimulatedStep containing the predicted state, reward, and terminal flag. MCTSConfig defaults to 64 simulations, depth 4, and at most one Jev policy evaluation per search.

MCTS confidence is the selected root action's share of visits. The principal variation is a predicted sequence; execute the first selected action through the host, then route again using the real result.

Performance and fallback behavior

Routing helps when several tools plausibly fit the current step or the registry is large. An obvious tool choice may need no additional routing call. Evaluate task success, retries, total latency, token usage, and provider cost across the whole harness loop.

The controlled benchmark found increased token usage and elapsed time for the tested skill-driven Codex integration. It also describes a separate native-loop benchmark. Those results depend on the tested tasks and integration; they do not establish a general performance improvement for hooks or custom hosts.

Hybrid library routing returns planner fallbacks on low confidence and handled provider errors. MCP reports missing credentials as a fallback. Hook adapters are designed to leave normal host behavior available when routing context or a usable decision is missing; ohmypi restores its original active tool set.

Confidence describes a routing choice. The host still applies its permissions, approvals, credentials, sandbox, and execution policy.

Development

From a checkout:

python -m pip install -e ".[dev]"
pytest
ruff check .
mypy

For ohmypi extension checks:

cd .omp
bun install --frozen-lockfile --ignore-scripts
bun run typecheck

License

MIT © 2026 Protocol Lattice.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables coding agents to query a Jev model for next-tool recommendations, exposing tools to check status and request tool-choice predictions, while logging all decisions for review.
    27 npm
    4
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables MCP-compatible agent hosts to interact with TypeSafe AI's Jev System One API through dependency-free STDIO tools for classification, scoring, verification, gating, routing, review, and health checks.
    9
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables Claude Code or any MCP client to ask TypeSafe's Jev for calibrated, typed judgments (probabilities, choices, scores) instead of prose, with local caching and cost tracking.
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP-capable agents to run TypeSafe's Jev judgment model as typed yes/no, choice, and score tools, with calibrated probabilities, confidence thresholds, escalation for uncertain or non-judgment tasks, and an optional action gate that fails open.
    1
    MIT