sad-mcp 0.1.7 → 0.1.8
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 +62 -11
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -46,7 +46,7 @@ export function registerToolHandlers(server) {
|
|
|
46
46
|
tools: [
|
|
47
47
|
{
|
|
48
48
|
name: "search_materials",
|
|
49
|
-
description: "Search across all course materials (
|
|
49
|
+
description: "Search across all course materials for a topic. Returns a SHORT summary list of matching files (name, category, match count). To read the actual content, use get_material on the most relevant file(s) from the results.",
|
|
50
50
|
inputSchema: {
|
|
51
51
|
type: "object",
|
|
52
52
|
properties: {
|
|
@@ -58,6 +58,20 @@ export function registerToolHandlers(server) {
|
|
|
58
58
|
required: ["query"],
|
|
59
59
|
},
|
|
60
60
|
},
|
|
61
|
+
{
|
|
62
|
+
name: "get_material",
|
|
63
|
+
description: "Get the full text content of a specific course material file. Use this AFTER search_materials to read the content of a relevant file.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
name: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "The file name (or partial name) to retrieve. Matched against file names from search_materials or list_materials results.",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
required: ["name"],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
61
75
|
{
|
|
62
76
|
name: "list_materials",
|
|
63
77
|
description: "List all available course materials, optionally filtered by category.",
|
|
@@ -109,12 +123,14 @@ export function registerToolHandlers(server) {
|
|
|
109
123
|
if (matches.length > 0) {
|
|
110
124
|
results.push({
|
|
111
125
|
fileName: file.name,
|
|
112
|
-
path: file.path,
|
|
113
126
|
category: categorizeFile(file),
|
|
114
|
-
|
|
127
|
+
matchCount: matches.length,
|
|
128
|
+
preview: matches[0].line.trim().substring(0, 120),
|
|
115
129
|
});
|
|
116
130
|
}
|
|
117
131
|
}
|
|
132
|
+
// Sort by match count descending (most relevant first)
|
|
133
|
+
results.sort((a, b) => b.matchCount - a.matchCount);
|
|
118
134
|
if (results.length === 0) {
|
|
119
135
|
return {
|
|
120
136
|
content: [
|
|
@@ -126,18 +142,53 @@ export function registerToolHandlers(server) {
|
|
|
126
142
|
};
|
|
127
143
|
}
|
|
128
144
|
const formatted = results
|
|
129
|
-
.map((r) => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
.map((r) => `- ${r.fileName} [${r.category}] (${r.matchCount} matches) — "${r.preview}"`)
|
|
146
|
+
.join("\n");
|
|
147
|
+
return {
|
|
148
|
+
content: [
|
|
149
|
+
{
|
|
150
|
+
type: "text",
|
|
151
|
+
text: `Found "${query}" in ${results.length} file(s). Use get_material to read the most relevant one(s):\n\n${formatted}`,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (name === "get_material") {
|
|
157
|
+
const queryName = args.name;
|
|
158
|
+
if (!queryName) {
|
|
159
|
+
return {
|
|
160
|
+
content: [{ type: "text", text: "Error: name parameter is required" }],
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
await ensureTextCache();
|
|
164
|
+
const queryLower = queryName.toLowerCase();
|
|
165
|
+
let bestMatch = null;
|
|
166
|
+
for (const [, entry] of textCache) {
|
|
167
|
+
if (entry.file.name.toLowerCase().includes(queryLower)) {
|
|
168
|
+
bestMatch = entry;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (!bestMatch) {
|
|
173
|
+
return {
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
type: "text",
|
|
177
|
+
text: `No file found matching "${queryName}". Use search_materials or list_materials to find available files.`,
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
// Truncate very large files
|
|
183
|
+
const maxLen = 30000;
|
|
184
|
+
const text = bestMatch.text.length > maxLen
|
|
185
|
+
? bestMatch.text.substring(0, maxLen) + "\n...[truncated]"
|
|
186
|
+
: bestMatch.text;
|
|
136
187
|
return {
|
|
137
188
|
content: [
|
|
138
189
|
{
|
|
139
190
|
type: "text",
|
|
140
|
-
text:
|
|
191
|
+
text: `📄 ${bestMatch.file.name} [${categorizeFile(bestMatch.file)}]\n\n${text}`,
|
|
141
192
|
},
|
|
142
193
|
],
|
|
143
194
|
};
|