How to Integrate 33+ CLI Tools with OmniRoute: A Complete Developer Guide
OmniRoute ships with a built‑in catalog that describes more than thirty command‑line interfaces, enabling integration through a three‑layer system that declares metadata in src/shared/constants/cliTools.ts, detects binaries via src/shared/services/cliRuntime.ts, and executes via MCP wrappers in open-sse/mcp-server/tools/.
OmniRoute is an open‑source routing platform that unifies access to AI models and developer tools behind a single API. To integrate 33+ CLI tools with OmniRoute, you work with a declarative catalog, a runtime detection service, and lightweight MCP wrappers that normalize every tool into a streaming, OpenAI‑compatible endpoint. The architecture treats each CLI as a routable resource, handling binary discovery, environment configuration, and output translation automatically.
Three‑Layer Integration Architecture
OmniRoute abstracts CLI tools through three distinct layers: a static catalog for metadata, a runtime scanner for binary discovery, and MCP wrappers for execution. Each layer is isolated, allowing you to add or modify tools without touching the core routing logic.
Catalog Definition in cliTools.ts
The catalog layer lives in src/shared/constants/cliTools.ts and exports a CLI_TOOLS object that acts as the source of truth for every integrated tool. Each entry defines the tool’s ID, human‑readable name, UI icons, vendor, default command, supported base‑URL modes, required environment variables, and optional UI guide steps. This declarative approach lets the frontend and backend agree on tool capabilities without hard‑coding logic in either place.
The catalog also exposes helper functions such as listCliTools() to enumerate available integrations at runtime.
Runtime Detection via cliRuntime.ts
The runtime layer in src/shared/services/cliRuntime.ts scans the host’s $PATH (and additional well‑known locations) for the binaries declared in the catalog. It exposes CLI_TOOL_IDS, getCliRuntimeStatus(), and getCliConfigPaths() to the rest of the application.
When getCliRuntimeStatus() is called with a tool ID, it invokes locateCommand("<binary>") to verify installation, returning the absolute binary path, version hints, and configuration file locations. This allows OmniRoute to fail fast with clear error messages if a user requests a tool that is not installed or misconfigured.
MCP Tool Wrappers
The execution layer resides under open-sse/mcp-server/tools/ (e.g., skillTools.ts). Each file implements a Zod‑validated input schema and a handler function that translates incoming MCP requests into spawned CLI processes.
These wrappers use the paths supplied by cliRuntime.ts to invoke the correct binary, inject environment variables (such as OPENCODE_BASE_URL), and pipe stdout/stderr into OmniRoute’s Server‑Sent Events (SSE) pipeline. The wrappers also normalize tool‑specific output so that translators in open-sse/translator/* can convert payloads back into standard OpenAI‑compatible JSON.
Request Lifecycle: From API to CLI Process
When a client sends a request to POST /v1/chat/completions, the flow through open-sse/handlers/chatCore.ts follows these steps:
- Signal Detection: If the request body contains
model: "opencode"(or similar tool identifiers), the handler recognizes it as a CLI tool call rather than a standard LLM request. - Catalog Lookup: The handler queries the
CLI_TOOLScatalog to retrieve metadata, default arguments, and required environment variables. - Runtime Verification: It calls
getCliRuntimeStatus()fromcliRuntime.tsto confirm the binary exists and fetch its absolute path. - MCP Execution: The request is delegated to the appropriate MCP wrapper in
open-sse/mcp-server/tools/, which spawns the process usingcross-spawnand streams output. - Response Translation: The translator layer converts CLI‑specific exit codes and text streams into the standard chat‑completion schema before returning to the client.
Throughout this pipeline, input validation runs through Zod schemas, error sanitization flows through open-sse/utils/error.ts, and per‑tool circuit‑breakers prevent transient CLI crashes from destabilizing the platform.
Step‑by‑Step: Adding a New CLI Tool
Extending OmniRoute to support an additional tool follows a deterministic, five‑step sequence:
- Extend the Catalog: Add an entry to
CLI_TOOLSinsrc/shared/constants/cliTools.tsspecifying the tool ID, command, environment variables, and UI metadata. - Update Runtime Detection: If the binary name differs from the catalog ID, add a detection rule in
src/shared/services/cliRuntime.ts(typically alocateCommand("<binary>")call). - Create an MCP Wrapper: Implement a new file under
open-sse/mcp-server/tools/that defines the Zod input schema and ahandlerspawning the binary using paths fromcliRuntime. - Expose the API Route: Most tools are reachable through the generic
POST /v1/cli-tools/{id}route, which forwards requests to the MCP wrapper via the internalcliRuntimeProviderMap. - Add Tests: Write unit tests that verify the catalog entry passes
CliCatalogEntrySchema, confirm the runtime detects the binary, and ensure the MCP wrapper produces correctly shaped responses. Referencetests/unit/t40-opencode-cli-tools-integration.test.tsandtests/unit/cli-tools.test.tsfor patterns.
Implementation Examples
Listing Available CLI Tools
// Client‑side enumeration of the 33+ tool catalog
import { listCliTools } from "@/shared/constants/cliTools";
const tools = listCliTools();
console.log(tools.map(t => t.id));
// => ['claude', 'codex', 'opencode', …]
Executing a Tool via the OmniRoute API
import fetch from "node-fetch";
await fetch("https://router.mycompany.com/api/mcp/execute", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
tool: "opencode", // matches a CLI_TOOLS entry
args: ["run", "implement feature X"],
config: { baseUrl: "https://my.custom.host/v1" } // optional override
}),
});
Inside the MCP Wrapper
export const opencodeTool = {
input: z.object({
args: z.array(z.string()),
config: z.object({}).optional()
}),
async handler({ args, config }) {
const { getCliRuntimeStatus } = await import("@/shared/services/cliRuntime");
const status = await getCliRuntimeStatus("opencode");
if (!status?.executable) {
throw new Error("Opencode CLI not found");
}
const spawn = require("cross-spawn");
const child = spawn(
status.executable,
args,
{
env: {
...process.env,
OPENCODE_BASE_URL: config?.baseUrl
}
}
);
// Pipe stdout/stderr into OmniRoute's SSE response stream
return child;
},
};
Summary
- Catalog Layer: Define tool metadata in
src/shared/constants/cliTools.tsusing theCLI_TOOLSobject. - Runtime Layer: Detect installed binaries and config paths via
src/shared/services/cliRuntime.tsusinggetCliRuntimeStatus()andlocateCommand(). - Execution Layer: Implement MCP wrappers in
open-sse/mcp-server/tools/to spawn processes and normalize I/O. - Integration Flow: Requests hit
chatCore.ts, resolve through the catalog and runtime, execute via MCP, and return through translators inopen-sse/translator/*. - Security: Zod validation, error sanitization through
open-sse/utils/error.ts, and circuit‑breaker handling protect against malformed input and flaky CLI processes. - Testing: Validate new tools with schema tests (
cli-tools.test.ts) and end‑to‑end integration tests (t40-opencode-cli-tools-integration.test.ts).
Frequently Asked Questions
How does OmniRoute detect if a CLI tool is installed?
The cliRuntime.ts service exposes getCliRuntimeStatus(), which scans the host’s $PATH and well‑known installation directories using locateCommand(). It returns an object indicating whether the binary exists, its absolute path, and where its configuration files reside, allowing the platform to fail fast with actionable error messages when a tool is missing.
Can I override default commands or base URLs for a specific tool?
Yes. Each catalog entry in CLI_TOOLS defines default commands and supported base‑URL modes. When calling the tool through the API, you can pass a config object in the request payload to override these defaults at runtime. The MCP wrapper merges this config into the spawn environment (for example, setting OPENCODE_BASE_URL) before executing the binary.
What testing strategy ensures CLI integrations remain stable?
The repository requires three levels of testing: (1) schema validation tests that verify catalog entries against CliCatalogEntrySchema in tests/unit/cli-tools.test.ts; (2) runtime detection tests confirming cliRuntime.ts correctly locates binaries; and (3) end‑to‑end integration tests like tests/unit/t40-opencode-cli-tools-integration.test.ts that execute real CLI processes and verify the OpenAI‑compatible response shape.
How does OmniRoute handle errors from external CLI processes?
Errors are sanitized through open-sse/utils/error.ts to prevent leakage of sensitive host information. Additionally, per‑tool circuit‑breaker logic wraps each MCP execution, ensuring that transient failures (such as a crashed CLI or network timeout) do not cascade into the broader routing platform or exhaust system resources.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →