sad-mcp 0.1.17 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tools.js +26 -40
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -12,12 +12,33 @@ function isExamFile(file) {
|
|
|
12
12
|
async function getExamList() {
|
|
13
13
|
const allFiles = await listAllFiles();
|
|
14
14
|
const examFiles = allFiles.filter(f => isExamFile(f) && isExtractable(f));
|
|
15
|
+
// Debug logging to stderr (visible in MCP server logs)
|
|
16
|
+
console.error(`[getExamList] allFiles=${allFiles.length}, examFiles=${examFiles.length}`);
|
|
17
|
+
for (const f of examFiles.slice(0, 10)) {
|
|
18
|
+
console.error(`[getExamList] path="${f.path}" name="${f.name}" mime="${f.mimeType}"`);
|
|
19
|
+
}
|
|
20
|
+
if (examFiles.length === 0) {
|
|
21
|
+
// Log files that ARE categorized as exams but fail isExtractable
|
|
22
|
+
const examOnly = allFiles.filter(f => isExamFile(f));
|
|
23
|
+
console.error(`[getExamList] files categorized as exams (before extractable filter): ${examOnly.length}`);
|
|
24
|
+
for (const f of examOnly.slice(0, 10)) {
|
|
25
|
+
console.error(`[getExamList] exam-cat: path="${f.path}" name="${f.name}" mime="${f.mimeType}"`);
|
|
26
|
+
}
|
|
27
|
+
// Log a few files that contain "מבחנ" in path
|
|
28
|
+
const withMbchn = allFiles.filter(f => f.path.includes("מבחנ"));
|
|
29
|
+
console.error(`[getExamList] files with מבחנ in path: ${withMbchn.length}`);
|
|
30
|
+
for (const f of withMbchn.slice(0, 10)) {
|
|
31
|
+
console.error(`[getExamList] מבחנ: path="${f.path}" name="${f.name}"`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
15
34
|
const examsMap = new Map();
|
|
16
35
|
for (const file of examFiles) {
|
|
17
36
|
// Match paths like "מבחנים-לסטודנטים/2024-א-א/מבחן.pdf"
|
|
18
37
|
const match = file.path.match(/(\d{4})-([\u0590-\u05FFa-z]+)-([\u0590-\u05FFa-z]+)\//i);
|
|
19
|
-
if (!match)
|
|
38
|
+
if (!match) {
|
|
39
|
+
console.error(`[getExamList] regex NO MATCH for path: "${file.path}"`);
|
|
20
40
|
continue;
|
|
41
|
+
}
|
|
21
42
|
const [, year, semester, moed] = match;
|
|
22
43
|
const id = `${year}-${semester}-${moed}`;
|
|
23
44
|
if (!examsMap.has(id)) {
|
|
@@ -188,7 +209,7 @@ export function registerToolHandlers(server) {
|
|
|
188
209
|
},
|
|
189
210
|
{
|
|
190
211
|
name: "practice_exam",
|
|
191
|
-
description: "Get a past exam for practice. Returns
|
|
212
|
+
description: "Get a past exam for practice. Returns a Google Drive link to the exam file so the student can download/view it. NEVER return the solution link — only provide it after the student explicitly asks to check their answers.",
|
|
192
213
|
inputSchema: {
|
|
193
214
|
type: "object",
|
|
194
215
|
properties: {
|
|
@@ -196,10 +217,6 @@ export function registerToolHandlers(server) {
|
|
|
196
217
|
type: "string",
|
|
197
218
|
description: "The exam identifier (e.g., '2024-א-א'). Get available IDs from list_exams.",
|
|
198
219
|
},
|
|
199
|
-
page: {
|
|
200
|
-
type: "number",
|
|
201
|
-
description: "Page number (1-indexed). Each page is ~5000 characters. Defaults to 1.",
|
|
202
|
-
},
|
|
203
220
|
user_question: {
|
|
204
221
|
type: "string",
|
|
205
222
|
description: "The student's original question exactly as they typed it. Always pass this for analytics.",
|
|
@@ -421,42 +438,11 @@ export function registerToolHandlers(server) {
|
|
|
421
438
|
trackToolCall(name, toolArgs, { success: false, responseChars: notFound.length }, Date.now() - startTime);
|
|
422
439
|
return { content: [{ type: "text", text: notFound }] };
|
|
423
440
|
}
|
|
424
|
-
|
|
425
|
-
let examText;
|
|
426
|
-
const cached = textCache.get(exam.examFile.id);
|
|
427
|
-
if (cached) {
|
|
428
|
-
examText = cached.text;
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
const diskCache = loadTextCache();
|
|
432
|
-
const diskEntry = diskCache[exam.examFile.id];
|
|
433
|
-
if (diskEntry && diskEntry.modifiedTime === exam.examFile.modifiedTime) {
|
|
434
|
-
examText = diskEntry.text;
|
|
435
|
-
textCache.set(exam.examFile.id, { file: exam.examFile, text: examText });
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
const buffer = await downloadFile(exam.examFile);
|
|
439
|
-
examText = await extractText(exam.examFile, buffer);
|
|
440
|
-
textCache.set(exam.examFile.id, { file: exam.examFile, text: examText });
|
|
441
|
-
saveTextEntry(exam.examFile.id, { modifiedTime: exam.examFile.modifiedTime, text: examText });
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
// Pagination
|
|
445
|
-
const PAGE_SIZE = 5000;
|
|
446
|
-
const page = Math.max(1, args.page || 1);
|
|
447
|
-
const totalChars = examText.length;
|
|
448
|
-
const totalPages = Math.ceil(totalChars / PAGE_SIZE);
|
|
449
|
-
const start = (page - 1) * PAGE_SIZE;
|
|
450
|
-
const end = Math.min(start + PAGE_SIZE, totalChars);
|
|
451
|
-
const pageText = examText.substring(start, end);
|
|
452
|
-
const header = `📝 מבחן ${examId} — Page ${page}/${totalPages} (${totalChars} chars total)`;
|
|
441
|
+
const examLink = `https://drive.google.com/file/d/${exam.examFile.id}/view`;
|
|
453
442
|
const solutionHint = exam.solutionFile
|
|
454
|
-
? `\n\n[Solution available —
|
|
443
|
+
? `\n\n[Solution available — ONLY share this link after the student explicitly asks to check their answers: https://drive.google.com/file/d/${exam.solutionFile.id}/view]`
|
|
455
444
|
: "\n\n[No solution available for this exam]";
|
|
456
|
-
const
|
|
457
|
-
? `\n\n[More questions available — call practice_exam with page: ${page + 1} to continue]`
|
|
458
|
-
: "";
|
|
459
|
-
const fullResponse = `${header}\n\n${pageText}${pageFooter}${solutionHint}`;
|
|
445
|
+
const fullResponse = `📝 מבחן ${examId} (שנה: ${exam.year}, סמסטר: ${exam.semester}, מועד: ${exam.moed})\n\nExam file: ${examLink}${solutionHint}`;
|
|
460
446
|
trackToolCall(name, toolArgs, { success: true, responseChars: fullResponse.length }, Date.now() - startTime);
|
|
461
447
|
return { content: [{ type: "text", text: fullResponse }] };
|
|
462
448
|
}
|