diff --git a/apps/docs/components/MetricsStackCards.tsx b/apps/docs/components/MetricsStackCards.tsx
index 19ba4b02edec9..3ee2cbfb0ec12 100644
--- a/apps/docs/components/MetricsStackCards.tsx
+++ b/apps/docs/components/MetricsStackCards.tsx
@@ -53,14 +53,11 @@ const metricsStackOptions: MetricsStackOption[] = [
Supabase alongside your app telemetry.
>
),
- href: '/guides/telemetry/metrics/datadog',
+ href: 'https://docs.datadoghq.com/integrations/supabase/',
icon: ,
iconColor: '#632CA6',
iconBg: 'rgba(99,44,166,0.1)',
- badges: [
- { label: 'Supabase guide', variant: 'default' },
- { label: 'Community', variant: 'community' },
- ],
+ badges: [{ label: 'Community', variant: 'community' }],
},
{
title: 'Vendor-agnostic / BYO Prometheus',
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
index 50330ec471144..ae10e996a5301 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}`,
+ },
],
},
],
@@ -2769,7 +2773,7 @@ export const telemetry: NavMenuConstant = {
},
{
name: 'Datadog',
- url: '/guides/telemetry/metrics/datadog' as `/${string}`,
+ url: 'https://docs.datadoghq.com/integrations/supabase/' as `/${string}`,
},
{
name: 'Vendor-agnostic setup',
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 0000000000000..69e6de4d4c24e
--- /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/apps/docs/content/guides/platform/upgrading.mdx b/apps/docs/content/guides/platform/upgrading.mdx
index 06dc63387e629..22567b873ddad 100644
--- a/apps/docs/content/guides/platform/upgrading.mdx
+++ b/apps/docs/content/guides/platform/upgrading.mdx
@@ -85,7 +85,10 @@ During the 90-day restore window a paused project can be restored to the platfor
src="/docs/img/guides/platform/paused-90-day.png"
/>
-After the 90-day restore window, you can download your project's backup file, and Storage objects from the project dashboard. See [restoring a backup locally](/docs/guides/local-development/restoring-downloaded-backup) for instructions on how to load that backup locally to recover your data.
+After the 90-day restore window, you can download your project's backup file, and Storage objects from the project dashboard. You can restore the data in the following ways:
+
+- [Restore a backup to a new Supabase project](/docs/guides/platform/migrating-within-supabase/dashboard-restore)
+- [Restore a backup locally](/docs/guides/local-development/restoring-downloaded-backup)
diff --git a/apps/docs/content/guides/telemetry/metrics/datadog.mdx b/apps/docs/content/guides/telemetry/metrics/datadog.mdx
deleted file mode 100644
index 8bcf3628ea17b..0000000000000
--- a/apps/docs/content/guides/telemetry/metrics/datadog.mdx
+++ /dev/null
@@ -1,83 +0,0 @@
----
-id: 'metrics-datadog'
-title: 'Metrics API with Datadog'
-description: 'Scrape Supabase metrics with the Datadog Agent or Prometheus integrations'
----
-
-Datadog can ingest the Supabase Metrics API through its [OpenMetrics integration](https://docs.datadoghq.com/integrations/openmetrics/) or by proxying Prometheus data into Datadog’s time-series store. This guide covers both options so you can monitor database health alongside application metrics, logs, and traces in one place.
-
-
-
-Datadog publishes a [Supabase integration reference](https://docs.datadoghq.com/integrations/supabase/) that uses the same Metrics API outlined here. That integration is managed by Datadog and is not officially supported by Supabase.
-
-
-
-## Prerequisites
-
-- Supabase service role key (or another secret key) with Metrics API access.
-- Datadog API/APP keys (for SaaS) and the Datadog Agent installed wherever you plan to run the scraper.
-
-<$Partial path="metrics_access.mdx" />
-
-## Option A: Datadog agent + OpenMetrics check
-
-1. Install the Datadog Agent on a host that can reach `https://.supabase.co`.
-2. Enable the OpenMetrics integration by creating `openmetrics.d/conf.yaml` (Linux path: `/etc/datadog-agent/conf.d/openmetrics.d/conf.yaml`):
-
-```yaml
-init_config:
-
-instances:
- - openmetrics_endpoint: https://.supabase.co/customer/v1/privileged/metrics
- namespace: supabase
- metrics:
- - 'supabase_*'
- auth_type: basic
- username: service_role
- password: { { env "SUPABASE_SERVICE_ROLE_KEY" } }
- ssl_verify: true
- prometheus_timeout: 30
- labels_mapper:
- project: project_ref
-```
-
-3. Restart the Datadog Agent (`sudo systemctl restart datadog-agent`).
-4. In Datadog, search for metrics with the `supabase.` prefix. Create dashboards or notebooks to visualize CPU, IO, replication, and connection metrics.
-
-### Tuning tips
-
-- Set `min_collection_interval: 60` to match Supabase’s one-minute refresh cadence.
-- Use `labels_mapper` to rename high-cardinality labels into shorter Datadog tag keys.
-- If you monitor multiple projects, duplicate the instance block with a unique `namespace` and tag each project (`tags: ["supabase_project:"]`).
-
-## Option B: Prometheus remote write into Datadog
-
-If you already run [Prometheus](https://prometheus.io/docs/prometheus/latest/installation/) (self-hosted or managed), push scraped metrics into Datadog using the [Prometheus Remote Write integration](https://docs.datadoghq.com/integrations/guide/prometheus_remote_write/):
-
-1. Configure Prometheus with the Supabase scrape job (see the [self-hosted guide](/guides/telemetry/metrics/grafana-self-hosted#1-deploy-prometheus)).
-2. Add a `remote_write` section to `prometheus.yml` that targets Datadog’s endpoint:
-
-```yaml
-remote_write:
- - url: https://api.datadoghq.com/api/v1/prometheus/write?api_key=
- write_relabel_configs:
- - source_labels: [job]
- target_label: datadog_namespace
- replacement: supabase
-```
-
-3. (Optional) Use [Datadog’s Prometheus mapping profiles](https://docs.datadoghq.com/integrations/guide/prometheus-metrics-map/) to convert metric/label names into Datadog-friendly conventions.
-
-## Dashboards and alerts
-
-- Use the Datadog Dashboard Gallery to build widgets for CPU usage, tuple IO, replication lag, and connection saturation.
-- Create monitors (for example, `avg(last_5m):supabase_pg_stat_database_xact_commit{project:} > 5e5`) to detect runaway workloads.
-- Tag metrics with `env`, `service`, or `team` so you can slice Supabase telemetry the same way you slice application telemetry.
-
-## Troubleshooting
-
-- **401 errors:** rotate the service role key in Supabase and update the Datadog credentials.
-- **SSL errors:** ensure the host trusts the standard certificate authorities used by `*.supabase.co`.
-- **High cardinality warnings:** filter out labels such as `datname` or `application_name` if you only need aggregate views.
-
-[← Back to the Metrics API landing](/guides/telemetry/metrics)
diff --git a/apps/docs/features/ui/CodeBlock/CodeBlock.client.tsx b/apps/docs/features/ui/CodeBlock/CodeBlock.client.tsx
index 9b3ca95161f1f..a075ae9f2e080 100644
--- a/apps/docs/features/ui/CodeBlock/CodeBlock.client.tsx
+++ b/apps/docs/features/ui/CodeBlock/CodeBlock.client.tsx
@@ -1,10 +1,11 @@
'use client'
-import { Check, Copy } from 'lucide-react'
-import { type MouseEvent, useCallback, useEffect, useState } from 'react'
+import { Check, Copy, WrapText, ArrowRightFromLine } from 'lucide-react'
+import { type MouseEvent, useCallback, useEffect, useState, useRef } from 'react'
import { type ThemedToken } from 'shiki'
import { type NodeHover } from 'twoslash'
import { cn, copyToClipboard, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
+import { getFontStyle } from './CodeBlock.utils'
export function AnnotatedSpan({
token,
@@ -113,3 +114,46 @@ export function CodeCopyButton({ className, content }: { className?: string; con
)
}
+
+export function CodeBlockControls({ content }: { content: string }) {
+ const [isWrapped, setIsWrapped] = useState(false)
+ const wrapperRef = useRef(null)
+
+ const toggleWrap = useCallback(() => {
+ setIsWrapped((prev) => {
+ const newValue = !prev
+ // Find the parent code block and toggle the wrap data attribute
+ const codeBlock = wrapperRef.current?.closest('.shiki')
+ if (codeBlock) {
+ if (newValue) {
+ codeBlock.setAttribute('data-wrapped', 'true')
+ } else {
+ codeBlock.removeAttribute('data-wrapped')
+ }
+ }
+ return newValue
+ })
+ }, [])
+
+ return (
+
+
+
+
+
+ {isWrapped ? 'Disable word wrap' : 'Enable word wrap'}
+
+
+
+ )
+}
diff --git a/apps/docs/features/ui/CodeBlock/CodeBlock.tsx b/apps/docs/features/ui/CodeBlock/CodeBlock.tsx
index 518171367c01e..c79c416b9a620 100644
--- a/apps/docs/features/ui/CodeBlock/CodeBlock.tsx
+++ b/apps/docs/features/ui/CodeBlock/CodeBlock.tsx
@@ -1,9 +1,9 @@
-import { type PropsWithChildren } from 'react'
+import { Fragment, type PropsWithChildren } from 'react'
import { bundledLanguages, createHighlighter, type BundledLanguage, type ThemedToken } from 'shiki'
import { createTwoslasher, type ExtraFiles, type NodeHover } from 'twoslash'
import { cn } from 'ui'
-import { AnnotatedSpan, CodeCopyButton } from './CodeBlock.client'
+import { AnnotatedSpan, CodeBlockControls } from './CodeBlock.client'
import { getFontStyle } from './CodeBlock.utils'
import theme from './supabase-2.json' with { type: 'json' }
import denoTypes from './types/lib.deno.d.ts.include'
@@ -72,27 +72,42 @@ export async function CodeBlock({
)}
>