diff --git a/packages/dev/s2-docs/pages/react-aria/mcp.mdx b/packages/dev/s2-docs/pages/react-aria/mcp.mdx new file mode 100644 index 00000000000..6c12e9e1575 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/mcp.mdx @@ -0,0 +1,83 @@ +import {Layout} from '../../src/Layout'; +import {StaticTable} from '../../src/StaticTable'; +import {Command} from '../../src/Command'; +import {Link} from '@react-spectrum/s2'; +export default Layout; + +export const section = 'Guides'; +export const description = 'MCP Server for React Aria'; +export const tags = ['mcp', 'ai', 'documentation', 'tools']; + +# MCP Server + +The `@react-spectrum/mcp` package allows you to run [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) servers for React Aria locally. It exposes a set of tools that MCP clients can discover and call to browse the docs. + +## Using with an MCP client + +Add one or both servers to your MCP client configuration (the exact file and schema may depend on your client). + +```js +{ + "mcpServers": { + "React Aria": { + "command": "npx", + "args": ["@react-spectrum/mcp", "react-aria"] + } + } +} +``` + +### Cursor + + + + + + Add to Cursor + + + + +Or follow Cursor's MCP install [guide](https://docs.cursor.com/en/context/mcp#installing-mcp-servers) and use the standard config above. + +### VS Code + + + Install in Visual Studio Code + + +Or follow VS Code's MCP install [guide](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server) and use the standard config above. You can also add servers using the VS Code CLI: + + + +### Claude Code + +Use the Claude Code CLI to add the servers: + + + +For more information, see the [Claude Code MCP documentation](https://docs.claude.com/en/docs/claude-code/mcp). + +### Codex + +Create or edit the configuration file `~/.codex/config.toml` and add: + +```js +[mcp_servers.react-aria] +command = "npx" +args = ["@react-spectrum/mcp", "react-aria"] +``` + +For more information, see the [Codex MCP documentation](https://github.com/openai/codex/blob/main/docs/config.md#mcp_servers). + +### Gemini CLI + +Use the Gemini CLI to add the servers: + + + +For more information, see the [Gemini CLI MCP documentation](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md#how-to-set-up-your-mcp-server). + +### Windsurf + +Follow the Windsurf MCP [documentation](https://docs.windsurf.com/windsurf/cascade/mcp) and use the standard config above. diff --git a/packages/dev/s2-docs/pages/s2/Icons.mdx b/packages/dev/s2-docs/pages/s2/Icons.mdx index d27f1f4e83e..468eff7f74c 100644 --- a/packages/dev/s2-docs/pages/s2/Icons.mdx +++ b/packages/dev/s2-docs/pages/s2/Icons.mdx @@ -1,5 +1,6 @@ import {Layout} from '../../src/Layout'; import {InstallCommand} from '../../src/InstallCommand'; +import {Command} from '../../src/Command'; import {IconCards} from '../../src/IconCards'; import {IconColors} from '../../src/IconColors'; import {IconSizes} from '../../src/IconSizes'; @@ -116,7 +117,7 @@ Now you can import icon SVGs using the `icon:` [pipeline](https://parceljs.org/f The `@react-spectrum/s2-icon-builder` CLI tool can be used to pre-process a folder of SVG icons into TSX files. -`npx @react-spectrum/s2-icon-builder -i 'path/to/icons/*.svg' -o 'path/to/destination'` + This outputs a folder of TSX files with names corresponding to the input SVG files. You may rename them as you wish. To use them in your application, import them like normal components. `import Icon from './path/to/destination/Icon';` @@ -126,7 +127,7 @@ This outputs a folder of TSX files with names corresponding to the input SVG fil You can also build the icons as a separate library for distribution so that multiple projects can share the same icons. Or possibly you simply do not want to output tsx files. To do this, use the `--isLibrary` flag. -`npx @react-spectrum/s2-icon-builder -i 'path/to/icons/*.svg' -o 'path/to/destination' --isLibrary` + This outputs a folder of ES modules and commonjs modules with names corresponding to the input SVG files. You may rename them as you wish. To use them in your application, import them like normal components. `import Icon from 'library-name/path/to/destination/Icon';` diff --git a/packages/dev/s2-docs/pages/s2/Illustrations.mdx b/packages/dev/s2-docs/pages/s2/Illustrations.mdx index 183ba392d91..7ca25b39d6b 100644 --- a/packages/dev/s2-docs/pages/s2/Illustrations.mdx +++ b/packages/dev/s2-docs/pages/s2/Illustrations.mdx @@ -1,5 +1,6 @@ import {Layout} from '../../src/Layout'; import {InstallCommand} from '../../src/InstallCommand'; +import {Command} from '../../src/Command'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; import {IllustrationCards} from '../../src/IllustrationCards'; export default Layout; @@ -54,7 +55,7 @@ Note that you must use the name `illustration` for the pipeline. The `@react-spectrum/s2-icon-builder` CLI tool can be used to pre-process a folder of SVG illustrations into TSX files. -`npx @react-spectrum/s2-icon-builder -i 'path/to/illustrations/*.svg' --type illustration -o 'path/to/destination'` + This outputs a folder of TSX files with names corresponding to the input SVG files. You may rename them as you wish. To use them in your application, import them like normal components. `import Illustration from './path/to/destination/Illustration';` diff --git a/packages/dev/s2-docs/pages/s2/mcp.mdx b/packages/dev/s2-docs/pages/s2/mcp.mdx new file mode 100644 index 00000000000..0b7a6b2f77b --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/mcp.mdx @@ -0,0 +1,117 @@ +import {Layout} from '../../src/Layout'; +import {StaticTable} from '../../src/StaticTable'; +import {Command} from '../../src/Command'; +import {Link} from '@react-spectrum/s2'; +export default Layout; + +export const section = 'Guides'; +export const description = 'MCP Server for React Spectrum'; +export const tags = ['mcp', 'ai', 'documentation', 'tools']; + +# MCP Server + +The `@react-spectrum/mcp` package allows you to run [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) servers for React Spectrum (S2) and React Aria locally. It exposes a set of tools that MCP clients can discover and call to browse the docs. + +## Using with an MCP client + +Add one or both servers to your MCP client configuration (the exact file and schema may depend on your client). + +```js +{ + "mcpServers": { + "React Spectrum (S2)": { + "command": "npx", + "args": ["@react-spectrum/mcp", "s2"] + }, + "React Aria": { + "command": "npx", + "args": ["@react-spectrum/mcp", "react-aria"] + } + } +} +``` + +### Cursor + +React Spectrum (S2): + + + + + + Add to Cursor + + + +React Aria: + + + + + + Add to Cursor + + + + +Or follow Cursor's MCP install [guide](https://docs.cursor.com/en/context/mcp#installing-mcp-servers) and use the standard config above. + +### VS Code + +React Spectrum (S2): + + + Install in Visual Studio Code + + +React Aria: + + + Install in Visual Studio Code + + +Or follow VS Code's MCP install [guide](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server) and use the standard config above. You can also add servers using the VS Code CLI: + + + + + +### Claude Code + +Use the Claude Code CLI to add the servers: + + + + + +For more information, see the [Claude Code MCP documentation](https://docs.claude.com/en/docs/claude-code/mcp). + +### Codex + +Create or edit the configuration file `~/.codex/config.toml` and add: + +```js +[mcp_servers.react-spectrum-s2] +command = "npx" +args = ["@react-spectrum/mcp", "s2"] + +[mcp_servers.react-aria] +command = "npx" +args = ["@react-spectrum/mcp", "react-aria"] +``` + +For more information, see the [Codex MCP documentation](https://github.com/openai/codex/blob/main/docs/config.md#mcp_servers). + +### Gemini CLI + +Use the Gemini CLI to add the servers: + + + + + +For more information, see the [Gemini CLI MCP documentation](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md#how-to-set-up-your-mcp-server). + +### Windsurf + +Follow the Windsurf MCP [documentation](https://docs.windsurf.com/windsurf/cascade/mcp) and use the standard config above. diff --git a/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs b/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs index fb93e6ff0e1..f9238c5299a 100644 --- a/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs +++ b/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs @@ -695,6 +695,37 @@ function remarkDocsComponentsToMarkdown() { return index; } + // Render a simple command snippet. + if (name === 'Command') { + const commandAttr = node.attributes?.find(a => a.name === 'command'); + if (!commandAttr) { + parent.children.splice(index, 1); + return index; + } + + let command = ''; + if (commandAttr.value?.type === 'mdxJsxAttributeValueExpression') { + command = commandAttr.value.value.replace(/['"`]/g, '').trim(); + } else if (typeof commandAttr.value === 'string') { + command = commandAttr.value.trim(); + } + + if (!command) { + parent.children.splice(index, 1); + return index; + } + + const codeNode = { + type: 'code', + lang: 'bash', + meta: '', + value: command + }; + + parent.children.splice(index, 1, codeNode); + return index; + } + // Render an unordered list of icon names. if (name === 'IconCards') { const iconList = getIconNames(); @@ -1082,6 +1113,18 @@ function remarkDocsComponentsToMarkdown() { } } + // Check for aria-label attribute first + const ariaLabelAttr = node.attributes?.find(a => a.name === 'aria-label'); + let ariaLabel = ''; + + if (ariaLabelAttr) { + if (ariaLabelAttr.value?.type === 'mdxJsxAttributeValueExpression') { + ariaLabel = ariaLabelAttr.value.value.replace(/['"`]/g, '').trim(); + } else if (typeof ariaLabelAttr.value === 'string') { + ariaLabel = ariaLabelAttr.value.trim(); + } + } + // Extract text content from children const extractText = (children) => { if (!children) {return '';} @@ -1098,7 +1141,8 @@ function remarkDocsComponentsToMarkdown() { .join(''); }; - const linkText = extractText(node.children) || href; + const childrenText = extractText(node.children); + const linkText = ariaLabel || childrenText || href; if (href) { const linkNode = { @@ -1106,10 +1150,26 @@ function remarkDocsComponentsToMarkdown() { url: href, children: [{type: 'text', value: linkText}] }; - parent.children[index] = linkNode; + + // If this is a flow element (block-level), wrap in paragraph to preserve spacing + if (node.type === 'mdxJsxFlowElement') { + parent.children[index] = { + type: 'paragraph', + children: [linkNode] + }; + } else { + parent.children[index] = linkNode; + } } else { // No href, just convert to plain text - parent.children[index] = {type: 'text', value: linkText}; + if (node.type === 'mdxJsxFlowElement') { + parent.children[index] = { + type: 'paragraph', + children: [{type: 'text', value: linkText}] + }; + } else { + parent.children[index] = {type: 'text', value: linkText}; + } } return; } diff --git a/packages/dev/s2-docs/src/Command.tsx b/packages/dev/s2-docs/src/Command.tsx new file mode 100644 index 00000000000..750ecc8ac44 --- /dev/null +++ b/packages/dev/s2-docs/src/Command.tsx @@ -0,0 +1,58 @@ +'use client'; + +import {CopyButton} from './CopyButton'; +import React from 'react'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; + +const container = style({ + backgroundColor: 'layer-1', + marginY: 20, + borderRadius: 'xl', + display: 'flex', + flexDirection: 'column' +}); + +const codeWrap = style({ + padding: 16 +}); + +const codeContainer = style({ + display: 'flex', + alignItems: 'center', + gap: 12, + padding: 4 +}); + +const preStyle = style({ + font: {default: 'code-xs', lg: 'code-sm'}, + overflowX: 'auto', + paddingY: 8, + paddingX: 0, + margin: 0, + whiteSpace: 'pre', + flex: 1, + minWidth: 0 +}); + +export interface CommandProps { + /** The command to display. */ + command: string, + /** Optional label preceding the code block. */ + label?: string +} + +export function Command({command, label}: CommandProps) { + return ( +
+
+ {label &&
{label}
} +
+
{command}
+ +
+
+
+ ); +} + +export default Command; diff --git a/packages/dev/s2-docs/src/ComponentCardView.tsx b/packages/dev/s2-docs/src/ComponentCardView.tsx index b20e9700b83..9a0397b3bee 100644 --- a/packages/dev/s2-docs/src/ComponentCardView.tsx +++ b/packages/dev/s2-docs/src/ComponentCardView.tsx @@ -42,6 +42,7 @@ import LabelSvg from '@react-spectrum/docs/pages/assets/component-illustrations/ import LinkSvg from '@react-spectrum/docs/pages/assets/component-illustrations/Link.svg'; import ListBoxSvg from '@react-spectrum/docs/pages/assets/component-illustrations/ListBox.svg'; import ListViewSvg from '@react-spectrum/docs/pages/assets/component-illustrations/ListView.svg'; +import mcpSvg from './icons/mcp.svg'; import MenuSvg from '@react-spectrum/docs/pages/assets/component-illustrations/Menu.svg'; import MeterSvg from '@react-spectrum/docs/pages/assets/component-illustrations/Meter.svg'; import NumberFieldSvg from '@react-spectrum/docs/pages/assets/component-illustrations/NumberField.svg'; @@ -77,7 +78,6 @@ import useKeyboardSvg from '@react-spectrum/docs/pages/assets/component-illustra import useLongPressSvg from '@react-spectrum/docs/pages/assets/component-illustrations/useLongPress.svg'; import useMoveSvg from '@react-spectrum/docs/pages/assets/component-illustrations/useMove.svg'; import usePressSvg from '@react-spectrum/docs/pages/assets/component-illustrations/usePress.svg'; - export interface ComponentCardItem { id: string, name: string, @@ -163,7 +163,8 @@ const componentIllustrations: Record = 'useKeyboard': useKeyboardSvg, 'useLongPress': useLongPressSvg, 'useMove': useMoveSvg, - 'usePress': usePressSvg + 'usePress': usePressSvg, + 'MCP Server': mcpSvg }; // Overrides for specific illustrations so they fit within the cards. diff --git a/packages/dev/s2-docs/src/icons/mcp.svg b/packages/dev/s2-docs/src/icons/mcp.svg new file mode 100644 index 00000000000..0f69b3966ac --- /dev/null +++ b/packages/dev/s2-docs/src/icons/mcp.svg @@ -0,0 +1 @@ +ModelContextProtocol \ No newline at end of file