@tpitre/story-ui 2.2.0 → 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 (188) hide show
  1. package/.env.sample +82 -11
  2. package/README.md +89 -0
  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 +26 -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 +120 -2
  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 +8 -1
  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 +160 -76
  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/mcpRemote.d.ts +14 -0
  35. package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -0
  36. package/dist/mcp-server/routes/mcpRemote.js +489 -0
  37. package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
  38. package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
  39. package/dist/mcp-server/routes/providers.d.ts +89 -0
  40. package/dist/mcp-server/routes/providers.d.ts.map +1 -0
  41. package/dist/mcp-server/routes/providers.js +369 -0
  42. package/dist/mcp-server/routes/storySync.d.ts +26 -0
  43. package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
  44. package/dist/mcp-server/routes/streamTypes.d.ts +110 -0
  45. package/dist/mcp-server/routes/streamTypes.d.ts.map +1 -0
  46. package/dist/mcp-server/routes/streamTypes.js +18 -0
  47. package/dist/mcp-server/sessionManager.d.ts +50 -0
  48. package/dist/mcp-server/sessionManager.d.ts.map +1 -0
  49. package/dist/story-generator/componentBlacklist.d.ts +21 -0
  50. package/dist/story-generator/componentBlacklist.d.ts.map +1 -0
  51. package/dist/story-generator/componentDiscovery.d.ts +28 -0
  52. package/dist/story-generator/componentDiscovery.d.ts.map +1 -0
  53. package/dist/story-generator/componentRegistryGenerator.d.ts +49 -0
  54. package/dist/story-generator/componentRegistryGenerator.d.ts.map +1 -0
  55. package/dist/story-generator/componentRegistryGenerator.js +205 -0
  56. package/dist/story-generator/configLoader.d.ts +33 -0
  57. package/dist/story-generator/configLoader.d.ts.map +1 -0
  58. package/dist/story-generator/considerationsLoader.d.ts +32 -0
  59. package/dist/story-generator/considerationsLoader.d.ts.map +1 -0
  60. package/dist/story-generator/documentation-sources.d.ts +28 -0
  61. package/dist/story-generator/documentation-sources.d.ts.map +1 -0
  62. package/dist/story-generator/documentationLoader.d.ts +64 -0
  63. package/dist/story-generator/documentationLoader.d.ts.map +1 -0
  64. package/dist/story-generator/dynamicPackageDiscovery.d.ts +97 -0
  65. package/dist/story-generator/dynamicPackageDiscovery.d.ts.map +1 -0
  66. package/dist/story-generator/enhancedComponentDiscovery.d.ts +125 -0
  67. package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -0
  68. package/dist/story-generator/enhancedComponentDiscovery.js +111 -11
  69. package/dist/story-generator/framework-adapters/angular-adapter.d.ts +40 -0
  70. package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -0
  71. package/dist/story-generator/framework-adapters/angular-adapter.js +427 -0
  72. package/dist/story-generator/framework-adapters/base-adapter.d.ts +75 -0
  73. package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -0
  74. package/dist/story-generator/framework-adapters/base-adapter.js +147 -0
  75. package/dist/story-generator/framework-adapters/framework-detector.d.ts +55 -0
  76. package/dist/story-generator/framework-adapters/framework-detector.d.ts.map +1 -0
  77. package/dist/story-generator/framework-adapters/framework-detector.js +323 -0
  78. package/dist/story-generator/framework-adapters/index.d.ts +97 -0
  79. package/dist/story-generator/framework-adapters/index.d.ts.map +1 -0
  80. package/dist/story-generator/framework-adapters/index.js +198 -0
  81. package/dist/story-generator/framework-adapters/react-adapter.d.ts +40 -0
  82. package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -0
  83. package/dist/story-generator/framework-adapters/react-adapter.js +316 -0
  84. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts +40 -0
  85. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts.map +1 -0
  86. package/dist/story-generator/framework-adapters/svelte-adapter.js +372 -0
  87. package/dist/story-generator/framework-adapters/types.d.ts +182 -0
  88. package/dist/story-generator/framework-adapters/types.d.ts.map +1 -0
  89. package/dist/story-generator/framework-adapters/types.js +8 -0
  90. package/dist/story-generator/framework-adapters/vue-adapter.d.ts +36 -0
  91. package/dist/story-generator/framework-adapters/vue-adapter.d.ts.map +1 -0
  92. package/dist/story-generator/framework-adapters/vue-adapter.js +336 -0
  93. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +54 -0
  94. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -0
  95. package/dist/story-generator/framework-adapters/web-components-adapter.js +387 -0
  96. package/dist/story-generator/generateStory.d.ts +7 -0
  97. package/dist/story-generator/generateStory.d.ts.map +1 -0
  98. package/dist/story-generator/gitignoreManager.d.ts +50 -0
  99. package/dist/story-generator/gitignoreManager.d.ts.map +1 -0
  100. package/dist/story-generator/imageProcessor.d.ts +80 -0
  101. package/dist/story-generator/imageProcessor.d.ts.map +1 -0
  102. package/dist/story-generator/imageProcessor.js +391 -0
  103. package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
  104. package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
  105. package/dist/story-generator/llm-providers/base-provider.d.ts +36 -0
  106. package/dist/story-generator/llm-providers/base-provider.d.ts.map +1 -0
  107. package/dist/story-generator/llm-providers/base-provider.js +135 -0
  108. package/dist/story-generator/llm-providers/claude-provider.d.ts +23 -0
  109. package/dist/story-generator/llm-providers/claude-provider.d.ts.map +1 -0
  110. package/dist/story-generator/llm-providers/claude-provider.js +414 -0
  111. package/dist/story-generator/llm-providers/gemini-provider.d.ts +24 -0
  112. package/dist/story-generator/llm-providers/gemini-provider.d.ts.map +1 -0
  113. package/dist/story-generator/llm-providers/gemini-provider.js +406 -0
  114. package/dist/story-generator/llm-providers/index.d.ts +63 -0
  115. package/dist/story-generator/llm-providers/index.d.ts.map +1 -0
  116. package/dist/story-generator/llm-providers/index.js +169 -0
  117. package/dist/story-generator/llm-providers/openai-provider.d.ts +24 -0
  118. package/dist/story-generator/llm-providers/openai-provider.d.ts.map +1 -0
  119. package/dist/story-generator/llm-providers/openai-provider.js +458 -0
  120. package/dist/story-generator/llm-providers/settings-manager.d.ts +75 -0
  121. package/dist/story-generator/llm-providers/settings-manager.d.ts.map +1 -0
  122. package/dist/story-generator/llm-providers/settings-manager.js +173 -0
  123. package/dist/story-generator/llm-providers/story-llm-service.d.ts +79 -0
  124. package/dist/story-generator/llm-providers/story-llm-service.d.ts.map +1 -0
  125. package/dist/story-generator/llm-providers/story-llm-service.js +240 -0
  126. package/dist/story-generator/llm-providers/types.d.ts +153 -0
  127. package/dist/story-generator/llm-providers/types.d.ts.map +1 -0
  128. package/dist/story-generator/llm-providers/types.js +8 -0
  129. package/dist/story-generator/logger.d.ts +14 -0
  130. package/dist/story-generator/logger.d.ts.map +1 -0
  131. package/dist/story-generator/logger.js +96 -29
  132. package/dist/story-generator/postProcessStory.d.ts +6 -0
  133. package/dist/story-generator/postProcessStory.d.ts.map +1 -0
  134. package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
  135. package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
  136. package/dist/story-generator/promptGenerator.d.ts +48 -0
  137. package/dist/story-generator/promptGenerator.d.ts.map +1 -0
  138. package/dist/story-generator/promptGenerator.js +186 -1
  139. package/dist/story-generator/storyHistory.d.ts +44 -0
  140. package/dist/story-generator/storyHistory.d.ts.map +1 -0
  141. package/dist/story-generator/storySync.d.ts +68 -0
  142. package/dist/story-generator/storySync.d.ts.map +1 -0
  143. package/dist/story-generator/storyTracker.d.ts +48 -0
  144. package/dist/story-generator/storyTracker.d.ts.map +1 -0
  145. package/dist/story-generator/storyValidator.d.ts +6 -0
  146. package/dist/story-generator/storyValidator.d.ts.map +1 -0
  147. package/dist/story-generator/universalDesignSystemAdapter.d.ts +68 -0
  148. package/dist/story-generator/universalDesignSystemAdapter.d.ts.map +1 -0
  149. package/dist/story-generator/universalDesignSystemAdapter.js +138 -1
  150. package/dist/story-generator/urlRedirectService.d.ts +21 -0
  151. package/dist/story-generator/urlRedirectService.d.ts.map +1 -0
  152. package/dist/story-generator/validateStory.d.ts +19 -0
  153. package/dist/story-generator/validateStory.d.ts.map +1 -0
  154. package/dist/story-generator/validateStory.js +6 -2
  155. package/dist/story-generator/visionPrompts.d.ts +88 -0
  156. package/dist/story-generator/visionPrompts.d.ts.map +1 -0
  157. package/dist/story-generator/visionPrompts.js +462 -0
  158. package/dist/story-ui.config.d.ts +78 -0
  159. package/dist/story-ui.config.d.ts.map +1 -0
  160. package/dist/templates/StoryUI/StoryUIPanel.d.ts +4 -0
  161. package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -0
  162. package/dist/templates/StoryUI/StoryUIPanel.js +1874 -0
  163. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts +18 -0
  164. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts.map +1 -0
  165. package/dist/templates/StoryUI/StoryUIPanel.stories.js +37 -0
  166. package/dist/templates/StoryUI/index.d.ts +3 -0
  167. package/dist/templates/StoryUI/index.d.ts.map +1 -0
  168. package/dist/templates/StoryUI/index.js +2 -0
  169. package/package.json +17 -3
  170. package/templates/StoryUI/StoryUIPanel.tsx +1960 -384
  171. package/templates/StoryUI/index.tsx +1 -1
  172. package/templates/StoryUI/manager.tsx +264 -0
  173. package/templates/production-app/.env.example +11 -0
  174. package/templates/production-app/index.html +66 -0
  175. package/templates/production-app/package.json +30 -0
  176. package/templates/production-app/public/favicon.svg +5 -0
  177. package/templates/production-app/src/App.tsx +1157 -0
  178. package/templates/production-app/src/LivePreviewRenderer.tsx +420 -0
  179. package/templates/production-app/src/componentRegistry.ts +315 -0
  180. package/templates/production-app/src/considerations.ts +16 -0
  181. package/templates/production-app/src/index.css +284 -0
  182. package/templates/production-app/src/main.tsx +25 -0
  183. package/templates/production-app/tsconfig.json +32 -0
  184. package/templates/production-app/tsconfig.node.json +11 -0
  185. package/templates/production-app/vite.config.ts +83 -0
  186. package/templates/react-import-rule.json +2 -2
  187. package/dist/index.js +0 -12
  188. package/dist/story-ui.config.loader.js +0 -205
@@ -0,0 +1,18 @@
1
+ import { Request, Response } from 'express';
2
+ /**
3
+ * Get all stories from both memory and file system
4
+ */
5
+ export declare function getAllStories(req: Request, res: Response): void;
6
+ /**
7
+ * Get a specific story by ID from memory or file system
8
+ */
9
+ export declare function getStoryById(req: Request, res: Response): Response<any, Record<string, any>> | undefined;
10
+ /**
11
+ * Get story content by ID
12
+ */
13
+ export declare function getStoryContent(req: Request, res: Response): Response<any, Record<string, any>> | undefined;
14
+ /**
15
+ * Delete a story by ID
16
+ */
17
+ export declare function deleteStory(req: Request, res: Response): Response<any, Record<string, any>> | undefined;
18
+ //# sourceMappingURL=hybridStories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybridStories.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/hybridStories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAO5C;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,QA6DxD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,kDA0DvD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,kDA0C1D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,kDA6CtD"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * MCP Remote HTTP Transport Routes
3
+ *
4
+ * Implements the SSE transport from the MCP SDK to enable
5
+ * remote connections from Claude Desktop and other MCP clients.
6
+ *
7
+ * This allows Story UI to be accessed from Claude Desktop without requiring
8
+ * a local process - useful for cloud deployments and shared Storybook instances.
9
+ *
10
+ * Uses the SSE transport available in MCP SDK v0.5.0
11
+ */
12
+ export declare const router: import("express-serve-static-core").Router;
13
+ export default router;
14
+ //# sourceMappingURL=mcpRemote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpRemote.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/mcpRemote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAgCH,eAAO,MAAM,MAAM,4CAAW,CAAC;AAygB/B,eAAe,MAAM,CAAC"}
@@ -0,0 +1,489 @@
1
+ /**
2
+ * MCP Remote HTTP Transport Routes
3
+ *
4
+ * Implements the SSE transport from the MCP SDK to enable
5
+ * remote connections from Claude Desktop and other MCP clients.
6
+ *
7
+ * This allows Story UI to be accessed from Claude Desktop without requiring
8
+ * a local process - useful for cloud deployments and shared Storybook instances.
9
+ *
10
+ * Uses the SSE transport available in MCP SDK v0.5.0
11
+ */
12
+ import { Router } from 'express';
13
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
14
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
15
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
16
+ import fs from 'fs';
17
+ import path from 'path';
18
+ import fetch from 'node-fetch';
19
+ import { loadUserConfig } from '../../story-generator/configLoader.js';
20
+ import { EnhancedComponentDiscovery } from '../../story-generator/enhancedComponentDiscovery.js';
21
+ // Get package version
22
+ const packageJsonPath = path.resolve(process.cwd(), 'package.json');
23
+ let PACKAGE_VERSION = '1.0.0';
24
+ try {
25
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
26
+ PACKAGE_VERSION = packageJson.version;
27
+ }
28
+ catch {
29
+ // Use default version
30
+ }
31
+ // Get HTTP server port from environment variables
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
+ // Load configuration
35
+ const config = loadUserConfig();
36
+ export const router = Router();
37
+ // Store SSE transports for session management
38
+ const sseTransports = {};
39
+ // Define available tools - same as the stdio server
40
+ const TOOLS = [
41
+ {
42
+ name: 'test-connection',
43
+ description: 'Test if MCP connection is working',
44
+ inputSchema: {
45
+ type: 'object',
46
+ properties: {},
47
+ },
48
+ },
49
+ {
50
+ name: 'generate-story',
51
+ description: 'Generate a Storybook story from a natural language prompt',
52
+ inputSchema: {
53
+ type: 'object',
54
+ properties: {
55
+ prompt: {
56
+ type: 'string',
57
+ description: 'The prompt describing what UI to generate',
58
+ },
59
+ chatId: {
60
+ type: 'string',
61
+ description: 'Optional chat ID for tracking',
62
+ },
63
+ },
64
+ required: ['prompt'],
65
+ },
66
+ },
67
+ {
68
+ name: 'list-components',
69
+ description: 'List all available components that can be used in stories',
70
+ inputSchema: {
71
+ type: 'object',
72
+ properties: {
73
+ category: {
74
+ type: 'string',
75
+ description: 'Filter by category',
76
+ },
77
+ },
78
+ },
79
+ },
80
+ {
81
+ name: 'list-stories',
82
+ description: 'List all generated stories',
83
+ inputSchema: {
84
+ type: 'object',
85
+ properties: {},
86
+ },
87
+ },
88
+ {
89
+ name: 'get-story',
90
+ description: 'Get the content of a specific generated story',
91
+ inputSchema: {
92
+ type: 'object',
93
+ properties: {
94
+ storyId: {
95
+ type: 'string',
96
+ description: 'The ID of the story to retrieve',
97
+ },
98
+ },
99
+ required: ['storyId'],
100
+ },
101
+ },
102
+ {
103
+ name: 'delete-story',
104
+ description: 'Delete a generated story',
105
+ inputSchema: {
106
+ type: 'object',
107
+ properties: {
108
+ storyId: {
109
+ type: 'string',
110
+ description: 'The ID of the story to delete',
111
+ },
112
+ },
113
+ required: ['storyId'],
114
+ },
115
+ },
116
+ {
117
+ name: 'get-component-props',
118
+ description: 'Get detailed prop information for a specific component',
119
+ inputSchema: {
120
+ type: 'object',
121
+ properties: {
122
+ componentName: {
123
+ type: 'string',
124
+ description: 'The name of the component',
125
+ },
126
+ },
127
+ required: ['componentName'],
128
+ },
129
+ },
130
+ {
131
+ name: 'update-story',
132
+ description: 'Update an existing Storybook story with modifications',
133
+ inputSchema: {
134
+ type: 'object',
135
+ properties: {
136
+ storyId: {
137
+ type: 'string',
138
+ description: 'Optional: The ID of the story to update',
139
+ },
140
+ prompt: {
141
+ type: 'string',
142
+ description: 'Description of the changes to make to the story',
143
+ },
144
+ },
145
+ required: ['prompt'],
146
+ },
147
+ },
148
+ ];
149
+ /**
150
+ * Create a configured MCP server instance with Story UI tools
151
+ */
152
+ function createMcpServer() {
153
+ const server = new Server({
154
+ name: 'story-ui-remote',
155
+ version: PACKAGE_VERSION,
156
+ }, {
157
+ capabilities: {
158
+ tools: {},
159
+ },
160
+ });
161
+ // Register tool listing handler
162
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
163
+ return { tools: TOOLS };
164
+ });
165
+ // Register tool execution handler
166
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
167
+ const { name, arguments: args } = request.params;
168
+ try {
169
+ switch (name) {
170
+ case 'test-connection': {
171
+ return {
172
+ content: [{
173
+ type: 'text',
174
+ text: 'MCP remote connection is working! Story UI is connected via HTTP/SSE.',
175
+ }],
176
+ };
177
+ }
178
+ case 'generate-story': {
179
+ const { prompt, chatId } = args;
180
+ const response = await fetch(`${HTTP_BASE_URL}/mcp/generate-story`, {
181
+ method: 'POST',
182
+ headers: { 'Content-Type': 'application/json' },
183
+ body: JSON.stringify({ prompt, chatId }),
184
+ });
185
+ if (!response.ok) {
186
+ const error = await response.text();
187
+ throw new Error(`Failed to generate story: ${error}`);
188
+ }
189
+ const result = await response.json();
190
+ return {
191
+ content: [{
192
+ type: 'text',
193
+ 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}`,
194
+ }],
195
+ };
196
+ }
197
+ case 'list-components': {
198
+ const { category } = args;
199
+ const discovery = new EnhancedComponentDiscovery(config);
200
+ const components = await discovery.discoverAll();
201
+ let filteredComponents = components;
202
+ if (category) {
203
+ filteredComponents = components.filter(comp => comp.category?.toLowerCase() === category.toLowerCase());
204
+ }
205
+ const maxComponents = 50;
206
+ const displayComponents = filteredComponents.slice(0, maxComponents);
207
+ const componentList = displayComponents.map(comp => `- ${comp.name} (${comp.category || 'Uncategorized'})`).join('\n');
208
+ const responseText = filteredComponents.length > maxComponents
209
+ ? `Found ${filteredComponents.length} components (showing first ${maxComponents}):\n\n${componentList}\n\n...and ${filteredComponents.length - maxComponents} more components`
210
+ : `Found ${filteredComponents.length} components:\n\n${componentList}`;
211
+ return {
212
+ content: [{ type: 'text', text: responseText }],
213
+ };
214
+ }
215
+ case 'list-stories': {
216
+ let fileStories = [];
217
+ if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
218
+ const files = fs.readdirSync(config.generatedStoriesPath);
219
+ fileStories = files
220
+ .filter(file => file.endsWith('.stories.tsx'))
221
+ .map(file => {
222
+ const hash = file.match(/-([a-f0-9]{8})\.stories\.tsx$/)?.[1] || '';
223
+ const storyId = hash ? `story-${hash}` : file.replace('.stories.tsx', '');
224
+ let title = file.replace('.stories.tsx', '').replace(/-/g, ' ');
225
+ try {
226
+ const filePath = path.join(config.generatedStoriesPath, file);
227
+ const content = fs.readFileSync(filePath, 'utf-8');
228
+ const titleMatch = content.match(/title:\s*['"]([^'"]+)['"]/);
229
+ if (titleMatch) {
230
+ title = titleMatch[1].replace('Generated/', '');
231
+ }
232
+ }
233
+ catch {
234
+ // Use filename as fallback
235
+ }
236
+ return { id: storyId, fileName: file, title };
237
+ });
238
+ }
239
+ if (fileStories.length === 0) {
240
+ return {
241
+ content: [{
242
+ type: 'text',
243
+ text: 'No stories have been generated yet.\n\nGenerate your first story by describing what UI component you\'d like to create!',
244
+ }],
245
+ };
246
+ }
247
+ let responseText = `**Available stories (${fileStories.length}):**\n`;
248
+ fileStories.forEach(story => {
249
+ responseText += `\n- ${story.title}\n ID: ${story.id}\n File: ${story.fileName}\n`;
250
+ });
251
+ return { content: [{ type: 'text', text: responseText }] };
252
+ }
253
+ case 'get-story': {
254
+ const { storyId } = args;
255
+ const response = await fetch(`${HTTP_BASE_URL}/mcp/stories/${storyId}`);
256
+ if (!response.ok) {
257
+ throw new Error(`Story with ID ${storyId} not found`);
258
+ }
259
+ const story = await response.json();
260
+ const contentResponse = await fetch(`${HTTP_BASE_URL}/mcp/stories/${storyId}/content`);
261
+ const content = contentResponse.ok ? await contentResponse.text() : story.content || story.story || 'Content not available';
262
+ return {
263
+ content: [{
264
+ type: 'text',
265
+ text: `# ${story.title || story.fileName || 'Untitled'}\n\nID: ${story.id || story.storyId || storyId}\n\n## Story Code:\n\`\`\`tsx\n${content}\n\`\`\``,
266
+ }],
267
+ };
268
+ }
269
+ case 'delete-story': {
270
+ const { storyId } = args;
271
+ if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
272
+ const files = fs.readdirSync(config.generatedStoriesPath);
273
+ const hashMatch = storyId.match(/^story-([a-f0-9]{8})$/);
274
+ const hash = hashMatch ? hashMatch[1] : null;
275
+ const matchingFile = files.find(file => {
276
+ if (hash && file.includes(`-${hash}.stories.tsx`))
277
+ return true;
278
+ if (file === `${storyId}.stories.tsx`)
279
+ return true;
280
+ if (file === storyId)
281
+ return true;
282
+ return false;
283
+ });
284
+ if (matchingFile) {
285
+ const filePath = path.join(config.generatedStoriesPath, matchingFile);
286
+ fs.unlinkSync(filePath);
287
+ return {
288
+ content: [{
289
+ type: 'text',
290
+ text: `Story "${matchingFile}" has been deleted successfully.`,
291
+ }],
292
+ };
293
+ }
294
+ }
295
+ throw new Error(`Story not found in file system`);
296
+ }
297
+ case 'get-component-props': {
298
+ const { componentName } = args;
299
+ const response = await fetch(`${HTTP_BASE_URL}/mcp/props?component=${encodeURIComponent(componentName)}`);
300
+ if (!response.ok) {
301
+ throw new Error(`Failed to get component props: ${response.statusText}`);
302
+ }
303
+ const props = await response.json();
304
+ if (!props || Object.keys(props).length === 0) {
305
+ return {
306
+ content: [{
307
+ type: 'text',
308
+ text: `No prop information found for component ${componentName}.`,
309
+ }],
310
+ };
311
+ }
312
+ const propsList = Object.entries(props).map(([propName, info]) => `- ${propName}: ${info.type} ${info.required ? '(required)' : '(optional)'}${info.description ? ` - ${info.description}` : ''}`).join('\n');
313
+ return {
314
+ content: [{
315
+ type: 'text',
316
+ text: `Props for ${componentName}:\n\n${propsList}`,
317
+ }],
318
+ };
319
+ }
320
+ case 'update-story': {
321
+ let { storyId, prompt } = args;
322
+ // Find the story to update if no ID provided
323
+ if (!storyId && config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
324
+ const files = fs.readdirSync(config.generatedStoriesPath)
325
+ .filter(f => f.endsWith('.stories.tsx'))
326
+ .sort((a, b) => {
327
+ const statA = fs.statSync(path.join(config.generatedStoriesPath, a));
328
+ const statB = fs.statSync(path.join(config.generatedStoriesPath, b));
329
+ return statB.mtime.getTime() - statA.mtime.getTime();
330
+ });
331
+ if (files.length > 0) {
332
+ const hash = files[0].match(/-([a-f0-9]{8})\.stories\.tsx$/)?.[1];
333
+ storyId = hash ? `story-${hash}` : files[0].replace('.stories.tsx', '');
334
+ }
335
+ }
336
+ if (!storyId) {
337
+ return {
338
+ content: [{
339
+ type: 'text',
340
+ text: 'No story found to update. Please generate a story first or specify which story you\'d like to update.',
341
+ }],
342
+ isError: true,
343
+ };
344
+ }
345
+ // Get existing story content
346
+ let existingCode = '';
347
+ let storyMetadata = {};
348
+ if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
349
+ const files = fs.readdirSync(config.generatedStoriesPath);
350
+ const hashMatch = storyId.match(/^story-([a-f0-9]{8})$/);
351
+ const hash = hashMatch ? hashMatch[1] : null;
352
+ const matchingFile = files.find(file => {
353
+ if (hash && file.includes(`-${hash}.stories.tsx`))
354
+ return true;
355
+ return false;
356
+ });
357
+ if (matchingFile) {
358
+ const filePath = path.join(config.generatedStoriesPath, matchingFile);
359
+ existingCode = fs.readFileSync(filePath, 'utf-8');
360
+ const titleMatch = existingCode.match(/title:\s*['"]([^'"]+)['"]/);
361
+ storyMetadata = {
362
+ fileName: matchingFile,
363
+ title: titleMatch ? titleMatch[1] : 'Untitled',
364
+ };
365
+ }
366
+ }
367
+ const conversation = [
368
+ { role: 'user', content: storyMetadata.prompt || 'Generate a story' },
369
+ { role: 'assistant', content: existingCode },
370
+ { role: 'user', content: prompt },
371
+ ];
372
+ const response = await fetch(`${HTTP_BASE_URL}/mcp/generate-story`, {
373
+ method: 'POST',
374
+ headers: { 'Content-Type': 'application/json' },
375
+ body: JSON.stringify({
376
+ prompt,
377
+ fileName: storyMetadata.fileName || storyId,
378
+ conversation,
379
+ isUpdate: true,
380
+ originalTitle: storyMetadata.title,
381
+ storyId,
382
+ }),
383
+ });
384
+ if (!response.ok) {
385
+ const error = await response.text();
386
+ throw new Error(`Failed to update story: ${error}`);
387
+ }
388
+ const result = await response.json();
389
+ return {
390
+ content: [{
391
+ type: 'text',
392
+ 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.`,
393
+ }],
394
+ };
395
+ }
396
+ default:
397
+ return {
398
+ content: [{
399
+ type: 'text',
400
+ text: `Unknown tool: ${name}`,
401
+ }],
402
+ isError: true,
403
+ };
404
+ }
405
+ }
406
+ catch (error) {
407
+ return {
408
+ content: [{
409
+ type: 'text',
410
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
411
+ }],
412
+ isError: true,
413
+ };
414
+ }
415
+ });
416
+ return server;
417
+ }
418
+ /**
419
+ * SSE endpoint for establishing the MCP connection
420
+ * GET /mcp-remote/sse
421
+ */
422
+ router.get('/sse', async (req, res) => {
423
+ try {
424
+ console.log('[MCP Remote] New SSE connection request');
425
+ // Create SSE transport
426
+ const transport = new SSEServerTransport('/mcp-remote/messages', res);
427
+ sseTransports[transport.sessionId] = transport;
428
+ console.log(`[MCP Remote] SSE session created: ${transport.sessionId}`);
429
+ // Handle connection close
430
+ res.on('close', () => {
431
+ delete sseTransports[transport.sessionId];
432
+ console.log(`[MCP Remote] SSE session closed: ${transport.sessionId}`);
433
+ });
434
+ // Create and connect the MCP server
435
+ // Note: server.connect() calls transport.start() internally
436
+ const server = createMcpServer();
437
+ await server.connect(transport);
438
+ }
439
+ catch (error) {
440
+ console.error('[MCP Remote] Error creating SSE session:', error);
441
+ if (!res.headersSent) {
442
+ res.status(500).send('Failed to create SSE session');
443
+ }
444
+ }
445
+ });
446
+ /**
447
+ * Message endpoint for SSE transport
448
+ * POST /mcp-remote/messages
449
+ */
450
+ router.post('/messages', async (req, res) => {
451
+ const sessionId = req.query.sessionId;
452
+ if (!sessionId) {
453
+ res.status(400).json({ error: 'sessionId query parameter is required' });
454
+ return;
455
+ }
456
+ const transport = sseTransports[sessionId];
457
+ if (!transport) {
458
+ res.status(400).json({ error: `No transport found for sessionId: ${sessionId}` });
459
+ return;
460
+ }
461
+ try {
462
+ // Use the raw node request/response for handlePostMessage
463
+ await transport.handlePostMessage(req, res);
464
+ }
465
+ catch (error) {
466
+ console.error('[MCP Remote] Error handling message:', error);
467
+ if (!res.headersSent) {
468
+ res.status(500).json({ error: 'Failed to handle message' });
469
+ }
470
+ }
471
+ });
472
+ /**
473
+ * Health check endpoint for the MCP remote service
474
+ * GET /mcp-remote/health
475
+ */
476
+ router.get('/health', (req, res) => {
477
+ res.json({
478
+ status: 'ok',
479
+ service: 'story-ui-mcp-remote',
480
+ version: PACKAGE_VERSION,
481
+ transport: 'SSE',
482
+ activeSessions: Object.keys(sseTransports).length,
483
+ endpoints: {
484
+ sse: '/mcp-remote/sse',
485
+ messages: '/mcp-remote/messages',
486
+ },
487
+ });
488
+ });
489
+ export default router;
@@ -0,0 +1,26 @@
1
+ import { Request, Response } from 'express';
2
+ /**
3
+ * Get all stories metadata
4
+ */
5
+ export declare function getStoriesMetadata(req: Request, res: Response): void;
6
+ /**
7
+ * Get a specific story by ID
8
+ */
9
+ export declare function getStoryById(req: Request, res: Response): Response<any, Record<string, any>> | undefined;
10
+ /**
11
+ * Get story content for Storybook integration
12
+ */
13
+ export declare function getStoryContent(req: Request, res: Response): Response<any, Record<string, any>> | undefined;
14
+ /**
15
+ * Delete a story by ID
16
+ */
17
+ export declare function deleteStory(req: Request, res: Response): Response<any, Record<string, any>> | undefined;
18
+ /**
19
+ * Clear all stories
20
+ */
21
+ export declare function clearAllStories(req: Request, res: Response): void;
22
+ /**
23
+ * Get memory usage statistics
24
+ */
25
+ export declare function getMemoryStats(req: Request, res: Response): void;
26
+ //# sourceMappingURL=memoryStories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryStories.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/memoryStories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,QAiB7D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,kDAwBvD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,kDAuB1D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,kDAwBtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,QAgB1D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,QAoBzD"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * LLM Provider Management Routes
3
+ *
4
+ * API endpoints for managing LLM providers, API keys, and model selection.
5
+ */
6
+ import { Request, Response } from 'express';
7
+ /**
8
+ * GET /mcp/providers
9
+ * Returns list of available providers and their configuration status
10
+ */
11
+ export declare function getProviders(req: Request, res: Response): void;
12
+ /**
13
+ * GET /mcp/providers/models
14
+ * Returns all available models across all providers
15
+ */
16
+ export declare function getModels(req: Request, res: Response): void;
17
+ /**
18
+ * POST /mcp/providers/configure
19
+ * Configure a provider with API key and optional model
20
+ *
21
+ * Body: {
22
+ * provider: 'claude' | 'openai' | 'gemini',
23
+ * apiKey: string,
24
+ * model?: string,
25
+ * setAsDefault?: boolean
26
+ * }
27
+ */
28
+ export declare function configureProviderRoute(req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
29
+ /**
30
+ * POST /mcp/providers/validate
31
+ * Validate an API key for a provider without saving it
32
+ *
33
+ * Body: {
34
+ * provider: 'claude' | 'openai' | 'gemini',
35
+ * apiKey: string
36
+ * }
37
+ */
38
+ export declare function validateApiKey(req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
39
+ /**
40
+ * POST /mcp/providers/default
41
+ * Set the default provider
42
+ *
43
+ * Body: {
44
+ * provider: 'claude' | 'openai' | 'gemini'
45
+ * }
46
+ */
47
+ export declare function setDefaultProvider(req: Request, res: Response): Response<any, Record<string, any>> | undefined;
48
+ /**
49
+ * POST /mcp/providers/model
50
+ * Set the model for a provider
51
+ *
52
+ * Body: {
53
+ * provider?: 'claude' | 'openai' | 'gemini', // defaults to current provider
54
+ * model: string
55
+ * }
56
+ */
57
+ export declare function setModel(req: Request, res: Response): Response<any, Record<string, any>> | undefined;
58
+ /**
59
+ * GET /mcp/providers/settings
60
+ * Get UI settings configuration with available providers/models
61
+ *
62
+ * This endpoint is designed for non-technical users to select
63
+ * from pre-approved providers and models configured by DevOps.
64
+ *
65
+ * Query params:
66
+ * provider?: current selected provider
67
+ * model?: current selected model
68
+ */
69
+ export declare function getUISettings(req: Request, res: Response): void;
70
+ /**
71
+ * POST /mcp/providers/settings
72
+ * Apply user's provider/model selection
73
+ *
74
+ * Body: {
75
+ * provider?: 'claude' | 'openai' | 'gemini',
76
+ * model?: string
77
+ * }
78
+ *
79
+ * Returns effective settings (may fall back to defaults if invalid)
80
+ */
81
+ export declare function applyUISettings(req: Request, res: Response): void;
82
+ /**
83
+ * GET /mcp/providers/config
84
+ * Get current environment configuration (for admin/debugging)
85
+ *
86
+ * This reveals which restrictions are in place.
87
+ */
88
+ export declare function getSettingsConfig(req: Request, res: Response): void;
89
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/providers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAoB5C;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,QAqBvD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,QA0BpD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,2DAkDvE;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,2DAkC/D;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,kDAwC7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,kDAwDnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,QAkBxD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,QAyC1D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,QAuB5D"}