Skip to main content
A provider plugin is an executable that adds an LLM provider to ChatCLI. Reach for one when the backend you need is not built in — a corporate gateway that fronts several model vendors behind its own authentication, or an internal SDK that is the only sanctioned way to reach a model. A provider plugin is designed to behave like a built-in provider: it appears in /switch, /provider and the command palette, and cost tracking, failover chains, model routing, compaction and memory all run on top of it.
Preview. The protocol, the Go SDK and the chatcli provider test conformance kit are available, so plugins can be built and verified today. Loading provider plugins into a running ChatCLI — process supervision, /switch, login and the encrypted credential store — is the next step of this feature and is not available yet.

How it fits

ChatCLI keeps everything above the wire: the coder loop, tool execution, history, memory, compaction, prompt-cache planning, cost and failover. The plugin owns only how to reach the backend.

Two modes

dialect

The plugin implements authentication (login, token, refresh) and the model list. ChatCLI talks to the backend with its built-in client for the wire format — openai-chat, openai-responses, anthropic-messages or gemini — sending the plugin’s headers.Native tools, streaming, prompt caching and exact usage come for free. Prefer this mode whenever the backend speaks one of those formats.

full

The plugin also implements chat over a normalized message schema: messages, tool calls, images, thinking blocks, streamed deltas and usage.Use it when the backend — or the SDK you are required to use — has a request format of its own.

Credentials stay with ChatCLI

The plugin never stores secrets. Its login ends with a credential — a flat key/value object — that ChatCLI encrypts, stores and hands back on every call that needs it. The login itself is a sequence of steps the plugin describes and ChatCLI renders: prompt fields, open_url and device_code. The plugin never touches the terminal, so the same flow works in the CLI, the web UI, IDE clients and messaging channels.

Gateway models keep their real limits and prices

Each listed model can name the built-in model it fronts with an upstream entry, for example {"provider": "CLAUDEAI", "model": "claude-sonnet-4-6"}. ChatCLI then reuses what it already knows about that model — context window, output limit, pricing and capabilities — unless the plugin sets them explicitly. A per-call cost reported by the gateway (costUSD) is treated as authoritative.

Build a plugin

The Go SDK, github.com/diillson/chatcli/pkg/providersdk, implements the protocol. Implement Provider plus the interfaces that match your backend; the capabilities announced to ChatCLI are derived from them, so a plugin can never advertise something it does not serve.A minimal dialect plugin for an OpenAI-compatible gateway:
While providersdk.Serve runs, os.Stdout points at stderr, so a stray fmt.Println in plugin code ends up in the log instead of corrupting the protocol stream. Use providersdk.Log(ctx, "info", "…") for structured log lines.
The repository ships a complete reference plugin in full mode — login, model list, streaming with cancellation, token counting and logout — at pkg/providersdk/examples/echo. It passes every conformance check and is the best starting point.

Rules every plugin must follow

Anything else on stdout corrupts the stream. Log to stderr — ChatCLI keeps a tail of it — or send $/log notifications.
The name must match ^[A-Z][A-Z0-9_]{1,31}$ and can never be the name of a built-in provider (OPENAI, CLAUDEAI, BEDROCK, …). It is the value users pass to LLM_PROVIDER and /switch.
Requests that arrived before shutdown are still served; later ones fail with -32600. After exit, the process terminates with status 0.

Verify a plugin with chatcli provider test

chatcli provider test runs the conformance kit against a plugin binary, talking to it over stdin and stdout exactly as ChatCLI does — the verdict is the same for a Go, Python or Java plugin.
The checks cover lifecycle ordering, version negotiation, name rules, the login state machine, token headers and expiry, model-list sanity (unique ids, valid upstream, non-negative limits and prices), chat results, streaming (the deltas must spell the final text), liveness after a cancelled request, token counting, a clean exit and stdout hygiene.
A check that needs something the run cannot provide is skipped with a hint, never failed: auth.login asks for --input when a prompt field has no value, and a browser or device-code login asks for --credential-file.
The command exits 0 when nothing failed, 1 when a check failed and 2 on a usage error, so a plugin repository can gate its CI on it:

Security model

Signed binaries

Sign provider plugins with the same trusted-key tooling as tool plugins — chatcli plugin keygen | sign | verify | trust — and ChatCLI verifies them with that mechanism when it loads them. See Signature Verification.

Least environment

A plugin declares the env vars it reads (authEnv). Those are the only credentials it should receive, and the secret redactor always treats them as secrets.

No secrets in the plugin

Credentials live in ChatCLI’s encrypted store and are passed per call. A plugin that writes a secret to stdout or stderr is broken.

Conversations reach the plugin

A provider plugin sees every prompt sent through it, exactly like the vendor it fronts. Install only plugins you trust.

See also

Plugin System

Tool plugins (@command) the model can call.

Supported Models

The built-in providers and their models.

Provider Fallback

Failover chains across providers.

Cost Tracking

How usage and prices become /cost.