@axiom-lattice/cli-a2a
v0.1.1
Published
Multi-provider A2A (Agent-to-Agent) CLI gateway wrapping local coding agents (OpenCode, Codex, Claude Code) as spec-compliant A2A servers
Maintainers
Readme
@axiom-lattice/cli-a2a
Multi-provider A2A (Agent-to-Agent) CLI gateway. Wraps local coding agents
(OpenCode, Codex, Claude Code) as spec-compliant A2A servers. Drop a JSON
config, get a fully spec-compliant A2A agent — launchable via npx.
Quick Start
# 1. Start your local OpenCode server
opencode serve
# 2. Launch the A2A gateway
npx @axiom-lattice/cli-a2a --provider opencode --port 3000
# 3. Verify
curl http://localhost:3000/health
# → { "status": "healthy", "agent": "CLI A2A Agent", "provider": "opencode" }
curl http://localhost:3000/.well-known/agent-card.json
# → A2A v0.3 AgentCard JSONProviders
| Provider | Status | Connection |
|----------|--------|------------|
| opencode | ✅ Ready | HTTP API (opencode serve) |
| codex | ✅ Ready | CLI subprocess (codex) |
| claude | ✅ Ready | CLI subprocess (claude -p) |
Usage
npx @axiom-lattice/cli-a2a [options]Options
--provider <name> Provider: opencode | codex | claude
--config, -c <path> Path to agent JSON config file
--port, -p <number> A2A server port (default: 3000)
--hostname <addr> Bind address (default: 0.0.0.0)
--advertise-host <host> Hostname for agent card URLs
--agent-name <name> Agent display name
--agent-description <desc> Agent description
--log-level <level> debug | info | warn | error (default: info)OpenCode Options
--opencode-url <url> OpenCode server URL (default: http://localhost:4096)
--directory, -d <path> Project directory
--model, -m <provider/model> LLM model
--agent <name> OpenCode agent presetCodex Options
--codex-model <model> Codex model ID (e.g. gpt-4o)
--codex-api-key <key> OpenAI API keyClaude Code Options
--claude-model <model> Claude Code model ID
--claude-api-key <key> Anthropic API keyGateway Registration
The CLI can auto-register with an @axiom-lattice/gateway server so the
gateway discovers and routes to it as a first-class remote agent.
--register-url <url> Gateway URL to register this agent with
--register-key <key> API key for gateway authentication
--unregister-on-shutdown Remove agent from gateway on exitnpx @axiom-lattice/cli-a2a \
--provider opencode \
--port 3001 \
--register-url http://gateway:8080 \
--register-key sk-a2a-gateway-key \
--unregister-on-shutdown &On startup the CLI POSTs to {registerUrl}/api/assistants with a
graphDefinition of type a2a_remote pointing to its own agent card.
On exit (if --unregister-on-shutdown) it DELETEs the assistant.
After registration the agent appears alongside all other agents in the
gateway and can be invoked via the standard /api/runs endpoint.
Configuration File
{
"provider": "opencode",
"agentCard": {
"name": "My Coding Agent",
"description": "A coding assistant powered by OpenCode",
"skills": [
{
"id": "code-gen",
"name": "Code Generation",
"description": "Generate and refactor code",
"tags": ["coding", "typescript"]
}
]
},
"server": {
"port": 3000,
"advertiseHost": "localhost"
},
"opencode": {
"baseUrl": "http://localhost:4096",
"projectDirectory": "/path/to/project",
"model": "anthropic/claude-sonnet-4"
},
"features": {
"autoApprovePermissions": true,
"streamArtifactChunks": false
},
"timeouts": {
"prompt": 600000
}
}Config layers merge in priority order: defaults ← config file ← env vars ← CLI args.
Environment Variables
| Variable | Maps to |
|----------|---------|
| OPENCODE_URL | opencode.baseUrl |
| OPENCODE_DIRECTORY | opencode.projectDirectory |
| OPENCODE_MODEL | opencode.model |
| OPENCODE_AGENT | opencode.agent |
| OPENAI_API_KEY | codex.apiKey |
| ANTHROPIC_API_KEY | claude.apiKey |
Endpoints
| Method | Path | Description |
|--------|------|-------------|
| GET | /health | Health check |
| GET | /.well-known/agent-card.json | A2A Agent Card |
| POST | /a2a/jsonrpc | A2A JSON-RPC transport |
| POST | /a2a/rest | A2A REST transport |
Programmatic API
import {
createA2AServer,
resolveConfig,
registerExecutor,
createOpenCodeExecutor,
} from '@axiom-lattice/cli-a2a';
// Register executors
registerExecutor('opencode', createOpenCodeExecutor);
// Load config
const config = resolveConfig('agent-config.json');
// Start server
const server = await createA2AServer(config);
// Graceful shutdown
process.on('SIGINT', async () => {
await server.shutdown();
process.exit(0);
});Architecture
┌──────────────────────────────────────────────────────┐
│ cli-a2a (Express A2A Server) │
│ │
│ ┌─────────────┐ ┌──────────┐ ┌──────────────────┐│
│ │ Agent Card │ │ JSON-RPC │ │ REST ││
│ └─────────────┘ └──────────┘ └──────────────────┘│
│ │ │
│ ┌──────────────────────▼───────────────────────────┐│
│ │ Executor Registry ││
│ │ ┌────────────┐ ┌─────────┐ ┌──────────────────┐ ││
│ │ │ OpenCode │ │ Codex │ │ Claude Code │ ││
│ │ │ Executor │ │ Executor│ │ Executor │ ││
│ │ └─────┬──────┘ └────┬────┘ └───────┬──────────┘ ││
│ └────────┼─────────────┼──────────────┼────────────┘│
└───────────┼─────────────┼──────────────┼─────────────┘
│ HTTP │ spawn │ spawn
┌───────▼────────┐ ┌──▼───────┐ ┌───▼────────────┐
│ opencode serve │ │ codex CLI│ │ claude CLI (-p) │
└────────────────┘ └──────────┘ └────────────────┘Networking — Exposing to External Networks
The agent runs a local HTTP server. To reach it from outside, use the built-in WebSocket bridge (no SSH tunnel or ngrok required).
Built-in WebSocket Bridge (Recommended)
# Agent — opens outbound WebSocket to gateway
npx @axiom-lattice/cli-a2a \
--provider opencode \
--port 3001 \
--bridge-url ws://gateway:8080/api/a2a/bridge \
--bridge-key sk-xxx \
--register-url http://gateway:8080 \
--register-key sk-xxxInternal Network (agent) Public (gateway)
┌──────────────────┐ ┌─────────────────────┐
│ cli-a2a :3001 │──WebSocket──→│ /api/a2a/bridge │
│ ↑ │ outbound │ ↑ │
│ │ local call │ │ │ HTTP proxy │
│ │ │ │ /api/a2a/bridge/ │
│ Express A2A │ │ proxy/{agentId}/* │
│ OpenCode/Codex/ │ │ │
│ Claude │ │ A2A_REMOTE agent → │
└──────────────────┘ └─────────────────────┘How it works:
- Agent opens an outbound WebSocket to gateway (outbound almost never blocked)
- Gateway stores the connection keyed by agent ID
- Gateway exposes
GET/POST /api/a2a/bridge/proxy/{agentId}/* - When the orchestrator calls the proxy URL, gateway pushes the request over WebSocket
- Agent makes a local HTTP call to its Express server, returns the response
The agent's agentCardUrl in registration points to
http://gateway:8080/api/a2a/bridge/proxy/{agentId}/.well-known/agent-card.json.
SSH Reverse Tunnel (Alternative)
ssh -R 3001:localhost:3001 [email protected] -N &
npx cli-a2a --provider opencode --port 3001 \
--advertise-host public-bastion.com \
--register-url http://gateway:8080 --register-key sk-xxxSame Network
npx cli-a2a --provider opencode --port 3001 \
--advertise-host 192.168.1.42 \
--register-url http://gateway:8080 --register-key sk-xxxMulti-Project
Run multiple instances on different ports for different projects:
npx @axiom-lattice/cli-a2a --provider opencode -d /proj/frontend --port 3001 &
npx @axiom-lattice/cli-a2a --provider opencode -d /proj/backend --port 3002 &Package Exports
| Export | Description |
|--------|-------------|
| createA2AServer(config) | Start an Express A2A server |
| resolveConfig(path?, overrides?) | Merge config layers |
| loadConfigFile(path) | Read and parse a JSON config file |
| registerExecutor(provider, factory) | Register an executor factory |
| createOpenCodeExecutor(config) | Create an OpenCode executor |
| OpenCodeExecutor | Executor class (for extension) |
| logger, LogLevel | Structured logger |
License
ISC
