#
tokens: 6302/50000 7/7 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── index.mjs
├── package-lock.json
├── package.json
├── README.md
├── scripts
│   ├── extract-docs.mjs
│   └── test-server.mjs
└── tsconfig.json
```

# Files

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

```
node_modules

```

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

```markdown
# Ant Design Components Model Context Protocol Server

A Model Context Protocol (MCP) server that exposes Ant Design component documentation to Large Language Models (LLMs) like Claude. This server allows an LLM to explore and understand Ant Design components through a set of specialized tools.

## Features

- Easy to use - no need to clone the entire Ant Design repository
- Pre-extracted component documentation for faster startup
- List all available Ant Design components
- Get detailed component documentation including descriptions and usage
- View component props and API definitions
- Browse code examples for specific components
- Search for components by name or functionality

## Initial Setup

Before using the MCP server for the first time, you need to extract the documentation from the Ant Design repository:

```bash
# First, clone the Ant Design repository (can be temporary)
git clone https://github.com/ant-design/ant-design.git

# Extract documentation
cd mcp-antd-components
npm run extract   # Uses the default ./ant-design path
# OR
node scripts/extract-docs.mjs /path/to/ant-design  # For a custom path

# After extraction is complete, the Ant Design repo can be deleted if desired
```

This will create a `data` directory with all the extracted component documentation, which the MCP server will use.

### Testing the Server

To verify that everything is working correctly, you can run the test script:

```bash
npm test
# OR
node scripts/test-server.mjs
```

This will run the MCP server and test all available tools with sample queries.

## Usage

### Command Line

Run the MCP server:

```bash
# Run server with pre-extracted data
npm start
# OR
npx -y mcp-antd-components
```

### Claude Desktop Integration

To use this MCP server with Claude Desktop, edit your `claude_desktop_config.json` configuration file:

```json
{
  "mcpServers": {
    "Ant Design Components": {
      "command": "npx",
      "args": ["-y", "mcp-antd-components"]
    }
  }
}
```

Location of the configuration file:

- macOS/Linux: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `$env:AppData\Claude\claude_desktop_config.json`

### Claude Code Integration

To use this MCP server with Claude Code CLI, follow these steps:

1. **Add the Ant Design Components MCP server to Claude Code**

   ```bash
   # Basic syntax
   claude mcp add antd-components npx -y mcp-antd-components
   ```

2. **Verify the MCP server is registered**

   ```bash
   # List all configured servers
   claude mcp list

   # Get details for your Ant Design components server
   claude mcp get antd-components
   ```

3. **Remove the server if needed**

   ```bash
   claude mcp remove antd-components
   ```

4. **Use the tool in Claude Code**

   Once configured, you can invoke the tool in your Claude Code session by asking questions about Ant Design components.

**Tips:**

- Use the `-s` or `--scope` flag with `project` (default) or `global` to specify where the configuration is stored

## MCP Tools

The server provides the following tools for LLMs to interact with Ant Design component documentation:

- `list-components`: Lists all available Ant Design components in PascalCase format (e.g., Button, DatePicker)
- `get-component-props`: Gets the props and API documentation for a specific component (use PascalCase names like "Button")
- `get-component-docs`: Gets detailed documentation for a specific component (use PascalCase names like "DatePicker")
- `list-component-examples`: Lists all examples available for a specific component (use PascalCase names like "Table")
- `get-component-example`: Gets the code for a specific component example (component name in PascalCase)
- `search-components`: Search for components by name pattern (works with PascalCase patterns)

## Examples

Example queries to try:

```
What components are available in Ant Design?
Show me the documentation for the Button component.
What props does the Table component accept?
Show me code examples for the Modal component.
Get the example "basic" for the Form component.
Find components related to Input or Form elements.
```

Note: Component names are provided in PascalCase (e.g., Button, DatePicker, Table) to match React component naming conventions, even though the internal directory structure uses kebab-case (e.g., button, date-picker, table).

## How It Works

The `scripts/extract-docs.mjs` script extracts documentation from the Ant Design repository and saves it to the `data` directory. This includes:

- Component documentation (markdown)
- API/props documentation
- Example code
- Common props documentation

This approach has several advantages:
1. Users don't need to clone the entire Ant Design repository
2. Faster startup time for the MCP server
3. Smaller package size
4. Easier to update when new versions are released

To update the documentation for a new version of Ant Design, simply run the extraction script again with the updated repository.
```

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

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true
  },
  "include": [
    "*.mjs",
    "*.ts"
  ]
}
```

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

```json
{
  "name": "mcp-antd-components",
  "version": "0.0.1",
  "type": "module",
  "sideEffects": false,
  "main": "index.mjs",
  "description": "Model Context Protocol server for Ant Design components",
  "bin": {
    "mcp-antd-components": "index.mjs"
  },
  "scripts": {
    "start": "node index.mjs",
    "help": "node index.mjs --help",
    "typecheck": "true",
    "test": "node scripts/test-server.mjs",
    "extract": "node scripts/extract-docs.mjs ./ant-design"
  },
  "keywords": [
    "mcp",
    "antd",
    "ant design",
    "model context protocol",
    "ui components"
  ],
  "author": "Hannes Junnila",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.7.0",
    "zod": "^3.24.2"
  },
  "engines": {
    "node": ">=16.0.0"
  }
}
```

--------------------------------------------------------------------------------
/scripts/test-server.mjs:
--------------------------------------------------------------------------------

```
#!/usr/bin/env node
/**
 * This script tests the MCP Ant Design Components server
 * using the official MCP SDK client. It runs through all the available tools
 * and displays sample output for each.
 *
 * Usage:
 *   node scripts/test-server.mjs
 */
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = resolve(__dirname, "..");

// Set up the MCP client to communicate with our server
const transport = new StdioClientTransport({
  command: "node",
  args: [resolve(rootDir, "index.mjs")],
});

const client = new Client({
  name: "antd-components-client",
  version: "1.0.0",
});

// Connect to the server
console.log("Connecting to MCP server...");
await client.connect(transport);
console.log("Connected to MCP server successfully!");

// Run example tool calls
try {
  // List components
  console.log("\n--- LISTING COMPONENTS ---");
  const components = await client.callTool({
    name: "list-components",
    arguments: {},
  });
  console.log(components.content[0].text);

  // Get component documentation
  console.log("\n--- GET COMPONENT DOCUMENTATION ---");
  const docs = await client.callTool({
    name: "get-component-docs",
    arguments: {
      componentName: "Button", // Using PascalCase
    },
  });
  console.log(docs.content[0].text);

  // Get component props
  console.log("\n--- GET COMPONENT PROPS ---");
  const props = await client.callTool({
    name: "get-component-props",
    arguments: {
      componentName: "Button", // Using PascalCase
    },
  });
  console.log(props.content[0].text);

  // List component examples
  console.log("\n--- LIST COMPONENT EXAMPLES ---");
  const examples = await client.callTool({
    name: "list-component-examples",
    arguments: {
      componentName: "Button", // Using PascalCase
    },
  });
  console.log(examples.content[0].text);

  // Get component example
  console.log("\n--- GET COMPONENT EXAMPLE ---");
  const example = await client.callTool({
    name: "get-component-example",
    arguments: {
      componentName: "Button", // Using PascalCase
      exampleName: "basic",
    },
  });
  console.log(example.content[0].text);

  // Search components
  console.log("\n--- SEARCH COMPONENTS ---");
  const searchResults = await client.callTool({
    name: "search-components",
    arguments: {
      pattern: "Button|Input", // Using PascalCase
    },
  });
  console.log(searchResults.content[0].text);
} catch (error) {
  console.error("Error during testing:", error);
} finally {
  // Close the connection
  await client.close();
  console.log("\nTests completed, disconnected from server.");
}

```

--------------------------------------------------------------------------------
/scripts/extract-docs.mjs:
--------------------------------------------------------------------------------

```
#!/usr/bin/env node
/**
 * This script extracts component documentation from the Ant Design repository
 * and saves it to a local data directory for use by the MCP server.
 *
 * Usage:
 *   node extract-docs.mjs [path/to/ant-design]
 *
 * If path is not provided, it defaults to ./ant-design
 */
import { existsSync } from "node:fs";
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";

// Get the directory of the current script
const __dirname = dirname(fileURLToPath(import.meta.url));
const dataDir = resolve(__dirname, "../data");

// ==================================
// Utility functions
// ==================================

// Convert snake-case to PascalCase
const toPascalCase = (str) => {
  return str
    .split("-")
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join("");
};

// Remove YAML frontmatter from markdown
const removeFrontmatter = (content) => {
  return content.replace(/^---\n([\s\S]*?)\n---\n/, "");
};

// Extract API section from markdown
const extractApiSection = (markdown) => {
  // Find the API section start
  const apiIndex = markdown.indexOf("\n## API\n");

  if (apiIndex !== -1) {
    // Find the next heading at the same level (starts with ## but not ###)
    const startPos = apiIndex + 1; // Skip the newline before ## API
    let endPos = markdown.length;

    // Find the next ## heading (but not ###+)
    const nextHeadingMatch = markdown.slice(startPos).match(/\n## [^#]/);
    if (nextHeadingMatch) {
      endPos = startPos + nextHeadingMatch.index;
    }

    // Extract the full API section
    return markdown.slice(startPos, endPos).trim();
  }

  return null;
};

// Extract examples with descriptions from markdown content
const extractExamples = (markdown) => {
  // Look for code snippets that reference demo files with descriptions
  const codeRefs = [
    ...markdown.matchAll(/<code src="\.\/demo\/([^"]+)\.tsx"(?:\s+[^>]*)?>(.*?)<\/code>/g),
  ];

  if (codeRefs && codeRefs.length > 0) {
    return codeRefs
      .filter((match) => !match[1].startsWith("debug-") && !match[1].startsWith("_"))
      .map((match) => ({
        name: match[1],
        description: match[2]?.trim() || match[1], // Use example name if no description
      }));
  }

  return [];
};

// ==================================
// Main extraction functions
// ==================================

// Process a component's documentation and examples
async function processComponent(componentsPath, dirName) {
  const componentPath = join(componentsPath, dirName);
  const indexMdPath = join(componentPath, "index.en-US.md");
  const demoPath = join(componentPath, "demo");

  if (!existsSync(indexMdPath)) {
    console.log(`⚠️ Skipping ${dirName} - no documentation found`);
    return null;
  }

  // Initialize component data
  const componentName = toPascalCase(dirName);
  console.log(`📝 Processing ${componentName}...`);

  const componentData = {
    name: componentName,
    dirName: dirName,
    documentation: null,
    apiSection: null,
    examples: {},
  };

  try {
    // Read and parse documentation
    const docContent = await readFile(indexMdPath, "utf-8");
    componentData.documentation = removeFrontmatter(docContent);
    componentData.apiSection = extractApiSection(componentData.documentation)?.replace(
      "Common props ref:[Common props](/docs/react/common-props)\n",
      "",
    );

    // Extract examples with descriptions from documentation
    componentData.examplesInfo = extractExamples(componentData.documentation);

    // Read example files from the demo directory
    if (existsSync(demoPath)) {
      // Get all example files from the directory
      const demoFiles = await readdir(demoPath, { withFileTypes: true });
      const exampleFiles = demoFiles.filter(
        (file) =>
          !file.isDirectory() &&
          (file.name.endsWith(".tsx") || file.name.endsWith(".jsx")) &&
          !file.name.startsWith("_") &&
          !file.name.startsWith("debug-"),
      );

      // Process each example file
      for (const exampleFile of exampleFiles) {
        const exampleName = exampleFile.name.replace(/\.(tsx|jsx)$/, "");
        const examplePath = join(demoPath, exampleFile.name);

        try {
          componentData.examples[exampleName] = await readFile(examplePath, "utf-8");
        } catch (error) {
          console.error(`  ❌ Error reading example ${exampleName}:`, error.message);
        }
      }

      console.log(`  ✓ Found ${Object.keys(componentData.examples).length} examples`);
    } else {
      console.log(`  ℹ️ No examples directory for ${componentName}`);
    }

    return componentData;
  } catch (error) {
    console.error(`  ❌ Error processing ${componentName}:`, error.message);
    return null;
  }
}

// Main function to process all components and export data
async function extractAllData(antdRepoPath) {
  // Ensure the data directory exists
  await mkdir(dataDir, { recursive: true });

  console.log(`🔍 Extracting documentation from ${antdRepoPath}...`);
  const componentsPath = join(antdRepoPath, "components");

  if (!existsSync(componentsPath)) {
    console.error(`❌ Error: ${antdRepoPath} does not appear to be an Ant Design repository.`);
    console.error("The 'components' directory was not found.");
    process.exit(1);
  }

  // Read all component directories
  const entries = await readdir(componentsPath, { withFileTypes: true });
  const componentDirs = entries.filter(
    (entry) =>
      entry.isDirectory() &&
      !entry.name.startsWith(".") &&
      !entry.name.startsWith("_") &&
      entry.name !== "locale" &&
      entry.name !== "style" &&
      entry.name !== "version",
  );

  console.log(`📊 Found ${componentDirs.length} potential components`);

  // Process each component
  const components = {};
  let processedCount = 0;

  for (const entry of componentDirs) {
    const componentData = await processComponent(componentsPath, entry.name);
    if (componentData) {
      components[componentData.name] = componentData;
      processedCount++;
    }
  }

  console.log(`✅ Successfully processed ${processedCount} of ${componentDirs.length} components`);

  // Save the data
  const metaData = {
    generatedAt: new Date().toISOString(),
    componentCount: processedCount,
    version: "1.0.0",
  };

  // Write component index (just names and dirNames)
  const componentsIndex = Object.values(components).map((c) => ({
    name: c.name,
    dirName: c.dirName,
  }));

  await writeFile(join(dataDir, "components-index.json"), JSON.stringify(componentsIndex, null, 2));
  console.log(`💾 Saved components index to components-index.json`);

  // Write metadata
  await writeFile(join(dataDir, "metadata.json"), JSON.stringify(metaData, null, 2));
  console.log(`💾 Saved metadata to metadata.json`);

  // Create components directory
  const componentsDataDir = join(dataDir, "components");
  await mkdir(componentsDataDir, { recursive: true });

  // Write individual component files
  for (const componentData of Object.values(components)) {
    // Create a directory for the component
    const componentDir = join(componentsDataDir, componentData.dirName);
    await mkdir(componentDir, { recursive: true });

    // Write documentation
    await writeFile(join(componentDir, "docs.md"), componentData.documentation);

    // Write API section if available
    if (componentData.apiSection) {
      await writeFile(join(componentDir, "api.md"), componentData.apiSection);
    }

    // Write examples
    if (Object.keys(componentData.examples).length > 0) {
      const examplesDir = join(componentDir, "examples");
      await mkdir(examplesDir, { recursive: true });

      // Create a markdown file with example descriptions
      if (componentData.examplesInfo && componentData.examplesInfo.length > 0) {
        let examplesMarkdown = "## Examples\n\n";

        componentData.examplesInfo.forEach((example) => {
          examplesMarkdown += `- **${example.name}**: ${example.description}\n`;
        });

        await writeFile(join(componentDir, "examples.md"), examplesMarkdown);
      }

      for (const [exampleName, exampleCode] of Object.entries(componentData.examples)) {
        // Determine if it's TSX or JSX based on content
        const extension = exampleCode.includes("React.FC") ? ".tsx" : ".jsx";
        await writeFile(join(examplesDir, `${exampleName}${extension}`), exampleCode);
      }
    }
  }

  console.log(`🎉 Documentation extraction complete! Data saved to ${dataDir}`);
}

// Parse command line arguments
const args = process.argv.slice(2);
const antdRepoArg = args[0];

// Default to ./ant-design if no argument provided
const antdRepoPath = resolve(antdRepoArg ?? "./ant-design");

// Run the extraction
extractAllData(antdRepoPath).catch((error) => {
  console.error("❌ Fatal error:", error);
  process.exit(1);
});

```

--------------------------------------------------------------------------------
/index.mjs:
--------------------------------------------------------------------------------

```
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { Console } from "node:console";
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { z } from "zod";

// Get the directory of the current script
const __dirname = dirname(fileURLToPath(import.meta.url));
globalThis.console = new Console(process.stderr);

// Path to the pre-extracted data
const dataDir = resolve(__dirname, "data");
const componentsDir = join(dataDir, "components");

// Check if the data directory exists
if (!existsSync(dataDir) || !existsSync(componentsDir)) {
  console.error(`Error: Data directory not found at ${dataDir}`);
  console.error("Please run the extraction script first:");
  console.error("  node scripts/extract-docs.mjs [path/to/ant-design]");
  process.exit(1);
}

// Check if the components index exists
const componentsIndexPath = join(dataDir, "components-index.json");
if (!existsSync(componentsIndexPath)) {
  console.error(`Error: Components index not found at ${componentsIndexPath}`);
  console.error("Please run the extraction script first.");
  process.exit(1);
}

// Initialize the MCP server
const server = new McpServer({
  name: "Ant Design Components",
  version: "1.0.0",
  description: "Provides documentation and examples for Ant Design components",
});

// ===============================================
// Utility functions
// ===============================================

// Load components index from the extracted data
async function loadComponentsIndex() {
  try {
    const indexData = await readFile(componentsIndexPath, "utf-8");
    return JSON.parse(indexData);
  } catch (error) {
    console.error(`Error loading components index: ${error.message}`);
    return [];
  }
}

// Find a component by name (case-insensitive)
async function findComponentByName(componentName) {
  const components = await loadComponentsIndex();
  return components.find(
    (c) =>
      c.name.toLowerCase() === componentName.toLowerCase() ||
      c.dirName.toLowerCase() === componentName.toLowerCase(),
  );
}

// Find components matching a pattern
async function findComponentsByPattern(pattern) {
  const components = await loadComponentsIndex();
  const regexPattern = new RegExp(pattern, "i");

  return components.filter((c) => regexPattern.test(c.name) || regexPattern.test(c.dirName));
}

// ===============================================
// Data fetching functions
// ===============================================

// Get component markdown documentation
const getComponentDocumentation = async (componentName) => {
  const component = await findComponentByName(componentName);

  if (!component) {
    return `Documentation for component "${componentName}" not found`;
  }

  const docPath = join(componentsDir, component.dirName, "docs.md");

  try {
    if (existsSync(docPath)) {
      return await readFile(docPath, "utf-8");
    } else {
      return `Documentation for ${component.name} not found`;
    }
  } catch (error) {
    console.error(`Error reading documentation for ${component.name}: ${error.message}`);
    return `Error reading documentation: ${error.message}`;
  }
};

// Get component API documentation
const getComponentProps = async (componentName) => {
  const component = await findComponentByName(componentName);

  if (!component) {
    return `API documentation for component "${componentName}" not found`;
  }

  try {
    const apiPath = join(componentsDir, component.dirName, "api.md");

    if (existsSync(apiPath)) {
      return await readFile(apiPath, "utf-8");
    }
    return `API documentation for ${component.name} not found`;
  } catch (error) {
    console.error(`Error reading API for ${component.name}: ${error.message}`);
    return `Error reading API documentation: ${error.message}`;
  }
};

// List component examples
const listComponentExamples = async (componentName) => {
  const component = await findComponentByName(componentName);

  if (!component) {
    return "Component not found";
  }

  // First, check if we have examples.md with descriptions
  const examplesMdPath = join(componentsDir, component.dirName, "examples.md");

  if (!existsSync(examplesMdPath)) {
    return `No examples found for ${component.name}`;
  }
  try {
    return await readFile(examplesMdPath, "utf-8");
  } catch (error) {
    console.error(`Error reading examples markdown for ${component.name}: ${error.message}`);
    return `No examples found for ${component.name}`;
  }
};

// Get specific component example
const getComponentExample = async (componentName, exampleName) => {
  const component = await findComponentByName(componentName);

  if (!component) {
    return `Component "${componentName}" not found`;
  }

  const examplesDir = join(componentsDir, component.dirName, "examples");

  if (!existsSync(examplesDir)) {
    return `No examples found for ${component.name}`;
  }

  // Check for both TSX and JSX extensions
  const tsxPath = join(examplesDir, `${exampleName}.tsx`);
  const jsxPath = join(examplesDir, `${exampleName}.jsx`);

  const filePath = existsSync(tsxPath) ? tsxPath : existsSync(jsxPath) ? jsxPath : null;

  if (!filePath) {
    return `Example "${exampleName}" not found for ${component.name}`;
  }

  try {
    const exampleCode = await readFile(filePath, "utf-8");

    // Try to find description for this example
    let description = "";
    const examplesMdPath = join(componentsDir, component.dirName, "examples.md");

    if (existsSync(examplesMdPath)) {
      const examplesMd = await readFile(examplesMdPath, "utf-8");
      const match = examplesMd.match(new RegExp(`- \\*\\*${exampleName}\\*\\*: (.+)$`, "m"));
      if (match && match[1]) {
        description = match[1].trim();
      }
    }

    // Add a header with description if available
    if (description) {
      return `// Example: ${exampleName} - ${description}\n\n${exampleCode}`;
    }

    return exampleCode;
  } catch (error) {
    console.error(`Error reading example "${exampleName}" for ${component.name}: ${error.message}`);
    return `Error reading example: ${error.message}`;
  }
};

// ===============================================
// MCP Tools
// ===============================================

// Tool: list-components
server.tool("list-components", "Lists all available Ant Design components", async () => {
  const components = await loadComponentsIndex();
  return {
    content: [
      {
        type: "text",
        text: components.map((c) => c.name).join(", "),
      },
    ],
  };
});

// Tool: get-component-docs
server.tool(
  "get-component-docs",
  "Gets detailed documentation for a specific component",
  { componentName: z.string() },
  async ({ componentName }) => {
    const documentation = await getComponentDocumentation(componentName);
    return {
      content: [
        {
          type: "text",
          text: documentation,
        },
      ],
    };
  },
);

// Tool: get-component-props
server.tool(
  "get-component-props",
  "Gets the props and API documentation for a specific component",
  { componentName: z.string() },
  async ({ componentName }) => {
    const propsSection = await getComponentProps(componentName);
    return {
      content: [
        {
          type: "text",
          text: propsSection,
        },
      ],
    };
  },
);

// Tool: list-component-examples
server.tool(
  "list-component-examples",
  "Lists all examples available for a specific component with descriptions",
  { componentName: z.string() },
  async ({ componentName }) => {
    const examplesMarkdown = await listComponentExamples(componentName);

    return {
      content: [
        {
          type: "text",
          text: examplesMarkdown,
        },
      ],
    };
  },
);

// Tool: get-component-example
server.tool(
  "get-component-example",
  "Gets the code for a specific component example",
  {
    componentName: z.string(),
    exampleName: z.string(),
  },
  async ({ componentName, exampleName }) => {
    const exampleCode = await getComponentExample(componentName, exampleName);
    return {
      content: [
        {
          type: "text",
          text: exampleCode,
        },
      ],
    };
  },
);

// Tool: search-components
server.tool(
  "search-components",
  "Search for components by name pattern",
  { pattern: z.string() },
  async ({ pattern }) => {
    const matchingComponents = await findComponentsByPattern(pattern);

    return {
      content: [
        {
          type: "text",
          text: matchingComponents.length
            ? `Matching components: ${matchingComponents.map((c) => c.name).join(", ")}`
            : `No components found matching '${pattern}'`,
        },
      ],
    };
  },
);

// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);

```