@rk0429/agentic-relay
v0.15.0
Published
Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI with MCP-based multi-layer sub-agent orchestration
Maintainers
Readme
agentic-relay
A unified CLI that brings Claude Code, Codex CLI, and Gemini CLI under a single interface -- solving tool fragmentation, enabling multi-layer sub-agent orchestration via MCP, and providing proactive context window monitoring.
Why agentic-relay?
Working with multiple AI coding CLIs means juggling different command syntaxes, configurations, and authentication flows. Nesting sub-agents across tools is not supported natively. And context window exhaustion catches you off guard.
agentic-relay addresses three problems:
| Problem | Solution | |---|---| | Tool fragmentation -- three CLIs with different flags, config formats, and auth mechanisms | Unified interface with backend adapters that normalize the differences | | No nested sub-agents -- parent agents cannot spawn grandchild agents through different backends | MCP server mode lets any MCP-capable client spawn agents across all three backends, with recursion guards | | Context window surprise -- sessions silently hit token limits | Hooks + ContextMonitor track usage and notify before the limit is reached |
Prerequisites
- Node.js 22+
- One or more backend tools installed: Claude Code, Codex CLI, or Gemini CLI
- macOS 13+ / Ubuntu 22.04+ / Debian 12+
Installation
# Install globally
npm install -g @rk0429/agentic-relay
# Initialize project-level config (optional)
relay init
# Verify installation
relay doctorFrom Source
git clone https://github.com/RK0429/agentic-relay.git
cd agentic-relay
pnpm install
pnpm build
pnpm link --globalQuick Start
# Start an interactive session with Claude Code
relay claude
# One-shot prompt with Codex CLI
relay codex -p "Refactor this function to use async/await"
# Continue the latest Gemini session
relay gemini -c
# Check which backends are available
relay doctorUsage
Backend Commands
relay claude|codex|gemini # Interactive mode
relay claude|codex|gemini -p "query" # Non-interactive (one-shot)
relay claude|codex|gemini -c # Continue latest session
relay claude|codex|gemini -r ID # Resume session by ID
relay claude|codex|gemini --agent NAME # Specify agent
relay claude|codex|gemini --model NAME # Specify modelManagement Commands
relay config show # Show merged config
relay config get <key> # Get config value
relay config set <key> <value> # Set config value
relay auth <backend> login # Authenticate a backend
relay auth <backend> status # Check auth status
relay sessions # List/search sessions
relay update # Update backend tools
relay version # Show versions
relay doctor # Run diagnostics
relay init # Initialize .relay/ configMCP Commands
relay mcp list # List MCP servers
relay mcp add <name> -- CMD # Add MCP server
relay mcp remove <name> # Remove MCP server
relay mcp sync # Sync MCP config to backends
relay mcp serve # Start as MCP server (stdio, default)
relay mcp serve --transport http # Start as MCP server (HTTP)
relay mcp serve --transport http --port 8080 # Custom portConfiguration
agentic-relay uses a three-tier configuration system. Each tier overrides the one above it.
| Tier | Path | Scope |
|---|---|---|
| Global | ~/.relay/config.json | User-wide defaults |
| Project | .relay/config.json | Shared across team (commit to VCS) |
| Local | .relay/config.local.json | Personal overrides (gitignored) |
Example configuration:
{
"defaultBackend": "claude",
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"]
}
},
"hooks": {
"hooks": [
{
"event": "ContextThreshold",
"command": ["node", "scripts/save-state.js"],
"timeoutMs": 5000,
"onError": "warn"
}
]
},
"contextMonitor": {
"enabled": true,
"thresholdPercent": 75,
"notifyMethod": "hook"
}
}Environment Variables
| Variable | Description | Default |
|---|---|---|
| RELAY_HOME | Home directory for relay | ~/.relay |
| RELAY_LOG_LEVEL | Log level (debug/info/warn/error) | info |
| RELAY_MAX_DEPTH | Max recursion depth in MCP server mode | 5 |
| RELAY_CONTEXT_THRESHOLD | Context warning threshold (%) | 75 |
| RELAY_CLAUDE_PERMISSION_MODE | Claude permission mode (bypassPermissions or default) | bypassPermissions |
| ANTHROPIC_API_KEY | Passed through to Claude Code (optional with subscription) | -- |
| OPENAI_API_KEY | Passed through to Codex CLI (optional with subscription) | -- |
| GEMINI_API_KEY | Passed through to Gemini CLI (optional with subscription) | -- |
Security Considerations
- Claude adapter permission bypass: By default, the Claude adapter runs with
bypassPermissionsmode to enable non-interactive sub-agent execution. This means spawned Claude Code agents can execute tools without user confirmation. To change this behavior, set theRELAY_CLAUDE_PERMISSION_MODEenvironment variable todefault.
MCP Server Mode
agentic-relay can act as an MCP (Model Context Protocol) server, allowing any MCP-capable client to spawn sub-agents across all three backends. This is how nested sub-agent orchestration works -- a parent agent calls relay via MCP, which spawns a child agent on any backend, and that child can call relay again to spawn a grandchild.
Setup
Add relay as an MCP server in your client's configuration. For Claude Code:
{
"mcpServers": {
"relay": {
"command": "relay",
"args": ["mcp", "serve"]
}
}
}Exposed Tools
| Tool | Description |
|---|---|
| spawn_agent | Spawn a sub-agent on a specified backend |
| list_sessions | Retrieve session history |
| get_context_status | Query context window usage |
Recursion Guard
The MCP server includes a three-layer recursion guard to prevent runaway agent chains:
- Depth limit -- rejects calls that exceed the maximum nesting depth (default: 5)
- Per-session call limit -- caps the total number of spawns within a single session (default: 20)
- Loop detection -- identifies repeated calls with the same backend + prompt hash
Context propagation between parent and child agents uses environment variables: RELAY_TRACE_ID, RELAY_PARENT_SESSION_ID, and RELAY_DEPTH.
Multi-layer Agent Call Flow
sequenceDiagram
participant User
participant Parent as Parent Agent<br>(Claude Code)
participant Relay as agentic-relay<br>(MCP Server)
participant Child as Child Agent<br>(Codex CLI)
participant Relay2 as agentic-relay<br>(MCP Server)
participant Grandchild as Grandchild Agent<br>(Gemini CLI)
User->>Parent: Task request
Parent->>Relay: spawn_agent(codex, prompt)
Note over Relay: RecursionGuard check<br>depth=1, calls=1
Relay->>Child: Codex SDK thread.run("prompt")<br>RELAY_DEPTH=1
Child->>Relay2: spawn_agent(gemini, sub-prompt)
Note over Relay2: RecursionGuard check<br>depth=2, calls=1
Relay2->>Grandchild: gemini -p "sub-prompt"<br>RELAY_DEPTH=2
Grandchild-->>Relay2: Result
Relay2-->>Child: Result
Child-->>Relay: Result
Relay-->>Parent: Result
Parent-->>User: Final answerArchitecture
agentic-relay is organized into six layers.
graph TD
A["CLI Layer<br><i>citty entry point</i>"] --> B["Command Handlers<br><i>backend, mcp, config, auth,<br>update, sessions, version, doctor, init</i>"]
B --> C["Core Layer<br><i>SessionManager, ConfigManager,<br>AuthManager, HooksEngine,<br>ContextMonitor, EventBus</i>"]
B --> D["MCP Server<br><i>RelayMCPServer,<br>spawn_agent, list_sessions,<br>get_context_status, RecursionGuard</i>"]
C --> E["Backend Adapters<br><i>BaseAdapter, ClaudeAdapter,<br>CodexAdapter, GeminiAdapter,<br>FlagMapper</i>"]
C --> F["Infrastructure<br><i>ProcessManager, Logger</i>"]
D --> E
D --> FBackend Adapters
Each backend CLI has different flags, output formats, and session handling. The adapter layer normalizes these differences behind a common BackendAdapter interface. Adding a new backend means implementing this interface and registering it with the AdapterRegistry.
Non-interactive execution uses official SDKs where available:
| Backend | Non-interactive (-p) | Interactive | Session listing |
|---|---|---|---|
| Claude Code | Agent SDK query() | CLI spawn | Agent SDK listSessions() |
| Codex CLI | Codex SDK thread.run() | CLI spawn | -- |
| Gemini CLI | CLI spawn | CLI spawn | CLI --list-sessions |
Hooks Engine
An event-driven hook system that executes external commands via stdin/stdout JSON pipes.
| Event | Trigger |
|---|---|
| SessionStart / SessionEnd | Session lifecycle |
| PreToolUse / PostToolUse | Tool invocation |
| PreCompact | Before context compaction |
| ContextThreshold | Context usage exceeds threshold (relay-specific) |
| SubagentSpawn / SubagentComplete | Sub-agent lifecycle (relay-specific) |
Context Monitor
Monitors context window usage and fires ContextThreshold events when usage exceeds the configured threshold. Notification methods: stderr (direct output) or hook (triggers the Hooks Engine).
Project Structure
src/
bin/relay.ts # Entry point
commands/ # Command handlers
backend.ts # relay claude/codex/gemini
mcp.ts # relay mcp (list/add/remove/sync/serve)
config.ts # relay config
auth.ts # relay auth
update.ts # relay update
sessions.ts # relay sessions
version.ts # relay version
doctor.ts # relay doctor
init.ts # relay init
core/ # Core modules
session-manager.ts
config-manager.ts
auth-manager.ts
hooks-engine.ts
context-monitor.ts
event-bus.ts
adapters/ # Backend adapters
base-adapter.ts
claude-adapter.ts
codex-adapter.ts
gemini-adapter.ts
adapter-registry.ts
flag-mapper.ts
install-guides.ts
mcp-server/ # MCP server mode
server.ts
recursion-guard.ts
tools/
spawn-agent.ts
list-sessions.ts
get-context-status.ts
infrastructure/ # Infrastructure
process-manager.ts
logger.ts
types/ # Type definitions
schemas/ # Zod validation schemasTech Stack
- Runtime: Node.js 22+
- Language: TypeScript
- Package manager: pnpm
- CLI framework: citty
- Bundler: tsup (esbuild-based)
- Backend SDKs: @anthropic-ai/claude-agent-sdk, @openai/codex-sdk
- MCP: @modelcontextprotocol/sdk
- Process management: execa (interactive modes, Gemini CLI)
- Validation: zod
- Logging: consola
- Testing: vitest (771 tests across 35 files)
- Coverage: @vitest/coverage-v8
License
Apache-2.0
