Files
supabase/apps/ui-library/config/docs.ts
T
19d7233580 feat(ui-library): add headless app block for TanStack Start (#49579)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature — a new UI Library block. Stacked on #49573 (already in main)

Fixes AI-1064

## What is the new behavior?

Adds `headless-app-tanstack`: customers sign in, authorize an MCP
client, and use the product through agent tool calls. It composes the
existing Password-Based Auth, OAuth Consent, and MCP Server blocks.

- `/agents` provides a copyable connection prompt, lists OAuth
authorizations, and lets customers revoke access.
- The shared MCP runtime exposes `whoami` plus example task CRUD tools.
Tools use the caller's Supabase client, with database grants and RLS
enforcing ownership.
- A root-level `supabase/` directory supplies local Auth/OAuth
configuration, a declarative tasks schema, and Edge Function files,
including `.env.example`.
- Docs cover local setup, signing keys, migrations, environment
configuration, deployment, and extending the tools.
`/example/headless-app` previews the sign-in, consent, connect, and
connected states.

Shared block fixes make a fresh install work:

- Explicit public URL resolution fixes OAuth discovery in local Edge
Runtime when middleware runtime detection fails. Both external OAuth
access tokens and ordinary authenticated app session tokens remain
supported; embedded agents do not need an additional consent flow.
- Registry targets keep backend files outside `src/`, and generated
consumer routes omit source-only TypeScript suppressions.
- Signup respects `auth.email.enable_confirmations`; sign-in/signup
preserve the return destination. Missing consent IDs retain the existing
error state without serializing `null` into the URL.

## How to test

Use the UI Library on **staging** and follow the block pages'
instructions.

1. Open the **Headless App** block page for TanStack Start. Install it
into a fresh app and follow the setup instructions through connecting an
MCP client.
2. Sign up, open `/agents`, and use the connection prompt to authorize a
client. Call `whoami`, then create, list, update, and delete a task.
3. Confirm the client appears on `/agents`. Revoke access and verify it
disappears and token refresh fails. An existing access token can
continue working until it expires.
4. Follow the **MCP Server** block page's embedded-agent instructions
using an authenticated app session. Confirm tools work without another
OAuth consent flow and `whoami` returns `client_id: null`.
5. With a second user, confirm each user can only access their own
tasks. Check that signup behaves correctly for the configured
email-confirmation setting.
6. Check the Headless App preview states and run the installed app's
typecheck and production build.

## Validation performed

Fresh local installation and browser/SDK verification passed: 26 live
MCP/Data API checks, 10 Deno tests, and 7 connection-page component
tests. Also passed UI Library typecheck, targeted lint,
registry/Markdown builds, and fresh consumer typecheck/production build.
Both OAuth and ordinary app session authentication were exercised.

Hosted deployment and consuming the confirmation-email link were not
tested.



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added a TanStack Headless App example with sign-in, OAuth consent, MCP
connection, and connected-agent screens.
- Added task management tools for listing, creating, updating, and
deleting tasks through MCP.
- Added connected-agent management, including server URL and prompt
copying, refresh, and access revocation.
  - Added a new Headless App registry block and documentation.

- **Bug Fixes**
- Preserved intended destinations through sign-up, email confirmation,
and protected-route login redirects.
- Improved OAuth discovery URL handling across forwarded-host
deployments.

- **Documentation**
- Updated setup, environment, deployment, and Supabase CLI guidance for
headless apps and MCP servers.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saxon Fletcher <SaxonF@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: repro <repro@local>
Co-authored-by: Raúl Barroso <code@raulb.dev>
2026-09-14 10:30:26 +10:00

190 lines
4.9 KiB
TypeScript

import { SidebarNavGroup } from '@/types/nav'
export const gettingStarted: SidebarNavGroup = {
title: 'Getting Started',
items: [
{
title: 'Introduction',
href: '/docs/getting-started/introduction',
items: [],
commandItemLabel: 'Introduction',
},
{
title: 'Quick Start',
href: '/docs/getting-started/quickstart',
items: [],
commandItemLabel: 'Quick Start',
},
{
title: 'FAQ',
href: '/docs/getting-started/faq',
items: [],
commandItemLabel: 'FAQ',
},
],
}
export const platformBlocks: SidebarNavGroup = {
title: 'Platform',
items: [
{
title: 'Platform Kit',
href: '/docs/platform/platform-kit',
items: [],
commandItemLabel: 'Platform Kit',
},
],
}
export const oauthBlocks: SidebarNavGroup = {
title: 'OAuth',
items: [
{
title: 'OAuth Consent',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
href: '/docs/nextjs/oauth-consent',
items: [],
new: true,
commandItemLabel: 'OAuth Consent',
},
],
}
export const mcpBlocks: SidebarNavGroup = {
title: 'MCP',
items: [
{
title: 'MCP Server',
href: '/docs/headless/mcp-server',
items: [],
new: true,
commandItemLabel: 'MCP Server',
},
{
title: 'Headless App',
supportedFrameworks: ['tanstack'],
href: '/docs/tanstack/headless-app',
items: [],
new: true,
commandItemLabel: 'Headless App',
},
],
}
// Component definitions with supported frameworks
export const componentPages: SidebarNavGroup = {
title: 'Components',
items: [
{
title: 'Client',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'],
href: '/docs/nextjs/client',
items: [],
commandItemLabel: 'Supabase Client',
},
{
title: 'Password-Based Auth',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'],
href: '/docs/nextjs/password-based-auth',
items: [],
commandItemLabel: 'Password-Based Auth',
},
{
title: 'Social Auth',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'],
href: '/docs/nextjs/social-auth',
items: [],
new: true,
commandItemLabel: 'Social Auth',
},
{
title: 'Dropzone',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'],
href: '/docs/nextjs/dropzone',
items: [],
commandItemLabel: 'Dropzone (File Upload)',
},
{
title: 'Realtime Cursor',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'],
href: '/docs/nextjs/realtime-cursor',
items: [],
commandItemLabel: 'Realtime Cursor',
},
{
title: 'Realtime Monaco',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
href: '/docs/nextjs/realtime-monaco',
items: [],
new: true,
commandItemLabel: 'Realtime Monaco',
},
{
title: 'Realtime Flow',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react'],
href: '/docs/nextjs/realtime-flow',
items: [],
new: true,
commandItemLabel: 'Realtime Flow',
},
{
title: 'Current User Avatar',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'],
href: '/docs/nextjs/current-user-avatar',
items: [],
commandItemLabel: 'Current User Avatar',
},
{
title: 'Realtime Avatar Stack',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'],
href: '/docs/nextjs/realtime-avatar-stack',
items: [],
commandItemLabel: 'Realtime Avatar Stack',
},
{
title: 'Realtime Chat',
supportedFrameworks: ['nextjs', 'react-router', 'tanstack', 'react', 'vue', 'nuxtjs'],
href: '/docs/nextjs/realtime-chat',
items: [],
commandItemLabel: 'Realtime Chat',
},
{
title: 'Infinite Query',
supportedFrameworks: ['react', 'vue'],
href: '/docs/react/infinite-query',
new: true,
items: [],
commandItemLabel: 'Infinite Query',
},
],
}
export const COMMAND_ITEMS = [
...gettingStarted.items.map((item) => ({
label: item.commandItemLabel,
href: item.href,
})),
...componentPages.items.map((item) => ({
label: item.commandItemLabel,
href: item.href,
})),
...oauthBlocks.items.map((item) => ({
label: item.commandItemLabel,
href: item.href,
})),
...mcpBlocks.items.map((item) => ({
label: item.commandItemLabel,
href: item.href,
})),
]
// Framework titles for display
export const frameworkTitles: Record<string, string> = {
nextjs: 'Next.js',
'react-router': 'React Router',
tanstack: 'TanStack Start',
react: 'React SPA',
vue: 'Vue',
nuxtjs: 'Nuxt.js',
}