Status: Guided setup and living reference implemented
Date: 2026-08-20
Current package version: 0.12.2
License: MIT
Primary runtime: Supabase Edge Functions (Deno/TypeScript)
Protocol target: MCP 2026-07-28, with stateless legacy compatibility where the
official SDK provides it
chumbo will be a small TypeScript package and initializer that
adds an end-user-facing MCP server to an existing Supabase application.
The product promise is:
A Supabase builder runs one command, deploys one Edge Function, and an end user connects an MCP client through the builder's existing Supabase Auth. MCP handlers receive a request-scoped Supabase client, so the application's existing Row Level Security policies govern every database operation.
The package will compose, not replace:
@modelcontextprotocol/serverfor MCP protocol behavior;@supabase/serverand Supabase Auth for request identity and Supabase clients;- Supabase OAuth 2.1 Server as the authorization server;
- Supabase Edge Functions as the protected MCP resource server runtime.
Chumbo owns the reusable boundary between an existing Supabase application and its end-user-facing MCP: protocol wiring, request-scoped identity, guided setup, diagnostics, result contracts, and optional advanced patterns.
The stable expansion model is one library, not product-tier architecture:
Chumbo makes one Supabase-native MCP easy and supports increasingly advanced patterns – including many MCPs from one function – through the same library.
Advanced patterns live in the open-source reference project first. They become reusable package API only when doing so reduces adopter work without adding conceptual weight to the ordinary one-server path.
Supabase documents how to run a simple MCP server in an Edge Function, but the current example stops at a public arithmetic tool. A builder who wants to give their product's end users an MCP endpoint must still solve:
- modern Streamable HTTP MCP wiring;
- OAuth protected-resource metadata and
WWW-Authenticatechallenges; - delegation to Supabase Auth as the OAuth 2.1 authorization server;
- request-scoped Supabase JWT validation and client construction;
- preserving RLS rather than accidentally using a privileged client;
- Edge Function routing and
config.tomlconfiguration; - local testing, deployment, client connection, and common diagnostics;
- protocol-version drift as MCP evolves.
Existing GitHub projects demonstrate the pattern, but no widely adopted package currently owns this complete application-facing happy path. Several existing projects are examples, old-protocol handlers, broad proposed SDKs, or Supabase administration servers rather than a maintained adapter for an existing app.
A developer already building a product on Supabase who wants their users to connect ChatGPT, Claude, Codex, Cursor, or another remote MCP client to selected application capabilities.
"Expose my application's existing actions and data through MCP without creating a second identity system, a second authorization model, or another hosted service."
An existing application user who connects an MCP client, signs in through the application's Supabase Auth flow, grants access, and receives only the data and actions allowed by the application's existing permissions.
Chumbo is the MCP layer for Supabase apps. It helps application builders expose their own product capabilities to users and agents through one Supabase-native MCP endpoint.
Recommended public language:
Add an end-user MCP server to your Supabase app, with OAuth and RLS already wired.
Lead with what builders can create and deploy. Explain neighboring categories only when a concrete comparison helps the reader make a decision.
Version 0.1.0 must:
- initialize safely inside an existing Supabase repository;
- generate a deployable Edge Function using the official MCP TypeScript SDK;
- support the modern MCP
2026-07-28stateless request model; - expose OAuth protected-resource metadata from the MCP endpoint;
- direct MCP clients to the project's Supabase OAuth authorization server;
- validate bearer tokens and create request-scoped, RLS-aware Supabase clients;
- make tools, resources, and prompts straightforward to register;
- demonstrate one Multi Round-Trip Request (MRTR) input flow;
- provide deterministic local and cloud verification;
- prove tenant separation with two users from different organizations;
- provide a
doctorcommand that catches the common configuration failures; - publish a pinned, reproducible package and tagged public GitHub release.
- support a clean public MCP URL without coupling it to the Supabase OAuth issuer, and verify the complete proxied discovery route.
Version 0.1.0 will not:
- implement the MCP wire protocol itself;
- replace or wrap the complete official MCP SDK API;
- create generic MCP tools from every database table;
- expose Supabase service-role access to user handlers by default;
- provide a resident Durable Object, actor affinity, mailbox, alarms, or transport sessions; the optional durable-state primitive is bounded request-to-request coordination rather than an actor runtime;
- ship graph, vector, WASM, AI inference, or agent orchestration frameworks;
- implement a general OAuth authorization server;
- generate a branded consent UI for every frontend framework;
- require tasks, queues, prompts, resources, or elicitation merely because MCP supports them;
- support deprecated Roots, Sampling, or Logging capabilities in new examples;
- guarantee compatibility with every MCP host before it has been tested.
An optional Postgres-backed MCP Tasks adapter is a follow-on. It must not delay the authenticated tool/resource golden path.
The release is done only when the following end-to-end scenario passes against a deployed Supabase project:
- Alice belongs to organization A.
- Bob belongs to organization B.
- Both organizations contain private fixture rows protected by RLS.
- A real remote MCP client is pointed at the deployed MCP URL.
- Alice receives an OAuth challenge and is redirected to the application's Supabase authorization flow.
- Alice signs in, approves access, and the client obtains access and refresh tokens.
- A tool call executed as Alice returns only organization A rows.
- A resource read executed as Alice returns only organization A content.
- Bob completes the same flow and receives only organization B rows.
- Concurrent Alice and Bob calls do not share identities, clients, cached tenant state, or results.
- Token refresh works without manually replacing a credential.
- Revocation or an invalid token produces a standards-shaped OAuth challenge, not an opaque server error.
- The same generated project passes local tests, deploys with the documented Supabase CLI command, and connects through the official MCP client SDK.
Passing a public sum tool or a curl call with a manually copied service key is
not sufficient evidence.
Primary command:
npx chumbo setupNon-interactive equivalent:
npx chumbo setup \
--function mcp \
--auth oauth \
--server-name my-app \
--yes \
--jsoninit remains the deterministic file-generation primitive. setup composes
it with local checks, optional migration/deployment execution, remote
verification, and an ordered next-action report. status provides the same
observation model without mutation.
The setup flow must:
- locate
supabase/config.toml; - refuse to invent a new Supabase project unless
--create-projectis explicitly supplied in a later release; - inspect existing target files and never overwrite them silently;
- print a proposed file plan before mutation;
- support
--dry-run; - generate or patch only the files listed in the plan;
- print exact local-run, test, deploy, OAuth-setup, and connection commands;
- expose a versioned JSON envelope with stable step IDs;
- never prompt in JSON mode;
- resume by re-observing project and endpoint state rather than trusting a hidden mutable state file.
Interactive prompts:
- authentication mode in plain builder language:
oauth,api-key,bearer, orpublic; - confirmation of the complete file plan.
Function name, display name, consent fallback, config patching, migration application, project ref, and deployment remain explicit flags. This keeps the human path short and the agent path deterministic.
oauth is the recommended end-user choice. api-key is the shortest
authenticated application path. bearer is useful for automated tests and
developer clients that can inject an existing Supabase JWT. public must
require explicit confirmation and should be presented as a demo/webhook mode,
not the normal end-user configuration.
The intended hand-authored file should remain small:
import {
createSupabaseMcp,
structuredResult,
type SupabaseMcpContext,
type SupabaseMcpServer,
} from "chumbo";
import { z } from "zod";
const app = createSupabaseMcp({
server: { name: "acme", version: "1.0.0" },
resourceUrl: new URL(`${Deno.env.get("SUPABASE_URL")}/functions/v1/mcp`),
auth: { mode: "oauth" },
register(server: SupabaseMcpServer, ctx: SupabaseMcpContext) {
server.registerTool(
"list_projects",
{
description: "List projects visible to the connected user.",
inputSchema: z.object({}),
outputSchema: z.object({
projects: z.array(
z.object({
id: z.string(),
name: z.string(),
status: z.string(),
}),
),
}),
},
async () => {
const { data, error } = await ctx.supabase
.from("projects")
.select("id, name, status")
.order("name");
if (error) throw error;
return structuredResult({ projects: data ?? [] });
},
);
},
});
if (import.meta.main) Deno.serve(app.fetch);
export default app;The implementation deliberately passes through the official McpServer
registration API. Handlers receive request-scoped context, while application
authors do not implement JSON-RPC, OAuth challenges, protocol headers, or
Supabase client construction.
npx chumbo devThe command may delegate to the installed Supabase CLI rather than wrapping its entire behavior. It should print the resolved MCP URL and client/Inspector commands.
The generated repository must also remain usable with ordinary commands:
supabase start
supabase functions serve mcp
deno test supabase/functions/mcpnpx chumbo doctordoctor should check, without exposing secrets:
- Supabase repository and CLI availability;
- generated function and import configuration;
- pinned dependency resolution;
- function configuration and auth mode;
- project URL and expected MCP URL;
- OAuth authorization-server discovery metadata;
- MCP protected-resource metadata URL;
WWW-Authenticatechallenge shape;- whether the endpoint advertises the expected protocol version;
- optional live
tools/listcall when credentials are supplied; - whether a remote response was produced by Chumbo or by an upstream gateway;
- deployed runtime version, auth mode, API-key strategy, and advertised resource URL through non-secret response metadata;
- common RLS symptom: authenticated call succeeds but returns no rows;
- accidental service-role configuration in a user-facing template.
Diagnostics distinguish required runtime conditions from recommended generated project hygiene. A missing entrypoint, unpinned runtime, or incorrect gateway configuration blocks readiness. Alternate capability-file or test-task layouts are advisory when the function can otherwise run. Remote verification is graded as reachable, runtime-confirmed, access-protected, credential-accepted, and MCP-discovered; omitting an optional test credential must not become a failure or a false claim of authenticated completion.
Each blocking failure must name an immediate recovery command or documentation section.
npx chumbo skill install
npx chumbo skill status
npx chumbo skill updateThe skill is versioned inside the npm artifact and installed at
skills/chumbo/. It is the recommended operating path for agent-assisted
development and remains optional for manual work. The skill teaches Chumbo's
current API and commands, translates application operations into coherent MCP capabilities,
chooses explicit result contracts, preserves request-scoped authority, and
verifies the protocol boundary. It is not a copy of Supabase documentation or
an automatic schema-to-tools generator.
The installer appends one marked pointer to the root AGENTS.md while
preserving all existing content. A managed manifest records the installed
package version and exact file hashes. Updates replace or remove a managed file
only when its current hash still matches the manifest; otherwise they report a
conflict without writing. --plan, --yes, and --json follow setup's
non-interactive conventions. Setup recommends this command for agent-assisted
work but never runs it implicitly.
Proposed default output:
supabase/
├── config.toml
└── functions/
└── mcp/
├── index.ts
├── capabilities.ts
├── deno.json
├── index_test.ts
└── README.md
Responsibilities:
index.ts: constructs and exports the MCP Edge handler.capabilities.ts: application-authored tools, resources, and prompts.deno.json: exact pinned imports and test tasks.index_test.ts: generated transport, context, and example capability tests.- function
README.md: project-local usage, URLs, and auth setup.
Do not generate a large framework directory. Shared runtime logic belongs in the published package. Generated code should remain readable and removable.
A Chumbo app is a web-standard fetch handler, so the Edge Function is the
default home rather than a requirement. setup --target next generates a
colocated App Router scaffold and --target node generates a standalone
server served through chumbo/node:
app/mcp/ mcp/
├── [[...path]]/route.ts ├── index.ts
├── capabilities.ts ├── capabilities.ts
└── README.md └── README.md
Host targets keep the same capabilities.ts seam, access modes, and result
contracts. They read configuration from process environment variables, place
generated database support in supabase/migrations/ when the repository has
a Supabase directory and beside the scaffold otherwise, and lean on
chumbo doctor as their executable contract instead of generated Deno tests.
Doctor detects host scaffolds, checks their files and pinned runtime, and
probes any deployed URL without requiring supabase/config.toml. The
generated fallback consent function remains Edge-only; host applications own
consent in their signed-in UI. Chumbo does not become a web framework: no
middleware, framework configuration ownership, or deployment pipelines.
Keep one repository and one npm package unless implementation evidence forces a split.
chumbo/
├── src/
│ ├── runtime.ts
│ ├── types.ts
│ ├── results.ts
│ ├── testing.ts
│ ├── project.ts
│ ├── doctor.ts
│ └── cli.ts
├── templates/
│ ├── function/
│ └── consent/
├── examples/
│ └── multi-tenant/
├── test/
├── package.json
├── README.md
├── SPEC.md
└── LICENSE
Expected exports:
{
".": "./dist/index.js",
"./testing": "./dist/testing.js",
"./node": "./dist/node.js"
}chumbo/node is the standalone-server adapter: toNodeHandler bridges a
fetch-handler app onto node:http, and serve runs it with Cloud Run's
PORT convention, streamed SSE bodies, and reverse-proxy URL reconstruction
from x-forwarded-proto and x-forwarded-host.
The canonical executable is chumbo and exposes setup, doctor, dev, and
skill subcommands. The package also retains supa-mcp as a transition alias.
MCP client
|
| 1. unauthenticated MCP request
v
Supabase Edge Function / MCP protected resource
|
| 2. 401 + WWW-Authenticate(resource_metadata=...)
v
Protected-resource metadata
|
| 3. authorization_servers -> Supabase Auth issuer
v
Supabase OAuth 2.1 Server
|
| 4. application login + consent + PKCE
| 5. Supabase user access/refresh tokens
v
MCP client
|
| 6. authenticated MCP request
v
Edge Function request context
|
| 7. request-scoped Supabase client carrying user JWT
v
PostgREST / Postgres RLS / Storage
The official MCP SDK owns:
- protocol versions and era negotiation;
server/discover, required MCP headers, JSON-RPC, and result encoding;- tool, resource, prompt, MRTR, and cache-hint wire behavior;
- stateless legacy compatibility supplied by
createMcpHandler; - OAuth helper types and protected-resource metadata response utilities.
Supabase owns:
- Edge routing and isolate execution;
- OAuth 2.1 authorization-server endpoints;
- user authentication, PKCE token issuance, refresh, and revocation;
- JWT signing and discovery;
- request-scoped Supabase Data API behavior and RLS enforcement.
chumbo owns:
- composing the web-standard MCP handler with Supabase request identity;
- protected-resource metadata and OAuth challenge configuration for the Edge Function URL;
- safe construction of request-scoped application context;
- ergonomic registration wrappers only where they remove Supabase-specific boilerplate;
- scaffolding, tests, diagnostics, and deployment/client instructions.
Application code owns:
- tool/resource/prompt definitions;
- product permissions beyond existing RLS;
- organization semantics;
- consent-screen branding and copy;
- any privileged server-side operation explicitly chosen by the builder.
Every MCP invocation must receive a new immutable context object. No current user, organization, token, Supabase client, or authorization decision may be stored in a mutable module global.
Proposed context:
export interface SupabaseMcpContext<Database = unknown> {
request: Request;
supabase: SupabaseClient<Database>;
user: {
id: string;
email?: string;
};
jwtClaims: Record<string, unknown>;
clientId?: string;
scopes: readonly string[];
hasScope(scope: string): boolean;
hasScopes(scopes: readonly string[]): boolean;
traceId?: string;
state?: SupabaseMcpState;
json(value: unknown): CallToolResult;
}Organization identity is intentionally not a universal built-in field. Apps model organizations differently. The package may accept a resolver:
context: {
async extend(base) {
return {
organizationIds: await membershipsFor(base.supabase, base.user.id),
};
},
}The resolver runs per request. It must not convert a user-supplied organization ID into authority without a database-backed membership check.
- MCP endpoint acts as an OAuth protected resource.
- Supabase Auth acts as the default authorization server.
- Missing or invalid access tokens return the correct Bearer challenge.
- Protected-resource metadata advertises the configured authorization issuer and supported scopes.
- Valid Supabase user tokens become request-scoped, RLS-aware Supabase clients.
- Access and refresh tokens remain managed by the MCP client and configured authorization server.
- An application-owned issuer may supply a verifier for opaque access tokens. Chumbo passes the canonical issuer and resource URL to that verifier; the verifier checks issuer, exact resource, expiry, and revocation. Chumbo checks the returned subject and expiry and gives handlers an anonymous Supabase client, leaving application permissions to capability code.
The adapter must use the official SDK's web-standard OAuth metadata helpers where possible. It must not copy protocol response shapes by hand when the SDK already supplies them.
- Accepts an existing Supabase user access token.
- Intended for automated tests and developer clients with custom headers.
- Preserves RLS exactly like OAuth access tokens.
- Does not pretend to provide interactive MCP authorization discovery.
- Accepts
Authorization: Bearer <application-key>without advertising OAuth. - The generated default loads one static key from the
MCP_API_KEYEdge Function secret. - The request context exposes a stable application principal as
ctx.subject, whilectx.userandctx.jwtClaimsremain null. - The default Supabase client remains anonymous. The library must not fabricate a Supabase user or imply user-RLS semantics for an application key.
- An advanced verifier may use a service-role client to resolve an existing application-owned key table and return subject, client ID, and scopes. The privileged client is confined to verification and never exposed to tools.
- The package prescribes neither a key-table schema nor organization roles.
- No user identity.
- Only appropriate for genuinely public capabilities.
- Generated configuration and documentation must make the absence of RLS user context explicit.
- The initializer requires explicit selection; it is never the production default.
- New public scaffolds install a private fixed-window Postgres limiter and enable it in generated configuration. Existing authenticated scaffolds do not receive a migration or additional setup.
Scopes are an optional capability boundary above authentication and below RLS.
Unscoped registration remains unchanged. Advanced builders can register a
tool, resource, prompt, or resource template through
server.withScopes([...]); capabilities missing any required scope are not
advertised or callable for that request.
Token scopes are the initial value. An optional per-request resolver can replace them with application-owned grants loaded through the appropriate request-scoped authority. The package does not prescribe scope names, roles, organizations, or a grant-table schema. Supabase OAuth's standard identity scopes must not be presented as arbitrary application permissions.
The default handler context does not expose supabaseAdmin. A builder who needs
an internal operation must opt into a separately named privileged capability and
supply an authorization predicate. This prevents an attractive nuisance in an
end-user-facing starter.
Authenticated servers may explicitly configure a bounded namespace allowlist
and deployment-secret HMAC key. The runtime derives an opaque partition from
authentication mode, exact strategy, and the exact presented credential.
Capability code receives only get, revision-checked put, and
revision-checked delete; it never receives the partition, credential, HMAC
key, or service-role client.
The opt-in SQL stores JSON values in a private RLS-enabled table and exposes
three security invoker RPCs granted only to service_role. Runtime and SQL
both bound namespaces, keys, encoded values, revisions, and TTL. Raw JSON text
is measured before database parsing, read text is measured before runtime
parsing, expiry is deterministic missing state, and writes are atomic CAS.
Each write opportunistically reclaims at most 16 expired rows using the expiry
index and FOR UPDATE SKIP LOCKED; cleanup failure rolls back with the write.
Credential rotation deliberately creates a new partition. That false negative is safer than correlating or accidentally sharing state across credentials. Applications remain responsible for domain concurrency and authorization; durable state does not weaken RLS, Files-style version CAS, or another data-plane precondition.
State revision and resource version are separate concurrency domains. State CAS protects only the receipt row. A guarded mutation must first validate the receipt, then atomically compare the observed resource version while changing the application row, and only after success advance or invalidate the receipt. If those stores are separate and receipt advancement fails after the domain write, require a reread; never advance a receipt before the domain write.
Per-record bounds do not create a global storage quota. Capabilities must derive keys from real, authorized, immutable resource identities and maintain a bounded live keyspace rather than mapping arbitrary caller input directly into object keys. The living observation-before-action pattern is the executable reference for these requirements.
Run identity is an optional application-owned boundary above invocation traces
and separate from authentication, MCP transport sessions, and Durable actor
identity. Chumbo never infers a run from a credential, connection, IP address,
traceparent, or timing proximity. A caller that supplies no handle retains
invocation-only behavior.
createRunCorrelation mints and verifies a canonical HMAC-SHA256 handle scoped
to one installation, MCP surface, and builder-authorized principal, account, or
tenant partition. Configuration contains one current deployment secret and may
contain one previous key with an explicit ISO 8601 verification-overlap end.
The codec bounds key material, identifiers, token size, default and maximum
TTL, clock skew, and canonical encoding. The verified fact exposes only an
opaque digest plus start and expiry times; credentials, application object IDs,
tool inputs, prompts, outputs, and signing material are absent.
Controlled clients carry the handle in _meta["dev.chumbo/run"]. Generic MCP
clients may pass the same handle through run_id only when a builder chooses to
add that field to a particular tool. A builder-authored domain begin tool may
call mint; Chumbo does not add a universal workflow API or change every tool
schema. Matching dual carriers are accepted. Conflicting, malformed, expired,
cross-scope, or unknown-key handles fail before capability code executes.
The runtime and builder capability code share one request-cached verification
result through the official MCP ServerContext. Configured lifecycle sinks
receive schema v2 with the bounded run fact or run: null. Unconfigured
servers retain schema v1 byte for byte.
The signed handle supplies stateless correlation, not persistence, authorization, worker execution, early close, revocation, or per-account run quotas. Applications add Supabase or Durable storage only when those behaviors are real product requirements.
Applications may opt into onSurface to capture what a real caller can
actually discover. The runtime derives one versioned proof only after a
complete successful tools/list; authentication rejection, protocol errors,
non-discovery requests, malformed or oversized results, and paginated partial
results emit nothing. Scope-filtered tools remain absent exactly as they are in
the caller's response.
The proof normalizes and bounds the returned tool catalog, explicitly bounds its server and authentication metadata, caps the complete serialized envelope, strips arbitrary protocol metadata, and includes a stable SHA-256 digest over canonical tool content. It may identify the server, Chumbo runtime, requested protocol version when available, and effective authentication strategy. It never contains the principal, effective scopes, credential, headers, request ID, arguments, results, prompts, errors, or pagination cursors. The builder owns persistence and delivery; Chumbo adds no account, network, or hosted-service dependency. An unconfigured runtime does no proof inspection or hashing.
The highest-priority technical uncertainty was how the Supabase gateway routes path-aware RFC 9728 protected-resource metadata for an MCP resource located at:
https://<project-ref>.supabase.co/functions/v1/mcp
The official MCP SDK can return web-standard metadata responses and can point a
WWW-Authenticate challenge at an arbitrary absolute resource_metadata URL.
The deployed spike proved this function-local public URL:
https://<project-ref>.supabase.co/functions/v1/mcp/.well-known/oauth-protected-resource
Supabase's hosted gateway invokes the correct function for that suffix but
rewrites the URL seen by Edge Runtime from
/functions/v1/mcp/.well-known/... to /mcp/.well-known/.... The runtime now
matches both the canonical public path and the stable well-known suffix. A live
deployment returned the advertised challenge and reached the metadata handler;
it currently returns the explicit upstream-unavailable response because OAuth
Server is still disabled on the prototype project.
The remaining release verification is interoperability with:
- the official MCP TypeScript client;
- MCP Inspector;
- at least one independent remote MCP host with interactive OAuth.
This is a release blocker because it is the difference between a manually header-configured server and an end-user-connectable server.
Supabase OAuth Server redirects authorization requests to an application-owned authorization path. The builder must therefore provide a page that:
- authenticates or recognizes the existing application user;
- displays the requesting client and requested scopes;
- allows approval or denial;
- completes the Supabase OAuth authorization request.
Version 0.1.0 provides an optional minimal, unbranded consent Edge Function
that calls Supabase's getAuthorizationDetails, approveAuthorization, and
denyAuthorization APIs. It is a runnable fallback and a readable integration
reference, not a new consent framework. Existing applications should normally
move those three calls into their signed-in frontend.
The initializer should not modify a framework-specific web application without an explicit flag. A later release may add Next.js, React Router, SvelteKit, or other adapters based on demand.
collectionInputSchema supplies default limit 20, maximum 100 and a bounded
string cursor, with optional application cursor validation and configurable
smaller limits. Builders extend the schema with their query filters.
collectionOutputSchema(itemSchema) describes {items, has_more, next_cursor, next_call} for structured or hybrid modes. Terminal continuation is null.
collectionResult takes a bounded source window, a required compact projection
and item schema, stable per-row cursor function, explicit tool name and safe
filter arguments. It validates projected JSON and chooses the largest prefix
within the caller's count limit and configured UTF-8 JSON-encoded result budget
(default 16 KiB, configurable 512 bytes to 1 MiB). The budget includes all MCP
content, structured data, exact continuation and namespaced budget metadata;
transport envelopes and downstream host limits remain outside this contract.
A terminal page is tested before partial pages because removing continuation
can reduce its size. Source windows are limited to maxLimit + 1 records.
The builder fetches limit + 1 ordered rows to detect continuation, or explicitly reports more rows beyond the supplied window with hasMore. Byte-limited pages resume strictly after the last returned row, never after the source window. An empty window cannot claim hasMore. Duplicate/nonadvancing candidate cursors are rejected. An oversized first item or continuation returns a bounded recoverable MCP error; no item is silently skipped. Builder-supplied recovery can point to an authorized detail tool or Resource. Existing results and tool output schemas are unchanged until deliberately migrated.
Text mode is the default and requires purpose-written rendering; structured mode has no manufactured text; hybrid deliberately supplies both and validates the same page. Text/hybrid carry explicit has_more, next_cursor and exact next-call instructions; structured mode carries the same facts as data. Middleware may add contextual guidance but cannot exceed the complete marked collection budget, change the page, or infer source-query pagination.
The source query, stable total order, meaning and validation of cursors, projection semantics, detail/Resource capabilities and every authorization check remain application-owned. There is no total-count query, cursor store, snapshot manager, implicit database query or automatic slicing of arbitrary JSON. Live keyset reads can observe inserts and updates; applications needing snapshot consistency must bind their own snapshot/version and filters to the cursor. A cursor grants no authority; every page rechecks the current caller. The docs reference uses immutable unique slug order, query/kind-bound cursors and compact URI summaries; the result showcase demonstrates middleware-guided page navigation.
Builders may add bounded MCP content before or after a successful authored
tool result without reconstructing it. Composition preserves the original
content ordering, structuredContent, _meta, Resources, and unknown valid
top-level fields. The package validates and bounds additions. For collection results, it also
enforces the complete result budget carried by the helper.
Optional resultMiddleware runs in declaration order for successful tool
results registered through the ordinary official MCP API, including scoped
registrations. Every middleware receives the same immutable snapshot of the
authored result plus the request context and tool name. Middleware can return
only additive prepend or append blocks. It does not run for tool errors,
input-required results, or thrown handler failures. Invalid additions and
middleware failures leave the last valid composition intact and reach the
application error hook without changing the tool outcome.
Added result text is model-facing content. It is not a system message, cannot force another tool call, and does not create a workflow engine.
- tools: registration, list, call, structured results, protocol errors;
- resources: static resources, templates, list/read, cache hints;
- prompts: registration, list/get;
- MRTR: one input-required/elicitation example using the official SDK;
- modern discovery and stateless requests;
- official SDK's stateless legacy compatibility unless tests show it harms the Edge deployment or auth flow.
io.modelcontextprotocol/tasksadapter backed by a Postgres table and a Supabase Queue/worker;subscriptions/listenintegration;- client-specific connection helpers;
- typed database generation integration;
- ChatGPT Apps UI resources.
The library must not re-expose every MCP method with a parallel API. Application
authors can access the underlying official McpServer when they need an
advanced capability.
Errors must remain useful at three layers:
- HTTP/OAuth errors for clients and authorization discovery;
- MCP protocol errors handled by the official SDK;
- application tool errors readable by the model and end user.
Defaults:
- missing/invalid token:
401plus standards-shaped Bearer challenge; - insufficient scopes:
403withinsufficient_scope; - invalid arguments: ordinary MCP tool error with concise correction guidance;
- RLS empty result: not automatically treated as an authorization error;
- database or Storage failure: sanitized tool error plus server-side trace ID;
- unknown exception: no secret, SQL text, JWT, or service key in the response.
The package should provide a narrow error hook for Sentry or another logger but must not prescribe an observability vendor.
RLS separates database rows, not Edge compute or in-memory state. The package must document and test these rules:
- create Supabase clients per request;
- never store the current user or organization in module globals;
- module-level caches must use verified tenant and version keys;
- cached application data is outside RLS once loaded into memory;
- service-role clients bypass RLS and are never the default user context;
- views must be security-invoker or otherwise explicitly governed;
- OAuth
client_idand application claims may refine RLS, but user-editable metadata must not become authorization authority.
The example app must contain a negative cross-tenant test, not just two positive reads.
- deterministic CLI plans;
- dry-run produces no writes;
- conflict handling and idempotent re-run;
- request-context isolation under concurrent requests;
- OAuth metadata and challenge generation;
- invalid, expired, and insufficient-scope tokens;
- no privileged client in default context;
- no credential, HMAC key, state partition, or privileged state client in the handler context, errors, or responses;
- state isolation across users, rotated credentials, API keys, namespaces, and keys; atomic CAS/delete races; TTL and encoded-size boundaries;
- result helpers preserve MCP content and structured content;
- error redaction.
- Deno type-check;
- local Edge Function boot;
server/discoverfor modern clients;- stateless legacy connection when enabled;
- tools list/call;
- resources list/read;
- prompts list/get;
- one MRTR retry;
- malformed header/body mismatch handled by the official SDK;
- two independent requests require no shared session.
Seed:
- Alice, Bob;
- organization A, organization B;
- separate memberships and private rows;
- RLS policies used by the example MCP capability.
Assertions:
- Alice cannot read or mutate B;
- Bob cannot read or mutate A;
- concurrent requests retain the correct user context;
- a privileged test path is isolated from the public runtime API.
Use a dedicated test Supabase project. Verify:
- OAuth authorization-server discovery;
- protected-resource discovery through the MCP challenge;
- PKCE authorization and consent;
- access-token call;
- refresh-token call;
- revoke/expire behavior;
- real RLS results;
- a real remote MCP client connection.
Cloud tests that create OAuth clients or users must use dedicated fixtures and clean up only their own identified records.
Use the official MCP conformance suite where it applies. Do not claim protocol compliance solely because the official SDK is a dependency: the adapter's HTTP routing, auth middleware ordering, response handling, and Edge gateway can still break compliant behavior.
As of 2026-08-11:
- 17 unit/protocol tests pass, including modern discovery, tools, resources, prompts, MRTR retry, legacy stateless serving, malformed modern requests, OAuth challenges, token redaction, gateway-rewritten metadata paths, doctor, and concurrent identity isolation;
- generated MCP and consent functions pass Deno typechecking, and the generated MCP test passes;
- a real local Supabase Alice/Bob integration passes positive and negative RLS assertions concurrently;
- the exact npm tarball installs and answers MCP from an empty consumer project;
- the deployed cloud function returns the correct challenge and passes
authenticated
tools/listpluswhoamiwith a real Supabase user JWT; - the disposable cloud user was removed after the smoke test.
Interactive PKCE, refresh, revocation, independent-client compatibility, public npm installation, and release publication remain open release gates.
Initial pinned dependencies observed on 2026-08-11:
@modelcontextprotocol/server@2.0.0;@supabase/server@1.4.1(public beta under SemVer);- Zod 4 compatible with the MCP SDK's Standard Schema support;
- a pinned Supabase CLI version in integration CI.
Implementation must re-check current versions and changelogs before installing. The repository commits all lockfiles.
Version policy:
- package patch: bug fixes and compatible upstream bumps;
- package minor: new adapters, auth modes, CLI features, or generated optional files;
- package major: generated-code contract or public runtime API breaks;
- upstream MCP protocol changes are absorbed through the official SDK whenever possible;
- generated projects pin a known-good package version rather than
latest.
The initializer is operating inside someone else's application repository. It must:
- inspect before writing;
- use an explicit file plan;
- preserve unrelated
config.tomlcontent and formatting where practical; - never delete application files; a skill update may remove only an obsolete file it owns whose hash still matches its managed manifest;
- never overwrite a non-generated capability file without confirmation;
- mark generated files clearly without making them unpleasant to edit;
- create a recoverable backup or patch preview for modifications to existing files;
- return non-zero on partial failure and list exactly what changed;
- never print secrets;
- offer an idempotent
initre-run, a repository-derivedsetup --resume, and read-onlystatusanddoctorcommands; - emit JSON only on stdout in machine mode and keep subprocess output captured;
- return stable statuses and next commands without serializing tokens.
The skill installer additionally preserves every byte of AGENTS.md outside
its marked block, writes its manifest last, refuses malformed or path-escaping
manifests, and does not overwrite locally edited managed files.
The public README must get a builder from an existing repository to a local tool call quickly, then clearly separate the production OAuth path.
Required documentation:
- five-minute local quickstart;
- production end-user OAuth setup;
- writing tools with request-scoped
ctx.supabase; - resources and prompts;
- RLS and multi-tenancy model;
- consent-page integration;
- deploy and connect;
- client compatibility matrix based on real tests;
- troubleshooting by observable symptom;
- architecture and extension boundaries;
- package version and upstream compatibility policy;
- migration notes for every release that changes generated code.
Do not lead the README with protocol history. Lead with the outcome and the working commands.
Version 0.1.0 requires:
- public GitHub repository;
- MIT license;
- npm package with provenance where practical;
- exact dependency lockfile;
- GitHub Actions for type-check, unit tests, generated-project test, and build;
- one reproducible cloud smoke workflow with secrets documented but not stored;
- tagged GitHub release and changelog;
- example Supabase project;
- verified quickstart from a clean checkout;
- no credentials, project secrets, access tokens, or real user data in history.
- Create repository/package/CI skeleton.
- Pin official MCP and Supabase server dependencies.
- Serve one tool through
createMcpHandlerin a Supabase Edge Function. - Verify modern and stateless legacy behavior through official clients.
- Record the exact Edge routing behavior.
Exit: fresh checkout deploys a public test tool without handwritten MCP wire code.
- Serve protected-resource metadata.
- Compose Supabase OAuth issuer metadata.
- Return correct OAuth challenges.
- Validate Supabase OAuth JWTs.
- Construct request-scoped RLS-aware clients.
- Prove two-user isolation locally.
Exit: automated bearer-token integration passes with separate Alice/Bob rows.
- Add minimal consent UI/example.
- Enable and configure Supabase OAuth Server in a dedicated cloud project.
- Connect official MCP client and one independent real host.
- Verify PKCE, refresh, revocation, and RLS.
Exit: the complete end-user definition of done passes.
- Implement guided
setup, deterministicinit, read-onlystatus, auth-awaredoctor, and minimaldevdelegation. - Generate capability and test files.
- Add conflict-safe mutation and dry run.
- Run human and machine setup paths against clean and existing Supabase fixtures.
Exit: a new builder reaches the proven runtime without copying repository code.
- Add resource, prompt, and MRTR examples.
- Run conformance and deployment regression corpus.
- Tighten README and troubleshooting.
- Publish
0.1.0and verify installation from npm rather than a workspace.
Exit: public release artifacts and clean-install verification pass.
Risk: endpoint behavior or setup may change.
Mitigation: isolate OAuth composition, pin tested versions, run cloud smoke, and
state the beta dependency in the README.
Risk: hosts and SDKs will adopt it unevenly.
Mitigation: use the official TypeScript SDK's dual-era handler, test real hosts,
and publish a dated compatibility matrix.
Risk: root well-known URL assumptions may not match Supabase gateway routing.
Mitigation: make the protected-resource URL explicit in the challenge and prove
it through live clients before designing the public API around it.
Risk: every builder uses a different frontend stack.
Mitigation: provide a framework-neutral flow and minimal examples, while keeping
framework adapters outside the core release gate.
Risk: tools, tasks, UI, queues, schemas, and database generation expand the
project beyond its useful seam.
Mitigation: measure every feature against the one-command OAuth/RLS outcome and
expose the official MCP server for advanced use rather than cloning its API.
Risk: examples using service-role clients are copied into production.
Mitigation: omit admin access from default context, include negative tenant
tests, and make privileged operations an explicitly named opt-in.
Resolved:
- Use the function-local protected-resource URL and normalize Supabase's hosted
/functions/v1rewrite inside the runtime. - Use
verifyCredentialsandcreateContextClientfrom@supabase/server/core; do not duplicate JWT parsing. - Serve modern MCP and official stateless legacy compatibility by default.
- Host the optional minimal consent UI as a second Supabase Edge Function.
- Keep one combined runtime/CLI package; the packed artifact is 27 KB and its clean-consumer proof passes.
- Use the unscoped
chumboname and position it explicitly as an end-user application MCP, not the Supabase management MCP.
Still to verify before release:
- Which independent MCP hosts complete dynamic registration and PKCE against the enabled Supabase OAuth Server without pre-registration?
- Does refresh and revocation behave correctly through those hosts?
Observed as of 2026-08-11:
- Supabase Edge Functions are TypeScript/Deno functions and support npm imports.
- Supabase Auth can act as an OAuth 2.1/OIDC provider for MCP, with PKCE, authorization-server discovery, refresh tokens, and Supabase JWTs governed by existing RLS.
- Supabase OAuth Server is public beta.
- MCP
2026-07-28removes the required handshake/session model and introduces stateless per-request metadata andserver/discover. @modelcontextprotocol/server@2.0.0supports Deno and providescreateMcpHandler, which can serve modern and stateless legacy requests.- The official SDK provides web-standard OAuth protected-resource metadata helpers; the Supabase adapter must still prove routing under the Edge Function URL.
@supabase/server@1.4.1provides stateless header-based auth and request- scoped Supabase client context and is itself a public-beta package under SemVer.- Supabase's current MCP Edge Function example uses
mcp-liteand a public sum tool; it does not provide the complete end-user OAuth/RLS application path.
Primary references:
- Supabase MCP authentication: https://supabase.com/docs/guides/auth/oauth-server/mcp-authentication
- Supabase OAuth Server: https://supabase.com/docs/guides/auth/oauth-server
- Supabase Edge Function authentication: https://supabase.com/docs/guides/functions/auth
- Supabase MCP Edge example: https://supabase.com/docs/guides/functions/examples/mcp-server-mcp-lite
- Official MCP TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
- Official SDK protocol versions: https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/protocol-versions.md
- Official SDK authorization guide: https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/serving/authorization.md
- MCP
2026-07-28release: https://blog.modelcontextprotocol.io/posts/2026-07-28/ - Supabase server utilities: https://github.com/supabase/server
- Re-read this specification and preserve its product boundary.
- Re-check npm versions and Supabase/MCP changelogs; both key packages are newly released.
- Create the package skeleton and lock dependencies.
- Implement Plateau 1 only: official
createMcpHandler, one tool, one deployed Edge Function, automated modern/legacy clients. - Inspect actual HTTP requests and responses, especially route paths and protocol headers.
- Move immediately to the protected-resource metadata routing spike; do not spend time polishing a public tool registry first.
- Record observed behavior and update the open questions before expanding the CLI surface.
The repository succeeds when it removes the Supabase-specific distance between "I have an application with users and RLS" and "my users can connect their AI client to it through MCP."
The hard and valuable part is not tool registration. It is making OAuth discovery, Edge routing, request identity, RLS, generated configuration, and real-client verification feel like one coherent Supabase feature.