storybook-mcp-server
Version:
MCP server for Storybook - provides AI assistants access to components, stories, properties and screenshots
271 lines • 10.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const storybook_client_js_1 = require("./storybook-client.js");
const screenshot_service_js_1 = require("./screenshot-service.js");
const logger_js_1 = require("./logger.js");
const config_js_1 = require("./config.js");
const args = (0, config_js_1.parseArgs)();
class StorybookMCPServer {
server;
storybookClient;
screenshotService;
constructor(storybookUrl) {
this.server = new index_js_1.Server({
name: 'storybook-mcp-server',
version: '0.1.0',
}, {
capabilities: {
tools: {},
},
});
this.storybookClient = new storybook_client_js_1.StorybookClient(storybookUrl);
this.screenshotService = new screenshot_service_js_1.ScreenshotService();
this.setupHandlers();
}
setupHandlers() {
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
tools: this.getToolDefinitions(),
}));
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'storybook_list_components':
return await this.handleListComponents();
case 'storybook_list_stories':
return await this.handleListStories(args);
case 'storybook_get_story_details':
return await this.handleGetStoryDetails(args);
case 'storybook_get_component_props':
return await this.handleGetComponentProps(args);
case 'storybook_capture_screenshot':
return await this.handleCaptureScreenshot(args);
case 'storybook_capture_all_screenshots':
return await this.handleCaptureAllScreenshots(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
}
catch (error) {
logger_js_1.logger.error(`Error executing tool ${name}:`, error);
return {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
};
}
});
}
getToolDefinitions() {
return [
{
name: 'storybook_list_components',
description: 'List all components available in the Storybook instance',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'storybook_list_stories',
description: 'List all stories, optionally filtered by component',
inputSchema: {
type: 'object',
properties: {
componentId: {
type: 'string',
description: 'Optional component ID to filter stories',
},
},
},
},
{
name: 'storybook_get_story_details',
description: 'Get detailed information about a specific story',
inputSchema: {
type: 'object',
properties: {
storyId: {
type: 'string',
description: 'The ID of the story',
},
},
required: ['storyId'],
},
},
{
name: 'storybook_get_component_props',
description: 'Get the props/properties definition for a component',
inputSchema: {
type: 'object',
properties: {
componentId: {
type: 'string',
description: 'The ID of the component',
},
},
required: ['componentId'],
},
},
{
name: 'storybook_capture_screenshot',
description: 'Capture a screenshot of a specific story',
inputSchema: {
type: 'object',
properties: {
storyId: {
type: 'string',
description: 'The ID of the story to capture',
},
viewport: {
type: 'object',
properties: {
width: {
type: 'number',
description: 'Viewport width in pixels',
},
height: {
type: 'number',
description: 'Viewport height in pixels',
},
},
description: 'Optional viewport dimensions',
},
},
required: ['storyId'],
},
},
{
name: 'storybook_capture_all_screenshots',
description: 'Capture screenshots of all stories',
inputSchema: {
type: 'object',
properties: {
viewport: {
type: 'object',
properties: {
width: {
type: 'number',
description: 'Viewport width in pixels',
},
height: {
type: 'number',
description: 'Viewport height in pixels',
},
},
description: 'Optional viewport dimensions',
},
},
},
},
];
}
async handleListComponents() {
const components = await this.storybookClient.listComponents();
return {
content: [
{
type: 'text',
text: JSON.stringify(components, null, 2),
},
],
};
}
async handleListStories(args) {
const stories = await this.storybookClient.listStories(args.componentId);
return {
content: [
{
type: 'text',
text: JSON.stringify(stories, null, 2),
},
],
};
}
async handleGetStoryDetails(args) {
const details = await this.storybookClient.getStoryDetails(args.storyId);
return {
content: [
{
type: 'text',
text: JSON.stringify(details, null, 2),
},
],
};
}
async handleGetComponentProps(args) {
const props = await this.storybookClient.getComponentProps(args.componentId);
return {
content: [
{
type: 'text',
text: JSON.stringify(props, null, 2),
},
],
};
}
async handleCaptureScreenshot(args) {
const screenshotPath = await this.screenshotService.captureStoryScreenshot(this.storybookClient.getStorybookUrl(), args.storyId, args.viewport);
return {
content: [
{
type: 'text',
text: `Screenshot captured: ${screenshotPath}`,
},
],
};
}
async handleCaptureAllScreenshots(args) {
const stories = await this.storybookClient.listStories();
const screenshots = await this.screenshotService.captureAllScreenshots(this.storybookClient.getStorybookUrl(), stories.map((s) => s.id), args.viewport);
return {
content: [
{
type: 'text',
text: `Captured ${screenshots.length} screenshots:\n${screenshots.join('\n')}`,
},
],
};
}
async start() {
logger_js_1.logger.info('Starting Storybook MCP Server...');
await this.storybookClient.initialize();
const transport = new stdio_js_1.StdioServerTransport();
await this.server.connect(transport);
logger_js_1.logger.info('Storybook MCP Server started successfully');
}
async stop() {
await this.screenshotService.close();
logger_js_1.logger.info('Storybook MCP Server stopped');
}
}
async function main() {
const server = new StorybookMCPServer(args.storybookUrl);
process.on('SIGINT', async () => {
await server.stop();
process.exit(0);
});
process.on('SIGTERM', async () => {
await server.stop();
process.exit(0);
});
try {
await server.start();
}
catch (error) {
logger_js_1.logger.error('Failed to start server:', error);
process.exit(1);
}
}
main().catch((error) => {
logger_js_1.logger.error('Unhandled error:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map