Multi-tenant MCP platform with OAuth 2.1, Entra SSO, RBAC and audit logging.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste into ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
The production platform for MCP tools.
Claude Desktop can connect to your internal tools β databases, filesystems, APIs, anything β through a single authenticated endpoint. You control who can use which tools, every action is logged, and no raw credentials ever leave your server.
Built-in tools: SQL query (Postgres, MySQL, SQLite, MSSQL), filesystem access. Custom tools: plug in anything that implements the MCP tool interface.
See it in action β short demo of Claude Desktop querying a database through MCP Gateway.
MCP Gateway sits between AI assistants and your databases. It:
For your organisation
For your tools
For your security team
| Database | Driver | DSN Format |
|---|---|---|
| PostgreSQL | psycopg2 | postgresql://user:pass@host/db |
| MySQL / MariaDB | PyMySQL | mysql+pymysql://user:pass@host/db |
| Microsoft SQL Server | pymssql | mssql+pymssql://user:pass@host/db |
| SQLite | Built-in | sqlite:///path/to/file.db |
FILESYSTEM_ALLOWED_DIRS environment variablefs_read_file, fs_list_directory, fs_directory_tree, fs_search_files, fs_get_file_infofs_write_file, fs_create_directory, fs_move_file/admin/| Layer | Technology | Version |
|---|---|---|
| API Framework | FastAPI | 0.131.0 |
| ASGI Server | Uvicorn | 0.34.0 |
| ORM | SQLAlchemy | 2.0.30 |
| Migrations | Alembic | 1.13.1 |
| Auth / JWT | PyJWT + bcrypt | 2.12.0 / 4.0.1 |
| Encryption | cryptography (Fernet) | 46.0.5 |
| LLM | Anthropic SDK | 0.42.0 |
| MCP Protocol | mcp | 1.23.0 |
| SQL Validation | sqlglot | 25.1.0 |
| Rate Limiting | slowapi | 0.1.9 |
| Frontend | React 18 + TypeScript + Vite | β |
/query/ endpoint; not needed for raw MCP tool access)Edit .env:
Generate secure random values:
Services started:
api on port 8000 (FastAPI + admin UI)db on port 5432 (PostgreSQL, internal only)The slug becomes part of your MCP URL: http://localhost:8000/t/my-org/mcp/sse
Navigate to http://localhost:8000/admin/ and sign in with your admin credentials.
In the admin UI β Connections β Create connection, or via API:
Add to your Claude Desktop MCP config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
Restart Claude Desktop. It will open a browser window for OAuth login. After authenticating, Claude can use your database tools.
All configuration is via environment variables. See .env.example for a template.
| Variable | Description |
|---|---|
SECRET_KEY | JWT signing secret β use a random 64-char string |
ENCRYPTION_KEY | Fernet AES key for DB credentials β minimum 32 characters; full key consumed via BLAKE2b |
POSTGRES_PASSWORD | PostgreSQL password β used by docker-compose for both the db service and DATABASE_URL |
DATABASE_URL | PostgreSQL DSN β set automatically by docker-compose; only needed for local (non-Docker) dev |
| Variable | Default | Description |
|---|---|---|
ANTHROPIC_API_KEY | β | Required for /query/ NL query endpoint |
BASE_URL | http://localhost:8000 | Public-facing URL (used in OAuth callbacks) |
CORS_ORIGINS | BASE_URL | Comma-separated allowed origins for CORS. Must be absolute URLs β wildcards (*) are rejected |
ACCESS_TOKEN_EXPIRE_MINUTES | 15 | JWT access token lifetime |
REFRESH_TOKEN_EXPIRE_DAYS | 30 | OAuth refresh token lifetime |
OAUTH_STATE_TTL_MINUTES | 10 | OAuth PKCE state validity window β increase for high-latency SSO providers |
OAUTH_CODE_TTL_MINUTES | 5 | OAuth authorization code validity window |
LLM_MODEL | claude-sonnet-4-6 | Anthropic model for SQL generation |
LLM_MAX_TOKENS_SQL | 1024 | Max tokens for SQL generation |
LLM_MAX_TOKENS_SUMMARY | 500 | Max tokens for result summarization |
FILESYSTEM_ALLOWED_DIRS | β | Comma-separated directories the MCP filesystem tools may access. When empty, no filesystem tools are exposed |
| Variable | Default | Description |
|---|---|---|
ENTRA_AUTHORITY_URL | https://login.microsoftonline.com | Microsoft identity platform base URL |
ENTRA_GRAPH_URL | https://graph.microsoft.com/v1.0 | Microsoft Graph API base URL |
Response:
Include the token in subsequent requests:
Access tokens expire after 15 minutes by default. Use the OAuth token endpoint with a refresh token to get a new pair.
Generate a key (requires authentication):
The raw_key in the response is shown once only β store it immediately:
Use via query parameter:
The gateway implements RFC 8414 OAuth discovery. MCP clients follow this flow automatically:
/t/{slug}/mcp/sse β receives 401 + WWW-Authenticate header pointing to the OAuth discovery URL/.well-known/oauth-authorization-server/t/{slug}POST /t/{slug}/oauth/register/t/{slug}/oauth/authorizePOST /t/{slug}/oauth/tokenNo manual configuration needed β just point mcp-remote at your tenant's SSE URL.
For CI/CD, scripts, or when you want to skip the browser login, pass an API key in the URL:
The SSE endpoint validates the key and establishes the session directly β no OAuth flow, no browser window. See API Keys for details.
All management endpoints are available at both their canonical paths (e.g. /tenants/) and the versioned prefix /api/v1/ (e.g. /api/v1/tenants/). The unversioned paths are kept for backward compatibility with the current frontend; new integrations should use /api/v1/. Protocol-defined routes (OAuth /t/{slug}/β¦, MCP /t/{slug}/β¦, /.well-known/) and infrastructure routes (/health, /admin) are intentionally unversioned.
| Method | Path | Role | Description |
|---|---|---|---|
POST | /tenants/ | Public | Register new tenant + admin user |
GET | /tenants/me | Any | Get your tenant details |
GET | /tenants/users | Admin | List all users in your tenant |
POST | /tenants/users | Admin | Create a local user |
PATCH | /tenants/users/{id} | Admin | Update user role |
Register tenant:
Create user:
Roles: viewer (default), analyst, admin. Passwords must be at least 12 characters.
| Method | Path | Role | Description |
|---|---|---|---|
POST | /connections/ | Admin | Add a database connection |
GET | /connections/ | Viewer+ | List accessible connections |
PATCH | /connections/{id} | Admin | Update connection |
DELETE | /connections/{id} | Admin | Soft-delete connection |
Add connection:
min_role controls who can query this connection. Users below this role cannot see or use it.
| Method | Path | Role | Rate Limit | Description |
|---|---|---|---|---|
POST | /query/ | Analyst+ | 30/min | Execute NL query |
GET | /query/history | Admin | 60/min | Paginated query audit history |
Query:
Response:
The query pipeline:
SELECT statement (blocks all writes)| Method | Path | Role | Description |
|---|---|---|---|
GET | /tools/ | Any | List MCP tools with role metadata |
PATCH | /tools/{tool_name} | Admin | Set or reset role override |
List tools:
Response:
Override tool role:
| Method | Path | Description |
|---|---|---|
POST | /api-keys/ | Generate a new key |
GET | /api-keys/ | List your keys |
DELETE | /api-keys/{id} | Revoke a key |
| Method | Path | Role | Rate Limit | Description |
|---|---|---|---|---|
GET | /audit-logs/ | Admin | 60/min | List audit events (filterable) |
Query parameters: skip (offset, default 0), limit (max 200, default 50), event_prefix (comma-separated, e.g. query, login, fs).
Returns 503 if the database is unreachable. Suitable for Kubernetes liveness and readiness probes.
Install mcp-remote:
Add to claude_desktop_config.json:
On first connection, a browser window opens for OAuth login. After authenticating, mcp-remote caches the tokens and reconnects automatically. Tokens refresh silently in the background.
For each active database connection the user can access, the gateway exposes two tools:
get_schema_{connection-name}_{id}
Returns the full database schema (tables, columns, types, constraints, indexes). Claude calls this first to understand the data structure before generating SQL.
execute_sql_{connection-name}_{id}
Executes a SELECT statement and returns rows as JSON. Any non-SELECT statement is rejected (INSERT, UPDATE, DELETE, DROP, etc.). Execution timeout: 30 seconds.
list_connections
Returns all database connections the user can access with their names and types.
get_current_time
Returns the current UTC time in ISO 8601 format. Available to all roles.
Filesystem tools (only when FILESYSTEM_ALLOWED_DIRS is configured):
| Tool | Role | Description |
|---|---|---|
fs_read_file | Analyst+ | Read a file as UTF-8 text |
fs_list_directory | Analyst+ | List directory contents |
fs_directory_tree | Analyst+ | Recursive directory tree (JSON) |
fs_search_files | Analyst+ | Glob pattern search |
fs_get_file_info | Analyst+ | File metadata (size, timestamps) |
fs_write_file | Admin | Create or overwrite a file |
fs_create_directory | Admin | Create a directory (with parents) |
fs_move_file | Admin | Move or rename a file |
| Endpoint | Auth | Description |
|---|---|---|
GET /t/{slug}/mcp/sse | Bearer JWT or ?api_key= | Tenant-scoped SSE (recommended) |
POST /t/{slug}/mcp/messages | Bearer JWT, ?api_key=, or session ID | Tenant-scoped message handler |
GET /mcp/sse | ?api_key= or ?token= | Legacy SSE (deprecated, sunset 2026-06-01) |
POST /mcp/messages | Bearer JWT, ?api_key=, or ?token= | Legacy message handler (deprecated) |
| Endpoint | RFC | Description |
|---|---|---|
GET /.well-known/oauth-authorization-server/t/{slug} | RFC 8414 | Authorization server metadata |
GET /.well-known/oauth-protected-resource/t/{slug}/mcp/sse | RFC 9728 | Protected resource metadata |
POST /t/{slug}/oauth/register | RFC 7591 | Dynamic client registration |
GET /t/{slug}/oauth/authorize | RFC 6749 | Authorization endpoint (PKCE S256) |
POST /t/{slug}/oauth/token | RFC 6749 | Token endpoint (code + refresh_token) |
Any Python function becomes an authenticated, audited MCP tool:
Restart the gateway. The tool appears in Claude Desktop automatically, with auth and audit logging included.
Three roles in ascending order of permission: viewer β analyst β admin
| Action | Viewer | Analyst | Admin |
|---|---|---|---|
| View connections | β | β | β |
| Run NL queries | β | β | β |
| Use filesystem tools (read) | β | β | β |
| Use filesystem tools (write) | β | β | β |
| View audit logs | β | β | β |
| View query history | β | β | β |
| Manage connections | β | β | β |
| Manage users | β | β | β |
| Configure SSO | β | β | β |
| Manage API keys | β | β | β |
| Override tool roles | β | β | β |
Each connection has a min_role. Users below this role cannot see or use that connection, or the MCP tools it generates.
Example: A sensitive production database with min_role: admin is invisible to analysts and viewers entirely β it won't appear in /connections/ or /tools/, and its MCP tools won't be listed.
Admins can override the effective minimum role for any MCP tool independently of the connection's min_role:
http://<gateway-url>/auth/entra/callback (admin UI SSO)http://<gateway-url>/t/<slug>/oauth/entra-callback (MCP OAuth flow)openid, profile, email, User.Read, GroupMember.Read.AllDirectory.Read.All (required for role sync during token refresh)GroupMember.Read.All and the application Directory.Read.AllVia admin UI: SSO Config tab, or via API:
Group IDs are optional β configure only what you need. Users in multiple mapped groups get the highest role.
Direct users to: http://<gateway>/auth/entra/login?tenant_slug=<slug>
The gateway redirects to Microsoft. After authentication it:
/me)/me/transitiveMemberOf)The Vite dev server proxies all API paths to http://localhost:8000, so the frontend and API can run independently during development.
Starts pre-seeded sample databases:
sample_postgres on port 5433 β postgresql://sampleuser:samplepass@localhost:5433/sampledbsample_mysql on port 3307 β mysql+pymysql://sampleuser:samplepass@localhost:3307/sampledbAdd these as connections in the admin UI to explore the natural language query feature.
docker-compose uses ${VAR:?error message} syntax β it fails fast if these are not set. Generate them:
Add to your .env file before running docker compose up.
The database is not reachable. Check:
Ensure mcp-remote is installed: npm install -g mcp-remote. Check that BASE_URL in .env matches the URL you put in claude_desktop_config.json. A mismatch causes the OAuth callback to fail silently.
"INVALID_QUERY"The LLM could not generate a valid SELECT for your question, or it generated a non-SELECT statement (which is blocked). Try:
ANTHROPIC_API_KEY is set and validThe tenant doesn't have an Entra ID configuration. Add one via Admin UI β SSO Config or POST /auth/entra/config.
The Azure AD user is not in any of the three groups configured for the tenant. Either:
POST /auth/entra/config)Refresh tokens are single-use β each use issues a new pair and revokes the old one. If two requests attempt to use the same refresh token simultaneously, the second fails. Re-authenticate to get a fresh pair.
| Endpoint | Limit |
|---|---|
POST /tenants/ | 5/min |
POST /auth/login | 10/min |
POST /t/{slug}/oauth/login | 10/min |
POST /api-keys/ | 10/min |
GET /auth/entra/login | 20/min |
GET /t/{slug}/oauth/authorize | 30/min |
POST /t/{slug}/oauth/token | 30/min |
POST /query/ | 30/min |
GET /audit-logs/ | 60/min |
GET /query/history | 60/min |
Wait 60 seconds for the limit window to reset.
| Data | Storage |
|---|---|
| Passwords | bcrypt (never stored plain) |
| JWT signing | SECRET_KEY (HS256) |
| DB connection strings | Fernet AES-256 encrypted |
| Entra client secrets | Fernet AES-256 encrypted |
| API keys | HMAC-SHA-256 keyed with SECRET_KEY (raw key returned once, never stored) |
| Refresh tokens | SHA-256 hash |
All responses include:
X-Content-Type-Options: nosniffX-Frame-Options: DENYStrict-Transport-Security: max-age=31536000Cache-Control: no-store on auth endpointslocalhost, 127.0.0.1, and ::1 are accepted as redirect targets (per RFC 8252)The execute_sql MCP tool rejects all non-SELECT statements via sqlglot AST parsing before any query reaches the database. INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, TRUNCATE, and EXEC are all blocked regardless of how they are formatted.
All database queries are scoped to current_user.tenant_id. Foreign key constraints enforce isolation at the schema level β there is no code path that allows data from one tenant to appear in another tenant's responses.
Rotating SECRET_KEY: All existing JWTs immediately become invalid. Users must re-authenticate. Refresh tokens (hashed separately) are also invalidated. API keys are also invalidated β they are HMAC-keyed with SECRET_KEY, so existing keys must be revoked and re-issued after rotation.
Rotating ENCRYPTION_KEY: Requires re-encrypting all stored connection strings and Entra client secrets with the new key before the old key is removed. Plan this as a maintenance window β the gateway cannot serve connections during the rotation.
All significant events are written to the audit_logs table:
| Event | When |
|---|---|
login.success / login.failure | Every login attempt |
oauth.login / oauth.entra_login | OAuth authorization |
oauth.token_issued / oauth.token_refreshed | Token exchange and refresh |
query.success / query.failure | Every NL query |
tool.execute_sql / tool.execute_sql.rejected / tool.execute_sql.error | MCP SQL tool usage |
fs.* (e.g. fs.fs_read_file, fs.fs_write_file.error) | Filesystem tool usage |
connection.created / connection.updated / connection.deleted | Connection changes |
tenant.created | Tenant registration |
user.deleted / user.role_updated | User management |
key.created / key.revoked | API key lifecycle |
Query the audit log:
| Guide | Description |
|---|---|
| Testing with Claude Desktop | End-to-end walkthrough: local users + Entra SSO |
| Deployment Guide | Railway, Render, and generic Docker/VPS deployment |
| OAuth 2.1 Flow | Full PKCE flow, endpoints, token lifecycle |
| Filesystem Tools | Sandboxed file access via MCP |
| Audit Logging | Event catalog, API, and metadata reference |
| API Keys | Key lifecycle, security model, usage |
| Tool Role Overrides | Per-tool RBAC configuration |
MCP Gateway is the open source foundation. A managed platform called SaltMine AI is currently in development, built on top of this project, following the same security principles and aimed at business teams who want to query their data without any infrastructure to manage.
Planned features include:
If you're interested in learning more, have a use case you'd like to discuss, or just want to follow the progress:
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/mcp-gateway)<a href="https://allmcps.com/mcp/mcp-gateway"><img src="https://allmcps.com/api/badge/mcp-gateway?style=directory" alt="MCP Gateway on AllMCPs" /></a>