@mcpc-tech/core 0.2.0-beta.10
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/mod.mjs +2963 -0
- package/package.json +38 -0
- package/plugins.mjs +289 -0
- package/src/plugins/large-result.mjs +290 -0
- package/src/plugins/search-tool.mjs +217 -0
- package/types/mod.d.ts +54 -0
- package/types/mod.d.ts.map +1 -0
- package/types/plugins.d.ts +43 -0
- package/types/plugins.d.ts.map +1 -0
- package/types/src/compose.d.ts +119 -0
- package/types/src/compose.d.ts.map +1 -0
- package/types/src/plugin-types.d.ts +50 -0
- package/types/src/plugin-types.d.ts.map +1 -0
- package/types/src/set-up-mcp-compose.d.ts +68 -0
- package/types/src/set-up-mcp-compose.d.ts.map +1 -0
- package/types/src/types.d.ts +18 -0
- package/types/src/types.d.ts.map +1 -0
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mcpc-tech/core",
|
|
3
|
+
"version": "0.2.0-beta.10",
|
|
4
|
+
"homepage": "https://jsr.io/@mcpc/core",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./types/*": "./types/*",
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./mod.mjs",
|
|
10
|
+
"types": "./types/mod.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./plugins": {
|
|
13
|
+
"import": "./plugins.mjs"
|
|
14
|
+
},
|
|
15
|
+
"./src/plugins/search-tool": {
|
|
16
|
+
"import": "./src/plugins/search-tool.mjs"
|
|
17
|
+
},
|
|
18
|
+
"./src/plugins/large-result": {
|
|
19
|
+
"import": "./src/plugins/large-result.mjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"jsonrepair": "^3.13.0",
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.8.0",
|
|
25
|
+
"ai": "^4.3.4",
|
|
26
|
+
"json-schema-traverse": "^1.0.0",
|
|
27
|
+
"cheerio": "^1.0.0",
|
|
28
|
+
"ajv": "^8.17.1",
|
|
29
|
+
"@segment/ajv-human-errors": "^2.15.0",
|
|
30
|
+
"ajv-formats": "^3.0.1",
|
|
31
|
+
"@mcpc-tech/ripgrep-napi": "^0.0.4"
|
|
32
|
+
},
|
|
33
|
+
"main": "./mod.mjs",
|
|
34
|
+
"types": "./types/mod.d.ts",
|
|
35
|
+
"scripts": {
|
|
36
|
+
"start": "node mod.mjs"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/plugins.mjs
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// ../__mcpc__core_0.2.0-beta.10/node_modules/@mcpc/core/src/plugins/search-tool.js
|
|
2
|
+
import rg from "@mcpc-tech/ripgrep-napi";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { jsonSchema } from "ai";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { relative } from "node:path";
|
|
7
|
+
function createSearchPlugin(options = {}) {
|
|
8
|
+
const maxResults = options.maxResults || 20;
|
|
9
|
+
const maxOutputSize = options.maxOutputSize || 5e3;
|
|
10
|
+
const allowedSearchDir = options.allowedDir || tmpdir();
|
|
11
|
+
const timeoutMs = options.timeoutMs || 3e4;
|
|
12
|
+
const global = options.global ?? true;
|
|
13
|
+
return {
|
|
14
|
+
name: "plugin-search",
|
|
15
|
+
configureServer: (server) => {
|
|
16
|
+
server.tool("search-tool-result", `Search for text patterns in files and directories. Use this to find specific content, code, or information within files. Provide a simple literal string or a regular expression. If your pattern is a regex, ensure it's valid; otherwise use quotes or escape special characters to treat it as a literal string.
|
|
17
|
+
Only search within the allowed directory: ${allowedSearchDir}`, jsonSchema({
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
pattern: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Text to search for. Can be a plain string or a regular expression. For regexes, don't include delimiters (e.g. use `^foo` not `/^foo/`). If you get a regex parse error, try escaping special chars or using a simpler literal search."
|
|
23
|
+
},
|
|
24
|
+
path: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "File or folder path to limit the search (optional). Must be within the allowed directory."
|
|
27
|
+
},
|
|
28
|
+
maxResults: {
|
|
29
|
+
type: "number",
|
|
30
|
+
description: "Maximum number of matches to return (optional). Lower this to reduce output size and runtime."
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
required: [
|
|
34
|
+
"pattern"
|
|
35
|
+
]
|
|
36
|
+
}), async (args) => {
|
|
37
|
+
const isBroad = (raw) => {
|
|
38
|
+
const t = (raw ?? "").trim();
|
|
39
|
+
if (!t) return true;
|
|
40
|
+
if (/^[*.\s]{2,}$/.test(t)) return true;
|
|
41
|
+
if (t === ".*" || t === "." || t === "^.*$") return true;
|
|
42
|
+
if (/^\^?\.\*\$?$/.test(t)) return true;
|
|
43
|
+
if (/^\\s?\*+$/.test(t)) return true;
|
|
44
|
+
return false;
|
|
45
|
+
};
|
|
46
|
+
const appendMatchSafely = (current, addition, limit) => {
|
|
47
|
+
if ((current + addition).length > limit) {
|
|
48
|
+
return {
|
|
49
|
+
current,
|
|
50
|
+
added: false
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
current: current + addition,
|
|
55
|
+
added: true
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
const requestedPath = args.path || allowedSearchDir;
|
|
60
|
+
const limit = args.maxResults || maxResults;
|
|
61
|
+
if (args.path) {
|
|
62
|
+
const resolvedRequested = resolve(args.path);
|
|
63
|
+
const resolvedAllowed = resolve(allowedSearchDir);
|
|
64
|
+
const relativePath = relative(resolvedAllowed, resolvedRequested);
|
|
65
|
+
if (relativePath && relativePath.startsWith("..")) {
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: `\u274C Path "${args.path}" not allowed. Must be within: ${allowedSearchDir}`
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
isError: true
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const searchPath = requestedPath;
|
|
78
|
+
const rawPattern = args.pattern ?? "";
|
|
79
|
+
if (isBroad(rawPattern)) {
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: `\u274C Search pattern too broad: "${rawPattern}"
|
|
85
|
+
Provide a more specific pattern (e.g. include a filename fragment, a keyword, or limit with the "path" parameter). Avoid patterns that only contain wildcards like "*" or ".*".`
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
isError: true
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
let timeoutId;
|
|
92
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
93
|
+
timeoutId = setTimeout(() => {
|
|
94
|
+
reject(new Error(`Search timeout after ${timeoutMs}ms`));
|
|
95
|
+
}, timeoutMs);
|
|
96
|
+
});
|
|
97
|
+
console.log(`Searching for "${args.pattern}" in ${searchPath}`);
|
|
98
|
+
const searchPromise = new Promise((resolve2, reject) => {
|
|
99
|
+
try {
|
|
100
|
+
const result2 = rg.search(args.pattern, [
|
|
101
|
+
searchPath
|
|
102
|
+
]);
|
|
103
|
+
resolve2(result2);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
reject(error);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const result = await Promise.race([
|
|
109
|
+
searchPromise,
|
|
110
|
+
timeoutPromise
|
|
111
|
+
]);
|
|
112
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
113
|
+
if (!result.success || !result.matches?.length) {
|
|
114
|
+
return {
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: "text",
|
|
118
|
+
text: `No matches found for: "${args.pattern}"
|
|
119
|
+
|
|
120
|
+
Try:
|
|
121
|
+
- **Simpler pattern** or \`*\`
|
|
122
|
+
- Check if files exist in: ${searchPath}
|
|
123
|
+
- Use specific file path`
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const matches = result.matches.slice(0, limit);
|
|
129
|
+
let output = `Found ${result.matches.length} matches (showing up to ${matches.length}):
|
|
130
|
+
|
|
131
|
+
`;
|
|
132
|
+
let matchesIncluded = 0;
|
|
133
|
+
for (const match of matches) {
|
|
134
|
+
const baseMatchText = `**${match.path}:${match.lineNumber}**
|
|
135
|
+
`;
|
|
136
|
+
const fullMatchText = `${baseMatchText}\`\`\`
|
|
137
|
+
${match.line}
|
|
138
|
+
\`\`\`
|
|
139
|
+
|
|
140
|
+
`;
|
|
141
|
+
const res = appendMatchSafely(output, fullMatchText, maxOutputSize);
|
|
142
|
+
if (!res.added) {
|
|
143
|
+
if (matchesIncluded === 0) {
|
|
144
|
+
const remainingSpace = maxOutputSize - output.length - 100;
|
|
145
|
+
if (remainingSpace > 50) {
|
|
146
|
+
const truncatedLine = match.line.slice(0, remainingSpace);
|
|
147
|
+
output += `${baseMatchText}\`\`\`
|
|
148
|
+
${truncatedLine}...
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
`;
|
|
152
|
+
output += `\u26A0\uFE0F Content truncated
|
|
153
|
+
`;
|
|
154
|
+
matchesIncluded++;
|
|
155
|
+
} else {
|
|
156
|
+
output += `\u26A0\uFE0F Content too large, use specific file path
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
output = res.current;
|
|
163
|
+
matchesIncluded++;
|
|
164
|
+
}
|
|
165
|
+
if (matchesIncluded < matches.length) {
|
|
166
|
+
output += `
|
|
167
|
+
\u26A0\uFE0F Showing ${matchesIncluded}/${matches.length} matches (size limit)
|
|
168
|
+
`;
|
|
169
|
+
output += `
|
|
170
|
+
For more results:
|
|
171
|
+
`;
|
|
172
|
+
output += `- Use specific pattern: "${args.pattern} keyword"
|
|
173
|
+
`;
|
|
174
|
+
output += `- Search specific file: {"pattern": "${args.pattern}", "path": "/file.txt"}
|
|
175
|
+
`;
|
|
176
|
+
output += `- Use fewer results: {"maxResults": 5}`;
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
content: [
|
|
180
|
+
{
|
|
181
|
+
type: "text",
|
|
182
|
+
text: output
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
};
|
|
186
|
+
} catch (error) {
|
|
187
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
188
|
+
const isTimeout = errorMsg.includes("timeout");
|
|
189
|
+
return {
|
|
190
|
+
content: [
|
|
191
|
+
{
|
|
192
|
+
type: "text",
|
|
193
|
+
text: `Search error: ${errorMsg}
|
|
194
|
+
|
|
195
|
+
${isTimeout ? `Timeout after ${timeoutMs}ms. Try simpler pattern or smaller directory.` : `Check pattern syntax or directory exists.`}`
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}, {
|
|
201
|
+
internal: !global
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
var defaultSearchPlugin = createSearchPlugin({
|
|
207
|
+
global: true,
|
|
208
|
+
maxResults: 20,
|
|
209
|
+
maxOutputSize: 5e3,
|
|
210
|
+
caseSensitive: false,
|
|
211
|
+
timeoutMs: 3e4
|
|
212
|
+
});
|
|
213
|
+
var search_tool_default = defaultSearchPlugin;
|
|
214
|
+
|
|
215
|
+
// ../__mcpc__core_0.2.0-beta.10/node_modules/@mcpc/core/src/plugins/large-result.js
|
|
216
|
+
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
217
|
+
import { join } from "node:path";
|
|
218
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
219
|
+
function createLargeResultPlugin(options = {}) {
|
|
220
|
+
const maxSize = options.maxSize || 8e3;
|
|
221
|
+
const previewSize = options.previewSize || 4e3;
|
|
222
|
+
let tempDir = options.tempDir || null;
|
|
223
|
+
const serversConfigured = /* @__PURE__ */ new WeakSet();
|
|
224
|
+
const searchConfig = {
|
|
225
|
+
maxResults: options.search?.maxResults || 15,
|
|
226
|
+
maxOutputSize: options.search?.maxOutputSize || 4e3,
|
|
227
|
+
global: true
|
|
228
|
+
};
|
|
229
|
+
return {
|
|
230
|
+
name: "plugin-large-result-handler",
|
|
231
|
+
configureServer: async (server) => {
|
|
232
|
+
if (!serversConfigured.has(server)) {
|
|
233
|
+
const searchPlugin = createSearchPlugin(searchConfig);
|
|
234
|
+
await server.addPlugin(searchPlugin);
|
|
235
|
+
serversConfigured.add(server);
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
transformTool: (tool, context) => {
|
|
239
|
+
const originalExecute = tool.execute;
|
|
240
|
+
tool.execute = async (args) => {
|
|
241
|
+
const result = await originalExecute(args);
|
|
242
|
+
const resultText = JSON.stringify(result);
|
|
243
|
+
if (resultText.length <= maxSize) {
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
if (!tempDir) {
|
|
247
|
+
tempDir = await mkdtemp(join(tmpdir2(), "mcpc-results-"));
|
|
248
|
+
}
|
|
249
|
+
const safeToolName = encodeURIComponent(context.toolName ?? "tool");
|
|
250
|
+
const fileName = `${safeToolName}-${Date.now()}.txt`;
|
|
251
|
+
const filePath = join(tempDir, fileName);
|
|
252
|
+
await writeFile(filePath, resultText);
|
|
253
|
+
const preview = resultText.slice(0, previewSize);
|
|
254
|
+
return {
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: "text",
|
|
258
|
+
text: `**Result too large (${resultText.length} chars), saved to file**
|
|
259
|
+
|
|
260
|
+
\u{1F4C1} **File:** ${filePath}
|
|
261
|
+
\u{1F4CA} **Size:** ${(resultText.length / 1024).toFixed(1)} KB
|
|
262
|
+
|
|
263
|
+
**Preview (${previewSize} chars):**
|
|
264
|
+
\`\`\`
|
|
265
|
+
${preview}
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
**To read/understand the full content:**
|
|
269
|
+
- Use the \`search-tool-result\` tool with pattern: \`search-tool-result {"pattern": "your-search-term"}\`
|
|
270
|
+
- Search supports regex patterns for advanced queries`
|
|
271
|
+
}
|
|
272
|
+
]
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
return tool;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
var defaultLargeResultPlugin = createLargeResultPlugin({
|
|
280
|
+
maxSize: 8e3,
|
|
281
|
+
previewSize: 4e3
|
|
282
|
+
});
|
|
283
|
+
var large_result_default = defaultLargeResultPlugin;
|
|
284
|
+
export {
|
|
285
|
+
createLargeResultPlugin,
|
|
286
|
+
createSearchPlugin,
|
|
287
|
+
large_result_default as defaultLargeResultPlugin,
|
|
288
|
+
search_tool_default as defaultSearchPlugin
|
|
289
|
+
};
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// ../__mcpc__core_0.2.0-beta.10/node_modules/@mcpc/core/src/plugins/large-result.ts
|
|
2
|
+
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
5
|
+
|
|
6
|
+
// ../__mcpc__core_0.2.0-beta.10/node_modules/@mcpc/core/src/plugins/search-tool.js
|
|
7
|
+
import rg from "@mcpc-tech/ripgrep-napi";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { jsonSchema } from "ai";
|
|
10
|
+
import { resolve } from "node:path";
|
|
11
|
+
import { relative } from "node:path";
|
|
12
|
+
function createSearchPlugin(options = {}) {
|
|
13
|
+
const maxResults = options.maxResults || 20;
|
|
14
|
+
const maxOutputSize = options.maxOutputSize || 5e3;
|
|
15
|
+
const allowedSearchDir = options.allowedDir || tmpdir();
|
|
16
|
+
const timeoutMs = options.timeoutMs || 3e4;
|
|
17
|
+
const global = options.global ?? true;
|
|
18
|
+
return {
|
|
19
|
+
name: "plugin-search",
|
|
20
|
+
configureServer: (server) => {
|
|
21
|
+
server.tool("search-tool-result", `Search for text patterns in files and directories. Use this to find specific content, code, or information within files. Provide a simple literal string or a regular expression. If your pattern is a regex, ensure it's valid; otherwise use quotes or escape special characters to treat it as a literal string.
|
|
22
|
+
Only search within the allowed directory: ${allowedSearchDir}`, jsonSchema({
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
pattern: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Text to search for. Can be a plain string or a regular expression. For regexes, don't include delimiters (e.g. use `^foo` not `/^foo/`). If you get a regex parse error, try escaping special chars or using a simpler literal search."
|
|
28
|
+
},
|
|
29
|
+
path: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "File or folder path to limit the search (optional). Must be within the allowed directory."
|
|
32
|
+
},
|
|
33
|
+
maxResults: {
|
|
34
|
+
type: "number",
|
|
35
|
+
description: "Maximum number of matches to return (optional). Lower this to reduce output size and runtime."
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
required: [
|
|
39
|
+
"pattern"
|
|
40
|
+
]
|
|
41
|
+
}), async (args) => {
|
|
42
|
+
const isBroad = (raw) => {
|
|
43
|
+
const t = (raw ?? "").trim();
|
|
44
|
+
if (!t) return true;
|
|
45
|
+
if (/^[*.\s]{2,}$/.test(t)) return true;
|
|
46
|
+
if (t === ".*" || t === "." || t === "^.*$") return true;
|
|
47
|
+
if (/^\^?\.\*\$?$/.test(t)) return true;
|
|
48
|
+
if (/^\\s?\*+$/.test(t)) return true;
|
|
49
|
+
return false;
|
|
50
|
+
};
|
|
51
|
+
const appendMatchSafely = (current, addition, limit) => {
|
|
52
|
+
if ((current + addition).length > limit) {
|
|
53
|
+
return {
|
|
54
|
+
current,
|
|
55
|
+
added: false
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
current: current + addition,
|
|
60
|
+
added: true
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
try {
|
|
64
|
+
const requestedPath = args.path || allowedSearchDir;
|
|
65
|
+
const limit = args.maxResults || maxResults;
|
|
66
|
+
if (args.path) {
|
|
67
|
+
const resolvedRequested = resolve(args.path);
|
|
68
|
+
const resolvedAllowed = resolve(allowedSearchDir);
|
|
69
|
+
const relativePath = relative(resolvedAllowed, resolvedRequested);
|
|
70
|
+
if (relativePath && relativePath.startsWith("..")) {
|
|
71
|
+
return {
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: "text",
|
|
75
|
+
text: `\u274C Path "${args.path}" not allowed. Must be within: ${allowedSearchDir}`
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
isError: true
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const searchPath = requestedPath;
|
|
83
|
+
const rawPattern = args.pattern ?? "";
|
|
84
|
+
if (isBroad(rawPattern)) {
|
|
85
|
+
return {
|
|
86
|
+
content: [
|
|
87
|
+
{
|
|
88
|
+
type: "text",
|
|
89
|
+
text: `\u274C Search pattern too broad: "${rawPattern}"
|
|
90
|
+
Provide a more specific pattern (e.g. include a filename fragment, a keyword, or limit with the "path" parameter). Avoid patterns that only contain wildcards like "*" or ".*".`
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
isError: true
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
let timeoutId;
|
|
97
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
98
|
+
timeoutId = setTimeout(() => {
|
|
99
|
+
reject(new Error(`Search timeout after ${timeoutMs}ms`));
|
|
100
|
+
}, timeoutMs);
|
|
101
|
+
});
|
|
102
|
+
console.log(`Searching for "${args.pattern}" in ${searchPath}`);
|
|
103
|
+
const searchPromise = new Promise((resolve2, reject) => {
|
|
104
|
+
try {
|
|
105
|
+
const result2 = rg.search(args.pattern, [
|
|
106
|
+
searchPath
|
|
107
|
+
]);
|
|
108
|
+
resolve2(result2);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
reject(error);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
const result = await Promise.race([
|
|
114
|
+
searchPromise,
|
|
115
|
+
timeoutPromise
|
|
116
|
+
]);
|
|
117
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
118
|
+
if (!result.success || !result.matches?.length) {
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
text: `No matches found for: "${args.pattern}"
|
|
124
|
+
|
|
125
|
+
Try:
|
|
126
|
+
- **Simpler pattern** or \`*\`
|
|
127
|
+
- Check if files exist in: ${searchPath}
|
|
128
|
+
- Use specific file path`
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const matches = result.matches.slice(0, limit);
|
|
134
|
+
let output = `Found ${result.matches.length} matches (showing up to ${matches.length}):
|
|
135
|
+
|
|
136
|
+
`;
|
|
137
|
+
let matchesIncluded = 0;
|
|
138
|
+
for (const match of matches) {
|
|
139
|
+
const baseMatchText = `**${match.path}:${match.lineNumber}**
|
|
140
|
+
`;
|
|
141
|
+
const fullMatchText = `${baseMatchText}\`\`\`
|
|
142
|
+
${match.line}
|
|
143
|
+
\`\`\`
|
|
144
|
+
|
|
145
|
+
`;
|
|
146
|
+
const res = appendMatchSafely(output, fullMatchText, maxOutputSize);
|
|
147
|
+
if (!res.added) {
|
|
148
|
+
if (matchesIncluded === 0) {
|
|
149
|
+
const remainingSpace = maxOutputSize - output.length - 100;
|
|
150
|
+
if (remainingSpace > 50) {
|
|
151
|
+
const truncatedLine = match.line.slice(0, remainingSpace);
|
|
152
|
+
output += `${baseMatchText}\`\`\`
|
|
153
|
+
${truncatedLine}...
|
|
154
|
+
\`\`\`
|
|
155
|
+
|
|
156
|
+
`;
|
|
157
|
+
output += `\u26A0\uFE0F Content truncated
|
|
158
|
+
`;
|
|
159
|
+
matchesIncluded++;
|
|
160
|
+
} else {
|
|
161
|
+
output += `\u26A0\uFE0F Content too large, use specific file path
|
|
162
|
+
`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
output = res.current;
|
|
168
|
+
matchesIncluded++;
|
|
169
|
+
}
|
|
170
|
+
if (matchesIncluded < matches.length) {
|
|
171
|
+
output += `
|
|
172
|
+
\u26A0\uFE0F Showing ${matchesIncluded}/${matches.length} matches (size limit)
|
|
173
|
+
`;
|
|
174
|
+
output += `
|
|
175
|
+
For more results:
|
|
176
|
+
`;
|
|
177
|
+
output += `- Use specific pattern: "${args.pattern} keyword"
|
|
178
|
+
`;
|
|
179
|
+
output += `- Search specific file: {"pattern": "${args.pattern}", "path": "/file.txt"}
|
|
180
|
+
`;
|
|
181
|
+
output += `- Use fewer results: {"maxResults": 5}`;
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
content: [
|
|
185
|
+
{
|
|
186
|
+
type: "text",
|
|
187
|
+
text: output
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
};
|
|
191
|
+
} catch (error) {
|
|
192
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
193
|
+
const isTimeout = errorMsg.includes("timeout");
|
|
194
|
+
return {
|
|
195
|
+
content: [
|
|
196
|
+
{
|
|
197
|
+
type: "text",
|
|
198
|
+
text: `Search error: ${errorMsg}
|
|
199
|
+
|
|
200
|
+
${isTimeout ? `Timeout after ${timeoutMs}ms. Try simpler pattern or smaller directory.` : `Check pattern syntax or directory exists.`}`
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}, {
|
|
206
|
+
internal: !global
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
var defaultSearchPlugin = createSearchPlugin({
|
|
212
|
+
global: true,
|
|
213
|
+
maxResults: 20,
|
|
214
|
+
maxOutputSize: 5e3,
|
|
215
|
+
caseSensitive: false,
|
|
216
|
+
timeoutMs: 3e4
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// ../__mcpc__core_0.2.0-beta.10/node_modules/@mcpc/core/src/plugins/large-result.ts
|
|
220
|
+
function createLargeResultPlugin(options = {}) {
|
|
221
|
+
const maxSize = options.maxSize || 8e3;
|
|
222
|
+
const previewSize = options.previewSize || 4e3;
|
|
223
|
+
let tempDir = options.tempDir || null;
|
|
224
|
+
const serversConfigured = /* @__PURE__ */ new WeakSet();
|
|
225
|
+
const searchConfig = {
|
|
226
|
+
maxResults: options.search?.maxResults || 15,
|
|
227
|
+
maxOutputSize: options.search?.maxOutputSize || 4e3,
|
|
228
|
+
global: true
|
|
229
|
+
};
|
|
230
|
+
return {
|
|
231
|
+
name: "plugin-large-result-handler",
|
|
232
|
+
configureServer: async (server) => {
|
|
233
|
+
if (!serversConfigured.has(server)) {
|
|
234
|
+
const searchPlugin = createSearchPlugin(searchConfig);
|
|
235
|
+
await server.addPlugin(searchPlugin);
|
|
236
|
+
serversConfigured.add(server);
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
transformTool: (tool, context) => {
|
|
240
|
+
const originalExecute = tool.execute;
|
|
241
|
+
tool.execute = async (args) => {
|
|
242
|
+
const result = await originalExecute(args);
|
|
243
|
+
const resultText = JSON.stringify(result);
|
|
244
|
+
if (resultText.length <= maxSize) {
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
if (!tempDir) {
|
|
248
|
+
tempDir = await mkdtemp(join(tmpdir2(), "mcpc-results-"));
|
|
249
|
+
}
|
|
250
|
+
const safeToolName = encodeURIComponent(context.toolName ?? "tool");
|
|
251
|
+
const fileName = `${safeToolName}-${Date.now()}.txt`;
|
|
252
|
+
const filePath = join(tempDir, fileName);
|
|
253
|
+
await writeFile(filePath, resultText);
|
|
254
|
+
const preview = resultText.slice(0, previewSize);
|
|
255
|
+
return {
|
|
256
|
+
content: [
|
|
257
|
+
{
|
|
258
|
+
type: "text",
|
|
259
|
+
text: `**Result too large (${resultText.length} chars), saved to file**
|
|
260
|
+
|
|
261
|
+
\u{1F4C1} **File:** ${filePath}
|
|
262
|
+
\u{1F4CA} **Size:** ${(resultText.length / 1024).toFixed(1)} KB
|
|
263
|
+
|
|
264
|
+
**Preview (${previewSize} chars):**
|
|
265
|
+
\`\`\`
|
|
266
|
+
${preview}
|
|
267
|
+
\`\`\`
|
|
268
|
+
|
|
269
|
+
**To read/understand the full content:**
|
|
270
|
+
- Use the \`search-tool-result\` tool with pattern: \`search-tool-result {"pattern": "your-search-term"}\`
|
|
271
|
+
- Search supports regex patterns for advanced queries`
|
|
272
|
+
}
|
|
273
|
+
]
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
return tool;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
var defaultLargeResultPlugin = createLargeResultPlugin({
|
|
281
|
+
maxSize: 8e3,
|
|
282
|
+
previewSize: 4e3
|
|
283
|
+
});
|
|
284
|
+
var createPlugin = createLargeResultPlugin;
|
|
285
|
+
var large_result_default = defaultLargeResultPlugin;
|
|
286
|
+
export {
|
|
287
|
+
createLargeResultPlugin,
|
|
288
|
+
createPlugin,
|
|
289
|
+
large_result_default as default
|
|
290
|
+
};
|