cfb-stats-mcp 0.1.0
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/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/cache.d.ts +4 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +17 -0
- package/dist/cache.js.map +1 -0
- package/dist/cfbd-client.d.ts +9 -0
- package/dist/cfbd-client.d.ts.map +1 -0
- package/dist/cfbd-client.js +49 -0
- package/dist/cfbd-client.js.map +1 -0
- package/dist/cfbd-client.test.d.ts +2 -0
- package/dist/cfbd-client.test.d.ts.map +1 -0
- package/dist/cfbd-client.test.js +37 -0
- package/dist/cfbd-client.test.js.map +1 -0
- package/dist/embedded-resources.d.ts +3 -0
- package/dist/embedded-resources.d.ts.map +1 -0
- package/dist/embedded-resources.js +300 -0
- package/dist/embedded-resources.js.map +1 -0
- package/dist/exports.d.ts +7 -0
- package/dist/exports.d.ts.map +1 -0
- package/dist/exports.js +33 -0
- package/dist/exports.js.map +1 -0
- package/dist/exports.test.d.ts +2 -0
- package/dist/exports.test.d.ts.map +1 -0
- package/dist/exports.test.js +80 -0
- package/dist/exports.test.js.map +1 -0
- package/dist/formatters/advanced-formatter.d.ts +3 -0
- package/dist/formatters/advanced-formatter.d.ts.map +1 -0
- package/dist/formatters/advanced-formatter.js +106 -0
- package/dist/formatters/advanced-formatter.js.map +1 -0
- package/dist/formatters/box-score-formatter.d.ts +3 -0
- package/dist/formatters/box-score-formatter.d.ts.map +1 -0
- package/dist/formatters/box-score-formatter.js +154 -0
- package/dist/formatters/box-score-formatter.js.map +1 -0
- package/dist/formatters/conference-ratings-formatter.d.ts +4 -0
- package/dist/formatters/conference-ratings-formatter.d.ts.map +1 -0
- package/dist/formatters/conference-ratings-formatter.js +62 -0
- package/dist/formatters/conference-ratings-formatter.js.map +1 -0
- package/dist/formatters/conference-ratings-formatter.test.d.ts +2 -0
- package/dist/formatters/conference-ratings-formatter.test.d.ts.map +1 -0
- package/dist/formatters/conference-ratings-formatter.test.js +153 -0
- package/dist/formatters/conference-ratings-formatter.test.js.map +1 -0
- package/dist/formatters/game-formatter.d.ts +3 -0
- package/dist/formatters/game-formatter.d.ts.map +1 -0
- package/dist/formatters/game-formatter.js +46 -0
- package/dist/formatters/game-formatter.js.map +1 -0
- package/dist/formatters/head-to-head-formatter.d.ts +3 -0
- package/dist/formatters/head-to-head-formatter.d.ts.map +1 -0
- package/dist/formatters/head-to-head-formatter.js +162 -0
- package/dist/formatters/head-to-head-formatter.js.map +1 -0
- package/dist/formatters/head-to-head-formatter.test.d.ts +2 -0
- package/dist/formatters/head-to-head-formatter.test.d.ts.map +1 -0
- package/dist/formatters/head-to-head-formatter.test.js +160 -0
- package/dist/formatters/head-to-head-formatter.test.js.map +1 -0
- package/dist/formatters/ratings-formatter.d.ts +3 -0
- package/dist/formatters/ratings-formatter.d.ts.map +1 -0
- package/dist/formatters/ratings-formatter.js +75 -0
- package/dist/formatters/ratings-formatter.js.map +1 -0
- package/dist/formatters/ratings-formatter.test.d.ts +2 -0
- package/dist/formatters/ratings-formatter.test.d.ts.map +1 -0
- package/dist/formatters/ratings-formatter.test.js +137 -0
- package/dist/formatters/ratings-formatter.test.js.map +1 -0
- package/dist/formatters/season-advanced-formatter.d.ts +3 -0
- package/dist/formatters/season-advanced-formatter.d.ts.map +1 -0
- package/dist/formatters/season-advanced-formatter.js +160 -0
- package/dist/formatters/season-advanced-formatter.js.map +1 -0
- package/dist/formatters/season-advanced-formatter.test.d.ts +2 -0
- package/dist/formatters/season-advanced-formatter.test.d.ts.map +1 -0
- package/dist/formatters/season-advanced-formatter.test.js +239 -0
- package/dist/formatters/season-advanced-formatter.test.js.map +1 -0
- package/dist/formatters/season-records-formatter.d.ts +3 -0
- package/dist/formatters/season-records-formatter.d.ts.map +1 -0
- package/dist/formatters/season-records-formatter.js +64 -0
- package/dist/formatters/season-records-formatter.js.map +1 -0
- package/dist/formatters/season-stats-formatter.d.ts +3 -0
- package/dist/formatters/season-stats-formatter.d.ts.map +1 -0
- package/dist/formatters/season-stats-formatter.js +156 -0
- package/dist/formatters/season-stats-formatter.js.map +1 -0
- package/dist/formatters/season-stats-formatter.test.d.ts +2 -0
- package/dist/formatters/season-stats-formatter.test.d.ts.map +1 -0
- package/dist/formatters/season-stats-formatter.test.js +76 -0
- package/dist/formatters/season-stats-formatter.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +16 -0
- package/dist/logger.js.map +1 -0
- package/dist/prompts.d.ts +3 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +242 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +3 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +12 -0
- package/dist/resources.js.map +1 -0
- package/dist/tools/advanced-stats.d.ts +3 -0
- package/dist/tools/advanced-stats.d.ts.map +1 -0
- package/dist/tools/advanced-stats.js +67 -0
- package/dist/tools/advanced-stats.js.map +1 -0
- package/dist/tools/box-score.d.ts +3 -0
- package/dist/tools/box-score.d.ts.map +1 -0
- package/dist/tools/box-score.js +50 -0
- package/dist/tools/box-score.js.map +1 -0
- package/dist/tools/build-glossary.d.ts +8 -0
- package/dist/tools/build-glossary.d.ts.map +1 -0
- package/dist/tools/build-glossary.js +125 -0
- package/dist/tools/build-glossary.js.map +1 -0
- package/dist/tools/build-glossary.test.d.ts +2 -0
- package/dist/tools/build-glossary.test.d.ts.map +1 -0
- package/dist/tools/build-glossary.test.js +182 -0
- package/dist/tools/build-glossary.test.js.map +1 -0
- package/dist/tools/conference-ratings.d.ts +3 -0
- package/dist/tools/conference-ratings.d.ts.map +1 -0
- package/dist/tools/conference-ratings.js +74 -0
- package/dist/tools/conference-ratings.js.map +1 -0
- package/dist/tools/export-subtitles.d.ts +23 -0
- package/dist/tools/export-subtitles.d.ts.map +1 -0
- package/dist/tools/export-subtitles.js +102 -0
- package/dist/tools/export-subtitles.js.map +1 -0
- package/dist/tools/export-subtitles.test.d.ts +2 -0
- package/dist/tools/export-subtitles.test.d.ts.map +1 -0
- package/dist/tools/export-subtitles.test.js +152 -0
- package/dist/tools/export-subtitles.test.js.map +1 -0
- package/dist/tools/games.d.ts +3 -0
- package/dist/tools/games.d.ts.map +1 -0
- package/dist/tools/games.js +64 -0
- package/dist/tools/games.js.map +1 -0
- package/dist/tools/head-to-head.d.ts +3 -0
- package/dist/tools/head-to-head.d.ts.map +1 -0
- package/dist/tools/head-to-head.js +74 -0
- package/dist/tools/head-to-head.js.map +1 -0
- package/dist/tools/ratings.d.ts +3 -0
- package/dist/tools/ratings.d.ts.map +1 -0
- package/dist/tools/ratings.js +63 -0
- package/dist/tools/ratings.js.map +1 -0
- package/dist/tools/read-transcript.d.ts +9 -0
- package/dist/tools/read-transcript.d.ts.map +1 -0
- package/dist/tools/read-transcript.js +72 -0
- package/dist/tools/read-transcript.js.map +1 -0
- package/dist/tools/read-transcript.test.d.ts +2 -0
- package/dist/tools/read-transcript.test.d.ts.map +1 -0
- package/dist/tools/read-transcript.test.js +94 -0
- package/dist/tools/read-transcript.test.js.map +1 -0
- package/dist/tools/season-advanced.d.ts +3 -0
- package/dist/tools/season-advanced.d.ts.map +1 -0
- package/dist/tools/season-advanced.js +103 -0
- package/dist/tools/season-advanced.js.map +1 -0
- package/dist/tools/season-records.d.ts +3 -0
- package/dist/tools/season-records.d.ts.map +1 -0
- package/dist/tools/season-records.js +51 -0
- package/dist/tools/season-records.js.map +1 -0
- package/dist/tools/season-stats.d.ts +3 -0
- package/dist/tools/season-stats.d.ts.map +1 -0
- package/dist/tools/season-stats.js +75 -0
- package/dist/tools/season-stats.js.map +1 -0
- package/dist/tools/status.d.ts +3 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/status.js +19 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/tools/transcript-types.d.ts +36 -0
- package/dist/tools/transcript-types.d.ts.map +1 -0
- package/dist/tools/transcript-types.js +2 -0
- package/dist/tools/transcript-types.js.map +1 -0
- package/dist/tools/write-chapters.d.ts +40 -0
- package/dist/tools/write-chapters.d.ts.map +1 -0
- package/dist/tools/write-chapters.js +143 -0
- package/dist/tools/write-chapters.js.map +1 -0
- package/dist/tools/write-chapters.test.d.ts +2 -0
- package/dist/tools/write-chapters.test.d.ts.map +1 -0
- package/dist/tools/write-chapters.test.js +167 -0
- package/dist/tools/write-chapters.test.js.map +1 -0
- package/dist/tools/write-clip-candidates.d.ts +30 -0
- package/dist/tools/write-clip-candidates.d.ts.map +1 -0
- package/dist/tools/write-clip-candidates.js +130 -0
- package/dist/tools/write-clip-candidates.js.map +1 -0
- package/dist/tools/write-clip-candidates.test.d.ts +2 -0
- package/dist/tools/write-clip-candidates.test.d.ts.map +1 -0
- package/dist/tools/write-clip-candidates.test.js +213 -0
- package/dist/tools/write-clip-candidates.test.js.map +1 -0
- package/dist/tools/write-description.d.ts +11 -0
- package/dist/tools/write-description.d.ts.map +1 -0
- package/dist/tools/write-description.js +71 -0
- package/dist/tools/write-description.js.map +1 -0
- package/dist/tools/write-description.test.d.ts +2 -0
- package/dist/tools/write-description.test.d.ts.map +1 -0
- package/dist/tools/write-description.test.js +42 -0
- package/dist/tools/write-description.test.js.map +1 -0
- package/dist/tools/write-transcript.d.ts +20 -0
- package/dist/tools/write-transcript.d.ts.map +1 -0
- package/dist/tools/write-transcript.js +133 -0
- package/dist/tools/write-transcript.js.map +1 -0
- package/dist/tools/write-transcript.test.d.ts +2 -0
- package/dist/tools/write-transcript.test.d.ts.map +1 -0
- package/dist/tools/write-transcript.test.js +133 -0
- package/dist/tools/write-transcript.test.js.map +1 -0
- package/package.json +59 -0
- package/resources/football-glossary.json +69 -0
- package/resources/workflow.md +228 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { logger } from "../logger.js";
|
|
4
|
+
/**
|
|
5
|
+
* Convert float seconds to SRT timestamp format "HH:MM:SS,mmm".
|
|
6
|
+
* Uses comma separator (not period) per SRT spec.
|
|
7
|
+
*/
|
|
8
|
+
export function formatSrtTimestamp(seconds) {
|
|
9
|
+
const totalMs = Math.round(seconds * 1000);
|
|
10
|
+
const ms = totalMs % 1000;
|
|
11
|
+
const totalSecs = Math.floor(totalMs / 1000);
|
|
12
|
+
const s = totalSecs % 60;
|
|
13
|
+
const totalMins = Math.floor(totalSecs / 60);
|
|
14
|
+
const m = totalMins % 60;
|
|
15
|
+
const h = Math.floor(totalMins / 60);
|
|
16
|
+
return (`${String(h).padStart(2, "0")}:` +
|
|
17
|
+
`${String(m).padStart(2, "0")}:` +
|
|
18
|
+
`${String(s).padStart(2, "0")},` +
|
|
19
|
+
`${String(ms).padStart(3, "0")}`);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Generate SRT subtitle string from a transcript.
|
|
23
|
+
* Uses segment-level timing (not word-level) per D4 decision.
|
|
24
|
+
* Returns empty string for transcripts with no segments.
|
|
25
|
+
*/
|
|
26
|
+
export function generateSrt(transcript) {
|
|
27
|
+
if (transcript.segments.length === 0) {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
const entries = transcript.segments.map((segment, index) => {
|
|
31
|
+
const num = index + 1;
|
|
32
|
+
const start = formatSrtTimestamp(segment.start);
|
|
33
|
+
const end = formatSrtTimestamp(segment.end);
|
|
34
|
+
return `${num}\n${start} --> ${end}\n${segment.text}`;
|
|
35
|
+
});
|
|
36
|
+
return entries.join("\n\n") + "\n";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read a corrected transcript JSON from disk, generate SRT, and write to file.
|
|
40
|
+
* Returns success/failure with descriptive message.
|
|
41
|
+
*/
|
|
42
|
+
export function exportSubtitles(transcriptPath, outputPath) {
|
|
43
|
+
try {
|
|
44
|
+
const raw = readFileSync(transcriptPath, "utf-8");
|
|
45
|
+
const transcript = JSON.parse(raw);
|
|
46
|
+
const srt = generateSrt(transcript);
|
|
47
|
+
writeFileSync(outputPath, srt, "utf-8");
|
|
48
|
+
const count = transcript.segments.length;
|
|
49
|
+
return {
|
|
50
|
+
success: true,
|
|
51
|
+
message: `Exported ${count} subtitle segments to ${outputPath}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
message: `Failed to export subtitles: ${error instanceof Error ? error.message : String(error)}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const inputSchema = {
|
|
62
|
+
transcript_path: z
|
|
63
|
+
.string()
|
|
64
|
+
.describe("Path to the corrected transcript JSON file"),
|
|
65
|
+
output_path: z.string().describe("Output path for the SRT file"),
|
|
66
|
+
};
|
|
67
|
+
export function registerExportSubtitlesTool(server) {
|
|
68
|
+
server.registerTool("export_subtitles", {
|
|
69
|
+
title: "Export Subtitles",
|
|
70
|
+
description: "Exports a corrected transcript as an SRT subtitle file for YouTube upload. " +
|
|
71
|
+
"Reads the transcript JSON and writes a .srt file with segment-level timestamps.",
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
73
|
+
inputSchema: inputSchema,
|
|
74
|
+
}, (args) => {
|
|
75
|
+
try {
|
|
76
|
+
const result = exportSubtitles(args.transcript_path, args.output_path);
|
|
77
|
+
logger.info("export_subtitles tool invoked", {
|
|
78
|
+
transcript_path: args.transcript_path,
|
|
79
|
+
output_path: args.output_path,
|
|
80
|
+
success: result.success,
|
|
81
|
+
});
|
|
82
|
+
if (!result.success) {
|
|
83
|
+
return {
|
|
84
|
+
content: [{ type: "text", text: result.message }],
|
|
85
|
+
isError: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
content: [{ type: "text", text: result.message }],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const message = `Failed to export subtitles: ${error instanceof Error ? error.message : String(error)}`;
|
|
94
|
+
logger.error("export_subtitles tool error", { error: message });
|
|
95
|
+
return {
|
|
96
|
+
content: [{ type: "text", text: message }],
|
|
97
|
+
isError: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=export-subtitles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-subtitles.js","sourceRoot":"","sources":["../../src/tools/export-subtitles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-subtitles.test.d.ts","sourceRoot":"","sources":["../../src/tools/export-subtitles.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { vi, describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
vi.mock("node:fs", () => ({
|
|
3
|
+
readFileSync: vi.fn(),
|
|
4
|
+
writeFileSync: vi.fn(),
|
|
5
|
+
}));
|
|
6
|
+
vi.mock("../logger.js", () => ({
|
|
7
|
+
logger: {
|
|
8
|
+
debug: vi.fn(),
|
|
9
|
+
info: vi.fn(),
|
|
10
|
+
warn: vi.fn(),
|
|
11
|
+
error: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
import { formatSrtTimestamp, generateSrt, exportSubtitles, } from "./export-subtitles.js";
|
|
15
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
16
|
+
const mockReadFileSync = vi.mocked(readFileSync);
|
|
17
|
+
const mockWriteFileSync = vi.mocked(writeFileSync);
|
|
18
|
+
function makeTranscript(overrides) {
|
|
19
|
+
const segments = overrides?.segments ?? [
|
|
20
|
+
{
|
|
21
|
+
start: 0.0,
|
|
22
|
+
end: 5.0,
|
|
23
|
+
text: "Hello world",
|
|
24
|
+
words: [
|
|
25
|
+
{
|
|
26
|
+
word: "Hello",
|
|
27
|
+
start: 0.0,
|
|
28
|
+
end: 0.5,
|
|
29
|
+
score: 0.95,
|
|
30
|
+
speaker: "SPEAKER_00",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
word: "world",
|
|
34
|
+
start: 0.6,
|
|
35
|
+
end: 1.0,
|
|
36
|
+
score: 0.98,
|
|
37
|
+
speaker: "SPEAKER_00",
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
speaker: "SPEAKER_00",
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
// Ensure all segments have words array
|
|
44
|
+
const normalizedSegments = segments.map((s) => ({
|
|
45
|
+
start: s.start,
|
|
46
|
+
end: s.end,
|
|
47
|
+
text: s.text,
|
|
48
|
+
words: s.words ?? [],
|
|
49
|
+
speaker: s.speaker ?? null,
|
|
50
|
+
}));
|
|
51
|
+
return {
|
|
52
|
+
version: "1.0",
|
|
53
|
+
language: "en",
|
|
54
|
+
segments: normalizedSegments,
|
|
55
|
+
chapters: [],
|
|
56
|
+
metadata: null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
describe("formatSrtTimestamp", () => {
|
|
60
|
+
it("formats zero seconds", () => {
|
|
61
|
+
expect(formatSrtTimestamp(0)).toBe("00:00:00,000");
|
|
62
|
+
});
|
|
63
|
+
it("formats fractional seconds with correct rounding", () => {
|
|
64
|
+
expect(formatSrtTimestamp(5.4)).toBe("00:00:05,400");
|
|
65
|
+
});
|
|
66
|
+
it("formats hours, minutes, seconds, and milliseconds", () => {
|
|
67
|
+
expect(formatSrtTimestamp(3723.15)).toBe("01:02:03,150");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("generateSrt", () => {
|
|
71
|
+
it("generates valid SRT from 2 segments", () => {
|
|
72
|
+
const transcript = makeTranscript({
|
|
73
|
+
segments: [
|
|
74
|
+
{
|
|
75
|
+
start: 0.0,
|
|
76
|
+
end: 5.0,
|
|
77
|
+
text: "Hello world",
|
|
78
|
+
words: [],
|
|
79
|
+
speaker: null,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
start: 5.0,
|
|
83
|
+
end: 10.5,
|
|
84
|
+
text: "Second segment here",
|
|
85
|
+
words: [],
|
|
86
|
+
speaker: null,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
const srt = generateSrt(transcript);
|
|
91
|
+
// 1-based indices
|
|
92
|
+
expect(srt).toContain("1\n");
|
|
93
|
+
expect(srt).toContain("2\n");
|
|
94
|
+
// Comma-separated timestamps with --> separator
|
|
95
|
+
expect(srt).toContain("00:00:00,000 --> 00:00:05,000");
|
|
96
|
+
expect(srt).toContain("00:00:05,000 --> 00:00:10,500");
|
|
97
|
+
// Segment text
|
|
98
|
+
expect(srt).toContain("Hello world");
|
|
99
|
+
expect(srt).toContain("Second segment here");
|
|
100
|
+
// Entries separated by blank lines
|
|
101
|
+
const entries = srt.trim().split("\n\n");
|
|
102
|
+
expect(entries).toHaveLength(2);
|
|
103
|
+
// Trailing newline
|
|
104
|
+
expect(srt.endsWith("\n")).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
it("returns empty string for 0 segments", () => {
|
|
107
|
+
const transcript = makeTranscript({ segments: [] });
|
|
108
|
+
expect(generateSrt(transcript)).toBe("");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe("exportSubtitles", () => {
|
|
112
|
+
beforeEach(() => {
|
|
113
|
+
vi.clearAllMocks();
|
|
114
|
+
});
|
|
115
|
+
it("reads transcript, generates SRT, and writes file", () => {
|
|
116
|
+
const transcript = makeTranscript({
|
|
117
|
+
segments: [
|
|
118
|
+
{
|
|
119
|
+
start: 0.0,
|
|
120
|
+
end: 5.0,
|
|
121
|
+
text: "Hello world",
|
|
122
|
+
words: [],
|
|
123
|
+
speaker: null,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
start: 5.0,
|
|
127
|
+
end: 10.0,
|
|
128
|
+
text: "Goodbye",
|
|
129
|
+
words: [],
|
|
130
|
+
speaker: null,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(transcript));
|
|
135
|
+
const result = exportSubtitles("/in/transcript.json", "/out/subtitles.srt");
|
|
136
|
+
expect(result.success).toBe(true);
|
|
137
|
+
expect(result.message).toContain("2");
|
|
138
|
+
expect(result.message).toContain("/out/subtitles.srt");
|
|
139
|
+
expect(mockReadFileSync).toHaveBeenCalledWith("/in/transcript.json", "utf-8");
|
|
140
|
+
expect(mockWriteFileSync).toHaveBeenCalledWith("/out/subtitles.srt", expect.any(String), "utf-8");
|
|
141
|
+
});
|
|
142
|
+
it("returns success false when transcript file does not exist", () => {
|
|
143
|
+
mockReadFileSync.mockImplementation(() => {
|
|
144
|
+
throw new Error("ENOENT: no such file or directory");
|
|
145
|
+
});
|
|
146
|
+
const result = exportSubtitles("/missing/transcript.json", "/out/subtitles.srt");
|
|
147
|
+
expect(result.success).toBe(false);
|
|
148
|
+
expect(result.message).toMatch(/ENOENT|no such file/i);
|
|
149
|
+
expect(mockWriteFileSync).not.toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
//# sourceMappingURL=export-subtitles.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-subtitles.test.js","sourceRoot":"","sources":["../../src/tools/export-subtitles.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"games.d.ts","sourceRoot":"","sources":["../../src/tools/games.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmCzE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAoDzD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getGames } from "cfbd";
|
|
3
|
+
import { cachedFetch } from "../cfbd-client.js";
|
|
4
|
+
import { formatGames } from "../formatters/game-formatter.js";
|
|
5
|
+
import { logger } from "../logger.js";
|
|
6
|
+
// Schema defined separately for readability. Type assertion needed because
|
|
7
|
+
// MCP SDK v1.27 resolves zod/v3 types from the hoisted zod v4 package,
|
|
8
|
+
// which are structurally identical but nominally distinct from the local
|
|
9
|
+
// zod v3 package's types. Runtime behavior is unaffected.
|
|
10
|
+
const inputSchema = {
|
|
11
|
+
team: z
|
|
12
|
+
.string()
|
|
13
|
+
.describe("Team school name (e.g. 'Alabama', 'Ohio State', 'Texas A&M')"),
|
|
14
|
+
year: z.number().describe("Season year (e.g. 2024)"),
|
|
15
|
+
week: z
|
|
16
|
+
.number()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Week number - omit for full season"),
|
|
19
|
+
seasonType: z
|
|
20
|
+
.enum(["regular", "postseason", "both"])
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Season type filter - defaults to 'both' for full picture"),
|
|
23
|
+
};
|
|
24
|
+
export function registerGamesTool(server) {
|
|
25
|
+
server.registerTool("cfbd_games", {
|
|
26
|
+
title: "Game Results",
|
|
27
|
+
description: "Look up college football game results by team and season. Returns scores, dates, venues, and Elo ratings.",
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
inputSchema: inputSchema,
|
|
30
|
+
}, async (args) => {
|
|
31
|
+
const { team, year, week } = args;
|
|
32
|
+
const resolvedSeasonType = args.seasonType ?? "both";
|
|
33
|
+
try {
|
|
34
|
+
const games = await cachedFetch("getGames", { year, team, week, seasonType: resolvedSeasonType }, () => getGames({
|
|
35
|
+
query: { year, team, week, seasonType: resolvedSeasonType },
|
|
36
|
+
}));
|
|
37
|
+
const formatted = formatGames(games, team);
|
|
38
|
+
logger.info("cfbd_games tool invoked", {
|
|
39
|
+
team,
|
|
40
|
+
year,
|
|
41
|
+
week,
|
|
42
|
+
seasonType: resolvedSeasonType,
|
|
43
|
+
gameCount: games.length,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: "text", text: formatted }],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const message = `Failed to fetch games: ${error instanceof Error ? error.message : String(error)}`;
|
|
51
|
+
logger.error("cfbd_games tool error", {
|
|
52
|
+
team,
|
|
53
|
+
year,
|
|
54
|
+
week,
|
|
55
|
+
error: message,
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text", text: message }],
|
|
59
|
+
isError: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=games.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"games.js","sourceRoot":"","sources":["../../src/tools/games.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAA8B,MAAM,MAAM,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,2EAA2E;AAC3E,uEAAuE;AACvE,yEAAyE;AACzE,0DAA0D;AAC1D,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,CACP,8DAA8D,CAC/D;IACH,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IACpD,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,oCAAoC,CAAC;IACjD,UAAU,EAAE,CAAC;SACV,IAAI,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;SACvC,QAAQ,EAAE;SACV,QAAQ,CAAC,0DAA0D,CAAC;CACxE,CAAC;AASF,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,CAAC,YAAY,CACjB,YAAY,EACZ;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,2GAA2G;QAC7G,8DAA8D;QAC9D,WAAW,EAAE,WAAkB;KAChC,EACD,KAAK,EAAE,IAAgB,EAAE,EAAE;QACzB,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;QAClC,MAAM,kBAAkB,GAAe,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC;QAEjE,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,WAAW,CAC7B,UAAU,EACV,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,EACpD,GAAG,EAAE,CACH,QAAQ,CAAC;gBACP,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE;aAC5D,CAAC,CACL,CAAC;YAEF,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE;gBACrC,IAAI;gBACJ,IAAI;gBACJ,IAAI;gBACJ,UAAU,EAAE,kBAAkB;gBAC9B,SAAS,EAAE,KAAK,CAAC,MAAM;aACxB,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;aACtD,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,0BAA0B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACnG,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE;gBACpC,IAAI;gBACJ,IAAI;gBACJ,IAAI;gBACJ,KAAK,EAAE,OAAO;aACf,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"head-to-head.d.ts","sourceRoot":"","sources":["../../src/tools/head-to-head.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoCzE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAoE9D"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getMatchup } from "cfbd";
|
|
3
|
+
import { cachedFetch } from "../cfbd-client.js";
|
|
4
|
+
import { formatHeadToHead } from "../formatters/head-to-head-formatter.js";
|
|
5
|
+
import { logger } from "../logger.js";
|
|
6
|
+
// See games.ts for why `as any` is needed on inputSchema.
|
|
7
|
+
const inputSchema = {
|
|
8
|
+
team1: z
|
|
9
|
+
.string()
|
|
10
|
+
.describe("First team school name (e.g. 'Alabama')"),
|
|
11
|
+
team2: z
|
|
12
|
+
.string()
|
|
13
|
+
.describe("Second team school name (e.g. 'Auburn')"),
|
|
14
|
+
startYear: z
|
|
15
|
+
.number()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Start of year range filter (e.g. 2017) \u2014 omit for all-time series"),
|
|
18
|
+
endYear: z
|
|
19
|
+
.number()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("End of year range filter (e.g. 2024) \u2014 omit for all-time series"),
|
|
22
|
+
};
|
|
23
|
+
export function registerHeadToHeadTool(server) {
|
|
24
|
+
server.registerTool("cfbd_head_to_head", {
|
|
25
|
+
title: "Head-to-Head Matchup History",
|
|
26
|
+
description: "Get historical head-to-head series data between two teams. Shows all-time series record, last 10 and last 5 results, and venue breakdowns. Use startYear/endYear to scope to a coaching era or specific period.",
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
inputSchema: inputSchema,
|
|
29
|
+
}, async (args) => {
|
|
30
|
+
const queryParams = {
|
|
31
|
+
team1: args.team1,
|
|
32
|
+
team2: args.team2,
|
|
33
|
+
};
|
|
34
|
+
if (args.startYear != null)
|
|
35
|
+
queryParams.minYear = args.startYear;
|
|
36
|
+
if (args.endYear != null)
|
|
37
|
+
queryParams.maxYear = args.endYear;
|
|
38
|
+
try {
|
|
39
|
+
const matchup = await cachedFetch("getMatchup", queryParams, () => getMatchup({
|
|
40
|
+
query: {
|
|
41
|
+
team1: args.team1,
|
|
42
|
+
team2: args.team2,
|
|
43
|
+
minYear: args.startYear,
|
|
44
|
+
maxYear: args.endYear,
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
const hasYearRange = args.startYear != null || args.endYear != null;
|
|
48
|
+
const formatted = formatHeadToHead(matchup, args.team1, args.team2, hasYearRange);
|
|
49
|
+
logger.info("cfbd_head_to_head tool invoked", {
|
|
50
|
+
team1: args.team1,
|
|
51
|
+
team2: args.team2,
|
|
52
|
+
startYear: args.startYear,
|
|
53
|
+
endYear: args.endYear,
|
|
54
|
+
gameCount: matchup.games.length,
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: formatted }],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const message = `Failed to fetch head-to-head data: ${error instanceof Error ? error.message : String(error)}`;
|
|
62
|
+
logger.error("cfbd_head_to_head tool error", {
|
|
63
|
+
team1: args.team1,
|
|
64
|
+
team2: args.team2,
|
|
65
|
+
error: message,
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: "text", text: message }],
|
|
69
|
+
isError: true,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=head-to-head.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"head-to-head.js","sourceRoot":"","sources":["../../src/tools/head-to-head.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratings.d.ts","sourceRoot":"","sources":["../../src/tools/ratings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAgCzE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+D3D"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getSp, getSrs, getElo, getFpi, } from "cfbd";
|
|
3
|
+
import { cachedFetch } from "../cfbd-client.js";
|
|
4
|
+
import { formatRatings } from "../formatters/ratings-formatter.js";
|
|
5
|
+
import { logger } from "../logger.js";
|
|
6
|
+
// See games.ts for why `as any` is needed on inputSchema.
|
|
7
|
+
const inputSchema = {
|
|
8
|
+
team: z
|
|
9
|
+
.string()
|
|
10
|
+
.describe("Team school name (e.g. 'Alabama', 'Ohio State')"),
|
|
11
|
+
year: z
|
|
12
|
+
.number()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("Season year (e.g. 2024) — defaults to current season"),
|
|
15
|
+
};
|
|
16
|
+
export function registerRatingsTool(server) {
|
|
17
|
+
server.registerTool("cfbd_ratings", {
|
|
18
|
+
title: "Team Ratings",
|
|
19
|
+
description: "Get multi-system ratings (SP+, SRS, Elo, FPI) for a team. Shows headline numbers: overall rating and ranking, offense/defense breakdowns where available. Use for comparing a team across rating systems or getting a comprehensive strength assessment.",
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
inputSchema: inputSchema,
|
|
22
|
+
}, async (args) => {
|
|
23
|
+
const year = args.year ?? new Date().getFullYear();
|
|
24
|
+
try {
|
|
25
|
+
const [spAll, srsAll, eloAll, fpiAll] = await Promise.all([
|
|
26
|
+
cachedFetch("getSp", { year }, () => getSp({ query: { year } })),
|
|
27
|
+
cachedFetch("getSrs", { year, team: args.team }, () => getSrs({ query: { year, team: args.team } })),
|
|
28
|
+
cachedFetch("getElo", { year, team: args.team }, () => getElo({ query: { year, team: args.team } })),
|
|
29
|
+
cachedFetch("getFpi", { year, team: args.team }, () => getFpi({ query: { year, team: args.team } })),
|
|
30
|
+
]);
|
|
31
|
+
const teamLower = args.team.toLowerCase();
|
|
32
|
+
const sp = spAll.find((r) => r.team.toLowerCase() === teamLower);
|
|
33
|
+
const srs = srsAll.find((r) => r.team.toLowerCase() === teamLower);
|
|
34
|
+
const elo = eloAll.find((r) => r.team.toLowerCase() === teamLower);
|
|
35
|
+
const fpi = fpiAll.find((r) => r.team.toLowerCase() === teamLower);
|
|
36
|
+
const formatted = formatRatings(sp, srs, elo, fpi, args.team, year);
|
|
37
|
+
logger.info("cfbd_ratings tool invoked", {
|
|
38
|
+
team: args.team,
|
|
39
|
+
year,
|
|
40
|
+
hasSp: !!sp,
|
|
41
|
+
hasSrs: !!srs,
|
|
42
|
+
hasElo: !!elo,
|
|
43
|
+
hasFpi: !!fpi,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: "text", text: formatted }],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const message = `Failed to fetch team ratings: ${error instanceof Error ? error.message : String(error)}`;
|
|
51
|
+
logger.error("cfbd_ratings tool error", {
|
|
52
|
+
team: args.team,
|
|
53
|
+
year,
|
|
54
|
+
error: message,
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: message }],
|
|
58
|
+
isError: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=ratings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratings.js","sourceRoot":"","sources":["../../src/tools/ratings.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,KAAK,EACL,MAAM,EACN,MAAM,EACN,MAAM,GAKP,MAAM,MAAM,CAAC;AACd,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,0DAA0D;AAC1D,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,CAAC,iDAAiD,CAAC;IAC9D,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,sDAAsD,CAAC;CACpE,CAAC;AAOF,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,0PAA0P;QAC5P,8DAA8D;QAC9D,WAAW,EAAE,WAAkB;KAChC,EACD,KAAK,EAAE,IAAkB,EAAE,EAAE;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAEnD,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACxD,WAAW,CAAW,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAC5C,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAC3B;gBACD,WAAW,CAAY,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAC/D,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAC7C;gBACD,WAAW,CAAY,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAC/D,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAC7C;gBACD,WAAW,CAAY,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAC/D,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAC7C;aACF,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1C,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,CAAC;YACjE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,CAAC;YACnE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,CAAC;YACnE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,CAAC;YAEnE,MAAM,SAAS,GAAG,aAAa,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACpE,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE;gBACvC,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI;gBACJ,KAAK,EAAE,CAAC,CAAC,EAAE;gBACX,MAAM,EAAE,CAAC,CAAC,GAAG;gBACb,MAAM,EAAE,CAAC,CAAC,GAAG;gBACb,MAAM,EAAE,CAAC,CAAC,GAAG;aACd,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;aACtD,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,iCAAiC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1G,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE;gBACtC,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI;gBACJ,KAAK,EAAE,OAAO;aACf,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { Transcript } from "./transcript-types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Read a transcript JSON file from disk and validate its structure.
|
|
5
|
+
* Exported separately from the MCP registration for direct testing.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readTranscript(filePath: string): Transcript;
|
|
8
|
+
export declare function registerReadTranscriptTool(server: McpServer): void;
|
|
9
|
+
//# sourceMappingURL=read-transcript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-transcript.d.ts","sourceRoot":"","sources":["../../src/tools/read-transcript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAQxD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CA4B3D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAqClE"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { logger } from "../logger.js";
|
|
4
|
+
const inputSchema = {
|
|
5
|
+
file_path: z
|
|
6
|
+
.string()
|
|
7
|
+
.describe("Absolute path to the transcript JSON file"),
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Read a transcript JSON file from disk and validate its structure.
|
|
11
|
+
* Exported separately from the MCP registration for direct testing.
|
|
12
|
+
*/
|
|
13
|
+
export function readTranscript(filePath) {
|
|
14
|
+
let raw;
|
|
15
|
+
try {
|
|
16
|
+
raw = readFileSync(filePath, "utf-8");
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
const code = err.code;
|
|
20
|
+
if (code === "ENOENT") {
|
|
21
|
+
throw new Error(`Transcript file not found: ${filePath}`);
|
|
22
|
+
}
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
let parsed;
|
|
26
|
+
try {
|
|
27
|
+
parsed = JSON.parse(raw);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new Error(`Invalid JSON in transcript file: ${filePath}`);
|
|
31
|
+
}
|
|
32
|
+
const transcript = parsed;
|
|
33
|
+
if (transcript.version !== "1.0") {
|
|
34
|
+
throw new Error(`Unsupported transcript version "${transcript.version}" (expected "1.0")`);
|
|
35
|
+
}
|
|
36
|
+
return transcript;
|
|
37
|
+
}
|
|
38
|
+
export function registerReadTranscriptTool(server) {
|
|
39
|
+
server.registerTool("read_transcript", {
|
|
40
|
+
title: "Read Transcript",
|
|
41
|
+
description: "Reads a transcript JSON file from disk and returns its content. " +
|
|
42
|
+
"Validates the transcript version is 1.0. Use this to load a transcript " +
|
|
43
|
+
"before starting proper noun correction.",
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
inputSchema: inputSchema,
|
|
46
|
+
}, (args) => {
|
|
47
|
+
try {
|
|
48
|
+
const transcript = readTranscript(args.file_path);
|
|
49
|
+
logger.info("read_transcript tool invoked", {
|
|
50
|
+
file_path: args.file_path,
|
|
51
|
+
segments: transcript.segments.length,
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: "text",
|
|
57
|
+
text: JSON.stringify(transcript, null, 2),
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const message = `Failed to read transcript: ${error instanceof Error ? error.message : String(error)}`;
|
|
64
|
+
logger.error("read_transcript tool error", { error: message });
|
|
65
|
+
return {
|
|
66
|
+
content: [{ type: "text", text: message }],
|
|
67
|
+
isError: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=read-transcript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-transcript.js","sourceRoot":"","sources":["../../src/tools/read-transcript.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAGtC,MAAM,WAAW,GAAG;IAClB,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,CAAC,2CAA2C,CAAC;CACzD,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,IAAI,GAAI,GAAyB,CAAC,IAAI,CAAC;QAC7C,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,UAAU,GAAG,MAAoB,CAAC;IAExC,IAAI,UAAU,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,mCAAmC,UAAU,CAAC,OAAO,oBAAoB,CAC1E,CAAC;IACJ,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,kEAAkE;YAClE,yEAAyE;YACzE,yCAAyC;QAC3C,8DAA8D;QAC9D,WAAW,EAAE,WAAkB;KAChC,EACD,CAAC,IAA2B,EAAE,EAAE;QAC9B,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAClD,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE;gBAC1C,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,MAAM;aACrC,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;qBAC1C;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,8BAA8B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACvG,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAC/D,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-transcript.test.d.ts","sourceRoot":"","sources":["../../src/tools/read-transcript.test.ts"],"names":[],"mappings":""}
|