Files
supabase/apps/docs/data/content-listings/ai-tools.data.ts
T
Nik RichersandNik Richers e0ecaadc21 docs: make AI tools section agent-first (#48167)
## 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?

This PR reworks the `/guides/ai-tools` docs section to be agent-first.
The overview now leads with the fastest path to a working setup (the
plugin install command), a "What's supported?" card grid showing which
coding agents and IDEs work via Plugin and/or MCP (with each product's
own tagline, not a generated sentence), and a concepts glossary —
instead of a plain four-item list. The sidebar "AI Tools" widget, shown
on every guides page, now links to this hub ("Connect your AI agent")
instead of opening a ChatGPT/Claude chat frontend.

Closes DOCS-1201.

## What is the current behavior?

- The `/guides/ai-tools` overview is a plain four-item bullet list with
no getting-started path, compatibility info, or concepts explanation.
- The sidebar "AI Tools" widget offers "Copy as Markdown", "Ask
ChatGPT", and "Ask Claude" — the latter two send you to a chat frontend
instead of agent setup.

## What is the new behavior?

- `ai-tools.mdx`: intro → plugin install callout → "What's supported?"
card grid (`<ContentListings id="ai-tools-supported-agents" />`, icon +
tagline + Plugin/MCP badge per agent) → "Key concepts" glossary →
"Building AI into your app?" (also converted to `ContentListings`).
- New `data/content-listings/ai-tools.data.ts` builds the card grid from
the existing `PLUGIN_CLIENTS`/`MCP_CLIENT_DATA` client lists (no new
hand-maintained data) — fixing two latent bugs found along the way:
GitHub Copilot was keyed differently between the two sources (would have
produced duplicate cards), and Windsurf has no upstream docs URL (would
have been silently dropped).
- New opt-in `badgePosition` field on `ContentListingItem` so the badge
renders under the title for the agent grid, without changing the one
other existing badge usage (self-hosting's "Official" tag, still
inline).
- `plugins.mdx`/`mcp.mdx`/`ai-skills.mdx` each get a one-line "Quick
start" lead-in so they stand alone via the `.md` content-negotiation
route.
- `GuidesSidebar.tsx` + `telemetry-constants.ts`: Added "Connect your AI
agent" → `/guides/ai-tools`, and the `ask_ai_clicked` event with
`agent_setup_clicked`.
- Accessibility fix (from review): the "Not supported" indicator now
exposes an `sr-only` label instead of being fully `aria-hidden`.

## Additional context

- Worktree:
`~/GitHub/supabase/supabase-worktrees/nikrichers/docs-1201-make-guidesai-tools-agent-first-and-replace-chat-frontend`
- **Open question — Windsurf card**: `windsurf.com` now redirects to a
Devin Desktop page (Cognition acquired Windsurf in 2025), but Supabase's
own `MCP_CLIENT_DATA` still targets Windsurf's distinct config path
(`~/.codeium/windsurf/mcp_config.json`), so the card is still labeled
"Windsurf" with its pre-acquisition tagline ("The first agentic IDE.
Tomorrow's editor, today."). Needs a follow-up decision on whether to
relabel/merge/drop this card once Devin Desktop's MCP support (if any)
is confirmed.
- Follow-up (not in this PR): deeper IA rework of the ai-tools section
belongs to the broader agent-first audit;
`content/guides/resources/glossary.mdx` has no MCP/Agent
Skills/Plugin/Prompts entries yet — this PR's "Key concepts" is
currently the only definition of these terms site-wide.
- Verification:

  | Check | Result |
  | --- | --- |
| Lint (`lint:mdx`, `eslint`), `typecheck`, `test:local
lib/content-listings.test.ts` | Pass — 13/13 tests, 0 errors |
| `build:guides-markdown` | Pass — card grid flattens cleanly to
markdown |
| Playwright: broken icon requests, light + dark theme, PR preview |
Pass — 0 in either theme |
| `/guides/self-hosting` "Official" badge (existing `ContentListings`
usage) | Pass — unaffected by the new `badgePosition` opt-in |

### Before & After

#### [`/guides/ai-tools`](https://supabase.com/docs/guides/ai-tools)

Also shows the sidebar change (right rail): "Ask ChatGPT" / "Ask Claude"
→ "Connect your AI agent".

| [Before](https://supabase.com/docs/guides/ai-tools) |
[After](https://docs-git-nikrichers-docs-1201-make-guidesai-too-7c7493-supabase.vercel.app/docs/guides/ai-tools)
|
| --- | --- |
|
![Before](https://moijyfpvgnmgoxvwcikq.supabase.co/storage/v1/object/public/pr-proof/supabase/supabase/pr48167/ai-tools-before-79e9cdcf.png)
|
![After](https://moijyfpvgnmgoxvwcikq.supabase.co/storage/v1/object/public/pr-proof/supabase/supabase/pr48167/ai-tools-final-full-7ab9f52f.png)
|

Sub-pages each just add a one-line "Quick start" callout under the intro
(no other layout change):
[plugins](https://supabase.com/docs/guides/ai-tools/plugins)
([preview](https://docs-git-nikrichers-docs-1201-make-guidesai-too-7c7493-supabase.vercel.app/docs/guides/ai-tools/plugins))
· [mcp](https://supabase.com/docs/guides/ai-tools/mcp)
([preview](https://docs-git-nikrichers-docs-1201-make-guidesai-too-7c7493-supabase.vercel.app/docs/guides/ai-tools/mcp))
· [ai-skills](https://supabase.com/docs/guides/ai-tools/ai-skills)
([preview](https://docs-git-nikrichers-docs-1201-make-guidesai-too-7c7493-supabase.vercel.app/docs/guides/ai-tools/ai-skills)).

### Test plan

- [x] `/guides/ai-tools` renders callout → card grid → concepts →
Building AI into your app, in order
- [x] Card grid: one card per agent (no duplicate Copilot), Windsurf
present, icons clean in both themes, taglines shown, badges below title
- [x] Sidebar shows "Connect your AI agent"; self-hosting's "Official"
badge unaffected
- [x] `.md` route still serves clean markdown; no lingering
`ask_ai_clicked`, ChatGPT/Claude icon, or `SupportedAgentsTable`
references

---------

Co-authored-by: Nik Richers <nik@validmind.ai>
2026-07-23 16:01:54 -07:00

144 lines
5.9 KiB
TypeScript

import { PLUGIN_CLIENTS } from '~/features/ui/AgentPluginsPanel.data'
import type { ContentListingGroup } from '~/lib/content-listings.schema'
import { MCP_CLIENT_DATA } from 'ui-patterns/McpUrlBuilder/clients.data'
// MCP_CLIENT_DATA and PLUGIN_CLIENTS key GitHub Copilot differently — normalize before merging.
const KEY_ALIASES: Record<string, string> = {
'copilot-cli': 'github-copilot',
}
// ContentListings defaults hasLightIcon to true for string icon paths (see
// ContentListings.client.tsx: `item.hasLightIcon ?? typeof item.icon === 'string'`), so
// single-variant icons must set `hasLightIcon: false` explicitly or they'll request a
// nonexistent "-light" file.
const ICON_ASSETS: Record<string, { icon: string; hasLightIcon?: boolean }> = {
'claude-code': { icon: '/docs/img/icons/agent-claude-icon', hasLightIcon: false },
codex: { icon: '/docs/img/icons/agent-openai-icon', hasLightIcon: true },
cursor: { icon: '/docs/img/icons/agent-cursor-icon', hasLightIcon: true },
'gemini-cli': { icon: '/docs/img/icons/agent-gemini-cli-icon', hasLightIcon: false },
'github-copilot': { icon: '/docs/img/icons/agent-copilot-icon', hasLightIcon: true },
kimi: { icon: '/docs/img/icons/agent-kimi-icon', hasLightIcon: true },
vscode: { icon: '/docs/img/icons/agent-vscode-icon', hasLightIcon: false },
antigravity: { icon: '/docs/img/icons/agent-antigravity-icon', hasLightIcon: false },
windsurf: { icon: '/docs/img/icons/agent-windsurf-icon', hasLightIcon: true },
goose: { icon: '/docs/img/icons/agent-goose-icon', hasLightIcon: true },
factory: { icon: '/docs/img/icons/agent-factory-icon', hasLightIcon: true },
opencode: { icon: '/docs/img/icons/agent-opencode-icon', hasLightIcon: true },
kiro: { icon: '/docs/img/icons/agent-kiro-icon', hasLightIcon: false },
}
// Claude.ai and ChatGPT are MCP connectors for a chat web app, not a coding agent or IDE.
const EXCLUDED_KEYS = new Set(['claude-ai', 'chatgpt'])
// Each product's own tagline, sourced from its marketing site (or, where noted, an official
// GitHub repo description or last-known pre-acquisition tagline). Not Supabase-specific copy —
// see the "Open questions" note in the PR for the Windsurf/Devin caveat.
const TAGLINES: Record<string, string> = {
antigravity: 'Experience liftoff with the next-gen agent platform.',
'claude-code': 'Work with Claude directly in your codebase, from your terminal, IDE, and more.',
codex: 'A lightweight coding agent that runs in your terminal.',
cursor: 'Your coding agent for building ambitious software.',
factory: 'A self-improving system for your SDLC.',
'gemini-cli': 'Build, debug & deploy with AI.',
'github-copilot': 'Your AI accelerator for every workflow, from the editor to the enterprise.',
goose: 'Your native open source AI agent — desktop app, CLI, and API.',
kimi: 'Engineered to drop into any dev workflow and get programming tasks done fast.',
kiro: 'Move beyond AI coding to agentic engineering.',
opencode: 'The open source AI coding agent.',
vscode: 'The open source AI code editor — your home for multi-agent development.',
// Pre-acquisition tagline (Cognition/Devin acquired Windsurf in 2025) — see "Open questions".
windsurf: "The first agentic IDE. Tomorrow's editor, today.",
}
interface AgentEntry {
key: string
label: string
plugin: boolean
mcp: boolean
}
const PLUGIN_KEYS = new Set(PLUGIN_CLIENTS.map((client) => client.key))
const MCP_KEYS = new Set(MCP_CLIENT_DATA.map((client) => KEY_ALIASES[client.key] ?? client.key))
function buildAgents(): AgentEntry[] {
const byKey = new Map<string, AgentEntry>()
for (const client of MCP_CLIENT_DATA) {
const key = KEY_ALIASES[client.key] ?? client.key
if (EXCLUDED_KEYS.has(key)) continue
byKey.set(key, {
key,
label: client.label,
plugin: PLUGIN_KEYS.has(key),
mcp: true,
})
}
for (const client of PLUGIN_CLIENTS) {
if (EXCLUDED_KEYS.has(client.key)) continue
if (byKey.has(client.key)) continue
byKey.set(client.key, {
key: client.key,
label: client.label,
plugin: true,
mcp: MCP_KEYS.has(client.key),
})
}
return Array.from(byKey.values()).sort((a, b) => a.label.localeCompare(b.label))
}
function badgeFor(plugin: boolean, mcp: boolean): string {
if (plugin && mcp) return 'Plugin + MCP'
if (plugin) return 'Plugin'
return 'MCP'
}
// Route to our own setup instructions rather than each vendor's external docs.
function hrefFor(agent: AgentEntry): string {
return agent.plugin ? '/guides/ai-tools/plugins' : '/guides/ai-tools/mcp'
}
export const aiToolsSupportedAgents: ContentListingGroup = {
id: 'ai-tools-supported-agents',
type: 'grid',
columns: 3,
items: buildAgents().map((agent) => ({
title: agent.label,
href: hrefFor(agent),
description: TAGLINES[agent.key] ?? 'Connect using the Supabase MCP server or plugin.',
icon: ICON_ASSETS[agent.key]?.icon,
hasLightIcon: ICON_ASSETS[agent.key]?.hasLightIcon,
badge: badgeFor(agent.plugin, agent.mcp),
badgePosition: 'below',
})),
}
export const aiToolsBuildingIntoApp: ContentListingGroup = {
id: 'ai-tools-building-into-app',
heading: 'Building AI into your app?',
headingLevel: 'h2',
description:
"The tools above are for your development workflow. If you're building AI capabilities into your own product:",
type: 'grid',
columns: 2,
items: [
{
title: 'Deploy MCP servers',
href: '/guides/ai-tools/byo-mcp',
description:
'Host your own MCP server on Supabase Edge Functions so your users can connect their AI agents to your product',
icon: '/docs/img/icons/product-edge-functions-icon',
hasLightIcon: true,
},
{
title: 'Vectors / Embeddings',
href: '/guides/ai',
description:
'Build semantic search, RAG pipelines, and other AI-powered features using pgvector',
icon: '/docs/img/icons/product-vector-icon',
hasLightIcon: true,
},
],
}