@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.
Files changed (203) hide show
  1. package/.env.sample +82 -11
  2. package/README.md +130 -4
  3. package/dist/cli/deploy.d.ts +17 -0
  4. package/dist/cli/deploy.d.ts.map +1 -0
  5. package/dist/cli/deploy.js +696 -0
  6. package/dist/cli/index.d.ts +3 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +55 -2
  9. package/dist/cli/setup.d.ts +11 -0
  10. package/dist/cli/setup.d.ts.map +1 -0
  11. package/dist/cli/setup.js +437 -110
  12. package/dist/mcp-server/index.d.ts +2 -0
  13. package/dist/mcp-server/index.d.ts.map +1 -0
  14. package/dist/mcp-server/index.js +138 -6
  15. package/dist/mcp-server/mcp-stdio-server.d.ts +3 -0
  16. package/dist/mcp-server/mcp-stdio-server.d.ts.map +1 -0
  17. package/dist/mcp-server/mcp-stdio-server.js +638 -0
  18. package/dist/mcp-server/routes/claude.d.ts +3 -0
  19. package/dist/mcp-server/routes/claude.d.ts.map +1 -0
  20. package/dist/mcp-server/routes/claude.js +60 -23
  21. package/dist/mcp-server/routes/components.d.ts +4 -0
  22. package/dist/mcp-server/routes/components.d.ts.map +1 -0
  23. package/dist/mcp-server/routes/frameworks.d.ts +38 -0
  24. package/dist/mcp-server/routes/frameworks.d.ts.map +1 -0
  25. package/dist/mcp-server/routes/frameworks.js +183 -0
  26. package/dist/mcp-server/routes/generateStory.d.ts +3 -0
  27. package/dist/mcp-server/routes/generateStory.d.ts.map +1 -0
  28. package/dist/mcp-server/routes/generateStory.js +274 -115
  29. package/dist/mcp-server/routes/generateStoryStream.d.ts +12 -0
  30. package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -0
  31. package/dist/mcp-server/routes/generateStoryStream.js +947 -0
  32. package/dist/mcp-server/routes/hybridStories.d.ts +18 -0
  33. package/dist/mcp-server/routes/hybridStories.d.ts.map +1 -0
  34. package/dist/mcp-server/routes/hybridStories.js +214 -0
  35. package/dist/mcp-server/routes/mcpRemote.d.ts +14 -0
  36. package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -0
  37. package/dist/mcp-server/routes/mcpRemote.js +489 -0
  38. package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
  39. package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
  40. package/dist/mcp-server/routes/memoryStories.js +13 -7
  41. package/dist/mcp-server/routes/providers.d.ts +89 -0
  42. package/dist/mcp-server/routes/providers.d.ts.map +1 -0
  43. package/dist/mcp-server/routes/providers.js +369 -0
  44. package/dist/mcp-server/routes/storySync.d.ts +26 -0
  45. package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
  46. package/dist/mcp-server/routes/streamTypes.d.ts +110 -0
  47. package/dist/mcp-server/routes/streamTypes.d.ts.map +1 -0
  48. package/dist/mcp-server/routes/streamTypes.js +18 -0
  49. package/dist/mcp-server/sessionManager.d.ts +50 -0
  50. package/dist/mcp-server/sessionManager.d.ts.map +1 -0
  51. package/dist/mcp-server/sessionManager.js +125 -0
  52. package/dist/story-generator/componentBlacklist.d.ts +21 -0
  53. package/dist/story-generator/componentBlacklist.d.ts.map +1 -0
  54. package/dist/story-generator/componentBlacklist.js +4 -0
  55. package/dist/story-generator/componentDiscovery.d.ts +28 -0
  56. package/dist/story-generator/componentDiscovery.d.ts.map +1 -0
  57. package/dist/story-generator/componentRegistryGenerator.d.ts +49 -0
  58. package/dist/story-generator/componentRegistryGenerator.d.ts.map +1 -0
  59. package/dist/story-generator/componentRegistryGenerator.js +205 -0
  60. package/dist/story-generator/configLoader.d.ts +33 -0
  61. package/dist/story-generator/configLoader.d.ts.map +1 -0
  62. package/dist/story-generator/configLoader.js +8 -1
  63. package/dist/story-generator/considerationsLoader.d.ts +32 -0
  64. package/dist/story-generator/considerationsLoader.d.ts.map +1 -0
  65. package/dist/story-generator/considerationsLoader.js +2 -1
  66. package/dist/story-generator/documentation-sources.d.ts +28 -0
  67. package/dist/story-generator/documentation-sources.d.ts.map +1 -0
  68. package/dist/story-generator/documentationLoader.d.ts +64 -0
  69. package/dist/story-generator/documentationLoader.d.ts.map +1 -0
  70. package/dist/story-generator/documentationLoader.js +4 -3
  71. package/dist/story-generator/dynamicPackageDiscovery.d.ts +97 -0
  72. package/dist/story-generator/dynamicPackageDiscovery.d.ts.map +1 -0
  73. package/dist/story-generator/dynamicPackageDiscovery.js +31 -22
  74. package/dist/story-generator/enhancedComponentDiscovery.d.ts +125 -0
  75. package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -0
  76. package/dist/story-generator/enhancedComponentDiscovery.js +162 -21
  77. package/dist/story-generator/framework-adapters/angular-adapter.d.ts +40 -0
  78. package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -0
  79. package/dist/story-generator/framework-adapters/angular-adapter.js +427 -0
  80. package/dist/story-generator/framework-adapters/base-adapter.d.ts +75 -0
  81. package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -0
  82. package/dist/story-generator/framework-adapters/base-adapter.js +147 -0
  83. package/dist/story-generator/framework-adapters/framework-detector.d.ts +55 -0
  84. package/dist/story-generator/framework-adapters/framework-detector.d.ts.map +1 -0
  85. package/dist/story-generator/framework-adapters/framework-detector.js +323 -0
  86. package/dist/story-generator/framework-adapters/index.d.ts +97 -0
  87. package/dist/story-generator/framework-adapters/index.d.ts.map +1 -0
  88. package/dist/story-generator/framework-adapters/index.js +198 -0
  89. package/dist/story-generator/framework-adapters/react-adapter.d.ts +40 -0
  90. package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -0
  91. package/dist/story-generator/framework-adapters/react-adapter.js +316 -0
  92. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts +40 -0
  93. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts.map +1 -0
  94. package/dist/story-generator/framework-adapters/svelte-adapter.js +372 -0
  95. package/dist/story-generator/framework-adapters/types.d.ts +182 -0
  96. package/dist/story-generator/framework-adapters/types.d.ts.map +1 -0
  97. package/dist/story-generator/framework-adapters/types.js +8 -0
  98. package/dist/story-generator/framework-adapters/vue-adapter.d.ts +36 -0
  99. package/dist/story-generator/framework-adapters/vue-adapter.d.ts.map +1 -0
  100. package/dist/story-generator/framework-adapters/vue-adapter.js +336 -0
  101. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +54 -0
  102. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -0
  103. package/dist/story-generator/framework-adapters/web-components-adapter.js +387 -0
  104. package/dist/story-generator/generateStory.d.ts +7 -0
  105. package/dist/story-generator/generateStory.d.ts.map +1 -0
  106. package/dist/story-generator/gitignoreManager.d.ts +50 -0
  107. package/dist/story-generator/gitignoreManager.d.ts.map +1 -0
  108. package/dist/story-generator/gitignoreManager.js +7 -6
  109. package/dist/story-generator/imageProcessor.d.ts +80 -0
  110. package/dist/story-generator/imageProcessor.d.ts.map +1 -0
  111. package/dist/story-generator/imageProcessor.js +391 -0
  112. package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
  113. package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
  114. package/dist/story-generator/llm-providers/base-provider.d.ts +36 -0
  115. package/dist/story-generator/llm-providers/base-provider.d.ts.map +1 -0
  116. package/dist/story-generator/llm-providers/base-provider.js +135 -0
  117. package/dist/story-generator/llm-providers/claude-provider.d.ts +23 -0
  118. package/dist/story-generator/llm-providers/claude-provider.d.ts.map +1 -0
  119. package/dist/story-generator/llm-providers/claude-provider.js +414 -0
  120. package/dist/story-generator/llm-providers/gemini-provider.d.ts +24 -0
  121. package/dist/story-generator/llm-providers/gemini-provider.d.ts.map +1 -0
  122. package/dist/story-generator/llm-providers/gemini-provider.js +406 -0
  123. package/dist/story-generator/llm-providers/index.d.ts +63 -0
  124. package/dist/story-generator/llm-providers/index.d.ts.map +1 -0
  125. package/dist/story-generator/llm-providers/index.js +169 -0
  126. package/dist/story-generator/llm-providers/openai-provider.d.ts +24 -0
  127. package/dist/story-generator/llm-providers/openai-provider.d.ts.map +1 -0
  128. package/dist/story-generator/llm-providers/openai-provider.js +458 -0
  129. package/dist/story-generator/llm-providers/settings-manager.d.ts +75 -0
  130. package/dist/story-generator/llm-providers/settings-manager.d.ts.map +1 -0
  131. package/dist/story-generator/llm-providers/settings-manager.js +173 -0
  132. package/dist/story-generator/llm-providers/story-llm-service.d.ts +79 -0
  133. package/dist/story-generator/llm-providers/story-llm-service.d.ts.map +1 -0
  134. package/dist/story-generator/llm-providers/story-llm-service.js +240 -0
  135. package/dist/story-generator/llm-providers/types.d.ts +153 -0
  136. package/dist/story-generator/llm-providers/types.d.ts.map +1 -0
  137. package/dist/story-generator/llm-providers/types.js +8 -0
  138. package/dist/story-generator/logger.d.ts +14 -0
  139. package/dist/story-generator/logger.d.ts.map +1 -0
  140. package/dist/story-generator/logger.js +119 -0
  141. package/dist/story-generator/postProcessStory.d.ts +6 -0
  142. package/dist/story-generator/postProcessStory.d.ts.map +1 -0
  143. package/dist/story-generator/postProcessStory.js +8 -7
  144. package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
  145. package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
  146. package/dist/story-generator/productionGitignoreManager.js +11 -10
  147. package/dist/story-generator/promptGenerator.d.ts +48 -0
  148. package/dist/story-generator/promptGenerator.d.ts.map +1 -0
  149. package/dist/story-generator/promptGenerator.js +186 -1
  150. package/dist/story-generator/storyHistory.d.ts +44 -0
  151. package/dist/story-generator/storyHistory.d.ts.map +1 -0
  152. package/dist/story-generator/storySync.d.ts +68 -0
  153. package/dist/story-generator/storySync.d.ts.map +1 -0
  154. package/dist/story-generator/storyTracker.d.ts +48 -0
  155. package/dist/story-generator/storyTracker.d.ts.map +1 -0
  156. package/dist/story-generator/storyTracker.js +2 -1
  157. package/dist/story-generator/storyValidator.d.ts +6 -0
  158. package/dist/story-generator/storyValidator.d.ts.map +1 -0
  159. package/dist/story-generator/universalDesignSystemAdapter.d.ts +68 -0
  160. package/dist/story-generator/universalDesignSystemAdapter.d.ts.map +1 -0
  161. package/dist/story-generator/universalDesignSystemAdapter.js +141 -3
  162. package/dist/story-generator/urlRedirectService.d.ts +21 -0
  163. package/dist/story-generator/urlRedirectService.d.ts.map +1 -0
  164. package/dist/story-generator/urlRedirectService.js +140 -0
  165. package/dist/story-generator/validateStory.d.ts +19 -0
  166. package/dist/story-generator/validateStory.d.ts.map +1 -0
  167. package/dist/story-generator/validateStory.js +6 -2
  168. package/dist/story-generator/visionPrompts.d.ts +88 -0
  169. package/dist/story-generator/visionPrompts.d.ts.map +1 -0
  170. package/dist/story-generator/visionPrompts.js +462 -0
  171. package/dist/story-ui.config.d.ts +78 -0
  172. package/dist/story-ui.config.d.ts.map +1 -0
  173. package/dist/templates/StoryUI/StoryUIPanel.d.ts +4 -0
  174. package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -0
  175. package/dist/templates/StoryUI/StoryUIPanel.js +1874 -0
  176. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts +18 -0
  177. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts.map +1 -0
  178. package/dist/templates/StoryUI/StoryUIPanel.stories.js +37 -0
  179. package/dist/templates/StoryUI/index.d.ts +3 -0
  180. package/dist/templates/StoryUI/index.d.ts.map +1 -0
  181. package/dist/templates/StoryUI/index.js +2 -0
  182. package/package.json +35 -4
  183. package/templates/StoryUI/StoryUIPanel.tsx +1973 -388
  184. package/templates/StoryUI/index.tsx +1 -1
  185. package/templates/StoryUI/manager.tsx +264 -0
  186. package/templates/mcp-config-claude.json +11 -0
  187. package/templates/mcp-example.md +76 -0
  188. package/templates/production-app/.env.example +11 -0
  189. package/templates/production-app/index.html +66 -0
  190. package/templates/production-app/package.json +30 -0
  191. package/templates/production-app/public/favicon.svg +5 -0
  192. package/templates/production-app/src/App.tsx +1157 -0
  193. package/templates/production-app/src/LivePreviewRenderer.tsx +420 -0
  194. package/templates/production-app/src/componentRegistry.ts +315 -0
  195. package/templates/production-app/src/considerations.ts +16 -0
  196. package/templates/production-app/src/index.css +284 -0
  197. package/templates/production-app/src/main.tsx +25 -0
  198. package/templates/production-app/tsconfig.json +32 -0
  199. package/templates/production-app/tsconfig.node.json +11 -0
  200. package/templates/production-app/vite.config.ts +83 -0
  201. package/templates/react-import-rule.json +2 -2
  202. package/dist/index.js +0 -12
  203. 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,3 @@
1
+ import { Request, Response } from 'express';
2
+ export declare function claudeProxy(req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
3
+ //# sourceMappingURL=claude.d.ts.map
@@ -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"}