tracegist-mcp-bridge 0.1.4 → 0.2.2
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 +6 -6
- package/bin/tracegist-mcp-bridge.mjs +104 -80
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,13 +48,13 @@ Set `TRACEGIST_DIR` to change where the bridge looks for packages (defaults to `
|
|
|
48
48
|
|
|
49
49
|
Each session ZIP may include:
|
|
50
50
|
|
|
51
|
-
| Path
|
|
52
|
-
|
|
53
|
-
| `*-coding-agent-handoff.md`
|
|
54
|
-
| `*-manifest.json`
|
|
55
|
-
| `*-playwright-repro.spec.ts` | Auto-generated Playwright repro script skeleton
|
|
51
|
+
| Path | Description |
|
|
52
|
+
| ---------------------------- | -------------------------------------------------------------------------------------- |
|
|
53
|
+
| `*-coding-agent-handoff.md` | Structured handoff for the coding agent |
|
|
54
|
+
| `*-manifest.json` | Session metadata and file index |
|
|
55
|
+
| `*-playwright-repro.spec.ts` | Auto-generated Playwright repro script skeleton |
|
|
56
56
|
| `network/api-requests.jsonl` | API request/response bodies (opt-in, requires Deep Diagnostics + body capture enabled) |
|
|
57
|
-
| `markers/marker-NN-*/`
|
|
57
|
+
| `markers/marker-NN-*/` | Per-marker screenshots, voice notes, highlight captures |
|
|
58
58
|
|
|
59
59
|
## Prerequisites for transcription
|
|
60
60
|
|
|
@@ -11,14 +11,14 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
11
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
12
|
|
|
13
13
|
const BRIDGE_NAME = "tracegist-mcp-bridge";
|
|
14
|
-
const BRIDGE_VERSION =
|
|
14
|
+
const { version: BRIDGE_VERSION } = JSON.parse(
|
|
15
|
+
await fs.readFile(new URL("../package.json", import.meta.url), "utf8"),
|
|
16
|
+
);
|
|
15
17
|
|
|
16
18
|
function resolveDefaultDirectory() {
|
|
17
19
|
const envDir = process.env.TRACEGIST_DIR;
|
|
18
20
|
if (envDir) {
|
|
19
|
-
const resolved = envDir.startsWith("~")
|
|
20
|
-
? path.join(os.homedir(), envDir.slice(1))
|
|
21
|
-
: envDir;
|
|
21
|
+
const resolved = envDir.startsWith("~") ? path.join(os.homedir(), envDir.slice(1)) : envDir;
|
|
22
22
|
return path.resolve(resolved);
|
|
23
23
|
}
|
|
24
24
|
return path.join(os.homedir(), "Downloads");
|
|
@@ -126,7 +126,9 @@ async function readZipEntries(zipPath) {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
async function tryReadManifest(zip) {
|
|
129
|
-
const manifestEntry = Object.values(zip.files).find((entry) =>
|
|
129
|
+
const manifestEntry = Object.values(zip.files).find((entry) =>
|
|
130
|
+
entry.name.endsWith("-manifest.json"),
|
|
131
|
+
);
|
|
130
132
|
if (!manifestEntry) return { manifest: null, manifestEntryName: null };
|
|
131
133
|
const manifestText = await manifestEntry.async("text");
|
|
132
134
|
try {
|
|
@@ -147,9 +149,10 @@ function normalizeZipBaseName(entryName) {
|
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
async function tryReadHandoffMarkdown(zip, entryNames, manifest = null) {
|
|
150
|
-
const manifestNamedHandoff =
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
const manifestNamedHandoff =
|
|
153
|
+
typeof manifest?.handoffMarkdownFilename === "string"
|
|
154
|
+
? normalizeZipBaseName(manifest.handoffMarkdownFilename)
|
|
155
|
+
: null;
|
|
153
156
|
const byBaseName = new Map();
|
|
154
157
|
for (const name of entryNames) {
|
|
155
158
|
byBaseName.set(normalizeZipBaseName(name), name);
|
|
@@ -160,11 +163,12 @@ async function tryReadHandoffMarkdown(zip, entryNames, manifest = null) {
|
|
|
160
163
|
selectedEntryName = byBaseName.get(manifestNamedHandoff) || null;
|
|
161
164
|
}
|
|
162
165
|
if (!selectedEntryName) {
|
|
163
|
-
selectedEntryName =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
selectedEntryName =
|
|
167
|
+
entryNames.find((name) => {
|
|
168
|
+
const baseName = normalizeZipBaseName(name);
|
|
169
|
+
if (HANDOFF_SUFFIXES.some((suffix) => baseName.endsWith(suffix))) return true;
|
|
170
|
+
return /handoff.*\.md$/i.test(baseName);
|
|
171
|
+
}) || null;
|
|
168
172
|
}
|
|
169
173
|
if (!selectedEntryName) {
|
|
170
174
|
return { handoffMarkdown: null, handoffEntryName: null, debugCandidates: [] };
|
|
@@ -354,10 +358,7 @@ async function injectTranscriptsIntoMarkdown(markdown, zip, entryNames) {
|
|
|
354
358
|
} else {
|
|
355
359
|
const hint = whisperDependencyWarnings.join(" | ");
|
|
356
360
|
for (const shortId of voiceByShortId.keys()) {
|
|
357
|
-
transcriptByShortId.set(
|
|
358
|
-
shortId,
|
|
359
|
-
`[TRANSCRIPTION REQUIRED — Whisper not available: ${hint}]`,
|
|
360
|
-
);
|
|
361
|
+
transcriptByShortId.set(shortId, `[TRANSCRIPTION REQUIRED — Whisper not available: ${hint}]`);
|
|
361
362
|
}
|
|
362
363
|
}
|
|
363
364
|
|
|
@@ -414,7 +415,7 @@ server.registerPrompt(
|
|
|
414
415
|
"",
|
|
415
416
|
"1. `list_tracegist_packages` — find available packages (searches TRACEGIST_DIR env var, or ~/Downloads by default).",
|
|
416
417
|
"2. `get_tracegist_handoff_markdown` — call without a `section` parameter to get a table of contents",
|
|
417
|
-
|
|
418
|
+
' showing all sections and their sizes. Then request specific sections by name (e.g. `section: "Marker Timeline"`).',
|
|
418
419
|
' Use `section: "all"` to load the full document at once (can be large for deep exports).',
|
|
419
420
|
" **Voice note transcripts are injected automatically** into the marker sections — no separate step needed.",
|
|
420
421
|
" If Whisper is unavailable, a placeholder is injected with setup instructions.",
|
|
@@ -462,23 +463,25 @@ server.registerTool(
|
|
|
462
463
|
async ({ zipPath, model = "base", language }) => {
|
|
463
464
|
if (whisperDependencyWarnings.length > 0) {
|
|
464
465
|
return {
|
|
465
|
-
content: [
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
466
|
+
content: [
|
|
467
|
+
{
|
|
468
|
+
type: "text",
|
|
469
|
+
text: JSON.stringify(
|
|
470
|
+
{
|
|
471
|
+
zipPath,
|
|
472
|
+
model,
|
|
473
|
+
language: language || null,
|
|
474
|
+
transcriptions: [],
|
|
475
|
+
failures: [],
|
|
476
|
+
warning:
|
|
477
|
+
"Local Whisper dependencies are missing. Fix the dependency warnings and retry transcription.",
|
|
478
|
+
dependencyWarnings: whisperDependencyWarnings,
|
|
479
|
+
},
|
|
480
|
+
null,
|
|
481
|
+
2,
|
|
482
|
+
),
|
|
483
|
+
},
|
|
484
|
+
],
|
|
482
485
|
isError: true,
|
|
483
486
|
};
|
|
484
487
|
}
|
|
@@ -487,20 +490,22 @@ server.registerTool(
|
|
|
487
490
|
const voiceEntries = entryNames.filter((entryName) => looksLikeVoiceNote(entryName));
|
|
488
491
|
if (voiceEntries.length === 0) {
|
|
489
492
|
return {
|
|
490
|
-
content: [
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
493
|
+
content: [
|
|
494
|
+
{
|
|
495
|
+
type: "text",
|
|
496
|
+
text: JSON.stringify(
|
|
497
|
+
{
|
|
498
|
+
zipPath,
|
|
499
|
+
model,
|
|
500
|
+
language: language || null,
|
|
501
|
+
transcriptions: [],
|
|
502
|
+
warning: "No voice-note files found in package.",
|
|
503
|
+
},
|
|
504
|
+
null,
|
|
505
|
+
2,
|
|
506
|
+
),
|
|
507
|
+
},
|
|
508
|
+
],
|
|
504
509
|
};
|
|
505
510
|
}
|
|
506
511
|
|
|
@@ -562,7 +567,8 @@ server.registerTool(
|
|
|
562
567
|
server.registerTool(
|
|
563
568
|
"list_tracegist_packages",
|
|
564
569
|
{
|
|
565
|
-
description:
|
|
570
|
+
description:
|
|
571
|
+
"List TraceGist marker package zip files. Searches TRACEGIST_DIR (env var), falls back to ~/Downloads, or uses the given directory.",
|
|
566
572
|
inputSchema: z.object({
|
|
567
573
|
directory: z.string().optional(),
|
|
568
574
|
limit: z.number().int().min(1).max(200).optional(),
|
|
@@ -591,7 +597,11 @@ server.registerTool(
|
|
|
591
597
|
async ({ zipPath }) => {
|
|
592
598
|
const { zip, entryNames } = await readZipEntries(zipPath);
|
|
593
599
|
const { manifest, manifestEntryName } = await tryReadManifest(zip);
|
|
594
|
-
const { handoffMarkdown, handoffEntryName } = await tryReadHandoffMarkdown(
|
|
600
|
+
const { handoffMarkdown, handoffEntryName } = await tryReadHandoffMarkdown(
|
|
601
|
+
zip,
|
|
602
|
+
entryNames,
|
|
603
|
+
manifest,
|
|
604
|
+
);
|
|
595
605
|
|
|
596
606
|
const overview = {
|
|
597
607
|
zipPath,
|
|
@@ -637,17 +647,19 @@ server.registerTool(
|
|
|
637
647
|
.filter((name) => /handoff|coding-agent/i.test(path.posix.basename(name)))
|
|
638
648
|
.slice(0, 5);
|
|
639
649
|
return {
|
|
640
|
-
content: [
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
650
|
+
content: [
|
|
651
|
+
{
|
|
652
|
+
type: "text",
|
|
653
|
+
text: [
|
|
654
|
+
`No handoff markdown entry found in ${zipPath}.`,
|
|
655
|
+
manifest?.handoffMarkdownFilename
|
|
656
|
+
? `Manifest handoff filename: ${manifest.handoffMarkdownFilename}`
|
|
657
|
+
: "Manifest handoff filename: (missing)",
|
|
658
|
+
`Matching entry candidates: ${hintedEntries.length > 0 ? hintedEntries.join(", ") : "(none)"}`,
|
|
659
|
+
`Debug candidates: ${debugCandidates.length > 0 ? debugCandidates.join(", ") : "(none)"}`,
|
|
660
|
+
].join("\n"),
|
|
661
|
+
},
|
|
662
|
+
],
|
|
651
663
|
isError: true,
|
|
652
664
|
};
|
|
653
665
|
}
|
|
@@ -657,10 +669,12 @@ server.registerTool(
|
|
|
657
669
|
// Full document retrieval
|
|
658
670
|
if (section && section.toLowerCase() === "all") {
|
|
659
671
|
return {
|
|
660
|
-
content: [
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
672
|
+
content: [
|
|
673
|
+
{
|
|
674
|
+
type: "text",
|
|
675
|
+
text: `# Source: ${handoffEntryName}\n\n${enrichedMarkdown}`,
|
|
676
|
+
},
|
|
677
|
+
],
|
|
664
678
|
};
|
|
665
679
|
}
|
|
666
680
|
|
|
@@ -669,10 +683,12 @@ server.registerTool(
|
|
|
669
683
|
// No section requested — return table of contents
|
|
670
684
|
if (!section) {
|
|
671
685
|
return {
|
|
672
|
-
content: [
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
686
|
+
content: [
|
|
687
|
+
{
|
|
688
|
+
type: "text",
|
|
689
|
+
text: `# Source: ${handoffEntryName}\n\n${renderSectionToc(sections, zipPath)}`,
|
|
690
|
+
},
|
|
691
|
+
],
|
|
676
692
|
};
|
|
677
693
|
}
|
|
678
694
|
|
|
@@ -682,18 +698,22 @@ server.registerTool(
|
|
|
682
698
|
if (!match) {
|
|
683
699
|
const available = sections.map((s) => s.name).join(", ");
|
|
684
700
|
return {
|
|
685
|
-
content: [
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
701
|
+
content: [
|
|
702
|
+
{
|
|
703
|
+
type: "text",
|
|
704
|
+
text: `Section "${section}" not found. Available sections: ${available}`,
|
|
705
|
+
},
|
|
706
|
+
],
|
|
689
707
|
isError: true,
|
|
690
708
|
};
|
|
691
709
|
}
|
|
692
710
|
return {
|
|
693
|
-
content: [
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
711
|
+
content: [
|
|
712
|
+
{
|
|
713
|
+
type: "text",
|
|
714
|
+
text: match.content,
|
|
715
|
+
},
|
|
716
|
+
],
|
|
697
717
|
};
|
|
698
718
|
},
|
|
699
719
|
);
|
|
@@ -749,7 +769,9 @@ server.registerTool(
|
|
|
749
769
|
const outputPath = path.resolve(outDir, path.basename(entryName));
|
|
750
770
|
if (!outputPath.startsWith(outDir + path.sep) && outputPath !== outDir) {
|
|
751
771
|
return {
|
|
752
|
-
content: [
|
|
772
|
+
content: [
|
|
773
|
+
{ type: "text", text: `Refusing to write outside output directory: ${outputPath}` },
|
|
774
|
+
],
|
|
753
775
|
isError: true,
|
|
754
776
|
};
|
|
755
777
|
}
|
|
@@ -763,7 +785,9 @@ server.registerTool(
|
|
|
763
785
|
);
|
|
764
786
|
|
|
765
787
|
whisperDependencyWarnings = await checkWhisperDependencies();
|
|
766
|
-
console.error(
|
|
788
|
+
console.error(
|
|
789
|
+
`[${BRIDGE_NAME}] Default package directory: ${DEFAULT_DOWNLOADS_DIR}${process.env.TRACEGIST_DIR ? " (from TRACEGIST_DIR)" : ""}`,
|
|
790
|
+
);
|
|
767
791
|
if (whisperDependencyWarnings.length > 0) {
|
|
768
792
|
for (const warning of whisperDependencyWarnings) {
|
|
769
793
|
console.error(`[${BRIDGE_NAME}] WARNING: ${warning}`);
|