Skip to main content

Invincible — AI Continuity Gateway & Local MCP Agent

The Python package is named invincible (the repository directory is ai-gateway). Throughout this repo the project is referred to as Invincible.

Project direction: Invincible is a remote-first, multi-user AI continuity platform — accounts, projects, shared memory across models, context intelligence, and a web dashboard (invincible-ai.me). Users install one CLI command on their own PC and pair with the hosted service; the database, provider credentials, and all state live server-side. The same code runs as a single-user self-host. See docs/ROADMAP.md for the direction, what is implemented today, and what is planned.


What is Invincible?

A remote-first, multi-user AI gateway: one Python (FastAPI) service that many users sign into, each with their own projects, API keys, provider credentials, memory, and continuity — served over HTTPS from any host. The same code runs as a single-user self-host on a laptop (the local/self-hosted mode is supported indefinitely); nothing else changes between the two.

It serves three roles in one process:

  1. Self-Service BYOK Gateway — an OpenAI-compatible /v1/chat/completions endpoint where every user authenticates with their own inv_ API key and routes through their own connected provider credentials (BYOK), with transparent failover on rate limits (429) and server errors, so a free-tier 429 no longer kills an agent's workflow. It also speaks the Anthropic Messages API (POST /v1/messages) and the OpenAI Responses API (POST /v1/responses), so Claude Code, Codex CLI, and other native clients plug in with a one-line config change.
  2. Multi-user account platform — browser accounts (email + password, or GitHub), a dashboard for sessions, memory, usage, and provider credentials, and strict per-user ownership: one account can never read another's sessions, memory, or history.
  3. MCP Tool Server — a JSON-RPC 2.0 /mcp endpoint exposing read_file, execute_bash, and write_file to a cloud-hosted AI. With INVINCIBLE_AGENT_ROUTING=1 (required on any public multi-user deployment) the tools execute on the user's own paired machine, so a remote AI acts on their files under their account.

Why it exists

  • The 429 problem. AI coding agents using free/open-source providers get killed when they hit a rate limit. Invincible sits between the agent and the providers; on a 429 (or 5xx) it records the failure, puts the provider in a short cooldown, and retries the next credential in the user's own routing order. The agent sees a single, stable endpoint.
  • The cloud-to-local gap. Cloud AI tools (e.g. the Claude web/mobile app) can reason well but cannot read the user's files, write to disk, or run terminal commands. Invincible's MCP server exposes those capabilities over HTTPS, so a remote model can act on each user's own machine — under that user's confirmation for anything destructive.

Features

Feature What it gives you
Remote-first, multi-user One deployment serves many accounts over HTTPS: browser accounts (email+password or GitHub), per-user projects/API keys/provider credentials, a dashboard, and strict per-user ownership — one account can never read another's data (audited; see docs/MULTI-TENANT-AUDIT.md). The same core self-hosts on a laptop, and docs/DEPLOYMENT.md covers running it on a host.
BYOK routing Every user connects their own provider credentials (dashboard → Providers) and routes only through them: auto/pinned/chain routing per user, 429/5xx → cooldown + next credential, 401/403 → skip, network errors → next. No credentials connected → HTTP 400. All credentials exhausted → HTTP 503.
Exponential cooldown 30s → 60s → 120s → 240s → capped at 300s; a success resets the counter (in-memory, process-scoped).
Conversation memory PostgreSQL-backed (Phase 16), keyed by the X-Session-Id header (default default). History is merged into every request and the assistant reply is persisted back.
Context trimming Per-credential max_context; system messages always kept; everything else dropped as atomic turns (an assistant tool_calls is never separated from its tool results); the most recent turn is always sent.
Per-provider timeouts Split connect/read/write/pool with sane defaults and per-provider overrides.
MCP tool server read_file (no approval), execute_bash and write_file (staged, then approved via a token round-trip through the confirm_action tool), guarded by denylists and an OAuth 2.1 + PKCE bearer-token auth layer (account login + per-client consent, tokens don't survive on requests like a shared header does). With INVINCIBLE_AGENT_ROUTING on, confirmed actions execute on the user's own PC via the paired invincible agent (server keeps every security decision; see § Run tools on your own PC).
Accounts & projects (Phase 3) Sign up / sign in in a browser (email + argon2id passwords, or GitHub login), manage your own projects and inv_ API keys over HTTP, list your sessions — all under ownership predicates so users never see each other's data. Pair a CLI with invincible login via device-code approval.
Protocol-agnostic Native OpenAI, Anthropic, and Responses protocols, all translated into one internal message model. Claude Code works with ANTHROPIC_BASE_URL pointing at the gateway; Codex CLI works out of the box.

Installation

Users: one install command. That's all.

pip install invincible-ai
invincible agent

The first run pairs your machine with the hosted service (https://invincible-ai.me — the default): your browser opens, you register or sign in, click Approve, and the agent starts. There is no database to set up, no .env, no provider configuration on your machine — your account, connected provider keys, memory, and sessions all live on the hosted service. The local agent only executes confirmed tool actions on your PC. Finish by adding https://invincible-ai.me/mcp as the MCP connector in Claude (or any MCP client) — see Quick Start. (invincible login does the same pairing without starting the agent loop.)

Operators only: running your own server. Everything above works against the hosted service without it. If you self-host or operate an instance, you need Python 3.10+ and a PostgreSQL database (all state — conversations, OAuth grants, task state, staged approvals — lives there; INVINCIBLE_DB_URL is required to start):

pip install -e .
invincible setup --db-url postgresql://user:pass@your-db-host:5432/invincible
invincible db upgrade                # create/migrate the schema (explicit, never auto-run)
invincible start --host 0.0.0.0      # omit --host for a loopback-only dev server

TLS, $PORT, proxy headers, the two-role database split, the container start command, required secrets, and the go-live checklist: docs/DEPLOYMENT.md. Development shortcuts (invincible dev-db, docker compose up) and the full CLI reference: docs/CONFIGURATION.md.


Quick Start

A. Use the hosted service (one install command)

pip install invincible-ai
invincible agent       # first run: browser opens → sign in or register → Approve

Pairing registers your account on https://invincible-ai.me and saves the minted key to ~/.invincible/config.json. Then, in the dashboard:

  1. Providers — connect your own provider keys (BYOK).
  2. MCP connector — add https://invincible-ai.me/mcp to Claude (or any MCP client) and approve the client when the browser asks.

With the agent running, a remote AI can read/write files and run commands on your machine, under your account's approvals. Prefer raw API access instead? Mint an inv_ key (dashboard → Account → API keys) and call the gateway directly:

export INVINCIBLE_BASE=https://invincible-ai.me
export INVINCIBLE_API_KEY=inv_...        # dashboard -> Account -> API keys

curl $INVINCIBLE_BASE/v1/chat/completions \
  -H "Authorization: Bearer $INVINCIBLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "Hello!"}]}'

Every account is independent: the same commands work for every user with their own inv_ key, their own connected provider credentials, and their own sessions — no account can see or route through another's.

B. Run your own server (operators / self-hosters — users never do this)

invincible setup --db-url postgresql://user:pass@db-host:5432/invincible
invincible db upgrade               # create/migrate the schema (explicit, never auto-run)
invincible start                    # http://127.0.0.1:8000 (dev loopback)
invincible start --host 0.0.0.0     # reachable from other machines

invincible setup writes missing secret values (INVINCIBLE_OWNER_SECRET) as random secrets.token_urlsafe(32) tokens, generates the BYOK credential master key (INVINCIBLE_CREDENTIAL_KEY), and takes the INVINCIBLE_DB_URL via --db-url (non-interactive) — preserving your existing .env comments and values. INVINCIBLE_OWNER_SECRET signs account browser sessions (dashboard login, OAuth consent) — not something /mcp requests send.

For a real host — TLS, $PORT, proxy headers, the two-role database split, the container start command (0.0.0.0:$PORT), and the go-live checklist — follow docs/DEPLOYMENT.md. Development-only database shortcuts are covered in docs/CONFIGURATION.md.

Clients of your own server point at it explicitly: invincible agent --server https://mycompany.ai (and ANTHROPIC_BASE_URL=https://mycompany.ai for Claude Code). The hosted service is the CLI default, so your users pass no flag at all.

See Examples for ready-to-run curl calls, or continue reading for the full configuration, API, and tooling reference.


Configuration

Everything is environment variables plus one YAML file — no other config.

.env variables

Variable Required by Purpose
INVINCIBLE_DB_URL startup (required since Phase 16) PostgreSQL DSN for all persistent state. Use a managed/reachable PostgreSQL on a remote deployment, e.g. postgresql+asyncpg://invincible_app:***@your-db-host:5432/invincible (Neon, RDS, Azure Database for PostgreSQL, a container, or your own cluster). invincible dev-db (local, dev-only) or the bundled compose pair are the laptop shortcuts. Masked in doctor output.
INVINCIBLE_MIGRATE_DB_URL container/platform deploys Schema-owner DSN used only for the one db upgrade the image runs at startup (falling back to INVINCIBLE_DB_URL when unset), so migrations run as the migrate role while the server serves as the CRUD-only runtime role. See docs/DEPLOYMENT.md §Database.
INVINCIBLE_OWNER_SECRET account sessions Signs account browser sessions (dashboard login, OAuth consent). Not sent on /mcp — requests use short-lived OAuth Bearer tokens. If unset, browser sessions fail closed (no login, no consent). The legacy MCP_SHARED_SECRET key is still read as a fallback. Rotating it logs every browser out but does not revoke MCP grants — use invincible oauth revoke <client_id> for that.
INVINCIBLE_CREDENTIAL_KEY BYOK Fernet master key encrypting stored provider credentials at rest. Generated by invincible setup (or invincible secret credential-key); back it up — losing it makes every saved provider key undecryptable. Never rotated by setup --force.
INVINCIBLE_PERSIST_PENDING_ACTIONS startup Opt-in: when set, staged execute_bash/write_file approvals are written to the PostgreSQL database (pending_actions table) and survive a server restart. Off by default — pending actions are memory-only and a restart orphans them (clean slate).
INVINCIBLE_AGENT_ROUTING startup (required on public multi-user deployments) Routes confirmed tool execution to the caller's paired agent (invincible agent) instead of the server host. Unset/off is the single-user self-host posture (a one-person invincible start), where tools run on the server host under the server's privileges — never acceptable when strangers can register. Set 1 on every public/hosted deployment: see docs/SECURITY.md §10 and the checklist in docs/DEPLOYMENT.md.
INVINCIBLE_AGENT_ROOT agent Sandbox root for the local agent (default: the user's home directory). Reads and writes outside it are blocked; .env*, .git, .ssh, SSH keys, *.pem, *credentials* are blocked by name everywhere.

/v1/* requests authenticate with per-user inv_ API keys (minted on the dashboard's Account page, or by the host with invincible api-key create --user <email-or-id>). Each user connects their own provider credentials on the dashboard's Providers page (BYOK) and routes only through them — there is no shared gateway key and no shared provider pool.

The secrets are independent: a leaked /mcp URL alone is not enough to reach tool execution (a live OAuth bearer token is required as well), and rotating one secret never affects the other.

providers.yaml

The packaged invincible/providers.yaml is a static fixture (tests and direct Router construction); live traffic never reads it — every /v1/* request routes through the caller's own connected BYOK credentials. There is no repository-root copy.

Full reference — schema, validation rules, timeout resolution: docs/CONFIGURATION.md. How to add a provider, aliases, and supported shapes: docs/PROVIDERS.md.


CLI Commands

Two commands, both exposed as invincible and inv:

Command Purpose
invincible setup Create/update .env: generates missing secrets (token_urlsafe(32), never echoed), prompts for provider keys, preserves existing comments/values; carries a legacy MCP_SHARED_SECRET over to INVINCIBLE_OWNER_SECRET automatically. --force re-prompts existing values.
invincible secret rotate Generate a brand-new INVINCIBLE_OWNER_SECRET and rewrite it in place — no manual .env editing, never echoes the value (unless --show). Preserves every other line; migrates a legacy MCP_SHARED_SECRET key away. Does not revoke already-issued OAuth grants (that's invincible oauth revoke).
invincible start Start the server. --host (default 127.0.0.1; pass 0.0.0.0 to be reachable from other machines), --port (default 8000), --reload, --log-level, --env-file, --config (custom providers.yaml), --tunnel/--no-tunnel (local convenience: starts a Cloudflare tunnel named invincible by default so a laptop can be reached from the internet), --tunnel-name (or INVINCIBLE_TUNNEL_NAME). The tunnel is shut down with the server (Ctrl+C or a crash); a dead tunnel is reported as soon as it exits. There is no database flag — INVINCIBLE_DB_URL comes from the env/.env. Hosted platforms do not use this command: the container command in Dockerfile/railway.json/Procfile binds 0.0.0.0:$PORT with proxy headers — see docs/DEPLOYMENT.md.
invincible doctor Environment/config diagnostics: providers.yaml, secrets, and PostgreSQL connectivity + schema revision (DSN always password-masked); loud FAIL on a stale/unmanaged schema.
invincible dev-db Provision or verify a local Postgres development database (Docker fallback included) and print/write a working INVINCIBLE_DB_URL. Loopback-only and dev-credential by design — never the provisioning path for a remote/hosted database (docs/DEPLOYMENT.md).
invincible db upgrade Run the packaged Alembic migrations to head against INVINCIBLE_DB_URL. Explicit by design — nothing auto-migrates.
invincible db import <sessions.db> One-shot legacy SQLite importer (sessions/turns/messages, facts, OAuth rows).
invincible oauth list Show registered OAuth clients, their redirect URIs, and active/revoked grants.
invincible oauth revoke <client_id> Revoke every access/refresh token for a client immediately.
invincible oauth test-client Headless helper: registers a client, approves it, and prints a ready-to-use Bearer token + curl for /mcp (no browser needed).
invincible api-key create --user <id-or-email> --label L Mint an API key (inv_…) under a named account (host tool) — raw key shown once, only its SHA-256 hash is stored.
invincible api-key list List API keys by visible prefix (never hashes or raw keys).
invincible api-key revoke <id-or-prefix> Revoke a key immediately.
invincible users list List accounts (host tool; roles are informational only).
invincible users reset-password <email> Reset an account's password (host recovery path — database access is the proof of authority; --generate prints a strong password once). Every browser session for the account is signed out; inv_ keys and MCP tokens are untouched.
invincible login [--server URL] Pair this machine with an Invincible server (device flow): opens the approval page in your browser — click Approve and the command finishes, saving the inv_ key to ~/.invincible/config.json. Defaults to the hosted service (https://invincible-ai.me); pass --server for a self-hosted or local server. URL + code are printed for headless terminals; the Account page also has a "Pair a device" box for typing a code by hand.
invincible agent Run the local agent (Phase 10): polls the paired server for confirmed tool jobs and executes them on this machine with your own user privileges — denylist re-checked locally, reads/writes sandboxed to your home. Ctrl+C to stop. First run pairs automatically (device flow); invincible login is the explicit pairing/repair tool.
invincible setup --force
invincible secret rotate            # new owner secret, in place
invincible secret rotate --show     # ...and print it (rarely needed)
invincible start --port 9000 --config ./my-providers.yaml

Full CLI reference: docs/CONFIGURATION.md → CLI reference.


API

Endpoints

Method Path Auth Purpose
GET / none Health check → {"status": "healthy"}
HEAD / none 200 OK — Claude Code's base-URL probe
GET /health none Service detail → {"service", "status", "version"}
GET /v1/models Authorization: Bearer inv_… OpenAI-compatible model list built from your connected credentials (empty when you have none)
POST /v1/chat/completions Bearer inv_… API key OpenAI chat completion routed through your own credentials with failover
POST /v1/messages Bearer inv_… API key Anthropic Messages API (same routing)
POST /v1/responses Bearer inv_… API key OpenAI Responses API for Codex CLI (same routing)

Chat request

  • Body: {"messages": [...], "stream": false} — OpenAI message format. messages, stream, and model are accepted. model is a soft routing hint (matches a configured alias or exact model_id; unknown names are ignored). Other OpenAI fields are rejected with 422.
  • Streaming: stream: true returns an OpenAI-compatible Server-Sent Events (text/event-stream) response. Each event is a chat.completion.chunk (data: {…}\n\n), and the stream ends with data: [DONE]. Chunks are forwarded from the upstream provider as they arrive — nothing is buffered. Providers that fail before the first chunk trigger the normal failover; an error after streaming has begun terminates the stream with a well-formed data: {"error": …} event.
  • Sessions: history is loaded from PostgreSQL keyed by the X-Session-Id header (default default), prepended to your messages, and the assistant reply is persisted back. session_id is a partition key, not a credential. For streamed responses the reply is reconstructed from the chunk deltas and saved once the stream completes.
  • Response: the upstream provider's JSON is forwarded verbatim (non-streaming).

Anthropic Messages (POST /v1/messages)

Invincible also speaks the Anthropic Messages API, so Claude Code and other Anthropic-native clients work without modification:

# .env for Claude Code (or your shell):
ANTHROPIC_BASE_URL=http://127.0.0.1:8000

Claude Code probes HEAD /, then calls POST /v1/messages?beta=true — both are served. Supported request fields: model, system, messages, max_tokens, stream. Everything else Claude Code sends (tools, tool_choice, metadata, temperature, top_p, top_k, stop_sequences, unknown fields, anthropic-beta / anthropic-version headers, the ?beta=true query) is accepted and ignored — never a 422.

The model field is treated as a client hint: if it matches one of your connected credentials (an alias or an exact model_id) that credential is preferred — the Router still fails over through the rest of your routing order if it is down. An unknown model name (like Claude Code's own model ids) changes nothing. The upstream model always comes from your connected credentials, and the response reports that model — the one that actually served — so a fallback across models stays visible to the client instead of being masked by an echo of what you asked for.

  • Streaming: stream: true returns Anthropic SSE events in the canonical order — message_start → content_block_start → content_block_delta (one per text delta) → content_block_stop → message_delta → message_stop. tool_use content blocks are preserved and streamed as structured events (content_block_start + input_json_delta frames) with stop_reason: "tool_use" at the end; tool_result blocks in the request are carried as role: "tool" messages, so tool-shaped conversations round-trip losslessly (ids preserved). image content blocks are still skipped. A mid-stream upstream failure emits a well-formed Anthropic error event and closes — never malformed SSE.
  • Sessions: the same X-Session-Id header and PostgreSQL store are used, and history is serialized in the shared internal format — an OpenAI client and a Claude Code session on the same id see the same conversation.
  • Errors: mapped to Anthropic error types (invalid_request_error, authentication_error, permission_error, not_found_error, rate_limit_error, api_error, overloaded_error) with sanitized messages; upstream provider bodies are never forwarded.
  • Unsupported today: image content (skipped during conversion).

Status codes

Status When
200 Upstream success — JSON body forwarded verbatim, or SSE stream (stream: true)
400 Caller has no connected provider credentials (BYOK) — connect one on the dashboard's Providers page
401 Missing/invalid inv_ API key
422 Body fails validation (missing messages, extra fields)
4xx Upstream returned a non-failover error (e.g. 400) — forwarded verbatim
503 All of the caller's credentials failed or are in cooldown (before streaming starts)

The Anthropic endpoint uses the same statuses; error bodies are Anthropic shaped ({"type": "error", "error": {"type": …, "message": …}}) and map to Anthropic error types.

Full contract — sessions, trimming, timeout semantics: docs/API_REFERENCE.md.


MCP Support

POST /mcp implements a minimal JSON-RPC 2.0 subset: initialize, tools/list, and tools/call. Protocol version: 2025-06-18.

  • Auth: OAuth 2.1 + PKCE via the built-in authorization server. Clients discover it at /.well-known/oauth-protected-resource (RFC 9728), register at /oauth/register, get owner approval on the /oauth/authorize consent page, then send Authorization: Bearer <access_token> on every /mcp request. Wrong/missing/expired/revoked token → 401 with a WWW-Authenticate: Bearer resource_metadata="…" challenge. (No X-MCP-Secret header anymore; the legacy MCP_SHARED_SECRET env var is only read as a fallback for the owner login.)
  • Notifications: a request without an id still runs its side effect but the server replies 204 No Content with no body.

Tools

Tool Arguments Confirmation Gate
read_file path No Blocks only real secrets/state: .env*, sessions.db, .git/. Allows invincible/, tests/, providers.yaml.
execute_bash command + a confirm_action token round-trip Yes — staged with a token; runs only after confirm_action(token, approve=true) (30s execution timeout) Blocks high-blast-radius commands (rm -rf /, fork bombs, dd of=/dev/, mkfs, sudo, curl | sh, rd /s C:\, …).
write_file path, content + a confirm_action token round-trip Yes — staged with a token; writes only after confirm_action(token, approve=true) Blocks writes to .env*, providers.yaml, sessions.db, invincible/, tests/, .git/. Creates parent directories.
confirm_action token, approve — Approves/denies a pending execute_bash/write_file; token is single-use and expires after 10 minutes.

Security model, full denylist inventory, and known limits: docs/SECURITY.md.

Run tools on your own PC (the local agent)

On a hosted deployment with INVINCIBLE_AGENT_ROUTING=1, confirmed tool actions execute on your machine, not the server — the security checks (denylist, staging, approval tokens) all stay server-side, but the work travels to a paired local agent. From zero to "my AI just ran a command on my PC" is two commands — no account to create first, no separate pairing step:

pip install invincible-ai
invincible agent       # first run: browser opens → sign in or register → Approve

On first run the agent pairs the machine itself: the browser opens on the approval page (create your account right there if you don't have one, then click Approve), the minted key is saved to ~/.invincible/config.json, and the agent falls straight into its polling loop — it never asks you to run another command first. Every later start is just invincible agent again; pairing happens once per machine, ever.

Self-hosters point at their own server once: invincible agent --server https://mycompany.ai (or --server http://127.0.0.1:8000 against a local invincible start). invincible login remains as the explicit re-pair/repair tool.

Then connect Claude/Grok/any MCP client to https://your-server.example.com/mcp as usual. While the agent is running, its console shows each dispatched job; the dashboard MCP page shows a live Agent: online/offline badge. The agent sandbox is your home directory (INVINCIBLE_AGENT_ROOT to override), with .env*, .git, .ssh, and key material blocked everywhere. An offline agent means tool calls answer agent_offline immediately — nothing hangs. Full transport and threat model: docs/MCP_PROTOCOL.md and SECURITY.md §10.


Provider Routing

Every /v1/* request routes through the caller's own connected BYOK credentials (dashboard → Providers), in the order the user configured (auto by ability, or an explicit pinned/chain order). Per attempt:

Upstream status Router behavior
200 record_success (resets cooldown) → return body
429 / 5xx record_failure → cooldown → try next credential
401 / 403 Skip that credential for this request → try next
Other 4xx (e.g. 400) Abort — forward the provider's status and body
Network error record_failure → cooldown → try next credential
In cooldown Skipped silently (log only)

All of the caller's credentials exhausted → HTTP 503. No credentials connected at all → HTTP 400. Cooldowns follow 30 * 2**(failures-1), capped at 300s; all health state is in-memory and resets on restart.

Deep dive (failover state machine, context trimming): docs/ARCHITECTURE.md.


Examples

Every call below uses $INVINCIBLE_BASE — the hosted service, or your own server:

export INVINCIBLE_BASE=https://invincible-ai.me   # or http://127.0.0.1:8000 locally
export INVINCIBLE_API_KEY=inv_...                 # dashboard -> Account -> API keys

1. Health check

curl $INVINCIBLE_BASE/
# {"status": "healthy"}

2. List models

curl $INVINCIBLE_BASE/v1/models \
  -H "Authorization: Bearer $INVINCIBLE_API_KEY"
# {
#   "object": "list",
#   "data": [
#     {"id": "<your connected credentials' model ids>", "object": "model", "owned_by": "invincible"},
#     {"id": "<your configured aliases>", "object": "model", "owned_by": "invincible"}
#   ]
# }

The list is built from your connected BYOK credentials, so it reflects exactly what the gateway can route to for you (an empty list when you have none connected). Real model ids are listed first, then the aliases you configured.

3. Chat with session memory

curl $INVINCIBLE_BASE/v1/chat/completions \
  -H "Authorization: Bearer $INVINCIBLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Session-Id: my-conversation" \
  -d '{"messages": [{"role": "user", "content": "Hello!"}]}'

The assistant reply is stored under my-conversation and will be included in your next request with the same X-Session-Id — the model remembers the conversation.

4. Stream a chat (SSE)

curl -N $INVINCIBLE_BASE/v1/chat/completions \
  -H "Authorization: Bearer $INVINCIBLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "Hello!"}], "stream": true}'

-N (aka --no-buffer) prints each event as it arrives. Tokens are streamed as OpenAI-compatible chat.completion.chunk events:

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1783161600,"model":"gemini-2.5-flash","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1783161600,"model":"gemini-2.5-flash","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1783161600,"model":"gemini-2.5-flash","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

5. Use it from Claude Code (Anthropic)

ANTHROPIC_BASE_URL=https://invincible-ai.me claude   # or your own server URL

Claude Code probes HEAD /, then calls POST /v1/messages with streaming. You can send the same call directly:

curl $INVINCIBLE_BASE/v1/messages \
  -H "Authorization: Bearer $INVINCIBLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"claude-sonnet-4","max_tokens":1024,
       "messages":[{"role":"user","content":"Hello!"}]}'

And stream it (stream: true) to receive Anthropic SSE events ending in message_stop.

6. List MCP tools

First get an MCP access token. On the hosted service, add the connector in your MCP client at $INVINCIBLE_BASE/mcp and approve it in the browser — the client then holds the token. On a local/self-hosted server the headless helper registers a client against that server's database and prints a ready-to-use Bearer token:

invincible oauth test-client --env-file .env   # prints a token + curl example
export ACCESS_TOKEN=...
curl -X POST $INVINCIBLE_BASE/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

7. Run a command via MCP

curl -X POST $INVINCIBLE_BASE/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
       "params":{"name":"execute_bash","arguments":{"command":"git status"}}}'

The call returns a pending_confirmation result carrying a token. To approve it, call confirm_action with that token (or deny with approve: false):

curl -X POST $INVINCIBLE_BASE/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call",
       "params":{"name":"confirm_action",
                 "arguments":{"token":"<token from step 2>","approve":true}}}'

The command runs (or the file is written) only after approval; the token is single-use and expires after 10 minutes.

8. Reach it from a cloud AI

On a deployed server the plain HTTPS URL is the entry point: point the MCP client at https://your-domain/mcp (or https://invincible-ai.me/mcp on the hosted service) and approve the connector in the browser.

A laptop has no public URL, so invincible start starts a Cloudflare tunnel alongside the server by default: cloudflared tunnel run <name> (name defaults to invincible, override with --tunnel-name or INVINCIBLE_TUNNEL_NAME). cloudflared's log lines (and the public URL when cloudflared prints one) appear prefixed [tunnel]; a tunnel that dies is reported as soon as it exits, and it is shut down with the server. To skip the tunnel, pass --no-tunnel.

For a one-off quick tunnel instead (no named-tunnel config required):

cloudflared tunnel --url http://127.0.0.1:8000
# → https://random-name.trycloudflare.com  — call /mcp on this URL

Tunnel or not, the URL alone is useless without an access token — and any valid token can be revoked immediately with invincible oauth revoke <client_id>.

More MCP protocol details: docs/MCP_PROTOCOL.md.


Architecture

                         ┌──────────────────────────────────┐
  OpenAI-compatible      │  invincible/main.py              │
  agent  ─── /v1/chat ─► │  (FastAPI)                        │
         Claude Code     │                        compat/    │
  (Anthropic) ─ /v1/msg ►│  openai_compat ──────► anthropic │
                         │  │ mcp routers │                 │
  Cloud AI      ─── /mcp ─►  │ core/router.py               │
  (via tunnel)           │  │ core/tool_executor (denylist) │
                         └──────┬──────────────┬────────────┘
                                │              │
                  ┌─────────────▼──┐   ┌───────▼────────────┐
                  │ core/router.py │   │ core/tool_executor │
                  │ tiered failover│   │ (denylist + approval)│
                  │ + ctx trimming │   └─────────────────────┘
                  └───────┬────────┘
                          │
            ┌──────────────────────────┐
            │ core/provider_health.py  │
            │ core/session_store.py    │
            │ (PostgreSQL stores)      │
            └──────────────────────────┘

The compatibility layers (OpenAI and Anthropic) only translate; both produce the same internal message model, which is what the Router, session store, and trimming logic consume.

Package layout

Path Role
invincible/main.py FastAPI app, lifespan, auth dependencies, router wiring, HEAD / and /health.
invincible/endpoints/openai_compat.py POST /v1/chat/completions (JSON + SSE streaming, session merge + upstream call); GET /v1/models.
invincible/endpoints/anthropic_compat.py POST /v1/messages; translates Anthropic ↔ internal model, calls the same Router.
invincible/models/anthropic.py Pydantic request model: only real fields declared; everything else ignored.
invincible/compat/common.py Protocol-neutral internal-message/usage helpers shared by compat layers.
invincible/compat/anthropic.py Pure Anthropic translators: flattening, finish-reason map, error map, Anthropic SSE streaming.
invincible/endpoints/mcp.py POST /mcp; JSON-RPC 2.0 dispatch, tools/list, tools/call.
invincible/core/router.py Tiered failover over per-user BYOK credentials, response trimming, timeouts.
invincible/core/provider_health.py Per-credential failure counts + exponential cooldowns.
invincible/core/db.py SQLAlchemy engine factory + schema metadata (single source of truth).
invincible/migrations/ Packaged Alembic environment (invincible db upgrade).
invincible/core/session_store.py Conversation memory on PostgreSQL, partitioned by session id.
invincible/core/tool_executor.py Denylists, pending-action approval (confirm_action), tool execution.
invincible/endpoints/byok.py Per-user provider-credential management (connect, test, order, routing config).
invincible/cli.py Click CLI: setup, start, doctor, api-key, users, oauth, db, login, agent.
invincible/providers.yaml Static provider fixture (tests/direct construction; live traffic is BYOK-only).

Documentation

Doc What it covers
docs/ARCHITECTURE.md Module map, request flows, context-trimming deep dive, failover state machine.
docs/API_REFERENCE.md The /v1/chat/completions contract: request, response, status codes, failover semantics.
docs/CONFIGURATION.md .env variables, providers.yaml schema, timeouts, database options, CLI reference.
docs/DEPLOYMENT.md Running it remotely: required env vars, ports/TLS/proxy headers, two-role database, migrations, single-instance constraints, go-live checklist.
docs/PROVIDERS.md Adding providers, the full schema, model aliases, auth types, supported shapes, troubleshooting.
docs/MCP_PROTOCOL.md Client-facing /mcp spec: JSON-RPC shape, tools, notifications, hosted URL vs. self-host tunnel.
docs/SECURITY.md Threat model, auth realms, denylist inventory, approval flow, known limits.
docs/TESTING.md How tests work, fixtures, per-file coverage map.
docs/ROADMAP.md Current platform direction, a verified snapshot of what is implemented, and the phased plan (identity, isolation, accounts, memory/context intelligence, dashboard, deployment).

Known limits (tl;dr)

  • Invincible translates Anthropic tool calls correctly (tool_use → tool_calls, tool_result → role: "tool" messages, ids preserved, and responses close with stop_reason: "tool_use"), but it does not execute the tools itself — execution is the client's job (Claude Code runs the tool and sends back tool_result).
  • Image content blocks are skipped during flattening.
  • Denylists are text-pattern matches, not shell parsers — wrappers like powershell -Command can smuggle commands past them; the token approval step is the real safety boundary.
  • Remote approval — approval goes through /mcp itself: whoever holds a valid OAuth Bearer token can approve pending actions; there is no terminal prompt and no separate human-authentication surface. Revoke the client with invincible oauth revoke <client_id> to cut that off.
  • Sessions are stored plaintext in PostgreSQL (protect the DSN); cooldowns and provider disables are in-memory only. Since the multi-tenant audit, every store path is ownership-scoped per principal — one user can never read another's sessions, graph, or history, and the former operator override is gone.
  • In-memory server state. Provider cooldowns, staged approvals (unless INVINCIBLE_PERSIST_PENDING_ACTIONS is set), and the agent registry live in the process, so a remote deployment runs a single instance (no horizontal autoscaling) and clients simply retry across a restart — see docs/DEPLOYMENT.md.

Full details: docs/SECURITY.md → Known limits.


Development

pip install -e ".[dev]"
pytest

See docs/TESTING.md.

Release files for invincible-ai 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for invincible-ai 0.3.1
File Size Uploaded
invincible_ai-0.3.1.tar.gz 495.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for invincible-ai 0.3.1
File Interpreter ABI Platform
invincible_ai-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 818.4 kB

Release files / invincible_ai-0.3.1.tar.gz

Download URL invincible_ai-0.3.1.tar.gz
Size 495.7 kB
Tags Source
SHA-256 checksum
How to use checksums
86f160155dcbb9c14f0c63d6a24659b4cfc651e82714d1495b465501757df86b
BLAKE2b-256 checksum
How to use checksums
d5fc0c9ffc2618d5d85b037b1ece727f1f44dcff2e3b278bcf410b777450fc98
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / invincible_ai-0.3.1-py3-none-any.whl

Download URL invincible_ai-0.3.1-py3-none-any.whl
Size 322.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d5d1c753c822247d0395e4e8e8141c8663cc286db3942801f2d4de509a7d7d3d
BLAKE2b-256 checksum
How to use checksums
b8a27bd94b58d935afbfabde06668c0eb833b8ed697611d873eb2144b4dfe1c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.0

2 release files

This release

0.3.1 This release

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page