Use Claude via Kiro — from any tool you already love.
giro is a lightweight API gateway that translates OpenAI and Anthropic API formats to the Kiro API (AWS CodeWhisperer / Amazon Q Developer). Point your favorite AI coding tool at localhost:8080 and go.
┌─────────────────────────────────┐
│ Claude Code · OpenCode · Droid │
│ Cursor · aider · any client │
└───────────────┬─────────────────┘
│ OpenAI or Anthropic format
▼
┌──────────┐
│ giro │ translates + authenticates
└────┬─────┘
│ Kiro API format
▼
Kiro API (AWS)
curl -fsSL https://raw.githubusercontent.com/miltonparedes/giro/main/install.sh | shDetects OS and architecture automatically (Linux & macOS, amd64 & arm64). To install elsewhere:
curl -fsSL https://raw.githubusercontent.com/miltonparedes/giro/main/install.sh | INSTALL_DIR=~/.local/bin shFrom source:
go install github.com/miltonparedes/giro/cmd/giro@latestOr grab a binary from the releases page.
# 1 — Start it
just dev # or: go run ./cmd/giro
# giro now autodetects default local Kiro stores when available:
# - kiro-cli SQLite DB
# - kiro-ide credentials file
#
# Optional explicit overrides still win:
# export KIRO_CLI_DB_FILE="~/Library/Application Support/kiro-cli/data.sqlite3" # macOS
# export KIRO_CLI_DB_FILE="~/.local/share/kiro-cli/data.sqlite3" # Linux
# export KIRO_CREDS_FILE="path/to/credentials.json"
# export REFRESH_TOKEN="your-token"
# 2 — Check it's alive
curl localhost:8080/healthThat's it. Now point your tools at http://localhost:8080.
export ANTHROPIC_BASE_URL="http://localhost:8080"
export ANTHROPIC_API_KEY="your-proxy-api-key" # must match PROXY_API_KEY if set
# If giro rejects anthropic-beta headers:
export CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1
claudeOr make it permanent in ~/.claude/settings.json:
{
"env": {
"ANTHROPIC_BASE_URL": "http://localhost:8080",
"ANTHROPIC_API_KEY": "your-proxy-api-key"
}
}Drop an opencode.json in your project root (or ~/.config/opencode/opencode.json for global):
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"giro": {
"npm": "@ai-sdk/openai-compatible",
"name": "Giro (Kiro Proxy)",
"options": {
"baseURL": "http://localhost:8080/v1",
"apiKey": "{env:PROXY_API_KEY}"
},
"models": {
"claude-sonnet-4": {
"name": "Claude Sonnet 4",
"limit": { "context": 200000, "output": 65536 }
},
"claude-opus-4-6": {
"name": "Claude Opus 4.6",
"limit": { "context": 200000, "output": 32768 }
}
}
}
},
"model": "giro/claude-sonnet-4"
}Add a custom model in ~/.factory/settings.json:
{
"customModels": [
{
"model": "claude-sonnet-4",
"displayName": "Giro — Sonnet 4",
"baseUrl": "http://localhost:8080/v1",
"apiKey": "${PROXY_API_KEY}",
"provider": "anthropic"
},
{
"model": "claude-opus-4-6",
"displayName": "Giro — Opus 4.6",
"baseUrl": "http://localhost:8080/v1",
"apiKey": "${PROXY_API_KEY}",
"provider": "anthropic"
}
]
}Then select it in Droid with /model or pass --model "custom:Giro-0" in headless mode.
Set the API base URL to http://localhost:8080/v1 and pick any model from /v1/models. If you set PROXY_API_KEY, enter that as the API key.
curl http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer your-proxy-api-key" \
-H "Content-Type: application/json" \
-d '{"model": "claude-sonnet-4", "messages": [{"role": "user", "content": "Hello!"}]}'curl http://localhost:8080/v1/messages \
-H "x-api-key: your-proxy-api-key" \
-H "Content-Type: application/json" \
-d '{"model": "claude-sonnet-4", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello!"}]}'| Method | Path | What it does |
|---|---|---|
GET |
/health |
Health check |
GET |
/v1/models |
List available models (OpenAI format) |
POST |
/v1/chat/completions |
Chat completions (OpenAI format) |
POST |
/v1/messages |
Messages (Anthropic format) |
- Speaks both OpenAI and Anthropic protocols simultaneously
- Streaming (SSE), tool calling, and vision support
- Secure autodetection of local
kiro-cliandkiro-idecredentials - Automatic token refresh — you authenticate once, giro keeps it alive
- Explicit env configuration still overrides autodetection when you want to pin a source
- Model name normalization — use friendly names like
claude-sonnet-4
giro handles two auth layers:
- Upstream — giro talks to Kiro using your credentials (refresh token, credentials file, or CLI database)
- Client (optional) — your tools talk to giro using a
PROXY_API_KEYyou define
You need an active Kiro session. giro now tries the easiest path first:
- explicit env-backed sources if you set them
- autodetected default
kiro-clistore - autodetected default
kiro-idestore
So in many setups you can just run just dev with no credential env vars at all.
If you want to override autodetection, pick one:
| Method | How |
|---|---|
| Autodetect (default) | Start giro normally and let it discover the default local kiro-cli / kiro-ide store on your machine. |
| Kiro CLI database (easiest) | Set KIRO_CLI_DB_FILE to your Kiro SQLite DB path. Tokens refresh automatically. |
| Credentials file | Set KIRO_CREDS_FILE to a JSON file with refreshToken, accessToken, profileArn, etc. |
| Direct token | Set REFRESH_TOKEN (and optionally PROFILE_ARN). Quick but not persisted across restarts. |
Auth type is detected automatically — if the DB has clientId + clientSecret, it uses AWS SSO OIDC; otherwise it uses the Kiro Desktop flow.
Off by default. To require it:
export PROXY_API_KEY="my-secret-key"Clients send this as Authorization: Bearer <key> (OpenAI) or x-api-key: <key> (Anthropic).
| Variable | Default | Description |
|---|---|---|
HOST |
0.0.0.0 |
Bind address |
PORT |
8080 |
Listen port |
LOG_LEVEL |
info |
debug · info · warn · error |
| Variable | Description |
|---|---|
KIRO_CLI_DB_FILE |
Path to Kiro CLI SQLite database |
KIRO_CREDS_FILE |
Path to JSON credentials file |
REFRESH_TOKEN |
Kiro refresh token directly |
PROFILE_ARN |
AWS CodeWhisperer profile ARN |
KIRO_REGION |
AWS region (default: us-east-1) |
| Variable | Default | Description |
|---|---|---|
PROXY_API_KEY |
(none) | API key clients must provide |
VPN_PROXY_URL |
(none) | HTTP proxy for upstream Kiro requests |
| Variable | Default | Description |
|---|---|---|
STREAMING_READ_TIMEOUT |
300 |
Max seconds waiting for streaming data |
FIRST_TOKEN_TIMEOUT |
15 |
Max seconds waiting for first token |
FIRST_TOKEN_MAX_RETRIES |
3 |
Retries on first-token timeout |
FAKE_REASONING |
true |
Detect <thinking> tags |
FAKE_REASONING_HANDLING |
as_reasoning_content |
as_reasoning_content · remove · pass · strip_tags |
TRUNCATION_RECOVERY |
true |
Retry on truncated responses |
DEBUG_MODE |
off |
off · errors · all |
Requires: Go 1.24+ · just · gofumpt · golangci-lint v2
just build # → bin/giro
just test # tests with race detection
just check # fmt + lint + test (run before committing)
just validate # boots local giro and runs the full live validation matrix
just cover # tests with coverage report → coverage.htmlRun just to see all available commands.