#!/usr/bin/env bun /** * Preview mcp-ui HTML views in a browser. * * Usage: bun run preview:ui [stores|search] */ import { mkdirSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { execSync } from 'node:child_process'; import { renderStoresDashboard } from '../src/mcp/ui/stores-dashboard.js'; import { renderSearchResults } from '../src/mcp/ui/search-results.js'; const view = process.argv[2] ?? 'stores'; const outDir = join(import.meta.dirname, '..', '.preview'); mkdirSync(outDir, { recursive: true }); if (view === 'stores') { const html = renderStoresDashboard([ { id: 'abc-123', name: 'mcp-ui', type: 'repo', url: 'https://github.com/MCP-UI-Org/mcp-ui', branch: 'main', modelId: 'Xenova/bge-small-en-v1.5', createdAt: new Date(Date.now() - 2 * 86400000).toISOString(), tags: ['sdk', 'ui', 'mcp'], description: 'MCP UI SDK for building interactive web components over MCP', }, { id: 'def-456', name: 'react', type: 'repo', url: 'https://github.com/facebook/react', branch: 'main', modelId: 'Xenova/bge-small-en-v1.5', createdAt: new Date(Date.now() - 30 * 86400000).toISOString(), tags: ['frontend', 'framework'], description: 'The library for web and native user interfaces', }, { id: 'ghi-789', name: 'project-docs', type: 'file', path: '/Users/dev/repos/my-project/docs', createdAt: new Date(Date.now() - 1 * 86400000).toISOString(), description: 'Local project documentation', }, { id: 'jkl-012', name: 'mdn-web-apis', type: 'web', url: 'https://developer.mozilla.org/en-US/docs/Web/API', createdAt: new Date(Date.now() - 7 * 86400000).toISOString(), tags: ['docs', 'web-apis'], description: 'MDN Web API reference documentation', }, { id: 'mno-345', name: 'pending-store', type: 'repo', url: 'https://github.com/example/lib', createdAt: new Date().toISOString(), description: 'A store still being indexed', }, ]); const outFile = join(outDir, 'stores.html'); writeFileSync(outFile, html); console.log(`Wrote ${outFile}`); execSync(`open "${outFile}"`); } else if (view === 'search') { const html = renderSearchResults( 'createUIResource options and usage', [ { id: 'r1', score: 0.92, summary: { type: 'function', name: 'createUIResource', signature: 'function createUIResource(options: CreateUIResourceOptions): UIResource', purpose: 'Creates a UIResource object to include in MCP tool result content arrays', location: 'sdks/typescript/server/src/index.ts:32', relevanceReason: 'Matches: createUIResource, options, usage', storeName: 'mcp-ui', }, context: { interfaces: ['CreateUIResourceOptions', 'UIResource'], keyImports: ['@modelcontextprotocol/ext-apps'], relatedConcepts: ['rawHtml', 'externalUrl', 'encoding', 'mimeType'], usage: { calledBy: 12, calls: 3 }, }, full: { completeCode: `export async function createUIResource(options: CreateUIResourceOptions): Promise { let actualContentString: string; const mimeType: MimeType = RESOURCE_MIME_TYPE; if (!options.uri.startsWith('ui://')) { throw new Error("MCP-UI SDK: URI must start with 'ui://'."); } if (options.content.type === 'rawHtml') { actualContentString = options.content.htmlString; } else if (options.content.type === 'externalUrl') { actualContentString = await fetchExternalUrl(options.content.iframeUrl); } // ... }`, documentation: 'Creates a UIResource for inclusion in tool results.', relatedCode: [ { file: 'types.ts', summary: 'Type definitions for CreateUIResourceOptions', relationship: 'defines input types', }, ], }, }, { id: 'r2', score: 0.78, summary: { type: 'interface', name: 'CreateUIResourceOptions', signature: 'interface CreateUIResourceOptions', purpose: 'Configuration options for creating a UI resource', location: 'sdks/typescript/server/src/types.ts:15', relevanceReason: 'Matches: options, createUIResource', storeName: 'mcp-ui', }, context: { interfaces: ['ResourceContentPayload'], keyImports: [], relatedConcepts: ['uri', 'content', 'encoding', 'embeddedResourceProps'], }, }, { id: 'r3', score: 0.65, summary: { type: 'documentation', name: 'Server Walkthrough', purpose: 'Step-by-step guide for integrating mcp-ui into an MCP server', location: 'docs/src/guide/server/typescript/walkthrough.md', relevanceReason: 'Matches: usage, createUIResource', storeName: 'mcp-ui', }, }, { id: 'r4', score: 0.41, summary: { type: 'const', name: 'RESOURCE_MIME_TYPE', signature: "const RESOURCE_MIME_TYPE = 'text/html;profile=mcp-app'", purpose: 'Standard MIME type for MCP UI resources', location: 'sdks/typescript/server/src/types.ts:3', storeName: 'mcp-ui', }, }, { id: 'r5', score: 0.33, summary: { type: 'example', name: 'showRawHtml tool', purpose: 'Example tool handler returning a raw HTML UI resource', location: 'examples/typescript-server-demo/src/index.ts:79', relevanceReason: 'Matches: usage example of createUIResource', storeName: 'mcp-ui', }, full: { completeCode: `server.registerTool('showRawHtml', { title: 'Show Raw HTML', description: 'Creates a UI resource displaying raw HTML.', inputSchema: {}, }, async () => { const uiResource = await createUIResource({ uri: 'ui://raw-html-demo', content: { type: 'rawHtml', htmlString: '

Hello from Raw HTML

' }, encoding: 'text', }); return { content: [uiResource] }; });`, }, }, ], { timeMs: 29, confidence: 'high', mode: 'hybrid', stores: { 'mcp-ui': { type: 'repo', url: 'https://github.com/MCP-UI-Org/mcp-ui' }, }, } ); const outFile = join(outDir, 'search.html'); writeFileSync(outFile, html); console.log(`Wrote ${outFile}`); execSync(`open "${outFile}"`); } else { console.error(`Unknown view: ${view}. Use "stores" or "search".`); process.exit(1); }