Skip to main content
Glama
sphilius

dxt-openrouter-router

by sphilius

DXT OpenRouter Router

npm license

A tiny MCP (stdio) server that returns a ready-to-send OpenRouter request body from a named preset and your current energy budget:

energy

Model slug becomes

Meaning

low

…:floor

cheapest provider serving that model

balanced

(base slug)

the model's default provider

high

…:nitro

highest-throughput provider

Two things make this different from a normal cost router:

  1. It routes on energy, not just cost. The input is a fact about you, not about the task. "Cheap" and "fast" are the same axis viewed from different energy levels.

  2. Zero Data Retention is the default. Every body ships provider.data_collection: "deny", so a preset has to explicitly opt out of privacy rather than opt in to it.

It builds the request. It does not send it — so it never needs your API key in-process, and never sees a response.


Install

npm install -g dxt-openrouter-router

Or run it without installing:

npx dxt-openrouter-router

Related MCP server: mcp-openrouter

Register it with an MCP host

Claude Desktop (claude_desktop_config.json) or any MCP client:

{
  "mcpServers": {
    "openrouter-router": {
      "command": "npx",
      "args": ["-y", "dxt-openrouter-router"],
      "env": {
        "ROUTING_PRESETS_PATH": "C:\\path\\to\\routing.presets.json"
      }
    }
  }
}

ROUTING_PRESETS_PATH is optional — omit it and the bundled routing.presets.json is used. The file is re-read on every call, so you can edit presets without restarting the host.

The routeLLM tool

Argument

Required

Description

preset

A key from routing.presets.json, e.g. research_long_context

energy

low | balanced | high (default balanced)

user_prompt

The user message to place in the body

system

Overrides the preset's own system prompt

overrides

Extra body fields merged in last, e.g. { "temperature": 0.2 }

Example

// call
{ "preset": "research_long_context", "energy": "low", "user_prompt": "Synthesize these sources..." }
// result
{
  "url": "https://openrouter.ai/api/v1/chat/completions",
  "method": "POST",
  "headers": {
    "Authorization": "Bearer $OPENROUTER_API_KEY",  // literal placeholder — never your real key
    "Content-Type": "application/json"
  },
  "api_key_configured": true,
  "body": {
    "temperature": 0.2,
    "model": "meta-llama/llama-3.1-70b-instruct:floor",
    "messages": [
      { "role": "system", "content": "You are a careful research synthesist. ..." },
      { "role": "user", "content": "Synthesize these sources..." }
    ],
    "provider": { "data_collection": "deny" }
  }
}

Presets

routing.presets.json is a plain map of name → preset:

{
  "presets": {
    "daily_driver": {
      "model": "google/gemini-3.6-flash",   // no :floor/:nitro here — energy adds that
      "description": "Default workhorse — unit tests, refactors, docs, CLI loops.",
      "system": "Optional default system prompt.",
      "provider": { "data_collection": "deny" },      // merged over the ZDR default
      "response_format": { "type": "json_object" },   // passed straight through
      "params": { "temperature": 0.4 }                // any other OpenRouter body field
    }
  }
}

Bundled presets mirror a simple decision tree:

Preset

Model

Reach for it when

quick_ping

x-ai/grok-4.5

formats, clarifications, vibe checks

daily_driver

google/gemini-3.6-flash

~80% of volume — tests, refactors, docs

logic_engine

openai/gpt-5.6-sol

math, symbolic logic, formal reasoning

high_reasoning

anthropic/claude-opus-4.8

architecture, nuanced review, agentic work

research_long_context

meta-llama/llama-3.1-70b-instruct

long-context synthesis across sources

structured_json

google/gemini-3.6-flash

extraction that must return parseable JSON

Slugs were verified against https://openrouter.ai/api/v1/models on 2026-07-22. OpenRouter slugs change as models ship — re-check before relying on them.

Secrets

🔒 This package contains no API key, and the tool output never includes one.

  • OPENROUTER_API_KEY is read from the environment, and only ever reported as the boolean api_key_configured.

  • The Authorization header is emitted as the literal string Bearer $OPENROUTER_API_KEY, so tool output is safe to paste into a chat log, an issue, or a commit.

  • Copy .env.example.env for local use. .env is gitignored.

Use as a library

The pure core is exported, so you can build bodies without MCP:

import { buildRequestBody, loadPresets } from "dxt-openrouter-router";

const presets = loadPresets("./routing.presets.json");
const body = buildRequestBody(presets, { preset: "daily_driver", energy: "low" });

Develop

npm install
npm run build     # tsc -> dist/
npm test          # node --test test/

License

MIT © Sasha Philius

Available Tools

1 tool
routeLLMA

Build a ready-to-send OpenRouter chat-completion request body from a named preset and the user's current energy budget. energy=low appends :floor (cheapest provider), energy=high appends :nitro (highest throughput), energy=balanced uses the base model slug. Zero Data Retention (provider.data_collection=deny) is on by default. Returns the URL, headers and body — it does NOT send the request.

ParametersJSON Schema
NameRequiredDescriptionDefault
energyNoYour current energy budget. low = cheapest (:floor), high = fastest (:nitro), balanced = the model's default provider.balanced
presetYesName of a preset from routing.presets.json, e.g. 'research_long_context'.
systemNoOverrides the preset's own system prompt when supplied.
overridesNoExtra OpenRouter body fields merged in last, e.g. { "temperature": 0.2 }.
user_promptNoThe user message to place in the request body.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and communicates key behavior well: it explicitly states Zero Data Retention default, the energy-based provider suffix logic, and the crucial fact that it does NOT send the request. The one gap is not disclosing auth requirements (API keys) or error behavior for unknown presets, but the provided behavioral context is strong.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact (three sentences) and front-loaded with the core purpose in the first sentence. Each sentence earns its place: purpose, energy logic, data retention, and the explicit non-sending behavior. It loses the top score only because it crams several distinct facts (energy mapping, retention, non-sending) into a somewhat dense structure, but there's no waste or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 5 parameters with 100% schema coverage, a nested object field, and no output schema, the description is appropriately complete. It adds the non-obvious details the schema can't convey: the energy→suffix mapping, the Zero Data Retention default, the overrides merge priority, and the crucial non-sending behavior. There is no output schema, but the description explicitly states what it returns (URL, headers, body), compensating well.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents every parameter, including the energy enum values with direct mapping to suffix behavior. The description adds context about energy mapping (floor/nitro/base) and overrides merging semantics ("merged in last") which aligns with and enriches the schema. The description reinforces rather than extends, which is appropriate given full coverage. Baseline 3 is correct.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ("Build a ready-to-send OpenRouter chat-completion request body") with a clear resource (named preset + energy budget). It distinctly states what it does NOT do (sends the request), which is an excellent positive and negative definition. Despite no siblings being provided, the purpose is unambiguous and self-contained.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly narrates the energy-preset selection logic (low→:floor, high→:nitro, balanced→base slug), telling the agent when to use which energy value. It doesn't explicitly name alternatives or exclusions, since no sibling tools exist to differentiate against. The guidance is strong on the core decision, slightly lighter on external context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool updatev0.1.0
    • First observedrouteLLM

TDQS

A3.8/5.0

Scored across 1 tool

Disambiguation5/5

There is only one tool, so there is no possibility of ambiguity or overlap with other tools. The purpose of routeLLM is clearly defined and distinct.

Naming Consistency3/5

With only a single tool, there is no pattern to evaluate for consistency. The name routeLLM uses a camelCase convention mixing a verb and a domain noun, which is readable though generic.

Tool Count2/5

A single tool feels thin for a server named 'openrouter-router.' The router domain likely requires at least companion operations (e.g., send, list presets, get budget) to be practically useful on its own, so one tool is under-scoped.

Completeness2/5

The tool only builds a request body and explicitly does not send the request. For routing purposes, there is a notable dead end—no send capability, no way to list/manage presets, and no health or configuration operations. The surface is significantly incomplete for the stated routing purpose.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers