sad-mcp 0.1.14 → 0.1.16
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 +13 -7
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -5,11 +5,15 @@ import { loadTextCache, saveTextEntry } from "./text-cache.js";
|
|
|
5
5
|
import { trackToolCall } from "./tracking.js";
|
|
6
6
|
// In-memory text cache for search (populated from disk cache + fresh extractions)
|
|
7
7
|
const textCache = new Map();
|
|
8
|
+
function isExamFile(file) {
|
|
9
|
+
return categorizeFile(file) === "exams";
|
|
10
|
+
}
|
|
8
11
|
async function ensureTextCache() {
|
|
9
12
|
if (textCache.size > 0)
|
|
10
13
|
return;
|
|
11
14
|
const files = await listAllFiles();
|
|
12
|
-
|
|
15
|
+
// Skip exam files during eager loading — they're extracted on demand via get_material
|
|
16
|
+
const extractableFiles = files.filter(f => isExtractable(f) && !isExamFile(f));
|
|
13
17
|
const diskCache = loadTextCache();
|
|
14
18
|
for (const file of extractableFiles) {
|
|
15
19
|
try {
|
|
@@ -84,7 +88,7 @@ export function registerToolHandlers(server) {
|
|
|
84
88
|
properties: {
|
|
85
89
|
name: {
|
|
86
90
|
type: "string",
|
|
87
|
-
description: "The file name (or partial
|
|
91
|
+
description: "The file name or path (or partial match) to retrieve. Use the path from search_materials results for exact matching (e.g., 'מבחנים-לסטודנטים/2024-א-א/מבחן.pdf').",
|
|
88
92
|
},
|
|
89
93
|
page: {
|
|
90
94
|
type: "number",
|
|
@@ -160,6 +164,7 @@ export function registerToolHandlers(server) {
|
|
|
160
164
|
if (matches.length > 0 || nameMatch) {
|
|
161
165
|
results.push({
|
|
162
166
|
fileName: file.name,
|
|
167
|
+
path: file.path,
|
|
163
168
|
category: categorizeFile(file),
|
|
164
169
|
matchCount: nameMatch ? matches.length + 100 : matches.length, // Boost file-name matches
|
|
165
170
|
preview: matches.length > 0
|
|
@@ -176,6 +181,7 @@ export function registerToolHandlers(server) {
|
|
|
176
181
|
if (file.name.toLowerCase().includes(queryLower) || file.path.toLowerCase().includes(queryLower)) {
|
|
177
182
|
results.push({
|
|
178
183
|
fileName: file.name,
|
|
184
|
+
path: file.path,
|
|
179
185
|
category: categorizeFile(file),
|
|
180
186
|
matchCount: 100,
|
|
181
187
|
preview: `(file name matches "${query}" — use get_material to read)`,
|
|
@@ -186,7 +192,7 @@ export function registerToolHandlers(server) {
|
|
|
186
192
|
results.sort((a, b) => b.matchCount - a.matchCount);
|
|
187
193
|
const responseText = results.length === 0
|
|
188
194
|
? `No results found for "${query}" in course materials.`
|
|
189
|
-
: `Found "${query}" in ${results.length} file(s). Use get_material to read the most relevant one(s):\n\n${results.map((r) => `- ${r.
|
|
195
|
+
: `Found "${query}" in ${results.length} file(s). Use get_material with the file path to read the most relevant one(s):\n\n${results.map((r) => `- ${r.path} [${r.category}] (${r.matchCount} matches) — "${r.preview}"`).join("\n")}`;
|
|
190
196
|
trackToolCall(name, toolArgs, { resultCount: results.length, success: results.length > 0, responseChars: responseText.length }, Date.now() - startTime);
|
|
191
197
|
return { content: [{ type: "text", text: responseText }] };
|
|
192
198
|
}
|
|
@@ -199,18 +205,18 @@ export function registerToolHandlers(server) {
|
|
|
199
205
|
}
|
|
200
206
|
await ensureTextCache();
|
|
201
207
|
const queryLower = queryName.toLowerCase();
|
|
202
|
-
// First: check text cache
|
|
208
|
+
// First: check text cache (match against both name and path)
|
|
203
209
|
let bestMatch = null;
|
|
204
210
|
for (const [, entry] of textCache) {
|
|
205
|
-
if (entry.file.name.toLowerCase().includes(queryLower)) {
|
|
211
|
+
if (entry.file.name.toLowerCase().includes(queryLower) || entry.file.path.toLowerCase().includes(queryLower)) {
|
|
206
212
|
bestMatch = entry;
|
|
207
213
|
break;
|
|
208
214
|
}
|
|
209
215
|
}
|
|
210
|
-
// Fallback: search all files by name and attempt fresh extraction
|
|
216
|
+
// Fallback: search all files by name/path and attempt fresh extraction
|
|
211
217
|
if (!bestMatch) {
|
|
212
218
|
const allFiles = await listAllFiles();
|
|
213
|
-
const matchedFile = allFiles.find(f => f.name.toLowerCase().includes(queryLower));
|
|
219
|
+
const matchedFile = allFiles.find(f => f.name.toLowerCase().includes(queryLower) || f.path.toLowerCase().includes(queryLower));
|
|
214
220
|
if (matchedFile && isExtractable(matchedFile)) {
|
|
215
221
|
try {
|
|
216
222
|
const buffer = await downloadFile(matchedFile);
|