wyrd/tools
esc

    ↑↓ Move ↵ Open xolotl results first Search by Pagefind
    Releases

    Clients

    Clients use xolotl like a provider API. They send their client key and a model name the key’s policy exposes. The examples use http://xolotl:8317 as the xolotl URL and XOLOTL_API_KEY for the client key (see Issue a client key).

    Xolotl accepts the key in Authorization (Bearer or bare), x-api-key or x-management-key.

    Claude Code

    export ANTHROPIC_BASE_URL=http://xolotl:8317
    export ANTHROPIC_AUTH_TOKEN=$XOLOTL_API_KEY      # or ANTHROPIC_API_KEY

    Claude Code’s model list comes from GET /v1/models, in Anthropic’s format, with model descriptions, context limits and API-equivalent list prices. Models with a known input limit of at least one million tokens receive a [1m] hint. Both the hinted and canonical names route and account as the same model. Alias hints require every fallback target to meet the limit. A 200K–999K window stays untagged so Claude Code can compact early. Unknown models receive no hint. Claude Code discovery filters for names containing claude or anthropic; select other names with --model or /model <name>.

    For an exact custom-model window, set CLAUDE_CODE_MAX_CONTEXT_TOKENS in the client session. Xolotl cannot set this environment value through the API. See Claude Code model configuration. Upstream context-limit errors become prompt is too long so Claude Code can compact and retry. Translated token counts are approximate and labeled as such.

    Codex CLI

    In ~/.codex/config.toml:

    model_provider = "xolotl"
    
    [model_providers.xolotl]
    name = "xolotl"
    base_url = "http://xolotl:8317/v1"
    env_key = "XOLOTL_API_KEY"
    wire_api = "responses"
    model_catalog_url = "http://xolotl:8317/backend-api/codex/models"

    The Codex-native catalog uses the same client key as inference. It is filtered to the key’s policy and includes aliases as independent entries. Native Codex metadata passes through; other models use LiteLLM windows and configured capabilities. Configure unknown models with [catalog.metadata].

    Instead of env_key, Codex can obtain the key from a helper:

    [model_providers.xolotl.auth]
    command = "secrets-helper xolotl-key"

    The helper prints the client key. Codex sends provider authentication when it fetches model_catalog_url; no public catalog is required. See the official configuration reference.

    Usage limits in Codex

    Codex reads its usage limits from the headers of each reply. Through xolotl, /status and the status line show the limits of the pool account that served the last turn. Session affinity keeps a conversation on one account, so the numbers change only when xolotl moves the conversation to another account. When every account is cooling down, Codex shows “You’ve hit your usage limit” with the limits of the account that comes back first.

    Before the first turn, /status shows “data not available yet”. Codex reads account usage at startup only from ChatGPT, with a ChatGPT login, so that read does not go through xolotl. For the same reason, T3 Code’s Codex usage needs a T3 Code hub. xolotl accounts list and the web UI show every account in the pool.

    Grok CLI

    The Grok CLI runs auth_provider_command and sends what it prints as the bearer token. Point it at a command that prints the client key, and declare a model that uses the Responses backend:

    [auth]
    auth_provider_command = "secrets-helper xolotl-key"   # prints $XOLOTL_API_KEY
    
    [model."grok-build"]
    base_url = "http://xolotl:8317/v1"
    model = "grok-build"
    api_backend = "responses"

    OpenAI-compatible SDKs

    Use base URL http://xolotl:8317/v1, the client key as the API key, and any model name the policy exposes.

    curl http://xolotl:8317/v1/chat/completions \
      -H "Authorization: Bearer $XOLOTL_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"model": "fast", "messages": [{"role": "user", "content": "Hello"}]}'

    MCP clients

    Add a Streamable HTTP server with URL http://xolotl:8317/mcp and header Authorization: Bearer $XOLOTL_API_KEY. The key’s policy needs the mcp scope. For Claude Code:

    claude mcp add --transport http xolotl http://xolotl:8317/mcp \
      --header "Authorization: Bearer $XOLOTL_API_KEY"

    See MCP gateway.

    T3 Code

    Add a CLIProxyAPI usage source with xolotl’s base URL, the same URL your other clients use (here http://xolotl:8317), and a key whose policy has the management scope. The hubs endpoints are under /v0/management/ on the same port as everything else. A reverse proxy in front of xolotl must forward that path too. T3 Code then shows the usage of your Codex and Claude accounts, and the Codex reset credits.

    T3 Code redeems a Codex reset credit itself: it sends the redemption request to Codex through xolotl’s api-call endpoint, which adds the account’s token and only reaches the account’s own provider. Then it calls reset-quota, and xolotl clears that account’s cooldown and last error and polls its usage again. reset-quota alone sends nothing to the provider.

    Endpoints

    RequestGoes to
    POST /v1/messages, /v1/responses, /v1/chat/completions, other POST /v1/*The target the policy gives for model
    GET /backend-api/codex/modelsPolicy-filtered Codex-native metadata
    GET /v1/modelsThe policy’s model names and their endpoints
    /<provider>/<path>Passthrough to a provider account, with its key
    /opencode-go/<path>Passthrough to OpenCode Go through the account pool
    /mcpThe MCP gateway
    /v0/management/...T3 Code hubs
    /admin/...The CLI and the web UI
    /The web UI

    /backend-api/codex/responses is the same as /v1/responses, for clients set up against the ChatGPT backend path.

    Passthroughs

    A passthrough sends any method and path to the provider. Xolotl removes the client’s credentials and adds the provider’s. Use them for endpoints that are not chat, for example:

    curl http://xolotl:8317/mistral/v1/ocr -H "Authorization: Bearer $XOLOTL_API_KEY" ...
    curl http://xolotl:8317/vercel/v1/credits -H "Authorization: Bearer $XOLOTL_API_KEY"

    The policy must allow the provider. When the body is JSON, the policy must also allow its model.

    Wire formats and translation

    There are three LLM formats: Anthropic Messages (/v1/messages), OpenAI Responses (/v1/responses) and OpenAI Chat Completions (/v1/chat/completions). Every model is reachable on all three. When the provider does not speak the endpoint’s format, xolotl translates the request and the response, streams included.

    ProviderSpeaks natively
    claudeMessages
    codex, grokResponses
    opencode-goMessages, Responses or Chat, depending on the model
    vercel kindAll three
    Other provider accountsChat

    Change this with [catalog.wires].

    Exceptions:

    • Claude subscription accounts serve only /v1/messages. To use a Claude model on every endpoint, alias it to a second provider: ["claude/claude-haiku-5-5", "opencode-go/claude-haiku-5-5"]. Xolotl skips targets that can’t serve the endpoint.
    • /v1/messages/count_tokens calls native Messages providers or returns a local UTF-8 byte estimate for translated models (estimated: true and x-xolotl-token-count header). Tools and system prompts are included; image and tokenizer costs are approximate. /v1/responses/compact is native only.
    • A model that can’t serve the endpoint gets a 400 that names the endpoints it is served on.

    Translation drops what has no match in the other format: hosted tools such as web search, and attachments that are not images. See Known limitations.

    Errors

    Errors have the shape of the endpoint you called: Anthropic-style on /v1/messages, OpenAI-style on the others.

    StatusMeaning
    401No key, or the key is unknown, expired, disabled or revoked
    403The key lacks the scope, or a passthrough’s provider or model is not allowed
    404The policy exposes no such model name
    400The model is not served on this endpoint
    429Every account is cooling down, or the key is over a rate limit. Retry-After gives the seconds to wait