Use Kiro models from inside Opencode by reusing your existing kiro-cli session — or by pasting your own KIRO_API_KEY. The plugin runs in-process: it wraps kiro-cli acp via kiro-acp-ai-provider and translates between OpenAI Chat Completions and the AI SDK v3 stream surface that Opencode already understands. No separate gateway server, no localhost ports.
- Claude Opus 4.7 / 4.6, Sonnet 4.6, Haiku 4.5 through Kiro
- DeepSeek 3.2 and Qwen3 Coder Next when your Kiro plan exposes them
- Two auth modes —
KIRO_API_KEYpaste or existingkiro-cli loginsession - Multi-account rotation — add multiple Kiro keys, plugin auto-fails over on rate limit / quota / 5xx
- Three rotation strategies —
sticky,round-robin,hybrid(default: stick to one account, switch only on errors) - Streaming + tool calls — full SSE pipeline, including assistant
tool_calls - First-chunk failover — streaming requests can switch accounts before any byte reaches the client
- Auto-config — one click to add provider/model entries to
opencode.json - Secret hygiene — keys stored in
~/.config/opencode/kiro-accounts.json(mode0600); never written toopencode.json; logs redactksk_*, bearer, JWT, and OAuth token fields - In-process — no daemon, no localhost server, no
ngrok-style routing
⚠️ Unofficial — Read Before Installing
[!CAUTION] This is an unofficial local interoperability plugin. You must bring your own Kiro account / subscription / API key. By installing you acknowledge:
- The plugin is not endorsed by Kiro.
- You are responsible for compliance with Kiro's terms of service.
- The plugin does not sell, share, or proxy any account.
- APIs and quotas may change without notice.
For Humans
Option A — Let an LLM do it
Paste this into Opencode, Claude Code, or any agent:
Install the opencode-kiro-plugin and add Kiro model definitions to ~/.config/opencode/opencode.json by following: https://raw.githubusercontent.com/d-init-d/opencode-kiro-plugin/main/README.md
Option B — Manual
-
Add the plugin to
~/.config/opencode/opencode.json:{ "plugin": ["opencode-kiro-plugin@latest"] } -
Authenticate. Pick one:
opencode auth login # → Kiro → "Kiro API Key" (paste a `ksk_...` value) # or # → Kiro → "Use existing kiro-cli login"
-
Add models — choose one:
- Run
opencode auth login→ Kiro → "Configure Kiro models in opencode.json" (auto-configures everything below) - Or copy the full configuration yourself
- Run
-
Use it:
opencode run "Hello" --model=kiro/claude-opus-4.6
For LLM Agents
- Edit
~/.config/opencode/opencode.json(works on Windows, macOS, Linux —~resolves to the user home). - Add
"opencode-kiro-plugin@latest"to thepluginarray. - Add the model definitions from Models.
- Set
providerto"kiro"(or whatever you registered) and pick a model.
opencode run "Hello" --model=kiro/claude-opus-4.7If the call fails with kiro-cli chưa được cài install kiro-cli and run kiro-cli login once.
| Model | Streaming | Tool calls | Vision | Notes |
|---|---|---|---|---|
auto |
✅ | ✅ | — | Lets Kiro pick the best backing model |
claude-opus-4.7 |
✅ | ✅ | ✅ | Latest Opus tier |
claude-opus-4.6 |
✅ | ✅ | ✅ | Default for model |
claude-sonnet-4.6 |
✅ | ✅ | ✅ | Default for small_model |
claude-haiku-4.5 |
✅ | ✅ | ✅ | Lower latency |
deepseek-3.2 |
✅ | ✅ | — | Subject to Kiro plan |
qwen3-coder-next |
✅ | ✅ | — | Subject to Kiro plan |
Availability depends on what your Kiro account is entitled to. The plugin asks
kiro-acp-ai-providerfor a dynamic list when supported and falls back to the catalog above.
Full models configuration (copy-paste ready)
Add this to your ~/.config/opencode/opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-kiro-plugin@latest"],
"provider": {
"kiro": {
"npm": "@ai-sdk/openai-compatible",
"name": "Kiro",
"options": {
"baseURL": "https://kiro.local/v1"
},
"models": {
"auto": { "name": "Kiro Auto" },
"claude-opus-4.7": { "name": "Kiro Claude Opus 4.7" },
"claude-opus-4.6": { "name": "Kiro Claude Opus 4.6" },
"claude-sonnet-4.6": { "name": "Kiro Claude Sonnet 4.6" },
"claude-haiku-4.5": { "name": "Kiro Claude Haiku 4.5" },
"deepseek-3.2": { "name": "Kiro DeepSeek 3.2" },
"qwen3-coder-next": { "name": "Kiro Qwen3 Coder Next" }
}
}
},
"model": "kiro/claude-opus-4.6",
"small_model": "kiro/claude-sonnet-4.6"
}The
https://kiro.local/v1host is synthetic. The plugin'sauth.loader.fetchintercepts every request to it; no DNS lookup or outbound connection is made to that hostname.
Add several Kiro API keys (and optionally one existing kiro-cli login session) so the plugin can fail over automatically when one account is rate-limited, quota-exhausted, or returns 5xx errors. This mirrors the behavior of opencode-antigravity-auth's multi-account flow but uses Kiro's API key model instead of OAuth.
opencode auth login
# → Kiro → "Kiro API Key (add account)" # repeat for each `ksk_...` you own
# → Kiro → "Use existing kiro-cli login" # optional fallbackEach invocation appends to ~/.config/opencode/kiro-accounts.json (mode 0600). Duplicate keys are detected and ignored.
opencode auth login
# → Kiro → "List Kiro accounts"
# → Kiro → "Manage Kiro accounts (enable/disable/remove)"
# → Kiro → "Set rotation strategy (sticky / round-robin / hybrid)"You can also run the kiro_accounts tool inside any Opencode session to see the current state without leaving the chat.
| Strategy | When to use |
|---|---|
hybrid (default) |
Best for most users. Sticks to the first eligible account; switches only when it hits an error. Preserves prompt cache. |
sticky |
Single-account workflows. Same as hybrid but won't auto-rotate even when an account is on cooldown — surfaces the error directly. |
round-robin |
Many short requests. Picks the least recently used eligible account every call to spread load evenly. |
When an account fails, the plugin classifies the error and applies the right cooldown before considering it eligible again:
| Error class | Detection | Cooldown |
|---|---|---|
rate_limit |
HTTP 429 or "rate limit / throttled" wording | 60s, doubling per consecutive failure, capped at 30 min |
quota_exceeded |
HTTP 402 or "quota / billing / credits" wording | 15 min flat |
transient |
HTTP 5xx, ECONNRESET, fetch failed, "bad gateway" |
10s, doubling, capped at 5 min |
auth |
HTTP 401/403 or "invalid api key / forbidden" wording | 24 h and account is auto-disabled (re-add to recover) |
client_error |
HTTP 4xx other than auth/rate | not penalized — request is malformed |
unknown |
catch-all | 30s short cooldown |
Auth errors are intentionally not retried on a different account — the request is the problem, not the account choice. The original error is surfaced to the caller.
The plugin returns HTTP 429 with a Retry-After header and a JSON body:
{
"error": {
"type": "kiro_all_accounts_exhausted",
"message": "Tất cả tài khoản Kiro đều đang cooldown. Thử lại sau 42s. Chi tiết: work[rate_limit], personal[rate_limit]",
"attempts": [
{ "account": "work", "kind": "rate_limit", "message": "..." },
{ "account": "personal", "kind": "rate_limit", "message": "..." }
]
}
}Retry-After is set to the number of seconds until the first account becomes eligible again, so well-behaved clients (including Opencode) can back off automatically.
If something gets stuck, delete the store and re-add accounts:
rm ~/.config/opencode/kiro-accounts.json
opencode auth loginThe plugin holds no in-memory state beyond the lifetime of a single Opencode session, so a restart also clears any transient cooldown timers.
kiro-cli login # opens browser, authenticates once
opencode auth login
# → Kiro → "Use existing kiro-cli login"The plugin calls verifyAuth() from kiro-acp-ai-provider (when available) and otherwise lets the first request surface the real session error. The plugin never writes to or reads from your Kiro token cache.
opencode auth login
# → Kiro → "Kiro API Key"
# paste a value that looks like `ksk_...`The key is stored by Opencode's own auth subsystem. The plugin only injects it into the child kiro-cli process via the KIRO_API_KEY environment variable; it is never written to opencode.json, kiro.json, logs, or repo files.
If you set
KIRO_API_KEYin your shell, that value will override an existingkiro-cli loginsession for the child process. Unset it (unset KIRO_API_KEY/Remove-Item Env:KIRO_API_KEY) to use the CLI session.
Opencode
└─ provider: kiro / @ai-sdk/openai-compatible
└─ auth.loader.fetch (in-process interceptor)
└─ /v1/models, /v1/chat/completions
└─ OpenAI ↔ AI SDK v3 translator
└─ kiro-acp-ai-provider
└─ kiro-cli acp (subprocess)
└─ Kiro backend
- Opencode's
@ai-sdk/openai-compatibleprovider builds a realRequestforhttps://kiro.local/v1/.... - The plugin's
auth.loader.fetchrecognises the synthetic host and short-circuits the request inside the same Node process. - The request is validated with Zod, translated into AI SDK v3 prompt parts, and dispatched to a singleton
kiro-acp-ai-providerinstance. - AI SDK v3 stream parts are converted back into OpenAI Chat Completion SSE chunks (
textdeltas,tool_calls, finish reasons, usage). - An idle timer shuts down the underlying
kiro-cli acpsubprocess after five minutes of inactivity.
Two custom Opencode tools ship with the plugin:
| Tool | Purpose |
|---|---|
kiro_status |
Reports whether kiro-cli is installed/authenticated, the active auth mode, account count, cooldown summary, and the current model list. Output is fully redacted. |
kiro_models |
Returns the merged model catalog (dynamic list from kiro-acp-ai-provider when supported, curated catalog otherwise). |
kiro_accounts |
Read-only view of the configured Kiro accounts, the active rotation strategy, and the time until the next eligible account becomes available. Never returns the raw API key. |
Use them from inside an Opencode session, for example:
"Use the
kiro_statustool and tell me whether Kiro is ready."
| File | Path | Contains secrets? |
|---|---|---|
| Main Opencode config | ~/.config/opencode/opencode.json |
No — only provider/model definitions |
| Plugin config | ~/.config/opencode/kiro.json |
No — non-secret preferences only |
| Account store | ~/.config/opencode/kiro-accounts.json |
Yes — API keys (mode 0600, never copied to other files) |
| Opencode auth storage | managed by Opencode | Yes (when using API key mode) |
kiro-cli token cache |
managed by kiro-cli |
Yes (when using CLI login mode) |
Windows users:
~resolves to your user home (e.g.C:\Users\YourName). Do NOT use%APPDATA%.
| Variable | Purpose |
|---|---|
KIRO_API_KEY |
Used by the underlying kiro-cli subprocess when API-key mode is active. The plugin sets it from Opencode auth storage; you usually don't need to set it manually. |
KIRO_PLUGIN_LOG |
One of debug, info, warn, error. Default warn. |
OPENCODE_KIRO_LOG |
Alias for KIRO_PLUGIN_LOG. |
Quick reset: the plugin holds no per-user state of its own. If something is wrong, restart Opencode; if that doesn't help, run
kiro-cli loginagain or rotate the API key insideopencode auth login.
Install the runtime provider package once:
npm install kiro-acp-ai-providerIf you installed Opencode globally, install it in the same global scope (npm install -g kiro-acp-ai-provider).
Install kiro-cli from the official Kiro source. The plugin will not install it for you, and it will not bypass an interactive kiro-cli login prompt.
- Confirm
kiro-cli --versionworks in the same shell that launched Opencode. - Make sure no
KIRO_API_KEYis exported in your shell — it overrides the session for child processes.
The plugin rejected an upstream request that wasn't valid OpenAI Chat Completions JSON. Check that:
provider.kiro.npmis@ai-sdk/openai-compatibleprovider.kiro.options.baseURLis exactlyhttps://kiro.local/v1
The plugin only serves /v1/models, /v1/chat/completions, and /health. Embeddings and Anthropic-style /v1/messages are out of scope for the MVP.
Tool definitions must follow the OpenAI Chat shape:
{
"type": "function",
"function": {
"name": "search",
"parameters": { "type": "object", "properties": { "q": { "type": "string" } } }
}
}If your MCP server emits a non-OpenAI schema, expose it through Opencode's MCP layer rather than passing it raw.
# bash / zsh
KIRO_PLUGIN_LOG=debug opencode
# PowerShell
$env:KIRO_PLUGIN_LOG = "debug"; opencodeLogs are JSON lines on stderr/stdout. Secrets are redacted before they reach a stream — if you ever spot a ksk_*-shaped string in the output, please open an issue.
The plugin only registers a kiro provider and two tools (kiro_status, kiro_models). It does not touch other providers' models, fetch handlers, or auth methods, so it can sit alongside opencode-antigravity-auth, oh-my-opencode, @tarquinen/opencode-dcp, etc.
If another plugin also exposes a kiro provider id, register this plugin under a different name:
import { createKiroPlugin } from "opencode-kiro-plugin";
export default createKiroPlugin({ providerId: "kiro-personal" });Hard rules baked into the codebase:
KIRO_API_KEYis never written to any file the plugin manages.- The plugin opens no network ports.
- Logger redacts
ksk_*,Bearer …, JWT triplets, and JSONaccess_token/refresh_token/id_token/api_keyfields before printing. auth/api-key.tsshape-checks the key locally; it does not phone home to validate.- The plugin does not scrape Kiro token caches or call undocumented Kiro REST endpoints.
- The plugin does not spawn
kiro-cli loginfor you. If a login is needed, the plugin tells you to run it.
See docs/SECURITY.md for the full threat model.
git clone https://github.com/d-init-d/opencode-kiro-plugin.git
cd opencode-kiro-plugin
npm install
npm run typecheck
npm test
npm run buildTo load the local build into Opencode:
Project layout:
src/
index.ts # public exports + default plugin
plugin.ts # createKiroPlugin / KiroPlugin / hooks
constants.ts # synthetic URL, model catalog, env names
auth/ # api-key checks, cli-login probe, loader hook
kiro/ # provider lifecycle, model catalog, optional quota
openai/ # zod schema, translate, SSE stream, handler, helpers
config/ # opencode.json merge + kiro.json loader
plugin/ # JSON logger w/ redaction, status report
tools/ # kiro_status, kiro_models
tests/ # vitest specs (44 tests)
docs/ # ARCHITECTURE, TROUBLESHOOTING, SECURITY
examples/opencode.jsonc
- Optional
/v1/embeddingspassthrough whenkiro-acp-ai-providerexposes an embeddings model - Optional spawn-and-monitor
kiro-cli loginflow once it can be made non-interactive - Anthropic
/v1/messagesshim for tools that prefer that shape - Soft quota threshold (skip an account before it fully exhausts) like
opencode-antigravity-auth
These are explicitly out of scope for the MVP in IMPLEMENTATION_PLAN.md.
- Architecture — module layout, lifecycle, request flow
- Security — trust boundary, redaction, what is and isn't supported
- Troubleshooting — common errors and fixes
- opencode-antigravity-auth by @NoeFabris — proved that
auth.loader.fetchinterception is a viable Opencode plugin pattern. kiro-acp-ai-provider— the AI SDK v3 wrapper aroundkiro-cli acpthat this plugin sits on top of.
MIT. See LICENSE.
Legal
- Personal / internal development against your own Kiro account
- Not for production proxying or for sharing access between users
- Not for evading Kiro quotas, rate limits, or terms
By using this plugin you acknowledge:
- Terms of Service risk — using third-party tooling against Kiro may violate Kiro's ToS.
- Account risk — Kiro may suspend or ban accounts that use unsupported clients.
- No guarantees — the upstream API may change without notice and break this plugin.
- Assumption of risk — you accept all legal, financial, and technical risks.
Not affiliated with Kiro. "Kiro" is a trademark of its respective owner. This is an independent open-source project.
{ "plugin": ["/abs/path/to/opencode-kiro-plugin/dist/index.js"] }