Integrating Coding Agents with OmniRoute: A Complete Implementation Guide
OmniRoute is a unified AI proxy/router that lets coding agents plug into a single API endpoint and automatically dispatch requests to 340+ LLM providers through its modular AgentRouter protocol.
In this guide, you'll learn how to integrate custom coding agents—whether built-in agents like DevIn and HyperAgent or your own implementations—into the OmniRoute architecture. The platform's provider-agnostic design, implemented in diegosouzapw/OmniRoute, makes it straightforward to extend routing logic, add specialized executors, and expose agent-specific tools through the MCP server.
Understanding the OmniRoute Architecture for Coding Agents
OmniRoute organizes functionality into eight distinct layers. Each layer plays a specific role in request processing, allowing coding agents to hook into exactly the right abstraction.
| Layer | Location | Role |
|---|---|---|
| API Routes | src/app/api/v1/ |
Next.js App Router entry points. Each route follows the pattern: CORS → Zod validation → optional auth → handler delegation. |
| Handlers | open-sse/handlers/ |
Core request processing. chatCore.ts orchestrates the chat flow, delegating to the AgentRouter protocol. |
| AgentRouter Protocol | open-sse/handlers/chatCore/agentRouterProtocol.ts |
Determines which agent should handle a request, rewrites the payload accordingly, and selects the appropriate executor. |
| Executors | open-sse/executors/ |
Low-level HTTP dispatchers for each provider. Specialized executors exist for agents needing extra handling, such as hyperagent.ts and devin-cli-agentic.ts. |
| Translators | open-sse/translator/ |
Convert between provider-specific request/response formats (OpenAI ↔ Claude ↔ Gemini). |
| Services | open-sse/services/ |
Higher-level logic: combo routing, rate-limit handling, caching, and resilience (circuit breakers, connection cooldowns, model lockouts). |
| Database | src/lib/db/ |
SQLite-backed domain modules for persistence (quota tracking, agent-bridge state, feature flags). |
| Skills & Memory | src/lib/skills/, src/lib/memory/ |
Extensible skill framework and persistent conversational memory that agents can query or update. |
| MCP Server | open-sse/mcp-server/ |
44 canonical tools (including agent-skill tools) exposing a JSON-RPC-2.0 API used by agents for tool execution. |
| A2A Server | src/lib/a2a/ |
Agent-to-Agent protocol letting agents invoke each other's capabilities via standardized JSON-RPC. |
The critical integration point for coding agents is the AgentRouter protocol in open-sse/handlers/chatCore/agentRouterProtocol.ts. This file's resolveAgent() function inspects incoming requests and returns the correct executor and translator pair.
Step-by-Step: Integrating a Coding Agent with OmniRoute
Follow these six steps to integrate any coding agent into the OmniRoute pipeline. Each step maps to a specific file location and includes runnable code examples.
Step 1: Register the Agent in the Provider Registry
All providers and agents must be declared in src/shared/constants/providers.ts. This registry defines the agent's name, authentication method, default model, and optional custom executor class.
// src/shared/constants/providers.ts
export const PROVIDER_REGISTRY = {
// Existing providers …
hyperagent: {
name: "hyperagent",
auth: "apiKey",
defaultModel: "hyperagent-gpt-4",
// Optional: custom executor class
executor: "HyperAgentExecutor",
},
devin: {
name: "devin",
auth: "bearer",
defaultModel: "devin-2024",
executor: "DevInExecutor",
},
};
The registry is imported by the AgentRouter and used to validate model identifiers before routing.
Step 2: Implement a Custom Executor (If Needed)
Most agents can reuse the generic executor. However, coding agents often require specialized authentication, custom headers, or non-standard HTTP handling. In these cases, extend BaseExecutor as shown in open-sse/executors/hyperagent.ts.
// open-sse/executors/hyperagent.ts
import { BaseExecutor } from "./base.ts";
export class HyperAgentExecutor extends BaseExecutor {
async execute(request: any, ctx: ExecutionContext) {
// Add HyperAgent-specific headers or auth here
request.headers.set("x-hyperagent-key", ctx.credentials.apiKey);
request.headers.set("x-hyperagent-version", "2024.06");
// Optional: transform request body for agent-specific format
const agentPayload = this.transformToAgentFormat(request.body);
return await super.execute({ ...request, body: agentPayload }, ctx);
}
private transformToAgentFormat(body: any) {
// Convert OpenAI-compatible messages to HyperAgent's expected schema
return {
prompt: body.messages.map(m => m.content).join("\n"),
mode: "coding",
context_window: 128000,
};
}
}
The DevIn executor in open-sse/executors/devin-cli-agentic.ts demonstrates another pattern: handling streaming responses with server-sent events and agentic tool loops.
Step 3: Extend the AgentRouter Decision Logic
The resolveAgent() function in open-sse/handlers/chatCore/agentRouterProtocol.ts is the routing brain. It inspects the model name, optional agent flag in the request body, and route metadata to select the correct executor.
// open-sse/handlers/chatCore/agentRouterProtocol.ts
import { HyperAgentExecutor } from "@/open-sse/executors/hyperagent";
import { DevInExecutor } from "@/open-sse/executors/devin-cli-agentic";
import { defaultTranslator, claudeTranslator } from "@/open-sse/translator/";
export interface AgentResolution {
executor: BaseExecutor;
translator: Translator;
modelId: string;
}
export function resolveAgent(model: string, flags?: RequestFlags): AgentResolution {
// HyperAgent routing
if (model.startsWith("hyperagent-") || flags?.agent === "hyperagent") {
return {
executor: new HyperAgentExecutor(),
translator: defaultTranslator,
modelId: model.replace("hyperagent-", ""),
};
}
// DevIn routing with specialized translator
if (model.startsWith("devin-") || flags?.agent === "devin") {
return {
executor: new DevInExecutor(),
translator: claudeTranslator, // DevIn expects Anthropic-compatible format
modelId: model,
};
}
// Fallback to standard provider routing...
return resolveStandardProvider(model);
}
Add your conditional branch here to route new model identifiers to your custom executor.
Step 4: Expose Agent-Specific Tools via MCP
Coding agents often need to execute tools—file operations, code search, terminal commands. OmniRoute's MCP server exposes these capabilities through JSON-RPC-2.0. Register new tools in open-sse/mcp-server/tools/.
// open-sse/mcp-server/tools/agentSkillTools.ts
import { defineTool } from "@/open-sse/mcp-server/tools/base";
import { z } from "zod";
export const executeCode = defineTool({
name: "executeCode",
description: "Execute code in a sandboxed environment and return stdout/stderr.",
inputSchema: z.object({
language: z.enum(["typescript", "python", "rust", "go"]),
code: z.string().max(10000),
timeoutMs: z.number().max(60000).default(30000),
}),
async handler(args, ctx) {
const result = await ctx.agent.executeInSandbox({
language: args.language,
code: args.code,
timeout: args.timeoutMs,
});
return {
stdout: result.stdout,
stderr: result.stderr,
exitCode: result.exitCode,
executionTimeMs: result.duration,
};
},
});
export const listAgentTools = defineTool({
name: "listAgentTools",
description: "Returns the list of tools available to the current coding agent.",
inputSchema: z.object({}),
async handler(_, ctx) {
const tools = await ctx.agent.getAvailableTools();
return { tools };
},
});
Tools are automatically discovered by the MCP server and exposed to any agent that connects via the JSON-RPC interface.
Step 5: Persist Agent-Specific State
Use the SQLite-backed database modules in src/lib/db/ to store per-agent quotas, connection health, or custom metadata. The agentBridgeState.ts module provides a pattern for agent-specific persistence.
// src/lib/db/agentBridgeState.ts
import { getDbInstance } from "./core";
export interface AgentState {
lastExecutionAt: string;
accumulatedCost: number;
sessionTokens: number;
customConfig: Record<string, any>;
}
export async function setAgentState(agentId: string, state: AgentState) {
const db = getDbInstance();
await db.run(
`INSERT INTO agent_bridge_state (agent_id, state, updated_at)
VALUES (?, ?, datetime('now'))
ON CONFLICT(agent_id) DO UPDATE
SET state = excluded.state, updated_at = excluded.updated_at`,
[agentId, JSON.stringify(state)]
);
}
export async function getAgentState(agentId: string): Promise<AgentState | null> {
const db = getDbInstance();
const row = await db.get(
`SELECT state FROM agent_bridge_state WHERE agent_id = ?`,
[agentId]
);
return row ? JSON.parse(row.state) : null;
}
For quota tracking specifically, see open-sse/services/agentrouterQuotaFetcher.ts, which aggregates usage across providers and enforces limits.
Step 6: Add Dashboard UI and API Endpoints
Complete the integration with user-facing configuration and direct API access.
Dashboard pages for the new agent live under src/app/(dashboard)/dashboard/providers/:
// src/app/(dashboard)/dashboard/providers/hyperagent/page.tsx
export default function HyperAgentConfigPage() {
return (
<ProviderConfigForm
providerKey="hyperagent"
fields={[
{ name: "apiKey", type: "secret", label: "HyperAgent API Key" },
{ name: "defaultModel", type: "select", options: ["hyperagent-gpt-4", "hyperagent-claude-3"] },
{ name: "sandboxEnabled", type: "boolean", label: "Enable Code Sandbox" },
]}
/>
);
}
API routes for direct agent access go under src/app/api/v1/agent/:
// src/app/api/v1/agent/hyperagent/route.ts
import { NextRequest } from "next/server";
import { validateAuth } from "@/src/lib/auth";
import { resolveAgent } from "@/open-sse/handlers/chatCore/agentRouterProtocol";
export async function POST(req: NextRequest) {
await validateAuth(req);
const body = await req.json();
const { executor, translator } = resolveAgent(body.model, { agent: "hyperagent" });
const response = await executor.execute(translator.translateRequest(body), getContext(req));
return Response.json(translator.translateResponse(response));
}
Resilience and Rate-Limit Handling for Coding Agents
OmniRoute's three-layer resilience mechanism applies automatically to all agents because they share the same ProviderConnection abstraction. Coding agents that exceed quota or encounter transient errors are isolated without affecting other agents.
| Mechanism | Implementation | Behavior |
|---|---|---|
| Provider Circuit Breaker | src/shared/utils/circuitBreaker.ts |
Opens after consecutive failures; fast-fails requests until health recovers. |
| Connection Cooldown | src/sse/services/auth.ts::markAccountUnavailable() |
Temporarily disables a specific agent-provider pair when rate limits hit. |
| Model Lockout | open-sse/services/accountFallback.ts::checkFallbackError() |
Removes problematic models from rotation until manually cleared. |
These mechanisms ensure that a misbehaving coding agent cannot destabilize the broader provider fleet.
Security Guarantees for Agent Integration
OmniRoute enforces security at multiple layers:
- Input validation via Zod schemas runs before any agent logic executes in API routes.
- Error sanitization through
open-sse/utils/error.ts(buildErrorBody,sanitizeErrorMessage) prevents credential leakage in stack traces. - Public credential resolution via
open-sse/utils/publicCreds.ts(resolvePublicCred()) safely exposes non-sensitive OAuth client IDs to agents.
Coding agents that generate goals are additionally subject to policy enforcement in open-sse/utils/agentGoalPolicy.ts, which validates goal structure and prevents recursive or excessively expensive agent loops.
Testing Your Agent Integration
All agent-related changes must be covered by:
- Unit tests in
tests/unit/for executor and translator logic - MCP tool tests in
tests/unit/mcp-server/for tool handlers - Integration tests verifying end-to-end routing through
chatCore.ts
The test suite guarantees that new agents do not break existing routing, combo strategies, or resilience layers.
Summary
Integrating coding agents with OmniRoute requires six focused steps:
- Register the agent in
src/shared/constants/providers.tsto declare capabilities and defaults - Implement a custom executor in
open-sse/executors/only when specialized HTTP handling is needed - Extend
resolveAgent()inopen-sse/handlers/chatCore/agentRouterProtocol.tsto route requests to your executor - Expose agent-specific tools through the MCP server in
open-sse/mcp-server/tools/ - Persist state using database modules in
src/lib/db/for quotas and session tracking - Surface configuration via dashboard pages and dedicated API routes under
src/app/api/v1/agent/
Your agent automatically inherits OmniRoute's resilience, security, and multi-provider routing capabilities.
Frequently Asked Questions
How does OmniRoute decide which executor to use for a coding agent?
The resolveAgent() function in open-sse/handlers/chatCore/agentRouterProtocol.ts inspects the request's model name, optional agent flag, and route metadata. It returns an AgentResolution containing the executor, translator, and resolved model ID. Add a conditional branch matching your agent's model prefix to route requests appropriately.
Can I integrate a coding agent without building a custom executor?
Yes. If your agent uses standard API key authentication and accepts OpenAI-compatible request/response formats, you can register it in the provider registry with no custom executor. The generic BaseExecutor handles HTTP dispatch. Build a custom executor only when you need specialized headers, non-standard auth, or request/response transformation that translators cannot handle.
How do coding agents access tools like file operations or code execution?
Agents call tools through OmniRoute's MCP server, which exposes 44 canonical tools via JSON-RPC-2.0. Register new tools in open-sse/mcp-server/tools/ using defineTool(), then reference them by name in agent conversations. The agentSkillTools.ts file demonstrates the pattern for agent-specific capabilities.
What happens when a coding agent hits rate limits?
OmniRoute's resilience layer automatically puts the affected connection into cooldown via markAccountUnavailable() in src/sse/services/auth.ts. Subsequent requests to that agent-provider pair fast-fail with a 429 status, while other agents and providers remain operational. The circuit breaker in src/shared/utils/circuitBreaker.ts opens if failures persist, preventing cascade failures.
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 →