diff --git a/NOTICE.md b/NOTICE.md index 4e00a0376..4a0f207a4 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -28,7 +28,7 @@ This product includes the following bundled third-party software: - @modelcontextprotocol/ext-apps v1.7.5 [MIT] (used by: @nvidia-elements/cli) Copyright: Olivier Chafik -- @modelcontextprotocol/sdk v1.30.0 [MIT] (used by: @nvidia-elements/cli) +- @modelcontextprotocol/server v2.0.0 [MIT] (used by: @nvidia-elements/cli) Copyright: Anthropic, PBC (https://anthropic.com) - adm-zip v0.5.17 [MIT] (used by: @nvidia-elements/cli) @@ -308,7 +308,7 @@ The following bundled components are provided under the MIT license: @html-eslint/parser v0.61.0 - Copyright yeonjuan (https://github.com/yeonjuan) @inquirer/prompts v8.5.0 - Copyright Simon Boudrias @modelcontextprotocol/ext-apps v1.7.5 - Copyright Olivier Chafik -@modelcontextprotocol/sdk v1.30.0 - Copyright Anthropic, PBC (https://anthropic.com) +@modelcontextprotocol/server v2.0.0 - Copyright Anthropic, PBC (https://anthropic.com) adm-zip v0.5.17 - Copyright Nasca Iacob (https://github.com/cthackers) archiver v8.0.0 - Copyright Chris Talkington (http://christalkington.com/) markdown-it v14.3.0 - Copyright Unknown diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 425a7d415..dc7cd8175 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -477,9 +477,9 @@ importers: '@modelcontextprotocol/ext-apps': specifier: 'catalog:' version: 1.7.5(@modelcontextprotocol/sdk@1.30.0(zod@4.4.3))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.4.3) - '@modelcontextprotocol/sdk': - specifier: 'catalog:' - version: 1.30.0(zod@4.4.3) + '@modelcontextprotocol/server': + specifier: 2.0.0 + version: 2.0.0 '@nvidia-elements/code': specifier: workspace:* version: link:../code @@ -3927,6 +3927,10 @@ packages: engines: {node: '>=18'} hasBin: true + '@modelcontextprotocol/core@2.0.0': + resolution: {integrity: sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==} + engines: {node: '>=20'} + '@modelcontextprotocol/ext-apps@1.7.5': resolution: {integrity: sha512-TjPH2S2y5UEGKhmI6+XGFuqfqOV4ppe1x6DA3txnUaEWkgtA4G5vo14jGKFZmegdkZ1H4QMLyujLvoU1BEdnAg==} engines: {node: '>=20'} @@ -3961,6 +3965,10 @@ packages: '@cfworker/json-schema': optional: true + '@modelcontextprotocol/server@2.0.0': + resolution: {integrity: sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==} + engines: {node: '>=20'} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} cpu: [arm64] @@ -16080,6 +16088,10 @@ snapshots: - encoding - supports-color + '@modelcontextprotocol/core@2.0.0': + dependencies: + zod: 4.4.3 + '@modelcontextprotocol/ext-apps@1.7.5(@modelcontextprotocol/sdk@1.30.0(zod@4.4.3))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.4.3)': dependencies: '@modelcontextprotocol/sdk': 1.30.0(zod@4.4.3) @@ -16133,6 +16145,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@modelcontextprotocol/server@2.0.0': + dependencies: + '@modelcontextprotocol/core': 2.0.0 + zod: 4.4.3 + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': optional: true diff --git a/projects/cli/DEVELOPMENT.md b/projects/cli/DEVELOPMENT.md index 8b881a82c..d97d255fe 100644 --- a/projects/cli/DEVELOPMENT.md +++ b/projects/cli/DEVELOPMENT.md @@ -17,7 +17,7 @@ The package has two entry points: - **`./dist/index.js`** (`nve` command) - Interactive CLI using Yargs with Inquirer for prompts -- **`./dist/mcp/index.js`** (`nve mcp` command) - MCP server using @modelcontextprotocol/sdk +- **`./dist/mcp/index.js`** (`nve mcp` command) - MCP server using `@modelcontextprotocol/server` ### Tool System @@ -60,7 +60,7 @@ tools.forEach(tool => { #### MCP Mode (`src/mcp/index.ts`) - Sets `process.env.ELEMENTS_ENV = 'mcp'` -- Creates MCP server instance with stdio transport +- Serves an MCP server factory over stdio - **Tool registration** - Registers all tools with MCP server using Zod schemas - **Prompt registration** - Registers 6 built-in prompts (about, doctor, search, playground, new-project, migrate) - **Structured output** - Returns results with status, message, and structured content @@ -69,13 +69,14 @@ Example MCP tool registration: ```typescript // Convert JSON Schema to Zod and register with MCP -server.setRequestHandler(ListToolsRequestSchema, async () => ({ - tools: tools.map(tool => ({ - name: tool.toolName, +server.registerTool( + tool.toolName, + { description: tool.description, - inputSchema: zodToJsonSchema(jsonSchemaToZod(tool.inputSchema)) - })) -})); + inputSchema: jsonSchemaToZod(tool.inputSchema) + }, + async params => tool(params) +); ``` ## Data Flow diff --git a/projects/cli/NOTICE.md b/projects/cli/NOTICE.md index 7cbe48f42..175685cfa 100644 --- a/projects/cli/NOTICE.md +++ b/projects/cli/NOTICE.md @@ -11,7 +11,7 @@ This project includes the following bundled third-party software: - @modelcontextprotocol/ext-apps v1.7.5 [MIT] Copyright: Olivier Chafik -- @modelcontextprotocol/sdk v1.30.0 [MIT] +- @modelcontextprotocol/server v2.0.0 [MIT] Copyright: Anthropic, PBC (https://anthropic.com) - adm-zip v0.5.17 [MIT] @@ -49,7 +49,7 @@ The following bundled components are provided under the MIT license: @inquirer/prompts v8.5.0 - Copyright Simon Boudrias @modelcontextprotocol/ext-apps v1.7.5 - Copyright Olivier Chafik -@modelcontextprotocol/sdk v1.30.0 - Copyright Anthropic, PBC (https://anthropic.com) +@modelcontextprotocol/server v2.0.0 - Copyright Anthropic, PBC (https://anthropic.com) adm-zip v0.5.17 - Copyright Nasca Iacob (https://github.com/cthackers) archiver v8.0.0 - Copyright Chris Talkington (http://christalkington.com/) marked v18.0.3 - Copyright Christopher Jeffrey diff --git a/projects/cli/README.md b/projects/cli/README.md index f3fddbaff..4c7b89677 100644 --- a/projects/cli/README.md +++ b/projects/cli/README.md @@ -197,3 +197,4 @@ This hosted route does not install the Elements CLI or configure the MCP server. - [Changelog](https://NVIDIA.github.io/elements/docs/changelog/) - [GitHub Repo](https://github.com/NVIDIA/elements) - [npm](https://www.npmjs.com/package/@nvidia-elements/cli) +- [MCP Registry](https://registry.modelcontextprotocol.io/?q=io.github.NVIDIA%2Felements) diff --git a/projects/cli/package.json b/projects/cli/package.json index e6187f51c..71bddc6b1 100644 --- a/projects/cli/package.json +++ b/projects/cli/package.json @@ -35,7 +35,7 @@ "dist/**/*.js" ], "scripts": { - "dev": "pnpm run cli:install && npx @modelcontextprotocol/inspector@0.22.0 node ./dist/index.js mcp", + "dev": "pnpm run cli:install && npx @modelcontextprotocol/inspector@2.0.0 node ./dist/index.js mcp", "ci": "wireit", "build": "wireit", "lint": "wireit", @@ -47,8 +47,8 @@ }, "dependencies": { "@inquirer/prompts": "8.5.0", - "@modelcontextprotocol/sdk": "catalog:", "@modelcontextprotocol/ext-apps": "catalog:", + "@modelcontextprotocol/server": "2.0.0", "@nvidia-elements/code": "workspace:*", "@nvidia-elements/lint": "workspace:^", "adm-zip": "0.5.17", diff --git a/projects/cli/src/mcp/index.test.ts b/projects/cli/src/mcp/index.test.ts index bff4bc6f1..6e3b1ac85 100644 --- a/projects/cli/src/mcp/index.test.ts +++ b/projects/cli/src/mcp/index.test.ts @@ -8,15 +8,15 @@ const { mockRegisterPrompt, mockRegisterResource, mockRegisterCapabilities, - mockConnect, + mockServeStdio, + mockNotify, mcpTool, cliTool, allTool, uiTool, - mockPrompt, - mockZodSchema + mockPrompt } = vi.hoisted(() => { - const zodSchema = { shape: {}, optional: () => zodSchema }; + const stdioHandle = { close: vi.fn().mockResolvedValue(undefined) }; const createMockTool = (overrides: Record) => { const fn = vi.fn().mockResolvedValue({ status: 'complete', result: 'test' }); @@ -40,8 +40,11 @@ const { mockRegisterPrompt: vi.fn(), mockRegisterResource: vi.fn(), mockRegisterCapabilities: vi.fn(), - mockConnect: vi.fn().mockResolvedValue(undefined), - mockZodSchema: zodSchema, + mockServeStdio: vi.fn((factory: () => unknown) => { + factory(); + return stdioHandle; + }), + mockNotify: vi.fn().mockResolvedValue(undefined), mcpTool: createMockTool({ support: 1, toolName: 'mcp_tool', @@ -83,28 +86,37 @@ const { }; }); -vi.mock('@modelcontextprotocol/sdk/server/mcp.js', () => ({ +vi.mock('@modelcontextprotocol/server', () => ({ McpServer: vi.fn(function () { return { registerTool: mockRegisterTool, registerPrompt: mockRegisterPrompt, registerResource: mockRegisterResource, - server: { registerCapabilities: mockRegisterCapabilities }, - connect: mockConnect + server: { registerCapabilities: mockRegisterCapabilities } }; }) })); -vi.mock('@modelcontextprotocol/sdk/server/stdio.js', () => ({ - StdioServerTransport: vi.fn() +vi.mock('@modelcontextprotocol/server/stdio', () => ({ + serveStdio: mockServeStdio })); -vi.mock('@internals/tools', () => ({ - tools: [mcpTool, cliTool, allTool, uiTool], - prompts: [mockPrompt], - jsonSchemaToZod: vi.fn(() => mockZodSchema), - ToolSupport: { None: 0, MCP: 1, CLI: 2, All: 3 } -})); +vi.mock('@internals/tools', async () => { + const { default: z } = await import('zod'); + return { + tools: [mcpTool, cliTool, allTool, uiTool], + prompts: [mockPrompt], + jsonSchemaToZod: vi.fn(() => z.object({})), + ToolSupport: { None: 0, MCP: 1, CLI: 2, All: 3 } + }; +}); + +const createRequestContext = () => ({ + mcpReq: { + _meta: {}, + notify: mockNotify + } +}); describe('MCP server', () => { beforeEach(() => { @@ -151,12 +163,12 @@ describe('MCP server', () => { ); }); - it('should handle tools without inputSchema', async () => { + it('should register tools without inputSchema using an empty Zod object', async () => { const { startMcpServer } = await import('./index.js'); await startMcpServer(); // mcpTool has no inputSchema — should still register without error const mcpToolCall = mockRegisterTool.mock.calls.find(call => call[0] === 'mcp_tool'); - expect(mcpToolCall[1].inputSchema).toEqual({}); + expect(mcpToolCall[1].inputSchema.shape).toEqual({}); }); it('should register prompts', async () => { @@ -182,17 +194,19 @@ describe('MCP server', () => { expect(result).toEqual({ messages: [] }); }); - it('should connect to stdio transport', async () => { + it('should serve an MCP server factory over stdio', async () => { const { startMcpServer } = await import('./index.js'); await startMcpServer(); - expect(mockConnect).toHaveBeenCalledTimes(1); + expect(mockServeStdio).toHaveBeenCalledWith(expect.any(Function), { + onerror: expect.any(Function) + }); }); it('should return string result as text content', async () => { const { startMcpServer } = await import('./index.js'); await startMcpServer(); const handler = mockRegisterTool.mock.calls[0][2]; - const result = await handler({}); + const result = await handler({}, createRequestContext()); expect(result).toEqual({ structuredContent: { status: 'complete', result: 'test' }, content: [{ type: 'text', text: 'test' }] @@ -205,7 +219,7 @@ describe('MCP server', () => { const handler = mockRegisterTool.mock.calls[0][2]; const errorResult = { status: 'error', message: 'failed' }; mcpTool.mockResolvedValueOnce(errorResult); - const result = await handler({}); + const result = await handler({}, createRequestContext()); expect(result.content[0].text).toBe(JSON.stringify(errorResult)); }); @@ -215,10 +229,37 @@ describe('MCP server', () => { const handler = mockRegisterTool.mock.calls[0][2]; const objResult = { status: 'complete', result: { key: 'value' } }; mcpTool.mockResolvedValueOnce(objResult); - const result = await handler({}); + const result = await handler({}, createRequestContext()); expect(result.content[0].text).toBe(JSON.stringify(objResult)); }); + it('should report progress through the v2 request context', async () => { + const { startMcpServer } = await import('./index.js'); + await startMcpServer(); + const handler = mockRegisterTool.mock.calls[0][2]; + const params: Record = {}; + await handler(params, { + mcpReq: { + _meta: { progressToken: 'progress-token' }, + notify: mockNotify + } + }); + + if (typeof params.onProgress !== 'function') { + throw new TypeError('Expected an onProgress callback'); + } + params.onProgress('Loading metadata'); + + expect(mockNotify).toHaveBeenCalledWith({ + method: 'notifications/progress', + params: { + progressToken: 'progress-token', + progress: 1, + message: 'Loading metadata' + } + }); + }); + it('should advertise the io.modelcontextprotocol/ui extension capability', async () => { const { startMcpServer } = await import('./index.js'); await startMcpServer(); @@ -359,15 +400,17 @@ describe('MCP server', () => { expect(mcpToolCall![1]._meta).toBeUndefined(); }); - it('should exit on connection error', async () => { - mockConnect.mockRejectedValueOnce(new Error('Connection failed')); + it('should exit on stdio error', async () => { const exitSpy = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never); const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const { startMcpServer } = await import('./index.js'); await startMcpServer(); + const options = mockServeStdio.mock.calls[0][1]; + const error = new Error('Connection failed'); + options.onerror(error); - expect(errorSpy).toHaveBeenCalledWith(expect.any(Error)); + expect(errorSpy).toHaveBeenCalledWith(error); expect(exitSpy).toHaveBeenCalledWith(1); }); }); diff --git a/projects/cli/src/mcp/index.ts b/projects/cli/src/mcp/index.ts index ad5e735ef..668bae917 100644 --- a/projects/cli/src/mcp/index.ts +++ b/projects/cli/src/mcp/index.ts @@ -2,15 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 /* istanbul ignore file -- @preserve */ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { McpServer, type ServerContext, type ToolAnnotations } from '@modelcontextprotocol/server'; +import { serveStdio } from '@modelcontextprotocol/server/stdio'; import { tools, prompts, jsonSchemaToZod, ToolSupport } from '@internals/tools'; -import z, { type ZodObject } from 'zod'; -import { type ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; +import z from 'zod'; import { MCP_UI_MIME_TYPE, uiResources } from './ui/index.js'; const VERSION = '0.0.0'; +function getObjectSchema(schema: Parameters[0] | undefined) { + if (!schema) return z.object({}); + const zodSchema = jsonSchemaToZod(schema); + if (!(zodSchema instanceof z.ZodObject)) { + throw new TypeError('Expected an object JSON Schema'); + } + return zodSchema; +} + function registerCapabilities(server: McpServer): void { server.server.registerCapabilities({ extensions: { 'io.modelcontextprotocol/ui': { mimeTypes: [MCP_UI_MIME_TYPE] } } @@ -30,18 +38,14 @@ function registerResources(server: McpServer): void { }); } -function attachProgress( - params: Record, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - extra: any -): void { - const progressToken = extra?._meta?.progressToken; +function attachProgress(params: Record, ctx: ServerContext): void { + const progressToken = ctx.mcpReq._meta?.progressToken; if (progressToken === undefined) return; let progressCount = 0; params.onProgress = (message: string) => { progressCount++; - extra - .sendNotification({ + ctx.mcpReq + .notify({ method: 'notifications/progress', params: { progressToken, progress: progressCount, message } }) @@ -54,18 +58,16 @@ function registerTools(server: McpServer): void { .filter(tool => tool.metadata.support & ToolSupport.MCP) .forEach(tool => { const { summary, description, title, toolName } = tool.metadata; - const inputSchema = tool.metadata.inputSchema - ? (jsonSchemaToZod(tool.metadata.inputSchema) as ZodObject>).shape - : {}; + const inputSchema = getObjectSchema(tool.metadata.inputSchema); const resultSchema = tool.metadata.outputSchema ? jsonSchemaToZod(tool.metadata.outputSchema) : z.any(); const config = { title, inputSchema, - outputSchema: { + outputSchema: z.object({ status: z.enum(['complete', 'error']).optional(), message: z.string().optional(), result: resultSchema.optional() - }, + }), description: description ? description : summary, annotations: { title, @@ -77,8 +79,8 @@ function registerTools(server: McpServer): void { } as ToolAnnotations, _meta: !tool.metadata.app ? undefined : { ui: { resourceUri: tool.metadata.app.resourceUri } } }; - server.registerTool(toolName, config, async (params, extra) => { - attachProgress(params as Record, extra); + server.registerTool(toolName, config, async (params, ctx) => { + attachProgress(params as Record, ctx); const structuredContent = (await tool(params)) as unknown as { [x: string]: unknown }; // https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1624 const text = @@ -92,31 +94,36 @@ function registerTools(server: McpServer): void { function registerPrompts(server: McpServer): void { prompts.forEach(prompt => { - const argsSchema = (jsonSchemaToZod(prompt.argsSchema ?? {}) as ZodObject>).shape; + const argsSchema = getObjectSchema(prompt.argsSchema); const config = { title: prompt.title, description: prompt.description, argsSchema }; server.registerPrompt(prompt.name, config, async params => prompt.handler(params)); }); } -export async function startMcpServer() { +export function startMcpServer() { process.env.ELEMENTS_ENV = 'mcp'; - const server = new McpServer({ - name: 'io.github.NVIDIA/elements', - version: VERSION, - description: - 'NVIDIA Elements UI Design System (nve-*), custom element schemas, APIs and examples. Use the "elements" skill for more guidance if available.' - }); + return serveStdio( + () => { + const server = new McpServer({ + name: 'io.github.NVIDIA/elements', + version: VERSION, + description: + 'NVIDIA Elements UI Design System (nve-*), custom element schemas, APIs and examples. Use the "elements" skill for more guidance if available.' + }); - registerCapabilities(server); - registerResources(server); - registerTools(server); - registerPrompts(server); + registerCapabilities(server); + registerResources(server); + registerTools(server); + registerPrompts(server); - try { - await server.connect(new StdioServerTransport()); - } catch (error) { - console.error(error); - process.exit(1); - } + return server; + }, + { + onerror(error) { + console.error(error); + process.exit(1); + } + } + ); } diff --git a/projects/site/src/_11ty/plugins/sitemap-xml.js b/projects/site/src/_11ty/plugins/sitemap-xml.js index 71b102786..5c647d81a 100644 --- a/projects/site/src/_11ty/plugins/sitemap-xml.js +++ b/projects/site/src/_11ty/plugins/sitemap-xml.js @@ -4,7 +4,6 @@ import { getSiteUrl } from '../utils/site-url.js'; const EXCLUDED_PREFIXES = ['/docs/changelog/', '/docs/metrics/', '/examples/', '/404']; const UTILITY_FILE_URLS = ['/llms.txt', '/llms-full.txt']; -const INDEXABLE_FILE_URLS = ['/DESIGN.md']; const ROBOTS_NOINDEX = /]*name=["']robots["'][^>]*content=["'][^"']*\bnoindex\b/i; const JSON_LD_SCRIPT = /]*\btype=(?:"application\/ld\+json"|'application\/ld\+json'|application\/ld\+json)[^>]*>([\s\S]*?)<\/script>/gi; @@ -12,7 +11,6 @@ const JSON_LD_SCRIPT = export function isSitemapPageUrl(url) { if (!url) return false; if (UTILITY_FILE_URLS.includes(url)) return false; - if (INDEXABLE_FILE_URLS.includes(url)) return true; if (!url.endsWith('/') && !url.endsWith('.html')) return false; if (EXCLUDED_PREFIXES.some(prefix => url.startsWith(prefix))) return false; return true; @@ -38,10 +36,7 @@ function getResultModifiedDate(result) { } export function renderSitemap(results = []) { - const pages = new Map([ - ...INDEXABLE_FILE_URLS.map(url => [url, { url }]), - ...results.filter(isPublishableResult).map(result => [result.url, result]) - ]); + const pages = new Map(results.filter(isPublishableResult).map(result => [result.url, result])); const entries = [...pages.values()] .sort((left, right) => left.url.localeCompare(right.url)) .map(result => { diff --git a/projects/site/src/_11ty/plugins/sitemap-xml.test.ts b/projects/site/src/_11ty/plugins/sitemap-xml.test.ts index 02f5de0ab..55b33c149 100644 --- a/projects/site/src/_11ty/plugins/sitemap-xml.test.ts +++ b/projects/site/src/_11ty/plugins/sitemap-xml.test.ts @@ -8,12 +8,12 @@ describe('isSitemapPageUrl', () => { expect(isSitemapPageUrl('/docs/cli/')).toBe(true); expect(isSitemapPageUrl('/docs/elements/')).toBe(true); expect(isSitemapPageUrl('/docs/foundations/')).toBe(true); - expect(isSitemapPageUrl('/DESIGN.md')).toBe(true); }); it('should exclude agent utility files from sitemap pages', () => { expect(isSitemapPageUrl('/llms.txt')).toBe(false); expect(isSitemapPageUrl('/llms-full.txt')).toBe(false); + expect(isSitemapPageUrl('/DESIGN.md')).toBe(false); }); it('should exclude non-indexable site sections', () => { @@ -39,7 +39,7 @@ describe('isSitemapPageUrl', () => { 'https://nvidia.github.io/elements/docs/whats-new/06-2026/\n2026-07-24T00:00:00.000Z' ); expect(sitemap).toContain('https://nvidia.github.io/elements/docs/about/support/'); - expect(sitemap).toContain('https://nvidia.github.io/elements/DESIGN.md'); + expect(sitemap).not.toContain('https://nvidia.github.io/elements/DESIGN.md'); expect(sitemap.match(//g)).toHaveLength(1); }); }); diff --git a/projects/site/src/docs/mcp/index.md b/projects/site/src/docs/mcp/index.md index fa9d50131..d1d0d21e2 100644 --- a/projects/site/src/docs/mcp/index.md +++ b/projects/site/src/docs/mcp/index.md @@ -8,7 +8,7 @@ # {{title}} -

The Elements MCP server connects AI coding assistants to the Elements design system. It gives tools like Claude Code, Cursor, and Codex direct access to component APIs, design tokens, skills, template validation, project scaffolding, and interactive MCP Apps views so your AI assistant can build with Elements effectively

+

The NVIDIA Elements MCP server connects AI coding assistants to the Elements design system. It gives tools like Claude Code, Cursor, and Codex direct access to component APIs, design tokens, skills, template validation, project scaffolding, and interactive MCP Apps views so your AI assistant can build with Elements effectively

{% install-cli %} @@ -81,7 +81,7 @@ After adding the configuration in the root of your project, restart Codex for th
-```toml +```shell # .codex/config.toml [mcp_servers.elements] description = "NVIDIA Elements UI Design System (nve-*), custom element schemas, APIs and examples" @@ -328,3 +328,7 @@ npm config set registry {{ELEMENTS_REGISTRY_URL}} && npm login --auth-type=legac ### Unsupported Engine An `Unsupported engine` warning means your Node.js version is out of date. The CLI requires Node.js v20 or later. Update Node.js and try again. + +## Links + +- [MCP Registry](https://registry.modelcontextprotocol.io/?q=io.github.NVIDIA%2Felements)