@fragments-sdk/mcp 0.7.0 → 0.7.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/dist/bin.js +3 -3
- package/dist/{chunk-LZW5KUXV.js → chunk-HGGAXLRO.js} +505 -297
- package/dist/chunk-HGGAXLRO.js.map +1 -0
- package/dist/{chunk-7GGI7JJE.js → chunk-VV2PJ75X.js} +2 -2
- package/dist/chunk-YSRGQDEB.js +93 -0
- package/dist/chunk-YSRGQDEB.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/rules-CKBRD3UL.js +8 -0
- package/dist/server.js +2 -2
- package/package.json +3 -3
- package/dist/chunk-3IHXJEMM.js +0 -47
- package/dist/chunk-3IHXJEMM.js.map +0 -1
- package/dist/chunk-LZW5KUXV.js.map +0 -1
- package/dist/rules-W47BYPZV.js +0 -8
- /package/dist/{chunk-7GGI7JJE.js.map → chunk-VV2PJ75X.js.map} +0 -0
- /package/dist/{rules-W47BYPZV.js.map → rules-CKBRD3UL.js.map} +0 -0
|
@@ -4,12 +4,13 @@ import {
|
|
|
4
4
|
} from "./chunk-4SVS3AA3.js";
|
|
5
5
|
import {
|
|
6
6
|
componentNames,
|
|
7
|
+
findComponent,
|
|
7
8
|
findComponentByName,
|
|
8
9
|
getGuidanceWhen,
|
|
9
10
|
getGuidanceWhenNot,
|
|
10
11
|
listBlocks,
|
|
11
12
|
listComponents
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YSRGQDEB.js";
|
|
13
14
|
|
|
14
15
|
// src/server.ts
|
|
15
16
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -19,7 +20,7 @@ import {
|
|
|
19
20
|
ListToolsRequestSchema
|
|
20
21
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
21
22
|
import { existsSync as existsSync9 } from "fs";
|
|
22
|
-
import { readFileSync as
|
|
23
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
23
24
|
import { join as join8 } from "path";
|
|
24
25
|
import { fileURLToPath } from "url";
|
|
25
26
|
|
|
@@ -653,17 +654,20 @@ function buildLocalSearchData(data, indexes) {
|
|
|
653
654
|
}
|
|
654
655
|
async function buildImportStatements(components, resolvePackageName) {
|
|
655
656
|
const grouped = /* @__PURE__ */ new Map();
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
657
|
+
const uniqueComponents = [...new Set(components.filter(Boolean))];
|
|
658
|
+
const resolvedPackages = await Promise.all(
|
|
659
|
+
uniqueComponents.map(async (component) => ({
|
|
660
|
+
component,
|
|
661
|
+
packageName: await resolvePackageName(component)
|
|
662
|
+
}))
|
|
663
|
+
);
|
|
664
|
+
for (const { component, packageName } of resolvedPackages) {
|
|
659
665
|
const existing = grouped.get(packageName);
|
|
660
666
|
if (!existing) {
|
|
661
667
|
grouped.set(packageName, [component]);
|
|
662
668
|
continue;
|
|
663
669
|
}
|
|
664
|
-
|
|
665
|
-
existing.push(component);
|
|
666
|
-
}
|
|
670
|
+
existing.push(component);
|
|
667
671
|
}
|
|
668
672
|
return Array.from(grouped.entries()).map(
|
|
669
673
|
([packageName, componentNames2]) => `import { ${componentNames2.join(", ")} } from '${packageName}';`
|
|
@@ -687,6 +691,56 @@ function limitTokensPerCategory(categories, limit) {
|
|
|
687
691
|
return { categories: limited, total };
|
|
688
692
|
}
|
|
689
693
|
|
|
694
|
+
// src/search-helpers.ts
|
|
695
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
696
|
+
"a",
|
|
697
|
+
"an",
|
|
698
|
+
"and",
|
|
699
|
+
"bar",
|
|
700
|
+
"build",
|
|
701
|
+
"button",
|
|
702
|
+
"for",
|
|
703
|
+
"form",
|
|
704
|
+
"i",
|
|
705
|
+
"login",
|
|
706
|
+
"me",
|
|
707
|
+
"need",
|
|
708
|
+
"of",
|
|
709
|
+
"or",
|
|
710
|
+
"the",
|
|
711
|
+
"to",
|
|
712
|
+
"use",
|
|
713
|
+
"with"
|
|
714
|
+
]);
|
|
715
|
+
function normalizeTerms(value) {
|
|
716
|
+
return value.toLowerCase().split(/[^a-z0-9]+/g).filter((term) => term.length > 1 && !STOP_WORDS.has(term));
|
|
717
|
+
}
|
|
718
|
+
function hasDirectQueryOverlap(query, component) {
|
|
719
|
+
const queryTerms = normalizeTerms(query);
|
|
720
|
+
if (queryTerms.length === 0) return true;
|
|
721
|
+
const haystack = new Set(
|
|
722
|
+
normalizeTerms(
|
|
723
|
+
[
|
|
724
|
+
component.name,
|
|
725
|
+
component.description,
|
|
726
|
+
component.category,
|
|
727
|
+
component.tags.join(" "),
|
|
728
|
+
getGuidanceWhen(component).join(" "),
|
|
729
|
+
getGuidanceWhenNot(component).join(" "),
|
|
730
|
+
component.propsSummary.join(" ")
|
|
731
|
+
].join(" ")
|
|
732
|
+
)
|
|
733
|
+
);
|
|
734
|
+
return queryTerms.some((term) => haystack.has(term));
|
|
735
|
+
}
|
|
736
|
+
function getRankingBonus(component) {
|
|
737
|
+
let bonus = 0;
|
|
738
|
+
if (component.isCanonical) bonus += 30;
|
|
739
|
+
if (component.tier === "core") bonus += 20;
|
|
740
|
+
if (component.status === "stable") bonus += 5;
|
|
741
|
+
return bonus;
|
|
742
|
+
}
|
|
743
|
+
|
|
690
744
|
// src/tools/discover.ts
|
|
691
745
|
function renderContextMarkdown(args) {
|
|
692
746
|
const lines = ["# Design System Context", ""];
|
|
@@ -769,11 +823,13 @@ var discoverHandler = async (args, ctx) => {
|
|
|
769
823
|
const compact = args?.compact ?? false;
|
|
770
824
|
const includeCode = args?.includeCode ?? false;
|
|
771
825
|
const includeRelations = args?.includeRelations ?? false;
|
|
826
|
+
const depth = args?.depth ?? "quick";
|
|
772
827
|
const suggestLimit = typeof args?.limit === "number" ? Math.min(Math.max(args.limit, 1), 25) : 10;
|
|
773
828
|
const listLimit = typeof args?.limit === "number" ? Math.min(Math.max(args.limit, 1), 25) : void 0;
|
|
774
829
|
const verbosity = args?.verbosity ?? (compact ? "compact" : "standard");
|
|
775
|
-
|
|
776
|
-
|
|
830
|
+
const allSnapshotComponents = listComponents(data.snapshot);
|
|
831
|
+
if (!useCase && !componentForAlts && (args?.format || includeCode || includeRelations)) {
|
|
832
|
+
let components2 = allSnapshotComponents;
|
|
777
833
|
const allBlocks = listBlocks(data.snapshot);
|
|
778
834
|
if (category) {
|
|
779
835
|
components2 = components2.filter(
|
|
@@ -781,12 +837,16 @@ var discoverHandler = async (args, ctx) => {
|
|
|
781
837
|
);
|
|
782
838
|
}
|
|
783
839
|
if (search2) {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
840
|
+
const scored = keywordScoreComponents(
|
|
841
|
+
search2,
|
|
842
|
+
components2,
|
|
843
|
+
ctx.indexes.componentIndex ?? void 0
|
|
844
|
+
);
|
|
845
|
+
const allowedNames = new Set(components2.map((component) => component.name));
|
|
846
|
+
const sortedNames = scored.filter((result) => allowedNames.has(result.name)).map((result) => result.name.toLowerCase());
|
|
847
|
+
components2 = components2.filter((component) => sortedNames.includes(component.name.toLowerCase())).sort(
|
|
848
|
+
(a, b) => sortedNames.indexOf(a.name.toLowerCase()) - sortedNames.indexOf(b.name.toLowerCase())
|
|
849
|
+
);
|
|
790
850
|
}
|
|
791
851
|
if (status) {
|
|
792
852
|
components2 = components2.filter((component) => component.status === status);
|
|
@@ -838,6 +898,14 @@ var discoverHandler = async (args, ctx) => {
|
|
|
838
898
|
"component",
|
|
839
899
|
ctx.config.searchApiKey
|
|
840
900
|
);
|
|
901
|
+
const filteredSearchResults = searchResults.filter((result) => {
|
|
902
|
+
const component = componentsByName.get(result.name.toLowerCase());
|
|
903
|
+
if (!component) return false;
|
|
904
|
+
if (category && !categoryMatches(component.category, category)) return false;
|
|
905
|
+
if (status && component.status !== status) return false;
|
|
906
|
+
result.score += getRankingBonus(component);
|
|
907
|
+
return true;
|
|
908
|
+
});
|
|
841
909
|
const blockMatches = keywordScoreBlocks(
|
|
842
910
|
fullQuery,
|
|
843
911
|
allBlocks,
|
|
@@ -848,10 +916,10 @@ var discoverHandler = async (args, ctx) => {
|
|
|
848
916
|
(match) => allBlocks.find((block) => block.name.toLowerCase() === match.name.toLowerCase())
|
|
849
917
|
).filter(Boolean);
|
|
850
918
|
const blockFreq = buildBlockComponentFrequency(matchedBlocks);
|
|
851
|
-
boostByBlockFrequency(
|
|
919
|
+
boostByBlockFrequency(filteredSearchResults, blockFreq);
|
|
852
920
|
}
|
|
853
|
-
const maxScore =
|
|
854
|
-
const scored =
|
|
921
|
+
const maxScore = filteredSearchResults.length > 0 ? filteredSearchResults[0].score : 0;
|
|
922
|
+
const scored = filteredSearchResults.map((result) => {
|
|
855
923
|
const component = componentsByName.get(result.name.toLowerCase());
|
|
856
924
|
if (!component) return null;
|
|
857
925
|
return {
|
|
@@ -864,6 +932,11 @@ var discoverHandler = async (args, ctx) => {
|
|
|
864
932
|
when: getGuidanceWhen(component).slice(0, 3),
|
|
865
933
|
whenNot: getGuidanceWhenNot(component).slice(0, 2)
|
|
866
934
|
},
|
|
935
|
+
publicRef: component.publicRef,
|
|
936
|
+
componentKey: component.id,
|
|
937
|
+
tier: component.tier,
|
|
938
|
+
isCanonical: component.isCanonical ?? false,
|
|
939
|
+
sourcePath: component.sourcePath,
|
|
867
940
|
exampleCount: component.examples.length,
|
|
868
941
|
status: component.status
|
|
869
942
|
};
|
|
@@ -902,7 +975,10 @@ var discoverHandler = async (args, ctx) => {
|
|
|
902
975
|
const isStyleQuery = styleKeywords.some(
|
|
903
976
|
(keyword) => useCaseLower.includes(keyword)
|
|
904
977
|
);
|
|
905
|
-
const noMatch = suggestions.length === 0
|
|
978
|
+
const noMatch = suggestions.length === 0 || !suggestions.some((item) => {
|
|
979
|
+
const component = componentsByName.get(item.component.toLowerCase());
|
|
980
|
+
return component ? hasDirectQueryOverlap(useCase, component) : false;
|
|
981
|
+
});
|
|
906
982
|
const belowThreshold = !noMatch && maxScore > 1 && !meetsMinimumThreshold(maxScore);
|
|
907
983
|
const weakMatch = !noMatch && (belowThreshold || suggestions.every((item) => item.confidence === "low"));
|
|
908
984
|
let recommendation;
|
|
@@ -922,6 +998,80 @@ var discoverHandler = async (args, ctx) => {
|
|
|
922
998
|
(match) => allBlocks.find((block) => block.name.toLowerCase() === match.name.toLowerCase())
|
|
923
999
|
).filter(Boolean).slice(0, 3).map((block) => block.name);
|
|
924
1000
|
const blockHint = blockNames.length > 0 ? `Related blocks: ${blockNames.join(", ")}. Use ${ctx.toolNames.blocks}(search: "${useCase}") for ready-to-use patterns.` : void 0;
|
|
1001
|
+
let fullBlocks;
|
|
1002
|
+
let fullTokens;
|
|
1003
|
+
let fullImports;
|
|
1004
|
+
if (depth === "full" && !noMatch) {
|
|
1005
|
+
const tokenData = ctx.data.tokens;
|
|
1006
|
+
const [blockSearchResults, tokenSearchResults] = await Promise.all([
|
|
1007
|
+
hybridSearch(fullQuery, localData, 5, "block", ctx.config.searchApiKey),
|
|
1008
|
+
tokenData ? hybridSearch(fullQuery, localData, 10, "token", ctx.config.searchApiKey) : Promise.resolve([])
|
|
1009
|
+
]);
|
|
1010
|
+
const topBlockScore = blockSearchResults.length > 0 ? blockSearchResults[0].score : 0;
|
|
1011
|
+
const relevantBlockResults = blockSearchResults.filter(
|
|
1012
|
+
(result) => result.score >= topBlockScore * 0.3
|
|
1013
|
+
);
|
|
1014
|
+
if (relevantBlockResults.length > 0) {
|
|
1015
|
+
fullBlocks = (await Promise.all(
|
|
1016
|
+
relevantBlockResults.slice(0, 5).map(async (result) => {
|
|
1017
|
+
const block = allBlocks.find(
|
|
1018
|
+
(entry) => entry.name.toLowerCase() === result.name.toLowerCase()
|
|
1019
|
+
);
|
|
1020
|
+
if (!block) return null;
|
|
1021
|
+
const imports = await buildImportStatements(
|
|
1022
|
+
block.components,
|
|
1023
|
+
async (componentName) => ctx.resolvePackageName(componentName)
|
|
1024
|
+
);
|
|
1025
|
+
const codeLines = block.code.split("\n");
|
|
1026
|
+
const code = codeLines.length > 30 ? `${codeLines.slice(0, 20).join("\n")}
|
|
1027
|
+
// ... truncated (${codeLines.length} lines total)` : block.code;
|
|
1028
|
+
return {
|
|
1029
|
+
name: block.name,
|
|
1030
|
+
description: block.description,
|
|
1031
|
+
components: block.components,
|
|
1032
|
+
code,
|
|
1033
|
+
imports
|
|
1034
|
+
};
|
|
1035
|
+
})
|
|
1036
|
+
)).filter(Boolean);
|
|
1037
|
+
}
|
|
1038
|
+
if (tokenSearchResults.length > 0 && tokenData) {
|
|
1039
|
+
fullTokens = {};
|
|
1040
|
+
const tokensByName = /* @__PURE__ */ new Map();
|
|
1041
|
+
for (const [cat, tokens] of Object.entries(tokenData.categories)) {
|
|
1042
|
+
for (const token of tokens) {
|
|
1043
|
+
tokensByName.set(token.name, cat);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
for (const result of tokenSearchResults) {
|
|
1047
|
+
const cat = tokensByName.get(result.name);
|
|
1048
|
+
if (cat) {
|
|
1049
|
+
if (!fullTokens[cat]) fullTokens[cat] = [];
|
|
1050
|
+
fullTokens[cat].push(result.name);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
if (Object.keys(fullTokens).length === 0) fullTokens = void 0;
|
|
1054
|
+
}
|
|
1055
|
+
if (!fullTokens && tokenData) {
|
|
1056
|
+
const categories = extractTokenCategories(fullQuery);
|
|
1057
|
+
fullTokens = {};
|
|
1058
|
+
for (const cat of categories) {
|
|
1059
|
+
const tokens = tokenData.categories[cat];
|
|
1060
|
+
if (tokens && tokens.length > 0) {
|
|
1061
|
+
fullTokens[cat] = tokens.slice(0, 5).map((token) => token.name);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
if (Object.keys(fullTokens).length === 0) fullTokens = void 0;
|
|
1065
|
+
}
|
|
1066
|
+
if (suggestions.length > 0) {
|
|
1067
|
+
fullImports = {};
|
|
1068
|
+
for (const item of suggestions) {
|
|
1069
|
+
if (!item) continue;
|
|
1070
|
+
const pkgName = ctx.resolvePackageName(item.component);
|
|
1071
|
+
fullImports[item.component] = `import { ${item.component} } from '${pkgName}';`;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
925
1075
|
const suggestResponse = verbosity === "compact" ? {
|
|
926
1076
|
useCase,
|
|
927
1077
|
suggestions: suggestions.map((item) => ({
|
|
@@ -933,14 +1083,19 @@ var discoverHandler = async (args, ctx) => {
|
|
|
933
1083
|
} : {
|
|
934
1084
|
useCase,
|
|
935
1085
|
context: context || void 0,
|
|
936
|
-
suggestions
|
|
1086
|
+
suggestions: depth === "full" ? suggestions.map((item) => ({
|
|
1087
|
+
...item,
|
|
1088
|
+
import: fullImports?.[item.component]
|
|
1089
|
+
})) : suggestions,
|
|
937
1090
|
noMatch,
|
|
938
1091
|
weakMatch,
|
|
939
1092
|
recommendation,
|
|
940
1093
|
compositionHint,
|
|
941
1094
|
...tokenHint && { tokenHint },
|
|
942
1095
|
...blockHint && { blockHint },
|
|
943
|
-
nextStep
|
|
1096
|
+
nextStep,
|
|
1097
|
+
...fullBlocks && fullBlocks.length > 0 && { blocks: fullBlocks },
|
|
1098
|
+
...fullTokens && { tokens: fullTokens }
|
|
944
1099
|
};
|
|
945
1100
|
return {
|
|
946
1101
|
content: [
|
|
@@ -952,33 +1107,39 @@ var discoverHandler = async (args, ctx) => {
|
|
|
952
1107
|
};
|
|
953
1108
|
}
|
|
954
1109
|
if (componentForAlts) {
|
|
955
|
-
const component =
|
|
1110
|
+
const component = findComponent(data.snapshot, componentForAlts);
|
|
956
1111
|
if (!component) {
|
|
957
1112
|
const closest = findClosestMatch(
|
|
958
1113
|
componentForAlts,
|
|
959
1114
|
componentNames(data.snapshot)
|
|
960
1115
|
);
|
|
961
1116
|
const suggestion = closest ? ` Did you mean "${closest}"?` : "";
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1117
|
+
return {
|
|
1118
|
+
content: [
|
|
1119
|
+
{
|
|
1120
|
+
type: "text",
|
|
1121
|
+
text: JSON.stringify({
|
|
1122
|
+
error: `Component "${componentForAlts}" not found.${suggestion} Use ${ctx.toolNames.discover} to see available components.`
|
|
1123
|
+
})
|
|
1124
|
+
}
|
|
1125
|
+
],
|
|
1126
|
+
isError: true
|
|
1127
|
+
};
|
|
965
1128
|
}
|
|
966
1129
|
const relations = component.relations;
|
|
967
|
-
const referencedBy =
|
|
968
|
-
|
|
969
|
-
(
|
|
970
|
-
)
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
const sameCategory = listComponents(data.snapshot).filter(
|
|
981
|
-
(entry) => entry.category === component.category && entry.name.toLowerCase() !== componentForAlts.toLowerCase()
|
|
1130
|
+
const referencedBy = allSnapshotComponents.map((entry) => {
|
|
1131
|
+
const relation = entry.relations.find(
|
|
1132
|
+
(candidate) => candidate.componentName.toLowerCase() === component.name.toLowerCase()
|
|
1133
|
+
);
|
|
1134
|
+
if (!relation) return null;
|
|
1135
|
+
return {
|
|
1136
|
+
component: entry.name,
|
|
1137
|
+
relationship: relation.relationship,
|
|
1138
|
+
note: relation.note
|
|
1139
|
+
};
|
|
1140
|
+
}).filter(Boolean);
|
|
1141
|
+
const sameCategory = allSnapshotComponents.filter(
|
|
1142
|
+
(entry) => entry.category === component.category && entry.name.toLowerCase() !== component.name.toLowerCase()
|
|
982
1143
|
).map((entry) => ({
|
|
983
1144
|
component: entry.name,
|
|
984
1145
|
description: entry.description
|
|
@@ -1001,25 +1162,44 @@ var discoverHandler = async (args, ctx) => {
|
|
|
1001
1162
|
]
|
|
1002
1163
|
};
|
|
1003
1164
|
}
|
|
1004
|
-
|
|
1165
|
+
let filteredComponents = allSnapshotComponents.filter((component) => {
|
|
1005
1166
|
if (category && !categoryMatches(component.category, category)) return false;
|
|
1006
1167
|
if (status && (component.status ?? "stable") !== status) return false;
|
|
1007
|
-
if (search2) {
|
|
1008
|
-
const nameMatch = component.name.toLowerCase().includes(search2);
|
|
1009
|
-
const descMatch = component.description.toLowerCase().includes(search2);
|
|
1010
|
-
const tagMatch = component.tags.some(
|
|
1011
|
-
(tag) => tag.toLowerCase().includes(search2)
|
|
1012
|
-
);
|
|
1013
|
-
if (!nameMatch && !descMatch && !tagMatch) return false;
|
|
1014
|
-
}
|
|
1015
1168
|
return true;
|
|
1016
1169
|
});
|
|
1170
|
+
if (search2) {
|
|
1171
|
+
const scored = keywordScoreComponents(
|
|
1172
|
+
search2,
|
|
1173
|
+
filteredComponents,
|
|
1174
|
+
ctx.indexes.componentIndex ?? void 0
|
|
1175
|
+
).map((result) => {
|
|
1176
|
+
const component = filteredComponents.find(
|
|
1177
|
+
(entry) => entry.name.toLowerCase() === result.name.toLowerCase()
|
|
1178
|
+
);
|
|
1179
|
+
if (!component) return null;
|
|
1180
|
+
return {
|
|
1181
|
+
component,
|
|
1182
|
+
score: result.score + getRankingBonus(component)
|
|
1183
|
+
};
|
|
1184
|
+
}).filter(Boolean);
|
|
1185
|
+
filteredComponents = scored.sort((a, b) => b.score - a.score).map((entry) => entry.component);
|
|
1186
|
+
} else {
|
|
1187
|
+
filteredComponents = filteredComponents.sort((a, b) => {
|
|
1188
|
+
const bonusDiff = getRankingBonus(b) - getRankingBonus(a);
|
|
1189
|
+
if (bonusDiff !== 0) return bonusDiff;
|
|
1190
|
+
return a.name.localeCompare(b.name);
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1017
1193
|
const limitedComponents = listLimit === void 0 ? filteredComponents : filteredComponents.slice(0, listLimit);
|
|
1018
1194
|
const components = limitedComponents.map((component) => {
|
|
1019
1195
|
if (verbosity === "compact") {
|
|
1020
1196
|
return {
|
|
1021
1197
|
name: component.name,
|
|
1022
1198
|
category: component.category,
|
|
1199
|
+
publicRef: component.publicRef,
|
|
1200
|
+
componentKey: component.id,
|
|
1201
|
+
tier: component.tier,
|
|
1202
|
+
isCanonical: component.isCanonical ?? false,
|
|
1023
1203
|
...component.propsSummary.length > 0 && {
|
|
1024
1204
|
propsSummary: component.propsSummary
|
|
1025
1205
|
}
|
|
@@ -1030,6 +1210,11 @@ var discoverHandler = async (args, ctx) => {
|
|
|
1030
1210
|
category: component.category,
|
|
1031
1211
|
description: component.description,
|
|
1032
1212
|
status: component.status ?? "stable",
|
|
1213
|
+
publicRef: component.publicRef,
|
|
1214
|
+
componentKey: component.id,
|
|
1215
|
+
tier: component.tier,
|
|
1216
|
+
isCanonical: component.isCanonical ?? false,
|
|
1217
|
+
sourcePath: component.sourcePath,
|
|
1033
1218
|
exampleCount: component.examples.length,
|
|
1034
1219
|
tags: component.tags,
|
|
1035
1220
|
...(includeCode || verbosity === "full") && component.examples[0]?.code ? {
|
|
@@ -1054,7 +1239,8 @@ var discoverHandler = async (args, ctx) => {
|
|
|
1054
1239
|
};
|
|
1055
1240
|
|
|
1056
1241
|
// src/tools/inspect.ts
|
|
1057
|
-
import {
|
|
1242
|
+
import { promises as fs } from "fs";
|
|
1243
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1058
1244
|
import { join as join2 } from "path";
|
|
1059
1245
|
|
|
1060
1246
|
// src/utils.ts
|
|
@@ -1090,13 +1276,13 @@ function projectFields(obj, fields) {
|
|
|
1090
1276
|
}
|
|
1091
1277
|
|
|
1092
1278
|
// src/tools/inspect.ts
|
|
1093
|
-
function getSourceCode(component, projectRoot) {
|
|
1279
|
+
async function getSourceCode(component, projectRoot) {
|
|
1094
1280
|
const sourcePath = component.sourcePath;
|
|
1095
1281
|
if (!sourcePath) return void 0;
|
|
1096
1282
|
const fullPath = join2(projectRoot, sourcePath);
|
|
1097
1283
|
if (!existsSync2(fullPath)) return { path: sourcePath, code: null };
|
|
1098
1284
|
try {
|
|
1099
|
-
const code =
|
|
1285
|
+
const code = await fs.readFile(fullPath, "utf-8");
|
|
1100
1286
|
return { path: sourcePath, code };
|
|
1101
1287
|
} catch {
|
|
1102
1288
|
return { path: sourcePath, code: null };
|
|
@@ -1110,18 +1296,34 @@ var inspectHandler = async (args, ctx) => {
|
|
|
1110
1296
|
const maxLines = args?.maxLines;
|
|
1111
1297
|
const verbosity = args?.verbosity ?? "standard";
|
|
1112
1298
|
if (!componentName) {
|
|
1113
|
-
|
|
1299
|
+
return {
|
|
1300
|
+
content: [
|
|
1301
|
+
{
|
|
1302
|
+
type: "text",
|
|
1303
|
+
text: JSON.stringify({ error: "component is required" })
|
|
1304
|
+
}
|
|
1305
|
+
],
|
|
1306
|
+
isError: true
|
|
1307
|
+
};
|
|
1114
1308
|
}
|
|
1115
|
-
const component =
|
|
1309
|
+
const component = findComponent(ctx.data.snapshot, componentName);
|
|
1116
1310
|
if (!component) {
|
|
1117
1311
|
const closest = findClosestMatch(
|
|
1118
1312
|
componentName,
|
|
1119
1313
|
componentNames(ctx.data.snapshot)
|
|
1120
1314
|
);
|
|
1121
1315
|
const suggestion = closest ? ` Did you mean "${closest}"? Use ${ctx.toolNames.inspect}("${closest}") to inspect it.` : "";
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1316
|
+
return {
|
|
1317
|
+
content: [
|
|
1318
|
+
{
|
|
1319
|
+
type: "text",
|
|
1320
|
+
text: JSON.stringify({
|
|
1321
|
+
error: `Component "${componentName}" not found.${suggestion} Use ${ctx.toolNames.discover} to see available components.`
|
|
1322
|
+
})
|
|
1323
|
+
}
|
|
1324
|
+
],
|
|
1325
|
+
isError: true
|
|
1326
|
+
};
|
|
1125
1327
|
}
|
|
1126
1328
|
const pkgName = ctx.resolvePackageName(component.name);
|
|
1127
1329
|
let examples = component.examples;
|
|
@@ -1141,25 +1343,41 @@ var inspectHandler = async (args, ctx) => {
|
|
|
1141
1343
|
if (filtered.length > 0) {
|
|
1142
1344
|
examples = filtered;
|
|
1143
1345
|
} else {
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1346
|
+
return {
|
|
1347
|
+
content: [
|
|
1348
|
+
{
|
|
1349
|
+
type: "text",
|
|
1350
|
+
text: JSON.stringify({
|
|
1351
|
+
error: `Example "${exampleName}" not found for ${componentName}. Available: ${component.examples.map((example) => example.name).join(", ")}`
|
|
1352
|
+
})
|
|
1353
|
+
}
|
|
1354
|
+
],
|
|
1355
|
+
isError: true
|
|
1356
|
+
};
|
|
1147
1357
|
}
|
|
1148
1358
|
}
|
|
1149
1359
|
if (maxExamples && maxExamples > 0) {
|
|
1150
1360
|
examples = examples.slice(0, maxExamples);
|
|
1151
1361
|
}
|
|
1152
1362
|
const truncateCode = (code) => {
|
|
1153
|
-
if (!maxLines || maxLines <= 0)
|
|
1363
|
+
if (!maxLines || maxLines <= 0) {
|
|
1364
|
+
return { code, truncated: false, remainingLines: 0 };
|
|
1365
|
+
}
|
|
1154
1366
|
const lines = code.split("\n");
|
|
1155
|
-
if (lines.length <= maxLines)
|
|
1156
|
-
|
|
1157
|
-
|
|
1367
|
+
if (lines.length <= maxLines) {
|
|
1368
|
+
return { code, truncated: false, remainingLines: 0 };
|
|
1369
|
+
}
|
|
1370
|
+
return {
|
|
1371
|
+
code: lines.slice(0, maxLines).join("\n"),
|
|
1372
|
+
truncated: true,
|
|
1373
|
+
remainingLines: lines.length - maxLines
|
|
1374
|
+
};
|
|
1158
1375
|
};
|
|
1159
1376
|
const renderedExamples = examples.map((example) => ({
|
|
1377
|
+
...example.code ? truncateCode(example.code) : { truncated: false, remainingLines: 0 },
|
|
1160
1378
|
variant: example.name,
|
|
1161
1379
|
description: example.description,
|
|
1162
|
-
code: example.code ? truncateCode(example.code) : `<${component.name} />`,
|
|
1380
|
+
code: example.code ? truncateCode(example.code).code : `<${component.name} />`,
|
|
1163
1381
|
...example.code ? {} : {
|
|
1164
1382
|
note: "No code example provided. Refer to props for customization."
|
|
1165
1383
|
}
|
|
@@ -1189,13 +1407,13 @@ var inspectHandler = async (args, ctx) => {
|
|
|
1189
1407
|
status: component.status,
|
|
1190
1408
|
publicRef: component.publicRef,
|
|
1191
1409
|
publicSlug: component.publicSlug,
|
|
1410
|
+
isCanonical: component.isCanonical ?? false,
|
|
1192
1411
|
tier: component.tier
|
|
1193
1412
|
},
|
|
1194
|
-
props:
|
|
1413
|
+
props: propsReference,
|
|
1195
1414
|
examples: {
|
|
1196
1415
|
import: `import { ${component.name} } from '${pkgName}';`,
|
|
1197
|
-
code: renderedExamples
|
|
1198
|
-
propsReference
|
|
1416
|
+
code: renderedExamples
|
|
1199
1417
|
},
|
|
1200
1418
|
relations: component.relations,
|
|
1201
1419
|
compoundChildren: component.compoundChildren,
|
|
@@ -1215,7 +1433,7 @@ var inspectHandler = async (args, ctx) => {
|
|
|
1215
1433
|
})) ?? []
|
|
1216
1434
|
},
|
|
1217
1435
|
metadata: component.metadata,
|
|
1218
|
-
source: getSourceCode(component, ctx.config.projectRoot)
|
|
1436
|
+
source: await getSourceCode(component, ctx.config.projectRoot)
|
|
1219
1437
|
};
|
|
1220
1438
|
const aliasMap = { usage: "guidance" };
|
|
1221
1439
|
const resolvedFields = fields?.map((field) => {
|
|
@@ -1395,6 +1613,44 @@ function resolveCategoryKeys(categories, requestedCategory) {
|
|
|
1395
1613
|
}
|
|
1396
1614
|
return keys.filter((key) => (normalizeCategoryValue(key) ?? "").includes(normalized));
|
|
1397
1615
|
}
|
|
1616
|
+
function canonicalizeCategory(category, tokens) {
|
|
1617
|
+
const normalizedCategory = normalizeCategoryValue(category);
|
|
1618
|
+
const candidates = [
|
|
1619
|
+
normalizedCategory,
|
|
1620
|
+
...tokens.flatMap((token) => [
|
|
1621
|
+
normalizeCategoryValue(token.category),
|
|
1622
|
+
normalizeCategoryValue(token.path?.[0]),
|
|
1623
|
+
normalizeCategoryValue(token.name.split(/[.:/-]/)[0])
|
|
1624
|
+
])
|
|
1625
|
+
].filter(Boolean);
|
|
1626
|
+
for (const candidate of candidates) {
|
|
1627
|
+
for (const [canonical, aliases] of Object.entries(TOKEN_CATEGORY_ALIASES)) {
|
|
1628
|
+
if (candidate === canonical || aliases.some(
|
|
1629
|
+
(alias) => candidate === alias || candidate.includes(alias) || alias.includes(candidate)
|
|
1630
|
+
)) {
|
|
1631
|
+
return canonical;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
return normalizedCategory || "other";
|
|
1636
|
+
}
|
|
1637
|
+
function normalizeTokenCategories(categories) {
|
|
1638
|
+
const normalized = {};
|
|
1639
|
+
for (const [category, tokens] of Object.entries(categories)) {
|
|
1640
|
+
const canonical = canonicalizeCategory(category, tokens);
|
|
1641
|
+
if (!normalized[canonical]) normalized[canonical] = [];
|
|
1642
|
+
normalized[canonical].push(
|
|
1643
|
+
...tokens.map((token) => ({
|
|
1644
|
+
...token,
|
|
1645
|
+
category: canonical
|
|
1646
|
+
}))
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
for (const tokens of Object.values(normalized)) {
|
|
1650
|
+
tokens.sort((a, b) => a.name.localeCompare(b.name));
|
|
1651
|
+
}
|
|
1652
|
+
return normalized;
|
|
1653
|
+
}
|
|
1398
1654
|
var tokensHandler = async (args, ctx) => {
|
|
1399
1655
|
const category = args?.category;
|
|
1400
1656
|
const search2 = args?.search?.toLowerCase() ?? void 0;
|
|
@@ -1412,12 +1668,13 @@ var tokensHandler = async (args, ctx) => {
|
|
|
1412
1668
|
}]
|
|
1413
1669
|
};
|
|
1414
1670
|
}
|
|
1671
|
+
const normalizedCategories = normalizeTokenCategories(tokenData.categories);
|
|
1415
1672
|
let filteredCategories = {};
|
|
1416
1673
|
let filteredTotal = 0;
|
|
1417
|
-
const resolvedCategoryKeys = resolveCategoryKeys(
|
|
1674
|
+
const resolvedCategoryKeys = resolveCategoryKeys(normalizedCategories, category);
|
|
1418
1675
|
const friendlyCategories = Object.keys(TOKEN_CATEGORY_ALIASES);
|
|
1419
|
-
const searchMatchesCategory = search2 ? resolveCategoryKeys(
|
|
1420
|
-
for (const [cat, tokens] of Object.entries(
|
|
1676
|
+
const searchMatchesCategory = search2 ? resolveCategoryKeys(normalizedCategories, search2) : [];
|
|
1677
|
+
for (const [cat, tokens] of Object.entries(normalizedCategories)) {
|
|
1421
1678
|
if (category && !resolvedCategoryKeys.includes(cat)) continue;
|
|
1422
1679
|
let filtered = tokens;
|
|
1423
1680
|
if (search2) {
|
|
@@ -1425,7 +1682,7 @@ var tokensHandler = async (args, ctx) => {
|
|
|
1425
1682
|
filtered = tokens;
|
|
1426
1683
|
} else {
|
|
1427
1684
|
filtered = tokens.filter(
|
|
1428
|
-
(t) => t.name.toLowerCase().includes(search2) || t.description && t.description.toLowerCase().includes(search2) || (normalizeCategoryValue(cat) ?? "").includes(search2)
|
|
1685
|
+
(t) => t.name.toLowerCase().includes(search2) || t.description && t.description.toLowerCase().includes(search2) || (normalizeCategoryValue(cat) ?? "").includes(search2) || t.value && t.value.toLowerCase().includes(search2) || t.path && t.path.join(" ").toLowerCase().includes(search2)
|
|
1429
1686
|
);
|
|
1430
1687
|
}
|
|
1431
1688
|
}
|
|
@@ -1443,7 +1700,7 @@ var tokensHandler = async (args, ctx) => {
|
|
|
1443
1700
|
if (filteredTotal === 0) {
|
|
1444
1701
|
if (category && search2) {
|
|
1445
1702
|
const categoryTotal = resolvedCategoryKeys.reduce(
|
|
1446
|
-
(sum, key) => sum + (
|
|
1703
|
+
(sum, key) => sum + (normalizedCategories[key]?.length ?? 0),
|
|
1447
1704
|
0
|
|
1448
1705
|
);
|
|
1449
1706
|
hint = categoryTotal > 0 ? `No tokens matching "${search2}" in category "${category}" (${categoryTotal} tokens in this category). Try a broader search or remove the search term.` : `Category "${category}" not found. Try categories like: ${friendlyCategories.join(", ")}.`;
|
|
@@ -1465,7 +1722,7 @@ var tokensHandler = async (args, ctx) => {
|
|
|
1465
1722
|
categories: filteredCategories,
|
|
1466
1723
|
...hint && { hint },
|
|
1467
1724
|
...!category && !search2 && {
|
|
1468
|
-
availableCategories: Object.entries(
|
|
1725
|
+
availableCategories: Object.entries(normalizedCategories).map(([cat, tokens]) => ({
|
|
1469
1726
|
category: cat,
|
|
1470
1727
|
count: tokens.length
|
|
1471
1728
|
}))
|
|
@@ -1475,177 +1732,6 @@ var tokensHandler = async (args, ctx) => {
|
|
|
1475
1732
|
};
|
|
1476
1733
|
};
|
|
1477
1734
|
|
|
1478
|
-
// src/tools/implement.ts
|
|
1479
|
-
var implementHandler = async (args, ctx) => {
|
|
1480
|
-
const useCase = args?.useCase;
|
|
1481
|
-
if (!useCase) {
|
|
1482
|
-
throw new Error("useCase is required");
|
|
1483
|
-
}
|
|
1484
|
-
const verbosity = args?.verbosity ?? "standard";
|
|
1485
|
-
const snapshotComponents = listComponents(ctx.data.snapshot);
|
|
1486
|
-
const componentsByName = new Map(
|
|
1487
|
-
snapshotComponents.map((component) => [
|
|
1488
|
-
component.name.toLowerCase(),
|
|
1489
|
-
component
|
|
1490
|
-
])
|
|
1491
|
-
);
|
|
1492
|
-
const { allBlocks, localData } = buildLocalSearchData(
|
|
1493
|
-
{
|
|
1494
|
-
components: ctx.data.components,
|
|
1495
|
-
blocks: ctx.data.blocks,
|
|
1496
|
-
tokens: ctx.data.tokens,
|
|
1497
|
-
graph: ctx.data.graph
|
|
1498
|
-
},
|
|
1499
|
-
{
|
|
1500
|
-
componentIndex: ctx.indexes.componentIndex,
|
|
1501
|
-
blockIndex: ctx.indexes.blockIndex,
|
|
1502
|
-
tokenIndex: ctx.indexes.tokenIndex
|
|
1503
|
-
}
|
|
1504
|
-
);
|
|
1505
|
-
const tokenData = ctx.data.tokens;
|
|
1506
|
-
const implLimit = typeof args?.limit === "number" ? Math.min(Math.max(args.limit, 1), 15) : 5;
|
|
1507
|
-
const [componentResults, blockResults, tokenResults] = await Promise.all([
|
|
1508
|
-
hybridSearch(
|
|
1509
|
-
useCase,
|
|
1510
|
-
localData,
|
|
1511
|
-
implLimit * 3,
|
|
1512
|
-
"component",
|
|
1513
|
-
ctx.config.searchApiKey
|
|
1514
|
-
),
|
|
1515
|
-
hybridSearch(
|
|
1516
|
-
useCase,
|
|
1517
|
-
localData,
|
|
1518
|
-
implLimit,
|
|
1519
|
-
"block",
|
|
1520
|
-
ctx.config.searchApiKey
|
|
1521
|
-
),
|
|
1522
|
-
hybridSearch(
|
|
1523
|
-
useCase,
|
|
1524
|
-
localData,
|
|
1525
|
-
implLimit,
|
|
1526
|
-
"token",
|
|
1527
|
-
ctx.config.searchApiKey
|
|
1528
|
-
)
|
|
1529
|
-
]);
|
|
1530
|
-
const topBlockScore = blockResults.length > 0 ? blockResults[0].score : 0;
|
|
1531
|
-
const filteredBlockResults = blockResults.filter(
|
|
1532
|
-
(result) => result.score >= topBlockScore * 0.3
|
|
1533
|
-
);
|
|
1534
|
-
if (filteredBlockResults.length > 0) {
|
|
1535
|
-
const matchedBlocks = filteredBlockResults.map(
|
|
1536
|
-
(result) => allBlocks.find((block) => block.name.toLowerCase() === result.name.toLowerCase())
|
|
1537
|
-
).filter(Boolean);
|
|
1538
|
-
const blockFreq = buildBlockComponentFrequency(matchedBlocks);
|
|
1539
|
-
boostByBlockFrequency(componentResults, blockFreq);
|
|
1540
|
-
}
|
|
1541
|
-
const topComponentResults = componentResults.slice(0, implLimit);
|
|
1542
|
-
const maxCompScore = topComponentResults.length > 0 ? topComponentResults[0].score : 0;
|
|
1543
|
-
const topMatches = topComponentResults.map((result) => {
|
|
1544
|
-
const component = componentsByName.get(result.name.toLowerCase());
|
|
1545
|
-
return component ? { component, score: result.score } : null;
|
|
1546
|
-
}).filter(Boolean);
|
|
1547
|
-
const components = await Promise.all(
|
|
1548
|
-
topMatches.map(async ({ component, score }) => {
|
|
1549
|
-
const pkgName = ctx.resolvePackageName(component.name);
|
|
1550
|
-
if (verbosity === "compact") {
|
|
1551
|
-
return {
|
|
1552
|
-
name: component.name,
|
|
1553
|
-
description: component.description,
|
|
1554
|
-
confidence: assignConfidence(score, maxCompScore),
|
|
1555
|
-
import: `import { ${component.name} } from '${pkgName}';`
|
|
1556
|
-
};
|
|
1557
|
-
}
|
|
1558
|
-
const exampleLimit = verbosity === "full" ? component.examples.length : 2;
|
|
1559
|
-
const propsLimit = verbosity === "full" ? Object.keys(component.props ?? {}).length : 5;
|
|
1560
|
-
const examples = component.examples.slice(0, exampleLimit).map((example) => ({
|
|
1561
|
-
variant: example.name,
|
|
1562
|
-
code: example.code ?? `<${component.name} />`
|
|
1563
|
-
}));
|
|
1564
|
-
const propsSummary = Object.entries(component.props ?? {}).slice(0, propsLimit).map(
|
|
1565
|
-
([propName, prop]) => `${propName}${prop.required ? " (required)" : ""}: ${prop.type}${prop.values ? ` = ${prop.values.join("|")}` : ""}`
|
|
1566
|
-
);
|
|
1567
|
-
return {
|
|
1568
|
-
name: component.name,
|
|
1569
|
-
category: component.category,
|
|
1570
|
-
description: component.description,
|
|
1571
|
-
confidence: assignConfidence(score, maxCompScore),
|
|
1572
|
-
import: `import { ${component.name} } from '${pkgName}';`,
|
|
1573
|
-
props: propsSummary,
|
|
1574
|
-
examples,
|
|
1575
|
-
guidelines: getGuidanceWhen(component).slice(0, 3),
|
|
1576
|
-
accessibility: component.guidance.accessibility.slice(0, 2)
|
|
1577
|
-
};
|
|
1578
|
-
})
|
|
1579
|
-
);
|
|
1580
|
-
const matchingBlocks = (await Promise.all(
|
|
1581
|
-
filteredBlockResults.slice(0, 5).map(async (result) => {
|
|
1582
|
-
const block = allBlocks.find(
|
|
1583
|
-
(entry) => entry.name.toLowerCase() === result.name.toLowerCase()
|
|
1584
|
-
);
|
|
1585
|
-
if (!block) return null;
|
|
1586
|
-
const imports = await buildImportStatements(
|
|
1587
|
-
block.components,
|
|
1588
|
-
async (componentName) => ctx.resolvePackageName(componentName)
|
|
1589
|
-
);
|
|
1590
|
-
const codeLines = block.code.split("\n");
|
|
1591
|
-
const code = codeLines.length > 30 ? `${codeLines.slice(0, 20).join("\n")}
|
|
1592
|
-
// ... truncated (${codeLines.length} lines total)` : block.code;
|
|
1593
|
-
return {
|
|
1594
|
-
name: block.name,
|
|
1595
|
-
description: block.description,
|
|
1596
|
-
components: block.components,
|
|
1597
|
-
code,
|
|
1598
|
-
import: imports.join("\n"),
|
|
1599
|
-
imports
|
|
1600
|
-
};
|
|
1601
|
-
})
|
|
1602
|
-
)).filter(Boolean);
|
|
1603
|
-
let relevantTokens;
|
|
1604
|
-
if (tokenResults.length > 0 && tokenData) {
|
|
1605
|
-
relevantTokens = {};
|
|
1606
|
-
for (const result of tokenResults) {
|
|
1607
|
-
for (const [category, tokens] of Object.entries(tokenData.categories)) {
|
|
1608
|
-
if (tokens.some((token) => token.name === result.name)) {
|
|
1609
|
-
if (!relevantTokens[category]) relevantTokens[category] = [];
|
|
1610
|
-
relevantTokens[category].push(result.name);
|
|
1611
|
-
break;
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
if (Object.keys(relevantTokens).length === 0) {
|
|
1616
|
-
relevantTokens = void 0;
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
if (!relevantTokens && tokenData) {
|
|
1620
|
-
const categories = extractTokenCategories(useCase);
|
|
1621
|
-
relevantTokens = {};
|
|
1622
|
-
for (const category of categories) {
|
|
1623
|
-
const tokens = tokenData.categories[category];
|
|
1624
|
-
if (tokens && tokens.length > 0) {
|
|
1625
|
-
relevantTokens[category] = tokens.slice(0, 5).map((token) => token.name);
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
if (Object.keys(relevantTokens).length === 0) {
|
|
1629
|
-
relevantTokens = void 0;
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
return {
|
|
1633
|
-
content: [
|
|
1634
|
-
{
|
|
1635
|
-
type: "text",
|
|
1636
|
-
text: JSON.stringify({
|
|
1637
|
-
useCase,
|
|
1638
|
-
components,
|
|
1639
|
-
blocks: verbosity !== "compact" && matchingBlocks.length > 0 ? matchingBlocks : void 0,
|
|
1640
|
-
tokens: verbosity !== "compact" ? relevantTokens : void 0,
|
|
1641
|
-
noMatch: components.length === 0,
|
|
1642
|
-
summary: components.length > 0 ? `Found ${components.length} component(s) for "${useCase}". ${matchingBlocks.length > 0 ? `Plus ${matchingBlocks.length} ready-to-use block(s).` : ""}` : `No components match "${useCase}". Try ${ctx.toolNames.discover} with different terms${tokenData ? ` or ${ctx.toolNames.tokens} for tokens` : ""}.`
|
|
1643
|
-
})
|
|
1644
|
-
}
|
|
1645
|
-
]
|
|
1646
|
-
};
|
|
1647
|
-
};
|
|
1648
|
-
|
|
1649
1735
|
// src/service.ts
|
|
1650
1736
|
var DEFAULT_ENDPOINTS = {
|
|
1651
1737
|
render: "/fragments/render",
|
|
@@ -2327,8 +2413,18 @@ var governHandler = async (args, ctx) => {
|
|
|
2327
2413
|
}
|
|
2328
2414
|
const policyOverrides = args?.policy;
|
|
2329
2415
|
const format = args?.format ?? "json";
|
|
2416
|
+
const allowedComponents = Object.values(ctx.data.components).map(
|
|
2417
|
+
(component) => component.name
|
|
2418
|
+
);
|
|
2330
2419
|
const tokenPrefix = ctx.data.tokens?.prefix;
|
|
2331
2420
|
const basePolicy = tokenPrefix === "fui-" ? { rules: fragmentsPreset().rules } : { rules: universal().rules };
|
|
2421
|
+
basePolicy.rules["components/allow"] = {
|
|
2422
|
+
enabled: true,
|
|
2423
|
+
severity: "serious",
|
|
2424
|
+
options: {
|
|
2425
|
+
components: allowedComponents
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2332
2428
|
const engineOptions = ctx.data.tokens ? { tokenData: ctx.data.tokens } : void 0;
|
|
2333
2429
|
const input = {
|
|
2334
2430
|
spec,
|
|
@@ -2401,7 +2497,6 @@ var CORE_TOOLS = {
|
|
|
2401
2497
|
inspect: inspectHandler,
|
|
2402
2498
|
blocks: blocksHandler,
|
|
2403
2499
|
tokens: tokensHandler,
|
|
2404
|
-
implement: implementHandler,
|
|
2405
2500
|
graph: graphHandler,
|
|
2406
2501
|
perf: perfHandler,
|
|
2407
2502
|
govern: governHandler
|
|
@@ -2424,7 +2519,6 @@ var TOOL_CAPABILITIES = {
|
|
|
2424
2519
|
inspect: ["components"],
|
|
2425
2520
|
blocks: ["blocks"],
|
|
2426
2521
|
tokens: ["tokens"],
|
|
2427
|
-
implement: ["components"],
|
|
2428
2522
|
graph: ["graph"],
|
|
2429
2523
|
perf: ["performance"],
|
|
2430
2524
|
render: ["components"],
|
|
@@ -2576,7 +2670,7 @@ import { join as join7 } from "path";
|
|
|
2576
2670
|
import { readFile } from "fs/promises";
|
|
2577
2671
|
|
|
2578
2672
|
// src/discovery.ts
|
|
2579
|
-
import { existsSync as existsSync3, readFileSync as
|
|
2673
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync } from "fs";
|
|
2580
2674
|
import { join as join3, dirname, resolve } from "path";
|
|
2581
2675
|
import { createRequire } from "module";
|
|
2582
2676
|
function resolveWorkspaceGlob(baseDir, pattern) {
|
|
@@ -2609,7 +2703,7 @@ function getWorkspaceDirs(rootDir) {
|
|
|
2609
2703
|
const rootPkgPath = join3(rootDir, "package.json");
|
|
2610
2704
|
if (existsSync3(rootPkgPath)) {
|
|
2611
2705
|
try {
|
|
2612
|
-
const rootPkg = JSON.parse(
|
|
2706
|
+
const rootPkg = JSON.parse(readFileSync3(rootPkgPath, "utf-8"));
|
|
2613
2707
|
const workspaces = Array.isArray(rootPkg.workspaces) ? rootPkg.workspaces : rootPkg.workspaces?.packages;
|
|
2614
2708
|
if (Array.isArray(workspaces)) {
|
|
2615
2709
|
for (const pattern of workspaces) {
|
|
@@ -2623,7 +2717,7 @@ function getWorkspaceDirs(rootDir) {
|
|
|
2623
2717
|
const pnpmWsPath = join3(rootDir, "pnpm-workspace.yaml");
|
|
2624
2718
|
if (existsSync3(pnpmWsPath)) {
|
|
2625
2719
|
try {
|
|
2626
|
-
const content =
|
|
2720
|
+
const content = readFileSync3(pnpmWsPath, "utf-8");
|
|
2627
2721
|
const lines = content.split("\n");
|
|
2628
2722
|
let inPackages = false;
|
|
2629
2723
|
for (const line of lines) {
|
|
@@ -2656,7 +2750,7 @@ function resolveDepPackageJson(localRequire, depName) {
|
|
|
2656
2750
|
while (true) {
|
|
2657
2751
|
const candidate = join3(dir, "package.json");
|
|
2658
2752
|
if (existsSync3(candidate)) {
|
|
2659
|
-
const pkg = JSON.parse(
|
|
2753
|
+
const pkg = JSON.parse(readFileSync3(candidate, "utf-8"));
|
|
2660
2754
|
if (pkg.name === depName) return candidate;
|
|
2661
2755
|
}
|
|
2662
2756
|
const parent = dirname(dir);
|
|
@@ -2671,7 +2765,7 @@ function findFragmentsInDeps(dir, found, depField) {
|
|
|
2671
2765
|
const pkgJsonPath = join3(dir, "package.json");
|
|
2672
2766
|
if (!existsSync3(pkgJsonPath)) return;
|
|
2673
2767
|
try {
|
|
2674
|
-
const pkgJson = JSON.parse(
|
|
2768
|
+
const pkgJson = JSON.parse(readFileSync3(pkgJsonPath, "utf-8"));
|
|
2675
2769
|
const allDeps = {
|
|
2676
2770
|
...pkgJson.dependencies,
|
|
2677
2771
|
...pkgJson.devDependencies
|
|
@@ -2681,7 +2775,7 @@ function findFragmentsInDeps(dir, found, depField) {
|
|
|
2681
2775
|
try {
|
|
2682
2776
|
const depPkgPath = resolveDepPackageJson(localRequire, depName);
|
|
2683
2777
|
if (!depPkgPath) continue;
|
|
2684
|
-
const depPkg = JSON.parse(
|
|
2778
|
+
const depPkg = JSON.parse(readFileSync3(depPkgPath, "utf-8"));
|
|
2685
2779
|
if (depPkg[depField]) {
|
|
2686
2780
|
const fragmentsPath = join3(dirname(depPkgPath), depPkg[depField]);
|
|
2687
2781
|
if (existsSync3(fragmentsPath) && !found.includes(fragmentsPath)) {
|
|
@@ -2757,6 +2851,26 @@ function valueToString(value) {
|
|
|
2757
2851
|
return void 0;
|
|
2758
2852
|
}
|
|
2759
2853
|
}
|
|
2854
|
+
function buildCompoundChildren(contract, ai) {
|
|
2855
|
+
const contractChildren = Object.entries(contract?.compoundChildren ?? {});
|
|
2856
|
+
if (contractChildren.length > 0) {
|
|
2857
|
+
return contractChildren.map(([childName, child]) => ({
|
|
2858
|
+
name: childName,
|
|
2859
|
+
description: child.description,
|
|
2860
|
+
required: child.required,
|
|
2861
|
+
accepts: child.accepts,
|
|
2862
|
+
visibility: "public"
|
|
2863
|
+
}));
|
|
2864
|
+
}
|
|
2865
|
+
const subs = ai?.subComponents;
|
|
2866
|
+
if (!subs || subs.length === 0) return [];
|
|
2867
|
+
const requiredSet = new Set(ai?.requiredChildren ?? []);
|
|
2868
|
+
return subs.map((name) => ({
|
|
2869
|
+
name,
|
|
2870
|
+
required: requiredSet.has(name) || void 0,
|
|
2871
|
+
visibility: "public"
|
|
2872
|
+
}));
|
|
2873
|
+
}
|
|
2760
2874
|
function componentFromCompiledFragment(args) {
|
|
2761
2875
|
const { fragment, sourceType } = args;
|
|
2762
2876
|
const name = fragment.meta.name;
|
|
@@ -2806,15 +2920,7 @@ function componentFromCompiledFragment(args) {
|
|
|
2806
2920
|
relationship: relation.relationship,
|
|
2807
2921
|
note: relation.note
|
|
2808
2922
|
})),
|
|
2809
|
-
compoundChildren:
|
|
2810
|
-
([childName, child]) => ({
|
|
2811
|
-
name: childName,
|
|
2812
|
-
description: child.description,
|
|
2813
|
-
required: child.required,
|
|
2814
|
-
accepts: child.accepts,
|
|
2815
|
-
visibility: "public"
|
|
2816
|
-
})
|
|
2817
|
-
),
|
|
2923
|
+
compoundChildren: buildCompoundChildren(contract, ai),
|
|
2818
2924
|
guidance: {
|
|
2819
2925
|
when: usage.when ?? [],
|
|
2820
2926
|
whenNot: usage.whenNot ?? [],
|
|
@@ -3010,7 +3116,7 @@ If you're a library author, run \`${BRAND.cliCommand} build\` first.`
|
|
|
3010
3116
|
};
|
|
3011
3117
|
|
|
3012
3118
|
// src/adapters/auto-extract.ts
|
|
3013
|
-
import { existsSync as existsSync6, readFileSync as
|
|
3119
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
3014
3120
|
import { join as join6, relative, sep } from "path";
|
|
3015
3121
|
|
|
3016
3122
|
// src/adapters/discover-components.ts
|
|
@@ -3098,7 +3204,7 @@ function inferComponentName(fileName, dirPath) {
|
|
|
3098
3204
|
}
|
|
3099
3205
|
|
|
3100
3206
|
// src/adapters/scan-tokens.ts
|
|
3101
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
3207
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
3102
3208
|
import { join as join5, extname as extname2 } from "path";
|
|
3103
3209
|
function scanTokens(projectRoot) {
|
|
3104
3210
|
const cssFiles = discoverCssFiles(projectRoot);
|
|
@@ -3107,7 +3213,7 @@ function scanTokens(projectRoot) {
|
|
|
3107
3213
|
let prefix = "";
|
|
3108
3214
|
for (const filePath of cssFiles) {
|
|
3109
3215
|
try {
|
|
3110
|
-
const content =
|
|
3216
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
3111
3217
|
const tokens = extractCustomProperties(content);
|
|
3112
3218
|
allTokens.push(...tokens);
|
|
3113
3219
|
} catch {
|
|
@@ -3274,7 +3380,7 @@ var AutoExtractionAdapter = class {
|
|
|
3274
3380
|
async load(projectRoot) {
|
|
3275
3381
|
let extractMod;
|
|
3276
3382
|
try {
|
|
3277
|
-
extractMod = await
|
|
3383
|
+
extractMod = await loadExtractorModule();
|
|
3278
3384
|
} catch (e) {
|
|
3279
3385
|
throw new Error(
|
|
3280
3386
|
"Auto-extraction requires @fragments-sdk/extract and TypeScript.\n\nIf you see this error, the MCP server may not be installed correctly.\nAlternative: pre-build your design system with `npx fragments build`"
|
|
@@ -3390,6 +3496,13 @@ Check that your tsconfig.json includes the component directories.`
|
|
|
3390
3496
|
}
|
|
3391
3497
|
}
|
|
3392
3498
|
};
|
|
3499
|
+
var extractorModulePromise = null;
|
|
3500
|
+
async function loadExtractorModule() {
|
|
3501
|
+
if (!extractorModulePromise) {
|
|
3502
|
+
extractorModulePromise = import("./dist-V7D67NXS.js");
|
|
3503
|
+
}
|
|
3504
|
+
return extractorModulePromise;
|
|
3505
|
+
}
|
|
3393
3506
|
var UNIVERSAL_INHERITED = /* @__PURE__ */ new Set(["children", "className", "id", "disabled"]);
|
|
3394
3507
|
var FORM_INPUT_INHERITED = /* @__PURE__ */ new Set([
|
|
3395
3508
|
"placeholder",
|
|
@@ -3661,7 +3774,7 @@ function readPackageName(projectRoot) {
|
|
|
3661
3774
|
try {
|
|
3662
3775
|
const pkgPath = join6(projectRoot, "package.json");
|
|
3663
3776
|
if (!existsSync6(pkgPath)) return void 0;
|
|
3664
|
-
const pkg = JSON.parse(
|
|
3777
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
3665
3778
|
return pkg.name;
|
|
3666
3779
|
} catch {
|
|
3667
3780
|
return void 0;
|
|
@@ -3670,15 +3783,48 @@ function readPackageName(projectRoot) {
|
|
|
3670
3783
|
|
|
3671
3784
|
// src/adapters/cloud-catalog.ts
|
|
3672
3785
|
var DEFAULT_CLOUD_URL = "https://app.usefragments.com/api/catalog";
|
|
3786
|
+
var TOKEN_CATEGORY_ALIASES2 = {
|
|
3787
|
+
color: ["color", "colors", "accent", "background", "foreground", "danger", "brand"],
|
|
3788
|
+
spacing: ["spacing", "space", "padding", "margin", "gap", "inset"],
|
|
3789
|
+
typography: ["typography", "font", "text", "copy", "line-height", "letter"],
|
|
3790
|
+
border: ["border", "borders", "stroke", "outline"],
|
|
3791
|
+
radius: ["radius", "radii", "corner", "corners", "rounded"],
|
|
3792
|
+
shadow: ["shadow", "shadows", "elevation"],
|
|
3793
|
+
layout: ["layout", "grid", "container", "breakpoint"],
|
|
3794
|
+
focus: ["focus", "ring", "focus-ring"],
|
|
3795
|
+
surface: ["surface", "surfaces", "canvas", "card", "background"]
|
|
3796
|
+
};
|
|
3673
3797
|
function normalizeCatalogUrl(url) {
|
|
3674
3798
|
if (!url) return DEFAULT_CLOUD_URL;
|
|
3675
3799
|
if (url.endsWith("/api/catalog")) return url;
|
|
3676
3800
|
return `${url.replace(/\/+$/, "")}/api/catalog`;
|
|
3677
3801
|
}
|
|
3802
|
+
function normalizeValue(value) {
|
|
3803
|
+
return value?.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim() ?? "";
|
|
3804
|
+
}
|
|
3805
|
+
function canonicalizeTokenCategory(token) {
|
|
3806
|
+
const candidates = [
|
|
3807
|
+
token.category,
|
|
3808
|
+
token.path?.[0],
|
|
3809
|
+
token.name.split(/[.:/-]/)[0]
|
|
3810
|
+
].map(normalizeValue).filter(Boolean);
|
|
3811
|
+
for (const candidate of candidates) {
|
|
3812
|
+
for (const [canonical, aliases] of Object.entries(
|
|
3813
|
+
TOKEN_CATEGORY_ALIASES2
|
|
3814
|
+
)) {
|
|
3815
|
+
if (candidate === canonical || aliases.some(
|
|
3816
|
+
(alias) => candidate === alias || candidate.includes(alias) || alias.includes(candidate)
|
|
3817
|
+
)) {
|
|
3818
|
+
return canonical;
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
return candidates[0] || "other";
|
|
3823
|
+
}
|
|
3678
3824
|
function groupTokens(flat) {
|
|
3679
3825
|
const categories = {};
|
|
3680
3826
|
const normalizedFlat = (flat ?? []).map((token) => {
|
|
3681
|
-
const category = token
|
|
3827
|
+
const category = canonicalizeTokenCategory(token);
|
|
3682
3828
|
const normalized = {
|
|
3683
3829
|
name: token.name,
|
|
3684
3830
|
category,
|
|
@@ -3761,6 +3907,7 @@ function mapComponent(component, designSystem) {
|
|
|
3761
3907
|
importPath: designSystem?.importPath ?? designSystem?.packageName ?? void 0,
|
|
3762
3908
|
publicRef: component.publicRef,
|
|
3763
3909
|
publicSlug: component.publicSlug ?? null,
|
|
3910
|
+
isCanonical: component.isCanonical ?? false,
|
|
3764
3911
|
tier: component.tier,
|
|
3765
3912
|
parentComponentId: component.parentComponentKey,
|
|
3766
3913
|
parentComponentName: component.parentComponentName,
|
|
@@ -3778,7 +3925,7 @@ var CloudCatalogAdapter = class {
|
|
|
3778
3925
|
async load(_projectRoot) {
|
|
3779
3926
|
const response = await fetch(normalizeCatalogUrl(this.options.url), {
|
|
3780
3927
|
headers: {
|
|
3781
|
-
|
|
3928
|
+
"X-API-Key": this.options.apiKey
|
|
3782
3929
|
}
|
|
3783
3930
|
});
|
|
3784
3931
|
if (!response.ok) {
|
|
@@ -3822,9 +3969,21 @@ var CloudCatalogAdapter = class {
|
|
|
3822
3969
|
packageMap,
|
|
3823
3970
|
defaultPackageName: packageName
|
|
3824
3971
|
});
|
|
3972
|
+
const hydratedComponents = Object.fromEntries(
|
|
3973
|
+
Object.entries(snapshot.components).map(([componentId, component]) => [
|
|
3974
|
+
componentId,
|
|
3975
|
+
{
|
|
3976
|
+
...component,
|
|
3977
|
+
isCanonical: components[componentId]?.isCanonical ?? false
|
|
3978
|
+
}
|
|
3979
|
+
])
|
|
3980
|
+
);
|
|
3825
3981
|
return {
|
|
3826
|
-
snapshot
|
|
3827
|
-
|
|
3982
|
+
snapshot: {
|
|
3983
|
+
...snapshot,
|
|
3984
|
+
components: hydratedComponents
|
|
3985
|
+
},
|
|
3986
|
+
components: hydratedComponents,
|
|
3828
3987
|
blocks: snapshot.blocks,
|
|
3829
3988
|
tokens: snapshot.tokens,
|
|
3830
3989
|
graph: void 0,
|
|
@@ -3908,6 +4067,7 @@ function normalizeRelations(relations, fallback) {
|
|
|
3908
4067
|
function buildComponent(manifest, entry, shard) {
|
|
3909
4068
|
const sourcePackage = manifest.designSystem.importPath ?? manifest.designSystem.packageName ?? void 0;
|
|
3910
4069
|
const component = shard.component;
|
|
4070
|
+
const componentRecord = component;
|
|
3911
4071
|
const props = normalizeProps(component.props);
|
|
3912
4072
|
return {
|
|
3913
4073
|
id: shard.componentId,
|
|
@@ -3944,6 +4104,7 @@ function buildComponent(manifest, entry, shard) {
|
|
|
3944
4104
|
importPath: sourcePackage,
|
|
3945
4105
|
publicRef: component.publicRef,
|
|
3946
4106
|
publicSlug: component.publicSlug,
|
|
4107
|
+
isCanonical: componentRecord.isCanonical ?? component.tier === "core",
|
|
3947
4108
|
tier: component.tier,
|
|
3948
4109
|
parentComponentName: component.parentComponentName,
|
|
3949
4110
|
metadata: {
|
|
@@ -4036,9 +4197,21 @@ var BundleAdapter = class {
|
|
|
4036
4197
|
packageMap,
|
|
4037
4198
|
defaultPackageName: packageName
|
|
4038
4199
|
});
|
|
4200
|
+
const hydratedComponents = Object.fromEntries(
|
|
4201
|
+
Object.entries(snapshot.components).map(([componentId, component]) => [
|
|
4202
|
+
componentId,
|
|
4203
|
+
{
|
|
4204
|
+
...component,
|
|
4205
|
+
isCanonical: components[componentId]?.isCanonical ?? component.tier === "core"
|
|
4206
|
+
}
|
|
4207
|
+
])
|
|
4208
|
+
);
|
|
4039
4209
|
return {
|
|
4040
|
-
snapshot
|
|
4041
|
-
|
|
4210
|
+
snapshot: {
|
|
4211
|
+
...snapshot,
|
|
4212
|
+
components: hydratedComponents
|
|
4213
|
+
},
|
|
4214
|
+
components: hydratedComponents,
|
|
4042
4215
|
blocks: snapshot.blocks,
|
|
4043
4216
|
tokens: snapshot.tokens,
|
|
4044
4217
|
graph: snapshot.graph,
|
|
@@ -4122,6 +4295,9 @@ function resolveSearchApiKey(config, fileConfig) {
|
|
|
4122
4295
|
// src/server.ts
|
|
4123
4296
|
var TOOL_NAMES = buildToolNames();
|
|
4124
4297
|
var TOOLS = buildMcpTools();
|
|
4298
|
+
var TOOL_DEFINITION_BY_KEY = new Map(
|
|
4299
|
+
MCP_TOOL_DEFINITIONS.map((definition) => [definition.key, definition])
|
|
4300
|
+
);
|
|
4125
4301
|
function createMcpServer(config) {
|
|
4126
4302
|
const server = new Server(
|
|
4127
4303
|
{
|
|
@@ -4157,38 +4333,53 @@ function createMcpServer(config) {
|
|
|
4157
4333
|
}
|
|
4158
4334
|
}
|
|
4159
4335
|
let cachedData = null;
|
|
4336
|
+
let loadDataPromise = null;
|
|
4160
4337
|
let resolvedRoot = null;
|
|
4338
|
+
let resolveProjectRootPromise = null;
|
|
4161
4339
|
let componentIndex = null;
|
|
4162
4340
|
let blockIndex = null;
|
|
4163
4341
|
let tokenIndex = null;
|
|
4164
4342
|
async function resolveProjectRoot() {
|
|
4165
4343
|
if (resolvedRoot) return resolvedRoot;
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
const
|
|
4170
|
-
|
|
4171
|
-
|
|
4344
|
+
if (resolveProjectRootPromise) return resolveProjectRootPromise;
|
|
4345
|
+
resolveProjectRootPromise = (async () => {
|
|
4346
|
+
try {
|
|
4347
|
+
const result = await server.listRoots();
|
|
4348
|
+
if (result.roots?.length > 0) {
|
|
4349
|
+
const rootUri = result.roots[0].uri;
|
|
4350
|
+
resolvedRoot = fileURLToPath(rootUri);
|
|
4351
|
+
return resolvedRoot;
|
|
4352
|
+
}
|
|
4353
|
+
} catch {
|
|
4172
4354
|
}
|
|
4173
|
-
|
|
4355
|
+
resolvedRoot = config.projectRoot;
|
|
4356
|
+
return resolvedRoot;
|
|
4357
|
+
})();
|
|
4358
|
+
try {
|
|
4359
|
+
return await resolveProjectRootPromise;
|
|
4360
|
+
} finally {
|
|
4361
|
+
resolveProjectRootPromise = null;
|
|
4174
4362
|
}
|
|
4175
|
-
resolvedRoot = config.projectRoot;
|
|
4176
|
-
return resolvedRoot;
|
|
4177
4363
|
}
|
|
4178
4364
|
async function loadData() {
|
|
4179
4365
|
if (cachedData) return cachedData;
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4366
|
+
if (loadDataPromise) return loadDataPromise;
|
|
4367
|
+
loadDataPromise = (async () => {
|
|
4368
|
+
const projectRoot = await resolveProjectRoot();
|
|
4369
|
+
const loaded = await adapter.load(projectRoot);
|
|
4370
|
+
const allFragments = Object.values(loaded.components);
|
|
4371
|
+
const allBlocks = Object.values(loaded.blocks ?? {});
|
|
4372
|
+
componentIndex = buildComponentIndex(allFragments);
|
|
4373
|
+
blockIndex = allBlocks.length > 0 ? buildBlockIndex(allBlocks) : null;
|
|
4374
|
+
tokenIndex = loaded.tokens && loaded.tokens.total > 0 ? buildTokenIndex(loaded.tokens) : null;
|
|
4375
|
+
cachedData = loaded;
|
|
4376
|
+
return loaded;
|
|
4377
|
+
})();
|
|
4378
|
+
try {
|
|
4379
|
+
return await loadDataPromise;
|
|
4380
|
+
} finally {
|
|
4381
|
+
loadDataPromise = null;
|
|
4190
4382
|
}
|
|
4191
|
-
return cachedData;
|
|
4192
4383
|
}
|
|
4193
4384
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
4194
4385
|
const data = await loadData();
|
|
@@ -4220,7 +4411,7 @@ function createMcpServer(config) {
|
|
|
4220
4411
|
const packageJsonPath = join8(root, "package.json");
|
|
4221
4412
|
if (existsSync9(packageJsonPath)) {
|
|
4222
4413
|
try {
|
|
4223
|
-
const content =
|
|
4414
|
+
const content = readFileSync6(packageJsonPath, "utf-8");
|
|
4224
4415
|
const pkg = JSON.parse(content);
|
|
4225
4416
|
if (pkg.name) {
|
|
4226
4417
|
return pkg.name;
|
|
@@ -4234,6 +4425,23 @@ function createMcpServer(config) {
|
|
|
4234
4425
|
};
|
|
4235
4426
|
try {
|
|
4236
4427
|
const toolKey = registry.resolveKey(name);
|
|
4428
|
+
const definition = TOOL_DEFINITION_BY_KEY.get(toolKey);
|
|
4429
|
+
const argumentKeys = Object.keys(args ?? {});
|
|
4430
|
+
const allowedKeys = new Set(Object.keys(definition?.params ?? {}));
|
|
4431
|
+
const unknownKeys = definition ? argumentKeys.filter((key) => !allowedKeys.has(key)) : [];
|
|
4432
|
+
if (unknownKeys.length > 0) {
|
|
4433
|
+
return {
|
|
4434
|
+
content: [
|
|
4435
|
+
{
|
|
4436
|
+
type: "text",
|
|
4437
|
+
text: JSON.stringify({
|
|
4438
|
+
error: `Unknown argument(s) for ${toolKey}: ${unknownKeys.join(", ")}`
|
|
4439
|
+
})
|
|
4440
|
+
}
|
|
4441
|
+
],
|
|
4442
|
+
isError: true
|
|
4443
|
+
};
|
|
4444
|
+
}
|
|
4237
4445
|
const mCtx = {
|
|
4238
4446
|
toolName: name,
|
|
4239
4447
|
toolKey,
|
|
@@ -4290,4 +4498,4 @@ export {
|
|
|
4290
4498
|
startMcpServer,
|
|
4291
4499
|
createSandboxServer
|
|
4292
4500
|
};
|
|
4293
|
-
//# sourceMappingURL=chunk-
|
|
4501
|
+
//# sourceMappingURL=chunk-HGGAXLRO.js.map
|