简体中文 | English
CLI Gateway is a Go gateway that turns declarative HTTP API mappings into an authenticated CLI command catalog and MCP Streamable HTTP tools. Both transports use the same identity, policy, argument validation, downstream credential, rate-limit, tracing, and audit pipeline.
Status: pre-1.0. The core security and execution paths are tested, but configuration compatibility is not guaranteed until the first stable release.
- One governed API entry point for humans, CI jobs, and AI agents.
- Dynamic CLI commands without writing a separate client for every service.
- MCP tools generated from the same server-side manifest, with MCP
2026-07-28stateless requests and legacy stateful clients on one endpoint. - OIDC Authorization Code + PKCE and Device Authorization for the CLI.
- Explicit downstream authentication per API:
signed-identity, RFC 8693token-exchange,client-credentials, per-userauthorization-code, and reviewedbearer-passthrough. - Command allowlists, risk levels, confirmations, limits, circuit breakers, structured errors, metrics, and audit sinks.
- Build-time white-label clients with isolated environment variables, local storage, keyring entries, User-Agent, and manifest validation.
Codex, Claude, Claude Code, and other agents can either run the generated CLI
commands or connect directly to /mcp. Both paths reach the same governed
gateway pipeline and backend services.
flowchart LR
Codex["Codex"] --> Access["Access methods<br/>CLI commands: cg or a branded CLI<br/>MCP endpoint: /mcp"]
Claude["Claude / Claude Code"] --> Access
Other["Other AI agents"] --> Access
Access --> Gateway["CLI Gateway<br/>one governed execution pipeline"]
Gateway --> Internal["Internal services"]
Gateway --> Cloud["Cloud APIs"]
Gateway --> SaaS["GitHub / SaaS APIs"]
| Goal | Time | Guide |
|---|---|---|
| Run everything locally with no account | 5 min | Bundled demo |
| Test a real, fully local OIDC provider | 10 min | Keycloak |
| Use a hosted free-plan OIDC provider | 15 min | Auth0 Device Flow |
| Proxy GitHub with each user's identity | 20 min | GitHub App |
Requirements: Docker with Compose, curl, and jq.
docker compose up --build -d
TOKEN="$(curl -fsS http://127.0.0.1:18080/token | jq -r .access_token)"
curl -fsS http://127.0.0.1:18082/readyz
curl -fsS -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:18082/manifest | jq
curl -fsS -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"args":{"delay-ms":5}}' \
http://127.0.0.1:18082/exec/demo/work | jq
docker compose downThe bundled issuer and API are development fixtures. Never use them as production identity or business services.
Requirements: Go 1.25.12 or newer.
make verify
make build
sudo install -m 0755 bin/cli-gateway bin/cg /usr/local/bin/
cli-gateway -manifest manifest.example.yaml
cg --helpBuild only one component:
make build-gateway
make build-clientAfter the first public release, install the latest checksum-verified client with:
curl --proto '=https' --tlsv1.2 -fsSL \
https://raw.githubusercontent.com/wyh0626/cli-gateway/main/scripts/install.sh | shPin a release with CG_VERSION=v0.3.0. Review downloaded scripts before
executing them in a production environment.
Start the local issuer, demo API, and gateway in separate terminals:
go run ./cmd/devissuer
go run ./cmd/demoapi
go run ./cmd/cli-gateway -manifest examples/e2e/manifest.yamlThen authenticate and invoke the generated command:
export CG_SERVER=http://127.0.0.1:18082
cg login --device
# Open the printed URL and approve the device code.
cg capabilities -o json
cg demo work --delay-ms 5 -o json
cg whoamiOmit --device to use Authorization Code + PKCE with a random loopback
callback port. The CLI requests identity scopes only; command permissions do
not need to appear on the OAuth consent page. Each backend remains responsible
for business authorization.
cg capabilities returns the complete command and argument schema visible to
the current identity. Terminal agents can discover capabilities instead of
depending on a duplicated, stale command list:
CG_INVOKER=ai cg capabilities -o json
CG_INVOKER=ai cg demo stats -o jsonStart from manifest.example.yaml:
cli:
name: cg
domains:
- name: inventory
upstream: https://inventory.example.com
downstream_auth:
mode: signed-identity
identity_audience: inventory-api
commands:
- path: [item, get]
summary: Get an inventory item
method: GET
endpoint: /v1/items/{id}
risk: read
flags:
- name: id
type: string
required: true
in: pathAfter a gateway reload and cg update-commands, invoke it with:
cg inventory item get --id server-42The same command is exposed as an MCP tool at /mcp. The endpoint negotiates
MCP 2026-07-28 stateless requests and legacy stateful Streamable HTTP
sessions. Both variants reauthorize every execution; the local CLI cache is
never an authorization authority.
For a backend with its own user OAuth account, configure the
authorization-code downstream mode. CLI Gateway encrypts the backend refresh
token and coalesces refreshes. A backend may also return a safe open_url
action in a successful response:
{
"status": "PENDING_USER_AUTHORIZATION",
"userAction": {
"type": "open_url",
"url": "https://accounts.vendor.example/oauth/authorize?state=request-123",
"message": "Additional authorization is required",
"completionCommand": "cg inventory request get --id request-123"
}
}An interactive CLI shows the host and asks before opening the HTTPS URL. Without a TTY it prints the URL only. The completion command is displayed but never executed automatically.
The GitHub App tutorial provides a complete working
example using PKCE, client_secret_post, encrypted per-user refresh tokens,
and read-only GitHub commands.
The default client is cg. Build an isolated client for another organization:
make build-client CLI_NAME=acme
./bin/acme --help
# The gateway manifest must match:
# cli:
# name: acmeCLI_NAME=acme derives ACME_*, platform-specific acme config/cache
directories, acme-cli-refresh-token, and acme-cli/<version>. See
the white-label guide for advanced overrides.
The inbound CLI token authenticates the caller to CLI Gateway. It is not sent to a backend unless that domain deliberately enables reviewed bearer passthrough.
| Mode | Typical use |
|---|---|
signed-identity |
A first-party API trusts a short-lived gateway identity |
token-exchange |
The authorization server supports RFC 8693 delegation |
client-credentials |
The backend needs an application identity |
authorization-code |
The backend has its own user OAuth account |
bearer-passthrough |
Exceptional same-token trust-domain integration |
See Authentication and identity for the complete flows and trade-offs.
The container embeds checksum files and platform binaries for macOS and Linux
on amd64/arm64. After deploying CLI Gateway with an HTTPS server.public_url,
users can install the client and configure that gateway in one command:
curl --proto '=https' --tlsv1.2 -fsSL \
https://gateway.example.com/install.sh | sh
cg login
cg capabilitiesThe download route is an exact artifact allowlist; it cannot read arbitrary files from the container. Review the script before using it in production.
docker build -t cli-gateway:local .
kubectl apply -f deploy/kubernetes/cli-gateway.yamlBefore production, replace every example issuer, audience, URL, image, key, and secret; configure TLS and ingress; select downstream trust per domain; and review the production checklist.
Current scaling boundaries:
- Token caches, limiters, circuit breakers, pending downstream OAuth state, and legacy MCP sessions are process-local.
- The encrypted file token store is a single-writer, single-process store.
- Legacy stateful MCP sessions need load-balancer affinity with multiple
replicas. MCP
2026-07-28requests are stateless and need no affinity. - Global quotas and shared credential caches require an intentionally selected distributed implementation.
make verify
make test-raceThe official MCP Go SDK integration is covered by:
go test ./internal/adapter/mcp- Documentation index
- Getting started
- Auth0 tutorial
- Keycloak tutorial
- GitHub App tutorial
- Implementation specification
- Authentication and identity
- Threat model
- White-label CLI
- Production checklist
- Design references
- Roadmap
- Security policy
- Contributing
- Support
- Governance
- Changelog
Licensed under the Apache License 2.0.