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 anonpublic 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 supabaseVerify:
supabase --version
# Expect: 1.x or 2.x3. Scaffold the repo
mkdir shared-skills-mcp && cd shared-skills-mcp
git init
supabase initsupabase init creates a supabase/ directory with config files and migrations. Add a .gitignore:
cat > .gitignore <<'EOF'
.env
.env.local
node_modules/
.supabase/
EOF4. Link the local repo to your cloud project
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 mcpThat 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 yet8. Serve locally and verify
supabase functions serve mcpIn 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 mcpLook 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.