1. Create the Supabase project

Sign in at supabase.com, click New Project, pick an organization, and choose a region close to you. Region matters less than you'd think for a low-traffic MCP server, but pick the same region you'd put the rest of your stack in for future compatibility.

Wait for the project to provision (about 90 seconds). On the project page, copy down:

  • Project URL: https://<ref>.supabase.co (under Settings → API)
  • Project ref: the <ref> part of the URL — you'll use it with the CLI
  • anon public key: under Settings → API → Project API keys

You don't need the service role key for this blueprint. Resist the temptation. We'll be running every database query as the calling user so RLS does its job.

2. Install the Supabase CLI

# macOS
brew install supabase/tap/supabase
 
# Or via npm
npm install -g supabase

Verify:

supabase --version
# Expect: 1.x or 2.x

3. Scaffold the repo

mkdir shared-skills-mcp && cd shared-skills-mcp
git init
supabase init

supabase init creates a supabase/ directory with config files and migrations. Add a .gitignore:

cat > .gitignore <<'EOF'
.env
.env.local
node_modules/
.supabase/
EOF
supabase login                              # opens browser, authenticates the CLI
supabase link --project-ref <your-project-ref>

You're now able to push migrations to the cloud project and deploy functions to it.

5. Create the Edge Function

supabase functions new mcp

That creates supabase/functions/mcp/index.ts with a tiny Deno starter. We'll replace it shortly.

6. Install the MCP SDK + Hono via an import map

Edge Functions run on Deno, so dependencies come from URLs or NPM-via-npm: specifiers. Create supabase/functions/import_map.json:

{
  "imports": {
    "@modelcontextprotocol/sdk/": "npm:@modelcontextprotocol/sdk@^1.0.0/",
    "hono": "npm:hono@^4.6.0",
    "hono/": "npm:hono@^4.6.0/",
    "@supabase/supabase-js": "npm:@supabase/supabase-js@^2.45.0",
    "jose": "npm:jose@^5.9.0",
    "zod": "npm:zod@^3.23.0"
  }
}

jose will verify Supabase JWTs in step 6. zod will validate tool inputs. The MCP SDK ships its TypeScript types via the NPM package.

7. Replace the function stub with a Hello-MCP

Open supabase/functions/mcp/index.ts and replace it with:

import { Hono } from "hono";
import { logger } from "hono/logger";
 
const app = new Hono();
app.use("*", logger());
 
app.get("/health", (c) => c.json({ ok: true, ts: Date.now() }));
 
app.all("*", (c) => c.json({ message: "MCP server placeholder" }));
 
Deno.serve(app.fetch);

Tell the CLI to use the import map by adding to supabase/config.toml:

[functions.mcp]
import_map = "./functions/import_map.json"
verify_jwt = false        # we'll handle auth ourselves; Supabase's default JWT
                          # verification doesn't understand OAuth 2.1 tokens yet

8. Serve locally and verify

supabase functions serve mcp

In another terminal:

curl http://127.0.0.1:54321/functions/v1/mcp/health
# {"ok":true,"ts":1715774400000}

That confirms Deno + Hono + the import map all work together. If you see a "module not found" error, double-check the path in config.toml — it's relative to the supabase/ directory.

9. Local environment variables

Create supabase/functions/.env (this file is gitignored by Supabase's defaults):

SUPABASE_URL=https://<your-ref>.supabase.co
SUPABASE_ANON_KEY=<your-anon-public-key>

These get auto-loaded by supabase functions serve. We'll use them in step 7 to instantiate Supabase clients in request scope.

10. Smoke test: deploy the placeholder

supabase functions deploy mcp

Look for the line: Deployed Function mcp on project <ref>. Hit it:

curl https://<ref>.supabase.co/functions/v1/mcp/health
# {"ok":true,"ts":...}

You now have:

  • A linked Supabase project
  • An Edge Function that builds, deploys, and responds
  • The MCP SDK, Hono, jose, zod, and supabase-js available via the import map

That's the boring foundation. Step 3 adds the database schema for workspaces, members, and snippets — with the RLS policies that will do most of our security work.