From 67913228b5d2e0476187501e15b704383a76dc1d Mon Sep 17 00:00:00 2001 From: Pedro Rodrigues <44656907+Rodriguespn@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:16:40 +0000 Subject: [PATCH] docs: byom mcp server without auth (#41230) * docs: improve BYOM guide for MCP server deployment and OAuth integration * fix tutorial for simple mcp server * remove authentication section * fix rebase * fix pnpm.lock * add Zod to dictionary * remove authentication from the beginning * fix mcp-lite link * change order of deno.json * fix mcp-handler --- .../NavigationMenu.constants.ts | 6 +- .../content/guides/getting-started/byom.mdx | 265 ++++++++++++++++++ .../functions/mcp/simple-mcp-server/deno.json | 9 + .../functions/mcp/simple-mcp-server/index.ts | 35 +++ supa-mdx-lint/Rule003Spelling.toml | 1 + 5 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 apps/docs/content/guides/getting-started/byom.mdx create mode 100644 examples/edge-functions/supabase/functions/mcp/simple-mcp-server/deno.json create mode 100644 examples/edge-functions/supabase/functions/mcp/simple-mcp-server/index.ts diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 631ac8bc4ba..ae10e996a53 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -491,9 +491,13 @@ export const gettingstarted: NavMenuConstant = { url: '/guides/getting-started/ai-prompts' as `/${string}`, }, { - name: 'Model context protocol (MCP)', + name: 'Supabase MCP server', url: '/guides/getting-started/mcp' as `/${string}`, }, + { + name: 'Deploy MCP servers', + url: '/guides/getting-started/byom' as `/${string}`, + }, ], }, ], diff --git a/apps/docs/content/guides/getting-started/byom.mdx b/apps/docs/content/guides/getting-started/byom.mdx new file mode 100644 index 00000000000..69e6de4d4c2 --- /dev/null +++ b/apps/docs/content/guides/getting-started/byom.mdx @@ -0,0 +1,265 @@ +--- +id: 'ai-tools-byom' +title: 'Deploy MCP servers' +description: 'Build and deploy remote MCP servers on Supabase Edge Functions' +subtitle: 'Deploy custom MCP servers on Supabase Edge Functions' +--- + +Deploy your [Model Context Protocol](https://modelcontextprotocol.io/specification/2025-11-25) (MCP) servers on Supabase take advantage of features like [Edge Functions](/docs/guides/functions), [OAuth](/docs/guides/auth/oauth-server), and scaling for AI applications. + +- Get started with [deploying](#deploy-your-mcp-server) MCP servers on Supabase + +## Deploy your MCP server + +### Prerequisites + +Before you begin, make sure you have: + +- [Docker](https://docs.docker.com/get-docker/) installed (required for local Supabase development) +- [Deno](https://deno.land/) installed (Supabase Edge Functions runtime) +- [Supabase CLI](/docs/guides/cli/getting-started) installed + +### Create a new project + +Start by creating a new Supabase project: + +```bash +mkdir my-mcp-server +cd my-mcp-server +supabase init +``` + +### Create the MCP server function + +Create a new Edge Function for your MCP server: + +```bash +supabase functions new simple-mcp-server +``` + +Create a `deno.json` file in `supabase/functions/simple-mcp-server/` with the required dependencies: + +```json +{ + "imports": { + "@hono/mcp": "npm:@hono/mcp@^0.1.1", + "@modelcontextprotocol/sdk": "npm:@modelcontextprotocol/sdk@^1.24.3", + "@modelcontextprotocol/sdk/": "npm:/@modelcontextprotocol/sdk@^1.24.3/", + "hono": "npm:hono@^4.9.2", + "zod": "npm:zod@^4.1.13" + } +} +``` + + + +This tutorial uses the [official MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk), but you can use any MCP framework that's compatible with the [Edge Runtime](/docs/guides/functions), such as [mcp-lite](https://github.com/fiberplane/mcp-lite), [mcp-use](https://github.com/mcp-use/mcp-use), or [mcp-handler](https://github.com/vercel/mcp-handler). + + + +Replace the contents of `supabase/functions/simple-mcp-server/index.ts` with: + +```ts +// Setup type definitions for built-in Supabase Runtime APIs +import 'jsr:@supabase/functions-js/edge-runtime.d.ts' + +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' +import { StreamableHTTPTransport } from '@hono/mcp' +import { Hono } from 'hono' +import { z } from 'zod' + +// Change this to your function name +const functionName = 'simple-mcp-server' +const app = new Hono().basePath(`/${functionName}`) + +// Create your MCP server +const server = new McpServer({ + name: 'simple-mcp-server', + version: '1.0.0', +}) + +// Register a simple addition tool +server.registerTool( + 'add', + { + title: 'Addition Tool', + description: 'Add two numbers together', + inputSchema: { a: z.number(), b: z.number() }, + }, + ({ a, b }) => ({ + content: [{ type: 'text', text: String(a + b) }], + }) +) + +// Handle MCP requests +app.all('/mcp', async (c) => { + const transport = new StreamableHTTPTransport() + await server.connect(transport) + return transport.handleRequest(c) +}) + +Deno.serve(app.fetch) +``` + +### Understanding the code + +The MCP server implementation uses several key components: + +**Hono routing**: Supabase Edge Functions route all requests to `//*`. The Hono app uses `basePath` to handle this: + +```ts +const functionName = 'simple-mcp-server' +const app = new Hono().basePath(`/${functionName}`) +``` + +**MCP server setup**: The `McpServer` class from the official SDK handles the MCP protocol: + +```ts +const server = new McpServer({ + name: 'simple-mcp-server', + version: '1.0.0', +}) +``` + +**Tool registration**: Tools are registered with a name, metadata, input schema (using Zod), and a handler function: + +```ts +server.registerTool( + 'add', + { + title: 'Addition Tool', + description: 'Add two numbers together', + inputSchema: { a: z.number(), b: z.number() }, + }, + ({ a, b }) => ({ + content: [{ type: 'text', text: String(a + b) }], + }) +) +``` + +**HTTP transport**: The `StreamableHTTPTransport` from `@hono/mcp` connects your MCP server to HTTP requests: + +```ts +app.all('/mcp', async (c) => { + const transport = new StreamableHTTPTransport() + await server.connect(transport) + return transport.handleRequest(c) +}) +``` + +### Local development + +Start the Supabase local development stack: + +```bash +supabase start +``` + +In a separate terminal, serve your function: + +```bash +supabase functions serve --no-verify-jwt simple-mcp-server +``` + +Your MCP server is now running at: + +``` +http://localhost:54321/functions/v1/simple-mcp-server/mcp +``` + + + +The `--no-verify-jwt` flag disables JWT verification at the Edge Function layer. This is required because MCP authentication is handled by the MCP server itself, not by Supabase's standard JWT validation. + + + +### Test your MCP server + +Test your server with the official [MCP Inspector](https://github.com/modelcontextprotocol/inspector): + +```bash +npx -y @modelcontextprotocol/inspector +``` + +Enter your MCP endpoint URL in the inspector UI to explore available tools and test them interactively. + +### Deploy to production + +When you're ready to deploy, link your project and deploy the function: + +```bash +supabase link --project-ref +supabase functions deploy --no-verify-jwt simple-mcp-server +``` + +Your MCP server will be available at: + +``` +https://.supabase.co/functions/v1/simple-mcp-server/mcp +``` + +Update your MCP client configuration to use the production URL. + +## Adding more tools + +Extend your MCP server by registering additional tools. Here's an example that queries your Supabase database: + +```ts +import { createClient } from 'jsr:@supabase/supabase-js@2' + +// Create Supabase client +const supabase = createClient( + Deno.env.get('SUPABASE_URL')!, + Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! +) + +server.registerTool( + 'list_users', + { + title: 'List Users', + description: 'Get a list of users from the database', + inputSchema: { limit: z.number().optional().default(10) }, + }, + async ({ limit }) => { + const { data, error } = await supabase + .from('users') + .select('id, email, created_at') + .limit(limit) + + if (error) { + return { + content: [{ type: 'text', text: `Error: ${error.message}` }], + isError: true, + } + } + + return { + content: [{ type: 'text', text: JSON.stringify(data, null, 2) }], + } + } +) +``` + +## Add authentication + + + +MCP authentication is not yet supported on Edge Functions. For now, MCP servers deployed on Supabase Edge Functions are publicly accessible. + + + +## Examples + +You can find ready-to-use MCP server implementations here: + +- [MCP server examples on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/mcp/) + +## Resources + +- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/2025-11-25) +- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) +- [Supabase Edge Functions](/docs/guides/functions) +- [OAuth 2.1 Server](/docs/guides/auth/oauth-server) +- [MCP Authentication](/docs/guides/auth/oauth-server/mcp-authentication) +- [MCP server examples on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/mcp) +- [Building MCP servers with mcp-lite](/docs/guides/functions/examples/mcp-server-mcp-lite) - Alternative lightweight framework diff --git a/examples/edge-functions/supabase/functions/mcp/simple-mcp-server/deno.json b/examples/edge-functions/supabase/functions/mcp/simple-mcp-server/deno.json new file mode 100644 index 00000000000..7233c5f1d1a --- /dev/null +++ b/examples/edge-functions/supabase/functions/mcp/simple-mcp-server/deno.json @@ -0,0 +1,9 @@ +{ + "imports": { + "@hono/mcp": "npm:@hono/mcp@^0.1.1", + "@modelcontextprotocol/sdk": "npm:/@modelcontextprotocol/sdk@^1.17.3", + "@modelcontextprotocol/sdk/": "npm:/@modelcontextprotocol/sdk@^1.17.3/", + "hono": "npm:hono@^4.9.2", + "zod": "npm:zod@^3.25.76" + } +} diff --git a/examples/edge-functions/supabase/functions/mcp/simple-mcp-server/index.ts b/examples/edge-functions/supabase/functions/mcp/simple-mcp-server/index.ts new file mode 100644 index 00000000000..7676f61ad25 --- /dev/null +++ b/examples/edge-functions/supabase/functions/mcp/simple-mcp-server/index.ts @@ -0,0 +1,35 @@ +// Setup type definitions for built-in Supabase Runtime APIs +import "jsr:@supabase/functions-js/edge-runtime.d.ts"; + +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StreamableHTTPTransport } from "@hono/mcp"; +import { Hono } from "hono"; +import { z } from "zod"; + +// Change this to your function name +const functionName = "simple-mcp-server"; +const app = new Hono().basePath(`/${functionName}`); + +// Create your MCP server +const server = new McpServer({ + name: "simple-mcp-server", + version: "1.0.0", +}); + +// Register a simple addition tool +server.registerTool("add", { + title: "Addition Tool", + description: "Add two numbers together", + inputSchema: { a: z.number(), b: z.number() }, +}, ({ a, b }) => ({ + content: [{ type: "text", text: String(a + b) }], +})); + +// Handle MCP requests +app.all("/mcp", async (c) => { + const transport = new StreamableHTTPTransport(); + await server.connect(transport); + return transport.handleRequest(c); +}); + +Deno.serve(app.fetch); diff --git a/supa-mdx-lint/Rule003Spelling.toml b/supa-mdx-lint/Rule003Spelling.toml index f1ac4e5eab7..5da86acc7a3 100644 --- a/supa-mdx-lint/Rule003Spelling.toml +++ b/supa-mdx-lint/Rule003Spelling.toml @@ -344,6 +344,7 @@ allow_list = [ "Xcode", "Yang", "Zapier", + "Zod", "ZeptoMail", "asyncpg", "bcrypt",