@empathyds/mcp-server 0.0.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 ADDED
@@ -0,0 +1,145 @@
1
+ # @empathyds/mcp-server
2
+
3
+ MCP (Model Context Protocol) server for Empathy DS - enables AI assistants to discover and use Empathy DS Vue components and blocks.
4
+
5
+ ## Features
6
+
7
+ - **Component Discovery**: List all 60+ Empathy DS components
8
+ - **Detailed Information**: Get props, variants, and usage examples
9
+ - **Category Filtering**: Browse by Forms, Layout, Feedback, Navigation, etc.
10
+ - **Blocks Support**: Pre-built UI patterns (login forms, dashboards)
11
+ - **Search**: Find components by name or description
12
+ - **Remote Registry**: Fetches data from empathyds.com (works anywhere!)
13
+
14
+ ---
15
+
16
+ ## Quick Start
17
+
18
+ ### With Antigravity
19
+
20
+ Add to `~/.gemini/antigravity/mcp_config.json`:
21
+
22
+ ```json
23
+ {
24
+ "mcpServers": {
25
+ "empathy-ds": {
26
+ "command": "npx",
27
+ "args": ["-y", "@empathyds/mcp-server"]
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ---
34
+
35
+ ## How It Works
36
+
37
+ The MCP server fetches component data from `https://empathyds.com/registry.json`:
38
+ - Works for **anyone** using `npx @empathyds/mcp-server`
39
+ - Falls back to local filesystem for development
40
+ - Caches registry data for 5 minutes
41
+
42
+ ---
43
+
44
+ ## Available Tools (8 Total)
45
+
46
+ | Tool | Description |
47
+ |------|-------------|
48
+ | `getAllComponents` | Lists all 60+ components |
49
+ | `getComponent` | Gets props, examples for a component |
50
+ | `getComponentsByCategory` | Filter by Forms, Layout, Feedback, etc. |
51
+ | `searchComponents` | Search by name or description |
52
+ | `getCategories` | Lists all 7 categories |
53
+ | `getBlocks` | Lists all pre-built UI patterns |
54
+ | `getBlock` | Gets details for a specific block |
55
+ | `getUsageGuide` | Best practices for Empathy DS |
56
+
57
+ ---
58
+
59
+ ## Publishing to npm
60
+
61
+ ### 1. Generate the Registry
62
+
63
+ ```bash
64
+ # From monorepo root
65
+ node scripts/generate-registry.js
66
+ ```
67
+
68
+ This creates `apps/docs/public/registry.json` which gets deployed to empathyds.com.
69
+
70
+ ### 2. Deploy Docs Site
71
+
72
+ Deploy your docs to make `https://empathyds.com/registry.json` accessible.
73
+
74
+ ### 3. Publish MCP Server
75
+
76
+ ```bash
77
+ cd packages/mcp-server
78
+
79
+ # Ensure you're logged in
80
+ npm login
81
+
82
+ # Build
83
+ pnpm build
84
+
85
+ # Publish
86
+ npm publish --access public
87
+ ```
88
+
89
+ ### 4. Users Can Now Use It
90
+
91
+ ```bash
92
+ npx @empathyds/mcp-server
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Development
98
+
99
+ ### Local Testing
100
+
101
+ ```bash
102
+ cd packages/mcp-server
103
+ pnpm install
104
+ pnpm build
105
+ pnpm inspect # Opens MCP Inspector
106
+ ```
107
+
108
+ ### Local Antigravity Config
109
+
110
+ For development, use the local path:
111
+
112
+ ```json
113
+ {
114
+ "mcpServers": {
115
+ "empathy-ds": {
116
+ "command": "node",
117
+ "args": ["/path/to/eds-mono/packages/mcp-server/dist/server.js"],
118
+ "disabled": false
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ## When You Add New Components
127
+
128
+ | Step | Action |
129
+ |------|--------|
130
+ | 1. Add component | Create in `packages/empathy-ds/src/components/ui/` |
131
+ | 2. Add docs | Create `apps/docs/components/{name}.md` |
132
+ | 3. Categorize | Update `src/lib/categories.ts` |
133
+ | 4. Regenerate | Run `node scripts/generate-registry.js` |
134
+ | 5. Redeploy | Deploy docs to update registry.json |
135
+ | 6. (Optional) | Publish new MCP server version |
136
+
137
+ ---
138
+
139
+ ## Example AI Queries
140
+
141
+ - "What components does Empathy DS have?"
142
+ - "Show me the button variants"
143
+ - "Get the Card component props"
144
+ - "What form components are available?"
145
+ - "Show me the login form block"
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/server.js ADDED
@@ -0,0 +1,465 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/server.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z as z2 } from "zod";
7
+
8
+ // src/utils/schemas.ts
9
+ import { z } from "zod";
10
+ var ComponentMetadataSchema = z.object({
11
+ name: z.string(),
12
+ description: z.string(),
13
+ category: z.string().optional()
14
+ });
15
+ var ComponentPropSchema = z.object({
16
+ name: z.string(),
17
+ type: z.string(),
18
+ default: z.string().optional(),
19
+ description: z.string().optional()
20
+ });
21
+ var ComponentDetailSchema = z.object({
22
+ name: z.string(),
23
+ description: z.string(),
24
+ category: z.string().optional(),
25
+ import: z.string(),
26
+ props: z.array(ComponentPropSchema),
27
+ source: z.string().optional(),
28
+ indexSource: z.string().optional(),
29
+ examples: z.array(z.string()),
30
+ docsUrl: z.string()
31
+ });
32
+ var CategoryComponentsSchema = z.object({
33
+ category: z.string(),
34
+ components: z.array(ComponentMetadataSchema)
35
+ });
36
+
37
+ // src/lib/config.ts
38
+ var mcpConfig = {
39
+ projectName: "empathy-ds",
40
+ packageName: "@empathyds/vue",
41
+ baseUrl: "https://empathyds.com",
42
+ registryUrl: "https://empathyds.com/registry.json"
43
+ };
44
+
45
+ // src/utils/formatters.ts
46
+ function generateImportStatement(componentName) {
47
+ const pascalName = componentName.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
48
+ return `import { ${pascalName} } from '${mcpConfig.packageName}'`;
49
+ }
50
+
51
+ // src/utils/api.ts
52
+ var registryCache = null;
53
+ var registryCacheTime = 0;
54
+ var CACHE_TTL = 5 * 60 * 1e3;
55
+ async function getRegistry() {
56
+ if (registryCache && Date.now() - registryCacheTime < CACHE_TTL) {
57
+ return registryCache;
58
+ }
59
+ try {
60
+ const response = await fetch(mcpConfig.registryUrl);
61
+ if (!response.ok) {
62
+ throw new Error(`Failed to fetch registry: ${response.statusText}`);
63
+ }
64
+ registryCache = await response.json();
65
+ registryCacheTime = Date.now();
66
+ return registryCache;
67
+ } catch (error) {
68
+ console.error("Error fetching remote registry:", error);
69
+ return null;
70
+ }
71
+ }
72
+ async function listComponents() {
73
+ const registry = await getRegistry();
74
+ if (!registry) return [];
75
+ return registry.components.map((c) => ({
76
+ name: c.name,
77
+ description: c.description,
78
+ category: c.category || void 0
79
+ }));
80
+ }
81
+ async function getComponentDetail(componentName) {
82
+ const registry = await getRegistry();
83
+ if (!registry) return null;
84
+ const component = registry.components.find((c) => c.name === componentName);
85
+ if (!component) return null;
86
+ return {
87
+ name: component.name,
88
+ description: component.description,
89
+ category: component.category || void 0,
90
+ import: generateImportStatement(componentName),
91
+ props: component.props || [],
92
+ examples: component.examples || [],
93
+ docsUrl: component.docsUrl
94
+ };
95
+ }
96
+ async function searchComponents(query) {
97
+ const allComponents = await listComponents();
98
+ const lowerQuery = query.toLowerCase();
99
+ return allComponents.filter(
100
+ (c) => c.name.toLowerCase().includes(lowerQuery) || c.description.toLowerCase().includes(lowerQuery)
101
+ );
102
+ }
103
+ async function getComponentsByCategory(category) {
104
+ const allComponents = await listComponents();
105
+ return allComponents.filter((c) => c.category === category);
106
+ }
107
+ async function listBlocks() {
108
+ const registry = await getRegistry();
109
+ if (!registry) return [];
110
+ return registry.blocks.map((b) => ({
111
+ name: b.name,
112
+ description: b.description,
113
+ category: "Blocks"
114
+ }));
115
+ }
116
+ async function getBlockDetail(blockName) {
117
+ const registry = await getRegistry();
118
+ if (!registry) return null;
119
+ const block = registry.blocks.find((b) => b.name === blockName);
120
+ if (!block) return null;
121
+ return {
122
+ name: block.name,
123
+ description: block.description,
124
+ category: "Blocks",
125
+ import: `// Block: ${block.name} - see documentation`,
126
+ props: [],
127
+ examples: block.examples || [],
128
+ docsUrl: block.docsUrl
129
+ };
130
+ }
131
+
132
+ // src/server.ts
133
+ var server = new McpServer({
134
+ name: "empathy-ds-mcp",
135
+ version: "0.0.1"
136
+ });
137
+ server.tool(
138
+ "getAllComponents",
139
+ "Lists all 60+ Empathy DS Vue components with their names, descriptions, and categories. Use this to discover available components.",
140
+ {},
141
+ async () => {
142
+ try {
143
+ const components = await listComponents();
144
+ return {
145
+ content: [
146
+ {
147
+ type: "text",
148
+ text: JSON.stringify({
149
+ totalComponents: components.length,
150
+ packageName: mcpConfig.packageName,
151
+ docsUrl: mcpConfig.baseUrl,
152
+ components
153
+ }, null, 2)
154
+ }
155
+ ]
156
+ };
157
+ } catch (error) {
158
+ return {
159
+ content: [
160
+ {
161
+ type: "text",
162
+ text: `Error listing components: ${error instanceof Error ? error.message : "Unknown error"}`
163
+ }
164
+ ],
165
+ isError: true
166
+ };
167
+ }
168
+ }
169
+ );
170
+ server.tool(
171
+ "getComponent",
172
+ "Gets detailed information about a specific Empathy DS component including source code, props, variants, and usage examples. Use this when you need implementation details for a component.",
173
+ {
174
+ componentName: z2.string().describe(
175
+ "The kebab-case name of the component (e.g., 'button', 'alert-dialog', 'dropdown-menu')"
176
+ )
177
+ },
178
+ async ({ componentName }) => {
179
+ try {
180
+ const component = await getComponentDetail(componentName);
181
+ if (!component) {
182
+ return {
183
+ content: [
184
+ {
185
+ type: "text",
186
+ text: `Component "${componentName}" not found. Use getAllComponents to see available components.`
187
+ }
188
+ ],
189
+ isError: true
190
+ };
191
+ }
192
+ return {
193
+ content: [
194
+ {
195
+ type: "text",
196
+ text: JSON.stringify(component, null, 2)
197
+ }
198
+ ]
199
+ };
200
+ } catch (error) {
201
+ return {
202
+ content: [
203
+ {
204
+ type: "text",
205
+ text: `Error fetching component "${componentName}": ${error instanceof Error ? error.message : "Unknown error"}`
206
+ }
207
+ ],
208
+ isError: true
209
+ };
210
+ }
211
+ }
212
+ );
213
+ server.tool(
214
+ "getComponentsByCategory",
215
+ "Gets all Empathy DS components in a specific category. Categories: Forms, Layout, Feedback, Navigation, DataDisplay, Overlays, Actions.",
216
+ {
217
+ category: z2.enum([
218
+ "Forms",
219
+ "Layout",
220
+ "Feedback",
221
+ "Navigation",
222
+ "DataDisplay",
223
+ "Overlays",
224
+ "Actions"
225
+ ]).describe("The category to filter by")
226
+ },
227
+ async ({ category }) => {
228
+ try {
229
+ const components = await getComponentsByCategory(category);
230
+ return {
231
+ content: [
232
+ {
233
+ type: "text",
234
+ text: JSON.stringify({
235
+ category,
236
+ totalComponents: components.length,
237
+ components
238
+ }, null, 2)
239
+ }
240
+ ]
241
+ };
242
+ } catch (error) {
243
+ return {
244
+ content: [
245
+ {
246
+ type: "text",
247
+ text: `Error fetching category "${category}": ${error instanceof Error ? error.message : "Unknown error"}`
248
+ }
249
+ ],
250
+ isError: true
251
+ };
252
+ }
253
+ }
254
+ );
255
+ server.tool(
256
+ "searchComponents",
257
+ "Searches Empathy DS components by name or description. Use this to find components for a specific use case.",
258
+ {
259
+ query: z2.string().describe(
260
+ "The search query (e.g., 'button', 'form', 'dialog', 'navigation')"
261
+ )
262
+ },
263
+ async ({ query }) => {
264
+ try {
265
+ const results = await searchComponents(query);
266
+ return {
267
+ content: [
268
+ {
269
+ type: "text",
270
+ text: JSON.stringify({
271
+ query,
272
+ totalResults: results.length,
273
+ results,
274
+ tip: "Use getComponent with a component name to get full details including source code and examples."
275
+ }, null, 2)
276
+ }
277
+ ]
278
+ };
279
+ } catch (error) {
280
+ return {
281
+ content: [
282
+ {
283
+ type: "text",
284
+ text: `Error searching components: ${error instanceof Error ? error.message : "Unknown error"}`
285
+ }
286
+ ],
287
+ isError: true
288
+ };
289
+ }
290
+ }
291
+ );
292
+ server.tool(
293
+ "getCategories",
294
+ "Lists all component categories in Empathy DS with their component counts. Use this to understand how components are organized.",
295
+ {},
296
+ async () => {
297
+ try {
298
+ const components = await listComponents();
299
+ const categoriesMap = /* @__PURE__ */ new Map();
300
+ components.forEach((c) => {
301
+ if (c.category) {
302
+ categoriesMap.set(c.category, (categoriesMap.get(c.category) || 0) + 1);
303
+ }
304
+ });
305
+ const categories = Array.from(categoriesMap.entries()).map(([name, count]) => ({
306
+ name,
307
+ componentCount: count
308
+ }));
309
+ return {
310
+ content: [
311
+ {
312
+ type: "text",
313
+ text: JSON.stringify({
314
+ totalCategories: categories.length,
315
+ categories
316
+ }, null, 2)
317
+ }
318
+ ]
319
+ };
320
+ } catch (error) {
321
+ return {
322
+ content: [
323
+ {
324
+ type: "text",
325
+ text: `Error fetching categories: ${error instanceof Error ? error.message : "Unknown error"}`
326
+ }
327
+ ],
328
+ isError: true
329
+ };
330
+ }
331
+ }
332
+ );
333
+ server.tool(
334
+ "getBlocks",
335
+ "Lists all Empathy DS blocks (pre-built UI patterns like login forms, dashboards). Use blocks for complex, commonly-used UI patterns.",
336
+ {},
337
+ async () => {
338
+ try {
339
+ const blocks = await listBlocks();
340
+ return {
341
+ content: [
342
+ {
343
+ type: "text",
344
+ text: JSON.stringify({
345
+ totalBlocks: blocks.length,
346
+ docsUrl: `${mcpConfig.baseUrl}/blocks`,
347
+ blocks
348
+ }, null, 2)
349
+ }
350
+ ]
351
+ };
352
+ } catch (error) {
353
+ return {
354
+ content: [
355
+ {
356
+ type: "text",
357
+ text: `Error listing blocks: ${error instanceof Error ? error.message : "Unknown error"}`
358
+ }
359
+ ],
360
+ isError: true
361
+ };
362
+ }
363
+ }
364
+ );
365
+ server.tool(
366
+ "getBlock",
367
+ "Gets detailed information about a specific Empathy DS block including code examples. Use this for implementation details of a block pattern.",
368
+ {
369
+ blockName: z2.string().describe(
370
+ "The kebab-case name of the block (e.g., 'login-form')"
371
+ )
372
+ },
373
+ async ({ blockName }) => {
374
+ try {
375
+ const block = await getBlockDetail(blockName);
376
+ if (!block) {
377
+ return {
378
+ content: [
379
+ {
380
+ type: "text",
381
+ text: `Block "${blockName}" not found. Use getBlocks to see available blocks.`
382
+ }
383
+ ],
384
+ isError: true
385
+ };
386
+ }
387
+ return {
388
+ content: [
389
+ {
390
+ type: "text",
391
+ text: JSON.stringify(block, null, 2)
392
+ }
393
+ ]
394
+ };
395
+ } catch (error) {
396
+ return {
397
+ content: [
398
+ {
399
+ type: "text",
400
+ text: `Error fetching block "${blockName}": ${error instanceof Error ? error.message : "Unknown error"}`
401
+ }
402
+ ],
403
+ isError: true
404
+ };
405
+ }
406
+ }
407
+ );
408
+ server.tool(
409
+ "getUsageGuide",
410
+ "Returns the Empathy DS usage guide for AI assistants, including import patterns, styling rules, and best practices.",
411
+ {},
412
+ async () => {
413
+ const guide = `# Empathy DS Usage Guide
414
+
415
+ ## Import Pattern
416
+ Import components from \`${mcpConfig.packageName}\`:
417
+ \`\`\`ts
418
+ import { Button, Card, Input } from '${mcpConfig.packageName}'
419
+ \`\`\`
420
+
421
+ ## Styling
422
+ 1. Import the stylesheet in your main entry file:
423
+ \`\`\`ts
424
+ import '${mcpConfig.packageName}/style.css'
425
+ \`\`\`
426
+
427
+ 2. Use standard Tailwind utility classes for layout: \`flex\`, \`p-4\`, \`gap-2\`
428
+
429
+ 3. Use design tokens for colors - DO NOT hardcode colors:
430
+ - \`bg-primary\`, \`text-primary-foreground\`
431
+ - \`bg-secondary\`, \`text-secondary-foreground\`
432
+ - \`bg-muted\`, \`text-muted-foreground\`
433
+ - \`bg-destructive\`, \`text-destructive-foreground\`
434
+ - \`bg-accent\`, \`text-accent-foreground\`
435
+
436
+ ## Component Best Practices
437
+ 1. Always prefer Empathy DS components over building from scratch
438
+ 2. Use the \`variant\` prop to control visual style
439
+ 3. Use the \`size\` prop to control component size
440
+ 4. Check component documentation for all available props
441
+
442
+ ## Documentation
443
+ Full documentation: ${mcpConfig.baseUrl}
444
+ `;
445
+ return {
446
+ content: [
447
+ {
448
+ type: "text",
449
+ text: guide
450
+ }
451
+ ]
452
+ };
453
+ }
454
+ );
455
+ async function startServer() {
456
+ try {
457
+ const transport = new StdioServerTransport();
458
+ await server.connect(transport);
459
+ console.error("\u2705 Empathy DS MCP server started successfully");
460
+ } catch (error) {
461
+ console.error("\u274C Error starting MCP server:", error);
462
+ process.exit(1);
463
+ }
464
+ }
465
+ startServer();
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@empathyds/mcp-server",
3
+ "version": "0.0.1",
4
+ "description": "MCP server for Empathy DS - enables AI assistants to discover and use Empathy DS components",
5
+ "type": "module",
6
+ "main": "./dist/server.js",
7
+ "bin": {
8
+ "empathy-ds-mcp": "./dist/server.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup src/server.ts --format esm --dts --out-dir dist --clean",
15
+ "dev": "tsx watch src/server.ts",
16
+ "inspect": "npx -y @modelcontextprotocol/inspector node dist/server.js",
17
+ "start": "node dist/server.js"
18
+ },
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.13.1",
21
+ "zod": "^3.25.67"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.14.1",
25
+ "tsup": "^8.5.0",
26
+ "tsx": "^4.20.3",
27
+ "typescript": "^5.8.3"
28
+ },
29
+ "keywords": [
30
+ "mcp",
31
+ "model-context-protocol",
32
+ "empathy-ds",
33
+ "vue",
34
+ "ai",
35
+ "llm"
36
+ ],
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://bitbucket.org/underlings/eui-mono"
41
+ }
42
+ }