@vizejs/musea-mcp-server 0.81.0 → 0.83.0
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/dist/cli.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-Cp5GZpzj.mjs → src-BrRHhfXh.mjs} +46 -12
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as startServer, t as createMuseaServer } from "./src-
|
|
1
|
+
import { n as startServer, t as createMuseaServer } from "./src-BrRHhfXh.mjs";
|
|
2
2
|
export { createMuseaServer, createMuseaServer as default, startServer };
|
|
@@ -495,9 +495,36 @@ function tokenize(query) {
|
|
|
495
495
|
return Array.from(new Set(query.toLowerCase().split(/[\s/,_-]+/).map((term) => term.trim()).filter(Boolean)));
|
|
496
496
|
}
|
|
497
497
|
function toProjectPath(projectRoot, absolutePath) {
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
498
|
+
const root = realpathNearest(projectRoot);
|
|
499
|
+
const resolved = realpathNearest(absolutePath);
|
|
500
|
+
const relativePath = path.relative(root, resolved);
|
|
501
|
+
return isProjectPath(root, resolved) ? relativePath || "." : resolved;
|
|
502
|
+
}
|
|
503
|
+
function realpathNearest(targetPath) {
|
|
504
|
+
let current = path.resolve(targetPath);
|
|
505
|
+
const missingParts = [];
|
|
506
|
+
while (true) try {
|
|
507
|
+
const real = fs.realpathSync.native(current);
|
|
508
|
+
return missingParts.length > 0 ? path.join(real, ...missingParts.reverse()) : real;
|
|
509
|
+
} catch {
|
|
510
|
+
const parent = path.dirname(current);
|
|
511
|
+
if (parent === current) return path.resolve(targetPath);
|
|
512
|
+
missingParts.push(path.basename(current));
|
|
513
|
+
current = parent;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function isProjectPath(projectRoot, candidatePath) {
|
|
517
|
+
const root = realpathNearest(projectRoot);
|
|
518
|
+
const candidate = realpathNearest(candidatePath);
|
|
519
|
+
const relativePath = path.relative(root, candidate);
|
|
520
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
|
|
521
|
+
}
|
|
522
|
+
function resolveProjectPath(projectRoot, inputPath, label = "path") {
|
|
523
|
+
if (inputPath.includes("\0")) throw new McpError(ErrorCode.InvalidParams, `${label} contains an invalid character`);
|
|
524
|
+
const root = path.resolve(projectRoot);
|
|
525
|
+
const resolvedPath = path.isAbsolute(inputPath) ? path.resolve(inputPath) : path.resolve(root, inputPath);
|
|
526
|
+
if (!isProjectPath(root, resolvedPath)) throw new McpError(ErrorCode.InvalidParams, `${label} must stay inside the project root`);
|
|
527
|
+
return resolvedPath;
|
|
501
528
|
}
|
|
502
529
|
function buildResourceUris$1(relativePath, variantNames, hasComponentSource) {
|
|
503
530
|
const encodedPath = encodeURIComponent(relativePath);
|
|
@@ -628,7 +655,7 @@ async function resolveArtReference(ctx, args) {
|
|
|
628
655
|
const queryArg = typeof args?.query === "string" ? args.query : void 0;
|
|
629
656
|
const refArg = typeof args?.ref === "string" ? args.ref : void 0;
|
|
630
657
|
if (pathArg) {
|
|
631
|
-
const resolvedPath =
|
|
658
|
+
const resolvedPath = resolveProjectPath(ctx.projectRoot, pathArg, "path");
|
|
632
659
|
const normalizedResolvedPath = normalizePathLike(resolvedPath);
|
|
633
660
|
const normalizedRelativePath = normalizePathLike(path.relative(ctx.projectRoot, resolvedPath));
|
|
634
661
|
const directMatch = arts.find((info) => {
|
|
@@ -719,6 +746,13 @@ async function getComponentSourceDescriptor(ctx, resolved) {
|
|
|
719
746
|
exists: false,
|
|
720
747
|
error: "This art file does not declare a component source."
|
|
721
748
|
};
|
|
749
|
+
if (!isProjectPath(ctx.projectRoot, componentPath)) return {
|
|
750
|
+
reference: resolved.info.component,
|
|
751
|
+
absolutePath: componentPath,
|
|
752
|
+
path: componentPath,
|
|
753
|
+
exists: false,
|
|
754
|
+
error: "Component source is outside the project root."
|
|
755
|
+
};
|
|
722
756
|
try {
|
|
723
757
|
await fs.promises.access(componentPath, fs.constants.R_OK);
|
|
724
758
|
return {
|
|
@@ -977,7 +1011,7 @@ async function handleAnalyzeComponent(ctx, binding, args) {
|
|
|
977
1011
|
const directPath = args?.path;
|
|
978
1012
|
if (directPath?.endsWith(".vue") && !directPath.endsWith(".art.vue")) {
|
|
979
1013
|
if (!binding.analyzeSfc) throw new McpError(ErrorCode.InternalError, "analyzeSfc not available in native binding");
|
|
980
|
-
const absolutePath =
|
|
1014
|
+
const absolutePath = resolveProjectPath(ctx.projectRoot, directPath, "path");
|
|
981
1015
|
const source = await fs.promises.readFile(absolutePath, "utf-8");
|
|
982
1016
|
const analysis = binding.analyzeSfc(source, { filename: absolutePath });
|
|
983
1017
|
return { content: [{
|
|
@@ -1344,7 +1378,7 @@ async function handleGenerateVariants(ctx, binding, args) {
|
|
|
1344
1378
|
if (!componentRelPath) throw new McpError(ErrorCode.InvalidParams, "componentPath is required");
|
|
1345
1379
|
if (!binding.analyzeSfc) throw new McpError(ErrorCode.InternalError, "analyzeSfc not available in native binding");
|
|
1346
1380
|
if (!binding.generateVariants) throw new McpError(ErrorCode.InternalError, "generateVariants not available in native binding");
|
|
1347
|
-
const absolutePath =
|
|
1381
|
+
const absolutePath = resolveProjectPath(ctx.projectRoot, componentRelPath, "componentPath");
|
|
1348
1382
|
const source = await fs.promises.readFile(absolutePath, "utf-8");
|
|
1349
1383
|
const props = binding.analyzeSfc(source, { filename: absolutePath }).props.map((prop) => ({
|
|
1350
1384
|
name: prop.name,
|
|
@@ -1469,7 +1503,7 @@ async function handleGetTokens(ctx, args) {
|
|
|
1469
1503
|
const inputPath = args?.tokensPath;
|
|
1470
1504
|
const format = args?.format ?? "json";
|
|
1471
1505
|
let resolvedPath;
|
|
1472
|
-
if (inputPath) resolvedPath =
|
|
1506
|
+
if (inputPath) resolvedPath = resolveProjectPath(ctx.projectRoot, inputPath, "tokensPath");
|
|
1473
1507
|
else resolvedPath = await ctx.resolveTokensPath();
|
|
1474
1508
|
if (!resolvedPath) throw new McpError(ErrorCode.InvalidParams, "No tokens path provided and none auto-detected. Looked for: tokens/, design-tokens/, style-dictionary/ directories.");
|
|
1475
1509
|
const categories = await parseTokensFromPath(resolvedPath);
|
|
@@ -1494,7 +1528,7 @@ async function handleSearchTokens(ctx, args) {
|
|
|
1494
1528
|
const inputPath = args?.tokensPath;
|
|
1495
1529
|
const typeFilter = typeof args?.type === "string" ? args.type.toLowerCase() : void 0;
|
|
1496
1530
|
const limit = typeof args?.limit === "number" ? args.limit : 20;
|
|
1497
|
-
const resolvedPath = inputPath ?
|
|
1531
|
+
const resolvedPath = inputPath ? resolveProjectPath(ctx.projectRoot, inputPath, "tokensPath") : await ctx.resolveTokensPath();
|
|
1498
1532
|
if (!resolvedPath) throw new McpError(ErrorCode.InvalidParams, "No tokens path provided and none auto-detected. Looked for: tokens/, design-tokens/, style-dictionary/ directories.");
|
|
1499
1533
|
const flattened = flattenTokenCategories(await parseTokensFromPath(resolvedPath));
|
|
1500
1534
|
const normalizedQuery = query.toLowerCase();
|
|
@@ -1672,7 +1706,7 @@ async function readResource(ctx, uri) {
|
|
|
1672
1706
|
}
|
|
1673
1707
|
if (uri.startsWith("musea://source/")) {
|
|
1674
1708
|
const relativePath = decodeURIComponent(uri.slice(15));
|
|
1675
|
-
const absolutePath =
|
|
1709
|
+
const absolutePath = resolveProjectPath(ctx.projectRoot, relativePath, "source path");
|
|
1676
1710
|
return { contents: [{
|
|
1677
1711
|
uri,
|
|
1678
1712
|
mimeType: "text/plain",
|
|
@@ -1687,7 +1721,7 @@ async function readResource(ctx, uri) {
|
|
|
1687
1721
|
includeDocumentation: false
|
|
1688
1722
|
})).componentSource;
|
|
1689
1723
|
if (!componentSource?.path || componentSource.exists !== true) throw new McpError(ErrorCode.InvalidRequest, componentSource?.error ?? "Component source not available for this art file");
|
|
1690
|
-
const absolutePath =
|
|
1724
|
+
const absolutePath = resolveProjectPath(ctx.projectRoot, componentSource.path, "component source path");
|
|
1691
1725
|
return { contents: [{
|
|
1692
1726
|
uri,
|
|
1693
1727
|
mimeType: "text/plain",
|
|
@@ -1766,7 +1800,7 @@ function createMuseaServer(config) {
|
|
|
1766
1800
|
resources: {},
|
|
1767
1801
|
tools: {}
|
|
1768
1802
|
} });
|
|
1769
|
-
const projectRoot = config.projectRoot;
|
|
1803
|
+
const projectRoot = path.resolve(config.projectRoot);
|
|
1770
1804
|
const include = config.include ?? ["**/*.art.vue"];
|
|
1771
1805
|
const exclude = config.exclude ?? ["node_modules/**", "dist/**"];
|
|
1772
1806
|
const tokensPath = config.tokensPath;
|
|
@@ -1801,7 +1835,7 @@ function createMuseaServer(config) {
|
|
|
1801
1835
|
return artCache;
|
|
1802
1836
|
}
|
|
1803
1837
|
async function resolveTokensPath() {
|
|
1804
|
-
if (tokensPath) return
|
|
1838
|
+
if (tokensPath) return resolveProjectPath(projectRoot, tokensPath, "tokensPath");
|
|
1805
1839
|
for (const dir of [
|
|
1806
1840
|
"tokens",
|
|
1807
1841
|
"design-tokens",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizejs/musea-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.83.0",
|
|
4
4
|
"description": "MCP server for building Vue.js design systems - component analysis, documentation, variant generation, and design tokens",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
41
|
-
"@vizejs/native": "0.
|
|
41
|
+
"@vizejs/native": "0.83.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@tsdown/css": "0.22.0",
|