@thallylabs/mcp 0.10.24 → 0.10.27
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/index.js +37 -24
- package/dist/tools.js +37 -24
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -243,19 +243,25 @@ function formatGroup(group, indent) {
|
|
|
243
243
|
}
|
|
244
244
|
return lines;
|
|
245
245
|
}
|
|
246
|
+
function formatPages(pages, indent) {
|
|
247
|
+
return pages.flatMap((page) => {
|
|
248
|
+
if (typeof page !== "string") return formatGroup(page, indent);
|
|
249
|
+
const href = page === "introduction" ? "/" : `/${page}`;
|
|
250
|
+
return [`${indent}- ${page.padEnd(30)} \u2192 ${href}`];
|
|
251
|
+
});
|
|
252
|
+
}
|
|
246
253
|
async function handleListPages(input) {
|
|
247
254
|
const config = readDocsJson(input.projectDir);
|
|
248
255
|
const lines = [];
|
|
249
256
|
for (const tab of config.tabs) {
|
|
250
257
|
lines.push(`Tab: ${tab.tab}`);
|
|
251
|
-
|
|
258
|
+
const navigationPages = [...tab.pages ?? [], ...tab.groups ?? []];
|
|
259
|
+
if (tab.href && navigationPages.length === 0) {
|
|
252
260
|
lines.push(` \u2192 External: ${tab.href}`);
|
|
253
|
-
} else if (tab.api) {
|
|
261
|
+
} else if (tab.api && navigationPages.length === 0) {
|
|
254
262
|
lines.push(` \u2192 API Reference: ${tab.api.source}`);
|
|
255
|
-
} else if (
|
|
256
|
-
|
|
257
|
-
lines.push(...formatGroup(group, " "));
|
|
258
|
-
}
|
|
263
|
+
} else if (navigationPages.length > 0) {
|
|
264
|
+
lines.push(...formatPages(navigationPages, " "));
|
|
259
265
|
} else {
|
|
260
266
|
lines.push(" (no pages)");
|
|
261
267
|
}
|
|
@@ -884,8 +890,16 @@ function collectNavPageIds(groups, seen, duplicates) {
|
|
|
884
890
|
}
|
|
885
891
|
function addOrphanToNav(projectDir, pageId) {
|
|
886
892
|
const config = readDocsJson(projectDir);
|
|
887
|
-
const tab = config.tabs.find((
|
|
888
|
-
if (!tab
|
|
893
|
+
const tab = config.tabs.find((candidate) => !candidate.href && !candidate.api && (candidate.pages?.length || candidate.groups?.length));
|
|
894
|
+
if (!tab) return;
|
|
895
|
+
if (tab.pages) {
|
|
896
|
+
if (!tab.pages.includes(pageId)) {
|
|
897
|
+
tab.pages.push(pageId);
|
|
898
|
+
writeDocsJson(projectDir, config);
|
|
899
|
+
}
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
if (!tab.groups) return;
|
|
889
903
|
const lastGroup = tab.groups[tab.groups.length - 1];
|
|
890
904
|
const existing = lastGroup.pages.filter((p) => typeof p === "string");
|
|
891
905
|
if (!existing.includes(pageId)) {
|
|
@@ -904,12 +918,14 @@ async function handleLintProject(input) {
|
|
|
904
918
|
const navPageIds = /* @__PURE__ */ new Set();
|
|
905
919
|
const duplicates = /* @__PURE__ */ new Set();
|
|
906
920
|
for (const tab of config.tabs) {
|
|
907
|
-
|
|
908
|
-
if (
|
|
921
|
+
const hasNavigationNodes = Boolean(tab.pages?.length || tab.groups?.length);
|
|
922
|
+
if ((tab.href || tab.api) && !hasNavigationNodes) continue;
|
|
923
|
+
if (!hasNavigationNodes) {
|
|
909
924
|
issues.push({ severity: "error", message: `Tab "${tab.tab}" has no groups and no href \u2014 it will render empty` });
|
|
910
925
|
continue;
|
|
911
926
|
}
|
|
912
|
-
collectNavPageIds(tab.
|
|
927
|
+
collectNavPageIds(tab.pages ?? [], navPageIds, duplicates);
|
|
928
|
+
collectNavPageIds(tab.groups ?? [], navPageIds, duplicates);
|
|
913
929
|
}
|
|
914
930
|
for (const dup of duplicates) {
|
|
915
931
|
issues.push({ severity: "error", message: `[duplicate] "${dup}" appears more than once in docs.json` });
|
|
@@ -1003,7 +1019,7 @@ async function handleLintProject(input) {
|
|
|
1003
1019
|
|
|
1004
1020
|
// src/tools/translate-docs.ts
|
|
1005
1021
|
import { z as z14 } from "zod";
|
|
1006
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
1022
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
|
|
1007
1023
|
import { join as join9, dirname as dirname2 } from "path";
|
|
1008
1024
|
import Anthropic from "@anthropic-ai/sdk";
|
|
1009
1025
|
import pLimit from "p-limit";
|
|
@@ -1036,8 +1052,8 @@ function getAllPageIds(config) {
|
|
|
1036
1052
|
const seen = /* @__PURE__ */ new Set();
|
|
1037
1053
|
const hrefOnlyPages = [];
|
|
1038
1054
|
for (const tab of config.tabs) {
|
|
1039
|
-
if (tab.api && !tab.groups) continue;
|
|
1040
|
-
if (!tab.groups && tab.href) {
|
|
1055
|
+
if (tab.api && !tab.pages && !tab.groups) continue;
|
|
1056
|
+
if (!tab.pages && !tab.groups && tab.href) {
|
|
1041
1057
|
const pageId = tab.href.replace(/^\//, "");
|
|
1042
1058
|
if (pageId && !seen.has(pageId)) {
|
|
1043
1059
|
seen.add(pageId);
|
|
@@ -1046,13 +1062,10 @@ function getAllPageIds(config) {
|
|
|
1046
1062
|
}
|
|
1047
1063
|
continue;
|
|
1048
1064
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
seen.add(id);
|
|
1054
|
-
ids.push(id);
|
|
1055
|
-
}
|
|
1065
|
+
for (const id of collectPageIds([...tab.pages ?? [], ...tab.groups ?? []])) {
|
|
1066
|
+
if (!seen.has(id)) {
|
|
1067
|
+
seen.add(id);
|
|
1068
|
+
ids.push(id);
|
|
1056
1069
|
}
|
|
1057
1070
|
}
|
|
1058
1071
|
}
|
|
@@ -1150,7 +1163,7 @@ async function handleTranslateDocs(input) {
|
|
|
1150
1163
|
}
|
|
1151
1164
|
const translated = await translatePage(sourceContent, targetLocale.label, locale, model, client);
|
|
1152
1165
|
mkdirSync2(dirname2(targetFile), { recursive: true });
|
|
1153
|
-
|
|
1166
|
+
writeFileSync5(targetFile, translated + "\n", "utf8");
|
|
1154
1167
|
results.push({ pageId, success: true });
|
|
1155
1168
|
} catch (err) {
|
|
1156
1169
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1573,7 +1586,7 @@ async function handleReadApiSpec(input) {
|
|
|
1573
1586
|
}
|
|
1574
1587
|
|
|
1575
1588
|
// src/tools/update-api-spec.ts
|
|
1576
|
-
import { writeFileSync as
|
|
1589
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
1577
1590
|
import { extname as extname2 } from "path";
|
|
1578
1591
|
import { z as z17 } from "zod";
|
|
1579
1592
|
import { parse as parseYaml } from "yaml";
|
|
@@ -1599,7 +1612,7 @@ async function handleUpdateApiSpec(input) {
|
|
|
1599
1612
|
}
|
|
1600
1613
|
const content = input.content.endsWith("\n") ? input.content : `${input.content}
|
|
1601
1614
|
`;
|
|
1602
|
-
|
|
1615
|
+
writeFileSync6(source.path, content, "utf8");
|
|
1603
1616
|
return `\u2705 API source updated: ${source.source}`;
|
|
1604
1617
|
}
|
|
1605
1618
|
|
package/dist/tools.js
CHANGED
|
@@ -234,19 +234,25 @@ function formatGroup(group, indent) {
|
|
|
234
234
|
}
|
|
235
235
|
return lines;
|
|
236
236
|
}
|
|
237
|
+
function formatPages(pages, indent) {
|
|
238
|
+
return pages.flatMap((page) => {
|
|
239
|
+
if (typeof page !== "string") return formatGroup(page, indent);
|
|
240
|
+
const href = page === "introduction" ? "/" : `/${page}`;
|
|
241
|
+
return [`${indent}- ${page.padEnd(30)} \u2192 ${href}`];
|
|
242
|
+
});
|
|
243
|
+
}
|
|
237
244
|
async function handleListPages(input) {
|
|
238
245
|
const config = readDocsJson(input.projectDir);
|
|
239
246
|
const lines = [];
|
|
240
247
|
for (const tab of config.tabs) {
|
|
241
248
|
lines.push(`Tab: ${tab.tab}`);
|
|
242
|
-
|
|
249
|
+
const navigationPages = [...tab.pages ?? [], ...tab.groups ?? []];
|
|
250
|
+
if (tab.href && navigationPages.length === 0) {
|
|
243
251
|
lines.push(` \u2192 External: ${tab.href}`);
|
|
244
|
-
} else if (tab.api) {
|
|
252
|
+
} else if (tab.api && navigationPages.length === 0) {
|
|
245
253
|
lines.push(` \u2192 API Reference: ${tab.api.source}`);
|
|
246
|
-
} else if (
|
|
247
|
-
|
|
248
|
-
lines.push(...formatGroup(group, " "));
|
|
249
|
-
}
|
|
254
|
+
} else if (navigationPages.length > 0) {
|
|
255
|
+
lines.push(...formatPages(navigationPages, " "));
|
|
250
256
|
} else {
|
|
251
257
|
lines.push(" (no pages)");
|
|
252
258
|
}
|
|
@@ -875,8 +881,16 @@ function collectNavPageIds(groups, seen, duplicates) {
|
|
|
875
881
|
}
|
|
876
882
|
function addOrphanToNav(projectDir, pageId) {
|
|
877
883
|
const config = readDocsJson(projectDir);
|
|
878
|
-
const tab = config.tabs.find((
|
|
879
|
-
if (!tab
|
|
884
|
+
const tab = config.tabs.find((candidate) => !candidate.href && !candidate.api && (candidate.pages?.length || candidate.groups?.length));
|
|
885
|
+
if (!tab) return;
|
|
886
|
+
if (tab.pages) {
|
|
887
|
+
if (!tab.pages.includes(pageId)) {
|
|
888
|
+
tab.pages.push(pageId);
|
|
889
|
+
writeDocsJson(projectDir, config);
|
|
890
|
+
}
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
if (!tab.groups) return;
|
|
880
894
|
const lastGroup = tab.groups[tab.groups.length - 1];
|
|
881
895
|
const existing = lastGroup.pages.filter((p) => typeof p === "string");
|
|
882
896
|
if (!existing.includes(pageId)) {
|
|
@@ -895,12 +909,14 @@ async function handleLintProject(input) {
|
|
|
895
909
|
const navPageIds = /* @__PURE__ */ new Set();
|
|
896
910
|
const duplicates = /* @__PURE__ */ new Set();
|
|
897
911
|
for (const tab of config.tabs) {
|
|
898
|
-
|
|
899
|
-
if (
|
|
912
|
+
const hasNavigationNodes = Boolean(tab.pages?.length || tab.groups?.length);
|
|
913
|
+
if ((tab.href || tab.api) && !hasNavigationNodes) continue;
|
|
914
|
+
if (!hasNavigationNodes) {
|
|
900
915
|
issues.push({ severity: "error", message: `Tab "${tab.tab}" has no groups and no href \u2014 it will render empty` });
|
|
901
916
|
continue;
|
|
902
917
|
}
|
|
903
|
-
collectNavPageIds(tab.
|
|
918
|
+
collectNavPageIds(tab.pages ?? [], navPageIds, duplicates);
|
|
919
|
+
collectNavPageIds(tab.groups ?? [], navPageIds, duplicates);
|
|
904
920
|
}
|
|
905
921
|
for (const dup of duplicates) {
|
|
906
922
|
issues.push({ severity: "error", message: `[duplicate] "${dup}" appears more than once in docs.json` });
|
|
@@ -994,7 +1010,7 @@ async function handleLintProject(input) {
|
|
|
994
1010
|
|
|
995
1011
|
// src/tools/translate-docs.ts
|
|
996
1012
|
import { z as z14 } from "zod";
|
|
997
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
1013
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
|
|
998
1014
|
import { join as join9, dirname as dirname2 } from "path";
|
|
999
1015
|
import Anthropic from "@anthropic-ai/sdk";
|
|
1000
1016
|
import pLimit from "p-limit";
|
|
@@ -1027,8 +1043,8 @@ function getAllPageIds(config) {
|
|
|
1027
1043
|
const seen = /* @__PURE__ */ new Set();
|
|
1028
1044
|
const hrefOnlyPages = [];
|
|
1029
1045
|
for (const tab of config.tabs) {
|
|
1030
|
-
if (tab.api && !tab.groups) continue;
|
|
1031
|
-
if (!tab.groups && tab.href) {
|
|
1046
|
+
if (tab.api && !tab.pages && !tab.groups) continue;
|
|
1047
|
+
if (!tab.pages && !tab.groups && tab.href) {
|
|
1032
1048
|
const pageId = tab.href.replace(/^\//, "");
|
|
1033
1049
|
if (pageId && !seen.has(pageId)) {
|
|
1034
1050
|
seen.add(pageId);
|
|
@@ -1037,13 +1053,10 @@ function getAllPageIds(config) {
|
|
|
1037
1053
|
}
|
|
1038
1054
|
continue;
|
|
1039
1055
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
seen.add(id);
|
|
1045
|
-
ids.push(id);
|
|
1046
|
-
}
|
|
1056
|
+
for (const id of collectPageIds([...tab.pages ?? [], ...tab.groups ?? []])) {
|
|
1057
|
+
if (!seen.has(id)) {
|
|
1058
|
+
seen.add(id);
|
|
1059
|
+
ids.push(id);
|
|
1047
1060
|
}
|
|
1048
1061
|
}
|
|
1049
1062
|
}
|
|
@@ -1141,7 +1154,7 @@ async function handleTranslateDocs(input) {
|
|
|
1141
1154
|
}
|
|
1142
1155
|
const translated = await translatePage(sourceContent, targetLocale.label, locale, model, client);
|
|
1143
1156
|
mkdirSync2(dirname2(targetFile), { recursive: true });
|
|
1144
|
-
|
|
1157
|
+
writeFileSync5(targetFile, translated + "\n", "utf8");
|
|
1145
1158
|
results.push({ pageId, success: true });
|
|
1146
1159
|
} catch (err) {
|
|
1147
1160
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1564,7 +1577,7 @@ async function handleReadApiSpec(input) {
|
|
|
1564
1577
|
}
|
|
1565
1578
|
|
|
1566
1579
|
// src/tools/update-api-spec.ts
|
|
1567
|
-
import { writeFileSync as
|
|
1580
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
1568
1581
|
import { extname as extname2 } from "path";
|
|
1569
1582
|
import { z as z17 } from "zod";
|
|
1570
1583
|
import { parse as parseYaml } from "yaml";
|
|
@@ -1590,7 +1603,7 @@ async function handleUpdateApiSpec(input) {
|
|
|
1590
1603
|
}
|
|
1591
1604
|
const content = input.content.endsWith("\n") ? input.content : `${input.content}
|
|
1592
1605
|
`;
|
|
1593
|
-
|
|
1606
|
+
writeFileSync6(source.path, content, "utf8");
|
|
1594
1607
|
return `\u2705 API source updated: ${source.source}`;
|
|
1595
1608
|
}
|
|
1596
1609
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thallylabs/mcp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.27",
|
|
4
4
|
"description": "MCP server for managing Thally knowledge surfaces and tracing product changes into documentation work.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@anthropic-ai/sdk": "^0.36.0",
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
41
|
-
"create-thally-docs": "0.10.
|
|
41
|
+
"create-thally-docs": "0.10.25",
|
|
42
42
|
"gray-matter": "^4.0.3",
|
|
43
43
|
"p-limit": "^6.1.0",
|
|
44
44
|
"yaml": "^2.8.2",
|