Labsco
verivus-oss logo

llm-cli-gateway

10

from verivus-oss

Unified MCP server providing access to Claude Code, Codex, and Gemini CLIs through a single gateway. Features multi-LLM orchestration, persistent session management, async job execution with polling, approval gates, retry with circuit breakers, and token optimization. Install: npx -y llm-cli-gateway

🔥🔥🔥🔥✓ VerifiedFreeAdvanced setup

llm-cli-gateway

OpenSSF Scorecard

"Without consultation, plans are frustrated, but with many counselors they succeed." — Proverbs 15:22 (LSB)

Secure local control plane for AI coding agents.

llm-cli-gateway lets supported MCP clients operate Claude Code, Codex, Gemini/Antigravity, Grok Build, Mistral Vibe, Cognition Devin, Cursor Agent, and configured HTTP API providers through one user-owned gateway while preserving native CLI sessions, local credentials, durable async jobs, validation receipts, and review workflows.

Why developers try it: use the client you are already in to delegate work to local coding agents, scope remote execution to registered workspaces, gate risky actions, survive disconnects, and collect auditable review evidence without turning those agents into a generic chat proxy.

Current signals: CI and security workflows pass on main, OpenSSF Scorecard is published, OpenSSF Best Practices is passing, releases use Sigstore signing, and the package is MIT licensed.

What It Provides Today

llm-cli-gateway is a single-user MCP control plane for operating AI coding agents from supported local or remote clients. It is more than a thin CLI wrapper:

  • Runs registered provider CLIs and configured HTTP API providers through consistent sync and async MCP tools.
  • Persists long-running jobs, supports restart-safe result collection, deduplication, cancellation, and sync-to-async deferral.
  • Tracks sessions, real CLI resume paths, structured response metadata, and cache telemetry.
  • Supports cache-aware promptParts, including explicit Claude cache_control when opted in.
  • Can run requests inside gateway-managed git worktrees for isolated multi-agent review and implementation loops.
  • Ships personal-appliance setup surfaces: HTTP transport with bearer-token auth, doctor --json, setup UI artifacts, provider setup snippets, Docker fallback, and checked release bundles.
  • Remote web connectors use MCP OAuth discovery and authorization-code setup with static client or shared-secret gates. Client secrets are generated locally, stored only as hashes, and printed only by explicit copy-once commands.
  • Provider CLI requests can select registered workspaces by alias via workspace; every HTTP/tunnel request must use a registered alias, session workspace, or [workspaces].default before provider execution. Local unrestricted filesystem access is the stdio transport.

Workflow Assets

The repo ships agent-ready workflow skills under .agents/skills for async orchestration, session continuity, multi-LLM review, implement-review-fix loops, retrospective evidence walks, and secure approval-gated dispatch. Seven caller-facing skills are bundled in the published npm package: async-job-orchestration, multi-llm-review, session-workflow, secure-orchestration, implement-review-fix, retrospective-walk, and public-demo-session. Machine-readable DAG-TOML plans live under docs/plans and setup/install-plan.dag.toml for workflows that need deterministic sequencing and verification gates.

Skill packs can be updated outside the core npm release by placing skill directories in local, operator-controlled paths. The gateway loads bundled skills first, then [skills].paths, then LLM_GATEWAY_SKILLS_PATH, then ~/.llm-cli-gateway/skills when it exists; later roots override earlier skills by name. Each skill is a directory containing SKILL.md. A root may also carry skill-pack.json to pin expected SKILL.md hashes:

[skills]
paths = ["/opt/llm-cli-gateway/skills"]
export LLM_GATEWAY_SKILLS_PATH="/opt/team-skill-pack:/opt/incident-skill-pack"
{
  "name": "team-pack",
  "version": "1.0.0",
  "skills": [
    {
      "name": "incident-retrospective",
      "sha256": "<sha256 of incident-retrospective/SKILL.md>"
    }
  ]
}

The loader is intentionally local-only: it never fetches remote Markdown at startup. To update a pack, install or replace files through your package manager or deployment system, then restart the gateway so the advertised skills://... resources refresh.

The next documentation focus is provider-specific skill and DAG-TOML pairs for each outbound CLI and API-provider family: Claude, Codex, Gemini/Antigravity, Grok, Mistral Vibe, Devin, Cursor Agent, OpenAI-compatible endpoints, Anthropic Messages, and xAI Responses. The implementation plan is tracked in docs/plans/provider-workflow-assets.dag.toml, with each provider asset expected to cover install/login checks or token-env checks, session behavior, approval modes, cache/telemetry surfaces, failure modes, and a smoke-test gate.

Trust & Supply Chain

OpenSSF Best Practices

  • CI runs build, lint, format, tests, package checks, and npm audit.
  • Security CI runs actionlint, zizmor, shellcheck, typos, osv-scanner, gitleaks, and lychee.
  • GitHub release installer artifacts are checksummed and signed with Sigstore keyless signing.
  • npm releases use a generated prod-only shrinkwrap and release security audit; the publish token is fetched at runtime from Azure Key Vault through GitHub OIDC to Entra.
  • The npm package intentionally ships a generated, prod-only npm-shrinkwrap.json so registry installs resolve the audited release tree. Release gates regenerate it from package-lock.json, compare for parity, and run a registry-fidelity consumer install before publishing.
  • Socket behavioural alerts are documented in socket.yml and under "Security Considerations" below. shellAccess and shrinkwrap are reviewed package capabilities/configuration for this CLI appliance, not hidden install behaviour.

Personal MCP Appliance

The personal-appliance contract keeps that surface intentionally narrow: one trusted user runs the gateway on a machine or volume they own, connects one MCP endpoint, and lets supported clients operate local coding agents through workspace-scoped, approval-gated, auditable requests.

The product contract is documented in docs/personal-mcp/PRODUCT_CONTRACT.md. It defines the single-user scope, security posture, target support matrix, and provider-support verification gates. Public setup guides must not claim ChatGPT, Claude web, Claude Desktop, Codex, Gemini CLI, Gemini web, or Grok inbound support until the corresponding provider/client path has been verified.

This project does not provide hosted multi-tenant credential custody. Provider credentials stay on the user's machine or user-owned deployment volume.

Release-readiness history is tracked in docs/personal-mcp/RELEASE_READINESS.md. Dogfooding evidence (which target LLMs guided setup, what unsafe suggestions were captured, and which findings were deferred from the initial personal-appliance rollout) is in docs/personal-mcp/DOGFOODING_RESULTS.md.

Current personal-appliance artifacts include:

  • Streamable HTTP startup: LLM_GATEWAY_AUTH_TOKEN=<token> npm run start:http
  • Machine-readable diagnostics: npm run doctor
  • Go bootstrapper: installer/ with setup, doctor --json, start, stop, status, repair, upgrade, uninstall, print-client-config, and verified bundle download commands.
  • Release packaging: the release workflow builds Linux binaries on the local self-hosted runner, builds Windows/macOS binaries on GitHub-hosted runners, then publishes checksummed platform bundles with the gateway, production dependencies, and a managed Node runtime; see installer/packaging/README.md.
  • Docker Compose fallback: docker/personal.compose.yml + docker/Dockerfile.personal for users who already manage containers.
  • Local setup UI artifact: setup/ui/index.html
  • Provider setup snippets: setup/providers/
  • Cross-validation tools: validate_with_models, second_opinion, compare_answers, red_team_review, consensus_check, ask_model, synthesize_validation, job_status, job_result, and validation_receipt (plus the validation-receipt://{validationId} resource).

Install / Upgrade / Uninstall (single binary)

Windows PowerShell:

$Version = '<version>'
$Base = "https://github.com/verivus-oss/llm-cli-gateway/releases/download/v$Version"
$InstallDir = Join-Path (Join-Path $env:LOCALAPPDATA 'Programs') 'llm-cli-gateway'
$ExeName = "llm-cli-gateway-$Version-windows-amd64.exe"
$BundleName = "llm-cli-gateway-bundle-$Version-windows-amd64.tar.gz"
$Exe = Join-Path $InstallDir 'llm-cli-gateway.exe'
$Checksums = Join-Path $InstallDir 'SHA256SUMS'
$ChecksumBundle = Join-Path $InstallDir 'SHA256SUMS.sigstore.json'
New-Item -ItemType Directory -Force $InstallDir | Out-Null
Invoke-WebRequest -UseBasicParsing "$Base/$ExeName" -OutFile $Exe
Invoke-WebRequest -UseBasicParsing "$Base/SHA256SUMS" -OutFile $Checksums
Invoke-WebRequest -UseBasicParsing "$Base/SHA256SUMS.sigstore.json" -OutFile $ChecksumBundle
cosign verify-blob $Checksums --bundle $ChecksumBundle --certificate-identity "https://github.com/verivus-oss/llm-cli-gateway/.github/workflows/release-installer.yml@refs/tags/v$Version" --certificate-oidc-issuer "https://token.actions.githubusercontent.com"
if ($LASTEXITCODE -ne 0) { throw "Sigstore verification failed for SHA256SUMS" }
function Get-ReleaseSha256($Name) {
  $line = Select-String -Path $Checksums -Pattern "^[a-fA-F0-9]{64}\s+$([regex]::Escape($Name))$" | Select-Object -First 1
  if (-not $line) { throw "No SHA256SUMS entry found for $Name" }
  return (($line.Line -split "\s+")[0]).ToLowerInvariant()
}
if ((Get-FileHash $Exe -Algorithm SHA256).Hash.ToLowerInvariant() -ne (Get-ReleaseSha256 $ExeName)) { throw "Checksum mismatch for $ExeName" }
$env:RVWR_GATEWAY_BUNDLE_URL = "$Base/$BundleName"
$env:RVWR_GATEWAY_BUNDLE_SHA256 = Get-ReleaseSha256 $BundleName
& $Exe setup
& $Exe stop
& $Exe install-bundle
& $Exe start
& $Exe status
& $Exe doctor

The Windows installer keeps a stable llm-cli-gateway.exe command in %LOCALAPPDATA%\Programs\llm-cli-gateway and adds that directory to the user PATH. Do not script against release-versioned exe names after install.

# After downloading the binary that matches your OS/arch from a release:
cosign verify-blob SHA256SUMS --bundle SHA256SUMS.sigstore.json \
  --certificate-identity "https://github.com/verivus-oss/llm-cli-gateway/.github/workflows/release-installer.yml@refs/tags/v<version>" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com"
sha256sum --check SHA256SUMS            # verify before run (or `shasum -a 256 --check` on macOS)
chmod +x llm-cli-gateway-<ver>-<os>-<arch>
./llm-cli-gateway-<ver>-<os>-<arch> setup
./llm-cli-gateway-<ver>-<os>-<arch> install-bundle    # uses the platform bundle URL/SHA256
./llm-cli-gateway-<ver>-<os>-<arch> start
./llm-cli-gateway-<ver>-<os>-<arch> doctor

# Upgrade: replace the binary, set the new bundle env vars, run upgrade.
./llm-cli-gateway-<new>-<os>-<arch> upgrade

# Uninstall: dry-run first, then run with --yes.
./llm-cli-gateway-<ver>-<os>-<arch> uninstall
./llm-cli-gateway-<ver>-<os>-<arch> uninstall --yes

Docker fallback:

LLM_GATEWAY_AUTH_TOKEN=$(openssl rand -hex 32) \
  docker compose -f docker/personal.compose.yml up -d
docker compose -f docker/personal.compose.yml run --rm doctor

Features

Core Capabilities

  • Multi-LLM Orchestration: Unified interface for Claude Code, Codex, Gemini, Grok, Mistral (Vibe), Devin, and Cursor Agent CLIs
  • Session Management: Track and resume conversations across all CLIs with persistent storage
  • Gateway-owned worktrees: Run any sync or async provider request inside a managed git worktree, with per-session reuse and cleanup
  • Token Optimization: Automatic 44% reduction on prompts, 37% on responses (opt-in)
  • Correlation ID Tracking: Full request tracing across all LLM interactions
  • Cross-Tool Collaboration: LLMs can use each other via MCP (validated through dogfooding)

Observability

  • SQLite Flight Recorder: Every request/response logged to ~/.llm-cli-gateway/logs.db with correlation IDs, token usage, duration, retry counts, and circuit breaker state. Browse with Datasette: datasette ~/.llm-cli-gateway/logs.db
  • Structured Metadata: Tool responses include machine-readable structuredContent (model, cli, correlationId, sessionId, durationMs, token counts)
  • Cache observability resources: cache-state://global, cache-state://session/{id}, and cache-state://prefix/{hash} MCP resources return aggregate cache hit/miss/savings — tokens and hashes only, no prompt text. session_get includes a cacheState block when the session has prior requests.
  • Provider capability inventory: provider_tool_capabilities and provider-tools://catalog expose the gateway request fields, supported/degraded provider controls, local skill/tool discovery, and safe config-surface hints for Claude Code, Codex CLI, Gemini/Antigravity, Grok CLI/API, Mistral Vibe, Cognition Devin, and Cursor Agent. doctor --json includes a compact provider_capabilities summary for setup assistants.

Cache-aware operation

Every *_request and *_request_async tool except devin_request / devin_request_async and cursor_request / cursor_request_async accepts an optional promptParts field that structures the prompt for better cache hit rates (the Devin and Cursor headless paths take a plain prompt only). The gateway concatenates the parts in canonical order (system → tools → context → task) so that the stable prefix bytes precede the volatile task tail unchanged across calls, letting each provider's automatic prompt-caching land on the same content hash each time.

{
  "promptParts": {
    "system": "You are a helpful code reviewer.",
    "tools": "You have access to Read, Grep, Bash.",
    "context": "<long stable context block — file dumps, etc.>",
    "task": "Review the changes in src/foo.ts for security issues."
  }
}

prompt and promptParts are mutually exclusive — pass exactly one.

Per-CLI capability matrix (prefix discipline is automatic via promptParts for all providers except Devin and Cursor, which have no promptParts surface; explicit levers are provider-specific):

CLIPrefix disciplineExplicit lever(s)
claudeyespromptParts.cacheControl + outputFormat: "stream-json" (Anthropic cache_control breakpoints on stable blocks; ttl="1h" forced)
codexyesnone (OpenAI implicit)
geminiyesnone (implicit server-side)
grokyescompactionMode / compactionDetail (context compaction: `summary
mistralyesnone (implicit)
devinnoplain prompt only
cursornoplain prompt only

Claude example (explicit cacheControl)

claude_request({
  promptParts: {
    system: "You are a helpful code reviewer.",
    context: "<long stable file dump>",
    task: "Review the diff.",
    cacheControl: { system: true, context: true }, // task is never marked
  },
  outputFormat: "stream-json",
});

Gateway emits the stream-json stdin path with cache_control: {type:"ephemeral", ttl:"1h"} on marked blocks only.

Grok example (compaction)

grok_request({
  promptParts: { system: "...", context: "...", task: "..." },
  compactionMode: "segments",
  compactionDetail: "balanced",
});

Emits --compaction-mode segments --compaction-detail balanced.

See docs/personal-mcp/PROVIDER_CACHE_SURFACES.md for full surfaces, telemetry differences (e.g. Grok -p vs ACP), exact stream-json payload shapes, and cross-LLM review notes.

Opt-in flags (all default off) live under [cache_awareness] in ~/.llm-cli-gateway/config.toml.

Reliability & Performance

  • Retry Logic: Exponential backoff with circuit breaker for transient failures
  • Atomic File Writes: Process-specific temp files with fsync for data integrity
  • Host-protection backpressure: bounded HTTP session lifecycle (max sessions + idle reaper), global and per-provider job-execution limits with a bounded FIFO queue, and a configurable per-job output cap (default 50MB). See Host-protection limits.
  • NVM Path Caching: Eliminates I/O overhead on every request
  • Long-Running Jobs: Non-time-bound async execution via *_request_async + polling tools

Security & Quality

  • Comprehensive Testing: 1,700+ tests covering unit, integration, and regression scenarios with real CLI execution
  • Input Validation: Zod schemas prevent injection attacks
  • No Secret Leakage: Generic session descriptions only (file permissions 0o600)
  • No ReDoS: Bounded regex patterns prevent catastrophic backtracking
  • Type Safety: Strict TypeScript with comprehensive error handling
  • Supply-chain hardening: a dedicated .github/workflows/security.yml runs actionlint, zizmor, shellcheck, typos, osv-scanner, gitleaks, and lychee on every push and PR (see SECURITY.md for the threat model)

Provider capability surface

Every provider is reachable through the same request, session, job, and validation machinery, but the underlying CLIs differ in what they natively expose. The table records what actually shipped per provider; discover the live surface at runtime with provider_tool_capabilities, list_models, and the provider-acp://<provider> / provider-tools://<provider> resources.

ProviderCLI request toolsNative ACPLive model discoveryAdmin surface
Claude Code (claude)claude_request / _asyncNone (CLI-first; no ACP entrypoint at claude 2.1.198)model aliases, reasoning-effort levels, fallback modelread-only via provider_admin_list / provider_admin_run
OpenAI Codex (codex)codex_request / _async, codex_fork_sessionNone (codex-cli 0.142.4 advertises mcp-server / app-server transports, not native ACP)codex debug modelsread-only via provider_admin_list / provider_admin_run
Gemini / Antigravity (gemini, agy)gemini_request / _asyncNone (agy 1.0.14 exposes no ACP entrypoint; legacy Gemini CLI ACP evidence does not transfer)agy modelsread-only via provider_admin_list / provider_admin_run
xAI Grok (grok)grok_request (sync transport: "acp") / _asyncNative via grok agent stdiogrok models + ~/.grok/config.tomlread-only via provider_admin_list / provider_admin_run
Mistral Vibe (mistral)mistral_request (sync transport: "acp") / _asyncNative via vibe-acpVibe config plus the VIBE_ACTIVE_MODEL active model and agent profilesread-only via provider_admin_list / provider_admin_run
Cognition Devin (devin)devin_request (sync transport: "acp", agentType: summarizer|review) / _asyncNative via devin acp--model / DEVIN_MODELread-only via provider_admin_list / provider_admin_run
Cursor Agent (cursor)cursor_request (sync transport: "acp") / _asyncNative via cursor-agent acp (companion-owned)model aliasesread-only via provider_admin_list / provider_admin_run
  • Native ACP is reported honestly. grok, mistral, devin, and cursor expose a native ACP entrypoint, so provider-acp://<provider> carries the negotiated initialize capability set and the derived session-method availability, and the sync *_request accepts transport: "acp" (fails closed unless [acp] and the provider's runtime_enabled gate are set). ACP routing is sync-only: the *_request_async variants always run the CLI transport and do not accept transport: "acp" (nor Devin's agentType); async ACP parity is a later phase. claude, codex, and gemini have no native ACP entrypoint at their target CLI versions; their provider-acp:// records report native: false with no methods and no adapter-as-native masquerade, and they expose no transport: "acp" selector.
  • Resources are generated from the provider registry for every CLI provider: models://<provider>, sessions://<provider>, provider-acp://<provider>, provider-tools://<provider>, and provider-subcommands://<provider>.
  • Model discovery is live and account-aware: the discovery listed above reaches models://<provider> and list_models, degrading to static registry facts when a live probe is unavailable (a resource read never spawns a CLI).
  • Admin surfaces are discovery-driven and output-redacted. provider_admin_list and provider_admin_run are read-only for every provider. State-mutating admin operations are exposed only through provider_admin_mutate, gated behind [admin] allow_mutating_cli_admin_ops, the remote cli:admin scope, an approval gate, and an audit record. Mutating ACP session operations are likewise gated behind [acp] allow_mutating_session_ops.
  • Validation commands work across every provider: validate_with_models, second_opinion, compare_answers, red_team_review, consensus_check, ask_model, and synthesize_validation, with durable signed receipts via validation_receipt and the validation-receipt://{validationId} resource.