> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcp-use.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Supabase

> Deploy an MCP server on Supabase Edge Functions

Supabase Edge Functions run on a Deno-compatible runtime. `MCPServer` exposes
the Web-standard `fetch(Request)` boundary needed by that runtime; an Edge
Function forwards requests to `server.fetch` and does not call `listen()`.

This guide deploys the CLI build output at `.mcp-use/build/index.js`.

## Prerequisites

* A TypeScript MCP server built with `mcp-use`.
* The Supabase CLI, authenticated with `supabase login`.
* A Supabase project reference.
* Node.js 22.22.2 or later to run the `mcp-use` build.
* Docker only when you choose local Supabase bundling or local function
  serving. Supabase can also bundle through its API.

Initialize and link Supabase from the server project:

```bash theme={null}
supabase init
supabase login
supabase link --project-ref YOUR_PROJECT_REF
supabase functions new mcp-server
```

## Author the server

The public Edge Function URL contains the gateway prefix
`/functions/v1/mcp-server`. Give the MCP server a matching full base path so
the request path seen by `server.fetch` and the client endpoint agree:

```typescript theme={null}
// index.ts
import { MCPServer } from "mcp-use";
import { z } from "zod";

const server = new MCPServer({
  name: "supabase-edge-mcp",
  version: "1.0.0",
  basePath: "/functions/v1/mcp-server/mcp",
});

server.tool(
  {
    name: "get-city",
    description: "Return the configured demo city",
    outputSchema: z.object({ city: z.string() }),
  },
  async () => {
    const result = { city: "San Francisco" };
    return {
      content: [{ type: "text", text: result.city }],
      structuredContent: result,
    };
  },
);

export default server;
```

The callback returns the raw MCP result envelope. The exported server is
mounted through `server.fetch` later in this guide.

<Note>
  If your gateway or custom domain rewrites the function prefix before the
  request reaches the Edge Function, change `basePath` to the pathname the
  handler actually receives and use that same pathname in the client URL.
</Note>

## Build for the Edge Function

The build creates:

```text theme={null}
.mcp-use/build/
├── index.js
├── manifest.json
└── views/
    ├── <view-name>/...
    └── public/...
```

If the server has no Views, build normally:

```bash theme={null}
npm run typecheck
npm run build
```

If it has Views, create a public Supabase Storage bucket such as `widgets`.
Set its public URL prefix while building so the generated View manifest points
at Storage:

```bash theme={null}
PROJECT_REF="YOUR_PROJECT_REF"
ASSETS_URL="https://${PROJECT_REF}.supabase.co/storage/v1/object/public/widgets"
MCP_ASSETS_URL="${ASSETS_URL}" npm run build
```

Copy the generated server build into the Edge Function:

```bash theme={null}
mkdir -p supabase/functions/mcp-server/.mcp-use
cp -R .mcp-use/build supabase/functions/mcp-server/.mcp-use/
```

The deployed import target is
`supabase/functions/mcp-server/.mcp-use/build/index.js`, not a `dist`
artifact.

## Map npm imports in Deno

The built entry keeps its package imports external. Replace
`supabase/functions/mcp-server/deno.json` with:

```json theme={null}
{
  "imports": {
    "mcp-use": "npm:mcp-use@beta",
    "mcp-use/": "npm:mcp-use@beta/",
    "zod": "npm:zod@^4.4.3"
  }
}
```

The import map makes Deno resolve the built server's `mcp-use` imports through
the `npm:mcp-use@beta` package entry point.

Pin the `npm:mcp-use` version to the same version used by the project when
reproducible builds are required.

## Forward requests to `server.fetch`

Replace `supabase/functions/mcp-server/index.ts`:

```typescript theme={null}
import server from "./.mcp-use/build/index.js";

Deno.serve((request) => server.fetch(request));
```

`server.fetch` returns a Web `Response`, so no Node listener, adapter, or port
is involved.

## Upload View and public assets

With the default MCP path above, Storage must preserve the URL hierarchy
embedded by `mcp-use build`:

```text theme={null}
widgets/
└── functions/v1/mcp-server/mcp/
    └── _mcp-use/
        ├── views/<view-name>/...
        └── public/...
```

For a View named `animal-card`, upload its built files and the public assets:

```bash theme={null}
supabase storage cp -r \
  .mcp-use/build/views/animal-card/ \
  "ss:///widgets/functions/v1/mcp-server/mcp/_mcp-use/views/animal-card/" \
  --experimental

supabase storage cp -r \
  .mcp-use/build/views/public/ \
  "ss:///widgets/functions/v1/mcp-server/mcp/_mcp-use/public/" \
  --experimental
```

Repeat the first command for each View directory. You can use the Supabase
Dashboard or another supported Storage client instead; the required part is
the object layout:

* View bundles load from `<basePath>/_mcp-use/views/<name>/...`.
* Files from the project's `public/` directory load from
  `<basePath>/_mcp-use/public/...`.

Keep these generated paths intact rather than flattening View and public
assets into generic bucket directories.

## Build-time, runtime, and CSP values

The three concerns are distinct:

| Value                           | When              | Responsibility                                                                                                                                                                                  |
| ------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MCP_ASSETS_URL`                | Build and runtime | CDN/Storage prefix. At build time it rewrites View manifest URLs; at runtime it supplies the public asset base and its origin is added to `resourceDomains`. Use the same value in both places. |
| `MCP_URL`                       | Runtime           | Public server origin only, such as `https://PROJECT.supabase.co`. It is used for server-origin resolution and added to View `connectDomains`. The endpoint path comes from `basePath`.          |
| View `csp` or `CSP_*` variables | Authoring/runtime | Allow additional APIs, images, frames, or base URIs used by the View. The server and asset origins do not authorize unrelated external domains.                                                 |

Set the runtime values:

```bash theme={null}
supabase secrets set \
  MCP_URL="https://YOUR_PROJECT_REF.supabase.co" \
  MCP_ASSETS_URL="https://YOUR_PROJECT_REF.supabase.co/storage/v1/object/public/widgets" \
  --project-ref YOUR_PROJECT_REF
```

Use `CSP_URLS` only when intentionally granting the same extra origins to all
four CSP categories. Prefer the tool's `view.csp` or a category-specific
variable such as `CSP_CONNECT_DOMAINS` for narrower access.

## Choose gateway authentication

Supabase gateway authentication and MCP authentication are separate layers.
For a public demo, disable Supabase's gateway JWT check explicitly:

```toml theme={null}
[functions.mcp-server]
verify_jwt = false
```

For a protected function, keep the gateway check enabled and configure the
MCP client to send the required Supabase authorization headers. Do not put a
service-role key in a browser or public MCP client.

## Test and deploy

Check the Deno entry before deployment:

```bash theme={null}
deno check supabase/functions/mcp-server/index.ts
```

Serve locally when Docker is available:

```bash theme={null}
supabase functions serve mcp-server
```

The local MCP endpoint is:

```text theme={null}
http://127.0.0.1:54321/functions/v1/mcp-server/mcp
```

Deploy with the Supabase CLI:

```bash theme={null}
supabase functions deploy mcp-server
```

Use `--use-docker` to force local bundling, or `--use-api` to force
server-side bundling. Docker is not unconditionally required for deployment.

The hosted endpoint is:

```text theme={null}
https://YOUR_PROJECT_REF.supabase.co/functions/v1/mcp-server/mcp
```

Connect the included MCP client to the same path:

```bash theme={null}
MCP_ENDPOINT="https://YOUR_PROJECT_REF.supabase.co/functions/v1/mcp-server/mcp"
npx mcp-use client connect supabase-edge "${MCP_ENDPOINT}" --no-oauth
npx mcp-use client supabase-edge tools list
```

When gateway JWT verification is enabled, pass its authorization header to
`client connect` with `-H "Authorization: Bearer TOKEN"`.

## Troubleshooting

* **Function 404:** verify the project reference, function name, and that
  `basePath` matches the full request pathname received by the Edge Function.
* **MCP route 404:** make sure the client includes the final `/mcp`.
* **View asset 404:** compare the Storage object hierarchy with
  `<basePath>/_mcp-use/views/` and `<basePath>/_mcp-use/public/`.
* **View CSP violation:** add only the missing external origin to `view.csp`
  or the corresponding `CSP_*` runtime variable, then redeploy.
* **Bundle too large:** inspect it with `deno info` and try local bundling with
  `supabase functions deploy mcp-server --use-docker`.

## Platform references

* [Supabase Edge Functions quickstart](https://supabase.com/docs/guides/functions/quickstart)
* [Supabase function configuration](https://supabase.com/docs/guides/local-development/cli/config)
* [Supabase Storage public URLs](https://supabase.com/docs/guides/storage/serving/downloads)
* [MCP Apps Content Security Policy](/v2/typescript/mcp-apps/content-security-policy)
