# Directory Structure
```
├── .gitignore
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | node_modules/
2 | build/
3 | *.log
4 | .env*
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # shadcn-ui MCP Server
2 |
3 | MCP server for shadcn/ui component references
4 |
5 | This is a TypeScript-based MCP server that provides reference information for shadcn/ui components. It implements a Model Context Protocol (MCP) server that helps AI assistants access shadcn/ui component documentation and examples.
6 |
7 | ## Features
8 |
9 | ### Tools
10 |
11 | - `list_shadcn_components` - Get a list of all available shadcn/ui components
12 | - `get_component_details` - Get detailed information about a specific component
13 | - `get_component_examples` - Get usage examples for a specific component
14 | - `search_components` - Search for components by keyword
15 |
16 | ### Functionality
17 |
18 | This server scrapes and caches information from:
19 | - The official shadcn/ui documentation site (https://ui.shadcn.com)
20 | - The shadcn/ui GitHub repository
21 |
22 | It provides structured data including:
23 | - Component descriptions
24 | - Installation instructions
25 | - Usage examples
26 | - Props and variants
27 | - Code samples
28 |
29 | ## Development
30 |
31 | Install dependencies:
32 | ```bash
33 | npm install
34 | ```
35 |
36 | Build the server:
37 | ```bash
38 | npm run build
39 | ```
40 |
41 | For development with auto-rebuild:
42 | ```bash
43 | npm run watch
44 | ```
45 |
46 | ## Installation
47 |
48 | ### Claude Desktop Configuration
49 |
50 | To use with Claude Desktop, add the server config:
51 |
52 | On MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
53 | On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
54 |
55 | #### Option 1: Using local build
56 |
57 | ```json
58 | {
59 | "mcpServers": {
60 | "shadcn-ui-server": {
61 | "command": "/path/to/shadcn-ui-server/build/index.js"
62 | }
63 | }
64 | }
65 | ```
66 |
67 | #### Option 2: Using npx command
68 |
69 | ```json
70 | {
71 | "mcpServers": {
72 | "shadcn-ui-server": {
73 | "command": "npx",
74 | "args": ["-y", "shadcn-ui-mcp-server"]
75 | }
76 | }
77 | }
78 | ```
79 |
80 | ### Windsurf Configuration
81 |
82 | Add this to your `./codeium/windsurf/model_config.json`:
83 |
84 | ```json
85 | {
86 | "mcpServers": {
87 | "shadcn-ui-server": {
88 | "command": "npx",
89 | "args": ["-y", "shadcn-ui-mcp-server"]
90 | }
91 | }
92 | }
93 | ```
94 |
95 | ### Cursor Configuration
96 |
97 | Add this to your `.cursor/mcp.json`:
98 |
99 | ```json
100 | {
101 | "mcpServers": {
102 | "shadcn-ui-server": {
103 | "command": "npx",
104 | "args": ["-y", "shadcn-ui-mcp-server"]
105 | }
106 | }
107 | }
108 | ```
109 |
110 | ### Debugging
111 |
112 | Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), which is available as a package script:
113 |
114 | ```bash
115 | npm run inspector
116 | ```
117 |
118 | The Inspector will provide a URL to access debugging tools in your browser.
119 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "module": "Node16",
5 | "moduleResolution": "Node16",
6 | "outDir": "./build",
7 | "rootDir": "./src",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "skipLibCheck": true,
11 | "forceConsistentCasingInFileNames": true
12 | },
13 | "include": ["src/**/*"],
14 | "exclude": ["node_modules"]
15 | }
16 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "shadcn-ui-mcp-server",
3 | "version": "0.1.2",
4 | "description": "MCP server for shadcn/ui component references",
5 | "type": "module",
6 | "license": "MIT",
7 | "bin": {
8 | "shadcn-ui-server": "build/index.js"
9 | },
10 | "files": [
11 | "build"
12 | ],
13 | "scripts": {
14 | "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
15 | "prepare": "npm run build",
16 | "watch": "tsc --watch",
17 | "inspector": "npx @modelcontextprotocol/inspector build/index.js"
18 | },
19 | "dependencies": {
20 | "@modelcontextprotocol/sdk": "0.6.0",
21 | "axios": "^1.7.9",
22 | "cheerio": "^1.0.0-rc.12"
23 | },
24 | "devDependencies": {
25 | "@types/node": "^20.11.24",
26 | "typescript": "^5.3.3"
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/ymadd/shadcn-ui-mcp-server.git"
31 | },
32 | "author": "ymadd",
33 | "bugs": {
34 | "url": "https://github.com/ymadd/shadcn-ui-mcp-server/issues"
35 | },
36 | "homepage": "https://github.com/ymadd/shadcn-ui-mcp-server#readme",
37 | "keywords": [
38 | "shadcn/ui",
39 | "MCP",
40 | "model context protocol",
41 | "component references"
42 | ]
43 | }
44 |
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * MCP server for shadcn/ui component references
5 | * This server provides tools to:
6 | * - List all available shadcn/ui components
7 | * - Get detailed information about specific components
8 | * - Get usage examples for components
9 | * - Search for components by keyword
10 | */
11 |
12 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
13 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14 | import {
15 | CallToolRequestSchema,
16 | ErrorCode,
17 | ListToolsRequestSchema,
18 | McpError,
19 | } from "@modelcontextprotocol/sdk/types.js";
20 | import axios from "axios";
21 | import * as cheerio from "cheerio";
22 |
23 | /**
24 | * Interface for component information
25 | */
26 | interface ComponentInfo {
27 | name: string;
28 | description: string;
29 | url: string;
30 | sourceUrl?: string;
31 | apiReference?: string;
32 | installation?: string;
33 | usage?: string;
34 | props?: Record<string, ComponentProp>;
35 | examples?: ComponentExample[];
36 | }
37 |
38 | /**
39 | * Interface for component property information
40 | */
41 | interface ComponentProp {
42 | type: string;
43 | description: string;
44 | required: boolean;
45 | default?: string;
46 | example?: string;
47 | }
48 |
49 | /**
50 | * Interface for component example
51 | */
52 | interface ComponentExample {
53 | title: string;
54 | code: string;
55 | description?: string;
56 | }
57 |
58 | /**
59 | * ShadcnUiServer class that handles all the component reference functionality
60 | */
61 | class ShadcnUiServer {
62 | private server: Server;
63 | private axiosInstance;
64 | private componentCache: Map<string, ComponentInfo> = new Map();
65 | private componentsListCache: ComponentInfo[] | null = null;
66 | private readonly SHADCN_DOCS_URL = "https://ui.shadcn.com";
67 | private readonly SHADCN_GITHUB_URL = "https://github.com/shadcn-ui/ui";
68 | private readonly SHADCN_RAW_GITHUB_URL = "https://raw.githubusercontent.com/shadcn-ui/ui/main";
69 |
70 | constructor() {
71 | this.server = new Server(
72 | {
73 | name: "shadcn-ui-server",
74 | version: "0.1.0",
75 | },
76 | {
77 | capabilities: {
78 | tools: {},
79 | },
80 | }
81 | );
82 |
83 | this.axiosInstance = axios.create({
84 | timeout: 10000,
85 | headers: {
86 | "User-Agent": "Mozilla/5.0 (compatible; ShadcnUiMcpServer/0.1.0)",
87 | },
88 | });
89 |
90 | this.setupToolHandlers();
91 |
92 | this.server.onerror = (error) => console.error("[MCP Error]", error);
93 | process.on("SIGINT", async () => {
94 | await this.server.close();
95 | process.exit(0);
96 | });
97 | }
98 |
99 | /**
100 | * Set up the tool handlers for the server
101 | */
102 | private setupToolHandlers() {
103 | this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
104 | tools: [
105 | {
106 | name: "list_shadcn_components",
107 | description: "Get a list of all available shadcn/ui components",
108 | inputSchema: {
109 | type: "object",
110 | properties: {},
111 | required: [],
112 | },
113 | },
114 | {
115 | name: "get_component_details",
116 | description: "Get detailed information about a specific shadcn/ui component",
117 | inputSchema: {
118 | type: "object",
119 | properties: {
120 | componentName: {
121 | type: "string",
122 | description: "Name of the shadcn/ui component (e.g., \"accordion\", \"button\")",
123 | },
124 | },
125 | required: ["componentName"],
126 | },
127 | },
128 | {
129 | name: "get_component_examples",
130 | description: "Get usage examples for a specific shadcn/ui component",
131 | inputSchema: {
132 | type: "object",
133 | properties: {
134 | componentName: {
135 | type: "string",
136 | description: "Name of the shadcn/ui component (e.g., \"accordion\", \"button\")",
137 | },
138 | },
139 | required: ["componentName"],
140 | },
141 | },
142 | {
143 | name: "search_components",
144 | description: "Search for shadcn/ui components by keyword",
145 | inputSchema: {
146 | type: "object",
147 | properties: {
148 | query: {
149 | type: "string",
150 | description: "Search query to find relevant components",
151 | },
152 | },
153 | required: ["query"],
154 | },
155 | },
156 | ],
157 | }));
158 |
159 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
160 | switch (request.params.name) {
161 | case "list_shadcn_components":
162 | return await this.handleListComponents();
163 | case "get_component_details":
164 | return await this.handleGetComponentDetails(request.params.arguments);
165 | case "get_component_examples":
166 | return await this.handleGetComponentExamples(request.params.arguments);
167 | case "search_components":
168 | return await this.handleSearchComponents(request.params.arguments);
169 | default:
170 | throw new McpError(
171 | ErrorCode.MethodNotFound,
172 | `Unknown tool: ${request.params.name}`
173 | );
174 | }
175 | });
176 | }
177 |
178 | /**
179 | * Handle the list_shadcn_components tool request
180 | */
181 | private async handleListComponents() {
182 | try {
183 | if (!this.componentsListCache) {
184 | // Fetch the list of components
185 | const response = await this.axiosInstance.get(`${this.SHADCN_DOCS_URL}/docs/components`);
186 | const $ = cheerio.load(response.data);
187 |
188 | const components: ComponentInfo[] = [];
189 |
190 | // Extract component links
191 | $("a").each((_, element) => {
192 | const link = $(element);
193 | const url = link.attr("href");
194 |
195 | if (url && url.startsWith("/docs/components/")) {
196 | const name = url.split("/").pop() || "";
197 |
198 | components.push({
199 | name,
200 | description: "", // Will be populated when fetching details
201 | url: `${this.SHADCN_DOCS_URL}${url}`,
202 | });
203 | }
204 | });
205 |
206 | this.componentsListCache = components;
207 | }
208 |
209 | return {
210 | content: [
211 | {
212 | type: "text",
213 | text: JSON.stringify(this.componentsListCache, null, 2),
214 | },
215 | ],
216 | };
217 | } catch (error) {
218 | if (axios.isAxiosError(error)) {
219 | throw new McpError(
220 | ErrorCode.InternalError,
221 | `Failed to fetch shadcn/ui components: ${error.message}`
222 | );
223 | }
224 | throw error;
225 | }
226 | }
227 |
228 | /**
229 | * Validates component name from arguments
230 | * @param args Arguments object
231 | * @returns Validated component name
232 | * @throws McpError if validation fails
233 | */
234 | private validateComponentName(args: any): string {
235 | if (!args?.componentName || typeof args.componentName !== "string") {
236 | throw new McpError(
237 | ErrorCode.InvalidParams,
238 | "Component name is required and must be a string"
239 | );
240 | }
241 | return args.componentName.toLowerCase();
242 | }
243 |
244 | /**
245 | * Validates search query from arguments
246 | * @param args Arguments object
247 | * @returns Validated search query
248 | * @throws McpError if validation fails
249 | */
250 | private validateSearchQuery(args: any): string {
251 | if (!args?.query || typeof args.query !== "string") {
252 | throw new McpError(
253 | ErrorCode.InvalidParams,
254 | "Search query is required and must be a string"
255 | );
256 | }
257 | return args.query.toLowerCase();
258 | }
259 |
260 | /**
261 | * Handles Axios errors consistently
262 | * @param error The caught error
263 | * @param context Context information for the error message
264 | * @throws McpError with appropriate error code and message
265 | */
266 | private handleAxiosError(error: unknown, context: string): never {
267 | if (axios.isAxiosError(error)) {
268 | if (error.response?.status === 404) {
269 | throw new McpError(
270 | ErrorCode.InvalidParams,
271 | `${context} not found`
272 | );
273 | } else {
274 | throw new McpError(
275 | ErrorCode.InternalError,
276 | `${context}: ${error.message}`
277 | );
278 | }
279 | }
280 | throw error;
281 | }
282 |
283 | /**
284 | * Creates a standardized success response
285 | * @param data Data to include in the response
286 | * @returns Formatted response object
287 | */
288 | private createSuccessResponse(data: any) {
289 | return {
290 | content: [
291 | {
292 | type: "text",
293 | text: JSON.stringify(data, null, 2),
294 | },
295 | ],
296 | };
297 | }
298 |
299 | /**
300 | * Handle the get_component_details tool request
301 | */
302 | private async handleGetComponentDetails(args: any) {
303 | const componentName = this.validateComponentName(args);
304 |
305 | try {
306 | // Check cache first
307 | if (this.componentCache.has(componentName)) {
308 | return this.createSuccessResponse(this.componentCache.get(componentName));
309 | }
310 |
311 | // Fetch component details
312 | const componentInfo = await this.fetchComponentDetails(componentName);
313 |
314 | // Save to cache
315 | this.componentCache.set(componentName, componentInfo);
316 |
317 | return this.createSuccessResponse(componentInfo);
318 | } catch (error) {
319 | this.handleAxiosError(error, `Component "${componentName}"`);
320 | }
321 | }
322 |
323 | /**
324 | * Fetches component details from the shadcn/ui documentation
325 | * @param componentName Name of the component to fetch
326 | * @returns Component information
327 | */
328 | private async fetchComponentDetails(componentName: string): Promise<ComponentInfo> {
329 | const response = await this.axiosInstance.get(`${this.SHADCN_DOCS_URL}/docs/components/${componentName}`);
330 | const $ = cheerio.load(response.data);
331 |
332 | // Extract component information
333 | const title = $("h1").first().text().trim();
334 |
335 | // Extract description properly
336 | const description = this.extractDescription($);
337 |
338 | // Extract GitHub source code link
339 | const sourceUrl = `${this.SHADCN_GITHUB_URL}/tree/main/apps/www/registry/default/ui/${componentName}`;
340 |
341 | // Extract installation instructions
342 | const installation = this.extractInstallation($);
343 |
344 | // Extract usage examples
345 | const usage = this.extractUsage($);
346 |
347 | // Extract variant information
348 | const props = this.extractVariants($, componentName);
349 |
350 | return {
351 | name: componentName,
352 | description,
353 | url: `${this.SHADCN_DOCS_URL}/docs/components/${componentName}`,
354 | sourceUrl,
355 | installation: installation.trim(),
356 | usage: usage.trim(),
357 | props: Object.keys(props).length > 0 ? props : undefined,
358 | };
359 | }
360 |
361 | /**
362 | * Extracts component description from the page
363 | * @param $ Cheerio instance
364 | * @returns Extracted description
365 | */
366 | private extractDescription($: cheerio.CheerioAPI): string {
367 | let description = "";
368 | const descriptionElement = $("h1").first().next("p");
369 | if (descriptionElement.length > 0) {
370 | // Get only text content, removing any JavaScript code
371 | const clonedElement = descriptionElement.clone();
372 | clonedElement.find("script").remove();
373 | description = clonedElement.text().trim();
374 | }
375 | return description;
376 | }
377 |
378 | /**
379 | * Extracts installation instructions from the page
380 | * @param $ Cheerio instance
381 | * @returns Installation instructions
382 | */
383 | private extractInstallation($: cheerio.CheerioAPI): string {
384 | let installation = "";
385 | const installSection = $("h2").filter((_, el) => $(el).text().trim() === "Installation");
386 | if (installSection.length > 0) {
387 | // Find installation command
388 | const codeBlock = installSection.nextAll("pre").first();
389 | if (codeBlock.length > 0) {
390 | installation = codeBlock.text().trim();
391 | }
392 | }
393 | return installation;
394 | }
395 |
396 | /**
397 | * Extracts usage examples from the page
398 | * @param $ Cheerio instance
399 | * @returns Usage examples
400 | */
401 | private extractUsage($: cheerio.CheerioAPI): string {
402 | let usage = "";
403 | const usageSection = $("h2").filter((_, el) => $(el).text().trim() === "Usage");
404 | if (usageSection.length > 0) {
405 | const codeBlocks = usageSection.nextAll("pre");
406 | if (codeBlocks.length > 0) {
407 | codeBlocks.each((_, el) => {
408 | usage += $(el).text().trim() + "\n\n";
409 | });
410 | }
411 | }
412 | return usage;
413 | }
414 |
415 | /**
416 | * Extracts variant information from the page
417 | * @param $ Cheerio instance
418 | * @param componentName Name of the component
419 | * @returns Object containing variant properties
420 | */
421 | private extractVariants($: cheerio.CheerioAPI, componentName: string): Record<string, ComponentProp> {
422 | const props: Record<string, ComponentProp> = {};
423 |
424 | // Extract variants from Examples section
425 | const examplesSection = $("h2").filter((_, el) => $(el).text().trim() === "Examples");
426 | if (examplesSection.length > 0) {
427 | // Find each variant
428 | const variantHeadings = examplesSection.nextAll("h3");
429 |
430 | variantHeadings.each((_, heading) => {
431 | const variantName = $(heading).text().trim();
432 |
433 | // Get variant code example
434 | let codeExample = "";
435 |
436 | // Find Code tab
437 | const codeTab = $(heading).nextAll(".tabs-content").first();
438 | if (codeTab.length > 0) {
439 | const codeBlock = codeTab.find("pre");
440 | if (codeBlock.length > 0) {
441 | codeExample = codeBlock.text().trim();
442 | }
443 | }
444 |
445 | props[variantName] = {
446 | type: "variant",
447 | description: `${variantName} variant of the ${componentName} component`,
448 | required: false,
449 | example: codeExample
450 | };
451 | });
452 | }
453 |
454 | return props;
455 | }
456 |
457 | /**
458 | * Handle the get_component_examples tool request
459 | */
460 | private async handleGetComponentExamples(args: any) {
461 | const componentName = this.validateComponentName(args);
462 |
463 | try {
464 | // Fetch component examples
465 | const examples = await this.fetchComponentExamples(componentName);
466 | return this.createSuccessResponse(examples);
467 | } catch (error) {
468 | this.handleAxiosError(error, `Component examples for "${componentName}"`);
469 | }
470 | }
471 |
472 | /**
473 | * Fetches component examples from documentation and GitHub
474 | * @param componentName Name of the component
475 | * @returns Array of component examples
476 | */
477 | private async fetchComponentExamples(componentName: string): Promise<ComponentExample[]> {
478 | const response = await this.axiosInstance.get(`${this.SHADCN_DOCS_URL}/docs/components/${componentName}`);
479 | const $ = cheerio.load(response.data);
480 |
481 | const examples: ComponentExample[] = [];
482 |
483 | // Collect examples from different sources
484 | this.collectGeneralCodeExamples($, examples);
485 | this.collectSectionExamples($, "Usage", "Basic usage example", examples);
486 | this.collectSectionExamples($, "Link", "Link usage example", examples);
487 | await this.collectGitHubExamples(componentName, examples);
488 |
489 | return examples;
490 | }
491 |
492 | /**
493 | * Collects general code examples from the page
494 | * @param $ Cheerio instance
495 | * @param examples Array to add examples to
496 | */
497 | private collectGeneralCodeExamples($: cheerio.CheerioAPI, examples: ComponentExample[]): void {
498 | const codeBlocks = $("pre");
499 | codeBlocks.each((i, el) => {
500 | const code = $(el).text().trim();
501 | if (code) {
502 | // Find heading before code block
503 | let title = "Code Example " + (i + 1);
504 | let description = "Code example";
505 |
506 | // Look for headings
507 | let prevElement = $(el).prev();
508 | while (prevElement.length && !prevElement.is("h1") && !prevElement.is("h2") && !prevElement.is("h3")) {
509 | prevElement = prevElement.prev();
510 | }
511 |
512 | if (prevElement.is("h2") || prevElement.is("h3")) {
513 | title = prevElement.text().trim();
514 | description = `${title} example`;
515 | }
516 |
517 | examples.push({
518 | title,
519 | code,
520 | description
521 | });
522 | }
523 | });
524 | }
525 |
526 | /**
527 | * Collects examples from a specific section
528 | * @param $ Cheerio instance
529 | * @param sectionName Name of the section to collect from
530 | * @param descriptionPrefix Prefix for the description
531 | * @param examples Array to add examples to
532 | */
533 | private collectSectionExamples(
534 | $: cheerio.CheerioAPI,
535 | sectionName: string,
536 | descriptionPrefix: string,
537 | examples: ComponentExample[]
538 | ): void {
539 | const section = $("h2").filter((_, el) => $(el).text().trim() === sectionName);
540 | if (section.length > 0) {
541 | const codeBlocks = section.nextAll("pre");
542 | codeBlocks.each((i, el) => {
543 | const code = $(el).text().trim();
544 | if (code) {
545 | examples.push({
546 | title: `${sectionName} Example ${i + 1}`,
547 | code: code,
548 | description: descriptionPrefix
549 | });
550 | }
551 | });
552 | }
553 | }
554 |
555 | /**
556 | * Collects examples from GitHub repository
557 | * @param componentName Name of the component
558 | * @param examples Array to add examples to
559 | */
560 | private async collectGitHubExamples(componentName: string, examples: ComponentExample[]): Promise<void> {
561 | try {
562 | const githubResponse = await this.axiosInstance.get(
563 | `${this.SHADCN_RAW_GITHUB_URL}/apps/www/registry/default/example/${componentName}-demo.tsx`
564 | );
565 |
566 | if (githubResponse.status === 200) {
567 | examples.push({
568 | title: "GitHub Demo Example",
569 | code: githubResponse.data,
570 | });
571 | }
572 | } catch (error) {
573 | // Continue even if GitHub fetch fails
574 | console.error(`Failed to fetch GitHub example for ${componentName}:`, error);
575 | }
576 | }
577 |
578 | /**
579 | * Handle the search_components tool request
580 | */
581 | private async handleSearchComponents(args: any) {
582 | const query = this.validateSearchQuery(args);
583 |
584 | try {
585 | // Ensure components list is loaded
586 | await this.ensureComponentsListLoaded();
587 |
588 | // Filter components matching the search query
589 | const results = this.searchComponentsByQuery(query);
590 |
591 | return this.createSuccessResponse(results);
592 | } catch (error) {
593 | this.handleAxiosError(error, "Search failed");
594 | }
595 | }
596 |
597 | /**
598 | * Ensures the components list is loaded in cache
599 | * @throws McpError if components list cannot be loaded
600 | */
601 | private async ensureComponentsListLoaded(): Promise<void> {
602 | if (!this.componentsListCache) {
603 | await this.handleListComponents();
604 | }
605 |
606 | if (!this.componentsListCache) {
607 | throw new McpError(
608 | ErrorCode.InternalError,
609 | "Failed to load components list"
610 | );
611 | }
612 | }
613 |
614 | /**
615 | * Searches components by query string
616 | * @param query Search query
617 | * @returns Filtered components
618 | */
619 | private searchComponentsByQuery(query: string): ComponentInfo[] {
620 | if (!this.componentsListCache) {
621 | return [];
622 | }
623 |
624 | return this.componentsListCache.filter(component => {
625 | return (
626 | component.name.includes(query) ||
627 | component.description.toLowerCase().includes(query)
628 | );
629 | });
630 | }
631 |
632 | /**
633 | * Run the server
634 | */
635 | async run() {
636 | const transport = new StdioServerTransport();
637 | await this.server.connect(transport);
638 | console.error("shadcn/ui MCP server running on stdio");
639 | }
640 | }
641 |
642 | // Create and run the server
643 | const server = new ShadcnUiServer();
644 | server.run().catch((error) => {
645 | console.error("Server error:", error);
646 | process.exit(1);
647 | });
648 |
```