supabase-mcp
by MikeBild
README.md
# Supabase MCP
A secure remote [Model Context Protocol](https://modelcontextprotocol.io/) server for the complete API surface of **one self-hosted Supabase installation**.
This project deliberately has **no Supabase Cloud or Management API integration**. It does not know about organizations, hosted projects, billing, branches, regions, or access tokens for `api.supabase.com`.
The production shape is a single Bun/TypeScript binary behind Caddy:
```text
MCP client
│ HTTPS + OAuth 2.1 / scoped API key
▼
Caddy ── /mcp ──► supabase-mcp :4070
│
┌──────────────┼───────────────┐
▼ ▼ ▼
PostgreSQL local services control DB
+ pg-meta Auth/Storage/ OAuth, keys,
Realtime/Edge/ confirmations,
PostgREST/Logs audit
```
## What is covered
- PostgreSQL schema discovery, policies, functions, extensions, advisors, migrations, parameterized read queries, and confirmed administrative SQL.
- The complete local PostgREST, GoTrue/Auth, Storage, pg-meta, Realtime HTTP, Edge Functions, and Logflare APIs through service-specific, fixed-origin request tools.
- Agent-friendly helpers for Auth users, Storage buckets/objects, Edge Function source/deployment, and live Realtime subscriptions.
- Streamable HTTP MCP with sessions, SSE, tools, resources, prompts, protocol-version checks, Origin checks, credential-bound sessions, TTL, and capacity limits.
- OAuth 2.1 Authorization Code with PKCE, Dynamic Client Registration, Protected Resource Metadata, refresh-token rotation, and optional login through the installation's own GoTrue OIDC provider.
- Scoped operator API keys, scope-filtered tool discovery, encrypted human confirmation for destructive operations, and durable audit records.
- OpenAPI 3.1, operator documentation, `llms.txt`, `llms-full.txt`, CI, tests, and cross-compiled self-contained binaries.
The complete list of MCP tools is generated in [docs/tools.md](docs/tools.md).
## Quick start
Requirements: Bun 1.3+, PostgreSQL, and a self-hosted Supabase deployment.
```bash
bun install --frozen-lockfile
cp .env.example .env
# Fill every CHANGE_ME and point the service URLs at local/loopback services.
bun run dev
```
Check:
```bash
curl http://127.0.0.1:4070/health
curl http://127.0.0.1:4070/ready
curl http://127.0.0.1:4070/openapi.json
```
Configure an MCP client:
```json
{
"mcpServers": {
"supabase": {
"type": "http",
"url": "https://supabase.example.com/mcp"
}
}
}
```
Standards-aware clients discover OAuth from the `WWW-Authenticate` challenge. For automation,
send an operator-created `smcp_…` key as either `Authorization: Bearer …` or `X-API-Key: …`.
OAuth access tokens are accepted only as Bearer tokens.
## Security model
There are two PostgreSQL connections:
- `DATABASE_URL` stores only MCP control-plane state (`smcp_*` tables).
- `SUPABASE_TARGET_DATABASE_URL` reaches the Supabase database for privileged operations. `SUPABASE_TARGET_READONLY_DATABASE_URL` should use a read-only database role.
API keys and OAuth tokens are HMAC-hashed at rest. OAuth grants created through an API key remain
live-bound to that key: expiry or revocation invalidates their access and refresh tokens. Pending
destructive payloads are encrypted with AES-256-GCM. Tools are absent from `tools/list` unless the
credential has their scope. Service request tools select only an operator-configured base URL and
cannot become arbitrary network proxies. Supabase service-role and Logflare keys are never returned.
Destructive calls use a two-step flow:
1. The tool returns `human_confirmation_required`, a `confirmation_id`, and an expiring review URL.
2. A human approves the exact encrypted operation in the browser.
3. The client retries the same tool and arguments with `confirmation_id`.
Changing any argument invalidates the approval. Approval does not execute anything by itself.
Read [docs/security.md](docs/security.md) before exposing the service to a network.
## Scopes
| Scope | Capability |
| ------------------------------------------------------ | -------------------------------------------------------------------------------- |
| `supabase:metadata:read` | Schemas, tables, policies, functions, extensions, migrations, advisors |
| `supabase:data:read` / `supabase:data:write` | SQL reads and PostgREST data operations |
| `supabase:database:write` | Administrative SQL and pg-meta mutations |
| `supabase:auth:read` / `supabase:auth:write` | Local GoTrue APIs |
| `supabase:storage:read` / `supabase:storage:write` | Local Storage APIs |
| `supabase:functions:read` / `supabase:functions:write` | Edge Function invocation and lifecycle |
| `supabase:realtime:read` / `supabase:realtime:write` | Realtime subscriptions, messages, and HTTP API |
| `supabase:logs:read` | Local Logflare query API |
| `supabase:config:read` | Non-secret endpoint and capability information |
| `supabase:secrets:read` | Reserved for explicit secret-reading extensions; no secret is exposed by default |
| `supabase:config:write` | Reserved; host configuration remains GitOps-managed |
| `supabase:admin` | Administrative ceiling; normalized to every supported scope |
## Build
```bash
bun run gate
bun run build:binary
SUPABASE_MCP_BUN_TARGET=bun-linux-x64 OUTFILE=dist/supabase-mcp-linux-x64 ./build-binary.sh
./dist/supabase-mcp --version
```
`bun build --compile` embeds the runtime and dependencies. The service needs only the binary, environment file, and access to PostgreSQL/local Supabase endpoints.
## HTTP endpoints
- `GET /health`, `GET /ready`, `GET /version`
- `GET|POST|DELETE /mcp`
- OAuth metadata under `/.well-known/*`; endpoints under `/mcp/oauth/*`
- `GET /docs`, `GET /openapi.json`, `GET /llms.txt`, `GET /llms-full.txt`
- scoped operator API under `/api/v1/*`
- expiring human reviews under `/mcp/confirm/:id`
See [docs/operators.md](docs/operators.md), [docs/architecture.md](docs/architecture.md), and the generated [OpenAPI document](docs/openapi.json).
## Development
```bash
bun run format
bun run gate
```
Unit and contract tests do not require Supabase. Integration and E2E tests are separated because they require local services. Contributions must preserve the fixed-origin upstream boundary and must not add Supabase Cloud APIs.
## License
MIT. See [LICENSE](LICENSE) and [NOTICE](NOTICE).