+
+
)
}
+
+export default AssistantWidget
diff --git a/apps/ui-library/registry/default/blocks/assistant/registry-item.json b/apps/ui-library/registry/default/blocks/assistant/registry-item.json
index 07c31163534..8bd89ccc19a 100644
--- a/apps/ui-library/registry/default/blocks/assistant/registry-item.json
+++ b/apps/ui-library/registry/default/blocks/assistant/registry-item.json
@@ -6,6 +6,7 @@
"registryDependencies": [
"button",
"input",
+ "popover",
"scroll-area",
"@ai-elements/conversation",
"@ai-elements/message",
diff --git a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/chat/deno.json b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/chat/deno.json
index 3c2f9a89941..98531f3d115 100644
--- a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/chat/deno.json
+++ b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/chat/deno.json
@@ -4,10 +4,11 @@
"strict": true
},
"imports": {
- "hono": "npm:hono@^4.6.14",
+ "hono": "npm:hono@4.10.6",
"zod": "npm:zod@^4.1.12",
- "ai": "npm:ai@^5.0.90",
- "@ai-sdk/openai": "npm:@ai-sdk/openai@^2.0.32",
- "@ai-sdk/mcp": "npm:@ai-sdk/mcp@latest"
+ "ai": "npm:ai@^5.0.93",
+ "@ai-sdk/openai": "npm:@ai-sdk/openai@^2.0.68",
+ "@ai-sdk/mcp": "npm:@ai-sdk/mcp@latest",
+ "@modelcontextprotocol/sdk/client/streamableHttp.js": "npm:@modelcontextprotocol/sdk@^1.22.0/client/streamableHttp.js"
}
}
diff --git a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/chat/index.ts b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/chat/index.ts
index ddacde6830b..6c73467c788 100644
--- a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/chat/index.ts
+++ b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/chat/index.ts
@@ -1,9 +1,11 @@
+import { experimental_createMCPClient as createMCPClient } from '@ai-sdk/mcp'
import { openai } from '@ai-sdk/openai'
-import { streamText, type CoreMessage } from 'ai'
+import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
+import { convertToModelMessages, stepCountIs, streamText } from 'ai'
import { corsHeaders } from '../_shared/cors.ts'
type ChatRequestBody = {
- messages?: CoreMessage[]
+ messages?: unknown[]
model?: string
}
@@ -39,16 +41,77 @@ Deno.serve(async (req) => {
const modelId = body.model && typeof body.model === 'string' ? body.model : 'gpt-4o'
+ let normalizedMessages
+ try {
+ normalizedMessages = convertToModelMessages(body.messages as any)
+ } catch (conversionError) {
+ console.error('Assistant chat message normalization error:', conversionError)
+ return new Response(JSON.stringify({ error: 'Invalid message format' }), {
+ status: 400,
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
+ })
+ }
+
+ // Create MCP client to connect to our MCP server
+ let mcpClient
+ let tools = {}
+
+ try {
+ const supabaseUrl = Deno.env.get('SUPABASE_URL')
+ const mcpServerUrl = `${supabaseUrl}/functions/v1/mcp-server/mcp`
+
+ // Use StreamableHTTPClientTransport for compatibility with mcp-lite's StreamableHttpTransport
+ const transport = new StreamableHTTPClientTransport(new URL(mcpServerUrl), {
+ requestInit: {
+ headers: {
+ Authorization: authHeader,
+ },
+ },
+ })
+
+ mcpClient = await createMCPClient({
+ transport,
+ })
+
+ // Get available tools from the MCP server
+ tools = await mcpClient.tools()
+ } catch (mcpError) {
+ console.error('MCP client initialization error:', mcpError)
+ // Continue without MCP tools if initialization fails
+ }
+
try {
const result = await streamText({
model: openai(modelId),
- messages: body.messages,
+ messages: normalizedMessages,
+ stopWhen: stepCountIs(5),
system: 'You are a helpful AI assistant.',
+ tools,
+ onFinish: async () => {
+ // Close the MCP client when the stream is complete
+ if (mcpClient) {
+ await mcpClient.close()
+ }
+ },
})
- return result.toTextStreamResponse({ headers: corsHeaders })
+ return result.toUIMessageStreamResponse({
+ headers: corsHeaders,
+ sendReasoning: true,
+ sendSources: true,
+ })
} catch (error) {
console.error('Assistant chat error:', error)
+
+ // Ensure MCP client is closed even on error
+ if (mcpClient) {
+ try {
+ await mcpClient.close()
+ } catch (closeError) {
+ console.error('Error closing MCP client:', closeError)
+ }
+ }
+
return new Response(
JSON.stringify({
error: 'Failed to process chat request',
diff --git a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/deno.json b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/deno.json
index 78f0e38e9e7..33b00af98f8 100644
--- a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/deno.json
+++ b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/deno.json
@@ -4,9 +4,10 @@
"strict": true
},
"imports": {
- "hono": "npm:hono@^4.6.14",
- "mcp-lite": "npm:mcp-lite@0.8.2",
+ "hono": "npm:hono@4.10.6",
+ "mcp-lite": "npm:mcp-lite@0.10.0",
"zod": "npm:zod@^4.1.12",
"@supabase/supabase-js": "npm:@supabase/supabase-js@^2"
- }
+ },
+ "verify_jwt": false
}
diff --git a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/index.ts b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/index.ts
index 3d397388875..07e0207df47 100644
--- a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/index.ts
+++ b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/index.ts
@@ -1,13 +1,8 @@
-// Follow this setup guide to integrate the Deno language server with your editor:
-// https://deno.land/manual/getting_started/setup_your_environment
-// This enables autocomplete, go to definition, etc.
-//
-// Setup type definitions for built-in Supabase Runtime APIs
-///
-
-import { Hono } from "hono";
-import { McpServer, StreamableHttpTransport } from "mcp-lite";
-import { z } from "zod";
+import { Hono } from 'hono'
+import { McpServer, StreamableHttpTransport } from 'mcp-lite'
+import { z } from 'zod'
+import { corsHeaders } from '../_shared/cors.ts'
+import { createClient } from 'jsr:@supabase/supabase-js@2'
// We create two Hono instances:
// 1. `app` is the root handler for the Supabase Edge Function (must match the function name, e.g. /mcp-server)
@@ -15,77 +10,286 @@ import { z } from "zod";
// This pattern is required because Supabase Edge Functions route all requests to //*
const mcp = new McpServer({
- name: "supabase-assistant-mcp-server",
- version: "1.0.0",
+ name: 'supabase-assistant-mcp-server',
+ version: '1.0.0',
schemaAdapter: (schema: unknown) => z.toJSONSchema(schema as z.ZodType),
-});
+})
-// Temporary placeholder tool while wiring up the rest of the MCP server.
-mcp.tool("queryDatabase", {
- description:
- "Example placeholder tool that just echoes a string back to the caller.",
- inputSchema: z.object({
- message: z
- .string()
- .default("Hello from the Supabase MCP server!")
- .describe("Optional message you would like the tool to return"),
- }),
- handler: async ({ message }: { message: string }) => {
- return {
- content: [
- {
- type: "text",
- text: `Example tool response: ${message}`,
+// Auth Header Management
+// StreamableHttpTransport doesn't expose HTTP request headers to the MCP context (ctx.request.headers is undefined).
+// We use a module-level variable to pass the auth header from the Hono handler to MCP middleware.
+// This is safe because Supabase Edge Functions process requests sequentially, not concurrently.
+// The header is set before processing and cleared in a finally block to prevent leaks.
+// Alternative: Use InMemorySessionAdapter with custom session context, but adds unnecessary complexity.
+let currentAuthHeader: string | null = null
+
+// Helper function to create authenticated Supabase client
+function createAuthenticatedClient(authHeader: string) {
+ return createClient(
+ Deno.env.get('SUPABASE_URL')!,
+ Deno.env.get('SUPABASE_ANON_KEY')!,
+ {
+ global: {
+ headers: {
+ Authorization: authHeader,
},
- ],
- };
+ },
+ }
+ )
+}
+
+// Middleware to inject auth header into MCP context state
+mcp.use(async (ctx: any, next: any) => {
+ // Try to get auth header from ctx.request.headers (doesn't work with StreamableHttpTransport)
+ // Fall back to the stored currentAuthHeader set by the Hono handler
+ const authHeader = ctx.request?.headers?.get?.('Authorization') || currentAuthHeader
+
+ if (authHeader) {
+ // Make auth header available to all tool handlers via ctx.state
+ ctx.state.authHeader = authHeader
+ }
+
+ await next()
+})
+
+mcp.tool('getUserTasks', {
+ description: 'Fetch tasks for the authenticated user with optional filtering by due date and title search.',
+ inputSchema: z.object({
+ due_date: z
+ .string()
+ .optional()
+ .describe('Optional due date filter in YYYY-MM-DD format. Returns tasks with this exact due date.'),
+ title_search: z
+ .string()
+ .optional()
+ .describe('Optional title search term. Returns tasks where the title contains this text (case-insensitive).'),
+ }),
+ handler: async ({ due_date, title_search }: { due_date?: string; title_search?: string }, ctx: any) => {
+ const authHeader = ctx.state?.authHeader
+
+ if (!authHeader) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: 'Error: Authentication required to fetch tasks.',
+ },
+ ],
+ isError: true,
+ }
+ }
+
+ try {
+ const supabase = createAuthenticatedClient(authHeader)
+
+ let query = supabase
+ .from('tasks')
+ .select('id, created_at, title, assignee_id, due_at')
+ .order('due_at', { ascending: true, nullsFirst: false })
+
+ // Apply due_date filter if provided
+ if (due_date) {
+ query = query.eq('due_at', due_date)
+ }
+
+ // Apply title_search filter if provided
+ if (title_search) {
+ query = query.ilike('title', `%${title_search}%`)
+ }
+
+ const { data, error } = await query
+
+ if (error) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error fetching tasks: ${error.message}`,
+ },
+ ],
+ isError: true,
+ }
+ }
+
+ const taskCount = data?.length || 0
+ const tasksText = data && taskCount > 0
+ ? data.map((task) => {
+ const dueDate = task.due_at ? `Due: ${task.due_at}` : 'No due date'
+ return `- [${task.id}] ${task.title || 'Untitled'} (${dueDate})`
+ }).join('\n')
+ : 'No tasks found.'
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Found ${taskCount} task(s):\n\n${tasksText}`,
+ },
+ ],
+ }
+ } catch (err) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Unexpected error: ${err instanceof Error ? err.message : String(err)}`,
+ },
+ ],
+ isError: true,
+ }
+ }
},
-});
+})
-const transport = new StreamableHttpTransport();
-const httpHandler = transport.bind(mcp);
+mcp.tool('createTask', {
+ description: 'Create a task for the authenticated user. Optionally provide a due date (YYYY-MM-DD).',
+ inputSchema: z.object({
+ title: z
+ .string()
+ .min(1, 'Title must not be empty')
+ .describe('Task title to display in the list.'),
+ due_date: z
+ .string()
+ .optional()
+ .describe('Optional due date in YYYY-MM-DD format.'),
+ }),
+ handler: async ({ title, due_date }: { title: string; due_date?: string }, ctx: any) => {
+ const authHeader = ctx.state?.authHeader
-const app = new Hono();
-const mcpApp = new Hono();
+ if (!authHeader) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: 'Error: Authentication required to create tasks.',
+ },
+ ],
+ isError: true,
+ }
+ }
-mcpApp.get("/", (c) => {
+ try {
+ const supabase = createAuthenticatedClient(authHeader)
+
+ const {
+ data: { user },
+ error: userError,
+ } = await supabase.auth.getUser()
+
+ if (userError || !user) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error retrieving authenticated user: ${userError?.message ?? 'Not found'}`,
+ },
+ ],
+ isError: true,
+ }
+ }
+
+ const { data, error } = await supabase
+ .from('tasks')
+ .insert({
+ title,
+ due_at: due_date ?? null,
+ assignee_id: user.id,
+ })
+ .select('id, title, due_at, assignee_id')
+ .single()
+
+ if (error || !data) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error creating task: ${error?.message ?? 'Unknown error'}`,
+ },
+ ],
+ isError: true,
+ }
+ }
+
+ const dueDateText = data.due_at ? `Due ${data.due_at}` : 'No due date set'
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Created task [${data.id}] "${data.title}" for user ${data.assignee_id}. ${dueDateText}.`,
+ },
+ ],
+ }
+ } catch (err) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Unexpected error: ${err instanceof Error ? err.message : String(err)}`,
+ },
+ ],
+ isError: true,
+ }
+ }
+ },
+})
+
+const transport = new StreamableHttpTransport()
+const httpHandler = transport.bind(mcp)
+
+const app = new Hono()
+const mcpApp = new Hono()
+
+// Handle OPTIONS requests for CORS
+mcpApp.use('*', async (c, next) => {
+ if (c.req.method === 'OPTIONS') {
+ return new Response('ok', { headers: corsHeaders })
+ }
+ await next()
+})
+
+mcpApp.get('/', (c) => {
return c.json({
- message: "MCP Server on Supabase Edge Functions",
+ message: 'MCP Server on Supabase Edge Functions',
endpoints: {
- mcp: "/mcp",
- health: "/health",
+ mcp: '/mcp',
+ health: '/health',
},
- });
-});
+ })
+})
-mcpApp.get("/health", (c) => {
+mcpApp.get('/health', (c) => {
return c.json({
- message: "Service is up and running",
- });
-});
+ message: 'Service is up and running',
+ })
+})
-mcpApp.all("/mcp", async (c) => {
- const response = await httpHandler(c.req.raw);
- return response;
-});
+// Handle both POST (for regular MCP requests) and GET (for SSE connections)
+const handleMcpRequest = async (c: any) => {
+ // Validate Authorization header at the Hono level
+ // (StreamableHttpTransport doesn't expose HTTP headers to MCP context)
+ const authHeader = c.req.header('Authorization')
+
+ if (!authHeader) {
+ return c.json({ error: 'Unauthorized' }, { status: 401, headers: corsHeaders })
+ }
+
+ // Store auth header so MCP middleware can inject it into ctx.state for tool handlers
+ currentAuthHeader = authHeader
+
+ try {
+ const response = await httpHandler(c.req.raw)
+ return response
+ } finally {
+ // Clean up auth header after request completes to prevent leaks
+ currentAuthHeader = null
+ }
+}
+
+mcpApp.post('/mcp', handleMcpRequest)
+mcpApp.get('/mcp', handleMcpRequest)
// Mount the MCP app at /mcp-server (matches the function name)
-app.route("/mcp-server", mcpApp);
+app.route('/mcp-server', mcpApp)
-export default app;
-
-/* To invoke locally:
-
- 1. Run `supabase start` (see: https://supabase.com/docs/reference/cli/supabase-start)
- 2. Make an HTTP request:
-
- curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/mcp-server/mcp' \
- --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \
- --header 'Content-Type: application/json' \
- --data '{"jsonrpc":"2.0","method":"tools/list","id":1}'
-
- 3. Test the health endpoint:
-
- curl 'http://127.0.0.1:54321/functions/v1/mcp-server/health'
-
-*/
+export default app
diff --git a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/lib/supabase.ts b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/lib/supabase.ts
new file mode 100644
index 00000000000..c76f10457a7
--- /dev/null
+++ b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/lib/supabase.ts
@@ -0,0 +1,15 @@
+import { createClient } from 'jsr:@supabase/supabase-js@2'
+
+export function createAuthenticatedClient(authHeader: string) {
+ return createClient(
+ Deno.env.get('SUPABASE_URL')!,
+ Deno.env.get('SUPABASE_ANON_KEY')!,
+ {
+ global: {
+ headers: {
+ Authorization: authHeader,
+ },
+ },
+ }
+ )
+}
diff --git a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/tools/create-task.ts b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/tools/create-task.ts
new file mode 100644
index 00000000000..7908d4500ac
--- /dev/null
+++ b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/tools/create-task.ts
@@ -0,0 +1,98 @@
+import { z } from 'zod'
+import { createAuthenticatedClient } from '../lib/supabase.ts'
+
+export const createTaskTool = {
+ name: 'createTask',
+ options: {
+ description: 'Create a task for the authenticated user. Optionally provide a due date (YYYY-MM-DD).',
+ inputSchema: z.object({
+ title: z
+ .string()
+ .min(1, 'Title must not be empty')
+ .describe('Task title to display in the list.'),
+ due_date: z
+ .string()
+ .optional()
+ .describe('Optional due date in YYYY-MM-DD format.'),
+ }),
+ handler: async ({ title, due_date }: { title: string; due_date?: string }, ctx: any) => {
+ const authHeader = ctx.state?.authHeader
+
+ if (!authHeader) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: 'Error: Authentication required to create tasks.',
+ },
+ ],
+ isError: true,
+ }
+ }
+
+ try {
+ const supabase = createAuthenticatedClient(authHeader)
+
+ const {
+ data: { user },
+ error: userError,
+ } = await supabase.auth.getUser()
+
+ if (userError || !user) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error retrieving authenticated user: ${userError?.message ?? 'Not found'}`,
+ },
+ ],
+ isError: true,
+ }
+ }
+
+ const { data, error } = await supabase
+ .from('tasks')
+ .insert({
+ title,
+ due_at: due_date ?? null,
+ assignee_id: user.id,
+ })
+ .select('id, title, due_at, assignee_id')
+ .single()
+
+ if (error || !data) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error creating task: ${error?.message ?? 'Unknown error'}`,
+ },
+ ],
+ isError: true,
+ }
+ }
+
+ const dueDateText = data.due_at ? `Due ${data.due_at}` : 'No due date set'
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Created task [${data.id}] "${data.title}" for user ${data.assignee_id}. ${dueDateText}.`,
+ },
+ ],
+ }
+ } catch (err) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Unexpected error: ${err instanceof Error ? err.message : String(err)}`,
+ },
+ ],
+ isError: true,
+ }
+ }
+ },
+ },
+}
diff --git a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/tools/get-user-tasks.ts b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/tools/get-user-tasks.ts
new file mode 100644
index 00000000000..f74ca3fd89a
--- /dev/null
+++ b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/tools/get-user-tasks.ts
@@ -0,0 +1,101 @@
+import { z } from 'zod'
+import { createAuthenticatedClient } from '../lib/supabase.ts'
+
+export const getUserTasksTool = {
+ name: 'getUserTasks',
+ options: {
+ description:
+ 'Fetch tasks for the authenticated user with optional filtering by due date and title search.',
+ inputSchema: z.object({
+ due_date: z
+ .string()
+ .optional()
+ .describe('Optional due date filter in YYYY-MM-DD format. Returns tasks with this exact due date.'),
+ title_search: z
+ .string()
+ .optional()
+ .describe('Optional title search term. Returns tasks where the title contains this text (case-insensitive).'),
+ }),
+ handler: async (
+ { due_date, title_search }: { due_date?: string; title_search?: string },
+ ctx: any
+ ) => {
+ const authHeader = ctx.state?.authHeader
+
+ if (!authHeader) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: 'Error: Authentication required to fetch tasks.',
+ },
+ ],
+ isError: true,
+ }
+ }
+
+ try {
+ const supabase = createAuthenticatedClient(authHeader)
+
+ let query = supabase
+ .from('tasks')
+ .select('id, created_at, title, assignee_id, due_at')
+ .order('due_at', { ascending: true, nullsFirst: false })
+
+ if (due_date) {
+ query = query.eq('due_at', due_date)
+ }
+
+ if (title_search) {
+ query = query.ilike('title', `%${title_search}%`)
+ }
+
+ const { data, error } = await query
+
+ if (error) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error fetching tasks: ${error.message}`,
+ },
+ ],
+ isError: true,
+ }
+ }
+
+ const taskCount = data?.length || 0
+ const tasksText =
+ data && taskCount > 0
+ ? data
+ .map((task) => {
+ const dueDate = task.due_at ? `Due: ${task.due_at}` : 'No due date'
+ return `- [${task.id}] ${task.title || 'Untitled'} (${dueDate})`
+ })
+ .join('\n')
+ : 'No tasks found.'
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Found ${taskCount} task(s):\n\n${tasksText}`,
+ },
+ ],
+ }
+ } catch (err) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Unexpected error: ${err instanceof Error ? err.message : String(err)}`,
+ },
+ ],
+ isError: true,
+ }
+ }
+ },
+ },
+}
+
+export type ToolDefinition = typeof getUserTasksTool
diff --git a/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/tools/registry.ts b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/tools/registry.ts
new file mode 100644
index 00000000000..7239bd72f1f
--- /dev/null
+++ b/apps/ui-library/registry/default/blocks/assistant/supabase/functions/mcp-server/tools/registry.ts
@@ -0,0 +1,6 @@
+import { createTaskTool } from './create-task.ts'
+import { getUserTasksTool } from './get-user-tasks.ts'
+
+export const toolRegistry = [getUserTasksTool, createTaskTool]
+
+export type ToolDefinition = (typeof toolRegistry)[number]
diff --git a/apps/ui-library/registry/default/components/ui/popover.tsx b/apps/ui-library/registry/default/components/ui/popover.tsx
index cb3d6cd16c7..69739ae8a31 100644
--- a/apps/ui-library/registry/default/components/ui/popover.tsx
+++ b/apps/ui-library/registry/default/components/ui/popover.tsx
@@ -39,4 +39,8 @@ function PopoverAnchor({ ...props }: React.ComponentProps
}
-export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
+function PopoverClose({ ...props }: React.ComponentProps) {
+ return
+}
+
+export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverClose }
diff --git a/apps/ui-library/registry/default/examples/assistant-demo.tsx b/apps/ui-library/registry/default/examples/assistant-demo.tsx
index 8d84f907a5f..3d4805492f2 100644
--- a/apps/ui-library/registry/default/examples/assistant-demo.tsx
+++ b/apps/ui-library/registry/default/examples/assistant-demo.tsx
@@ -1,97 +1,45 @@
'use client'
import { AssistantWidget } from '@/registry/default/blocks/assistant/components/assistant-widget'
-import { Badge } from '@/registry/default/components/ui/badge'
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/registry/default/components/ui/card'
-import { Separator } from '@/registry/default/components/ui/separator'
-import { Check, Database, ShieldCheck, Sparkles } from 'lucide-react'
-
-const features = [
- {
- title: 'Query with plain English',
- description: 'Natural language requests turn into SQL via MCP tools.',
- icon: Sparkles,
- },
- {
- title: 'Database aware',
- description: 'Assistant can run validated SQL against your Supabase project.',
- icon: Database,
- },
- {
- title: 'Secure by default',
- description: 'Supabase auth token is forwarded for RLS protected data.',
- icon: ShieldCheck,
- },
-]
-
-const setupSteps = [
- 'Serve the `chat` and `mcp-server` Edge Functions.',
- 'Expose `NEXT_PUBLIC_ASSISTANT_CHAT_URL` and `MCP_SERVER_URL`.',
- 'Mount `` near the root layout of your app.',
-]
-
-const Feature = ({ title, description, icon: Icon }: (typeof features)[number]) => (
-
-
-
-
-
-
{title}
-
{description}
-
-
-)
-
-const SetupChecklist = () => (
-
-
- Quick setup
-
-
- {setupSteps.map((step) => (
-
-
- {step}
-
- ))}
-
-
-)
+import { createClient } from '@/registry/default/blocks/assistant/lib/supabase/client'
+import { useEffect } from 'react'
const AssistantDemoContent = () => (
-
-
-
- AI Assistant
-
- Ask your data anything
-
- The Assistant widget pairs MCP tooling with Supabase authentication so you can run secure,
- natural-language queries against your database without leaving the page.
-
-
-
-
- {features.map((feature) => (
-
- ))}
-
-
-
-
- Tip: Click the bubble in the bottom-right corner to open the live assistant powered by the
- same block you install via the registry.
-
-
-
-)
-
-const AssistantDemo = () => (
-
-
-
-
+
+
+ Click the Assistant widget and ask it to create, read, update, or delete tasks in your
+ database. Tasks will be scoped to your current session.
+