Skip to main content
Glama
umshere

UIFlowchartCreator

by umshere

generate_ui_flow

Create UI flow diagrams by scanning React/Angular repositories. Analyzes codebases to map components, relationships, and UI structure for local or GitHub repositories.

Instructions

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.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
fileExtensionsNoList of file extensions to analyze (e.g., ['js', 'jsx', 'ts', 'tsx'] for React, ['ts', 'html'] for Angular)
isLocalYesWhether to analyze a local repository (true) or GitHub repository (false)
ownerNoGitHub repository owner (required if isLocal is false)
repoNoGitHub repository name (required if isLocal is false)
repoPathYesPath to local repository or empty string for GitHub repos

Implementation Reference

  • Main execution logic for the generate_ui_flow tool within the CallToolRequestSchema handler.
    if (request.params.name !== "generate_ui_flow") {
      throw new McpError(
        ErrorCode.MethodNotFound,
        `Unknown tool: ${request.params.name}`
      );
    }
    
    const args = request.params.arguments as {
      repoPath: string;
      isLocal: boolean;
      owner?: string;
      repo?: string;
      fileExtensions?: string[];
    };
    const { repoPath, isLocal, owner, repo, fileExtensions } = args;
    
    try {
      let contents: RepoContents[];
      if (isLocal) {
        contents = await fetchLocalRepoContents(repoPath);
      } else {
        if (!owner || !repo) {
          throw new McpError(
            ErrorCode.InvalidParams,
            "Owner and repo are required for GitHub repositories"
          );
        }
        contents = await fetchGitHubRepoContents(owner, repo);
      }
    
      const components = await parseUIFlow(contents, isLocal, fileExtensions);
      const mermaidChart = generateMermaidFlowchart(JSON.parse(components));
    
      // Determine output path based on repository type
      const outputPath = isLocal
        ? path.join(repoPath, "userflo.md")
        : path.join(process.cwd(), "userflo.md");
      const flowDescription = `# UI Flow Diagram\n\nThis document describes the UI flow of the application.\n\n`;
      const fullContent =
        flowDescription + "```mermaid\n" + mermaidChart + "\n```\n\n";
    
      await fs.writeFile(outputPath, fullContent);
      console.log(`[MCP] UI flow saved to ${outputPath}`);
    
      return {
        content: [
          {
            type: "text",
            text: mermaidChart,
          },
        ],
      };
    } catch (error) {
      if (error instanceof McpError) {
        throw error;
      }
      throw new McpError(
        ErrorCode.InternalError,
        `Failed to generate UI flow: ${
          error instanceof Error ? error.message : String(error)
        }`
      );
    }
  • Input schema definition for the generate_ui_flow tool.
    inputSchema: {
      type: "object",
      properties: {
        repoPath: {
          type: "string",
          description:
            "Path to local repository or empty string for GitHub repos",
        },
        isLocal: {
          type: "boolean",
          description:
            "Whether to analyze a local repository (true) or GitHub repository (false)",
        },
        owner: {
          type: "string",
          description:
            "GitHub repository owner (required if isLocal is false)",
        },
        repo: {
          type: "string",
          description:
            "GitHub repository name (required if isLocal is false)",
        },
        fileExtensions: {
          type: "array",
          items: { type: "string" },
          description:
            "List of file extensions to analyze (e.g., ['js', 'jsx', 'ts', 'tsx'] for React, ['ts', 'html'] for Angular)",
          default: ["js", "jsx", "ts", "tsx"],
        },
      },
      required: ["repoPath", "isLocal"],
      additionalProperties: false,
    },
  • src/index.ts:34-73 (registration)
    Tool registration in server capabilities.
      generate_ui_flow: {
        name: "generate_ui_flow",
        description:
          "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.",
        inputSchema: {
          type: "object",
          properties: {
            repoPath: {
              type: "string",
              description:
                "Path to local repository or empty string for GitHub repos",
            },
            isLocal: {
              type: "boolean",
              description:
                "Whether to analyze a local repository (true) or GitHub repository (false)",
            },
            owner: {
              type: "string",
              description:
                "GitHub repository owner (required if isLocal is false)",
            },
            repo: {
              type: "string",
              description:
                "GitHub repository name (required if isLocal is false)",
            },
            fileExtensions: {
              type: "array",
              items: { type: "string" },
              description:
                "List of file extensions to analyze (e.g., ['js', 'jsx', 'ts', 'tsx'] for React, ['ts', 'html'] for Angular)",
              default: ["js", "jsx", "ts", "tsx"],
            },
          },
          required: ["repoPath", "isLocal"],
          additionalProperties: false,
        },
      },
    },
  • Core helper function that parses repository contents to extract UI component hierarchy and relationships.
    export async function parseUIFlow(
      contents: RepoContents[],
      isLocal: boolean,
      fileExtensions: string[] = ["js", "jsx", "ts", "tsx"]
    ): Promise<string> {
      console.log(
        `[MCP] Parsing UI flow with extensions: ${fileExtensions.join(", ")}`
      );
    
      const components: { [key: string]: ComponentInfo } = {};
    
      async function processContents(
        currentContents: RepoContents[],
        currentPath: string = ""
      ) {
        for (const item of currentContents) {
          if (
            item.type === "file" &&
            fileExtensions.some((ext) => item.name.endsWith(`.${ext}`))
          ) {
            let content: string;
            if (isLocal) {
              content = item.content || "";
            } else {
              try {
                const response = await axios.get(item.download_url || "");
                content = response.data;
              } catch (error) {
                console.warn(
                  `[MCP] Failed to fetch content for ${item.name}: ${error}`
                );
                continue;
              }
            }
    
            const componentName = item.name.split(".")[0];
            const componentPath = path.join(currentPath, componentName);
            const componentType = getComponentType(componentPath);
    
            components[componentPath] = {
              name: componentName,
              type: componentType,
              filePath: path.join(currentPath, item.name),
              imports: [],
              children: [],
            };
    
            // Analyze import statements
            const importMatches = content.match(
              /import\s+(\w+|\{[^}]+\})\s+from\s+['"]([^'"]+)['"]/g
            );
            if (importMatches) {
              importMatches.forEach((match) => {
                const [, importedComponent, importPath] =
                  match.match(
                    /import\s+(\w+|\{[^}]+\})\s+from\s+['"]([^'"]+)['"]/
                  ) || [];
                if (importedComponent) {
                  const cleanedImport = importedComponent
                    .replace(/[{}]/g, "")
                    .trim();
                  const resolvedPath = path.join(
                    currentPath,
                    path.dirname(importPath),
                    cleanedImport
                  );
                  components[componentPath].imports.push(resolvedPath);
                }
              });
            }
          } else if (item.type === "dir") {
            const subContents = isLocal
              ? await fetchLocalRepoContents(item.path)
              : await fetchGitHubRepoContents(
                  item.owner || "",
                  item.repo || "",
                  item.path
                );
    
            await processContents(subContents, path.join(currentPath, item.name));
          }
        }
      }
    
      await processContents(contents);
    
      // Build component hierarchy
      const rootComponents: ComponentInfo[] = [];
      Object.values(components).forEach((component) => {
        component.imports.forEach((importPath) => {
          if (components[importPath]) {
            components[importPath].children.push(component);
          }
        });
        if (component.imports.length === 0) {
          rootComponents.push(component);
        }
      });
    
      return JSON.stringify(rootComponents, null, 2);
    }
  • Helper function that generates Mermaid flowchart syntax from parsed UI components.
    export function generateMermaidFlowchart(components: ComponentInfo[]): string {
      let chart = "flowchart TD\n";
    
      // Create a map of all components for quick lookup
      const componentMap = new Map<string, ComponentInfo>();
      components.forEach((component) => {
        componentMap.set(component.name, component);
      });
    
      // Create nodes with proper styling and hierarchy
      const createNode = (component: ComponentInfo, depth: number = 0): string => {
        const nodeId = component.name.replace(/[^a-zA-Z0-9]/g, "_");
        const indent = "  ".repeat(depth);
    
        // Determine node style based on type
        let nodeStyle = "";
        switch (component.type) {
          case "page":
            nodeStyle = "(( ))";
            break;
          case "layout":
            nodeStyle = "{{ }}";
            break;
          default:
            nodeStyle = "[/ /]";
        }
    
        // Add node with proper indentation
        chart += `${indent}${nodeId}${nodeStyle}["${component.name} (${component.type})"]\n`;
    
        // Recursively process children
        component.children.forEach((child) => {
          const childComponent = componentMap.get(child.name);
          if (childComponent) {
            createNode(childComponent, depth + 1);
          }
        });
    
        return nodeId;
      };
    
      // Find root components (those with no parents)
      const rootComponents = components.filter(
        (component) =>
          !components.some((c) =>
            c.children.some((child) => child.name === component.name)
          )
      );
    
      // Start building the chart from root components
      rootComponents.forEach((component) => {
        createNode(component);
      });
    
      // Create relationships with labels
      components.forEach((component) => {
        const parentId = component.name.replace(/[^a-zA-Z0-9]/g, "_");
    
        component.children.forEach((child) => {
          const childId = child.name.replace(/[^a-zA-Z0-9]/g, "_");
          const relationshipType = determineRelationshipType(component, child);
          chart += `  ${parentId} -->|${relationshipType}| ${childId}\n`;
        });
      });
    
      // Validate Mermaid.js syntax
      try {
        // Basic validation - check for required elements
        if (!chart.includes("flowchart TD")) {
          throw new Error("Missing flowchart declaration");
        }
        if (!chart.match(/\[.*\]/)) {
          throw new Error("Missing node definitions");
        }
        if (!chart.match(/-->|--/)) {
          throw new Error("Missing relationship definitions");
        }
      } catch (error) {
        console.error("[MCP] Mermaid.js validation error:", error);
        throw new McpError(
          ErrorCode.InternalError,
          `Failed to generate valid Mermaid.js chart: ${error}`
        );
      }
    
      return chart;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions scanning codebases to identify components and relationships, but fails to describe critical behaviors like output format (e.g., diagram type, file format), performance implications (e.g., time to analyze large repos), error handling, or authentication needs for GitHub access. This leaves significant gaps for an agent to use the tool effectively.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded, stating the core purpose in the first sentence. Both sentences are relevant, with the second elaborating on the scanning process. There's no unnecessary information, though it could be slightly more structured (e.g., separating purpose from behavior).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of analyzing codebases and generating diagrams, with no annotations and no output schema, the description is incomplete. It doesn't explain what the output looks like (e.g., a visual diagram, JSON data), how errors are handled, or any limitations (e.g., supported React/Angular versions). This makes it inadequate for an agent to fully understand the tool's behavior and results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with all parameters well-documented in the schema itself. The description adds no additional parameter semantics beyond what's in the schema (e.g., it doesn't explain how 'fileExtensions' affect analysis or clarify 'repoPath' usage). With high schema coverage, the baseline score of 3 is appropriate, as the description doesn't compensate but also doesn't detract.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Generate a UI flow diagram by analyzing React/Angular repositories.' It specifies the verb ('generate'), resource ('UI flow diagram'), and scope ('React/Angular repositories'), making it easy to understand what the tool does. However, without sibling tools, it cannot demonstrate differentiation from alternatives, preventing a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage by mentioning 'React/Angular repositories,' suggesting it's for analyzing codebases in those frameworks. However, it lacks explicit guidance on when to use this tool versus other analysis tools, prerequisites, or exclusions. No sibling tools are provided, so no alternative comparisons are possible.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/umshere/uiflowchartcreator'

If you have feedback or need assistance with the MCP directory API, please join our Discord server