Local-first workflow and model-ranking router for MCP-capable coding agents.
Agent Router is a small stdio MCP server that answers "what should I do next and how much model budget is justified?" before implementation starts. It classifies tasks, suggests repeatable workflows, returns a ranked list of policy-compliant models, reports approval requirements, and logs routing outcomes.
Router recommends. It does not execute.
Current version: 0.5.2
Runtime requirements:
- Python 3.9+
- Standard library only
- No external dependencies
- No cloud service
- No database server
- No package install required
Without routing, agents tend to improvise: they pick a model tier based on context length, choose specialists arbitrarily, and skip stop conditions. This leads to silent cost escalation, unnecessary use of expensive models for trivial tasks, and architecture decisions slipping into code-edit sessions.
Router enforces a decision layer before any work starts:
- Workflow first — if the task matches a known, repeatable workflow, use it
- Specialist context second — if no workflow matches, select the appropriate specialist route
- Ranked model list third — estimate AI-credit cost and rank viable models by policy
- Approval before expensive — gate expensive routes behind explicit approval conditions
- Outcome logging after dispatch — record what was actually selected so routing quality can be reviewed
The recommended production call is suggest_workflow:
- Call
suggest_workflowonce per task. - Router returns workflow candidates plus
decision.rankedModels. - The orchestrating LLM chooses the subagent contextually.
- Dispatch that subagent with the first
rankedModelsentry present in the subagent frontmatter model list. - If that entry has
approvalRequired: true, obtain approval first. - Report the actual choice with
log_outcomeusingselectedModelIdandselectionRank.
route and classify remain available as legacy compatibility actions and are planned for removal before 1.0.
Specialist identity and model selection are decoupled by design. A specialist defines what judgment is needed. A model defines how much cost is justified. Routing one does not constrain the other independently — both are resolved separately against policy.
This separation prevents "picking an expensive model means picking a capable specialist" conflation, and prevents "choosing a cheap model means I am locked to a small specialist scope."
Router uses GitHub Copilot's AI-credit pricing model, not premium-request multipliers.
- 1 credit = $0.01 USD
- Costs are estimated from per-1M-token input, cached-input, output, and optional Anthropic cache-write prices
- Long-context pricing applies only when the caller provides
estimated_input_tokensabove a model threshold - Routing defaults to the model's
defaultpricing row otherwise
Task policies choose a cost profile (small, medium, large) and a credit ceiling. Router ranks viable models by:
- effective credit tier
- estimated credits
- model id
Multiplier-era fields are deprecated. maxAllowedMultiplier is retained as null for one release as a compatibility field.
Point your MCP client at server.py:
{
"servers": {
"router": {
"type": "stdio",
"command": "python",
"args": ["path/to/agent-router/server.py"],
"env": {
"AGENT_ROUTER_STATE_DIR": "path/to/state/router",
"AGENT_ROUTER_WORKSPACE_ROOT": "path/to/workspace"
}
}
}
}See examples/mcp.vscode.json for a VS Code example.
Router exposes exactly one public MCP tool:
router
Call it with an action and optional params object:
{"action":"suggest_workflow","params":{"task":"Update README wording to match existing GitHub style.","estimated_input_tokens":12000}}This keeps the MCP surface minimal for clients with tool-inventory limits.
doctorreload_registriesvalidate_registriessuggest_workflowmatch_workflowget_workflowlist_workflowsvalidate_workflow_paramsclassifylegacyroutelegacyvalidate_decisionlist_specialistslist_modelslog_decisionlog_outcomerecent_decisionsexplain
estimated_input_tokens— long-context-aware pricing input, usually supplied by Thrift.estimated_output_tokens— optional override for the policy cost profile's output-token assumption.candidate_models— optional list of dispatchable model ids. Router constrains ranking to that set and reports when the ideal model exists outside it.
{"action":"suggest_workflow","params":{"task":"Refactor the Router README and run tests.","estimated_input_tokens":16000,"candidate_models":["gpt-5-mini","gpt-5-thinking","claude-sonnet-4"]}}Returns workflow candidates and a decision containing rankedModels, approval flags, candidate-model constraint information, and an optional Governor start hint.
{"action":"match_workflow","params":{"name":"workflow.docs-sync","params":{"task_summary":"Update README wording to match existing GitHub style.","target_files":["README.md"]}}}Returns a workflow prescription: profile, specialist, model tier, allowed tools, checks, and parameter validation.
{"action":"log_outcome","params":{"decision_id":"rd_abc123def456","selectedModelId":"gpt-5-mini","selectionRank":1,"notes":"Used first dispatchable ranked model."}}Appends an outcome row to the route decision log.
{"action":"recent_decisions","params":{"limit":10}}Returns recent decision rows, including matching outcome rows when available. Rotated decision logs are included.
{"action":"route","params":{"task":"Update README wording to match existing GitHub style."}}Legacy actions remain available for backward compatibility but are deprecated for production workflows.
The selected model remains available in scalar fields for compatibility, but the primary output is rankedModels.
Key fields:
decisionIdcreatedAttaskClassrouteTypeworkflowIdspecialistIdselectedModelIdmodelTierestimatedCreditspricingAppliedapprovalRequiredapprovalConditionsrankedModelscandidateConstraintgovernorStartHintmatchedSignals
- Thrift can supply
estimated_input_tokensfromplan_context.total_planned_tokensorcount_tokens.tokens. - Router returns
rankedModelsand optionalgovernorStartHint. - Governor can consume
governorStartHint.params.profile. - Router logs decisions and outcomes using the shared
AGENT_SUITE_SESSION_IDconvention.
Router writes decision logs under AGENT_ROUTER_STATE_DIR when set, otherwise under state/router relative to the workspace.
Useful environment variables:
AGENT_ROUTER_STATE_DIR— location for Router state and route decision logsAGENT_ROUTER_WORKSPACE_ROOT— workspace root used for default state placementAGENT_SUITE_SESSION_ID— shared suite session id used for correlation across components
server.py MCP gateway server
router_core.py deterministic classification/routing logic
pricing.py AI-credit estimation helpers
registries.py registry loading/cache helpers
schemas.py shared constants and validation helpers
routing/*.json task, workflow, specialist, model, policy registries
docs/routing_model.md routing design documentation
docs/registries.md registry format reference
docs/tool_reference.md complete action reference
smoke_test.py subprocess smoke test
test_router.py router unit tests
test_pricing.py pricing unit tests
python -m compileall -q .
python smoke_test.py
python -m unittest discover -s . -p "test*.py"Current root suite: 81 tests.
- Stdlib-only, no external dependencies
- Deterministic routing: no LLM calls and no network requests
- Copilot-safe MCP schema
- Gateway pattern: one MCP tool with action dispatch
- Registry-driven task, workflow, specialist, model, and policy behavior
- No automatic execution or model switching