diff --git a/apps/docs/content/guides/ai-tools/byo-mcp.mdx b/apps/docs/content/guides/ai-tools/byo-mcp.mdx index 76af060bfb8..31b885c1b74 100644 --- a/apps/docs/content/guides/ai-tools/byo-mcp.mdx +++ b/apps/docs/content/guides/ai-tools/byo-mcp.mdx @@ -155,10 +155,10 @@ allow_dynamic_registration = true ### Step 2: Create the MCP server -The fastest path is the [MCP Server block](/library/docs/headless/mcp-server) in the Supabase Library. It installs an Edge Function with the middleware already wired, a `whoami` tool, and a small tool registry to extend: +The fastest path is the [MCP Server block](/library/docs/headless/mcp) in the Supabase Library. It installs an Edge Function with the middleware already wired, a `whoami` tool, and a small tool registry to extend: ```bash -npx shadcn@latest add https://supabase.com/library/r/mcp-server.json +npx shadcn@latest add https://supabase.com/library/r/mcp.json ``` To write the function yourself, start with a table for the tools to work on. Create it with RLS so each user only sees their own rows; the `user_id` default means inserts don't need to pass it. Save this as a migration with `supabase migration new create_todos` and paste it into the generated file: @@ -380,7 +380,7 @@ Both examples in this guide are in the `supabase/supabase` repository, ready to ## Resources -- [MCP Server block](/library/docs/headless/mcp-server) and [Headless App block](/library/docs/tanstack/headless-app) in the Supabase Library +- [MCP Server block](/library/docs/headless/mcp) and [Headless App block](/library/docs/tanstack/headless-app) in the Supabase Library - [`@supabase/server` reference](/docs/reference/server/introduction) - [MCP authentication with Supabase Auth](/docs/guides/auth/oauth-server/mcp-authentication) - [OAuth 2.1 server](/docs/guides/auth/oauth-server) diff --git a/apps/docs/content/guides/auth/oauth-server/mcp-authentication.mdx b/apps/docs/content/guides/auth/oauth-server/mcp-authentication.mdx index bcd2c53c5df..ca70650df63 100644 --- a/apps/docs/content/guides/auth/oauth-server/mcp-authentication.mdx +++ b/apps/docs/content/guides/auth/oauth-server/mcp-authentication.mdx @@ -98,7 +98,7 @@ Deno.serve( ) ``` -The [MCP Server block](/library/docs/headless/mcp-server) in the Supabase Library packages this as an installable Edge Function, and the [OAuth Consent block](/library/docs/nextjs/oauth-consent) provides the consent screen. See [Deploy MCP servers](/docs/guides/ai-tools/byo-mcp) for the full setup. +The [MCP Server block](/library/docs/headless/mcp) in the Supabase Library packages this as an installable Edge Function, and the [OAuth Consent block](/library/docs/nextjs/oauth-consent) provides the consent screen. See [Deploy MCP servers](/docs/guides/ai-tools/byo-mcp) for the full setup. diff --git a/apps/ui-library/app/example/headless-app/page.tsx b/apps/ui-library/app/example/headless-app/page.tsx index 5d1d1bf0d5f..cc68d4ad244 100644 --- a/apps/ui-library/app/example/headless-app/page.tsx +++ b/apps/ui-library/app/example/headless-app/page.tsx @@ -8,7 +8,7 @@ import { OAuthConsentCard } from '@/registry/default/blocks/oauth-consent/compon import { LoginForm } from '@/registry/default/blocks/password-based-auth-nextjs/components/login-form' const PRODUCT_NAME = 'Acme' -const MCP_SERVER_URL = 'https://your-project.supabase.co/functions/v1/mcp-server' +const MCP_SERVER_URL = 'https://your-project.supabase.co/functions/v1/mcp' const grants: OAuthGrant[] = [ { diff --git a/apps/ui-library/components/catalog-preview.tsx b/apps/ui-library/components/catalog-preview.tsx index aa4f0337340..7db3282dbd7 100644 --- a/apps/ui-library/components/catalog-preview.tsx +++ b/apps/ui-library/components/catalog-preview.tsx @@ -21,7 +21,7 @@ const previewTitles: Partial> = { storage: 'product-assets', chat: 'team chat', editor: 'index.ts', - mcp: 'mcp-server', + mcp: 'mcp', agents: 'connected agents', dashboard: 'acme workspace', client: 'supabase.ts', diff --git a/apps/ui-library/config/docs.ts b/apps/ui-library/config/docs.ts index b51d326aae2..f88e80496f4 100644 --- a/apps/ui-library/config/docs.ts +++ b/apps/ui-library/config/docs.ts @@ -55,7 +55,7 @@ export const mcpBlocks: SidebarNavGroup = { items: [ { title: 'MCP Server', - href: '/docs/headless/mcp-server', + href: '/docs/headless/mcp', items: [], new: true, commandItemLabel: 'MCP Server', diff --git a/apps/ui-library/config/library.ts b/apps/ui-library/config/library.ts index 913068e101a..1715f6caed5 100644 --- a/apps/ui-library/config/library.ts +++ b/apps/ui-library/config/library.ts @@ -114,7 +114,7 @@ const blockMetadata: Record +preview: --- Give embedded product agents and external clients such as Codex, Claude Code, @@ -11,7 +11,7 @@ and gives every tool an RLS-scoped client. ## Installation - + Installs Deno Edge Function files into a Supabase project or empty directory. No `components.json` is required. Backend files stay in `supabase/` at the project @@ -25,7 +25,7 @@ list, preserving existing entries. Check the function separately with Deno. The function verifies access tokens itself, so disable the gateway JWT check: ```toml -[functions.mcp-server] +[functions.mcp] verify_jwt = false ``` @@ -86,7 +86,7 @@ Deno.serve( ``` `withOAuthProtectedResource()` runs before the auth gate. It serves RFC 9728 -metadata at `/functions/v1/mcp-server/oauth-protected-resource` and adds a +metadata at `/functions/v1/mcp/oauth-protected-resource` and adds a `WWW-Authenticate` challenge to `401` responses so MCP clients can discover the authorization server. On Edge Functions it derives the public URLs itself, locally and hosted; off Edge Functions pass `resourceServer` and `authorizationServer`. @@ -115,7 +115,7 @@ authorization decisions. Each tool module exports one registration function: ```ts -// supabase/functions/mcp-server/tools/tasks.ts +// supabase/functions/mcp/tools/tasks.ts import type { McpServer } from 'npm:@modelcontextprotocol/server@2.0.0' import { z } from 'npm:zod@4.4.3' @@ -182,11 +182,11 @@ the `SupabaseClient` in `tools/types.ts` a `SupabaseClient`. | `MCP_SERVER_NAME` | `supabase-mcp` | Server name shown to MCP clients | | `MCP_SERVER_DESCRIPTION` | Generic sentence | Instructions shown to clients | -The block includes `supabase/functions/mcp-server/.env.example`. Copy it before +The block includes `supabase/functions/mcp/.env.example`. Copy it before serving locally, then customize the name and description: ```bash -cp supabase/functions/mcp-server/.env.example supabase/functions/.env +cp supabase/functions/mcp/.env.example supabase/functions/.env ``` Add `supabase/functions/.env` to `.gitignore`. Supabase supplies the project URL, @@ -199,10 +199,10 @@ function's public URL. Check the function before serving or deploying it: ```bash -cd supabase/functions/mcp-server +cd supabase/functions/mcp deno task check cd ../../.. -supabase functions serve mcp-server --env-file supabase/functions/.env +supabase functions serve mcp --env-file supabase/functions/.env ``` Then deploy: @@ -210,7 +210,7 @@ Then deploy: ```bash supabase config push supabase secrets set --env-file supabase/functions/.env -supabase functions deploy mcp-server +supabase functions deploy mcp ``` ## Further reading diff --git a/apps/ui-library/content/docs/tanstack/headless-app.mdx b/apps/ui-library/content/docs/tanstack/headless-app.mdx index 35e1e37edac..7537cffaf3b 100644 --- a/apps/ui-library/content/docs/tanstack/headless-app.mdx +++ b/apps/ui-library/content/docs/tanstack/headless-app.mdx @@ -32,7 +32,7 @@ Merge `supabase/config.toml` if you already have project settings. Add - Set `VITE_PRODUCT_NAME` in `.env.local` and edit the generated pages and components. - Define your tables, grants, and RLS policies in `supabase/schemas/`. -- Replace `supabase/functions/mcp-server/tools/tasks.ts` with your app's operations +- Replace `supabase/functions/mcp/tools/tasks.ts` with your app's operations and register them in `tools/index.ts`. Use the supplied user-scoped `supabase` client. - Set `MCP_SERVER_NAME` and `MCP_SERVER_DESCRIPTION` with `supabase secrets set`. @@ -77,7 +77,7 @@ Review the migration, including grants and RLS policies, then deploy: supabase link --project-ref supabase db push supabase config push -supabase functions deploy mcp-server +supabase functions deploy mcp ``` Open `/agents` on your deployed app, sign in, and copy the server URL into an diff --git a/apps/ui-library/lib/registry-resolution.test.ts b/apps/ui-library/lib/registry-resolution.test.ts index 3a0cb13b4c6..344d00ba65f 100644 --- a/apps/ui-library/lib/registry-resolution.test.ts +++ b/apps/ui-library/lib/registry-resolution.test.ts @@ -118,10 +118,10 @@ describe('registry composition and resolution', () => { // `~/` keeps backend files out of the installing project's src directory. expect( getInstalledPath({ - path: 'registry/default/blocks/mcp-server/supabase/functions/mcp-server/index.ts', - target: '~/supabase/functions/mcp-server/index.ts', + path: 'registry/default/blocks/mcp/supabase/functions/mcp/index.ts', + target: '~/supabase/functions/mcp/index.ts', }) - ).toBe('supabase/functions/mcp-server/index.ts') + ).toBe('supabase/functions/mcp/index.ts') expect(() => getInstalledPath({ path: 'source.ts', target: '../outside.ts' })).toThrow( /Invalid installed path/ ) diff --git a/apps/ui-library/next.config.mjs b/apps/ui-library/next.config.mjs index f16894b1087..75d04417fce 100644 --- a/apps/ui-library/next.config.mjs +++ b/apps/ui-library/next.config.mjs @@ -32,6 +32,17 @@ const nextConfig = { destination: '/api/registry/tanstack-db', permanent: true, }, + // The MCP server block was renamed from mcp-server to mcp + { + source: '/r/mcp-server.json', + destination: '/r/mcp.json', + permanent: true, + }, + { + source: '/docs/headless/mcp-server', + destination: '/docs/headless/mcp', + permanent: true, + }, ] }, } diff --git a/apps/ui-library/public/r/headless-app-tanstack.json b/apps/ui-library/public/r/headless-app-tanstack.json index 0a8ad223a58..616d7e3805a 100644 --- a/apps/ui-library/public/r/headless-app-tanstack.json +++ b/apps/ui-library/public/r/headless-app-tanstack.json @@ -14,20 +14,20 @@ ], "files": [ { - "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/index.ts", + "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/index.ts", "content": "import type { McpServer } from 'npm:@modelcontextprotocol/server@2.0.0'\n\nimport { registerTaskTools } from './tasks.ts'\nimport type { ToolContext } from './types.ts'\nimport { registerWhoamiTool } from './whoami.ts'\n\nexport type { ToolContext } from './types.ts'\n\n// Add your product's tool modules here. The shared MCP runtime supplies the\n// authenticated context for each request.\nexport function registerTools(server: McpServer, context: ToolContext): void {\n registerWhoamiTool(server, context)\n registerTaskTools(server, context)\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/index.ts" + "target": "~/supabase/functions/mcp/tools/index.ts" }, { - "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/tasks.ts", + "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/tasks.ts", "content": "import type { McpServer } from 'npm:@modelcontextprotocol/server@2.0.0'\nimport { z } from 'npm:zod@4.4.3'\n\nimport { errorResult, jsonResult, runtimeErrorResult } from './result.ts'\nimport type { ToolContext } from './types.ts'\n\nconst taskFields = 'id, title, closed, created_at'\nconst taskId = z.uuid().describe('The task ID returned by list_tasks or create_task.')\nconst taskTitle = z\n .string()\n .trim()\n .min(1)\n .max(200)\n .describe('A task title, 1–200 characters after trimming surrounding whitespace.')\nconst taskNotFound = 'Task not found or you do not have access.'\n\n// Use only the caller's client. Ownership comes from auth.uid() in the schema,\n// and RLS applies to reads and writes, including queries by a supplied task ID.\nexport function registerTaskTools(server: McpServer, { supabase }: ToolContext): void {\n server.registerTool(\n 'list_tasks',\n {\n description:\n 'List your tasks, newest first. Optionally filter by closed status. Pass next_offset as offset to fetch another page; null means there are no more tasks.',\n inputSchema: z.strictObject({\n closed: z.boolean().optional().describe('False for open tasks, true for closed tasks.'),\n limit: z.int().min(1).max(100).default(20).describe('Maximum tasks per page (1–100).'),\n offset: z.int().min(0).default(0).describe('Number of tasks to skip.'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ closed, limit, offset }) => {\n try {\n let query = supabase\n .from('tasks')\n .select(taskFields)\n .order('created_at', { ascending: false })\n .order('id', { ascending: false })\n .range(offset, offset + limit)\n\n if (closed !== undefined) query = query.eq('closed', closed)\n\n const { data } = await query.throwOnError()\n return jsonResult({\n tasks: data.slice(0, limit),\n next_offset: data.length > limit ? offset + limit : null,\n })\n } catch (error) {\n return runtimeErrorResult(error)\n }\n }\n )\n\n server.registerTool(\n 'create_task',\n {\n description:\n 'Create an open task for yourself and return it. Each call creates a new task; do not retry blindly after a connection failure.',\n inputSchema: z.strictObject({ title: taskTitle }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: false,\n },\n },\n async ({ title }) => {\n try {\n const { data } = await supabase\n .from('tasks')\n .insert({ title })\n .select(taskFields)\n .single()\n .throwOnError()\n\n return jsonResult({ task: data })\n } catch (error) {\n return runtimeErrorResult(error)\n }\n }\n )\n\n server.registerTool(\n 'update_task',\n {\n description:\n 'Rename, close, or reopen one of your tasks and return it. Supply title, closed, or both. Fields you omit keep their current values.',\n inputSchema: z\n .strictObject({\n id: taskId,\n title: taskTitle.optional(),\n closed: z.boolean().optional().describe('True to close the task; false to reopen it.'),\n })\n .refine(({ title, closed }) => title !== undefined || closed !== undefined, {\n message: 'Supply title or closed to update a task.',\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ id, title, closed }) => {\n try {\n const changes = {\n ...(title !== undefined ? { title } : {}),\n ...(closed !== undefined ? { closed } : {}),\n }\n const { data } = await supabase\n .from('tasks')\n .update(changes)\n .eq('id', id)\n .select(taskFields)\n .maybeSingle()\n .throwOnError()\n\n return data ? jsonResult({ task: data }) : errorResult(taskNotFound)\n } catch (error) {\n return runtimeErrorResult(error)\n }\n }\n )\n\n server.registerTool(\n 'delete_task',\n {\n description:\n 'Permanently delete one of your tasks by ID. Use update_task with closed: true to keep a completed task instead.',\n inputSchema: z.strictObject({ id: taskId }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ id }) => {\n try {\n const { data } = await supabase\n .from('tasks')\n .delete()\n .eq('id', id)\n .select('id')\n .maybeSingle()\n .throwOnError()\n\n return data ? jsonResult({ deleted: true, id: data.id }) : errorResult(taskNotFound)\n } catch (error) {\n return runtimeErrorResult(error)\n }\n }\n )\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/tasks.ts" + "target": "~/supabase/functions/mcp/tools/tasks.ts" }, { "path": "registry/default/blocks/headless-app-tanstack/routes/_protected/agents.tsx", - "content": "/// \nimport { createFileRoute } from '@tanstack/react-router'\n\nimport { ConnectedAgents } from '@/registry/default/blocks/headless-app-tanstack/components/connected-agents'\n\nconst PRODUCT_NAME = import.meta.env.VITE_PRODUCT_NAME?.trim() || 'Your product'\nconst MCP_SERVER_URL = `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/mcp-server`\n\n// Nested under _protected, so the layout route redirects signed-out visitors\n// before this renders.\nexport const Route = createFileRoute('/_protected/agents')({\n component: AgentsPage,\n})\n\nfunction AgentsPage() {\n return (\n
\n \n
\n )\n}\n", + "content": "/// \nimport { createFileRoute } from '@tanstack/react-router'\n\nimport { ConnectedAgents } from '@/registry/default/blocks/headless-app-tanstack/components/connected-agents'\n\nconst PRODUCT_NAME = import.meta.env.VITE_PRODUCT_NAME?.trim() || 'Your product'\nconst MCP_SERVER_URL = `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/mcp`\n\n// Nested under _protected, so the layout route redirects signed-out visitors\n// before this renders.\nexport const Route = createFileRoute('/_protected/agents')({\n component: AgentsPage,\n})\n\nfunction AgentsPage() {\n return (\n
\n \n
\n )\n}\n", "type": "registry:file", "target": "routes/_protected/agents.tsx" }, @@ -43,7 +43,7 @@ }, { "path": "registry/default/blocks/headless-app-tanstack/supabase/config.toml", - "content": "# Supabase configuration for a headless app: password auth for the product\n# session, an OAuth 2.1 server for external agents, and the MCP server that both\n# call. Values below are for local development. Set production Auth URLs and\n# enable email confirmations before running `supabase config push`.\n\nproject_id = \"headless-app\"\n\n[api]\nenabled = true\nport = 54321\nschemas = [\"public\", \"graphql_public\"]\nextra_search_path = [\"public\", \"extensions\"]\nmax_rows = 1000\n\n[db]\nport = 54322\n# Used to diff ./schemas into a migration.\nshadow_port = 54320\nmajor_version = 17\n\n# One directory per schema, then one file per object, in dependency order.\n[db.migrations]\nschema_paths = [\n \"./schemas/*/tables/*.sql\",\n \"./schemas/*/views/*.sql\",\n \"./schemas/*/functions/*.sql\",\n]\n\n[studio]\nenabled = true\nport = 54323\n\n[inbucket]\nenabled = true\nport = 54324\n\n[auth]\nenabled = true\n# Origin that serves this app, including /oauth/consent. Use HTTPS in production.\nsite_url = \"http://localhost:3000\"\nadditional_redirect_urls = [\"http://localhost:3000/**\"]\njwt_expiry = 3600\nenable_signup = true\n\n[auth.email]\nenable_signup = true\n# Confirmations are off so local sign-ups can reach the app immediately.\nenable_confirmations = false\n\n# External MCP clients authorize here. Auth sends the user to\n# authorization_url_path, which the OAuth Consent block serves.\n[auth.oauth_server]\nenabled = true\nauthorization_url_path = \"/oauth/consent\"\n# Lets any compatible client register itself. Set to false to register clients\n# yourself.\nallow_dynamic_registration = true\n\n# The MCP server verifies user access tokens itself, so skip the gateway check.\n[functions.mcp-server]\nverify_jwt = false\n", + "content": "# Supabase configuration for a headless app: password auth for the product\n# session, an OAuth 2.1 server for external agents, and the MCP server that both\n# call. Values below are for local development. Set production Auth URLs and\n# enable email confirmations before running `supabase config push`.\n\nproject_id = \"headless-app\"\n\n[api]\nenabled = true\nport = 54321\nschemas = [\"public\", \"graphql_public\"]\nextra_search_path = [\"public\", \"extensions\"]\nmax_rows = 1000\n\n[db]\nport = 54322\n# Used to diff ./schemas into a migration.\nshadow_port = 54320\nmajor_version = 17\n\n# One directory per schema, then one file per object, in dependency order.\n[db.migrations]\nschema_paths = [\n \"./schemas/*/tables/*.sql\",\n \"./schemas/*/views/*.sql\",\n \"./schemas/*/functions/*.sql\",\n]\n\n[studio]\nenabled = true\nport = 54323\n\n[inbucket]\nenabled = true\nport = 54324\n\n[auth]\nenabled = true\n# Origin that serves this app, including /oauth/consent. Use HTTPS in production.\nsite_url = \"http://localhost:3000\"\nadditional_redirect_urls = [\"http://localhost:3000/**\"]\njwt_expiry = 3600\nenable_signup = true\n\n[auth.email]\nenable_signup = true\n# Confirmations are off so local sign-ups can reach the app immediately.\nenable_confirmations = false\n\n# External MCP clients authorize here. Auth sends the user to\n# authorization_url_path, which the OAuth Consent block serves.\n[auth.oauth_server]\nenabled = true\nauthorization_url_path = \"/oauth/consent\"\n# Lets any compatible client register itself. Set to false to register clients\n# yourself.\nallow_dynamic_registration = true\n\n# The MCP server verifies user access tokens itself, so skip the gateway check.\n[functions.mcp]\nverify_jwt = false\n", "type": "registry:file", "target": "~/supabase/config.toml" }, @@ -54,46 +54,46 @@ "target": "~/supabase/schemas/public/tables/tasks.sql" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/index.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/index.ts", "content": "import 'jsr:@supabase/functions-js@2.108.2/edge-runtime.d.ts'\n\nimport { createMcpHandler, McpServer } from 'npm:@modelcontextprotocol/server@2.0.0'\nimport { pipeline } from 'npm:@supabase/middleware@0.5.0'\nimport {\n withOAuthProtectedResource,\n withSupabase,\n type SupabaseContext,\n} from 'npm:@supabase/server@1.6.0'\n\nimport { registerTools, type ToolContext } from './tools/index.ts'\n\n// An MCP server as a single Supabase Edge Function, composed as a pipeline:\n//\n// withOAuthProtectedResource OAuth discovery for external MCP clients. Runs\n// before the auth gate so unauthenticated clients\n// can fetch the RFC 9728 metadata, and adds the\n// WWW-Authenticate challenge to the gate's 401.\n// withSupabase Verifies the user access token and builds an\n// RLS-scoped client, so both embedded product\n// agents and external OAuth clients act as the\n// signed-in user.\n// handleMcp MCP transport and tools (./tools/index.ts).\n//\n// On Supabase Edge Functions the public URLs in the OAuth metadata are derived\n// automatically, locally and hosted. Off Edge Functions, pass `resourceServer`\n// and `authorizationServer` to withOAuthProtectedResource.\n\nfunction readTextEnv(name: string, fallback: string): string {\n return Deno.env.get(name)?.trim() || fallback\n}\n\nconst SERVER_NAME = readTextEnv('MCP_SERVER_NAME', 'supabase-mcp')\nconst SERVER_DESCRIPTION = readTextEnv(\n 'MCP_SERVER_DESCRIPTION',\n 'MCP access to this Supabase project for the signed-in user.'\n)\n\nconst SERVER_INSTRUCTIONS =\n `${SERVER_DESCRIPTION} ` +\n 'Every tool runs as the signed-in Supabase user, so role grants and Row Level Security apply. ' +\n \"Call tools/list to discover what this project exposes, and read a tool's description and \" +\n 'annotations before calling it — some tools have side effects.'\n\nconst CORS_HEADERS: Record = {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers':\n 'Authorization, Content-Type, Accept, Mcp-Protocol-Version, Mcp-Session-Id, Mcp-Method, Mcp-Name',\n 'Access-Control-Expose-Headers': 'WWW-Authenticate, Mcp-Session-Id',\n}\n\nfunction createServer(context: ToolContext): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: '1.0.0' },\n { instructions: SERVER_INSTRUCTIONS }\n )\n\n registerTools(server, context)\n return server\n}\n\nasync function handleMcp(request: Request, ctx: SupabaseContext): Promise {\n // The server and its tools are bound to this caller for exactly one request.\n const handler = createMcpHandler(\n () =>\n createServer({\n supabase: ctx.supabase,\n // auth: 'user' guarantees both claim shapes before this handler runs.\n userClaims: ctx.userClaims!,\n jwtClaims: ctx.jwtClaims!,\n }),\n { onerror: (error) => console.error('MCP request failed', error) }\n )\n\n return handler.fetch(request)\n}\n\n// The handler is passed inline so TypeScript infers its context from the entries.\n// Passing `handleMcp` directly collapses the inferred context to `object`.\nDeno.serve(\n pipeline(\n [withOAuthProtectedResource(), withSupabase({ auth: 'user', cors: { headers: CORS_HEADERS } })],\n (request, ctx) => handleMcp(request, ctx)\n )\n)\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/index.ts" + "target": "~/supabase/functions/mcp/index.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.json", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.json", "content": "{\n \"nodeModulesDir\": \"none\",\n \"compilerOptions\": {\n \"strict\": true\n },\n \"tasks\": {\n \"check\": \"deno check index.ts\"\n }\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.json" + "target": "~/supabase/functions/mcp/deno.json" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.lock", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.lock", "content": "{\n \"version\": \"5\",\n \"specifiers\": {\n \"jsr:@supabase/functions-js@2.108.2\": \"2.108.2\",\n \"npm:@modelcontextprotocol/server@2.0.0\": \"2.0.0\",\n \"npm:@supabase/middleware@0.5.0\": \"0.5.0\",\n \"npm:@supabase/server@1.6.0\": \"1.6.0_@supabase+supabase-js@2.108.2\",\n \"npm:@supabase/supabase-js@2.108.2\": \"2.108.2\",\n \"npm:openai@^4.52.5\": \"4.104.0_zod@4.4.3\",\n \"npm:zod@4.4.3\": \"4.4.3\"\n },\n \"jsr\": {\n \"@supabase/functions-js@2.108.2\": {\n \"integrity\": \"39665d68e1cb721b0714ed1f55c075fba16f8d992672b51458660b3c1ec77c8f\",\n \"dependencies\": [\n \"npm:openai\"\n ]\n }\n },\n \"npm\": {\n \"@modelcontextprotocol/core@2.0.0\": {\n \"integrity\": \"sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==\",\n \"dependencies\": [\n \"zod\"\n ]\n },\n \"@modelcontextprotocol/server@2.0.0\": {\n \"integrity\": \"sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==\",\n \"dependencies\": [\n \"@modelcontextprotocol/core\",\n \"zod\"\n ]\n },\n \"@supabase/auth-js@2.108.2\": {\n \"integrity\": \"sha512-tNaQmBgodDZwgB40mRwVbxFy8IDYwjdpcZ0BYrWiwlULCSQoJj4QoG4zgJT7QRPXcqipefNOzvO/qAu4dF98ag==\",\n \"dependencies\": [\n \"tslib\"\n ]\n },\n \"@supabase/functions-js@2.108.2\": {\n \"integrity\": \"sha512-RNUX8EiBy3iLwAX19jtRzLyePnl11/fHcgwDHLnpKcDSXt/5qBnh3LUwAtIjT21Q66QsmNUR2esrHziLCpNubw==\",\n \"dependencies\": [\n \"tslib\"\n ]\n },\n \"@supabase/middleware@0.5.0\": {\n \"integrity\": \"sha512-OjukUo+5p14zxTuylf2zVg1hZCHWKLO6VrZhtVeQQw09yrVo3GAduvFJPiFDhTAz/Du1ZfEzAR+s6aRAWD5wzQ==\",\n \"dependencies\": [\n \"std-env\"\n ]\n },\n \"@supabase/phoenix@0.4.5\": {\n \"integrity\": \"sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==\"\n },\n \"@supabase/postgrest-js@2.108.2\": {\n \"integrity\": \"sha512-GQ28/Y8hk3CFmkb3kXH1h/AQx6JIYSQfO0CJMRVBcEKZoNy6C45cXAZ4fcJvRC5Id0cs6xnkUV0+c0rIocigsw==\",\n \"dependencies\": [\n \"tslib\"\n ]\n },\n \"@supabase/realtime-js@2.108.2\": {\n \"integrity\": \"sha512-aAGxCSUemZvQIibnCdvNvgaKib28I4rfrNjKbQ9cG1uBLwUsI7hVpGXgEbypCCDhLjQlDTAiJlu7rgljYUT73g==\",\n \"dependencies\": [\n \"@supabase/phoenix\",\n \"tslib\"\n ]\n },\n \"@supabase/server@1.6.0_@supabase+supabase-js@2.108.2\": {\n \"integrity\": \"sha512-LtUkzUqUGip6I2+kvSmA04u3s+npwLseer2yomBnUdQ1zyJPtlgjAqbwsNuGC1DT/FHi1BvvJluI7dZVV2XdTw==\",\n \"dependencies\": [\n \"@supabase/middleware\",\n \"@supabase/supabase-js\",\n \"jose\"\n ]\n },\n \"@supabase/storage-js@2.108.2\": {\n \"integrity\": \"sha512-TVZPQxXGxY2+A6yTtm77zUHsh70lBhYUEaJL8RQC+BghcX/ygiMG/rmXrNVBce30/WAeNPa8FiG8HbqlGeV05g==\",\n \"dependencies\": [\n \"iceberg-js\",\n \"tslib\"\n ]\n },\n \"@supabase/supabase-js@2.108.2\": {\n \"integrity\": \"sha512-hFhnPveb5JQg4a0QYicM0swT253YHMdfeRAl2BKHOlI5VAzuHxUGSr8RbwNLYNPauWOgQMS1H8sz8bvYlgwUfQ==\",\n \"dependencies\": [\n \"@supabase/auth-js\",\n \"@supabase/functions-js\",\n \"@supabase/postgrest-js\",\n \"@supabase/realtime-js\",\n \"@supabase/storage-js\"\n ]\n },\n \"@types/node-fetch@2.6.13\": {\n \"integrity\": \"sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==\",\n \"dependencies\": [\n \"@types/node\",\n \"form-data\"\n ]\n },\n \"@types/node@18.19.130\": {\n \"integrity\": \"sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==\",\n \"dependencies\": [\n \"undici-types\"\n ]\n },\n \"abort-controller@3.0.0\": {\n \"integrity\": \"sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==\",\n \"dependencies\": [\n \"event-target-shim\"\n ]\n },\n \"agentkeepalive@4.6.0\": {\n \"integrity\": \"sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==\",\n \"dependencies\": [\n \"humanize-ms\"\n ]\n },\n \"asynckit@0.4.0\": {\n \"integrity\": \"sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==\"\n },\n \"call-bind-apply-helpers@1.0.2\": {\n \"integrity\": \"sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==\",\n \"dependencies\": [\n \"es-errors\",\n \"function-bind\"\n ]\n },\n \"combined-stream@1.0.8\": {\n \"integrity\": \"sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==\",\n \"dependencies\": [\n \"delayed-stream\"\n ]\n },\n \"delayed-stream@1.0.0\": {\n \"integrity\": \"sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==\"\n },\n \"dunder-proto@1.0.1\": {\n \"integrity\": \"sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==\",\n \"dependencies\": [\n \"call-bind-apply-helpers\",\n \"es-errors\",\n \"gopd\"\n ]\n },\n \"es-define-property@1.0.1\": {\n \"integrity\": \"sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==\"\n },\n \"es-errors@1.3.0\": {\n \"integrity\": \"sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==\"\n },\n \"es-object-atoms@1.1.2\": {\n \"integrity\": \"sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==\",\n \"dependencies\": [\n \"es-errors\"\n ]\n },\n \"es-set-tostringtag@2.1.0\": {\n \"integrity\": \"sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==\",\n \"dependencies\": [\n \"es-errors\",\n \"get-intrinsic\",\n \"has-tostringtag\",\n \"hasown\"\n ]\n },\n \"event-target-shim@5.0.1\": {\n \"integrity\": \"sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==\"\n },\n \"form-data-encoder@1.7.2\": {\n \"integrity\": \"sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==\"\n },\n \"form-data@4.0.6\": {\n \"integrity\": \"sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==\",\n \"dependencies\": [\n \"asynckit\",\n \"combined-stream\",\n \"es-set-tostringtag\",\n \"hasown\",\n \"mime-types\"\n ]\n },\n \"formdata-node@4.4.1\": {\n \"integrity\": \"sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==\",\n \"dependencies\": [\n \"node-domexception\",\n \"web-streams-polyfill\"\n ]\n },\n \"function-bind@1.1.2\": {\n \"integrity\": \"sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==\"\n },\n \"get-intrinsic@1.3.0\": {\n \"integrity\": \"sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==\",\n \"dependencies\": [\n \"call-bind-apply-helpers\",\n \"es-define-property\",\n \"es-errors\",\n \"es-object-atoms\",\n \"function-bind\",\n \"get-proto\",\n \"gopd\",\n \"has-symbols\",\n \"hasown\",\n \"math-intrinsics\"\n ]\n },\n \"get-proto@1.0.1\": {\n \"integrity\": \"sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==\",\n \"dependencies\": [\n \"dunder-proto\",\n \"es-object-atoms\"\n ]\n },\n \"gopd@1.2.0\": {\n \"integrity\": \"sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==\"\n },\n \"has-symbols@1.1.0\": {\n \"integrity\": \"sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==\"\n },\n \"has-tostringtag@1.0.2\": {\n \"integrity\": \"sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==\",\n \"dependencies\": [\n \"has-symbols\"\n ]\n },\n \"hasown@2.0.4\": {\n \"integrity\": \"sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==\",\n \"dependencies\": [\n \"function-bind\"\n ]\n },\n \"humanize-ms@1.2.1\": {\n \"integrity\": \"sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==\",\n \"dependencies\": [\n \"ms\"\n ]\n },\n \"iceberg-js@0.8.1\": {\n \"integrity\": \"sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==\"\n },\n \"jose@6.2.10\": {\n \"integrity\": \"sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==\"\n },\n \"math-intrinsics@1.1.0\": {\n \"integrity\": \"sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==\"\n },\n \"mime-db@1.52.0\": {\n \"integrity\": \"sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==\"\n },\n \"mime-types@2.1.35\": {\n \"integrity\": \"sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==\",\n \"dependencies\": [\n \"mime-db\"\n ]\n },\n \"ms@2.1.3\": {\n \"integrity\": \"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==\"\n },\n \"node-domexception@1.0.0\": {\n \"integrity\": \"sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==\",\n \"deprecated\": true\n },\n \"node-fetch@2.7.0\": {\n \"integrity\": \"sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==\",\n \"dependencies\": [\n \"whatwg-url\"\n ]\n },\n \"openai@4.104.0_zod@4.4.3\": {\n \"integrity\": \"sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==\",\n \"dependencies\": [\n \"@types/node\",\n \"@types/node-fetch\",\n \"abort-controller\",\n \"agentkeepalive\",\n \"form-data-encoder\",\n \"formdata-node\",\n \"node-fetch\",\n \"zod\"\n ],\n \"optionalPeers\": [\n \"zod\"\n ],\n \"bin\": true\n },\n \"std-env@4.2.0\": {\n \"integrity\": \"sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==\"\n },\n \"tr46@0.0.3\": {\n \"integrity\": \"sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==\"\n },\n \"tslib@2.8.1\": {\n \"integrity\": \"sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==\"\n },\n \"undici-types@5.26.5\": {\n \"integrity\": \"sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==\"\n },\n \"web-streams-polyfill@4.0.0-beta.3\": {\n \"integrity\": \"sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==\"\n },\n \"webidl-conversions@3.0.1\": {\n \"integrity\": \"sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==\"\n },\n \"whatwg-url@5.0.0\": {\n \"integrity\": \"sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==\",\n \"dependencies\": [\n \"tr46\",\n \"webidl-conversions\"\n ]\n },\n \"zod@4.4.3\": {\n \"integrity\": \"sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==\"\n }\n }\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.lock" + "target": "~/supabase/functions/mcp/deno.lock" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/.env.example", - "content": "# Copy this file to supabase/functions/.env before serving locally:\n# cp supabase/functions/mcp-server/.env.example supabase/functions/.env\n# supabase functions serve mcp-server --env-file supabase/functions/.env\n\n# Deploy these values after linking your project:\n# supabase secrets set --env-file supabase/functions/.env\n# Supabase provides the project URL and API keys automatically.\n\n# Keep the protocol-level server name short and project-specific.\nMCP_SERVER_NAME=supabase-mcp\nMCP_SERVER_DESCRIPTION=\"MCP access to this Supabase project for the signed-in user.\"\n", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/.env.example", + "content": "# Copy this file to supabase/functions/.env before serving locally:\n# cp supabase/functions/mcp/.env.example supabase/functions/.env\n# supabase functions serve mcp --env-file supabase/functions/.env\n\n# Deploy these values after linking your project:\n# supabase secrets set --env-file supabase/functions/.env\n# Supabase provides the project URL and API keys automatically.\n\n# Keep the protocol-level server name short and project-specific.\nMCP_SERVER_NAME=supabase-mcp\nMCP_SERVER_DESCRIPTION=\"MCP access to this Supabase project for the signed-in user.\"\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/.env.example" + "target": "~/supabase/functions/mcp/.env.example" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/types.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/types.ts", "content": "import type { SupabaseContext } from 'npm:@supabase/server@1.6.0'\nimport type { SupabaseClient } from 'npm:@supabase/supabase-js@2.108.2'\n\n// Only expose the user-scoped client and verified identity to tools. Keeping\n// supabaseAdmin out of this type makes bypassing RLS an explicit design choice.\nexport type ToolContext = {\n supabase: SupabaseClient\n userClaims: NonNullable\n jwtClaims: NonNullable\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/types.ts" + "target": "~/supabase/functions/mcp/tools/types.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/result.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/result.ts", "content": "import type { CallToolResult } from 'npm:@modelcontextprotocol/server@2.0.0'\n\n// Shared helpers for building MCP tool results, so every tool returns the same\n// shape and signals failure the same way.\n\n/**\n * A successful structured result with a JSON text fallback for older clients.\n */\nexport function jsonResult(value: unknown): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(value) ?? 'null' }],\n structuredContent: value ?? null,\n }\n}\n\n/**\n * A failed result. The message goes back to the model so it can correct itself,\n * so keep it actionable — and free of credentials, claims, and stack traces.\n */\nexport function errorResult(message: string): CallToolResult {\n return {\n isError: true,\n content: [{ type: 'text', text: message }],\n }\n}\n\nfunction readString(value: unknown, key: string): string | null {\n if (!value || typeof value !== 'object' || !(key in value)) return null\n const property = (value as Record)[key]\n return typeof property === 'string' && property ? property : null\n}\n\n/**\n * Turn an unknown thrown value into a safe MCP error. Supabase API errors often\n * carry a `code` and `hint`, both of which help a model fix its next call.\n */\nexport function runtimeErrorResult(error: unknown): CallToolResult {\n const message = error instanceof Error ? error.message : String(error)\n const code = readString(error, 'code')\n const hint = readString(error, 'hint')\n\n return errorResult(\n [code ? `[${code}]` : null, message, hint ? `Hint: ${hint}` : null].filter(Boolean).join(' ')\n )\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/result.ts" + "target": "~/supabase/functions/mcp/tools/result.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/whoami.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/whoami.ts", "content": "import type { McpServer } from 'npm:@modelcontextprotocol/server@2.0.0'\n\nimport { jsonResult } from './result.ts'\nimport type { ToolContext } from './types.ts'\n\n// Answers from verified claims, demonstrating that every tool runs as the\n// signed-in user. client_id is present for OAuth tokens and null for ordinary\n// product sessions.\nexport function registerWhoamiTool(\n server: McpServer,\n { userClaims, jwtClaims }: ToolContext\n): void {\n const clientId =\n typeof jwtClaims?.client_id === 'string' && jwtClaims.client_id ? jwtClaims.client_id : null\n\n server.registerTool(\n 'whoami',\n {\n description: \"Return the signed-in user's identity and OAuth client id, when present.\",\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n },\n () =>\n jsonResult({\n id: userClaims.id,\n email: userClaims.email ?? null,\n role: userClaims.role ?? null,\n client_id: clientId,\n })\n )\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/whoami.ts" + "target": "~/supabase/functions/mcp/tools/whoami.ts" }, { "path": "registry/default/clients/tanstack/lib/supabase/client.ts", diff --git a/apps/ui-library/public/r/mcp-server.json b/apps/ui-library/public/r/mcp.json similarity index 92% rename from apps/ui-library/public/r/mcp-server.json rename to apps/ui-library/public/r/mcp.json index aa9bfb0c8b8..fa298ff1a53 100644 --- a/apps/ui-library/public/r/mcp-server.json +++ b/apps/ui-library/public/r/mcp.json @@ -1,56 +1,56 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "mcp-server", + "name": "mcp", "title": "MCP Server", "description": "Add a user-scoped MCP server to your product.", "files": [ { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/index.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/index.ts", "content": "import 'jsr:@supabase/functions-js@2.108.2/edge-runtime.d.ts'\n\nimport { createMcpHandler, McpServer } from 'npm:@modelcontextprotocol/server@2.0.0'\nimport { pipeline } from 'npm:@supabase/middleware@0.5.0'\nimport {\n withOAuthProtectedResource,\n withSupabase,\n type SupabaseContext,\n} from 'npm:@supabase/server@1.6.0'\n\nimport { registerTools, type ToolContext } from './tools/index.ts'\n\n// An MCP server as a single Supabase Edge Function, composed as a pipeline:\n//\n// withOAuthProtectedResource OAuth discovery for external MCP clients. Runs\n// before the auth gate so unauthenticated clients\n// can fetch the RFC 9728 metadata, and adds the\n// WWW-Authenticate challenge to the gate's 401.\n// withSupabase Verifies the user access token and builds an\n// RLS-scoped client, so both embedded product\n// agents and external OAuth clients act as the\n// signed-in user.\n// handleMcp MCP transport and tools (./tools/index.ts).\n//\n// On Supabase Edge Functions the public URLs in the OAuth metadata are derived\n// automatically, locally and hosted. Off Edge Functions, pass `resourceServer`\n// and `authorizationServer` to withOAuthProtectedResource.\n\nfunction readTextEnv(name: string, fallback: string): string {\n return Deno.env.get(name)?.trim() || fallback\n}\n\nconst SERVER_NAME = readTextEnv('MCP_SERVER_NAME', 'supabase-mcp')\nconst SERVER_DESCRIPTION = readTextEnv(\n 'MCP_SERVER_DESCRIPTION',\n 'MCP access to this Supabase project for the signed-in user.'\n)\n\nconst SERVER_INSTRUCTIONS =\n `${SERVER_DESCRIPTION} ` +\n 'Every tool runs as the signed-in Supabase user, so role grants and Row Level Security apply. ' +\n \"Call tools/list to discover what this project exposes, and read a tool's description and \" +\n 'annotations before calling it — some tools have side effects.'\n\nconst CORS_HEADERS: Record = {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers':\n 'Authorization, Content-Type, Accept, Mcp-Protocol-Version, Mcp-Session-Id, Mcp-Method, Mcp-Name',\n 'Access-Control-Expose-Headers': 'WWW-Authenticate, Mcp-Session-Id',\n}\n\nfunction createServer(context: ToolContext): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: '1.0.0' },\n { instructions: SERVER_INSTRUCTIONS }\n )\n\n registerTools(server, context)\n return server\n}\n\nasync function handleMcp(request: Request, ctx: SupabaseContext): Promise {\n // The server and its tools are bound to this caller for exactly one request.\n const handler = createMcpHandler(\n () =>\n createServer({\n supabase: ctx.supabase,\n // auth: 'user' guarantees both claim shapes before this handler runs.\n userClaims: ctx.userClaims!,\n jwtClaims: ctx.jwtClaims!,\n }),\n { onerror: (error) => console.error('MCP request failed', error) }\n )\n\n return handler.fetch(request)\n}\n\n// The handler is passed inline so TypeScript infers its context from the entries.\n// Passing `handleMcp` directly collapses the inferred context to `object`.\nDeno.serve(\n pipeline(\n [withOAuthProtectedResource(), withSupabase({ auth: 'user', cors: { headers: CORS_HEADERS } })],\n (request, ctx) => handleMcp(request, ctx)\n )\n)\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/index.ts" + "target": "~/supabase/functions/mcp/index.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.json", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.json", "content": "{\n \"nodeModulesDir\": \"none\",\n \"compilerOptions\": {\n \"strict\": true\n },\n \"tasks\": {\n \"check\": \"deno check index.ts\"\n }\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.json" + "target": "~/supabase/functions/mcp/deno.json" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.lock", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.lock", "content": "{\n \"version\": \"5\",\n \"specifiers\": {\n \"jsr:@supabase/functions-js@2.108.2\": \"2.108.2\",\n \"npm:@modelcontextprotocol/server@2.0.0\": \"2.0.0\",\n \"npm:@supabase/middleware@0.5.0\": \"0.5.0\",\n \"npm:@supabase/server@1.6.0\": \"1.6.0_@supabase+supabase-js@2.108.2\",\n \"npm:@supabase/supabase-js@2.108.2\": \"2.108.2\",\n \"npm:openai@^4.52.5\": \"4.104.0_zod@4.4.3\",\n \"npm:zod@4.4.3\": \"4.4.3\"\n },\n \"jsr\": {\n \"@supabase/functions-js@2.108.2\": {\n \"integrity\": \"39665d68e1cb721b0714ed1f55c075fba16f8d992672b51458660b3c1ec77c8f\",\n \"dependencies\": [\n \"npm:openai\"\n ]\n }\n },\n \"npm\": {\n \"@modelcontextprotocol/core@2.0.0\": {\n \"integrity\": \"sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==\",\n \"dependencies\": [\n \"zod\"\n ]\n },\n \"@modelcontextprotocol/server@2.0.0\": {\n \"integrity\": \"sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==\",\n \"dependencies\": [\n \"@modelcontextprotocol/core\",\n \"zod\"\n ]\n },\n \"@supabase/auth-js@2.108.2\": {\n \"integrity\": \"sha512-tNaQmBgodDZwgB40mRwVbxFy8IDYwjdpcZ0BYrWiwlULCSQoJj4QoG4zgJT7QRPXcqipefNOzvO/qAu4dF98ag==\",\n \"dependencies\": [\n \"tslib\"\n ]\n },\n \"@supabase/functions-js@2.108.2\": {\n \"integrity\": \"sha512-RNUX8EiBy3iLwAX19jtRzLyePnl11/fHcgwDHLnpKcDSXt/5qBnh3LUwAtIjT21Q66QsmNUR2esrHziLCpNubw==\",\n \"dependencies\": [\n \"tslib\"\n ]\n },\n \"@supabase/middleware@0.5.0\": {\n \"integrity\": \"sha512-OjukUo+5p14zxTuylf2zVg1hZCHWKLO6VrZhtVeQQw09yrVo3GAduvFJPiFDhTAz/Du1ZfEzAR+s6aRAWD5wzQ==\",\n \"dependencies\": [\n \"std-env\"\n ]\n },\n \"@supabase/phoenix@0.4.5\": {\n \"integrity\": \"sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==\"\n },\n \"@supabase/postgrest-js@2.108.2\": {\n \"integrity\": \"sha512-GQ28/Y8hk3CFmkb3kXH1h/AQx6JIYSQfO0CJMRVBcEKZoNy6C45cXAZ4fcJvRC5Id0cs6xnkUV0+c0rIocigsw==\",\n \"dependencies\": [\n \"tslib\"\n ]\n },\n \"@supabase/realtime-js@2.108.2\": {\n \"integrity\": \"sha512-aAGxCSUemZvQIibnCdvNvgaKib28I4rfrNjKbQ9cG1uBLwUsI7hVpGXgEbypCCDhLjQlDTAiJlu7rgljYUT73g==\",\n \"dependencies\": [\n \"@supabase/phoenix\",\n \"tslib\"\n ]\n },\n \"@supabase/server@1.6.0_@supabase+supabase-js@2.108.2\": {\n \"integrity\": \"sha512-LtUkzUqUGip6I2+kvSmA04u3s+npwLseer2yomBnUdQ1zyJPtlgjAqbwsNuGC1DT/FHi1BvvJluI7dZVV2XdTw==\",\n \"dependencies\": [\n \"@supabase/middleware\",\n \"@supabase/supabase-js\",\n \"jose\"\n ]\n },\n \"@supabase/storage-js@2.108.2\": {\n \"integrity\": \"sha512-TVZPQxXGxY2+A6yTtm77zUHsh70lBhYUEaJL8RQC+BghcX/ygiMG/rmXrNVBce30/WAeNPa8FiG8HbqlGeV05g==\",\n \"dependencies\": [\n \"iceberg-js\",\n \"tslib\"\n ]\n },\n \"@supabase/supabase-js@2.108.2\": {\n \"integrity\": \"sha512-hFhnPveb5JQg4a0QYicM0swT253YHMdfeRAl2BKHOlI5VAzuHxUGSr8RbwNLYNPauWOgQMS1H8sz8bvYlgwUfQ==\",\n \"dependencies\": [\n \"@supabase/auth-js\",\n \"@supabase/functions-js\",\n \"@supabase/postgrest-js\",\n \"@supabase/realtime-js\",\n \"@supabase/storage-js\"\n ]\n },\n \"@types/node-fetch@2.6.13\": {\n \"integrity\": \"sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==\",\n \"dependencies\": [\n \"@types/node\",\n \"form-data\"\n ]\n },\n \"@types/node@18.19.130\": {\n \"integrity\": \"sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==\",\n \"dependencies\": [\n \"undici-types\"\n ]\n },\n \"abort-controller@3.0.0\": {\n \"integrity\": \"sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==\",\n \"dependencies\": [\n \"event-target-shim\"\n ]\n },\n \"agentkeepalive@4.6.0\": {\n \"integrity\": \"sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==\",\n \"dependencies\": [\n \"humanize-ms\"\n ]\n },\n \"asynckit@0.4.0\": {\n \"integrity\": \"sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==\"\n },\n \"call-bind-apply-helpers@1.0.2\": {\n \"integrity\": \"sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==\",\n \"dependencies\": [\n \"es-errors\",\n \"function-bind\"\n ]\n },\n \"combined-stream@1.0.8\": {\n \"integrity\": \"sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==\",\n \"dependencies\": [\n \"delayed-stream\"\n ]\n },\n \"delayed-stream@1.0.0\": {\n \"integrity\": \"sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==\"\n },\n \"dunder-proto@1.0.1\": {\n \"integrity\": \"sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==\",\n \"dependencies\": [\n \"call-bind-apply-helpers\",\n \"es-errors\",\n \"gopd\"\n ]\n },\n \"es-define-property@1.0.1\": {\n \"integrity\": \"sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==\"\n },\n \"es-errors@1.3.0\": {\n \"integrity\": \"sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==\"\n },\n \"es-object-atoms@1.1.2\": {\n \"integrity\": \"sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==\",\n \"dependencies\": [\n \"es-errors\"\n ]\n },\n \"es-set-tostringtag@2.1.0\": {\n \"integrity\": \"sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==\",\n \"dependencies\": [\n \"es-errors\",\n \"get-intrinsic\",\n \"has-tostringtag\",\n \"hasown\"\n ]\n },\n \"event-target-shim@5.0.1\": {\n \"integrity\": \"sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==\"\n },\n \"form-data-encoder@1.7.2\": {\n \"integrity\": \"sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==\"\n },\n \"form-data@4.0.6\": {\n \"integrity\": \"sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==\",\n \"dependencies\": [\n \"asynckit\",\n \"combined-stream\",\n \"es-set-tostringtag\",\n \"hasown\",\n \"mime-types\"\n ]\n },\n \"formdata-node@4.4.1\": {\n \"integrity\": \"sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==\",\n \"dependencies\": [\n \"node-domexception\",\n \"web-streams-polyfill\"\n ]\n },\n \"function-bind@1.1.2\": {\n \"integrity\": \"sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==\"\n },\n \"get-intrinsic@1.3.0\": {\n \"integrity\": \"sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==\",\n \"dependencies\": [\n \"call-bind-apply-helpers\",\n \"es-define-property\",\n \"es-errors\",\n \"es-object-atoms\",\n \"function-bind\",\n \"get-proto\",\n \"gopd\",\n \"has-symbols\",\n \"hasown\",\n \"math-intrinsics\"\n ]\n },\n \"get-proto@1.0.1\": {\n \"integrity\": \"sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==\",\n \"dependencies\": [\n \"dunder-proto\",\n \"es-object-atoms\"\n ]\n },\n \"gopd@1.2.0\": {\n \"integrity\": \"sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==\"\n },\n \"has-symbols@1.1.0\": {\n \"integrity\": \"sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==\"\n },\n \"has-tostringtag@1.0.2\": {\n \"integrity\": \"sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==\",\n \"dependencies\": [\n \"has-symbols\"\n ]\n },\n \"hasown@2.0.4\": {\n \"integrity\": \"sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==\",\n \"dependencies\": [\n \"function-bind\"\n ]\n },\n \"humanize-ms@1.2.1\": {\n \"integrity\": \"sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==\",\n \"dependencies\": [\n \"ms\"\n ]\n },\n \"iceberg-js@0.8.1\": {\n \"integrity\": \"sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==\"\n },\n \"jose@6.2.10\": {\n \"integrity\": \"sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==\"\n },\n \"math-intrinsics@1.1.0\": {\n \"integrity\": \"sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==\"\n },\n \"mime-db@1.52.0\": {\n \"integrity\": \"sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==\"\n },\n \"mime-types@2.1.35\": {\n \"integrity\": \"sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==\",\n \"dependencies\": [\n \"mime-db\"\n ]\n },\n \"ms@2.1.3\": {\n \"integrity\": \"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==\"\n },\n \"node-domexception@1.0.0\": {\n \"integrity\": \"sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==\",\n \"deprecated\": true\n },\n \"node-fetch@2.7.0\": {\n \"integrity\": \"sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==\",\n \"dependencies\": [\n \"whatwg-url\"\n ]\n },\n \"openai@4.104.0_zod@4.4.3\": {\n \"integrity\": \"sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==\",\n \"dependencies\": [\n \"@types/node\",\n \"@types/node-fetch\",\n \"abort-controller\",\n \"agentkeepalive\",\n \"form-data-encoder\",\n \"formdata-node\",\n \"node-fetch\",\n \"zod\"\n ],\n \"optionalPeers\": [\n \"zod\"\n ],\n \"bin\": true\n },\n \"std-env@4.2.0\": {\n \"integrity\": \"sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==\"\n },\n \"tr46@0.0.3\": {\n \"integrity\": \"sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==\"\n },\n \"tslib@2.8.1\": {\n \"integrity\": \"sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==\"\n },\n \"undici-types@5.26.5\": {\n \"integrity\": \"sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==\"\n },\n \"web-streams-polyfill@4.0.0-beta.3\": {\n \"integrity\": \"sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==\"\n },\n \"webidl-conversions@3.0.1\": {\n \"integrity\": \"sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==\"\n },\n \"whatwg-url@5.0.0\": {\n \"integrity\": \"sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==\",\n \"dependencies\": [\n \"tr46\",\n \"webidl-conversions\"\n ]\n },\n \"zod@4.4.3\": {\n \"integrity\": \"sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==\"\n }\n }\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.lock" + "target": "~/supabase/functions/mcp/deno.lock" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/.env.example", - "content": "# Copy this file to supabase/functions/.env before serving locally:\n# cp supabase/functions/mcp-server/.env.example supabase/functions/.env\n# supabase functions serve mcp-server --env-file supabase/functions/.env\n\n# Deploy these values after linking your project:\n# supabase secrets set --env-file supabase/functions/.env\n# Supabase provides the project URL and API keys automatically.\n\n# Keep the protocol-level server name short and project-specific.\nMCP_SERVER_NAME=supabase-mcp\nMCP_SERVER_DESCRIPTION=\"MCP access to this Supabase project for the signed-in user.\"\n", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/.env.example", + "content": "# Copy this file to supabase/functions/.env before serving locally:\n# cp supabase/functions/mcp/.env.example supabase/functions/.env\n# supabase functions serve mcp --env-file supabase/functions/.env\n\n# Deploy these values after linking your project:\n# supabase secrets set --env-file supabase/functions/.env\n# Supabase provides the project URL and API keys automatically.\n\n# Keep the protocol-level server name short and project-specific.\nMCP_SERVER_NAME=supabase-mcp\nMCP_SERVER_DESCRIPTION=\"MCP access to this Supabase project for the signed-in user.\"\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/.env.example" + "target": "~/supabase/functions/mcp/.env.example" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/types.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/types.ts", "content": "import type { SupabaseContext } from 'npm:@supabase/server@1.6.0'\nimport type { SupabaseClient } from 'npm:@supabase/supabase-js@2.108.2'\n\n// Only expose the user-scoped client and verified identity to tools. Keeping\n// supabaseAdmin out of this type makes bypassing RLS an explicit design choice.\nexport type ToolContext = {\n supabase: SupabaseClient\n userClaims: NonNullable\n jwtClaims: NonNullable\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/types.ts" + "target": "~/supabase/functions/mcp/tools/types.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/result.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/result.ts", "content": "import type { CallToolResult } from 'npm:@modelcontextprotocol/server@2.0.0'\n\n// Shared helpers for building MCP tool results, so every tool returns the same\n// shape and signals failure the same way.\n\n/**\n * A successful structured result with a JSON text fallback for older clients.\n */\nexport function jsonResult(value: unknown): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(value) ?? 'null' }],\n structuredContent: value ?? null,\n }\n}\n\n/**\n * A failed result. The message goes back to the model so it can correct itself,\n * so keep it actionable — and free of credentials, claims, and stack traces.\n */\nexport function errorResult(message: string): CallToolResult {\n return {\n isError: true,\n content: [{ type: 'text', text: message }],\n }\n}\n\nfunction readString(value: unknown, key: string): string | null {\n if (!value || typeof value !== 'object' || !(key in value)) return null\n const property = (value as Record)[key]\n return typeof property === 'string' && property ? property : null\n}\n\n/**\n * Turn an unknown thrown value into a safe MCP error. Supabase API errors often\n * carry a `code` and `hint`, both of which help a model fix its next call.\n */\nexport function runtimeErrorResult(error: unknown): CallToolResult {\n const message = error instanceof Error ? error.message : String(error)\n const code = readString(error, 'code')\n const hint = readString(error, 'hint')\n\n return errorResult(\n [code ? `[${code}]` : null, message, hint ? `Hint: ${hint}` : null].filter(Boolean).join(' ')\n )\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/result.ts" + "target": "~/supabase/functions/mcp/tools/result.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/whoami.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/whoami.ts", "content": "import type { McpServer } from 'npm:@modelcontextprotocol/server@2.0.0'\n\nimport { jsonResult } from './result.ts'\nimport type { ToolContext } from './types.ts'\n\n// Answers from verified claims, demonstrating that every tool runs as the\n// signed-in user. client_id is present for OAuth tokens and null for ordinary\n// product sessions.\nexport function registerWhoamiTool(\n server: McpServer,\n { userClaims, jwtClaims }: ToolContext\n): void {\n const clientId =\n typeof jwtClaims?.client_id === 'string' && jwtClaims.client_id ? jwtClaims.client_id : null\n\n server.registerTool(\n 'whoami',\n {\n description: \"Return the signed-in user's identity and OAuth client id, when present.\",\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n },\n () =>\n jsonResult({\n id: userClaims.id,\n email: userClaims.email ?? null,\n role: userClaims.role ?? null,\n client_id: clientId,\n })\n )\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/whoami.ts" + "target": "~/supabase/functions/mcp/tools/whoami.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/index.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/index.ts", "content": "import type { McpServer } from 'npm:@modelcontextprotocol/server@2.0.0'\n\nimport type { ToolContext } from './types.ts'\nimport { registerWhoamiTool } from './whoami.ts'\n\nexport type { ToolContext } from './types.ts'\n\n// The one composition point for this server. Add one registration call for\n// each tool module; the MCP SDK rejects duplicate protocol tool names.\nexport function registerTools(server: McpServer, context: ToolContext): void {\n registerWhoamiTool(server, context)\n}\n", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/index.ts" + "target": "~/supabase/functions/mcp/tools/index.ts" } ], "docs": "Disable gateway JWT verification, then deploy the Edge Function. A trusted product backend can call it with the signed-in user's access token. For external clients, install the [OAuth Consent block](https://supabase.com/library/docs/nextjs/oauth-consent), enable OAuth and dynamic registration, and set the Auth Site URL to the consent app. Every call runs through the user's RLS-scoped client. OAuth tokens include `client_id`; product sessions do not, so define policies for both paths. See [MCP authentication](https://supabase.com/docs/guides/auth/oauth-server/mcp-authentication) and [token security](https://supabase.com/docs/guides/auth/oauth-server/token-security).", diff --git a/apps/ui-library/public/r/registry.json b/apps/ui-library/public/r/registry.json index e3631303662..d0d9f2f9fe1 100644 --- a/apps/ui-library/public/r/registry.json +++ b/apps/ui-library/public/r/registry.json @@ -1747,51 +1747,51 @@ ] }, { - "name": "mcp-server", + "name": "mcp", "type": "registry:item", "title": "MCP Server", "description": "Add a user-scoped MCP server to your product.", "docs": "Disable gateway JWT verification, then deploy the Edge Function. A trusted product backend can call it with the signed-in user's access token. For external clients, install the [OAuth Consent block](https://supabase.com/library/docs/nextjs/oauth-consent), enable OAuth and dynamic registration, and set the Auth Site URL to the consent app. Every call runs through the user's RLS-scoped client. OAuth tokens include `client_id`; product sessions do not, so define policies for both paths. See [MCP authentication](https://supabase.com/docs/guides/auth/oauth-server/mcp-authentication) and [token security](https://supabase.com/docs/guides/auth/oauth-server/token-security).", "files": [ { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/index.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/index.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/index.ts" + "target": "~/supabase/functions/mcp/index.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.json", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.json", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.json" + "target": "~/supabase/functions/mcp/deno.json" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.lock", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.lock", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.lock" + "target": "~/supabase/functions/mcp/deno.lock" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/.env.example", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/.env.example", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/.env.example" + "target": "~/supabase/functions/mcp/.env.example" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/types.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/types.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/types.ts" + "target": "~/supabase/functions/mcp/tools/types.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/result.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/result.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/result.ts" + "target": "~/supabase/functions/mcp/tools/result.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/whoami.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/whoami.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/whoami.ts" + "target": "~/supabase/functions/mcp/tools/whoami.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/index.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/index.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/index.ts" + "target": "~/supabase/functions/mcp/tools/index.ts" } ] }, @@ -1813,14 +1813,14 @@ ], "files": [ { - "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/index.ts", + "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/index.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/index.ts" + "target": "~/supabase/functions/mcp/tools/index.ts" }, { - "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/tasks.ts", + "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/tasks.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/tasks.ts" + "target": "~/supabase/functions/mcp/tools/tasks.ts" }, { "path": "registry/default/blocks/headless-app-tanstack/routes/_protected/agents.tsx", @@ -1846,39 +1846,39 @@ "target": "~/supabase/schemas/public/tables/tasks.sql" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/index.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/index.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/index.ts" + "target": "~/supabase/functions/mcp/index.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.json", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.json", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.json" + "target": "~/supabase/functions/mcp/deno.json" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.lock", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.lock", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.lock" + "target": "~/supabase/functions/mcp/deno.lock" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/.env.example", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/.env.example", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/.env.example" + "target": "~/supabase/functions/mcp/.env.example" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/types.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/types.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/types.ts" + "target": "~/supabase/functions/mcp/tools/types.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/result.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/result.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/result.ts" + "target": "~/supabase/functions/mcp/tools/result.ts" }, { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/whoami.ts", + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/whoami.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/whoami.ts" + "target": "~/supabase/functions/mcp/tools/whoami.ts" }, { "path": "registry/default/clients/tanstack/lib/supabase/client.ts", diff --git a/apps/ui-library/registry/blocks.ts b/apps/ui-library/registry/blocks.ts index 95ab3dd8b1e..f9552cff598 100644 --- a/apps/ui-library/registry/blocks.ts +++ b/apps/ui-library/registry/blocks.ts @@ -5,7 +5,7 @@ import currentUserAvatar from './default/blocks/current-user-avatar/registry-ite import dropzone from './default/blocks/dropzone/registry-item.json' with { type: 'json' } import headlessAppTanstack from './default/blocks/headless-app-tanstack/registry-item.json' with { type: 'json' } import infiniteQueryHook from './default/blocks/infinite-query-hook/registry-item.json' with { type: 'json' } -import mcpServer from './default/blocks/mcp-server/registry-item.json' with { type: 'json' } +import mcp from './default/blocks/mcp/registry-item.json' with { type: 'json' } import oauthConsentNextjs from './default/blocks/oauth-consent-nextjs/registry-item.json' with { type: 'json' } import oauthConsentReactRouter from './default/blocks/oauth-consent-react-router/registry-item.json' with { type: 'json' } import oauthConsentReact from './default/blocks/oauth-consent-react/registry-item.json' with { type: 'json' } @@ -50,7 +50,7 @@ const headlessApp = { ...headlessAppTanstack, files: [ ...headlessAppTanstack.files, - ...mcpServer.files.filter( + ...mcp.files.filter( (file) => !headlessAppTanstack.files.some((ownFile) => ownFile.target === file.target) ), ], @@ -81,7 +81,7 @@ export const blocks = [ // Backend-only Deno Edge Function block. Every file has an explicit target, // so it can be installed directly into a Supabase project. - mcpServer as RegistryItem, + mcp as RegistryItem, // Composes the auth, OAuth consent and MCP server blocks into one app. registryItemAppend(headlessApp, [tanstackClient!]), diff --git a/apps/ui-library/registry/default/blocks/headless-app-tanstack/registry-item.json b/apps/ui-library/registry/default/blocks/headless-app-tanstack/registry-item.json index c16b04643c1..c1a3ddb7d87 100644 --- a/apps/ui-library/registry/default/blocks/headless-app-tanstack/registry-item.json +++ b/apps/ui-library/registry/default/blocks/headless-app-tanstack/registry-item.json @@ -14,14 +14,14 @@ ], "files": [ { - "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/index.ts", + "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/index.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/index.ts" + "target": "~/supabase/functions/mcp/tools/index.ts" }, { - "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/tasks.ts", + "path": "registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/tasks.ts", "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/tasks.ts" + "target": "~/supabase/functions/mcp/tools/tasks.ts" }, { "path": "registry/default/blocks/headless-app-tanstack/routes/_protected/agents.tsx", diff --git a/apps/ui-library/registry/default/blocks/headless-app-tanstack/routes/_protected/agents.tsx b/apps/ui-library/registry/default/blocks/headless-app-tanstack/routes/_protected/agents.tsx index 6e53f821030..085b737c67c 100644 --- a/apps/ui-library/registry/default/blocks/headless-app-tanstack/routes/_protected/agents.tsx +++ b/apps/ui-library/registry/default/blocks/headless-app-tanstack/routes/_protected/agents.tsx @@ -4,7 +4,7 @@ import { createFileRoute } from '@tanstack/react-router' import { ConnectedAgents } from '@/registry/default/blocks/headless-app-tanstack/components/connected-agents' const PRODUCT_NAME = import.meta.env.VITE_PRODUCT_NAME?.trim() || 'Your product' -const MCP_SERVER_URL = `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/mcp-server` +const MCP_SERVER_URL = `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/mcp` // Nested under _protected, so the layout route redirects signed-out visitors // before this renders. diff --git a/apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/config.toml b/apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/config.toml index fd5021ba93f..ce4b290bdaf 100644 --- a/apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/config.toml +++ b/apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/config.toml @@ -57,5 +57,5 @@ authorization_url_path = "/oauth/consent" allow_dynamic_registration = true # The MCP server verifies user access tokens itself, so skip the gateway check. -[functions.mcp-server] +[functions.mcp] verify_jwt = false diff --git a/apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/index.ts b/apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/index.ts similarity index 100% rename from apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/index.ts rename to apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/index.ts diff --git a/apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/tasks.ts b/apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/tasks.ts similarity index 100% rename from apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/functions/mcp-server/tools/tasks.ts rename to apps/ui-library/registry/default/blocks/headless-app-tanstack/supabase/functions/mcp/tools/tasks.ts diff --git a/apps/ui-library/registry/default/blocks/mcp-server/registry-item.json b/apps/ui-library/registry/default/blocks/mcp-server/registry-item.json deleted file mode 100644 index c95cc022ef8..00000000000 --- a/apps/ui-library/registry/default/blocks/mcp-server/registry-item.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "mcp-server", - "type": "registry:item", - "title": "MCP Server", - "description": "Add a user-scoped MCP server to your product.", - "docs": "Disable gateway JWT verification, then deploy the Edge Function. A trusted product backend can call it with the signed-in user's access token. For external clients, install the [OAuth Consent block](https://supabase.com/library/docs/nextjs/oauth-consent), enable OAuth and dynamic registration, and set the Auth Site URL to the consent app. Every call runs through the user's RLS-scoped client. OAuth tokens include `client_id`; product sessions do not, so define policies for both paths. See [MCP authentication](https://supabase.com/docs/guides/auth/oauth-server/mcp-authentication) and [token security](https://supabase.com/docs/guides/auth/oauth-server/token-security).", - "files": [ - { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/index.ts", - "type": "registry:file", - "target": "~/supabase/functions/mcp-server/index.ts" - }, - { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.json", - "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.json" - }, - { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.lock", - "type": "registry:file", - "target": "~/supabase/functions/mcp-server/deno.lock" - }, - { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/.env.example", - "type": "registry:file", - "target": "~/supabase/functions/mcp-server/.env.example" - }, - { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/types.ts", - "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/types.ts" - }, - { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/result.ts", - "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/result.ts" - }, - { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/whoami.ts", - "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/whoami.ts" - }, - { - "path": "registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/index.ts", - "type": "registry:file", - "target": "~/supabase/functions/mcp-server/tools/index.ts" - } - ] -} diff --git a/apps/ui-library/registry/default/blocks/mcp/registry-item.json b/apps/ui-library/registry/default/blocks/mcp/registry-item.json new file mode 100644 index 00000000000..3d4b43c77a6 --- /dev/null +++ b/apps/ui-library/registry/default/blocks/mcp/registry-item.json @@ -0,0 +1,49 @@ +{ + "name": "mcp", + "type": "registry:item", + "title": "MCP Server", + "description": "Add a user-scoped MCP server to your product.", + "docs": "Disable gateway JWT verification, then deploy the Edge Function. A trusted product backend can call it with the signed-in user's access token. For external clients, install the [OAuth Consent block](https://supabase.com/library/docs/nextjs/oauth-consent), enable OAuth and dynamic registration, and set the Auth Site URL to the consent app. Every call runs through the user's RLS-scoped client. OAuth tokens include `client_id`; product sessions do not, so define policies for both paths. See [MCP authentication](https://supabase.com/docs/guides/auth/oauth-server/mcp-authentication) and [token security](https://supabase.com/docs/guides/auth/oauth-server/token-security).", + "files": [ + { + "path": "registry/default/blocks/mcp/supabase/functions/mcp/index.ts", + "type": "registry:file", + "target": "~/supabase/functions/mcp/index.ts" + }, + { + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.json", + "type": "registry:file", + "target": "~/supabase/functions/mcp/deno.json" + }, + { + "path": "registry/default/blocks/mcp/supabase/functions/mcp/deno.lock", + "type": "registry:file", + "target": "~/supabase/functions/mcp/deno.lock" + }, + { + "path": "registry/default/blocks/mcp/supabase/functions/mcp/.env.example", + "type": "registry:file", + "target": "~/supabase/functions/mcp/.env.example" + }, + { + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/types.ts", + "type": "registry:file", + "target": "~/supabase/functions/mcp/tools/types.ts" + }, + { + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/result.ts", + "type": "registry:file", + "target": "~/supabase/functions/mcp/tools/result.ts" + }, + { + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/whoami.ts", + "type": "registry:file", + "target": "~/supabase/functions/mcp/tools/whoami.ts" + }, + { + "path": "registry/default/blocks/mcp/supabase/functions/mcp/tools/index.ts", + "type": "registry:file", + "target": "~/supabase/functions/mcp/tools/index.ts" + } + ] +} diff --git a/apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/.env.example b/apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/.env.example similarity index 73% rename from apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/.env.example rename to apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/.env.example index 7d5b7932140..e3ec2940a11 100644 --- a/apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/.env.example +++ b/apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/.env.example @@ -1,6 +1,6 @@ # Copy this file to supabase/functions/.env before serving locally: -# cp supabase/functions/mcp-server/.env.example supabase/functions/.env -# supabase functions serve mcp-server --env-file supabase/functions/.env +# cp supabase/functions/mcp/.env.example supabase/functions/.env +# supabase functions serve mcp --env-file supabase/functions/.env # Deploy these values after linking your project: # supabase secrets set --env-file supabase/functions/.env diff --git a/apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.json b/apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/deno.json similarity index 100% rename from apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.json rename to apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/deno.json diff --git a/apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.lock b/apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/deno.lock similarity index 100% rename from apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/deno.lock rename to apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/deno.lock diff --git a/apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/index.ts b/apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/index.ts similarity index 100% rename from apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/index.ts rename to apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/index.ts diff --git a/apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/index.ts b/apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/tools/index.ts similarity index 100% rename from apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/index.ts rename to apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/tools/index.ts diff --git a/apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/result.ts b/apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/tools/result.ts similarity index 100% rename from apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/result.ts rename to apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/tools/result.ts diff --git a/apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/types.ts b/apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/tools/types.ts similarity index 100% rename from apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/types.ts rename to apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/tools/types.ts diff --git a/apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/whoami.ts b/apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/tools/whoami.ts similarity index 100% rename from apps/ui-library/registry/default/blocks/mcp-server/supabase/functions/mcp-server/tools/whoami.ts rename to apps/ui-library/registry/default/blocks/mcp/supabase/functions/mcp/tools/whoami.ts diff --git a/apps/ui-library/scripts/test-headless-tools.mts b/apps/ui-library/scripts/test-headless-tools.mts index 62b049d1a6e..76f04a145ef 100644 --- a/apps/ui-library/scripts/test-headless-tools.mts +++ b/apps/ui-library/scripts/test-headless-tools.mts @@ -15,7 +15,7 @@ try { const files = block.files!.filter((file) => file.target?.startsWith('~/supabase/')) const targets = files.map((file) => file.target!) assert.equal(new Set(targets).size, targets.length, 'Install targets must be unique') - assert(!block.registryDependencies?.some((dependency) => dependency.endsWith('/mcp-server.json'))) + assert(!block.registryDependencies?.some((dependency) => dependency.endsWith('/mcp.json'))) for (const file of files) { const destination = join(installRoot, file.target!.replace(/^~\//, '')) @@ -23,7 +23,7 @@ try { await copyFile(join(appRoot, file.path), destination) } - const functionRoot = join(installRoot, 'supabase/functions/mcp-server') + const functionRoot = join(installRoot, 'supabase/functions/mcp') await copyFile( join(appRoot, 'tests/headless-task-tools.test.mts'), join(functionRoot, 'tasks.test.ts') diff --git a/apps/ui-library/tests/headless-app.test.tsx b/apps/ui-library/tests/headless-app.test.tsx index 169c3355032..9621cf0bd6c 100644 --- a/apps/ui-library/tests/headless-app.test.tsx +++ b/apps/ui-library/tests/headless-app.test.tsx @@ -14,7 +14,7 @@ vi.mock('@/registry/default/clients/tanstack/lib/supabase/client', () => ({ createClient: () => ({ auth: { oauth: { listGrants, revokeGrant } } }), })) -const serverUrl = 'https://example.supabase.co/functions/v1/mcp-server' +const serverUrl = 'https://example.supabase.co/functions/v1/mcp' const grant: OAuthGrant = { client: { id: 'test-agent', name: 'Test agent', uri: '', logo_uri: '' }, granted_at: '2026-09-01T00:00:00Z',