@thallylabs/mcp 0.10.25 → 0.10.28
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 +3 -4
- package/dist/index.js +65 -35
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +65 -35
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
A [Model Context Protocol](https://modelcontextprotocol.io) server that lets AI
|
|
4
4
|
tools — Claude Code, Claude Desktop, Cursor, Windsurf — manage
|
|
5
|
-
[Thally](https://github.com/thallylabs/thally)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
work.
|
|
5
|
+
[Thally](https://github.com/thallylabs/thally) docs through natural language.
|
|
6
|
+
Tools can create, read, update, search, and migrate documentation, then trace a
|
|
7
|
+
product change into an evidence-backed update for human review.
|
|
9
8
|
|
|
10
9
|
## Setup
|
|
11
10
|
|
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
|
}
|
|
@@ -270,18 +276,35 @@ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync
|
|
|
270
276
|
import { join as join3 } from "path";
|
|
271
277
|
|
|
272
278
|
// src/lib/frontmatter.ts
|
|
273
|
-
import
|
|
274
|
-
var FRONTMATTER_OPTIONS = {
|
|
275
|
-
engines: {
|
|
276
|
-
javascript: () => ({}),
|
|
277
|
-
js: () => ({})
|
|
278
|
-
}
|
|
279
|
-
};
|
|
279
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
280
280
|
function parseFrontmatter(raw) {
|
|
281
|
-
|
|
281
|
+
const source = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
|
|
282
|
+
const opening = /^---([^\r\n]*)\r?\n/.exec(source);
|
|
283
|
+
if (!opening || opening[1].startsWith("-")) return { content: source, data: {} };
|
|
284
|
+
const language = opening[1].trim().toLowerCase();
|
|
285
|
+
const remainder = source.slice(opening[0].length);
|
|
286
|
+
const closing = /^---[ \t]*\r?$/m.exec(remainder);
|
|
287
|
+
const matter = closing ? remainder.slice(0, closing.index) : remainder;
|
|
288
|
+
let content = closing ? remainder.slice(closing.index + closing[0].length) : "";
|
|
289
|
+
if (content.startsWith("\r\n")) content = content.slice(2);
|
|
290
|
+
else if (content.startsWith("\n")) content = content.slice(1);
|
|
291
|
+
if (matter.trim() === "" || !["", "yaml", "yml"].includes(language)) {
|
|
292
|
+
return { content, data: {} };
|
|
293
|
+
}
|
|
294
|
+
const parsed = parseYaml(matter);
|
|
295
|
+
return {
|
|
296
|
+
content,
|
|
297
|
+
data: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {}
|
|
298
|
+
};
|
|
282
299
|
}
|
|
283
300
|
function stringifyFrontmatter(body, data) {
|
|
284
|
-
|
|
301
|
+
const normalizedBody = body.endsWith("\n") ? body : `${body}
|
|
302
|
+
`;
|
|
303
|
+
const serialized = stringifyYaml(data, { lineWidth: 0 }).trimEnd();
|
|
304
|
+
return serialized === "{}" ? normalizedBody : `---
|
|
305
|
+
${serialized}
|
|
306
|
+
---
|
|
307
|
+
${normalizedBody}`;
|
|
285
308
|
}
|
|
286
309
|
|
|
287
310
|
// src/tools/update-page.ts
|
|
@@ -884,8 +907,16 @@ function collectNavPageIds(groups, seen, duplicates) {
|
|
|
884
907
|
}
|
|
885
908
|
function addOrphanToNav(projectDir, pageId) {
|
|
886
909
|
const config = readDocsJson(projectDir);
|
|
887
|
-
const tab = config.tabs.find((
|
|
888
|
-
if (!tab
|
|
910
|
+
const tab = config.tabs.find((candidate) => !candidate.href && !candidate.api && (candidate.pages?.length || candidate.groups?.length));
|
|
911
|
+
if (!tab) return;
|
|
912
|
+
if (tab.pages) {
|
|
913
|
+
if (!tab.pages.includes(pageId)) {
|
|
914
|
+
tab.pages.push(pageId);
|
|
915
|
+
writeDocsJson(projectDir, config);
|
|
916
|
+
}
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (!tab.groups) return;
|
|
889
920
|
const lastGroup = tab.groups[tab.groups.length - 1];
|
|
890
921
|
const existing = lastGroup.pages.filter((p) => typeof p === "string");
|
|
891
922
|
if (!existing.includes(pageId)) {
|
|
@@ -904,12 +935,14 @@ async function handleLintProject(input) {
|
|
|
904
935
|
const navPageIds = /* @__PURE__ */ new Set();
|
|
905
936
|
const duplicates = /* @__PURE__ */ new Set();
|
|
906
937
|
for (const tab of config.tabs) {
|
|
907
|
-
|
|
908
|
-
if (
|
|
938
|
+
const hasNavigationNodes = Boolean(tab.pages?.length || tab.groups?.length);
|
|
939
|
+
if ((tab.href || tab.api) && !hasNavigationNodes) continue;
|
|
940
|
+
if (!hasNavigationNodes) {
|
|
909
941
|
issues.push({ severity: "error", message: `Tab "${tab.tab}" has no groups and no href \u2014 it will render empty` });
|
|
910
942
|
continue;
|
|
911
943
|
}
|
|
912
|
-
collectNavPageIds(tab.
|
|
944
|
+
collectNavPageIds(tab.pages ?? [], navPageIds, duplicates);
|
|
945
|
+
collectNavPageIds(tab.groups ?? [], navPageIds, duplicates);
|
|
913
946
|
}
|
|
914
947
|
for (const dup of duplicates) {
|
|
915
948
|
issues.push({ severity: "error", message: `[duplicate] "${dup}" appears more than once in docs.json` });
|
|
@@ -1003,7 +1036,7 @@ async function handleLintProject(input) {
|
|
|
1003
1036
|
|
|
1004
1037
|
// src/tools/translate-docs.ts
|
|
1005
1038
|
import { z as z14 } from "zod";
|
|
1006
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
1039
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
|
|
1007
1040
|
import { join as join9, dirname as dirname2 } from "path";
|
|
1008
1041
|
import Anthropic from "@anthropic-ai/sdk";
|
|
1009
1042
|
import pLimit from "p-limit";
|
|
@@ -1036,8 +1069,8 @@ function getAllPageIds(config) {
|
|
|
1036
1069
|
const seen = /* @__PURE__ */ new Set();
|
|
1037
1070
|
const hrefOnlyPages = [];
|
|
1038
1071
|
for (const tab of config.tabs) {
|
|
1039
|
-
if (tab.api && !tab.groups) continue;
|
|
1040
|
-
if (!tab.groups && tab.href) {
|
|
1072
|
+
if (tab.api && !tab.pages && !tab.groups) continue;
|
|
1073
|
+
if (!tab.pages && !tab.groups && tab.href) {
|
|
1041
1074
|
const pageId = tab.href.replace(/^\//, "");
|
|
1042
1075
|
if (pageId && !seen.has(pageId)) {
|
|
1043
1076
|
seen.add(pageId);
|
|
@@ -1046,13 +1079,10 @@ function getAllPageIds(config) {
|
|
|
1046
1079
|
}
|
|
1047
1080
|
continue;
|
|
1048
1081
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
seen.add(id);
|
|
1054
|
-
ids.push(id);
|
|
1055
|
-
}
|
|
1082
|
+
for (const id of collectPageIds([...tab.pages ?? [], ...tab.groups ?? []])) {
|
|
1083
|
+
if (!seen.has(id)) {
|
|
1084
|
+
seen.add(id);
|
|
1085
|
+
ids.push(id);
|
|
1056
1086
|
}
|
|
1057
1087
|
}
|
|
1058
1088
|
}
|
|
@@ -1150,7 +1180,7 @@ async function handleTranslateDocs(input) {
|
|
|
1150
1180
|
}
|
|
1151
1181
|
const translated = await translatePage(sourceContent, targetLocale.label, locale, model, client);
|
|
1152
1182
|
mkdirSync2(dirname2(targetFile), { recursive: true });
|
|
1153
|
-
|
|
1183
|
+
writeFileSync5(targetFile, translated + "\n", "utf8");
|
|
1154
1184
|
results.push({ pageId, success: true });
|
|
1155
1185
|
} catch (err) {
|
|
1156
1186
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1573,10 +1603,10 @@ async function handleReadApiSpec(input) {
|
|
|
1573
1603
|
}
|
|
1574
1604
|
|
|
1575
1605
|
// src/tools/update-api-spec.ts
|
|
1576
|
-
import { writeFileSync as
|
|
1606
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
1577
1607
|
import { extname as extname2 } from "path";
|
|
1578
1608
|
import { z as z17 } from "zod";
|
|
1579
|
-
import { parse as
|
|
1609
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1580
1610
|
var updateApiSpecSchema = z17.object({
|
|
1581
1611
|
projectDir: z17.string().describe("Path to the Thally project root"),
|
|
1582
1612
|
source: z17.string().optional().describe("Configured OpenAPI source; omit when the project has one"),
|
|
@@ -1584,7 +1614,7 @@ var updateApiSpecSchema = z17.object({
|
|
|
1584
1614
|
});
|
|
1585
1615
|
function parseDocument(source, content) {
|
|
1586
1616
|
try {
|
|
1587
|
-
return extname2(source).toLowerCase() === ".json" ? JSON.parse(content) :
|
|
1617
|
+
return extname2(source).toLowerCase() === ".json" ? JSON.parse(content) : parseYaml2(content);
|
|
1588
1618
|
} catch {
|
|
1589
1619
|
throw new Error(
|
|
1590
1620
|
`OpenAPI source is not valid ${extname2(source).toLowerCase() === ".json" ? "JSON" : "YAML"}.`
|
|
@@ -1599,7 +1629,7 @@ async function handleUpdateApiSpec(input) {
|
|
|
1599
1629
|
}
|
|
1600
1630
|
const content = input.content.endsWith("\n") ? input.content : `${input.content}
|
|
1601
1631
|
`;
|
|
1602
|
-
|
|
1632
|
+
writeFileSync6(source.path, content, "utf8");
|
|
1603
1633
|
return `\u2705 API source updated: ${source.source}`;
|
|
1604
1634
|
}
|
|
1605
1635
|
|
package/dist/tools.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { z } from 'zod';
|
|
|
5
5
|
* - `project` — reads/writes a local Thally project directory (`projectDir`).
|
|
6
6
|
* - `site` — queries a deployed Thally site over HTTP (`siteUrl`).
|
|
7
7
|
*
|
|
8
|
-
* The remote MCP endpoint
|
|
9
|
-
* agent
|
|
8
|
+
* The remote MCP endpoint exposes only what's safe over HTTP; the docs
|
|
9
|
+
* agent drives the `project` tools against a checked-out docs repo. Both
|
|
10
10
|
* consume this one registry instead of re-declaring the tools.
|
|
11
11
|
*/
|
|
12
12
|
type ToolScope = "project" | "site";
|
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
|
}
|
|
@@ -261,18 +267,35 @@ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync
|
|
|
261
267
|
import { join as join3 } from "path";
|
|
262
268
|
|
|
263
269
|
// src/lib/frontmatter.ts
|
|
264
|
-
import
|
|
265
|
-
var FRONTMATTER_OPTIONS = {
|
|
266
|
-
engines: {
|
|
267
|
-
javascript: () => ({}),
|
|
268
|
-
js: () => ({})
|
|
269
|
-
}
|
|
270
|
-
};
|
|
270
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
271
271
|
function parseFrontmatter(raw) {
|
|
272
|
-
|
|
272
|
+
const source = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
|
|
273
|
+
const opening = /^---([^\r\n]*)\r?\n/.exec(source);
|
|
274
|
+
if (!opening || opening[1].startsWith("-")) return { content: source, data: {} };
|
|
275
|
+
const language = opening[1].trim().toLowerCase();
|
|
276
|
+
const remainder = source.slice(opening[0].length);
|
|
277
|
+
const closing = /^---[ \t]*\r?$/m.exec(remainder);
|
|
278
|
+
const matter = closing ? remainder.slice(0, closing.index) : remainder;
|
|
279
|
+
let content = closing ? remainder.slice(closing.index + closing[0].length) : "";
|
|
280
|
+
if (content.startsWith("\r\n")) content = content.slice(2);
|
|
281
|
+
else if (content.startsWith("\n")) content = content.slice(1);
|
|
282
|
+
if (matter.trim() === "" || !["", "yaml", "yml"].includes(language)) {
|
|
283
|
+
return { content, data: {} };
|
|
284
|
+
}
|
|
285
|
+
const parsed = parseYaml(matter);
|
|
286
|
+
return {
|
|
287
|
+
content,
|
|
288
|
+
data: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {}
|
|
289
|
+
};
|
|
273
290
|
}
|
|
274
291
|
function stringifyFrontmatter(body, data) {
|
|
275
|
-
|
|
292
|
+
const normalizedBody = body.endsWith("\n") ? body : `${body}
|
|
293
|
+
`;
|
|
294
|
+
const serialized = stringifyYaml(data, { lineWidth: 0 }).trimEnd();
|
|
295
|
+
return serialized === "{}" ? normalizedBody : `---
|
|
296
|
+
${serialized}
|
|
297
|
+
---
|
|
298
|
+
${normalizedBody}`;
|
|
276
299
|
}
|
|
277
300
|
|
|
278
301
|
// src/tools/update-page.ts
|
|
@@ -875,8 +898,16 @@ function collectNavPageIds(groups, seen, duplicates) {
|
|
|
875
898
|
}
|
|
876
899
|
function addOrphanToNav(projectDir, pageId) {
|
|
877
900
|
const config = readDocsJson(projectDir);
|
|
878
|
-
const tab = config.tabs.find((
|
|
879
|
-
if (!tab
|
|
901
|
+
const tab = config.tabs.find((candidate) => !candidate.href && !candidate.api && (candidate.pages?.length || candidate.groups?.length));
|
|
902
|
+
if (!tab) return;
|
|
903
|
+
if (tab.pages) {
|
|
904
|
+
if (!tab.pages.includes(pageId)) {
|
|
905
|
+
tab.pages.push(pageId);
|
|
906
|
+
writeDocsJson(projectDir, config);
|
|
907
|
+
}
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
if (!tab.groups) return;
|
|
880
911
|
const lastGroup = tab.groups[tab.groups.length - 1];
|
|
881
912
|
const existing = lastGroup.pages.filter((p) => typeof p === "string");
|
|
882
913
|
if (!existing.includes(pageId)) {
|
|
@@ -895,12 +926,14 @@ async function handleLintProject(input) {
|
|
|
895
926
|
const navPageIds = /* @__PURE__ */ new Set();
|
|
896
927
|
const duplicates = /* @__PURE__ */ new Set();
|
|
897
928
|
for (const tab of config.tabs) {
|
|
898
|
-
|
|
899
|
-
if (
|
|
929
|
+
const hasNavigationNodes = Boolean(tab.pages?.length || tab.groups?.length);
|
|
930
|
+
if ((tab.href || tab.api) && !hasNavigationNodes) continue;
|
|
931
|
+
if (!hasNavigationNodes) {
|
|
900
932
|
issues.push({ severity: "error", message: `Tab "${tab.tab}" has no groups and no href \u2014 it will render empty` });
|
|
901
933
|
continue;
|
|
902
934
|
}
|
|
903
|
-
collectNavPageIds(tab.
|
|
935
|
+
collectNavPageIds(tab.pages ?? [], navPageIds, duplicates);
|
|
936
|
+
collectNavPageIds(tab.groups ?? [], navPageIds, duplicates);
|
|
904
937
|
}
|
|
905
938
|
for (const dup of duplicates) {
|
|
906
939
|
issues.push({ severity: "error", message: `[duplicate] "${dup}" appears more than once in docs.json` });
|
|
@@ -994,7 +1027,7 @@ async function handleLintProject(input) {
|
|
|
994
1027
|
|
|
995
1028
|
// src/tools/translate-docs.ts
|
|
996
1029
|
import { z as z14 } from "zod";
|
|
997
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
1030
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
|
|
998
1031
|
import { join as join9, dirname as dirname2 } from "path";
|
|
999
1032
|
import Anthropic from "@anthropic-ai/sdk";
|
|
1000
1033
|
import pLimit from "p-limit";
|
|
@@ -1027,8 +1060,8 @@ function getAllPageIds(config) {
|
|
|
1027
1060
|
const seen = /* @__PURE__ */ new Set();
|
|
1028
1061
|
const hrefOnlyPages = [];
|
|
1029
1062
|
for (const tab of config.tabs) {
|
|
1030
|
-
if (tab.api && !tab.groups) continue;
|
|
1031
|
-
if (!tab.groups && tab.href) {
|
|
1063
|
+
if (tab.api && !tab.pages && !tab.groups) continue;
|
|
1064
|
+
if (!tab.pages && !tab.groups && tab.href) {
|
|
1032
1065
|
const pageId = tab.href.replace(/^\//, "");
|
|
1033
1066
|
if (pageId && !seen.has(pageId)) {
|
|
1034
1067
|
seen.add(pageId);
|
|
@@ -1037,13 +1070,10 @@ function getAllPageIds(config) {
|
|
|
1037
1070
|
}
|
|
1038
1071
|
continue;
|
|
1039
1072
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
seen.add(id);
|
|
1045
|
-
ids.push(id);
|
|
1046
|
-
}
|
|
1073
|
+
for (const id of collectPageIds([...tab.pages ?? [], ...tab.groups ?? []])) {
|
|
1074
|
+
if (!seen.has(id)) {
|
|
1075
|
+
seen.add(id);
|
|
1076
|
+
ids.push(id);
|
|
1047
1077
|
}
|
|
1048
1078
|
}
|
|
1049
1079
|
}
|
|
@@ -1141,7 +1171,7 @@ async function handleTranslateDocs(input) {
|
|
|
1141
1171
|
}
|
|
1142
1172
|
const translated = await translatePage(sourceContent, targetLocale.label, locale, model, client);
|
|
1143
1173
|
mkdirSync2(dirname2(targetFile), { recursive: true });
|
|
1144
|
-
|
|
1174
|
+
writeFileSync5(targetFile, translated + "\n", "utf8");
|
|
1145
1175
|
results.push({ pageId, success: true });
|
|
1146
1176
|
} catch (err) {
|
|
1147
1177
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1564,10 +1594,10 @@ async function handleReadApiSpec(input) {
|
|
|
1564
1594
|
}
|
|
1565
1595
|
|
|
1566
1596
|
// src/tools/update-api-spec.ts
|
|
1567
|
-
import { writeFileSync as
|
|
1597
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
1568
1598
|
import { extname as extname2 } from "path";
|
|
1569
1599
|
import { z as z17 } from "zod";
|
|
1570
|
-
import { parse as
|
|
1600
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1571
1601
|
var updateApiSpecSchema = z17.object({
|
|
1572
1602
|
projectDir: z17.string().describe("Path to the Thally project root"),
|
|
1573
1603
|
source: z17.string().optional().describe("Configured OpenAPI source; omit when the project has one"),
|
|
@@ -1575,7 +1605,7 @@ var updateApiSpecSchema = z17.object({
|
|
|
1575
1605
|
});
|
|
1576
1606
|
function parseDocument(source, content) {
|
|
1577
1607
|
try {
|
|
1578
|
-
return extname2(source).toLowerCase() === ".json" ? JSON.parse(content) :
|
|
1608
|
+
return extname2(source).toLowerCase() === ".json" ? JSON.parse(content) : parseYaml2(content);
|
|
1579
1609
|
} catch {
|
|
1580
1610
|
throw new Error(
|
|
1581
1611
|
`OpenAPI source is not valid ${extname2(source).toLowerCase() === ".json" ? "JSON" : "YAML"}.`
|
|
@@ -1590,7 +1620,7 @@ async function handleUpdateApiSpec(input) {
|
|
|
1590
1620
|
}
|
|
1591
1621
|
const content = input.content.endsWith("\n") ? input.content : `${input.content}
|
|
1592
1622
|
`;
|
|
1593
|
-
|
|
1623
|
+
writeFileSync6(source.path, content, "utf8");
|
|
1594
1624
|
return `\u2705 API source updated: ${source.source}`;
|
|
1595
1625
|
}
|
|
1596
1626
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thallylabs/mcp",
|
|
3
|
-
"version": "0.10.
|
|
4
|
-
"description": "MCP server for managing Thally
|
|
3
|
+
"version": "0.10.28",
|
|
4
|
+
"description": "MCP server for managing Thally docs and tracing product changes into evidence-backed updates for human review.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=18"
|
|
@@ -38,8 +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.
|
|
42
|
-
"gray-matter": "^4.0.3",
|
|
41
|
+
"create-thally-docs": "0.10.26",
|
|
43
42
|
"p-limit": "^6.1.0",
|
|
44
43
|
"yaml": "^2.8.2",
|
|
45
44
|
"zod": "^3.0.0"
|