mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
chore(library): rename mcp-server block to mcp (#50999)
Renames the `mcp-server` Library block to `mcp`. Installing it now creates `supabase/functions/mcp`, so the server is served at `/functions/v1/mcp`. - Block, Edge Function folder, and docs page renamed (`/docs/headless/mcp`) - Headless App block now installs its tools into `supabase/functions/mcp` and configures `[functions.mcp]` - Links in the BYO MCP and MCP authentication guides updated - Permanent redirects keep `/r/mcp-server.json` and `/docs/headless/mcp-server` working - `public/r` rebuilt <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Updates** * The MCP Server block is now named `mcp` across its documentation, installation links, and setup instructions. * Updated function endpoints and deployment commands to use `/mcp`. * Added permanent redirects from the previous `mcp-server` documentation and install URLs to their new locations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e0e58f8814
commit
a9c594a820
31 files changed
+171
-160
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
|
||||
@@ -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[] = [
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ const previewTitles: Partial<Record<CatalogPreviewKind, string>> = {
|
||||
storage: 'product-assets',
|
||||
chat: 'team chat',
|
||||
editor: 'index.ts',
|
||||
mcp: 'mcp-server',
|
||||
mcp: 'mcp',
|
||||
agents: 'connected agents',
|
||||
dashboard: 'acme workspace',
|
||||
client: 'supabase.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',
|
||||
|
||||
@@ -114,7 +114,7 @@ const blockMetadata: Record<string, Pick<LibraryBlock, 'description' | 'category
|
||||
category: 'Messaging',
|
||||
preview: 'chat',
|
||||
},
|
||||
'mcp-server': {
|
||||
mcp: {
|
||||
description: 'Add a user-scoped MCP server to your product.',
|
||||
category: 'AI & APIs',
|
||||
preview: 'mcp',
|
||||
|
||||
+10
-10
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: MCP Server
|
||||
description: Add a user-scoped MCP server to your product
|
||||
preview: <BlockOverview name="mcp-server" showFiles />
|
||||
preview: <BlockOverview name="mcp" showFiles />
|
||||
---
|
||||
|
||||
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
|
||||
|
||||
<BlockItem name="mcp-server" showOpenInV0={false} />
|
||||
<BlockItem name="mcp" showOpenInV0={false} />
|
||||
|
||||
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<Database>`.
|
||||
| `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
|
||||
@@ -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 <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
|
||||
|
||||
@@ -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/
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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 .thrLine truncated
|
||||
"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": "/// <reference types=\"vite/types/importMeta.d.ts\" />\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 <main className=\"flex min-h-svh items-center justify-center p-6 md:p-10\">\n <ConnectedAgents\n className=\"w-full max-w-lg\"\n mcpServerUrl={MCP_SERVER_URL}\n productName={PRODUCT_NAME}\n />\n </main>\n )\n}\n",
|
||||
"content": "/// <reference types=\"vite/types/importMeta.d.ts\" />\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 <main className=\"flex min-h-svh items-center justify-center p-6 md:p-10\">\n <ConnectedAgents\n className=\"w-full max-w-lg\"\n mcpServerUrl={MCP_SERVER_URL}\n productName={PRODUCT_NAME}\n />\n </main>\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<string, string> = {\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<Response> {\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==Line truncated
|
||||
"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<SupabaseContext['userClaims']>\n jwtClaims: NonNullable<SupabaseContext['jwtClaims']>\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<string, unknown>)[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",
|
||||
|
||||
@@ -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<string, string> = {\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<Response> {\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==Line truncated
|
||||
"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<SupabaseContext['userClaims']>\n jwtClaims: NonNullable<SupabaseContext['jwtClaims']>\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<string, unknown>)[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).",
|
||||
@@ -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",
|
||||
|
||||
@@ -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!]),
|
||||
|
||||
@@ -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",
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
@@ -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
|
||||
File renamed without changes.
File renamed without changes.
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
+2
-2
@@ -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
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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')
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in new issue
Block a user