@vizejs/musea-mcp-server 0.0.1-alpha.11

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,50 @@
1
+ # @vizejs/musea-mcp-server
2
+
3
+ MCP (Model Context Protocol) server for Musea design system integration.
4
+
5
+ ## Features
6
+
7
+ - **AI Integration** - Connect Musea to AI assistants
8
+ - **Component Discovery** - Query available components
9
+ - **Design Token Access** - Retrieve design system tokens
10
+ - **Documentation** - Access component docs via MCP
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install -g @vizejs/musea-mcp-server
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### With Claude Desktop
21
+
22
+ Add to `claude_desktop_config.json`:
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "musea": {
28
+ "command": "musea-mcp-server",
29
+ "args": ["--project", "/path/to/project"]
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ### Standalone
36
+
37
+ ```bash
38
+ musea-mcp-server --project ./my-vue-app
39
+ ```
40
+
41
+ ## MCP Tools
42
+
43
+ - `list_components` - List all components
44
+ - `get_component` - Get component details
45
+ - `get_variants` - Get component variants
46
+ - `get_tokens` - Get design tokens
47
+
48
+ ## License
49
+
50
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import { startServer } from "./src-DMEJQckJ.js";
3
+
4
+ //#region src/cli.ts
5
+ const projectRoot = process.argv[2] || process.env.MUSEA_PROJECT_ROOT || process.cwd();
6
+ console.error(`[musea-mcp] Starting server for project: ${projectRoot}`);
7
+ startServer(projectRoot).catch((error) => {
8
+ console.error("[musea-mcp] Failed to start:", error);
9
+ process.exit(1);
10
+ });
11
+
12
+ //#endregion
@@ -0,0 +1,20 @@
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+
3
+ //#region src/index.d.ts
4
+ /**
5
+ * Create and configure the MCP server.
6
+ */
7
+
8
+ /**
9
+ * Create and configure the MCP server.
10
+ */
11
+ declare function createMuseaServer(config: {
12
+ projectRoot: string;
13
+ include?: string[];
14
+ exclude?: string[];
15
+ }): Server;
16
+ /**
17
+ * Start the MCP server with stdio transport.
18
+ */
19
+ declare function startServer(projectRoot: string): Promise<void>; //#endregion
20
+ export { createMuseaServer, createMuseaServer as default, startServer };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { createMuseaServer, src_default, startServer } from "./src-DMEJQckJ.js";
2
+
3
+ export { createMuseaServer, src_default as default, startServer };
@@ -0,0 +1,313 @@
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { CallToolRequestSchema, ErrorCode, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { createRequire } from "node:module";
7
+
8
+ //#region src/index.ts
9
+ let native = null;
10
+ function loadNative() {
11
+ if (native) return native;
12
+ const require = createRequire(import.meta.url);
13
+ try {
14
+ native = require("@vizejs/native");
15
+ return native;
16
+ } catch (e) {
17
+ throw new Error(`Failed to load @vizejs/native. Make sure it's installed: ${String(e)}`);
18
+ }
19
+ }
20
+ /**
21
+ * Create and configure the MCP server.
22
+ */
23
+ function createMuseaServer(config) {
24
+ const server = new Server({
25
+ name: "musea-mcp-server",
26
+ version: "0.0.1-alpha.11"
27
+ }, { capabilities: {
28
+ resources: {},
29
+ tools: {}
30
+ } });
31
+ const projectRoot = config.projectRoot;
32
+ const include = config.include ?? ["**/*.art.vue"];
33
+ const exclude = config.exclude ?? ["node_modules/**", "dist/**"];
34
+ let artCache = new Map();
35
+ let lastScanTime = 0;
36
+ async function scanArtFiles() {
37
+ const now = Date.now();
38
+ if (now - lastScanTime < 5e3 && artCache.size > 0) return artCache;
39
+ const binding = loadNative();
40
+ const files = await findArtFiles(projectRoot, include, exclude);
41
+ artCache = new Map();
42
+ for (const file of files) try {
43
+ const source = await fs.promises.readFile(file, "utf-8");
44
+ const parsed = binding.parseArt(source, { filename: file });
45
+ artCache.set(file, {
46
+ path: file,
47
+ title: parsed.metadata.title,
48
+ description: parsed.metadata.description,
49
+ category: parsed.metadata.category,
50
+ tags: parsed.metadata.tags,
51
+ variantCount: parsed.variants.length
52
+ });
53
+ } catch (e) {
54
+ console.error(`Failed to parse ${file}:`, e);
55
+ }
56
+ lastScanTime = now;
57
+ return artCache;
58
+ }
59
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
60
+ const arts = await scanArtFiles();
61
+ const resources = [];
62
+ for (const [filePath, info] of arts) {
63
+ const relativePath = path.relative(projectRoot, filePath);
64
+ resources.push({
65
+ uri: `musea://art/${encodeURIComponent(relativePath)}`,
66
+ name: info.title,
67
+ description: info.description || `${info.category || "Component"} with ${info.variantCount} variants`,
68
+ mimeType: "application/json"
69
+ });
70
+ }
71
+ return { resources };
72
+ });
73
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
74
+ const { uri } = request.params;
75
+ if (!uri.startsWith("musea://art/")) throw new McpError(ErrorCode.InvalidRequest, `Unknown resource URI: ${uri}`);
76
+ const relativePath = decodeURIComponent(uri.slice(12));
77
+ const absolutePath = path.resolve(projectRoot, relativePath);
78
+ try {
79
+ const source = await fs.promises.readFile(absolutePath, "utf-8");
80
+ const binding = loadNative();
81
+ const parsed = binding.parseArt(source, { filename: absolutePath });
82
+ return { contents: [{
83
+ uri,
84
+ mimeType: "application/json",
85
+ text: JSON.stringify({
86
+ path: relativePath,
87
+ metadata: parsed.metadata,
88
+ variants: parsed.variants.map((v) => ({
89
+ name: v.name,
90
+ template: v.template,
91
+ isDefault: v.is_default,
92
+ skipVrt: v.skip_vrt
93
+ })),
94
+ hasScriptSetup: parsed.has_script_setup,
95
+ hasScript: parsed.has_script,
96
+ styleCount: parsed.style_count
97
+ }, null, 2)
98
+ }] };
99
+ } catch (e) {
100
+ throw new McpError(ErrorCode.InternalError, `Failed to read art file: ${String(e)}`);
101
+ }
102
+ });
103
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
104
+ return { tools: [
105
+ {
106
+ name: "list_components",
107
+ description: "List all components (Art files) in the project with their metadata",
108
+ inputSchema: {
109
+ type: "object",
110
+ properties: {
111
+ category: {
112
+ type: "string",
113
+ description: "Filter by category"
114
+ },
115
+ tag: {
116
+ type: "string",
117
+ description: "Filter by tag"
118
+ }
119
+ }
120
+ }
121
+ },
122
+ {
123
+ name: "get_component",
124
+ description: "Get detailed information about a specific component",
125
+ inputSchema: {
126
+ type: "object",
127
+ properties: { path: {
128
+ type: "string",
129
+ description: "Path to the Art file (relative to project root)"
130
+ } },
131
+ required: ["path"]
132
+ }
133
+ },
134
+ {
135
+ name: "get_variant",
136
+ description: "Get a specific variant from a component",
137
+ inputSchema: {
138
+ type: "object",
139
+ properties: {
140
+ path: {
141
+ type: "string",
142
+ description: "Path to the Art file"
143
+ },
144
+ variant: {
145
+ type: "string",
146
+ description: "Name of the variant"
147
+ }
148
+ },
149
+ required: ["path", "variant"]
150
+ }
151
+ },
152
+ {
153
+ name: "generate_csf",
154
+ description: "Generate Storybook CSF 3.0 code from an Art file",
155
+ inputSchema: {
156
+ type: "object",
157
+ properties: { path: {
158
+ type: "string",
159
+ description: "Path to the Art file"
160
+ } },
161
+ required: ["path"]
162
+ }
163
+ },
164
+ {
165
+ name: "search_components",
166
+ description: "Search components by title, description, or tags",
167
+ inputSchema: {
168
+ type: "object",
169
+ properties: { query: {
170
+ type: "string",
171
+ description: "Search query"
172
+ } },
173
+ required: ["query"]
174
+ }
175
+ }
176
+ ] };
177
+ });
178
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
179
+ const { name, arguments: args } = request.params;
180
+ const binding = loadNative();
181
+ switch (name) {
182
+ case "list_components": {
183
+ const arts = await scanArtFiles();
184
+ let results = Array.from(arts.values());
185
+ if (args?.category) results = results.filter((a) => a.category?.toLowerCase() === args.category.toLowerCase());
186
+ if (args?.tag) results = results.filter((a) => a.tags.some((t) => t.toLowerCase() === args.tag.toLowerCase()));
187
+ return { content: [{
188
+ type: "text",
189
+ text: JSON.stringify(results.map((r) => ({
190
+ path: path.relative(projectRoot, r.path),
191
+ title: r.title,
192
+ description: r.description,
193
+ category: r.category,
194
+ tags: r.tags,
195
+ variantCount: r.variantCount
196
+ })), null, 2)
197
+ }] };
198
+ }
199
+ case "get_component": {
200
+ const artPath = args?.path;
201
+ if (!artPath) throw new McpError(ErrorCode.InvalidParams, "path is required");
202
+ const absolutePath = path.resolve(projectRoot, artPath);
203
+ const source = await fs.promises.readFile(absolutePath, "utf-8");
204
+ const parsed = binding.parseArt(source, { filename: absolutePath });
205
+ return { content: [{
206
+ type: "text",
207
+ text: JSON.stringify({
208
+ metadata: parsed.metadata,
209
+ variants: parsed.variants.map((v) => ({
210
+ name: v.name,
211
+ template: v.template,
212
+ isDefault: v.is_default,
213
+ skipVrt: v.skip_vrt
214
+ })),
215
+ hasScriptSetup: parsed.has_script_setup,
216
+ hasScript: parsed.has_script,
217
+ styleCount: parsed.style_count
218
+ }, null, 2)
219
+ }] };
220
+ }
221
+ case "get_variant": {
222
+ const artPath = args?.path;
223
+ const variantName = args?.variant;
224
+ if (!artPath || !variantName) throw new McpError(ErrorCode.InvalidParams, "path and variant are required");
225
+ const absolutePath = path.resolve(projectRoot, artPath);
226
+ const source = await fs.promises.readFile(absolutePath, "utf-8");
227
+ const parsed = binding.parseArt(source, { filename: absolutePath });
228
+ const variant = parsed.variants.find((v) => v.name.toLowerCase() === variantName.toLowerCase());
229
+ if (!variant) throw new McpError(ErrorCode.InvalidParams, `Variant "${variantName}" not found`);
230
+ return { content: [{
231
+ type: "text",
232
+ text: JSON.stringify({
233
+ name: variant.name,
234
+ template: variant.template,
235
+ isDefault: variant.is_default,
236
+ skipVrt: variant.skip_vrt
237
+ }, null, 2)
238
+ }] };
239
+ }
240
+ case "generate_csf": {
241
+ const artPath = args?.path;
242
+ if (!artPath) throw new McpError(ErrorCode.InvalidParams, "path is required");
243
+ const absolutePath = path.resolve(projectRoot, artPath);
244
+ const source = await fs.promises.readFile(absolutePath, "utf-8");
245
+ const csf = binding.artToCsf(source, { filename: absolutePath });
246
+ return { content: [{
247
+ type: "text",
248
+ text: csf.code
249
+ }] };
250
+ }
251
+ case "search_components": {
252
+ const query = (args?.query)?.toLowerCase();
253
+ if (!query) throw new McpError(ErrorCode.InvalidParams, "query is required");
254
+ const arts = await scanArtFiles();
255
+ const results = Array.from(arts.values()).filter((a) => a.title.toLowerCase().includes(query) || a.description?.toLowerCase().includes(query) || a.tags.some((t) => t.toLowerCase().includes(query)));
256
+ return { content: [{
257
+ type: "text",
258
+ text: JSON.stringify(results.map((r) => ({
259
+ path: path.relative(projectRoot, r.path),
260
+ title: r.title,
261
+ description: r.description,
262
+ category: r.category,
263
+ tags: r.tags
264
+ })), null, 2)
265
+ }] };
266
+ }
267
+ default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
268
+ }
269
+ });
270
+ return server;
271
+ }
272
+ async function findArtFiles(root, include, exclude) {
273
+ const files = [];
274
+ async function scan(dir) {
275
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
276
+ for (const entry of entries) {
277
+ const fullPath = path.join(dir, entry.name);
278
+ const relative = path.relative(root, fullPath);
279
+ let excluded = false;
280
+ for (const pattern of exclude) if (matchGlob(relative, pattern) || matchGlob(entry.name, pattern)) {
281
+ excluded = true;
282
+ break;
283
+ }
284
+ if (excluded) continue;
285
+ if (entry.isDirectory()) await scan(fullPath);
286
+ else if (entry.isFile() && entry.name.endsWith(".art.vue")) {
287
+ for (const pattern of include) if (matchGlob(relative, pattern)) {
288
+ files.push(fullPath);
289
+ break;
290
+ }
291
+ }
292
+ }
293
+ }
294
+ await scan(root);
295
+ return files;
296
+ }
297
+ function matchGlob(filepath, pattern) {
298
+ const regex = pattern.replace(/\*\*/g, "{{DOUBLE_STAR}}").replace(/\*/g, "[^/]*").replace(/{{DOUBLE_STAR}}/g, ".*").replace(/\./g, "\\.");
299
+ return new RegExp(`^${regex}$`).test(filepath);
300
+ }
301
+ /**
302
+ * Start the MCP server with stdio transport.
303
+ */
304
+ async function startServer(projectRoot) {
305
+ const server = createMuseaServer({ projectRoot });
306
+ const transport = new StdioServerTransport();
307
+ await server.connect(transport);
308
+ console.error("[musea-mcp] Server started");
309
+ }
310
+ var src_default = createMuseaServer;
311
+
312
+ //#endregion
313
+ export { createMuseaServer, src_default, startServer };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@vizejs/musea-mcp-server",
3
+ "version": "0.0.1-alpha.11",
4
+ "description": "MCP server for Musea - AI-accessible component gallery",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "musea-mcp": "./dist/cli.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "keywords": [
21
+ "mcp",
22
+ "model-context-protocol",
23
+ "vue",
24
+ "component-gallery",
25
+ "musea",
26
+ "ai"
27
+ ],
28
+ "author": "ubugeeei",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/ubugeeei/vize.git",
33
+ "directory": "npm/musea-mcp-server"
34
+ },
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^0.5.0",
37
+ "@vizejs/native": "0.0.1-alpha.33"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.14.0",
41
+ "tsdown": "^0.9.0",
42
+ "typescript": "^5.7.0"
43
+ },
44
+ "publishConfig": {
45
+ "provenance": true,
46
+ "access": "public"
47
+ },
48
+ "scripts": {
49
+ "build": "tsdown",
50
+ "dev": "tsdown --watch",
51
+ "start": "node dist/cli.js",
52
+ "lint": "oxlint --deny-warnings --type-aware --tsconfig tsconfig.json",
53
+ "lint:fix": "oxlint --type-aware --tsconfig tsconfig.json --fix",
54
+ "fmt": "oxfmt --write src",
55
+ "fmt:check": "oxfmt src"
56
+ }
57
+ }