torii
Torii is an OAuth 2.1 authorization server and gateway for MCP servers, providing tool-level RBAC, audit trail, and credential management. It enables Claude clients to securely access upstream MCP servers through a single authenticated front door.
README
<p align="center"> <img src="assets/torii-300.png" alt="torii" width="120" height="120"> </p>
<h1 align="center">torii</h1>
<p align="center"> <a href="https://github.com/recklessop/torii/actions/workflows/ci.yml"><img src="https://github.com/recklessop/torii/actions/workflows/ci.yml/badge.svg" alt="CI"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/license-PolyForm%20NC%201.0.0-yellow.svg" alt="License: PolyForm NC 1.0.0"></a> <a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.12-blue.svg" alt="Python 3.12"></a> </p>
A self-owned gateway in front of your MCP servers. torii is an OAuth 2.1 authorization server toward Claude clients (web, mobile, Office add-ins, Claude Code, the API) and static bearer keys for everything else, with tool-level RBAC, an audit trail, and a credential GUI. Your upstream MCP servers become LAN-only services behind a single authenticated, authorized front door.
A torii is the shrine gate between the ordinary world and the ground behind it: outside, every Claude surface; behind it, your MCP estate.
Status: running in production for its author, pre-1.0, published source-available. The authorization model, OAuth server, proxy, and admin UI are built and tested (580+ tests).
What it does
- One front door for many MCP servers. An aggregate
/mcpendpoint and per-server/<slug>/mcpendpoints fan out to your upstreams. Tool names keep the<server>__<tool>namespacing, so migrating a client is a URL swap. - OAuth 2.1 authorization server. RFC 8414 + RFC 9728 metadata, Dynamic Client Registration (RFC 7591), PKCE, refresh-token rotation and revocation — everything a claude.ai custom connector needs.
- Tool-level RBAC, default deny. Access is granted per tool, to a principal
or a group, and narrowed per OAuth client. There is one authorization
resolver and no admin-bypass path. The dangerous states (admin without
2FA, wildcard grants, an empty tool list on a
listgrant) are unrepresentable in the database schema, not just checked in code. - Local credentials with TOTP. bcrypt passwords, TOTP required for admins,
WebAuthn passkeys, and
tor_-prefixed API keys — all hashed at rest, shown once. An auth-backend seam is in place for a future external IdP. - Audit trail. Every call and auth event, retained (default one year), with a viewer in the UI. No request/response payloads captured by default.
- A credential and admin GUI, plus a public MCP directory as the only crawlable surface.
Quickstart (Docker)
git clone https://github.com/recklessop/torii.git && cd torii
cp .env.example .env
Fill in .env — at minimum a strong POSTGRES_PASSWORD, VALKEY_PASSWORD,
and SESSION_SECRET, and your public PUBLIC_BASE_URL:
# generate secrets
openssl rand -hex 24 # each password
python -c "import secrets; print(secrets.token_hex(32))" # SESSION_SECRET
Then bring it up. The public compose builds from source and runs Postgres and valkey on a private network (neither publishes a host port):
docker compose up -d
curl -s http://localhost:8400/healthz
Bootstrap the first admin:
docker compose exec torii python -m torii.cli bootstrap
torii listens on :8400. Put it behind a reverse proxy or tunnel that
terminates TLS and controls forwarding headers — see the security notes below.
Run it locally (from source)
python -m venv .venv && . .venv/bin/activate
pip install -r requirements-dev.txt
docker compose up -d postgres valkey
export PUBLIC_BASE_URL=http://localhost:8400
export SESSION_SECRET=$(python -c "import secrets; print(secrets.token_hex(32))")
python -m torii.server # http://localhost:8400/healthz
pytest -q # see CONTRIBUTING.md for the DB env vars
Configuration
All runtime state (principals, upstreams, grants, keys) lives in Postgres and
is managed in the UI. The process reads only a handful of environment
variables at boot — see .env.example. torii runs a boot-time
configuration check that prints each security-relevant setting's posture and
refuses to start on the worst combinations (for example, an https
PUBLIC_BASE_URL with no SESSION_SECRET).
Key ones:
| Variable | Purpose |
|---|---|
PUBLIC_BASE_URL |
Public origin; the OAuth issuer and WebAuthn origin. Must match the hostname clients use, byte-for-byte. |
SESSION_SECRET |
Signs the UI session cookie. Set a stable value or every restart logs everyone out — and an unset secret on an https origin is refused at boot. |
SESSION_HTTPS_ONLY |
Marks the session cookie Secure. Set true behind TLS. |
TORII_ENCRYPTION_KEY |
Fernet key encrypting upstream auth headers at rest. Saving an upstream credential is refused if unset. |
METRICS_TOKEN |
Bearer token for /metrics. Empty means the endpoint is off (404). |
Security
torii is a security component. Before deploying, read SECURITY.md for the honest posture — in particular:
- Run it behind a proxy/tunnel you control; it trusts forwarding headers for audit context and should not face the internet directly.
- Set
SESSION_SECRET,SESSION_HTTPS_ONLY=true, andTORII_ENCRYPTION_KEYfor a real deployment. - Runtime dependencies are pinned and hashed in
requirements.lock; CI runsbandit,pip-audit, andgitleaks.
Report vulnerabilities privately per the process in SECURITY.md.
License
Released under PolyForm Noncommercial 1.0.0 (see LICENSE). torii is source-available, not OSI open-source: read, run, modify, and share it for noncommercial purposes; commercial use requires a separate arrangement.
Contributing
See CONTRIBUTING.md. The one rule that matters most: every caller-facing surface routes through the single RBAC resolver — never add a second authorization path.
Layout
| Path | What |
|---|---|
torii/rbac.py |
The authorization choke point: one resolver, default deny, no admin bypass |
torii/proxy.py |
/mcp aggregate + /<slug>/mcp per-server endpoints, fan-out, audit |
torii/oauth.py, routes_oauth.py |
OAuth 2.1 AS: metadata, DCR, PKCE, rotation, revocation |
torii/credentials.py |
Passwords, TOTP, tor_ keys, tokens — all hashing |
torii/startup.py |
Boot-time configuration validation |
torii/config.py |
Environment wiring only; runtime config lives in Postgres |
torii/migrations/ |
*.sql, applied in filename order on boot (advisory-locked) |
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.