Skip to main content
Glama
MikeBild

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).