#!/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);
}