elsheppo

Chumbo

Community elsheppo
Updated

Add an end-user MCP server to a Supabase app, with OAuth and RLS already wired.

Chumbo – MCP made easy on Supabase.

Chumbo

MCP made easy on Supabase.

Chumbo turns an existing Supabase application into a Streamable HTTP MCP serverrunning as a Supabase Edge Function. Your application keeps its Auth, Postgresdata, Row Level Security, Storage, and authorization model. MCP becomes anotherinterface to the product you already built.

MCP client
    ↓
Supabase Edge Function
    ↓
request-scoped identity and Supabase client
    ↓
your capabilities, Postgres data, and RLS policies

Start in one command

From a repository that already contains supabase/config.toml:

npx chumbo setup

Setup asks who may connect, previews every file it will write, generates theEdge Function and tests, and reports the remaining deployment or OAuth steps inorder. It is resumable and does not overwrite application-authoredcapabilities.

Requirements: Node 22+, the Supabase CLI, and preferably Deno for the generatedlocal type-check and tests.

The generated server lives at:

supabase/functions/mcp/
├── index.ts
├── capabilities.ts
├── deno.json
├── index_test.ts
└── README.md

Write one capability

Edit the generated capabilities.ts. Chumbo uses the official MCP SDK'sregistration API, so your capabilities remain ordinary MCP tools, Resources,and prompts.

import {
  textResult,
  type SupabaseMcpContext,
  type SupabaseMcpServer,
} from "chumbo";
import { z } from "zod";

export function registerCapabilities(
  server: SupabaseMcpServer,
  ctx: SupabaseMcpContext,
) {
  server.registerTool(
    "list_tasks",
    {
      description: "List tasks visible to the connected user.",
      inputSchema: z.object({}),
    },
    async () => {
      const { data, error } = await ctx.supabase
        .from("tasks")
        .select("id, title, status")
        .order("title");

      if (error) throw error;
      if (!data?.length) {
        return textResult("No tasks are visible to the connected user.");
      }

      return textResult(
        [
          `## Tasks – ${data.length}`,
          ...data.map(
            (task) => `- **${task.title}** – ${task.status} · ID: ${task.id}`,
          ),
        ].join("\n"),
      );
    },
  );
}

The important part is ctx.supabase. In OAuth and bearer modes, it is a freshclient carrying the connected user's access token, so the same Postgres grantsand RLS policies used by the rest of the application apply to every tool call.

You choose the application operations worth exposing and shape each result forits real consumer. Chumbo handles the MCP and request-authority boundary aroundthat application code.

Run, deploy, and verify

Run the generated checks and exercise MCP discovery locally:

supabase functions serve mcp
deno task --config supabase/functions/mcp/deno.json test
npx chumbo doctor --url http://127.0.0.1:54321/functions/v1/mcp

Then deploy and probe the hosted endpoint:

supabase functions deploy mcp --no-verify-jwt

npx chumbo doctor \
  --url https://PROJECT_REF.supabase.co/functions/v1/mcp

The generated function sets verify_jwt = false at the Supabase gateway so thefunction can issue the MCP OAuth challenge itself. Protected servers stillauthenticate the request inside the Chumbo runtime.

Your MCP URL is:

https://PROJECT_REF.supabase.co/functions/v1/mcp

Choose who can connect

Access mode Use it when Request authority
OAuth Your users should connect their own accounts. Recommended for a user-facing product. Supabase user token and existing RLS
API key You want the shortest authenticated start or already maintain application keys. Application-verified subject and scopes
Bearer Your own client already holds a Supabase user access token. Supabase user token and existing RLS
Public The capability is intentionally anonymous. Supabase anon role plus a generated Postgres rate-limit guardrail

Run npx chumbo setup interactively, or choose directly:

npx chumbo setup --auth oauth
npx chumbo setup --auth api-key
npx chumbo setup --auth bearer
npx chumbo setup --auth public

Start with OAuth for an end-user product and API key for a prototype or trustedmachine caller. One endpoint can also compose Supabase-user and application-keystrategies without merging their identities or database behavior.

Choose an access mode explains the tradeoffs andDifferent capability surfaces showsordinary and privileged identities receiving different MCP surfaces from oneEdge Function.

Connect a real client

For Claude Code:

claude mcp add --transport http my-app \
  https://PROJECT_REF.supabase.co/functions/v1/mcp

OAuth mode opens the application's sign-in and consent flow. API-key and bearerclients send their credential as an Authorization: Bearer header.

For claude.ai or Claude Desktop, open Settings → Connectors → Add customconnector and paste the endpoint URL. Hosted custom connectors require OAuthwith dynamic client registration enabled.

Cursor, MCP Inspector, and other Streamable HTTP clients use the same endpoint.See Connect your MCP client for exact setupand verified combinations.

What Chumbo handles

  • Supabase-native authority. Auth, RLS, Postgres, Storage, and EdgeFunctions remain authoritative.
  • Request isolation. Every request receives a new MCP server, normalizedprincipal, and Supabase client. Caller identity never lives in shared mutablemodule state.
  • Deliberate authentication. Supabase users receive an RLS-aware client.Application keys retain their application-owned subject and scopes.
  • Rotation-safe verification. OAuth and bearer requests use Supabase'spublic JWKS. Remote JWKS configuration is cached briefly per runtime to avoidadding a key-network round trip to every MCP request while still observingsigning-key rotation quickly.
  • Explicit result contracts. Agent-facing text, typed data, hybrids, andlarge Resources are separate choices rather than automatic duplicated output.
  • Protocol-native capabilities. Tools, Resources, prompts, instructions,and multi-round-trip flows use the official MCP SDK surface.
  • Deployable defaults. Setup is previewable, resumable, conflict-aware, andusable non-interactively by agents and CI. doctor verifies the real remoteMCP boundary.
  • No required Chumbo service. The runtime deploys into an ordinary Supabaseproject. Public mode's default guardrail is Postgres-backed.

Choose the result for its consumer

Helper Use it for
textResult(text) Purpose-written output for agents and people
structuredResult(value) Typed clients or UI consumers; declare the matching tool outputSchema
renderResult(value, render) A deliberate text and structured-data hybrid
resourceResult(text, link) A concise reading card whose full body is served through MCP Resources
errorResult(message, nextStep?) A failure that tells the agent how to recover

Shape each result around the consumer's next reasoning or interaction step.Preserve useful identifiers, omit internal fields, and use Resources orpagination for large payloads.

Model-facing results contains executableexamples of all result patterns.

Opt into small durable state

Most Chumbo servers should remain stateless. An authenticated capability thatgenuinely needs request-to-request coordination can explicitly generate oneallowlisted namespace:

npx chumbo setup \
  --auth oauth \
  --state-namespace file-ide.observations

This adds one opt-in migration and state configuration. Apply the migration andset a unique deployment secret of at least 32 random bytes:

supabase db push
supabase secrets set \
  CHUMBO_STATE_HMAC_KEY="replace-with-at-least-32-random-bytes"

Capability code then receives only get, revision-checked put, andrevision-checked delete:

const receipt = await ctx.state?.get(
  "file-ide.observations",
  `project:${projectId}:document:${documentId}`,
);

The runtime derives an opaque partition from the exact credential with adeployment-secret HMAC and keeps its service-role state client closure-confined.Public mode never receives state. Same-project storage is the default; advancedcompositions can set state.supabase.env to keep receipts in a separateSupabase project without moving authentication or ctx.supabase there.

State CAS protects coordination records, not application rows. Use immutable,scoped resource IDs, keep the capability's total keyspace bounded, and retainRLS or an atomic application-level version precondition for real mutations.

See Observation before action forthe complete executable read-before-edit pattern, safe cross-database ordering,credential-rotation behavior, and split-project runbook. This is coordinationstorage – not a resident actor or Durable Object runtime.

Optional depth when the application needs it

The ordinary path remains one Edge Function with builder-authored capabilities.The same library also supports more demanding applications without changingthat starting point:

  • Many MCPs from one function
  • Authenticated tools with RLS
  • Observation before action
  • Different capability surfaces
  • Interactive MCP Apps on Supabase
  • Clean client-facing URLs
  • Project-local capability guidance with npx chumbo skill install

These are composition patterns, not additional frameworks or required productarchitecture.

Reference project

This repository includes an open-source Supabase reference project. Itspatterns run through the real MCP transport against local Postgres. The suitecovers two-user RLS isolation, explicit result contracts, many row-defined MCPsurfaces, composed user and application identities, and interactive MCP Apps.

The public documentation MCP is available at:

https://dxrpeagddrpbezbkgvdv.supabase.co/functions/v1/docs-mcp

Its tools search Chumbo's own guides and return complete documents throughMCP Resources. It links to official Supabase documentation for the platformunderneath instead of reproducing it.

To rebuild the reference project from a clean clone:

pnpm install --frozen-lockfile
pnpm reference:check

Documentation

  • Five-step getting started guide
  • Choose an access mode
  • Connect an MCP client
  • Give an MCP a clean product URL
  • Runnable patterns
  • Examples
  • Architecture and protocol contract
  • Roadmap
  • Changelog

For automation, use npx chumbo setup --plan --json to inspect changes and--yes --json to apply them without prompts. Run npx chumbo --help for thecomplete command reference.

Development

pnpm install --frozen-lockfile
pnpm check
pnpm format:check
pnpm reference:check
npm pack --dry-run

Released under the MIT License.

MCP Server · Populars

MCP Server · New

    punkpeye

    mcp-remote

    Connect an MCP Client that only supports local (stdio) servers to a Remote MCP Server.

    Community punkpeye
    HiAi-gg

    DocsMint

    Self-hosted AI-native knowledge workspace and installable PWA with hybrid search, GraphRAG, REST, SDK, CLI, and MCP access for people and AI agents.

    Community HiAi-gg
    WYRE-AI

    ConnectWise Manage MCP Server

    MCP server for ConnectWise Manage (PSA) — tickets, companies, contacts, projects, and time entry tools for AI assistants

    Community WYRE-AI
    WYRE-AI

    NinjaOne MCP Server

    MCP server for NinjaOne — device monitoring, patching, scripting, and alert management tools for AI assistants

    Community WYRE-AI
    QVerisAI

    @qverisai/mcp

    Open-source toolkit for the QVeris capability routing network: CLI, MCP server, Python SDK, skills, and REST API docs for agents to discover, inspect, call, and audit real-world tools.

    Community QVerisAI