@tpitre/story-ui 2.1.5 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.sample +82 -11
- package/README.md +130 -4
- package/dist/cli/deploy.d.ts +17 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +696 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +55 -2
- package/dist/cli/setup.d.ts +11 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +437 -110
- package/dist/mcp-server/index.d.ts +2 -0
- package/dist/mcp-server/index.d.ts.map +1 -0
- package/dist/mcp-server/index.js +138 -6
- package/dist/mcp-server/mcp-stdio-server.d.ts +3 -0
- package/dist/mcp-server/mcp-stdio-server.d.ts.map +1 -0
- package/dist/mcp-server/mcp-stdio-server.js +638 -0
- package/dist/mcp-server/routes/claude.d.ts +3 -0
- package/dist/mcp-server/routes/claude.d.ts.map +1 -0
- package/dist/mcp-server/routes/claude.js +60 -23
- package/dist/mcp-server/routes/components.d.ts +4 -0
- package/dist/mcp-server/routes/components.d.ts.map +1 -0
- package/dist/mcp-server/routes/frameworks.d.ts +38 -0
- package/dist/mcp-server/routes/frameworks.d.ts.map +1 -0
- package/dist/mcp-server/routes/frameworks.js +183 -0
- package/dist/mcp-server/routes/generateStory.d.ts +3 -0
- package/dist/mcp-server/routes/generateStory.d.ts.map +1 -0
- package/dist/mcp-server/routes/generateStory.js +274 -115
- package/dist/mcp-server/routes/generateStoryStream.d.ts +12 -0
- package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -0
- package/dist/mcp-server/routes/generateStoryStream.js +947 -0
- package/dist/mcp-server/routes/hybridStories.d.ts +18 -0
- package/dist/mcp-server/routes/hybridStories.d.ts.map +1 -0
- package/dist/mcp-server/routes/hybridStories.js +214 -0
- package/dist/mcp-server/routes/mcpRemote.d.ts +14 -0
- package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -0
- package/dist/mcp-server/routes/mcpRemote.js +489 -0
- package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
- package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
- package/dist/mcp-server/routes/memoryStories.js +13 -7
- package/dist/mcp-server/routes/providers.d.ts +89 -0
- package/dist/mcp-server/routes/providers.d.ts.map +1 -0
- package/dist/mcp-server/routes/providers.js +369 -0
- package/dist/mcp-server/routes/storySync.d.ts +26 -0
- package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
- package/dist/mcp-server/routes/streamTypes.d.ts +110 -0
- package/dist/mcp-server/routes/streamTypes.d.ts.map +1 -0
- package/dist/mcp-server/routes/streamTypes.js +18 -0
- package/dist/mcp-server/sessionManager.d.ts +50 -0
- package/dist/mcp-server/sessionManager.d.ts.map +1 -0
- package/dist/mcp-server/sessionManager.js +125 -0
- package/dist/story-generator/componentBlacklist.d.ts +21 -0
- package/dist/story-generator/componentBlacklist.d.ts.map +1 -0
- package/dist/story-generator/componentBlacklist.js +4 -0
- package/dist/story-generator/componentDiscovery.d.ts +28 -0
- package/dist/story-generator/componentDiscovery.d.ts.map +1 -0
- package/dist/story-generator/componentRegistryGenerator.d.ts +49 -0
- package/dist/story-generator/componentRegistryGenerator.d.ts.map +1 -0
- package/dist/story-generator/componentRegistryGenerator.js +205 -0
- package/dist/story-generator/configLoader.d.ts +33 -0
- package/dist/story-generator/configLoader.d.ts.map +1 -0
- package/dist/story-generator/configLoader.js +8 -1
- package/dist/story-generator/considerationsLoader.d.ts +32 -0
- package/dist/story-generator/considerationsLoader.d.ts.map +1 -0
- package/dist/story-generator/considerationsLoader.js +2 -1
- package/dist/story-generator/documentation-sources.d.ts +28 -0
- package/dist/story-generator/documentation-sources.d.ts.map +1 -0
- package/dist/story-generator/documentationLoader.d.ts +64 -0
- package/dist/story-generator/documentationLoader.d.ts.map +1 -0
- package/dist/story-generator/documentationLoader.js +4 -3
- package/dist/story-generator/dynamicPackageDiscovery.d.ts +97 -0
- package/dist/story-generator/dynamicPackageDiscovery.d.ts.map +1 -0
- package/dist/story-generator/dynamicPackageDiscovery.js +31 -22
- package/dist/story-generator/enhancedComponentDiscovery.d.ts +125 -0
- package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -0
- package/dist/story-generator/enhancedComponentDiscovery.js +162 -21
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/angular-adapter.js +427 -0
- package/dist/story-generator/framework-adapters/base-adapter.d.ts +75 -0
- package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/base-adapter.js +147 -0
- package/dist/story-generator/framework-adapters/framework-detector.d.ts +55 -0
- package/dist/story-generator/framework-adapters/framework-detector.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/framework-detector.js +323 -0
- package/dist/story-generator/framework-adapters/index.d.ts +97 -0
- package/dist/story-generator/framework-adapters/index.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/index.js +198 -0
- package/dist/story-generator/framework-adapters/react-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/react-adapter.js +316 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.js +372 -0
- package/dist/story-generator/framework-adapters/types.d.ts +182 -0
- package/dist/story-generator/framework-adapters/types.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/types.js +8 -0
- package/dist/story-generator/framework-adapters/vue-adapter.d.ts +36 -0
- package/dist/story-generator/framework-adapters/vue-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/vue-adapter.js +336 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +54 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.js +387 -0
- package/dist/story-generator/generateStory.d.ts +7 -0
- package/dist/story-generator/generateStory.d.ts.map +1 -0
- package/dist/story-generator/gitignoreManager.d.ts +50 -0
- package/dist/story-generator/gitignoreManager.d.ts.map +1 -0
- package/dist/story-generator/gitignoreManager.js +7 -6
- package/dist/story-generator/imageProcessor.d.ts +80 -0
- package/dist/story-generator/imageProcessor.d.ts.map +1 -0
- package/dist/story-generator/imageProcessor.js +391 -0
- package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
- package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/base-provider.d.ts +36 -0
- package/dist/story-generator/llm-providers/base-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/base-provider.js +135 -0
- package/dist/story-generator/llm-providers/claude-provider.d.ts +23 -0
- package/dist/story-generator/llm-providers/claude-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/claude-provider.js +414 -0
- package/dist/story-generator/llm-providers/gemini-provider.d.ts +24 -0
- package/dist/story-generator/llm-providers/gemini-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/gemini-provider.js +406 -0
- package/dist/story-generator/llm-providers/index.d.ts +63 -0
- package/dist/story-generator/llm-providers/index.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/index.js +169 -0
- package/dist/story-generator/llm-providers/openai-provider.d.ts +24 -0
- package/dist/story-generator/llm-providers/openai-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/openai-provider.js +458 -0
- package/dist/story-generator/llm-providers/settings-manager.d.ts +75 -0
- package/dist/story-generator/llm-providers/settings-manager.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/settings-manager.js +173 -0
- package/dist/story-generator/llm-providers/story-llm-service.d.ts +79 -0
- package/dist/story-generator/llm-providers/story-llm-service.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/story-llm-service.js +240 -0
- package/dist/story-generator/llm-providers/types.d.ts +153 -0
- package/dist/story-generator/llm-providers/types.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/types.js +8 -0
- package/dist/story-generator/logger.d.ts +14 -0
- package/dist/story-generator/logger.d.ts.map +1 -0
- package/dist/story-generator/logger.js +119 -0
- package/dist/story-generator/postProcessStory.d.ts +6 -0
- package/dist/story-generator/postProcessStory.d.ts.map +1 -0
- package/dist/story-generator/postProcessStory.js +8 -7
- package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
- package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
- package/dist/story-generator/productionGitignoreManager.js +11 -10
- package/dist/story-generator/promptGenerator.d.ts +48 -0
- package/dist/story-generator/promptGenerator.d.ts.map +1 -0
- package/dist/story-generator/promptGenerator.js +186 -1
- package/dist/story-generator/storyHistory.d.ts +44 -0
- package/dist/story-generator/storyHistory.d.ts.map +1 -0
- package/dist/story-generator/storySync.d.ts +68 -0
- package/dist/story-generator/storySync.d.ts.map +1 -0
- package/dist/story-generator/storyTracker.d.ts +48 -0
- package/dist/story-generator/storyTracker.d.ts.map +1 -0
- package/dist/story-generator/storyTracker.js +2 -1
- package/dist/story-generator/storyValidator.d.ts +6 -0
- package/dist/story-generator/storyValidator.d.ts.map +1 -0
- package/dist/story-generator/universalDesignSystemAdapter.d.ts +68 -0
- package/dist/story-generator/universalDesignSystemAdapter.d.ts.map +1 -0
- package/dist/story-generator/universalDesignSystemAdapter.js +141 -3
- package/dist/story-generator/urlRedirectService.d.ts +21 -0
- package/dist/story-generator/urlRedirectService.d.ts.map +1 -0
- package/dist/story-generator/urlRedirectService.js +140 -0
- package/dist/story-generator/validateStory.d.ts +19 -0
- package/dist/story-generator/validateStory.d.ts.map +1 -0
- package/dist/story-generator/validateStory.js +6 -2
- package/dist/story-generator/visionPrompts.d.ts +88 -0
- package/dist/story-generator/visionPrompts.d.ts.map +1 -0
- package/dist/story-generator/visionPrompts.js +462 -0
- package/dist/story-ui.config.d.ts +78 -0
- package/dist/story-ui.config.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts +4 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.js +1874 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts +18 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.js +37 -0
- package/dist/templates/StoryUI/index.d.ts +3 -0
- package/dist/templates/StoryUI/index.d.ts.map +1 -0
- package/dist/templates/StoryUI/index.js +2 -0
- package/package.json +35 -4
- package/templates/StoryUI/StoryUIPanel.tsx +1973 -388
- package/templates/StoryUI/index.tsx +1 -1
- package/templates/StoryUI/manager.tsx +264 -0
- package/templates/mcp-config-claude.json +11 -0
- package/templates/mcp-example.md +76 -0
- package/templates/production-app/.env.example +11 -0
- package/templates/production-app/index.html +66 -0
- package/templates/production-app/package.json +30 -0
- package/templates/production-app/public/favicon.svg +5 -0
- package/templates/production-app/src/App.tsx +1157 -0
- package/templates/production-app/src/LivePreviewRenderer.tsx +420 -0
- package/templates/production-app/src/componentRegistry.ts +315 -0
- package/templates/production-app/src/considerations.ts +16 -0
- package/templates/production-app/src/index.css +284 -0
- package/templates/production-app/src/main.tsx +25 -0
- package/templates/production-app/tsconfig.json +32 -0
- package/templates/production-app/tsconfig.node.json +11 -0
- package/templates/production-app/vite.config.ts +83 -0
- package/templates/react-import-rule.json +2 -2
- package/dist/index.js +0 -12
- package/dist/story-ui.config.loader.js +0 -205
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import fetch from 'node-fetch';
|
|
6
|
+
import { loadUserConfig } from '../story-generator/configLoader.js';
|
|
7
|
+
import { EnhancedComponentDiscovery } from '../story-generator/enhancedComponentDiscovery.js';
|
|
8
|
+
import { getInMemoryStoryService } from '../story-generator/inMemoryStoryService.js';
|
|
9
|
+
import { SessionManager } from './sessionManager.js';
|
|
10
|
+
import dotenv from 'dotenv';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import crypto from 'crypto';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
// Get package version dynamically
|
|
16
|
+
const __filename_mcp = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname_mcp = path.dirname(__filename_mcp);
|
|
18
|
+
const packageJsonPath = path.resolve(__dirname_mcp, '../package.json');
|
|
19
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
20
|
+
const PACKAGE_VERSION = packageJson.version;
|
|
21
|
+
// Check for working directory override from environment or command line
|
|
22
|
+
const workingDir = process.env.STORY_UI_CWD || process.argv.find(arg => arg.startsWith('--cwd='))?.split('=')[1];
|
|
23
|
+
if (workingDir) {
|
|
24
|
+
console.error(`Story UI MCP Server: Changing working directory to ${workingDir}`);
|
|
25
|
+
process.chdir(workingDir);
|
|
26
|
+
}
|
|
27
|
+
// Load environment variables
|
|
28
|
+
dotenv.config({ path: path.resolve(process.cwd(), '.env') });
|
|
29
|
+
// Set MCP mode to suppress emojis in logging
|
|
30
|
+
process.env.STORY_UI_MCP_MODE = 'true';
|
|
31
|
+
// Get HTTP server port from environment variables (check multiple possible names)
|
|
32
|
+
const HTTP_PORT = process.env.VITE_STORY_UI_PORT || process.env.STORY_UI_HTTP_PORT || process.env.PORT || '4001';
|
|
33
|
+
const HTTP_BASE_URL = `http://localhost:${HTTP_PORT}`;
|
|
34
|
+
// Initialize configuration
|
|
35
|
+
const config = loadUserConfig();
|
|
36
|
+
const storyService = getInMemoryStoryService(config);
|
|
37
|
+
const sessionManager = SessionManager.getInstance();
|
|
38
|
+
// Generate a session ID for this MCP connection
|
|
39
|
+
const sessionId = crypto.randomBytes(16).toString('hex');
|
|
40
|
+
console.error(`[MCP] Session ID: ${sessionId}`);
|
|
41
|
+
// Create MCP server instance
|
|
42
|
+
const server = new Server({
|
|
43
|
+
name: "story-ui",
|
|
44
|
+
version: PACKAGE_VERSION,
|
|
45
|
+
}, {
|
|
46
|
+
capabilities: {
|
|
47
|
+
tools: {},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
// Define available tools
|
|
51
|
+
const TOOLS = [
|
|
52
|
+
{
|
|
53
|
+
name: "test-connection",
|
|
54
|
+
description: "Test if MCP connection is working",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: "object",
|
|
57
|
+
properties: {},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "generate-story",
|
|
62
|
+
description: "Generate a Storybook story from a natural language prompt",
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: "object",
|
|
65
|
+
properties: {
|
|
66
|
+
prompt: {
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "The prompt describing what UI to generate",
|
|
69
|
+
},
|
|
70
|
+
chatId: {
|
|
71
|
+
type: "string",
|
|
72
|
+
description: "Optional chat ID for tracking",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
required: ["prompt"],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "list-components",
|
|
80
|
+
description: "List all available components that can be used in stories",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
category: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "Filter by category",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "list-stories",
|
|
93
|
+
description: "List all generated stories",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "get-story",
|
|
101
|
+
description: "Get the content of a specific generated story",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: {
|
|
105
|
+
storyId: {
|
|
106
|
+
type: "string",
|
|
107
|
+
description: "The ID of the story to retrieve",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
required: ["storyId"],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "delete-story",
|
|
115
|
+
description: "Delete a generated story",
|
|
116
|
+
inputSchema: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
storyId: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "The ID of the story to delete",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
required: ["storyId"],
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "get-component-props",
|
|
129
|
+
description: "Get detailed prop information for a specific component",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {
|
|
133
|
+
componentName: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "The name of the component",
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
required: ["componentName"],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "update-story",
|
|
143
|
+
description: "Update an existing Storybook story with modifications. If no storyId is provided, I'll update the most recent story or find the right one based on context.",
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: "object",
|
|
146
|
+
properties: {
|
|
147
|
+
storyId: {
|
|
148
|
+
type: "string",
|
|
149
|
+
description: "Optional: The ID of the story to update. If not provided, will use context to find the right story.",
|
|
150
|
+
},
|
|
151
|
+
prompt: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: "Description of the changes to make to the story",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
required: ["prompt"],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
// Set up request handlers
|
|
161
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
162
|
+
return { tools: TOOLS };
|
|
163
|
+
});
|
|
164
|
+
// Handle tool calls
|
|
165
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
166
|
+
const { name, arguments: args } = request.params;
|
|
167
|
+
try {
|
|
168
|
+
switch (name) {
|
|
169
|
+
case "test-connection": {
|
|
170
|
+
return {
|
|
171
|
+
content: [{
|
|
172
|
+
type: "text",
|
|
173
|
+
text: "MCP connection is working! Story UI is connected and ready."
|
|
174
|
+
}]
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
case "generate-story": {
|
|
178
|
+
const { prompt, chatId } = args;
|
|
179
|
+
// Use the HTTP server endpoint to generate the story
|
|
180
|
+
const response = await fetch(`${HTTP_BASE_URL}/mcp/generate-story`, {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
headers: {
|
|
183
|
+
'Content-Type': 'application/json',
|
|
184
|
+
},
|
|
185
|
+
body: JSON.stringify({ prompt, chatId }),
|
|
186
|
+
});
|
|
187
|
+
if (!response.ok) {
|
|
188
|
+
const error = await response.text();
|
|
189
|
+
throw new Error(`Failed to generate story: ${error}`);
|
|
190
|
+
}
|
|
191
|
+
const result = await response.json();
|
|
192
|
+
// Debug log to see what we're getting
|
|
193
|
+
console.error('Story generation result:', JSON.stringify(result, null, 2));
|
|
194
|
+
// Track the story in session
|
|
195
|
+
if (result.storyId && result.fileName && result.title) {
|
|
196
|
+
sessionManager.trackStory(sessionId, {
|
|
197
|
+
id: result.storyId,
|
|
198
|
+
fileName: result.fileName,
|
|
199
|
+
title: result.title,
|
|
200
|
+
prompt: prompt
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
content: [{
|
|
205
|
+
type: "text",
|
|
206
|
+
text: `Story generated successfully!\n\nTitle: ${result.title || 'Untitled'}\nStory ID: ${result.storyId || 'Unknown'}\nFile Name: ${result.fileName || 'Unknown'}\n\nStory Code:\n\`\`\`tsx\n${result.story || 'Story code not available'}\n\`\`\`\n\nOpen your Storybook instance to see the generated story.\n\nTo update this story later, use the Story ID: ${result.storyId}`
|
|
207
|
+
}]
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
case "list-components": {
|
|
211
|
+
const { category } = args;
|
|
212
|
+
try {
|
|
213
|
+
const discovery = new EnhancedComponentDiscovery(config);
|
|
214
|
+
const components = await discovery.discoverAll();
|
|
215
|
+
let filteredComponents = components;
|
|
216
|
+
if (category) {
|
|
217
|
+
filteredComponents = components.filter(comp => comp.category?.toLowerCase() === category.toLowerCase());
|
|
218
|
+
}
|
|
219
|
+
// Limit response size for Claude Desktop
|
|
220
|
+
const maxComponents = 50;
|
|
221
|
+
const displayComponents = filteredComponents.slice(0, maxComponents);
|
|
222
|
+
const componentList = displayComponents.map(comp => `- ${comp.name} (${comp.category || 'Uncategorized'})`).join('\n');
|
|
223
|
+
const responseText = filteredComponents.length > maxComponents
|
|
224
|
+
? `Found ${filteredComponents.length} components (showing first ${maxComponents}):\n\n${componentList}\n\n...and ${filteredComponents.length - maxComponents} more components`
|
|
225
|
+
: `Found ${filteredComponents.length} components:\n\n${componentList}`;
|
|
226
|
+
return {
|
|
227
|
+
content: [{
|
|
228
|
+
type: "text",
|
|
229
|
+
text: responseText
|
|
230
|
+
}]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
console.error('Error in list-components:', error);
|
|
235
|
+
return {
|
|
236
|
+
content: [{
|
|
237
|
+
type: "text",
|
|
238
|
+
text: `Error discovering components: ${error instanceof Error ? error.message : String(error)}`
|
|
239
|
+
}],
|
|
240
|
+
isError: true
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
case "list-stories": {
|
|
245
|
+
try {
|
|
246
|
+
// Get session stories
|
|
247
|
+
const sessionStories = sessionManager.getSessionStories(sessionId);
|
|
248
|
+
// Also try to get file system stories
|
|
249
|
+
let fileStories = [];
|
|
250
|
+
if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
|
|
251
|
+
const files = fs.readdirSync(config.generatedStoriesPath);
|
|
252
|
+
fileStories = files
|
|
253
|
+
.filter(file => file.endsWith('.stories.tsx'))
|
|
254
|
+
.map(file => {
|
|
255
|
+
const hash = file.match(/-([a-f0-9]{8})\.stories\.tsx$/)?.[1] || '';
|
|
256
|
+
const storyId = hash ? `story-${hash}` : file.replace('.stories.tsx', '');
|
|
257
|
+
// Try to read title from file
|
|
258
|
+
let title = file.replace('.stories.tsx', '').replace(/-/g, ' ');
|
|
259
|
+
try {
|
|
260
|
+
const filePath = path.join(config.generatedStoriesPath, file);
|
|
261
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
262
|
+
const titleMatch = content.match(/title:\s*['"]([^'"]+)['"]/);
|
|
263
|
+
if (titleMatch) {
|
|
264
|
+
title = titleMatch[1].replace('Generated/', '');
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
// Use filename as fallback
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
id: storyId,
|
|
272
|
+
fileName: file,
|
|
273
|
+
title,
|
|
274
|
+
source: 'file-system',
|
|
275
|
+
isInSession: sessionStories.some(s => s.id === storyId)
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
if (sessionStories.length === 0 && fileStories.length === 0) {
|
|
280
|
+
return {
|
|
281
|
+
content: [{
|
|
282
|
+
type: "text",
|
|
283
|
+
text: "No stories have been generated yet.\n\nGenerate your first story by describing what UI component you'd like to create!"
|
|
284
|
+
}]
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
let responseText = '';
|
|
288
|
+
// Show session stories first
|
|
289
|
+
if (sessionStories.length > 0) {
|
|
290
|
+
responseText += `**Stories in current session:**\n`;
|
|
291
|
+
const currentStory = sessionManager.getCurrentStory(sessionId);
|
|
292
|
+
sessionStories.forEach(story => {
|
|
293
|
+
const isCurrent = currentStory?.id === story.id;
|
|
294
|
+
responseText += `\n${isCurrent ? '→ ' : ' '}${story.title}\n`;
|
|
295
|
+
responseText += ` ID: ${story.id}\n`;
|
|
296
|
+
responseText += ` File: ${story.fileName}\n`;
|
|
297
|
+
if (isCurrent) {
|
|
298
|
+
responseText += ` (Currently discussing this story)\n`;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
// Show other available stories
|
|
303
|
+
const nonSessionFiles = fileStories.filter(f => !f.isInSession);
|
|
304
|
+
if (nonSessionFiles.length > 0) {
|
|
305
|
+
responseText += `\n\n**Other available stories:**\n`;
|
|
306
|
+
nonSessionFiles.forEach(story => {
|
|
307
|
+
responseText += `\n- ${story.title}\n`;
|
|
308
|
+
responseText += ` ID: ${story.id}\n`;
|
|
309
|
+
responseText += ` File: ${story.fileName}\n`;
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
responseText += `\n\n**Tips:**\n`;
|
|
313
|
+
responseText += `- To update a story, just describe what changes you want\n`;
|
|
314
|
+
responseText += `- I'll automatically work with the most recent story or find the right one based on context\n`;
|
|
315
|
+
responseText += `- You can also specify a story ID directly if needed`;
|
|
316
|
+
return {
|
|
317
|
+
content: [{
|
|
318
|
+
type: "text",
|
|
319
|
+
text: responseText
|
|
320
|
+
}]
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
console.error('Error listing stories:', error);
|
|
325
|
+
return {
|
|
326
|
+
content: [{
|
|
327
|
+
type: "text",
|
|
328
|
+
text: `Error listing stories: ${error instanceof Error ? error.message : String(error)}`
|
|
329
|
+
}],
|
|
330
|
+
isError: true
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
case "get-story": {
|
|
335
|
+
const { storyId } = args;
|
|
336
|
+
try {
|
|
337
|
+
// First try to get the story metadata
|
|
338
|
+
const response = await fetch(`${HTTP_BASE_URL}/mcp/stories/${storyId}`);
|
|
339
|
+
if (!response.ok) {
|
|
340
|
+
throw new Error(`Story with ID ${storyId} not found`);
|
|
341
|
+
}
|
|
342
|
+
const story = await response.json();
|
|
343
|
+
// Then get the story content
|
|
344
|
+
const contentResponse = await fetch(`${HTTP_BASE_URL}/mcp/stories/${storyId}/content`);
|
|
345
|
+
const content = contentResponse.ok ? await contentResponse.text() : story.content || story.story || 'Content not available';
|
|
346
|
+
return {
|
|
347
|
+
content: [{
|
|
348
|
+
type: "text",
|
|
349
|
+
text: `# ${story.title || story.fileName || 'Untitled'}\n\nID: ${story.id || story.storyId || storyId}\nCreated: ${story.timestamp || story.createdAt ? new Date(story.timestamp || story.createdAt).toLocaleString() : 'Unknown'}\n\n## Story Code:\n\`\`\`tsx\n${content}\n\`\`\``
|
|
350
|
+
}]
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
console.error('Error getting story:', error);
|
|
355
|
+
return {
|
|
356
|
+
content: [{
|
|
357
|
+
type: "text",
|
|
358
|
+
text: `Story with ID ${storyId} not found or error retrieving it: ${error instanceof Error ? error.message : String(error)}`
|
|
359
|
+
}],
|
|
360
|
+
isError: true
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
case "delete-story": {
|
|
365
|
+
const { storyId } = args;
|
|
366
|
+
try {
|
|
367
|
+
// Try to delete from file system directly
|
|
368
|
+
if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
|
|
369
|
+
const files = fs.readdirSync(config.generatedStoriesPath);
|
|
370
|
+
// Extract hash from story ID
|
|
371
|
+
const hashMatch = storyId.match(/^story-([a-f0-9]{8})$/);
|
|
372
|
+
const hash = hashMatch ? hashMatch[1] : null;
|
|
373
|
+
// Find matching file
|
|
374
|
+
const matchingFile = files.find(file => {
|
|
375
|
+
if (hash && file.includes(`-${hash}.stories.tsx`))
|
|
376
|
+
return true;
|
|
377
|
+
if (file === `${storyId}.stories.tsx`)
|
|
378
|
+
return true;
|
|
379
|
+
if (file === storyId)
|
|
380
|
+
return true;
|
|
381
|
+
return false;
|
|
382
|
+
});
|
|
383
|
+
if (matchingFile) {
|
|
384
|
+
const filePath = path.join(config.generatedStoriesPath, matchingFile);
|
|
385
|
+
fs.unlinkSync(filePath);
|
|
386
|
+
console.error(`[MCP] Deleted story file: ${filePath}`);
|
|
387
|
+
// Also remove from session
|
|
388
|
+
const sessionStories = sessionManager.getSessionStories(sessionId);
|
|
389
|
+
const storyInSession = sessionStories.find(s => s.id === storyId);
|
|
390
|
+
if (storyInSession) {
|
|
391
|
+
// Note: SessionManager doesn't have a removeStory method yet
|
|
392
|
+
// For now, just clear current if it matches
|
|
393
|
+
const current = sessionManager.getCurrentStory(sessionId);
|
|
394
|
+
if (current?.id === storyId) {
|
|
395
|
+
sessionManager.setCurrentStory(sessionId, '');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
content: [{
|
|
400
|
+
type: "text",
|
|
401
|
+
text: `Story "${matchingFile}" has been deleted successfully.`
|
|
402
|
+
}]
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
// Fallback to HTTP endpoint
|
|
407
|
+
const response = await fetch(`${HTTP_BASE_URL}/mcp/stories/${storyId}`, {
|
|
408
|
+
method: 'DELETE'
|
|
409
|
+
});
|
|
410
|
+
if (!response.ok) {
|
|
411
|
+
throw new Error(`Story not found in file system or via HTTP endpoint`);
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
content: [{
|
|
415
|
+
type: "text",
|
|
416
|
+
text: `Story ${storyId} has been deleted successfully.`
|
|
417
|
+
}]
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
console.error('Error deleting story:', error);
|
|
422
|
+
return {
|
|
423
|
+
content: [{
|
|
424
|
+
type: "text",
|
|
425
|
+
text: `Failed to delete story ${storyId}: ${error instanceof Error ? error.message : String(error)}`
|
|
426
|
+
}],
|
|
427
|
+
isError: true
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
case "get-component-props": {
|
|
432
|
+
const { componentName } = args;
|
|
433
|
+
const response = await fetch(`${HTTP_BASE_URL}/mcp/props?component=${encodeURIComponent(componentName)}`);
|
|
434
|
+
if (!response.ok) {
|
|
435
|
+
throw new Error(`Failed to get component props: ${response.statusText}`);
|
|
436
|
+
}
|
|
437
|
+
const props = await response.json();
|
|
438
|
+
if (!props || Object.keys(props).length === 0) {
|
|
439
|
+
return {
|
|
440
|
+
content: [{
|
|
441
|
+
type: "text",
|
|
442
|
+
text: `No prop information found for component ${componentName}.`
|
|
443
|
+
}]
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
const propsList = Object.entries(props).map(([name, info]) => `- ${name}: ${info.type} ${info.required ? '(required)' : '(optional)'}${info.description ? ` - ${info.description}` : ''}`).join('\n');
|
|
447
|
+
return {
|
|
448
|
+
content: [{
|
|
449
|
+
type: "text",
|
|
450
|
+
text: `Props for ${componentName}:\n\n${propsList}`
|
|
451
|
+
}]
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
case "update-story": {
|
|
455
|
+
let { storyId, prompt } = args;
|
|
456
|
+
try {
|
|
457
|
+
// If no storyId provided, try to find the right story
|
|
458
|
+
if (!storyId) {
|
|
459
|
+
// First check current story in session
|
|
460
|
+
const currentStory = sessionManager.getCurrentStory(sessionId);
|
|
461
|
+
if (currentStory) {
|
|
462
|
+
storyId = currentStory.id;
|
|
463
|
+
console.error(`[MCP] Using current story: ${currentStory.title} (${storyId})`);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
// Try to find by context
|
|
467
|
+
const contextStory = sessionManager.findStoryByContext(sessionId, prompt);
|
|
468
|
+
if (contextStory) {
|
|
469
|
+
storyId = contextStory.id;
|
|
470
|
+
console.error(`[MCP] Found story by context: ${contextStory.title} (${storyId})`);
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
// Use the most recent story
|
|
474
|
+
const sessionStories = sessionManager.getSessionStories(sessionId);
|
|
475
|
+
if (sessionStories.length > 0) {
|
|
476
|
+
const recentStory = sessionStories[sessionStories.length - 1];
|
|
477
|
+
storyId = recentStory.id;
|
|
478
|
+
console.error(`[MCP] Using most recent story: ${recentStory.title} (${storyId})`);
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
return {
|
|
482
|
+
content: [{
|
|
483
|
+
type: "text",
|
|
484
|
+
text: "No story found to update. Please generate a story first or specify which story you'd like to update."
|
|
485
|
+
}],
|
|
486
|
+
isError: true
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
// Try to get story content directly from file system first
|
|
493
|
+
let existingCode = '';
|
|
494
|
+
let storyMetadata = {};
|
|
495
|
+
let foundLocally = false;
|
|
496
|
+
if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
|
|
497
|
+
const files = fs.readdirSync(config.generatedStoriesPath);
|
|
498
|
+
// Extract hash from story ID
|
|
499
|
+
const hashMatch = storyId.match(/^story-([a-f0-9]{8})$/);
|
|
500
|
+
const hash = hashMatch ? hashMatch[1] : null;
|
|
501
|
+
// Find matching file
|
|
502
|
+
const matchingFile = files.find(file => {
|
|
503
|
+
if (hash && file.includes(`-${hash}.stories.tsx`))
|
|
504
|
+
return true;
|
|
505
|
+
if (file === `${storyId}.stories.tsx`)
|
|
506
|
+
return true;
|
|
507
|
+
return false;
|
|
508
|
+
});
|
|
509
|
+
if (matchingFile) {
|
|
510
|
+
const filePath = path.join(config.generatedStoriesPath, matchingFile);
|
|
511
|
+
existingCode = fs.readFileSync(filePath, 'utf-8');
|
|
512
|
+
// Extract metadata from the story content
|
|
513
|
+
const titleMatch = existingCode.match(/title:\s*['"]([^'"]+)['"]/);
|
|
514
|
+
storyMetadata = {
|
|
515
|
+
fileName: matchingFile,
|
|
516
|
+
title: titleMatch ? titleMatch[1] : 'Untitled', // Keep the full title with prefix
|
|
517
|
+
prompt: prompt // Use the update prompt as context
|
|
518
|
+
};
|
|
519
|
+
foundLocally = true;
|
|
520
|
+
console.error(`[MCP] Found story locally: ${filePath}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// If not found locally, fall back to HTTP endpoint
|
|
524
|
+
if (!foundLocally) {
|
|
525
|
+
console.error(`[MCP] Story not found locally, trying HTTP endpoint`);
|
|
526
|
+
const storyResponse = await fetch(`${HTTP_BASE_URL}/mcp/stories/${storyId}`);
|
|
527
|
+
if (!storyResponse.ok) {
|
|
528
|
+
throw new Error(`Story with ID ${storyId} not found`);
|
|
529
|
+
}
|
|
530
|
+
storyMetadata = await storyResponse.json();
|
|
531
|
+
// Get the actual story content
|
|
532
|
+
const contentResponse = await fetch(`${HTTP_BASE_URL}/mcp/stories/${storyId}/content`);
|
|
533
|
+
if (!contentResponse.ok) {
|
|
534
|
+
throw new Error(`Could not retrieve content for story ${storyId}`);
|
|
535
|
+
}
|
|
536
|
+
existingCode = await contentResponse.text();
|
|
537
|
+
}
|
|
538
|
+
// Build conversation context for the update
|
|
539
|
+
const conversation = [
|
|
540
|
+
{
|
|
541
|
+
role: 'user',
|
|
542
|
+
content: storyMetadata.prompt || 'Generate a story'
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
role: 'assistant',
|
|
546
|
+
content: existingCode
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
role: 'user',
|
|
550
|
+
content: prompt
|
|
551
|
+
}
|
|
552
|
+
];
|
|
553
|
+
// Send update request to the generation endpoint
|
|
554
|
+
const response = await fetch(`${HTTP_BASE_URL}/mcp/generate-story`, {
|
|
555
|
+
method: 'POST',
|
|
556
|
+
headers: {
|
|
557
|
+
'Content-Type': 'application/json',
|
|
558
|
+
},
|
|
559
|
+
body: JSON.stringify({
|
|
560
|
+
prompt,
|
|
561
|
+
fileName: storyMetadata.fileName || storyId,
|
|
562
|
+
conversation,
|
|
563
|
+
isUpdate: true,
|
|
564
|
+
originalTitle: storyMetadata.title,
|
|
565
|
+
storyId: storyId
|
|
566
|
+
}),
|
|
567
|
+
});
|
|
568
|
+
if (!response.ok) {
|
|
569
|
+
const error = await response.text();
|
|
570
|
+
throw new Error(`Failed to update story: ${error}`);
|
|
571
|
+
}
|
|
572
|
+
const result = await response.json();
|
|
573
|
+
// Debug log to see what we're getting
|
|
574
|
+
console.error('Story update result:', JSON.stringify(result, null, 2));
|
|
575
|
+
// Update session tracking with preserved metadata
|
|
576
|
+
if (result.storyId && result.fileName && result.title) {
|
|
577
|
+
sessionManager.trackStory(sessionId, {
|
|
578
|
+
id: result.storyId,
|
|
579
|
+
fileName: result.fileName,
|
|
580
|
+
title: result.title,
|
|
581
|
+
prompt: prompt
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
return {
|
|
585
|
+
content: [{
|
|
586
|
+
type: "text",
|
|
587
|
+
text: `Story updated successfully!\n\nTitle: ${result.title || 'Untitled'}\nID: ${result.storyId || result.fileName || 'Unknown'}\n\nUpdated Story Code:\n\`\`\`tsx\n${result.story || 'Story code not available'}\n\`\`\`\n\nThe story has been updated in your Storybook instance.`
|
|
588
|
+
}]
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
catch (error) {
|
|
592
|
+
console.error('Error updating story:', error);
|
|
593
|
+
return {
|
|
594
|
+
content: [{
|
|
595
|
+
type: "text",
|
|
596
|
+
text: `Failed to update story: ${error instanceof Error ? error.message : String(error)}`
|
|
597
|
+
}],
|
|
598
|
+
isError: true
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
default:
|
|
603
|
+
return {
|
|
604
|
+
content: [{
|
|
605
|
+
type: "text",
|
|
606
|
+
text: `Unknown tool: ${name}`
|
|
607
|
+
}],
|
|
608
|
+
isError: true
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
catch (error) {
|
|
613
|
+
return {
|
|
614
|
+
content: [{
|
|
615
|
+
type: "text",
|
|
616
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
617
|
+
}],
|
|
618
|
+
isError: true
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
// Main function to start the server
|
|
623
|
+
async function main() {
|
|
624
|
+
// Log to stderr so it doesn't interfere with stdio communication
|
|
625
|
+
console.error("Story UI MCP Server starting...");
|
|
626
|
+
console.error(`Note: This requires the Story UI HTTP server to be running on port ${HTTP_PORT}`);
|
|
627
|
+
console.error("Run 'story-ui start' in a separate terminal if not already running.\n");
|
|
628
|
+
// Create stdio transport
|
|
629
|
+
const transport = new StdioServerTransport();
|
|
630
|
+
// Connect the server to the transport
|
|
631
|
+
await server.connect(transport);
|
|
632
|
+
console.error("Story UI MCP Server is now running and ready to accept connections.");
|
|
633
|
+
}
|
|
634
|
+
// Run the server
|
|
635
|
+
main().catch((error) => {
|
|
636
|
+
console.error("Failed to start Story UI MCP Server:", error);
|
|
637
|
+
process.exit(1);
|
|
638
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,2DAwE5D"}
|