#
tokens: 6882/50000 7/7 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── index.ts
│   └── utils
│       ├── flowParser.ts
│       └── repoHandlers.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | node_modules/
2 | build/
3 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # UIFlowchartCreator
 2 | 
 3 | UIFlowchartCreator is an MCP (Model Context Protocol) server for creating UI flowcharts. This tool helps developers and designers visualize user interfaces and their interactions.
 4 | 
 5 | ## GitHub Repository
 6 | 
 7 | The source code for this project is available on GitHub:
 8 | [https://github.com/umshere/uiflowchartcreator](https://github.com/umshere/uiflowchartcreator)
 9 | 
10 | ## Features
11 | 
12 | - Generate UI flowcharts based on input specifications
13 | - Integrate with MCP-compatible systems
14 | - Easy-to-use API for flowchart creation
15 | 
16 | ## Installation
17 | 
18 | ```bash
19 | npm install uiflowchartcreator
20 | ```
21 | 
22 | ## Usage
23 | 
24 | To use UIFlowchartCreator in your MCP-compatible system, add it to your MCP configuration:
25 | 
26 | ```json
27 | {
28 |   "mcpServers": {
29 |     "uiflowchartcreator": {
30 |       "command": "node",
31 |       "args": ["path/to/uiflowchartcreator/build/index.js"],
32 |       "env": {}
33 |     }
34 |   }
35 | }
36 | ```
37 | 
38 | For detailed usage instructions and API documentation, please refer to the source code and comments in `src/index.ts`.
39 | 
40 | ## Contributing
41 | 
42 | Contributions are welcome! Please feel free to submit a Pull Request.
43 | 
44 | ## License
45 | 
46 | This project is licensed under the ISC License.
47 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "es2020",
 4 |     "module": "es2020",
 5 |     "moduleResolution": "node",
 6 |     "esModuleInterop": true,
 7 |     "outDir": "./build",
 8 |     "strict": true,
 9 |     "skipLibCheck": true
10 |   },
11 |   "include": ["src/**/*"]
12 | }
13 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |     "name": "uiflowchartcreator",
 3 |     "version": "1.0.3",
 4 |     "description": "MCP server for creating UI flowcharts",
 5 |     "main": "build/index.js",
 6 |     "type": "module",
 7 |     "bin": {
 8 |         "uiflowchartcreator": "build/index.js"
 9 |     },
10 |     "scripts": {
11 |         "start": "node build/index.js",
12 |         "build": "echo 'Running TypeScript compiler...' && tsc && echo 'TypeScript compilation complete. Setting file permissions...' && node -e \"require('fs').chmodSync('build/index.js', '755')\" && echo 'Build completed successfully'"
13 |     },
14 |     "keywords": [
15 |         "mcp",
16 |         "ui",
17 |         "flowchart",
18 |         "generator",
19 |         "modelcontextprotocol",
20 |         "uiflowchart"
21 |     ],
22 |     "author": "",
23 |     "license": "ISC",
24 |     "files": [
25 |         "build",
26 |         "README.md"
27 |     ],
28 |     "repository": {
29 |         "type": "git",
30 |         "url": "https://github.com/umshere/uiflowchartcreator.git"
31 |     },
32 |     "homepage": "https://github.com/umshere/uiflowchartcreator#readme",
33 |     "bugs": {
34 |         "url": "https://github.com/umshere/uiflowchartcreator/issues"
35 |     },
36 |     "dependencies": {
37 |         "@modelcontextprotocol/sdk": "^1.0.4",
38 |         "axios": "^1.4.0"
39 |     },
40 |     "devDependencies": {
41 |         "@types/axios": "^0.14.0",
42 |         "@types/node": "^20.4.1",
43 |         "typescript": "^5.1.3"
44 |     }
45 | }
```

--------------------------------------------------------------------------------
/src/utils/repoHandlers.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import axios from "axios";
  2 | import fs from "fs/promises";
  3 | import path from "path";
  4 | import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
  5 | 
  6 | export interface RepoContents {
  7 |   name: string;
  8 |   path: string;
  9 |   type: string;
 10 |   content?: string;
 11 |   download_url?: string;
 12 |   owner?: string;
 13 |   repo?: string;
 14 | }
 15 | 
 16 | export async function fetchGitHubRepoContents(
 17 |   owner: string,
 18 |   repo: string,
 19 |   repoPath: string = ""
 20 | ): Promise<RepoContents[]> {
 21 |   console.log(
 22 |     `[MCP] Fetching GitHub repo contents for ${owner}/${repo}${
 23 |       repoPath ? `/${repoPath}` : ""
 24 |     }`
 25 |   );
 26 | 
 27 |   const githubToken = process.env.GITHUB_TOKEN;
 28 |   if (!githubToken) {
 29 |     throw new McpError(
 30 |       ErrorCode.InvalidRequest,
 31 |       "GitHub token is required. Set GITHUB_TOKEN environment variable."
 32 |     );
 33 |   }
 34 | 
 35 |   try {
 36 |     const response = await axios.get(
 37 |       `https://api.github.com/repos/${owner}/${repo}/contents/${repoPath}`,
 38 |       {
 39 |         headers: {
 40 |           Accept: "application/vnd.github.v3+json",
 41 |           Authorization: `token ${githubToken}`,
 42 |           "User-Agent": "UIFlowChartCreator-MCP",
 43 |         },
 44 |       }
 45 |     );
 46 | 
 47 |     if (!response.data) {
 48 |       throw new McpError(
 49 |         ErrorCode.InvalidRequest,
 50 |         `No data returned from GitHub API for ${owner}/${repo}`
 51 |       );
 52 |     }
 53 | 
 54 |     const excludeList = [
 55 |       "node_modules",
 56 |       ".git",
 57 |       "dist",
 58 |       "build",
 59 |       ".vscode",
 60 |       ".idea",
 61 |       "test",
 62 |       "__tests__",
 63 |     ];
 64 | 
 65 |     const excludeFiles = [
 66 |       ".env",
 67 |       ".gitignore",
 68 |       "package-lock.json",
 69 |       "yarn.lock",
 70 |     ];
 71 | 
 72 |     return response.data.filter((item: RepoContents) => {
 73 |       if (item.type === "dir" && excludeList.includes(item.name)) {
 74 |         return false;
 75 |       }
 76 |       if (item.type === "file" && excludeFiles.includes(item.name)) {
 77 |         return false;
 78 |       }
 79 |       return true;
 80 |     });
 81 |   } catch (error) {
 82 |     if (axios.isAxiosError(error)) {
 83 |       throw new McpError(
 84 |         ErrorCode.InvalidRequest,
 85 |         `GitHub API error: ${error.response?.data?.message || error.message}`
 86 |       );
 87 |     }
 88 |     throw error;
 89 |   }
 90 | }
 91 | 
 92 | export async function fetchLocalRepoContents(
 93 |   repoPath: string
 94 | ): Promise<RepoContents[]> {
 95 |   console.log(`[MCP] Fetching local repo contents from ${repoPath}`);
 96 | 
 97 |   try {
 98 |     const contents: RepoContents[] = [];
 99 |     const items = await fs.readdir(repoPath, { withFileTypes: true });
100 | 
101 |     const excludeList = [
102 |       "node_modules",
103 |       ".git",
104 |       "dist",
105 |       "build",
106 |       ".vscode",
107 |       ".idea",
108 |       "test",
109 |       "__tests__",
110 |     ];
111 | 
112 |     const excludeFiles = [
113 |       ".env",
114 |       ".gitignore",
115 |       "package-lock.json",
116 |       "yarn.lock",
117 |     ];
118 | 
119 |     for (const item of items) {
120 |       if (
121 |         excludeList.includes(item.name) ||
122 |         (item.isFile() && excludeFiles.includes(item.name))
123 |       )
124 |         continue;
125 | 
126 |       const itemPath = path.join(repoPath, item.name);
127 |       if (item.isDirectory()) {
128 |         contents.push({
129 |           name: item.name,
130 |           path: itemPath,
131 |           type: "dir",
132 |         });
133 |       } else if (item.isFile()) {
134 |         const content = await fs.readFile(itemPath, "utf-8");
135 |         contents.push({
136 |           name: item.name,
137 |           path: itemPath,
138 |           type: "file",
139 |           content,
140 |         });
141 |       }
142 |     }
143 | 
144 |     return contents;
145 |   } catch (error) {
146 |     throw new McpError(
147 |       ErrorCode.InvalidRequest,
148 |       `Failed to read local repository: ${
149 |         error instanceof Error ? error.message : String(error)
150 |       }`
151 |     );
152 |   }
153 | }
154 | 
```

--------------------------------------------------------------------------------
/src/utils/flowParser.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import path from "path";
  2 | import axios from "axios";
  3 | import {
  4 |   RepoContents,
  5 |   fetchLocalRepoContents,
  6 |   fetchGitHubRepoContents,
  7 | } from "./repoHandlers.js";
  8 | import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
  9 | 
 10 | export interface ComponentInfo {
 11 |   name: string;
 12 |   type: "page" | "layout" | "component";
 13 |   filePath: string;
 14 |   imports: string[];
 15 |   children: ComponentInfo[];
 16 | }
 17 | 
 18 | export async function parseUIFlow(
 19 |   contents: RepoContents[],
 20 |   isLocal: boolean,
 21 |   fileExtensions: string[] = ["js", "jsx", "ts", "tsx"]
 22 | ): Promise<string> {
 23 |   console.log(
 24 |     `[MCP] Parsing UI flow with extensions: ${fileExtensions.join(", ")}`
 25 |   );
 26 | 
 27 |   const components: { [key: string]: ComponentInfo } = {};
 28 | 
 29 |   async function processContents(
 30 |     currentContents: RepoContents[],
 31 |     currentPath: string = ""
 32 |   ) {
 33 |     for (const item of currentContents) {
 34 |       if (
 35 |         item.type === "file" &&
 36 |         fileExtensions.some((ext) => item.name.endsWith(`.${ext}`))
 37 |       ) {
 38 |         let content: string;
 39 |         if (isLocal) {
 40 |           content = item.content || "";
 41 |         } else {
 42 |           try {
 43 |             const response = await axios.get(item.download_url || "");
 44 |             content = response.data;
 45 |           } catch (error) {
 46 |             console.warn(
 47 |               `[MCP] Failed to fetch content for ${item.name}: ${error}`
 48 |             );
 49 |             continue;
 50 |           }
 51 |         }
 52 | 
 53 |         const componentName = item.name.split(".")[0];
 54 |         const componentPath = path.join(currentPath, componentName);
 55 |         const componentType = getComponentType(componentPath);
 56 | 
 57 |         components[componentPath] = {
 58 |           name: componentName,
 59 |           type: componentType,
 60 |           filePath: path.join(currentPath, item.name),
 61 |           imports: [],
 62 |           children: [],
 63 |         };
 64 | 
 65 |         // Analyze import statements
 66 |         const importMatches = content.match(
 67 |           /import\s+(\w+|\{[^}]+\})\s+from\s+['"]([^'"]+)['"]/g
 68 |         );
 69 |         if (importMatches) {
 70 |           importMatches.forEach((match) => {
 71 |             const [, importedComponent, importPath] =
 72 |               match.match(
 73 |                 /import\s+(\w+|\{[^}]+\})\s+from\s+['"]([^'"]+)['"]/
 74 |               ) || [];
 75 |             if (importedComponent) {
 76 |               const cleanedImport = importedComponent
 77 |                 .replace(/[{}]/g, "")
 78 |                 .trim();
 79 |               const resolvedPath = path.join(
 80 |                 currentPath,
 81 |                 path.dirname(importPath),
 82 |                 cleanedImport
 83 |               );
 84 |               components[componentPath].imports.push(resolvedPath);
 85 |             }
 86 |           });
 87 |         }
 88 |       } else if (item.type === "dir") {
 89 |         const subContents = isLocal
 90 |           ? await fetchLocalRepoContents(item.path)
 91 |           : await fetchGitHubRepoContents(
 92 |               item.owner || "",
 93 |               item.repo || "",
 94 |               item.path
 95 |             );
 96 | 
 97 |         await processContents(subContents, path.join(currentPath, item.name));
 98 |       }
 99 |     }
100 |   }
101 | 
102 |   await processContents(contents);
103 | 
104 |   // Build component hierarchy
105 |   const rootComponents: ComponentInfo[] = [];
106 |   Object.values(components).forEach((component) => {
107 |     component.imports.forEach((importPath) => {
108 |       if (components[importPath]) {
109 |         components[importPath].children.push(component);
110 |       }
111 |     });
112 |     if (component.imports.length === 0) {
113 |       rootComponents.push(component);
114 |     }
115 |   });
116 | 
117 |   return JSON.stringify(rootComponents, null, 2);
118 | }
119 | 
120 | function getComponentType(
121 |   componentPath: string
122 | ): "page" | "layout" | "component" {
123 |   // Special handling for App component
124 |   if (componentPath.toLowerCase().includes("app")) {
125 |     return "page";
126 |   }
127 | 
128 |   // Detect pages based on common patterns
129 |   if (
130 |     componentPath.includes("pages") ||
131 |     componentPath.includes("views") ||
132 |     componentPath.toLowerCase().includes("meals")
133 |   ) {
134 |     return "page";
135 |   }
136 | 
137 |   // Detect layouts based on common patterns
138 |   if (
139 |     componentPath.includes("layouts") ||
140 |     componentPath.toLowerCase().includes("header")
141 |   ) {
142 |     return "layout";
143 |   }
144 | 
145 |   // Default to component
146 |   return "component";
147 | }
148 | 
149 | export function generateMermaidFlowchart(components: ComponentInfo[]): string {
150 |   let chart = "flowchart TD\n";
151 | 
152 |   // Create a map of all components for quick lookup
153 |   const componentMap = new Map<string, ComponentInfo>();
154 |   components.forEach((component) => {
155 |     componentMap.set(component.name, component);
156 |   });
157 | 
158 |   // Create nodes with proper styling and hierarchy
159 |   const createNode = (component: ComponentInfo, depth: number = 0): string => {
160 |     const nodeId = component.name.replace(/[^a-zA-Z0-9]/g, "_");
161 |     const indent = "  ".repeat(depth);
162 | 
163 |     // Determine node style based on type
164 |     let nodeStyle = "";
165 |     switch (component.type) {
166 |       case "page":
167 |         nodeStyle = "(( ))";
168 |         break;
169 |       case "layout":
170 |         nodeStyle = "{{ }}";
171 |         break;
172 |       default:
173 |         nodeStyle = "[/ /]";
174 |     }
175 | 
176 |     // Add node with proper indentation
177 |     chart += `${indent}${nodeId}${nodeStyle}["${component.name} (${component.type})"]\n`;
178 | 
179 |     // Recursively process children
180 |     component.children.forEach((child) => {
181 |       const childComponent = componentMap.get(child.name);
182 |       if (childComponent) {
183 |         createNode(childComponent, depth + 1);
184 |       }
185 |     });
186 | 
187 |     return nodeId;
188 |   };
189 | 
190 |   // Find root components (those with no parents)
191 |   const rootComponents = components.filter(
192 |     (component) =>
193 |       !components.some((c) =>
194 |         c.children.some((child) => child.name === component.name)
195 |       )
196 |   );
197 | 
198 |   // Start building the chart from root components
199 |   rootComponents.forEach((component) => {
200 |     createNode(component);
201 |   });
202 | 
203 |   // Create relationships with labels
204 |   components.forEach((component) => {
205 |     const parentId = component.name.replace(/[^a-zA-Z0-9]/g, "_");
206 | 
207 |     component.children.forEach((child) => {
208 |       const childId = child.name.replace(/[^a-zA-Z0-9]/g, "_");
209 |       const relationshipType = determineRelationshipType(component, child);
210 |       chart += `  ${parentId} -->|${relationshipType}| ${childId}\n`;
211 |     });
212 |   });
213 | 
214 |   // Validate Mermaid.js syntax
215 |   try {
216 |     // Basic validation - check for required elements
217 |     if (!chart.includes("flowchart TD")) {
218 |       throw new Error("Missing flowchart declaration");
219 |     }
220 |     if (!chart.match(/\[.*\]/)) {
221 |       throw new Error("Missing node definitions");
222 |     }
223 |     if (!chart.match(/-->|--/)) {
224 |       throw new Error("Missing relationship definitions");
225 |     }
226 |   } catch (error) {
227 |     console.error("[MCP] Mermaid.js validation error:", error);
228 |     throw new McpError(
229 |       ErrorCode.InternalError,
230 |       `Failed to generate valid Mermaid.js chart: ${error}`
231 |     );
232 |   }
233 | 
234 |   return chart;
235 | }
236 | 
237 | function determineRelationshipType(
238 |   parent: ComponentInfo,
239 |   child: ComponentInfo
240 | ): string {
241 |   if (parent.type === "layout" && child.type === "page") {
242 |     return "contains";
243 |   }
244 |   if (parent.type === "page" && child.type === "component") {
245 |     return "uses";
246 |   }
247 |   if (parent.type === "component" && child.type === "component") {
248 |     return "composes";
249 |   }
250 |   return "relates to";
251 | }
252 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  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 {
  5 |   CallToolRequestSchema,
  6 |   ErrorCode,
  7 |   ListToolsRequestSchema,
  8 |   McpError,
  9 |   ReadResourceRequestSchema,
 10 | } from "@modelcontextprotocol/sdk/types.js";
 11 | import fs from "fs/promises";
 12 | import path from "path";
 13 | import {
 14 |   fetchLocalRepoContents,
 15 |   fetchGitHubRepoContents,
 16 |   RepoContents,
 17 | } from "./utils/repoHandlers.js";
 18 | import { parseUIFlow, generateMermaidFlowchart } from "./utils/flowParser.js";
 19 | 
 20 | // Initialize MCP server with capabilities
 21 | const server = new Server(
 22 |   {
 23 |     name: "uiflowchartcreator",
 24 |     version: "1.0.1",
 25 |     capabilities: {
 26 |       resources: {
 27 |         "ui-flow": {
 28 |           name: "UI Flow Resource",
 29 |           description: "Access generated UI flow diagrams",
 30 |           uriTemplate: "ui-flow://{owner}/{repo}",
 31 |         },
 32 |       },
 33 |       tools: {
 34 |         generate_ui_flow: {
 35 |           name: "generate_ui_flow",
 36 |           description:
 37 |             "Generate a UI flow diagram by analyzing React/Angular repositories. This tool scans the codebase to identify components, their relationships, and the overall UI structure.",
 38 |           inputSchema: {
 39 |             type: "object",
 40 |             properties: {
 41 |               repoPath: {
 42 |                 type: "string",
 43 |                 description:
 44 |                   "Path to local repository or empty string for GitHub repos",
 45 |               },
 46 |               isLocal: {
 47 |                 type: "boolean",
 48 |                 description:
 49 |                   "Whether to analyze a local repository (true) or GitHub repository (false)",
 50 |               },
 51 |               owner: {
 52 |                 type: "string",
 53 |                 description:
 54 |                   "GitHub repository owner (required if isLocal is false)",
 55 |               },
 56 |               repo: {
 57 |                 type: "string",
 58 |                 description:
 59 |                   "GitHub repository name (required if isLocal is false)",
 60 |               },
 61 |               fileExtensions: {
 62 |                 type: "array",
 63 |                 items: { type: "string" },
 64 |                 description:
 65 |                   "List of file extensions to analyze (e.g., ['js', 'jsx', 'ts', 'tsx'] for React, ['ts', 'html'] for Angular)",
 66 |                 default: ["js", "jsx", "ts", "tsx"],
 67 |               },
 68 |             },
 69 |             required: ["repoPath", "isLocal"],
 70 |             additionalProperties: false,
 71 |           },
 72 |         },
 73 |       },
 74 |     },
 75 |   },
 76 |   {
 77 |     capabilities: {
 78 |       resources: {},
 79 |       tools: {},
 80 |     },
 81 |   }
 82 | );
 83 | 
 84 | // List available tools
 85 | server.setRequestHandler(ListToolsRequestSchema, async () => {
 86 |   console.log("[MCP] Listing available tools");
 87 |   return {
 88 |     tools: [
 89 |       {
 90 |         name: "generate_ui_flow",
 91 |         description:
 92 |           "Generate a UI flow diagram by analyzing React/Angular repositories. This tool scans the codebase to identify components, their relationships, and the overall UI structure.",
 93 |         inputSchema: {
 94 |           type: "object",
 95 |           properties: {
 96 |             repoPath: {
 97 |               type: "string",
 98 |               description:
 99 |                 "Path to local repository or empty string for GitHub repos",
100 |             },
101 |             isLocal: {
102 |               type: "boolean",
103 |               description:
104 |                 "Whether to analyze a local repository (true) or GitHub repository (false)",
105 |             },
106 |             owner: {
107 |               type: "string",
108 |               description:
109 |                 "GitHub repository owner (required if isLocal is false)",
110 |             },
111 |             repo: {
112 |               type: "string",
113 |               description:
114 |                 "GitHub repository name (required if isLocal is false)",
115 |             },
116 |             fileExtensions: {
117 |               type: "array",
118 |               items: { type: "string" },
119 |               description:
120 |                 "List of file extensions to analyze (e.g., ['js', 'jsx', 'ts', 'tsx'] for React, ['ts', 'html'] for Angular)",
121 |               default: ["js", "jsx", "ts", "tsx"],
122 |             },
123 |           },
124 |           required: ["repoPath", "isLocal"],
125 |           additionalProperties: false,
126 |         },
127 |       },
128 |     ],
129 |   };
130 | });
131 | 
132 | // Handle tool execution
133 | server.setRequestHandler(CallToolRequestSchema, async (request) => {
134 |   console.log("[MCP] Received tool request:", request.params.name);
135 | 
136 |   if (request.params.name !== "generate_ui_flow") {
137 |     throw new McpError(
138 |       ErrorCode.MethodNotFound,
139 |       `Unknown tool: ${request.params.name}`
140 |     );
141 |   }
142 | 
143 |   const args = request.params.arguments as {
144 |     repoPath: string;
145 |     isLocal: boolean;
146 |     owner?: string;
147 |     repo?: string;
148 |     fileExtensions?: string[];
149 |   };
150 |   const { repoPath, isLocal, owner, repo, fileExtensions } = args;
151 | 
152 |   try {
153 |     let contents: RepoContents[];
154 |     if (isLocal) {
155 |       contents = await fetchLocalRepoContents(repoPath);
156 |     } else {
157 |       if (!owner || !repo) {
158 |         throw new McpError(
159 |           ErrorCode.InvalidParams,
160 |           "Owner and repo are required for GitHub repositories"
161 |         );
162 |       }
163 |       contents = await fetchGitHubRepoContents(owner, repo);
164 |     }
165 | 
166 |     const components = await parseUIFlow(contents, isLocal, fileExtensions);
167 |     const mermaidChart = generateMermaidFlowchart(JSON.parse(components));
168 | 
169 |     // Determine output path based on repository type
170 |     const outputPath = isLocal
171 |       ? path.join(repoPath, "userflo.md")
172 |       : path.join(process.cwd(), "userflo.md");
173 |     const flowDescription = `# UI Flow Diagram\n\nThis document describes the UI flow of the application.\n\n`;
174 |     const fullContent =
175 |       flowDescription + "```mermaid\n" + mermaidChart + "\n```\n\n";
176 | 
177 |     await fs.writeFile(outputPath, fullContent);
178 |     console.log(`[MCP] UI flow saved to ${outputPath}`);
179 | 
180 |     return {
181 |       content: [
182 |         {
183 |           type: "text",
184 |           text: mermaidChart,
185 |         },
186 |       ],
187 |     };
188 |   } catch (error) {
189 |     if (error instanceof McpError) {
190 |       throw error;
191 |     }
192 |     throw new McpError(
193 |       ErrorCode.InternalError,
194 |       `Failed to generate UI flow: ${
195 |         error instanceof Error ? error.message : String(error)
196 |       }`
197 |     );
198 |   }
199 | });
200 | 
201 | // Handle resource access
202 | server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
203 |   console.log("[MCP] Received resource request:", request.params.uri);
204 | 
205 |   const match = request.params.uri.match(/^ui-flow:\/\/([^\/]+)\/([^\/]+)$/);
206 |   if (!match) {
207 |     throw new McpError(
208 |       ErrorCode.InvalidRequest,
209 |       `Invalid resource URI format: ${request.params.uri}`
210 |     );
211 |   }
212 | 
213 |   const [, owner, repo] = match;
214 |   try {
215 |     const contents = await fetchGitHubRepoContents(owner, repo);
216 |     const uiFlowJson = await parseUIFlow(contents, false);
217 | 
218 |     return {
219 |       contents: [
220 |         {
221 |           uri: request.params.uri,
222 |           mimeType: "application/json",
223 |           text: uiFlowJson,
224 |         },
225 |       ],
226 |     };
227 |   } catch (error) {
228 |     throw new McpError(
229 |       ErrorCode.InternalError,
230 |       `Failed to read UI flow resource: ${
231 |         error instanceof Error ? error.message : String(error)
232 |       }`
233 |     );
234 |   }
235 | });
236 | 
237 | async function run() {
238 |   const transport = new StdioServerTransport();
239 |   await server.connect(transport);
240 |   console.log("[MCP] UI Flow Chart Creator server running on stdio");
241 | }
242 | 
243 | run().catch((error) => {
244 |   console.error("[MCP] Fatal error:", error);
245 |   process.exit(1);
246 | });
247 | 
248 | // Export to make it a proper ES module
249 | export { server, run };
250 | 
```