Invincible — AI Continuity Gateway & Local MCP Agent
The Python package is named
invincible(the repository directory isai-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:
- Self-Service BYOK Gateway — an OpenAI-compatible
/v1/chat/completionsendpoint where every user authenticates with their owninv_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. - 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.
- MCP Tool Server — a JSON-RPC 2.0
/mcpendpoint exposingread_file,execute_bash, andwrite_fileto a cloud-hosted AI. WithINVINCIBLE_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:
- Providers — connect your own provider keys (BYOK).
- MCP connector — add
https://invincible-ai.me/mcpto 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 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, andmodelare accepted.modelis a soft routing hint (matches a configured alias or exactmodel_id; unknown names are ignored). Other OpenAI fields are rejected with 422. - Streaming:
stream: truereturns an OpenAI-compatible Server-Sent Events (text/event-stream) response. Each event is achat.completion.chunk(data: {…}\n\n), and the stream ends withdata: [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-formeddata: {"error": …}event. - Sessions: history is loaded from PostgreSQL keyed by the
X-Session-Idheader (defaultdefault), prepended to your messages, and the assistant reply is persisted back.session_idis 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: truereturns 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_usecontent blocks are preserved and streamed as structured events (content_block_start+input_json_deltaframes) withstop_reason: "tool_use"at the end;tool_resultblocks in the request are carried asrole: "tool"messages, so tool-shaped conversations round-trip losslessly (ids preserved).imagecontent blocks are still skipped. A mid-stream upstream failure emits a well-formed Anthropicerrorevent and closes — never malformed SSE. - Sessions: the same
X-Session-Idheader 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/authorizeconsent page, then sendAuthorization: Bearer <access_token>on every/mcprequest. Wrong/missing/expired/revoked token →401with aWWW-Authenticate: Bearer resource_metadata="…"challenge. (NoX-MCP-Secretheader anymore; the legacyMCP_SHARED_SECRETenv var is only read as a fallback for the owner login.) - Notifications: a request without an
idstill runs its side effect but the server replies204 No Contentwith 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 withstop_reason: "tool_use"), but it does not execute the tools itself — execution is the client's job (Claude Code runs the tool and sends backtool_result). - Image content blocks are skipped during flattening.
- Denylists are text-pattern matches, not shell parsers — wrappers like
powershell -Commandcan smuggle commands past them; the token approval step is the real safety boundary. - Remote approval — approval goes through
/mcpitself: 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 withinvincible 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_ACTIONSis 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.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| invincible_ai-0.4.0.tar.gz | 523.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| invincible_ai-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 860.4 kB
Release files / invincible_ai-0.4.0.tar.gz
| Download URL | invincible_ai-0.4.0.tar.gz |
|---|---|
| Size | 523.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e631e56d8ecf44274a97247c27b6afe85ff31a4b9b63b2e4a0d733ce00202fb7
|
|
BLAKE2b-256 checksum How to use checksums |
3ae5e7592875340a401b3121595248a4f9bcee9ef08be5f7e078d7b76a9cbc2f
|
| 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 25, 2026.
Transparency logRelease files / invincible_ai-0.4.0-py3-none-any.whl
| Download URL | invincible_ai-0.4.0-py3-none-any.whl |
|---|---|
| Size | 337.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d6243ad4ee9121277c53c712d94d2dd84dfa803a0302985406f5682a8223f989
|
|
BLAKE2b-256 checksum How to use checksums |
106eab4f432aa061fa1eed359db2ad31d677c5d7e2488491ed250c2a8c956d77
|
| 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 25, 2026.
Transparency log