@amedia/brick-mcp 0.0.1-NEW-PATH-1 → 0.0.1-SNAPSHOT-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +269 -58
- package/dist/commands/serve.js +42 -0
- package/dist/commands/stdio.js +11 -0
- package/dist/extractors/packageScanner.js +150 -0
- package/dist/http.js +551 -107
- package/dist/http.js.map +4 -4
- package/dist/index.js +541 -63
- package/dist/index.js.map +4 -4
- package/dist/indexer/buildIndex.js +12 -0
- package/dist/indexer/search.js +12 -0
- package/dist/resources/componentResource.js +24 -0
- package/dist/resources/docsResource.js +44 -0
- package/dist/resources/index.js +49 -0
- package/dist/resources/tokenResource.js +20 -0
- package/dist/server.js +198 -0
- package/dist/tools/getComponentDocs.js +69 -0
- package/dist/tools/getDesignTokens.js +127 -0
- package/dist/tools/listComponents.js +16 -0
- package/dist/tools/searchComponents.js +66 -0
- package/dist/types.js +75 -0
- package/dist/utils.js +8 -0
- package/package.json +4 -18
- package/dist/data/components/brick-actions.json +0 -6
- package/dist/data/components/brick-alt-teaser.json +0 -10
- package/dist/data/components/brick-avatar.json +0 -11
- package/dist/data/components/brick-button.json +0 -12
- package/dist/data/components/brick-card.json +0 -10
- package/dist/data/components/brick-carousel.json +0 -11
- package/dist/data/components/brick-classnames.json +0 -10
- package/dist/data/components/brick-countdown.json +0 -7
- package/dist/data/components/brick-dialog.json +0 -11
- package/dist/data/components/brick-fonts.json +0 -10
- package/dist/data/components/brick-helloworld.json +0 -7
- package/dist/data/components/brick-icon.json +0 -10
- package/dist/data/components/brick-icons.json +0 -11
- package/dist/data/components/brick-illustrations.json +0 -7
- package/dist/data/components/brick-image.json +0 -10
- package/dist/data/components/brick-input.json +0 -12
- package/dist/data/components/brick-mcp.json +0 -6
- package/dist/data/components/brick-nifs.json +0 -7
- package/dist/data/components/brick-pill.json +0 -6
- package/dist/data/components/brick-player.json +0 -7
- package/dist/data/components/brick-published.json +0 -7
- package/dist/data/components/brick-share.json +0 -7
- package/dist/data/components/brick-stepper.json +0 -7
- package/dist/data/components/brick-tab.json +0 -7
- package/dist/data/components/brick-tabs.json +0 -11
- package/dist/data/components/brick-tag.json +0 -7
- package/dist/data/components/brick-teaser-player.json +0 -11
- package/dist/data/components/brick-teaser-reels.json +0 -11
- package/dist/data/components/brick-teaser.json +0 -11
- package/dist/data/components/brick-template.json +0 -11
- package/dist/data/components/brick-textarea.json +0 -7
- package/dist/data/components/brick-themes.json +0 -6
- package/dist/data/components/brick-toast.json +0 -11
- package/dist/data/components/brick-toggle.json +0 -7
- package/dist/data/components/brick-tokens.json +0 -10
- package/dist/data/components/brick-tooltip.json +0 -7
- package/dist/data/components-metadata.json +0 -290
- package/dist/data/components.json +0 -321
- package/dist/data/tokens.json +0 -256
- package/scripts/generate-data.js +0 -245
package/README.md
CHANGED
|
@@ -4,38 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
This is a Model Context Protocol (MCP) server for the Brick design system. It enables AI assistants (like Claude Code) to provide accurate, context-aware assistance when developers implement Brick components.
|
|
6
6
|
|
|
7
|
-
## Prerequisites
|
|
8
|
-
|
|
9
|
-
Before using the Brick MCP server, you need to set up Claude Code:
|
|
10
|
-
|
|
11
|
-
> **Note**: This guide assumes that you have pulled the latest changes and installed all dependencies.
|
|
12
|
-
|
|
13
|
-
### 1. Install Claude CLI
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
curl -fsSL https://claude.ai/install.sh | bash
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
### 2. Authenticate with Google Cloud
|
|
20
|
-
|
|
21
|
-
You'll need to authenticate daily to access Amedia's coding agent project:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
gcloud auth application-default login --project amedia-coding-agent
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
> **Note**: You will need to re-authenticate once per day.
|
|
28
|
-
|
|
29
|
-
### 3. Install Claude Code VS Code Extension (Optional)
|
|
30
|
-
|
|
31
|
-
If you prefer using Claude Code within VS Code:
|
|
32
|
-
|
|
33
|
-
1. Open VS Code
|
|
34
|
-
2. Go to the Extensions marketplace
|
|
35
|
-
3. Search for "Claude Code"
|
|
36
|
-
4. Install the extension (the one from Anthropic)
|
|
37
|
-
5. Restart VS Code if necessary
|
|
38
|
-
|
|
39
7
|
## Usage
|
|
40
8
|
|
|
41
9
|
The Brick MCP server can be run in two modes:
|
|
@@ -45,7 +13,7 @@ The Brick MCP server can be run in two modes:
|
|
|
45
13
|
To add the Brick MCP server to Claude Code using stdio transport:
|
|
46
14
|
|
|
47
15
|
```bash
|
|
48
|
-
claude mcp add --transport stdio Brick -- node /absolute/path/to/brick/
|
|
16
|
+
claude mcp add --transport stdio Brick -- node /absolute/path/to/brick/mcp-server/src/index.ts
|
|
49
17
|
```
|
|
50
18
|
|
|
51
19
|
Replace `/absolute/path/to/brick/` with the actual path to your Brick repository.
|
|
@@ -67,7 +35,6 @@ PORT=3001 HOST=localhost npm run start:http
|
|
|
67
35
|
```
|
|
68
36
|
|
|
69
37
|
The HTTP server exposes:
|
|
70
|
-
|
|
71
38
|
- `http://localhost:3000/health` - Health check endpoint
|
|
72
39
|
- `http://localhost:3000/sse` - SSE endpoint for MCP protocol communication
|
|
73
40
|
- `http://localhost:3000/message` - Message endpoint for client-to-server communication
|
|
@@ -104,16 +71,13 @@ The Model Context Protocol (MCP) is a standardized way to expose resources, tool
|
|
|
104
71
|
|
|
105
72
|
MCP servers can be written in TypeScript using the `@modelcontextprotocol/sdk` package.
|
|
106
73
|
|
|
107
|
-
##
|
|
74
|
+
## Proposed Architecture
|
|
108
75
|
|
|
109
|
-
|
|
76
|
+
### MCP Tools (Actions)
|
|
110
77
|
|
|
111
|
-
|
|
78
|
+
Tools allow AI assistants to actively query and search the Brick design system.
|
|
112
79
|
|
|
113
|
-
|
|
114
|
-
<summary>
|
|
115
|
-
<strong>1. <code>list-components</code></strong>
|
|
116
|
-
</summary>
|
|
80
|
+
#### 1. `list-components`
|
|
117
81
|
|
|
118
82
|
**Purpose**: List all available Brick components with metadata
|
|
119
83
|
|
|
@@ -142,10 +106,9 @@ These tools make Claude context-aware about the specific Brick setup, ensuring a
|
|
|
142
106
|
|
|
143
107
|
**Use Case**: "What components are available in Brick?"
|
|
144
108
|
|
|
145
|
-
|
|
109
|
+
---
|
|
146
110
|
|
|
147
|
-
|
|
148
|
-
<summary><strong>2. <code>get-component-docs</code></strong></summary>
|
|
111
|
+
#### 2. `get-component-docs`
|
|
149
112
|
|
|
150
113
|
**Purpose**: Retrieve detailed documentation for specific component(s)
|
|
151
114
|
|
|
@@ -220,10 +183,9 @@ These tools make Claude context-aware about the specific Brick setup, ensuring a
|
|
|
220
183
|
|
|
221
184
|
**Use Case**: "How do I use brick-button? What props does it accept?"
|
|
222
185
|
|
|
223
|
-
|
|
186
|
+
---
|
|
224
187
|
|
|
225
|
-
|
|
226
|
-
<summary><strong>3. <code>get-design-tokens</code></strong></summary>
|
|
188
|
+
#### 3. `get-design-tokens`
|
|
227
189
|
|
|
228
190
|
**Purpose**: Access design tokens from brick-tokens
|
|
229
191
|
|
|
@@ -252,10 +214,9 @@ These tools make Claude context-aware about the specific Brick setup, ensuring a
|
|
|
252
214
|
|
|
253
215
|
**Use Case**: "What color tokens are available in Brick?"
|
|
254
216
|
|
|
255
|
-
|
|
217
|
+
---
|
|
256
218
|
|
|
257
|
-
|
|
258
|
-
<summary><strong>4. <code>search-components</code></strong></summary>
|
|
219
|
+
#### 4. `search-components`
|
|
259
220
|
|
|
260
221
|
**Purpose**: Search components by keyword or functionality
|
|
261
222
|
|
|
@@ -283,10 +244,9 @@ These tools make Claude context-aware about the specific Brick setup, ensuring a
|
|
|
283
244
|
|
|
284
245
|
**Use Case**: "Find components related to forms"
|
|
285
246
|
|
|
286
|
-
|
|
247
|
+
---
|
|
287
248
|
|
|
288
|
-
|
|
289
|
-
<summary><strong>5. <code>get-usage-examples</code> (Optional for v1)</strong></summary>
|
|
249
|
+
#### 5. `get-usage-examples` (Optional for v1)
|
|
290
250
|
|
|
291
251
|
**Purpose**: Get real-world code examples from Storybook stories
|
|
292
252
|
|
|
@@ -314,13 +274,264 @@ These tools make Claude context-aware about the specific Brick setup, ensuring a
|
|
|
314
274
|
|
|
315
275
|
**Use Case**: "Show me examples of brick-button in different states"
|
|
316
276
|
|
|
317
|
-
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
### MCP Resources (Static/Dynamic Data)
|
|
280
|
+
|
|
281
|
+
Resources provide direct access to structured data without complex queries.
|
|
282
|
+
|
|
283
|
+
- **`brick://components`**
|
|
284
|
+
- List of all components (similar to list-components tool)
|
|
285
|
+
|
|
286
|
+
- **`brick://component/{name}`**
|
|
287
|
+
- Individual component documentation (e.g., `brick://component/brick-button`)
|
|
288
|
+
|
|
289
|
+
- **`brick://tokens`**
|
|
290
|
+
- All design tokens
|
|
291
|
+
|
|
292
|
+
- **`brick://tokens/{category}`**
|
|
293
|
+
- Tokens by category (e.g., `brick://tokens/colors`)
|
|
294
|
+
|
|
295
|
+
- **`brick://getting-started`**
|
|
296
|
+
- Setup and installation guide
|
|
297
|
+
|
|
298
|
+
- **`brick://architecture`**
|
|
299
|
+
- Architecture overview from CLAUDE.md
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Implementation Approach
|
|
304
|
+
|
|
305
|
+
### Package Structure
|
|
306
|
+
|
|
307
|
+
Create a new package in the monorepo:
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
packages/brick-mcp-server/
|
|
311
|
+
├── src/
|
|
312
|
+
│ ├── index.ts # Main MCP server entry point
|
|
313
|
+
│ ├── server.ts # MCP server setup and registration
|
|
314
|
+
│ ├── tools/
|
|
315
|
+
│ │ ├── listComponents.ts
|
|
316
|
+
│ │ ├── getComponentDocs.ts
|
|
317
|
+
│ │ ├── getDesignTokens.ts
|
|
318
|
+
│ │ ├── searchComponents.ts
|
|
319
|
+
│ │ └── getUsageExamples.ts
|
|
320
|
+
│ ├── resources/
|
|
321
|
+
│ │ ├── componentResource.ts
|
|
322
|
+
│ │ ├── tokenResource.ts
|
|
323
|
+
│ │ └── docsResource.ts
|
|
324
|
+
│ ├── extractors/
|
|
325
|
+
│ │ ├── packageScanner.ts # Scan packages/ for components
|
|
326
|
+
│ │ ├── mdxParser.ts # Parse Storybook MDX files
|
|
327
|
+
│ │ ├── typeExtractor.ts # Extract TS types from files
|
|
328
|
+
│ │ ├── tokenExtractor.ts # Extract design tokens
|
|
329
|
+
│ │ └── storyExtractor.ts # Extract examples from stories
|
|
330
|
+
│ ├── indexer/
|
|
331
|
+
│ │ ├── buildIndex.ts # Build searchable index
|
|
332
|
+
│ │ └── search.ts # Search implementation
|
|
333
|
+
│ ├── types.ts # TypeScript types
|
|
334
|
+
│ └── utils.ts # Helper functions
|
|
335
|
+
├── build.js # Build script
|
|
336
|
+
├── package.json
|
|
337
|
+
├── README.md
|
|
338
|
+
└── tsconfig.json
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Documentation Extraction Strategy
|
|
342
|
+
|
|
343
|
+
The server will extract documentation from various sources in the monorepo:
|
|
344
|
+
|
|
345
|
+
1. **Component Metadata** (from `package.json`):
|
|
346
|
+
- Name, version, description
|
|
347
|
+
- Dependencies
|
|
348
|
+
|
|
349
|
+
2. **Type Definitions** (from `src/types.ts` or `src/template.ts`):
|
|
350
|
+
- Props/attributes with types
|
|
351
|
+
- Event types
|
|
352
|
+
- Method signatures
|
|
353
|
+
- Parse TypeScript AST to extract interfaces
|
|
354
|
+
|
|
355
|
+
3. **Documentation** (from `stories/*.mdx`):
|
|
356
|
+
- Rich descriptions
|
|
357
|
+
- Usage guidelines
|
|
358
|
+
- Accessibility information
|
|
359
|
+
- Parse MDX frontmatter and content
|
|
360
|
+
|
|
361
|
+
4. **Examples** (from `stories/*.stories.ts`):
|
|
362
|
+
- Real code examples from Storybook
|
|
363
|
+
- Different component states
|
|
364
|
+
- Parse story definitions
|
|
365
|
+
|
|
366
|
+
5. **Styles** (from `src/styles.js`):
|
|
367
|
+
- CSS custom properties
|
|
368
|
+
- Available variants
|
|
369
|
+
- Parse Stitches configuration
|
|
370
|
+
|
|
371
|
+
6. **Design Tokens** (from `packages/brick-tokens`):
|
|
372
|
+
- Token definitions
|
|
373
|
+
- Theme variations
|
|
374
|
+
- Parse token JSON/JS files
|
|
375
|
+
|
|
376
|
+
7. **Repository Metadata** (from `CLAUDE.md`, `README.md`):
|
|
377
|
+
- Getting started guide
|
|
378
|
+
- Architecture overview
|
|
379
|
+
- Common patterns
|
|
380
|
+
|
|
381
|
+
### Indexing and Search
|
|
382
|
+
|
|
383
|
+
To enable fast search:
|
|
384
|
+
|
|
385
|
+
1. **Build-time indexing**: Create a JSON index of all components with searchable fields
|
|
386
|
+
2. **Fuzzy search**: Use libraries like `fuse.js` for fuzzy text matching
|
|
387
|
+
3. **Semantic categories**: Tag components by purpose (forms, navigation, feedback, etc.)
|
|
388
|
+
4. **Cache**: Cache extracted documentation to avoid repeated parsing
|
|
389
|
+
|
|
390
|
+
### Technology Stack
|
|
391
|
+
|
|
392
|
+
- **MCP SDK**: `@modelcontextprotocol/sdk` for server implementation
|
|
393
|
+
- **TypeScript**: Type-safe implementation
|
|
394
|
+
- **AST Parsing**:
|
|
395
|
+
- `typescript` compiler API for TS parsing
|
|
396
|
+
- `@mdx-js/mdx` or custom parser for MDX
|
|
397
|
+
- `@babel/parser` for JS/JSX parsing
|
|
398
|
+
- **Search**: `fuse.js` for fuzzy search
|
|
399
|
+
- **File System**: Node.js `fs` module for scanning packages
|
|
400
|
+
|
|
401
|
+
### Deployment Options
|
|
402
|
+
|
|
403
|
+
1. **Local Development** (stdio transport):
|
|
404
|
+
- Users run the server locally via `npx @amedia/brick-mcp-server`
|
|
405
|
+
- Configured in Claude Code settings
|
|
406
|
+
|
|
407
|
+
2. **HTTP Server** (optional):
|
|
408
|
+
- Deploy as a service for remote access
|
|
409
|
+
- Use `@modelcontextprotocol/sdk/server/streamableHttp.js`
|
|
410
|
+
|
|
411
|
+
3. **npm Package**:
|
|
412
|
+
- Publish as `@amedia/brick-mcp-server`
|
|
413
|
+
- Users install and configure in their MCP client
|
|
414
|
+
|
|
415
|
+
### Example Server Setup
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
import {
|
|
419
|
+
McpServer,
|
|
420
|
+
ResourceTemplate,
|
|
421
|
+
} from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
422
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
423
|
+
import { listComponents } from './tools/listComponents.js';
|
|
424
|
+
import { getComponentDocs } from './tools/getComponentDocs.js';
|
|
425
|
+
|
|
426
|
+
// Create MCP server
|
|
427
|
+
const server = new McpServer({
|
|
428
|
+
name: 'brick-design-system',
|
|
429
|
+
version: '1.0.0',
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Register tools
|
|
433
|
+
server.registerTool(
|
|
434
|
+
'list-components',
|
|
435
|
+
{
|
|
436
|
+
title: 'List Brick Components',
|
|
437
|
+
description: 'List all available Brick components with metadata',
|
|
438
|
+
inputSchema: { filter: z.string().optional() },
|
|
439
|
+
outputSchema: z.object({
|
|
440
|
+
components: z.array(
|
|
441
|
+
z.object({
|
|
442
|
+
name: z.string(),
|
|
443
|
+
version: z.string(),
|
|
444
|
+
selector: z.string(),
|
|
445
|
+
description: z.string(),
|
|
446
|
+
})
|
|
447
|
+
),
|
|
448
|
+
}),
|
|
449
|
+
},
|
|
450
|
+
async ({ filter }) => {
|
|
451
|
+
const components = await listComponents(filter);
|
|
452
|
+
return {
|
|
453
|
+
content: [{ type: 'text', text: JSON.stringify(components) }],
|
|
454
|
+
structuredContent: { components },
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
// Register resources
|
|
460
|
+
server.registerResource(
|
|
461
|
+
'components',
|
|
462
|
+
new ResourceTemplate('brick://components', { list: undefined }),
|
|
463
|
+
{
|
|
464
|
+
title: 'Brick Components',
|
|
465
|
+
description: 'List of all Brick components',
|
|
466
|
+
},
|
|
467
|
+
async (uri) => {
|
|
468
|
+
const components = await listComponents();
|
|
469
|
+
return {
|
|
470
|
+
contents: [
|
|
471
|
+
{
|
|
472
|
+
uri: uri.href,
|
|
473
|
+
text: JSON.stringify(components, null, 2),
|
|
474
|
+
mimeType: 'application/json',
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// Start server with stdio transport
|
|
482
|
+
const transport = new StdioServerTransport();
|
|
483
|
+
await server.connect(transport);
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## Development Workflow
|
|
489
|
+
|
|
490
|
+
### Phase 1: Foundation
|
|
491
|
+
|
|
492
|
+
1. Set up package structure in `packages/brick-mcp-server/`
|
|
493
|
+
2. Install MCP SDK and dependencies
|
|
494
|
+
3. Create basic server with one tool (list-components)
|
|
495
|
+
4. Implement package scanner to find all brick-\* packages
|
|
496
|
+
|
|
497
|
+
### Phase 2: Documentation Extraction
|
|
498
|
+
|
|
499
|
+
1. Build TypeScript type extractor
|
|
500
|
+
2. Build MDX documentation parser
|
|
501
|
+
3. Build Storybook story parser
|
|
502
|
+
4. Build design token extractor
|
|
503
|
+
|
|
504
|
+
### Phase 3: Core Tools
|
|
505
|
+
|
|
506
|
+
1. Implement all MCP tools
|
|
507
|
+
2. Implement search functionality
|
|
508
|
+
3. Build documentation index
|
|
509
|
+
|
|
510
|
+
### Phase 4: Resources
|
|
511
|
+
|
|
512
|
+
1. Implement MCP resources
|
|
513
|
+
2. Add caching layer
|
|
514
|
+
3. Add error handling
|
|
515
|
+
|
|
516
|
+
### Phase 5: Testing & Documentation
|
|
517
|
+
|
|
518
|
+
1. Write unit tests for extractors
|
|
519
|
+
2. Write integration tests for MCP tools
|
|
520
|
+
3. Create comprehensive README
|
|
521
|
+
4. Add usage examples
|
|
522
|
+
|
|
523
|
+
### Phase 6: Publishing
|
|
524
|
+
|
|
525
|
+
1. Build and bundle for npm
|
|
526
|
+
2. Publish to npm as `@amedia/brick-mcp-server`
|
|
527
|
+
3. Document installation in Brick docs
|
|
528
|
+
4. Create video/guide for users
|
|
318
529
|
|
|
319
530
|
---
|
|
320
531
|
|
|
321
532
|
## Usage Example
|
|
322
533
|
|
|
323
|
-
|
|
534
|
+
Once configured, you can interact with the Brick MCP server in Claude Code:
|
|
324
535
|
|
|
325
536
|
```
|
|
326
537
|
User: "I need to add a button to my app using Brick"
|
|
@@ -386,6 +597,6 @@ claude mcp add --transport stdio Brick -- npx @amedia/brick-mcp
|
|
|
386
597
|
|
|
387
598
|
## References
|
|
388
599
|
|
|
389
|
-
- MCP TypeScript SDK:
|
|
390
|
-
- MCP Example Servers:
|
|
391
|
-
- Brick
|
|
600
|
+
- MCP TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
|
|
601
|
+
- MCP Example Servers: https://github.com/modelcontextprotocol/servers
|
|
602
|
+
- Brick Monorepo: `/Users/simonsundstrom/dev/amedia/brick`
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP/Fastify transport command
|
|
3
|
+
* Used for running the MCP server as an HTTP service
|
|
4
|
+
*/
|
|
5
|
+
import Fastify from 'fastify';
|
|
6
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
7
|
+
import { createServer } from '../server.ts';
|
|
8
|
+
export async function runServe(options = {}) {
|
|
9
|
+
const port = options.port ?? 3000;
|
|
10
|
+
const host = options.host ?? '0.0.0.0';
|
|
11
|
+
const fastify = Fastify({
|
|
12
|
+
logger: true,
|
|
13
|
+
});
|
|
14
|
+
// Health check endpoint
|
|
15
|
+
fastify.get('/health', async () => {
|
|
16
|
+
return { status: 'ok' };
|
|
17
|
+
});
|
|
18
|
+
// MCP SSE endpoint
|
|
19
|
+
fastify.get('/sse', async (request, reply) => {
|
|
20
|
+
const server = createServer();
|
|
21
|
+
const transport = new SSEServerTransport('/messages', reply.raw);
|
|
22
|
+
await server.connect(transport);
|
|
23
|
+
// Keep connection alive
|
|
24
|
+
request.raw.on('close', () => {
|
|
25
|
+
server.close();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
// POST endpoint for messages
|
|
29
|
+
fastify.post('/messages', async () => {
|
|
30
|
+
// SSE transport handles this internally
|
|
31
|
+
return { received: true };
|
|
32
|
+
});
|
|
33
|
+
try {
|
|
34
|
+
await fastify.listen({ port, host });
|
|
35
|
+
console.log(`Brick MCP server listening on http://${host}:${port}`);
|
|
36
|
+
console.log(`SSE endpoint: http://${host}:${port}/sse`);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
fastify.log.error(err);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stdio transport command
|
|
3
|
+
* Used by Claude Desktop and other stdio-based MCP clients
|
|
4
|
+
*/
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { createServer } from '../server.ts';
|
|
7
|
+
export async function runStdio() {
|
|
8
|
+
const server = createServer();
|
|
9
|
+
const transport = new StdioServerTransport();
|
|
10
|
+
await server.connect(transport);
|
|
11
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package scanner for discovering Brick components
|
|
3
|
+
*/
|
|
4
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { PackageJsonSchema, } from '../types.ts';
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
/**
|
|
11
|
+
* Get the root directory of the Brick monorepo
|
|
12
|
+
* Assumes mcp-server is in the root of the brick repository
|
|
13
|
+
*/
|
|
14
|
+
export function getBrickRoot() {
|
|
15
|
+
// Go up from mcp-server/src to brick root
|
|
16
|
+
return join(__dirname, '..', '..');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Extract component selector from package name
|
|
20
|
+
* e.g., "@amedia/brick-button" -> "brick-button"
|
|
21
|
+
*/
|
|
22
|
+
export function extractSelector(packageName, version) {
|
|
23
|
+
const componentName = packageName.replace('@amedia/', '');
|
|
24
|
+
const majorVersion = version.split('.')[0];
|
|
25
|
+
return `${componentName}-v${majorVersion}`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Categorize component based on name or package.json metadata
|
|
29
|
+
* This is a simple heuristic - can be improved with actual metadata
|
|
30
|
+
*/
|
|
31
|
+
export function categorizeComponent(name) {
|
|
32
|
+
const categoryMap = {
|
|
33
|
+
Forms: ['button', 'input', 'checkbox', 'radio', 'select', 'form'],
|
|
34
|
+
Navigation: ['menu', 'nav', 'breadcrumb', 'tabs', 'link'],
|
|
35
|
+
Layout: ['grid', 'card', 'container', 'layout', 'section'],
|
|
36
|
+
Feedback: ['alert', 'toast', 'notification', 'banner', 'dialog', 'modal'],
|
|
37
|
+
Display: ['avatar', 'badge', 'icon', 'image', 'teaser', 'carousel'],
|
|
38
|
+
Utilities: ['tokens', 'classnames', 'template', 'fonts'],
|
|
39
|
+
};
|
|
40
|
+
const componentName = name.toLowerCase();
|
|
41
|
+
for (const [category, keywords] of Object.entries(categoryMap)) {
|
|
42
|
+
if (keywords.some((keyword) => componentName.includes(keyword))) {
|
|
43
|
+
return category;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Extract tags from component name
|
|
50
|
+
*/
|
|
51
|
+
export function extractTags(name, category) {
|
|
52
|
+
const tags = [];
|
|
53
|
+
if (category) {
|
|
54
|
+
tags.push(category.toLowerCase());
|
|
55
|
+
}
|
|
56
|
+
// Add interactive tag for form/button components
|
|
57
|
+
if (name.includes('button') ||
|
|
58
|
+
name.includes('input') ||
|
|
59
|
+
name.includes('select')) {
|
|
60
|
+
tags.push('interactive');
|
|
61
|
+
}
|
|
62
|
+
return tags;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Read and parse package.json for a given package path
|
|
66
|
+
*/
|
|
67
|
+
export async function readPackageJson(packagePath) {
|
|
68
|
+
try {
|
|
69
|
+
const packageJsonPath = join(packagePath, 'package.json');
|
|
70
|
+
const content = await readFile(packageJsonPath, 'utf-8');
|
|
71
|
+
const data = JSON.parse(content);
|
|
72
|
+
return PackageJsonSchema.parse(data);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`Error reading package.json at ${packagePath}:`, error);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Scan a single package directory and extract component metadata
|
|
81
|
+
*/
|
|
82
|
+
export async function scanPackage(packagePath) {
|
|
83
|
+
const packageJson = await readPackageJson(packagePath);
|
|
84
|
+
if (!packageJson) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
// Only include packages that start with @amedia/brick- and exclude utility packages
|
|
88
|
+
if (!packageJson.name.startsWith('@amedia/brick-')) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const componentName = packageJson.name.replace('@amedia/', '');
|
|
92
|
+
const selector = extractSelector(packageJson.name, packageJson.version);
|
|
93
|
+
const category = categorizeComponent(componentName);
|
|
94
|
+
const tags = extractTags(componentName, category);
|
|
95
|
+
return {
|
|
96
|
+
name: componentName,
|
|
97
|
+
version: packageJson.version,
|
|
98
|
+
selector,
|
|
99
|
+
description: packageJson.description,
|
|
100
|
+
category,
|
|
101
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
102
|
+
packagePath,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Scan all packages in the Brick monorepo
|
|
107
|
+
*/
|
|
108
|
+
export async function scanAllPackages() {
|
|
109
|
+
const brickRoot = getBrickRoot();
|
|
110
|
+
const packagesDir = join(brickRoot, 'packages');
|
|
111
|
+
try {
|
|
112
|
+
const entries = await readdir(packagesDir, { withFileTypes: true });
|
|
113
|
+
const packageDirs = entries
|
|
114
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith('brick-'))
|
|
115
|
+
.map((entry) => join(packagesDir, entry.name));
|
|
116
|
+
const components = await Promise.all(packageDirs.map((dir) => scanPackage(dir)));
|
|
117
|
+
// Filter out null results and sort by name
|
|
118
|
+
return components
|
|
119
|
+
.filter((component) => component !== null)
|
|
120
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error('Error scanning packages:', error);
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Filter components by category or tag
|
|
129
|
+
*/
|
|
130
|
+
export function filterComponents(components, filter) {
|
|
131
|
+
if (!filter) {
|
|
132
|
+
return components;
|
|
133
|
+
}
|
|
134
|
+
const filterLower = filter.toLowerCase();
|
|
135
|
+
return components.filter((component) => {
|
|
136
|
+
// Match against name
|
|
137
|
+
if (component.name.toLowerCase().includes(filterLower)) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
// Match against category
|
|
141
|
+
if (component.category?.toLowerCase().includes(filterLower)) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
// Match against tags
|
|
145
|
+
if (component.tags?.some((tag) => tag.toLowerCase().includes(filterLower))) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
});
|
|
150
|
+
}
|