@thallylabs/mcp 0.8.0 → 0.8.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/README.md +12 -5
- package/dist/index.js +53 -20
- package/dist/tools.js +53 -20
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# @thallylabs/mcp
|
|
2
2
|
|
|
3
3
|
A [Model Context Protocol](https://modelcontextprotocol.io) server that lets AI
|
|
4
|
-
tools — Claude Code, Claude Desktop, Cursor, Windsurf —
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
tools — Claude Code, Claude Desktop, Cursor, Windsurf — manage
|
|
5
|
+
[Thally](https://github.com/thallylabs/thally) knowledge surfaces through
|
|
6
|
+
natural language. Tools can create, read, update, search, and migrate
|
|
7
|
+
documentation, then trace a product-repository change into reviewable docs
|
|
8
|
+
work.
|
|
7
9
|
|
|
8
10
|
## Setup
|
|
9
11
|
|
|
@@ -28,17 +30,22 @@ Or in a `mcp.json` / client config:
|
|
|
28
30
|
|
|
29
31
|
## Tools
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
15 tools, including:
|
|
32
34
|
|
|
33
35
|
- **Authoring** — `create_project`, `add_page`, `update_page`, `read_page`, `list_pages`, `add_tab`
|
|
34
36
|
- **Context & search** — `get_context`, `search_docs`, `semantic_search` (against a deployed site)
|
|
35
37
|
- **Quality** — `lint_project`, `agent_readiness` (the Agent Readiness Score of a deployed site)
|
|
36
|
-
- **Migration** — `migrate_docs`, `translate_docs`
|
|
38
|
+
- **Migration** — `migrate_docs`, `import_docs`, `translate_docs`
|
|
39
|
+
- **Product changes** — `sync_from_repo`
|
|
37
40
|
|
|
38
41
|
`search_docs`, `read_page`, and `get_context` work against a local project on
|
|
39
42
|
disk; `semantic_search` and `agent_readiness` run against any **deployed** Thally
|
|
40
43
|
site over HTTP.
|
|
41
44
|
|
|
45
|
+
`migrate_docs` always scaffolds a fresh canonical Thally template before it
|
|
46
|
+
imports content. To preserve an existing Thally runtime and import content in
|
|
47
|
+
place, the caller must explicitly choose `import_docs`.
|
|
48
|
+
|
|
42
49
|
## License
|
|
43
50
|
|
|
44
51
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -511,7 +511,23 @@ async function handleListPages(input) {
|
|
|
511
511
|
import { z as z5 } from "zod";
|
|
512
512
|
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
513
513
|
import { join as join4 } from "path";
|
|
514
|
+
|
|
515
|
+
// src/lib/frontmatter.ts
|
|
514
516
|
import matter from "gray-matter";
|
|
517
|
+
var FRONTMATTER_OPTIONS = {
|
|
518
|
+
engines: {
|
|
519
|
+
javascript: () => ({}),
|
|
520
|
+
js: () => ({})
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
function parseFrontmatter(raw) {
|
|
524
|
+
return matter(raw, FRONTMATTER_OPTIONS);
|
|
525
|
+
}
|
|
526
|
+
function stringifyFrontmatter(body, data) {
|
|
527
|
+
return matter.stringify(body, data);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// src/tools/update-page.ts
|
|
515
531
|
var updatePageSchema = z5.object({
|
|
516
532
|
projectDir: z5.string().describe("Path to the Thally project root"),
|
|
517
533
|
pageId: z5.string().describe('Page identifier (e.g. "guides/auth"). No .mdx extension.'),
|
|
@@ -541,7 +557,7 @@ async function handleUpdatePage(input) {
|
|
|
541
557
|
);
|
|
542
558
|
}
|
|
543
559
|
const raw = readFileSync3(filePath, "utf8");
|
|
544
|
-
const parsed =
|
|
560
|
+
const parsed = parseFrontmatter(raw);
|
|
545
561
|
const newFm = { ...parsed.data };
|
|
546
562
|
if (input.title !== void 0) newFm["title"] = input.title;
|
|
547
563
|
if (input.description !== void 0) newFm["description"] = input.description;
|
|
@@ -549,7 +565,7 @@ async function handleUpdatePage(input) {
|
|
|
549
565
|
Object.assign(newFm, input.mergeFrontmatter);
|
|
550
566
|
}
|
|
551
567
|
const newBody = input.content !== void 0 ? stripEchoedPageHeader(input.content, pageId) : parsed.content;
|
|
552
|
-
const newContent =
|
|
568
|
+
const newContent = stringifyFrontmatter(newBody.trim(), newFm);
|
|
553
569
|
writeFileSync4(filePath, newContent, "utf8");
|
|
554
570
|
return [
|
|
555
571
|
`\u2705 Page updated: ${filePath}`,
|
|
@@ -563,22 +579,30 @@ async function handleUpdatePage(input) {
|
|
|
563
579
|
// src/tools/migrate-docs.ts
|
|
564
580
|
import { z as z6 } from "zod";
|
|
565
581
|
import { migrateDocs } from "create-thally-docs/migrate";
|
|
566
|
-
var
|
|
582
|
+
var migrationSourceShape = {
|
|
567
583
|
sourceUrl: z6.string().describe("GitHub repository URL or public documentation URL to migrate"),
|
|
568
|
-
projectDir: z6.string().describe("Path for new project or existing project dir"),
|
|
569
|
-
into: z6.boolean().optional().default(false).describe("Migrate into existing project instead of scaffolding"),
|
|
570
584
|
branch: z6.string().optional().describe("Git branch (default: auto-detect)"),
|
|
571
585
|
docsDir: z6.string().optional().describe("Docs subdirectory in repo (default: auto-detect)"),
|
|
572
586
|
apiKey: z6.string().optional().describe("Anthropic API key for non-Markdown file conversion"),
|
|
573
587
|
maxPages: z6.number().int().min(1).max(1e3).optional().describe("Maximum public URL pages to import"),
|
|
574
588
|
platform: z6.enum(["mintlify", "docusaurus"]).optional().describe("Source platform (default: auto-detect)")
|
|
589
|
+
};
|
|
590
|
+
var migrateDocsSchema = z6.object({
|
|
591
|
+
...migrationSourceShape,
|
|
592
|
+
projectDir: z6.string().describe("Path for the new canonical Thally project; the directory must be absent or empty")
|
|
575
593
|
});
|
|
576
|
-
|
|
594
|
+
var importDocsSchema = z6.object({
|
|
595
|
+
...migrationSourceShape,
|
|
596
|
+
projectDir: z6.string().describe("Path to an existing Thally project whose runtime should be preserved")
|
|
597
|
+
});
|
|
598
|
+
async function runMigration(input, isInPlaceImport) {
|
|
577
599
|
const apiKey = input.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
578
|
-
|
|
600
|
+
return migrateDocs({
|
|
579
601
|
sourceUrl: input.sourceUrl,
|
|
580
602
|
projectDir: input.projectDir,
|
|
581
|
-
|
|
603
|
+
// Keep this decision inside the adapter so stale or adversarial callers
|
|
604
|
+
// cannot turn a template-first migration into an in-place mutation.
|
|
605
|
+
into: isInPlaceImport,
|
|
582
606
|
apiKey,
|
|
583
607
|
branch: input.branch,
|
|
584
608
|
docsDir: input.docsDir,
|
|
@@ -586,14 +610,20 @@ async function handleMigrateDocs(input) {
|
|
|
586
610
|
platform: input.platform,
|
|
587
611
|
yes: true
|
|
588
612
|
});
|
|
589
|
-
|
|
613
|
+
}
|
|
614
|
+
async function handleMigrateDocs(input) {
|
|
615
|
+
const result = await runMigration(input, false);
|
|
616
|
+
return `Migration complete! Created a fresh Thally template at ${result.projectDir} and imported ${result.pagesWritten} pages.`;
|
|
617
|
+
}
|
|
618
|
+
async function handleImportDocs(input) {
|
|
619
|
+
const result = await runMigration(input, true);
|
|
620
|
+
return `Import complete! Imported ${result.pagesWritten} pages into the existing Thally project at ${result.projectDir}.`;
|
|
590
621
|
}
|
|
591
622
|
|
|
592
623
|
// src/tools/search-docs.ts
|
|
593
624
|
import { z as z7 } from "zod";
|
|
594
625
|
import { readdirSync as readdirSync2, statSync, readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
595
626
|
import { join as join5, relative, extname } from "path";
|
|
596
|
-
import matter2 from "gray-matter";
|
|
597
627
|
var searchDocsSchema = z7.object({
|
|
598
628
|
projectDir: z7.string().describe("Path to the Thally project root"),
|
|
599
629
|
query: z7.string().describe("Search query"),
|
|
@@ -629,7 +659,7 @@ function scoreFiles(files, contentDir, query) {
|
|
|
629
659
|
} catch {
|
|
630
660
|
continue;
|
|
631
661
|
}
|
|
632
|
-
const { data, content } =
|
|
662
|
+
const { data, content } = parseFrontmatter(raw);
|
|
633
663
|
const title = data.title ?? "";
|
|
634
664
|
const description = data.description ?? "";
|
|
635
665
|
const keywords = data.keywords ?? [];
|
|
@@ -748,7 +778,6 @@ async function handleAgentReadiness(input) {
|
|
|
748
778
|
import { z as z10 } from "zod";
|
|
749
779
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
750
780
|
import { join as join6 } from "path";
|
|
751
|
-
import matter3 from "gray-matter";
|
|
752
781
|
var readPageSchema = z10.object({
|
|
753
782
|
projectDir: z10.string().describe("Path to the Thally project root"),
|
|
754
783
|
pageId: z10.string().describe('Page ID, e.g. "guides/authentication"')
|
|
@@ -771,7 +800,7 @@ async function handleReadPage(input) {
|
|
|
771
800
|
throw new Error(`Page not found: "${pageId}". No file at src/content/${pageId}.mdx`);
|
|
772
801
|
}
|
|
773
802
|
const raw = readFileSync5(filePath, "utf8");
|
|
774
|
-
const { data, content } =
|
|
803
|
+
const { data, content } = parseFrontmatter(raw);
|
|
775
804
|
const title = data.title ?? pageId;
|
|
776
805
|
const description = data.description ?? "";
|
|
777
806
|
const lines = [`id: ${pageId}`, `title: ${title}`];
|
|
@@ -784,7 +813,6 @@ async function handleReadPage(input) {
|
|
|
784
813
|
import { z as z11 } from "zod";
|
|
785
814
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
786
815
|
import { join as join7 } from "path";
|
|
787
|
-
import matter4 from "gray-matter";
|
|
788
816
|
var getContextSchema = z11.object({
|
|
789
817
|
projectDir: z11.string().describe("Path to the Thally project root"),
|
|
790
818
|
topic: z11.string().describe("Topic or question to find relevant docs for"),
|
|
@@ -814,7 +842,7 @@ async function handleGetContext(input) {
|
|
|
814
842
|
for (const c of candidates) {
|
|
815
843
|
if (existsSync6(c)) {
|
|
816
844
|
const raw = readFileSync6(c, "utf8");
|
|
817
|
-
const { content: body } =
|
|
845
|
+
const { content: body } = parseFrontmatter(raw);
|
|
818
846
|
content = body.trim();
|
|
819
847
|
break;
|
|
820
848
|
}
|
|
@@ -840,7 +868,6 @@ async function handleGetContext(input) {
|
|
|
840
868
|
import { z as z12 } from "zod";
|
|
841
869
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
842
870
|
import { join as join8 } from "path";
|
|
843
|
-
import matter5 from "gray-matter";
|
|
844
871
|
var lintProjectSchema = z12.object({
|
|
845
872
|
projectDir: z12.string().describe("Path to the Thally project root"),
|
|
846
873
|
fix: z12.boolean().optional().default(false).describe("Auto-fix issues where possible (adds orphan pages to nav)")
|
|
@@ -923,7 +950,7 @@ async function handleLintProject(input) {
|
|
|
923
950
|
let content = "";
|
|
924
951
|
try {
|
|
925
952
|
const raw = readFileSync7(filePath, "utf8");
|
|
926
|
-
const parsed =
|
|
953
|
+
const parsed = parseFrontmatter(raw);
|
|
927
954
|
data = parsed.data;
|
|
928
955
|
content = parsed.content;
|
|
929
956
|
} catch {
|
|
@@ -981,7 +1008,6 @@ async function handleLintProject(input) {
|
|
|
981
1008
|
import { z as z13 } from "zod";
|
|
982
1009
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
983
1010
|
import { join as join9, dirname as dirname2 } from "path";
|
|
984
|
-
import matter6 from "gray-matter";
|
|
985
1011
|
import Anthropic from "@anthropic-ai/sdk";
|
|
986
1012
|
import pLimit from "p-limit";
|
|
987
1013
|
var translateDocsSchema = z13.object({
|
|
@@ -1121,7 +1147,7 @@ async function handleTranslateDocs(input) {
|
|
|
1121
1147
|
({ pageId, sourceFile, targetFile }) => limit(async () => {
|
|
1122
1148
|
try {
|
|
1123
1149
|
const sourceContent = readFileSync8(sourceFile, "utf8");
|
|
1124
|
-
const parsed =
|
|
1150
|
+
const parsed = parseFrontmatter(sourceContent);
|
|
1125
1151
|
if (!parsed.data.title) {
|
|
1126
1152
|
console.warn(`[translate] ${pageId}: missing title in frontmatter`);
|
|
1127
1153
|
}
|
|
@@ -1476,11 +1502,18 @@ var tools = [
|
|
|
1476
1502
|
}),
|
|
1477
1503
|
defineTool({
|
|
1478
1504
|
name: "migrate_docs",
|
|
1479
|
-
description: "
|
|
1505
|
+
description: "Create a fresh canonical Thally template, then migrate a GitHub repository or public docs site into it; the target must be new or empty",
|
|
1480
1506
|
scope: "project",
|
|
1481
1507
|
schema: migrateDocsSchema,
|
|
1482
1508
|
handler: handleMigrateDocs
|
|
1483
1509
|
}),
|
|
1510
|
+
defineTool({
|
|
1511
|
+
name: "import_docs",
|
|
1512
|
+
description: "Import content into an existing Thally project without scaffolding; use only when an in-place import is explicitly requested",
|
|
1513
|
+
scope: "project",
|
|
1514
|
+
schema: importDocsSchema,
|
|
1515
|
+
handler: handleImportDocs
|
|
1516
|
+
}),
|
|
1484
1517
|
defineTool({
|
|
1485
1518
|
name: "search_docs",
|
|
1486
1519
|
description: "Search documentation pages by keyword \u2014 returns ranked list of matching pages",
|
package/dist/tools.js
CHANGED
|
@@ -502,7 +502,23 @@ async function handleListPages(input) {
|
|
|
502
502
|
import { z as z5 } from "zod";
|
|
503
503
|
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
504
504
|
import { join as join4 } from "path";
|
|
505
|
+
|
|
506
|
+
// src/lib/frontmatter.ts
|
|
505
507
|
import matter from "gray-matter";
|
|
508
|
+
var FRONTMATTER_OPTIONS = {
|
|
509
|
+
engines: {
|
|
510
|
+
javascript: () => ({}),
|
|
511
|
+
js: () => ({})
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
function parseFrontmatter(raw) {
|
|
515
|
+
return matter(raw, FRONTMATTER_OPTIONS);
|
|
516
|
+
}
|
|
517
|
+
function stringifyFrontmatter(body, data) {
|
|
518
|
+
return matter.stringify(body, data);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/tools/update-page.ts
|
|
506
522
|
var updatePageSchema = z5.object({
|
|
507
523
|
projectDir: z5.string().describe("Path to the Thally project root"),
|
|
508
524
|
pageId: z5.string().describe('Page identifier (e.g. "guides/auth"). No .mdx extension.'),
|
|
@@ -532,7 +548,7 @@ async function handleUpdatePage(input) {
|
|
|
532
548
|
);
|
|
533
549
|
}
|
|
534
550
|
const raw = readFileSync3(filePath, "utf8");
|
|
535
|
-
const parsed =
|
|
551
|
+
const parsed = parseFrontmatter(raw);
|
|
536
552
|
const newFm = { ...parsed.data };
|
|
537
553
|
if (input.title !== void 0) newFm["title"] = input.title;
|
|
538
554
|
if (input.description !== void 0) newFm["description"] = input.description;
|
|
@@ -540,7 +556,7 @@ async function handleUpdatePage(input) {
|
|
|
540
556
|
Object.assign(newFm, input.mergeFrontmatter);
|
|
541
557
|
}
|
|
542
558
|
const newBody = input.content !== void 0 ? stripEchoedPageHeader(input.content, pageId) : parsed.content;
|
|
543
|
-
const newContent =
|
|
559
|
+
const newContent = stringifyFrontmatter(newBody.trim(), newFm);
|
|
544
560
|
writeFileSync4(filePath, newContent, "utf8");
|
|
545
561
|
return [
|
|
546
562
|
`\u2705 Page updated: ${filePath}`,
|
|
@@ -554,22 +570,30 @@ async function handleUpdatePage(input) {
|
|
|
554
570
|
// src/tools/migrate-docs.ts
|
|
555
571
|
import { z as z6 } from "zod";
|
|
556
572
|
import { migrateDocs } from "create-thally-docs/migrate";
|
|
557
|
-
var
|
|
573
|
+
var migrationSourceShape = {
|
|
558
574
|
sourceUrl: z6.string().describe("GitHub repository URL or public documentation URL to migrate"),
|
|
559
|
-
projectDir: z6.string().describe("Path for new project or existing project dir"),
|
|
560
|
-
into: z6.boolean().optional().default(false).describe("Migrate into existing project instead of scaffolding"),
|
|
561
575
|
branch: z6.string().optional().describe("Git branch (default: auto-detect)"),
|
|
562
576
|
docsDir: z6.string().optional().describe("Docs subdirectory in repo (default: auto-detect)"),
|
|
563
577
|
apiKey: z6.string().optional().describe("Anthropic API key for non-Markdown file conversion"),
|
|
564
578
|
maxPages: z6.number().int().min(1).max(1e3).optional().describe("Maximum public URL pages to import"),
|
|
565
579
|
platform: z6.enum(["mintlify", "docusaurus"]).optional().describe("Source platform (default: auto-detect)")
|
|
580
|
+
};
|
|
581
|
+
var migrateDocsSchema = z6.object({
|
|
582
|
+
...migrationSourceShape,
|
|
583
|
+
projectDir: z6.string().describe("Path for the new canonical Thally project; the directory must be absent or empty")
|
|
566
584
|
});
|
|
567
|
-
|
|
585
|
+
var importDocsSchema = z6.object({
|
|
586
|
+
...migrationSourceShape,
|
|
587
|
+
projectDir: z6.string().describe("Path to an existing Thally project whose runtime should be preserved")
|
|
588
|
+
});
|
|
589
|
+
async function runMigration(input, isInPlaceImport) {
|
|
568
590
|
const apiKey = input.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
569
|
-
|
|
591
|
+
return migrateDocs({
|
|
570
592
|
sourceUrl: input.sourceUrl,
|
|
571
593
|
projectDir: input.projectDir,
|
|
572
|
-
|
|
594
|
+
// Keep this decision inside the adapter so stale or adversarial callers
|
|
595
|
+
// cannot turn a template-first migration into an in-place mutation.
|
|
596
|
+
into: isInPlaceImport,
|
|
573
597
|
apiKey,
|
|
574
598
|
branch: input.branch,
|
|
575
599
|
docsDir: input.docsDir,
|
|
@@ -577,14 +601,20 @@ async function handleMigrateDocs(input) {
|
|
|
577
601
|
platform: input.platform,
|
|
578
602
|
yes: true
|
|
579
603
|
});
|
|
580
|
-
|
|
604
|
+
}
|
|
605
|
+
async function handleMigrateDocs(input) {
|
|
606
|
+
const result = await runMigration(input, false);
|
|
607
|
+
return `Migration complete! Created a fresh Thally template at ${result.projectDir} and imported ${result.pagesWritten} pages.`;
|
|
608
|
+
}
|
|
609
|
+
async function handleImportDocs(input) {
|
|
610
|
+
const result = await runMigration(input, true);
|
|
611
|
+
return `Import complete! Imported ${result.pagesWritten} pages into the existing Thally project at ${result.projectDir}.`;
|
|
581
612
|
}
|
|
582
613
|
|
|
583
614
|
// src/tools/search-docs.ts
|
|
584
615
|
import { z as z7 } from "zod";
|
|
585
616
|
import { readdirSync as readdirSync2, statSync, readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
586
617
|
import { join as join5, relative, extname } from "path";
|
|
587
|
-
import matter2 from "gray-matter";
|
|
588
618
|
var searchDocsSchema = z7.object({
|
|
589
619
|
projectDir: z7.string().describe("Path to the Thally project root"),
|
|
590
620
|
query: z7.string().describe("Search query"),
|
|
@@ -620,7 +650,7 @@ function scoreFiles(files, contentDir, query) {
|
|
|
620
650
|
} catch {
|
|
621
651
|
continue;
|
|
622
652
|
}
|
|
623
|
-
const { data, content } =
|
|
653
|
+
const { data, content } = parseFrontmatter(raw);
|
|
624
654
|
const title = data.title ?? "";
|
|
625
655
|
const description = data.description ?? "";
|
|
626
656
|
const keywords = data.keywords ?? [];
|
|
@@ -739,7 +769,6 @@ async function handleAgentReadiness(input) {
|
|
|
739
769
|
import { z as z10 } from "zod";
|
|
740
770
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
741
771
|
import { join as join6 } from "path";
|
|
742
|
-
import matter3 from "gray-matter";
|
|
743
772
|
var readPageSchema = z10.object({
|
|
744
773
|
projectDir: z10.string().describe("Path to the Thally project root"),
|
|
745
774
|
pageId: z10.string().describe('Page ID, e.g. "guides/authentication"')
|
|
@@ -762,7 +791,7 @@ async function handleReadPage(input) {
|
|
|
762
791
|
throw new Error(`Page not found: "${pageId}". No file at src/content/${pageId}.mdx`);
|
|
763
792
|
}
|
|
764
793
|
const raw = readFileSync5(filePath, "utf8");
|
|
765
|
-
const { data, content } =
|
|
794
|
+
const { data, content } = parseFrontmatter(raw);
|
|
766
795
|
const title = data.title ?? pageId;
|
|
767
796
|
const description = data.description ?? "";
|
|
768
797
|
const lines = [`id: ${pageId}`, `title: ${title}`];
|
|
@@ -775,7 +804,6 @@ async function handleReadPage(input) {
|
|
|
775
804
|
import { z as z11 } from "zod";
|
|
776
805
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
777
806
|
import { join as join7 } from "path";
|
|
778
|
-
import matter4 from "gray-matter";
|
|
779
807
|
var getContextSchema = z11.object({
|
|
780
808
|
projectDir: z11.string().describe("Path to the Thally project root"),
|
|
781
809
|
topic: z11.string().describe("Topic or question to find relevant docs for"),
|
|
@@ -805,7 +833,7 @@ async function handleGetContext(input) {
|
|
|
805
833
|
for (const c of candidates) {
|
|
806
834
|
if (existsSync6(c)) {
|
|
807
835
|
const raw = readFileSync6(c, "utf8");
|
|
808
|
-
const { content: body } =
|
|
836
|
+
const { content: body } = parseFrontmatter(raw);
|
|
809
837
|
content = body.trim();
|
|
810
838
|
break;
|
|
811
839
|
}
|
|
@@ -831,7 +859,6 @@ async function handleGetContext(input) {
|
|
|
831
859
|
import { z as z12 } from "zod";
|
|
832
860
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
833
861
|
import { join as join8 } from "path";
|
|
834
|
-
import matter5 from "gray-matter";
|
|
835
862
|
var lintProjectSchema = z12.object({
|
|
836
863
|
projectDir: z12.string().describe("Path to the Thally project root"),
|
|
837
864
|
fix: z12.boolean().optional().default(false).describe("Auto-fix issues where possible (adds orphan pages to nav)")
|
|
@@ -914,7 +941,7 @@ async function handleLintProject(input) {
|
|
|
914
941
|
let content = "";
|
|
915
942
|
try {
|
|
916
943
|
const raw = readFileSync7(filePath, "utf8");
|
|
917
|
-
const parsed =
|
|
944
|
+
const parsed = parseFrontmatter(raw);
|
|
918
945
|
data = parsed.data;
|
|
919
946
|
content = parsed.content;
|
|
920
947
|
} catch {
|
|
@@ -972,7 +999,6 @@ async function handleLintProject(input) {
|
|
|
972
999
|
import { z as z13 } from "zod";
|
|
973
1000
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
974
1001
|
import { join as join9, dirname as dirname2 } from "path";
|
|
975
|
-
import matter6 from "gray-matter";
|
|
976
1002
|
import Anthropic from "@anthropic-ai/sdk";
|
|
977
1003
|
import pLimit from "p-limit";
|
|
978
1004
|
var translateDocsSchema = z13.object({
|
|
@@ -1112,7 +1138,7 @@ async function handleTranslateDocs(input) {
|
|
|
1112
1138
|
({ pageId, sourceFile, targetFile }) => limit(async () => {
|
|
1113
1139
|
try {
|
|
1114
1140
|
const sourceContent = readFileSync8(sourceFile, "utf8");
|
|
1115
|
-
const parsed =
|
|
1141
|
+
const parsed = parseFrontmatter(sourceContent);
|
|
1116
1142
|
if (!parsed.data.title) {
|
|
1117
1143
|
console.warn(`[translate] ${pageId}: missing title in frontmatter`);
|
|
1118
1144
|
}
|
|
@@ -1467,11 +1493,18 @@ var tools = [
|
|
|
1467
1493
|
}),
|
|
1468
1494
|
defineTool({
|
|
1469
1495
|
name: "migrate_docs",
|
|
1470
|
-
description: "
|
|
1496
|
+
description: "Create a fresh canonical Thally template, then migrate a GitHub repository or public docs site into it; the target must be new or empty",
|
|
1471
1497
|
scope: "project",
|
|
1472
1498
|
schema: migrateDocsSchema,
|
|
1473
1499
|
handler: handleMigrateDocs
|
|
1474
1500
|
}),
|
|
1501
|
+
defineTool({
|
|
1502
|
+
name: "import_docs",
|
|
1503
|
+
description: "Import content into an existing Thally project without scaffolding; use only when an in-place import is explicitly requested",
|
|
1504
|
+
scope: "project",
|
|
1505
|
+
schema: importDocsSchema,
|
|
1506
|
+
handler: handleImportDocs
|
|
1507
|
+
}),
|
|
1475
1508
|
defineTool({
|
|
1476
1509
|
name: "search_docs",
|
|
1477
1510
|
description: "Search documentation pages by keyword \u2014 returns ranked list of matching pages",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thallylabs/mcp",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "MCP server for
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"description": "MCP server for managing Thally knowledge surfaces and tracing product changes into documentation work.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=18"
|