Skip to main content
This guide demonstrates how to deploy an MCP server on Supabase Edge Functions, which runs on Deno and provides a serverless environment for your MCP applications.

Prerequisites

  1. Supabase CLI: Install from Supabase CLI Installation Guide
  2. Supabase Account
  3. Docker Required to solve bug https://github.com/orgs/supabase/discussions/32815
  4. Node.js/Bun: For building your MCP server

Create your mcp-use project

npx create-mcp-use-app your-project-name
This will create a new directory called your-project-name with a basic MCP server setup.

Install dependencies

cd your-project-name
yarn

Init supabase project

supabase init
This will create a new directory called supabase with a basic Supabase project setup.

Quick Deployment

Deploy your MCP server to Supabase in one command using our automated deployment script:
# Download and run the deployment script (interactive mode)
curl -fsSL https://url.mcp-use.com/supabase | bash
Or download the script first to review it:
# Download the script
curl -fsSL https://url.mcp-use.com/supabase -o deploy.sh
chmod +x deploy.sh

# Run with your project ID
./deploy.sh YOUR_PROJECT_ID

# Optional: specify custom function name and bucket name
./deploy.sh YOUR_PROJECT_ID my-function-name my-bucket-name
Done! You can now inspect your MCP server at https://inspector.mcp-use.com

What the script does

The deployment script automatically:
  1. ✅ Validates Supabase CLI installation and authentication
  2. ✅ Checks if the project is initialized and linked
  3. ✅ Patches config.toml with your project ID
  4. ✅ Builds your MCP application with the correct MCP_URL and MCP_SERVER_URL
  5. ✅ Copies build artifacts to the function directory
  6. ✅ Sets MCP_URL and CSP_URLS (Supabase project URL) environment variables
  7. ✅ Deploys the function to Supabase Edge Functions
  8. ✅ Uploads widgets to the storage bucket
Prerequisites for the script - Supabase CLI installed (npm install -g supabase) - Logged in to Supabase (supabase login) - Docker running (for deployment)
For manual deployment or to understand each step in detail, continue reading below.

Manual Deployment

Setting Up Your MCP Server for Supabase

1. Initialize a Supabase Project

If you don’t have a Supabase project yet:
supabase init

2. Create an Edge Function

supabase functions new mcp-server
This creates a new function at supabase/functions/mcp-server/.

3. Adapt your MCP Server to use Deno (required for Supabase Edge Functions)

Widgets in the resources folder are automatically registered. Additional widgets can be registered manually. (see UI Widgets)
Complete ExampleA full working example is available in the repository: Supabase Example
// supabase/functions/mcp-server/index.ts

import { createMCPServer } from "https://esm.sh/mcp-use@latest/server";

const PROJECT_REF = Deno.env.get("SUPABASE_PROJECT_REF") || "your-project-ref";
const BASE_URL =
  Deno.env.get("MCP_URL") ||
  `https://${PROJECT_REF}.supabase.co/functions/v1/mcp-server`;

// Create the mcp-use MCP server instance
const server = createMCPServer("test-app", {
  version: "1.0.0",
  description:
    "MCP server with automatic UI widget registration deployed on Supabase",
  baseUrl: BASE_URL,
});

// Define your tools
server.tool({
  name: "get-my-city",
  description: "Get my city",
  cb: async () => {
    return { content: [{ type: "text", text: `My city is San Francisco` }] };
  },
});
await server.listen();

4. Widgets assets

Widget assets should be served from a CDN, for example Supabase Storage. Upload the content of the dist/resources/widgets folder to Supabase Storage and set the MCP_URL environment variable to the URL of the dist folder. E.g if you created a bucket called “widgets” in Supabase Storage:
# UPLOAD
supabase storage cp -r dist/resources/widgets ss://widgets/ --experimental
and then set the environment variables for Supabase deployments:
# MCP_URL: Where widget assets (JS/CSS) are stored
export MCP_URL="https://YOUR_PROJECT_REF.supabase.co/storage/v1/object/public/widgets"

# MCP_SERVER_URL: Where the MCP server runs (for API calls in widgets)
export MCP_SERVER_URL="https://YOUR_PROJECT_REF.supabase.co/functions/v1/YOUR_FUNCTION_NAME"

# CSP_URLS: Supabase project URL (for Content Security Policy)
export CSP_URLS="https://YOUR_PROJECT_REF.supabase.co"
URLs Required: Supabase deployments need these URLs because widget files are served from static storage while API calls go to the edge function:
  • MCP_URL: Where widget assets are stored
  • MCP_SERVER_URL: Enables build-time URL injection for static deployments
  • CSP_URLS: Whitelists the Supabase project domain in the Content Security Policy (use base URL without path)

5. Build the mcp-use app

echo $MCP_URL
# should output: https://YOUR_PROJECT_REF.supabase.co/storage/v1/object/public/widgets
echo $MCP_SERVER_URL
# should output: https://YOUR_PROJECT_REF.supabase.co/functions/v1/YOUR_FUNCTION_NAME
pnpm build

6. Copy the dist folder to the function directory

cp -r dist supabase/functions/mcp-server/

7. Configure the function in config.toml

[functions.mcp-server]
...
static_files = [ "./functions/mcp-server/dist/**/*.html", "./functions/mcp-server/dist/mcp-use.json" ]
...

8. Set Environment Variables for CSP

Set environment variables before deploying so the MCP server can configure Content Security Policy (CSP) to allow widgets to make API calls and load assets:
# MCP_URL: Server URL for API calls
supabase secrets set MCP_URL="https://YOUR_PROJECT_REF.supabase.co/functions/v1/mcp-server" --project-ref YOUR_PROJECT_REF

# CSP_URLS: Supabase project URL (without path) to allow storage and function access
supabase secrets set CSP_URLS="https://YOUR_PROJECT_REF.supabase.co" --project-ref YOUR_PROJECT_REF
Environment Variables:
  • MCP_URL: Whitelists the edge function domain for API calls from widgets
  • CSP_URLS: Whitelists the Supabase project URL (without path) for widget assets (JS/CSS) and storage access. Use the base project URL like https://nnpumlykjksvxivhywwo.supabase.co (no /storage/* or other paths needed). Supports comma-separated values for multiple domains.
Important: Set these before deploying the function. If you set them after deployment, you must redeploy for the changes to take effect.Alternative: You can also configure CSP per-widget in your widget’s appsSdkMetadata['openai/widgetCSP'] instead of using the global CSP_URLS environment variable. See UI Widgets documentation for examples.

9. Deploy the Function

supabase functions deploy mcp-server --use-docker

Using the MCP Server

You can connect to the MCP Server from any MCP client. Below are examples for browser and Node.js using the mcp-use library.

The MCP Server URL

// From your application
//const mcpUrl = `https://YOUR_PROJECT_ID.supabase.co/functions/v1/FUNCTION_NAME/mcp`;
// e.g. if your function name is "mcp-server" as above
const mcpUrl = `https://YOUR_PROJECT_ID.supabase.co/functions/v1/mcp-server/mcp`;

Browser/Client-Side Usage

import { BrowserMCPClient } from "mcp-use";

const client = new BrowserMCPClient({
  mcpServers: {
    supabase: {
      url: `https://YOUR_PROJECT_ID.supabase.co/functions/v1/mcp-server/mcp`,
      transport: "http",
      headers: {
        Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
        "Content-Type": "application/json",
        Accept: "application/json, text/event-stream",
      },
    },
  },
});

// Create a session and initialize
const session = await client.createSession("supabase");
await session.initialize();

// Now use the session to call tools, access resources, etc.
const response = await session.connector.callTool("greet", { name: "World" });

Node.js Usage

import { MCPClient } from "mcp-use";

const client = new MCPClient({
  mcpServers: {
    supabase: {
      url: `https://YOUR_PROJECT_ID.supabase.co/functions/v1/mcp-server/mcp`,
      transport: "http",
      headers: {
        Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
        "Content-Type": "application/json",
        Accept: "application/json, text/event-stream",
      },
    },
  },
});

// Create a session and initialize
const session = await client.createSession("supabase");
await session.initialize();

// Now use the session to call tools, access resources, etc.
const response = await session.connector.callTool("greet", { name: "World" });
Important: Docker Required for Static FilesStatic files configured in static_files are only bundled when Docker is running during deployment. This applies to both local development and production deployment.When deploying with supabase functions deploy, use the —use-docker flag.
docker info  # Verify Docker is running
supabase functions deploy mcp-server --use-docker
See the Supabase static files documentation for more details.

CORS Issues

  • Ensure your CORS headers are correctly configured
  • Check that the Accept header is included in requests
  • Verify your Authorization header has the correct token

”Initialising login role…” Appears Multiple Times

During deployment, you’ll see Initialising login role... appear 2-3 times. This is normal behavior - the Supabase CLI authenticates separately for:
  1. Project linking
  2. Widget file upload to storage
  3. Public file upload to storage
Each storage operation requires its own authentication.

Deployment Stuck on “Initialising login role…”

If the deployment script hangs at Initialising login role... (no progress for several minutes) or you see authentication errors, Supabase may have temporarily banned your IP address. This is a known issue tracked on GitHub. Solution:
  1. Go to your database settings: https://supabase.com/dashboard/project/{PROJECT_ID}/database/settings#banned-ips
  2. Check if your IP is listed under “Banned IPs”
  3. Click “Unban” to restore access
  4. Retry the deployment
This typically happens after multiple failed connection attempts or when the connection is interrupted unexpectedly. The ban is temporary and automatic.

Learn More