@mcpc-tech/core 0.3.13 → 0.3.15
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/index.cjs +503 -114
- package/index.mjs +503 -114
- package/package.json +6 -1
- package/plugins/large-result.cjs +25 -15
- package/plugins/large-result.mjs +25 -15
- package/plugins/search.cjs +5 -2
- package/plugins/search.mjs +5 -2
- package/plugins/skills.cjs +229 -0
- package/plugins/skills.mjs +207 -0
- package/plugins.cjs +224 -17
- package/plugins.mjs +223 -17
- package/types/plugins.d.ts +1 -0
- package/types/plugins.d.ts.map +1 -1
- package/types/src/plugins/large-result.d.ts.map +1 -1
- package/types/src/plugins/search-tool.d.ts +1 -0
- package/types/src/plugins/search-tool.d.ts.map +1 -1
- package/types/src/plugins/skills.d.ts +23 -0
- package/types/src/plugins/skills.d.ts.map +1 -0
package/plugins.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var plugins_exports = {};
|
|
|
32
32
|
__export(plugins_exports, {
|
|
33
33
|
createLargeResultPlugin: () => createLargeResultPlugin,
|
|
34
34
|
createSearchPlugin: () => createSearchPlugin,
|
|
35
|
+
createSkillsPlugin: () => createSkillsPlugin,
|
|
35
36
|
defaultLargeResultPlugin: () => large_result_default,
|
|
36
37
|
defaultSearchPlugin: () => search_tool_default
|
|
37
38
|
});
|
|
@@ -70,15 +71,18 @@ function createSearchPlugin(options = {}) {
|
|
|
70
71
|
const allowedSearchDir = options.allowedDir || (0, import_node_os.tmpdir)();
|
|
71
72
|
const timeoutMs = options.timeoutMs || 3e4;
|
|
72
73
|
const global = options.global ?? true;
|
|
74
|
+
const agentName = options.agentName;
|
|
75
|
+
const toolName = agentName ? `${agentName}__search-tool-result` : "search-tool-result";
|
|
73
76
|
const activeTimeouts = /* @__PURE__ */ new Set();
|
|
74
77
|
return {
|
|
75
78
|
name: "plugin-search",
|
|
76
79
|
version: "1.0.0",
|
|
77
80
|
configureServer: (server) => {
|
|
78
|
-
const defaultDescription = `Search for text patterns in files
|
|
81
|
+
const defaultDescription = agentName ? `Search for text patterns in files for the "${agentName}" agent. Use this to find specific content within large tool results. Provide a simple literal string or a regular expression.
|
|
82
|
+
Only search within the allowed directory: ${allowedSearchDir}` : `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.
|
|
79
83
|
Only search within the allowed directory: ${allowedSearchDir}`;
|
|
80
84
|
const toolDescription = options.toolDescription || defaultDescription;
|
|
81
|
-
server.tool(
|
|
85
|
+
server.tool(toolName, toolDescription, jsonSchema({
|
|
82
86
|
type: "object",
|
|
83
87
|
properties: {
|
|
84
88
|
pattern: {
|
|
@@ -159,12 +163,12 @@ Provide a more specific pattern (e.g. include a filename fragment, a keyword, or
|
|
|
159
163
|
}, timeoutMs);
|
|
160
164
|
activeTimeouts.add(timeoutId);
|
|
161
165
|
});
|
|
162
|
-
const searchPromise = new Promise((
|
|
166
|
+
const searchPromise = new Promise((resolve3, reject) => {
|
|
163
167
|
try {
|
|
164
168
|
const result2 = import_ripgrep_napi.default.search(args.pattern, [
|
|
165
169
|
searchPath
|
|
166
170
|
]);
|
|
167
|
-
|
|
171
|
+
resolve3(result2);
|
|
168
172
|
} catch (error) {
|
|
169
173
|
reject(error);
|
|
170
174
|
}
|
|
@@ -297,27 +301,33 @@ function createLargeResultPlugin(options = {}) {
|
|
|
297
301
|
const maxSize = options.maxSize || 8e3;
|
|
298
302
|
const previewSize = options.previewSize || 4e3;
|
|
299
303
|
let tempDir = options.tempDir || null;
|
|
300
|
-
|
|
304
|
+
let serverRef = null;
|
|
305
|
+
let agentName = null;
|
|
301
306
|
const defaultSearchDescription = `Search within large tool result files that were saved due to size limits. Use when: a tool result was saved to file because it exceeded the context limit. Do NOT use this tool before calling the actual tool first. Provide specific keywords or patterns related to the content you're looking for.`;
|
|
302
|
-
const searchConfig = {
|
|
303
|
-
maxResults: options.search?.maxResults || 15,
|
|
304
|
-
maxOutputSize: options.search?.maxOutputSize || 4e3,
|
|
305
|
-
toolDescription: options.search?.toolDescription || defaultSearchDescription,
|
|
306
|
-
global: true
|
|
307
|
-
};
|
|
308
307
|
return {
|
|
309
308
|
name: "plugin-large-result-handler",
|
|
310
309
|
version: "1.0.0",
|
|
311
310
|
dependencies: [],
|
|
312
|
-
configureServer:
|
|
313
|
-
|
|
311
|
+
configureServer: (server) => {
|
|
312
|
+
serverRef = server;
|
|
313
|
+
},
|
|
314
|
+
composeStart: async (context) => {
|
|
315
|
+
agentName = context.serverName;
|
|
316
|
+
if (serverRef) {
|
|
317
|
+
const searchConfig = {
|
|
318
|
+
maxResults: options.search?.maxResults || 15,
|
|
319
|
+
maxOutputSize: options.search?.maxOutputSize || 4e3,
|
|
320
|
+
toolDescription: options.search?.toolDescription || defaultSearchDescription,
|
|
321
|
+
global: true,
|
|
322
|
+
agentName
|
|
323
|
+
};
|
|
314
324
|
const searchPlugin = createSearchPlugin(searchConfig);
|
|
315
|
-
await
|
|
316
|
-
configuredServers.set(server, true);
|
|
325
|
+
await serverRef.addPlugin(searchPlugin);
|
|
317
326
|
}
|
|
318
327
|
},
|
|
319
328
|
transformTool: (tool, context) => {
|
|
320
329
|
const originalExecute = tool.execute;
|
|
330
|
+
const searchToolName = agentName ? `${agentName}__search-tool-result` : "search-tool-result";
|
|
321
331
|
tool.execute = async (args) => {
|
|
322
332
|
try {
|
|
323
333
|
const result = await originalExecute(args);
|
|
@@ -349,7 +359,7 @@ ${preview}
|
|
|
349
359
|
\`\`\`
|
|
350
360
|
|
|
351
361
|
**To read/understand the full content:**
|
|
352
|
-
- Use the \`
|
|
362
|
+
- Use the \`${searchToolName}\` tool with pattern: \`${searchToolName} {"pattern": "your-search-term"}\`
|
|
353
363
|
- Search supports regex patterns for advanced queries`
|
|
354
364
|
}
|
|
355
365
|
]
|
|
@@ -363,7 +373,8 @@ ${preview}
|
|
|
363
373
|
return tool;
|
|
364
374
|
},
|
|
365
375
|
dispose: () => {
|
|
366
|
-
|
|
376
|
+
serverRef = null;
|
|
377
|
+
agentName = null;
|
|
367
378
|
tempDir = null;
|
|
368
379
|
}
|
|
369
380
|
};
|
|
@@ -373,10 +384,206 @@ var defaultLargeResultPlugin = createLargeResultPlugin({
|
|
|
373
384
|
previewSize: 4e3
|
|
374
385
|
});
|
|
375
386
|
var large_result_default = defaultLargeResultPlugin;
|
|
387
|
+
|
|
388
|
+
// __mcpc__core_latest/node_modules/@mcpc/core/src/plugins/skills.js
|
|
389
|
+
var import_promises2 = require("node:fs/promises");
|
|
390
|
+
var import_node_path4 = require("node:path");
|
|
391
|
+
function parseFrontmatter(content) {
|
|
392
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
393
|
+
if (!match) return null;
|
|
394
|
+
const yaml = match[1];
|
|
395
|
+
const result = {};
|
|
396
|
+
for (const line of yaml.split("\n")) {
|
|
397
|
+
if (line.trim() === "metadata:") {
|
|
398
|
+
result.metadata = {};
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
const metadataMatch = line.match(/^\s{2}(\w+):\s*"?([^"]*)"?$/);
|
|
402
|
+
if (metadataMatch && result.metadata) {
|
|
403
|
+
result.metadata[metadataMatch[1]] = metadataMatch[2];
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
const fieldMatch = line.match(/^(\S+):\s*(.*)$/);
|
|
407
|
+
if (fieldMatch) {
|
|
408
|
+
const [, key, value] = fieldMatch;
|
|
409
|
+
result[key] = value.replace(/^["']|["']$/g, "");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (!result.name || !result.description) return null;
|
|
413
|
+
return result;
|
|
414
|
+
}
|
|
415
|
+
function extractBody(content) {
|
|
416
|
+
return content.replace(/^---\n[\s\S]*?\n---\n*/, "");
|
|
417
|
+
}
|
|
418
|
+
async function scanSkills(basePath) {
|
|
419
|
+
const skills = [];
|
|
420
|
+
try {
|
|
421
|
+
const entries = await (0, import_promises2.readdir)(basePath, {
|
|
422
|
+
withFileTypes: true
|
|
423
|
+
});
|
|
424
|
+
for (const entry of entries) {
|
|
425
|
+
if (!entry.isDirectory()) continue;
|
|
426
|
+
const skillDir = (0, import_node_path4.join)(basePath, entry.name);
|
|
427
|
+
const skillFile = (0, import_node_path4.join)(skillDir, "SKILL.md");
|
|
428
|
+
try {
|
|
429
|
+
await (0, import_promises2.stat)(skillFile);
|
|
430
|
+
const content = await (0, import_promises2.readFile)(skillFile, "utf-8");
|
|
431
|
+
const frontmatter = parseFrontmatter(content);
|
|
432
|
+
if (frontmatter && frontmatter.name === entry.name) {
|
|
433
|
+
skills.push({
|
|
434
|
+
...frontmatter,
|
|
435
|
+
basePath: skillDir,
|
|
436
|
+
filePath: skillFile
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
} catch {
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
} catch {
|
|
443
|
+
}
|
|
444
|
+
return skills;
|
|
445
|
+
}
|
|
446
|
+
function generateToolDescription(skills, agentName) {
|
|
447
|
+
if (skills.length === 0) {
|
|
448
|
+
return "Load a skill's instructions. No skills available.";
|
|
449
|
+
}
|
|
450
|
+
const skillsList = skills.map((s) => `- ${s.name}: ${s.description}`).join("\n");
|
|
451
|
+
const toolName = `${agentName}__load-skill`;
|
|
452
|
+
return `Load a skill's detailed instructions or reference files for the "${agentName}" agent.
|
|
453
|
+
|
|
454
|
+
Available skills:
|
|
455
|
+
${skillsList}
|
|
456
|
+
|
|
457
|
+
Usage:
|
|
458
|
+
- ${toolName}({ skill: "skill-name" }) - Load main SKILL.md content
|
|
459
|
+
- ${toolName}({ skill: "skill-name", ref: "references/file.md" }) - Load reference file`;
|
|
460
|
+
}
|
|
461
|
+
function createSkillsPlugin(options) {
|
|
462
|
+
const { paths } = options;
|
|
463
|
+
const skillsMap = /* @__PURE__ */ new Map();
|
|
464
|
+
let serverRef = null;
|
|
465
|
+
return {
|
|
466
|
+
name: "plugin-skills",
|
|
467
|
+
version: "1.0.0",
|
|
468
|
+
// Save server reference
|
|
469
|
+
configureServer: (server) => {
|
|
470
|
+
serverRef = server;
|
|
471
|
+
},
|
|
472
|
+
// Scan directories and register tool
|
|
473
|
+
composeStart: async (context) => {
|
|
474
|
+
skillsMap.clear();
|
|
475
|
+
for (const dir of paths) {
|
|
476
|
+
const skills = await scanSkills(dir);
|
|
477
|
+
for (const skill of skills) {
|
|
478
|
+
skillsMap.set(skill.name, skill);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const agentName = context.serverName;
|
|
482
|
+
const toolDescription = generateToolDescription(Array.from(skillsMap.values()), agentName);
|
|
483
|
+
const toolName = `${agentName}__load-skill`;
|
|
484
|
+
if (serverRef) {
|
|
485
|
+
serverRef.tool(toolName, toolDescription, jsonSchema({
|
|
486
|
+
type: "object",
|
|
487
|
+
properties: {
|
|
488
|
+
skill: {
|
|
489
|
+
type: "string",
|
|
490
|
+
description: "The skill name to load"
|
|
491
|
+
},
|
|
492
|
+
ref: {
|
|
493
|
+
type: "string",
|
|
494
|
+
description: "Optional: relative path to a reference file within the skill directory"
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
required: [
|
|
498
|
+
"skill"
|
|
499
|
+
]
|
|
500
|
+
}), async (args) => {
|
|
501
|
+
const meta = skillsMap.get(args.skill);
|
|
502
|
+
if (!meta) {
|
|
503
|
+
return {
|
|
504
|
+
content: [
|
|
505
|
+
{
|
|
506
|
+
type: "text",
|
|
507
|
+
text: `Skill "${args.skill}" not found`
|
|
508
|
+
}
|
|
509
|
+
],
|
|
510
|
+
isError: true
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
if (args.ref) {
|
|
514
|
+
const refPath = (0, import_node_path4.resolve)(meta.basePath, args.ref);
|
|
515
|
+
const relPath = (0, import_node_path4.relative)(meta.basePath, refPath);
|
|
516
|
+
if (relPath.startsWith("..")) {
|
|
517
|
+
return {
|
|
518
|
+
content: [
|
|
519
|
+
{
|
|
520
|
+
type: "text",
|
|
521
|
+
text: `Invalid ref path: ${args.ref}`
|
|
522
|
+
}
|
|
523
|
+
],
|
|
524
|
+
isError: true
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
try {
|
|
528
|
+
const content = await (0, import_promises2.readFile)(refPath, "utf-8");
|
|
529
|
+
return {
|
|
530
|
+
content: [
|
|
531
|
+
{
|
|
532
|
+
type: "text",
|
|
533
|
+
text: content
|
|
534
|
+
}
|
|
535
|
+
]
|
|
536
|
+
};
|
|
537
|
+
} catch {
|
|
538
|
+
return {
|
|
539
|
+
content: [
|
|
540
|
+
{
|
|
541
|
+
type: "text",
|
|
542
|
+
text: `File not found: ${args.ref}`
|
|
543
|
+
}
|
|
544
|
+
],
|
|
545
|
+
isError: true
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
try {
|
|
550
|
+
const content = await (0, import_promises2.readFile)(meta.filePath, "utf-8");
|
|
551
|
+
const body = extractBody(content);
|
|
552
|
+
return {
|
|
553
|
+
content: [
|
|
554
|
+
{
|
|
555
|
+
type: "text",
|
|
556
|
+
text: body
|
|
557
|
+
}
|
|
558
|
+
]
|
|
559
|
+
};
|
|
560
|
+
} catch {
|
|
561
|
+
return {
|
|
562
|
+
content: [
|
|
563
|
+
{
|
|
564
|
+
type: "text",
|
|
565
|
+
text: `Failed to load skill: ${args.skill}`
|
|
566
|
+
}
|
|
567
|
+
],
|
|
568
|
+
isError: true
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
}, {
|
|
572
|
+
internal: false
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
dispose: () => {
|
|
577
|
+
skillsMap.clear();
|
|
578
|
+
serverRef = null;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
376
582
|
// Annotate the CommonJS export names for ESM import in node:
|
|
377
583
|
0 && (module.exports = {
|
|
378
584
|
createLargeResultPlugin,
|
|
379
585
|
createSearchPlugin,
|
|
586
|
+
createSkillsPlugin,
|
|
380
587
|
defaultLargeResultPlugin,
|
|
381
588
|
defaultSearchPlugin
|
|
382
589
|
});
|
package/plugins.mjs
CHANGED
|
@@ -35,15 +35,18 @@ function createSearchPlugin(options = {}) {
|
|
|
35
35
|
const allowedSearchDir = options.allowedDir || tmpdir();
|
|
36
36
|
const timeoutMs = options.timeoutMs || 3e4;
|
|
37
37
|
const global = options.global ?? true;
|
|
38
|
+
const agentName = options.agentName;
|
|
39
|
+
const toolName = agentName ? `${agentName}__search-tool-result` : "search-tool-result";
|
|
38
40
|
const activeTimeouts = /* @__PURE__ */ new Set();
|
|
39
41
|
return {
|
|
40
42
|
name: "plugin-search",
|
|
41
43
|
version: "1.0.0",
|
|
42
44
|
configureServer: (server) => {
|
|
43
|
-
const defaultDescription = `Search for text patterns in files
|
|
45
|
+
const defaultDescription = agentName ? `Search for text patterns in files for the "${agentName}" agent. Use this to find specific content within large tool results. Provide a simple literal string or a regular expression.
|
|
46
|
+
Only search within the allowed directory: ${allowedSearchDir}` : `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.
|
|
44
47
|
Only search within the allowed directory: ${allowedSearchDir}`;
|
|
45
48
|
const toolDescription = options.toolDescription || defaultDescription;
|
|
46
|
-
server.tool(
|
|
49
|
+
server.tool(toolName, toolDescription, jsonSchema({
|
|
47
50
|
type: "object",
|
|
48
51
|
properties: {
|
|
49
52
|
pattern: {
|
|
@@ -124,12 +127,12 @@ Provide a more specific pattern (e.g. include a filename fragment, a keyword, or
|
|
|
124
127
|
}, timeoutMs);
|
|
125
128
|
activeTimeouts.add(timeoutId);
|
|
126
129
|
});
|
|
127
|
-
const searchPromise = new Promise((
|
|
130
|
+
const searchPromise = new Promise((resolve3, reject) => {
|
|
128
131
|
try {
|
|
129
132
|
const result2 = rg.search(args.pattern, [
|
|
130
133
|
searchPath
|
|
131
134
|
]);
|
|
132
|
-
|
|
135
|
+
resolve3(result2);
|
|
133
136
|
} catch (error) {
|
|
134
137
|
reject(error);
|
|
135
138
|
}
|
|
@@ -262,27 +265,33 @@ function createLargeResultPlugin(options = {}) {
|
|
|
262
265
|
const maxSize = options.maxSize || 8e3;
|
|
263
266
|
const previewSize = options.previewSize || 4e3;
|
|
264
267
|
let tempDir = options.tempDir || null;
|
|
265
|
-
|
|
268
|
+
let serverRef = null;
|
|
269
|
+
let agentName = null;
|
|
266
270
|
const defaultSearchDescription = `Search within large tool result files that were saved due to size limits. Use when: a tool result was saved to file because it exceeded the context limit. Do NOT use this tool before calling the actual tool first. Provide specific keywords or patterns related to the content you're looking for.`;
|
|
267
|
-
const searchConfig = {
|
|
268
|
-
maxResults: options.search?.maxResults || 15,
|
|
269
|
-
maxOutputSize: options.search?.maxOutputSize || 4e3,
|
|
270
|
-
toolDescription: options.search?.toolDescription || defaultSearchDescription,
|
|
271
|
-
global: true
|
|
272
|
-
};
|
|
273
271
|
return {
|
|
274
272
|
name: "plugin-large-result-handler",
|
|
275
273
|
version: "1.0.0",
|
|
276
274
|
dependencies: [],
|
|
277
|
-
configureServer:
|
|
278
|
-
|
|
275
|
+
configureServer: (server) => {
|
|
276
|
+
serverRef = server;
|
|
277
|
+
},
|
|
278
|
+
composeStart: async (context) => {
|
|
279
|
+
agentName = context.serverName;
|
|
280
|
+
if (serverRef) {
|
|
281
|
+
const searchConfig = {
|
|
282
|
+
maxResults: options.search?.maxResults || 15,
|
|
283
|
+
maxOutputSize: options.search?.maxOutputSize || 4e3,
|
|
284
|
+
toolDescription: options.search?.toolDescription || defaultSearchDescription,
|
|
285
|
+
global: true,
|
|
286
|
+
agentName
|
|
287
|
+
};
|
|
279
288
|
const searchPlugin = createSearchPlugin(searchConfig);
|
|
280
|
-
await
|
|
281
|
-
configuredServers.set(server, true);
|
|
289
|
+
await serverRef.addPlugin(searchPlugin);
|
|
282
290
|
}
|
|
283
291
|
},
|
|
284
292
|
transformTool: (tool, context) => {
|
|
285
293
|
const originalExecute = tool.execute;
|
|
294
|
+
const searchToolName = agentName ? `${agentName}__search-tool-result` : "search-tool-result";
|
|
286
295
|
tool.execute = async (args) => {
|
|
287
296
|
try {
|
|
288
297
|
const result = await originalExecute(args);
|
|
@@ -314,7 +323,7 @@ ${preview}
|
|
|
314
323
|
\`\`\`
|
|
315
324
|
|
|
316
325
|
**To read/understand the full content:**
|
|
317
|
-
- Use the \`
|
|
326
|
+
- Use the \`${searchToolName}\` tool with pattern: \`${searchToolName} {"pattern": "your-search-term"}\`
|
|
318
327
|
- Search supports regex patterns for advanced queries`
|
|
319
328
|
}
|
|
320
329
|
]
|
|
@@ -328,7 +337,8 @@ ${preview}
|
|
|
328
337
|
return tool;
|
|
329
338
|
},
|
|
330
339
|
dispose: () => {
|
|
331
|
-
|
|
340
|
+
serverRef = null;
|
|
341
|
+
agentName = null;
|
|
332
342
|
tempDir = null;
|
|
333
343
|
}
|
|
334
344
|
};
|
|
@@ -338,9 +348,205 @@ var defaultLargeResultPlugin = createLargeResultPlugin({
|
|
|
338
348
|
previewSize: 4e3
|
|
339
349
|
});
|
|
340
350
|
var large_result_default = defaultLargeResultPlugin;
|
|
351
|
+
|
|
352
|
+
// __mcpc__core_latest/node_modules/@mcpc/core/src/plugins/skills.js
|
|
353
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
354
|
+
import { join as join2, relative as relative2, resolve as resolve2 } from "node:path";
|
|
355
|
+
function parseFrontmatter(content) {
|
|
356
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
357
|
+
if (!match) return null;
|
|
358
|
+
const yaml = match[1];
|
|
359
|
+
const result = {};
|
|
360
|
+
for (const line of yaml.split("\n")) {
|
|
361
|
+
if (line.trim() === "metadata:") {
|
|
362
|
+
result.metadata = {};
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const metadataMatch = line.match(/^\s{2}(\w+):\s*"?([^"]*)"?$/);
|
|
366
|
+
if (metadataMatch && result.metadata) {
|
|
367
|
+
result.metadata[metadataMatch[1]] = metadataMatch[2];
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const fieldMatch = line.match(/^(\S+):\s*(.*)$/);
|
|
371
|
+
if (fieldMatch) {
|
|
372
|
+
const [, key, value] = fieldMatch;
|
|
373
|
+
result[key] = value.replace(/^["']|["']$/g, "");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (!result.name || !result.description) return null;
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
function extractBody(content) {
|
|
380
|
+
return content.replace(/^---\n[\s\S]*?\n---\n*/, "");
|
|
381
|
+
}
|
|
382
|
+
async function scanSkills(basePath) {
|
|
383
|
+
const skills = [];
|
|
384
|
+
try {
|
|
385
|
+
const entries = await readdir(basePath, {
|
|
386
|
+
withFileTypes: true
|
|
387
|
+
});
|
|
388
|
+
for (const entry of entries) {
|
|
389
|
+
if (!entry.isDirectory()) continue;
|
|
390
|
+
const skillDir = join2(basePath, entry.name);
|
|
391
|
+
const skillFile = join2(skillDir, "SKILL.md");
|
|
392
|
+
try {
|
|
393
|
+
await stat(skillFile);
|
|
394
|
+
const content = await readFile(skillFile, "utf-8");
|
|
395
|
+
const frontmatter = parseFrontmatter(content);
|
|
396
|
+
if (frontmatter && frontmatter.name === entry.name) {
|
|
397
|
+
skills.push({
|
|
398
|
+
...frontmatter,
|
|
399
|
+
basePath: skillDir,
|
|
400
|
+
filePath: skillFile
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
} catch {
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
return skills;
|
|
409
|
+
}
|
|
410
|
+
function generateToolDescription(skills, agentName) {
|
|
411
|
+
if (skills.length === 0) {
|
|
412
|
+
return "Load a skill's instructions. No skills available.";
|
|
413
|
+
}
|
|
414
|
+
const skillsList = skills.map((s) => `- ${s.name}: ${s.description}`).join("\n");
|
|
415
|
+
const toolName = `${agentName}__load-skill`;
|
|
416
|
+
return `Load a skill's detailed instructions or reference files for the "${agentName}" agent.
|
|
417
|
+
|
|
418
|
+
Available skills:
|
|
419
|
+
${skillsList}
|
|
420
|
+
|
|
421
|
+
Usage:
|
|
422
|
+
- ${toolName}({ skill: "skill-name" }) - Load main SKILL.md content
|
|
423
|
+
- ${toolName}({ skill: "skill-name", ref: "references/file.md" }) - Load reference file`;
|
|
424
|
+
}
|
|
425
|
+
function createSkillsPlugin(options) {
|
|
426
|
+
const { paths } = options;
|
|
427
|
+
const skillsMap = /* @__PURE__ */ new Map();
|
|
428
|
+
let serverRef = null;
|
|
429
|
+
return {
|
|
430
|
+
name: "plugin-skills",
|
|
431
|
+
version: "1.0.0",
|
|
432
|
+
// Save server reference
|
|
433
|
+
configureServer: (server) => {
|
|
434
|
+
serverRef = server;
|
|
435
|
+
},
|
|
436
|
+
// Scan directories and register tool
|
|
437
|
+
composeStart: async (context) => {
|
|
438
|
+
skillsMap.clear();
|
|
439
|
+
for (const dir of paths) {
|
|
440
|
+
const skills = await scanSkills(dir);
|
|
441
|
+
for (const skill of skills) {
|
|
442
|
+
skillsMap.set(skill.name, skill);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const agentName = context.serverName;
|
|
446
|
+
const toolDescription = generateToolDescription(Array.from(skillsMap.values()), agentName);
|
|
447
|
+
const toolName = `${agentName}__load-skill`;
|
|
448
|
+
if (serverRef) {
|
|
449
|
+
serverRef.tool(toolName, toolDescription, jsonSchema({
|
|
450
|
+
type: "object",
|
|
451
|
+
properties: {
|
|
452
|
+
skill: {
|
|
453
|
+
type: "string",
|
|
454
|
+
description: "The skill name to load"
|
|
455
|
+
},
|
|
456
|
+
ref: {
|
|
457
|
+
type: "string",
|
|
458
|
+
description: "Optional: relative path to a reference file within the skill directory"
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
required: [
|
|
462
|
+
"skill"
|
|
463
|
+
]
|
|
464
|
+
}), async (args) => {
|
|
465
|
+
const meta = skillsMap.get(args.skill);
|
|
466
|
+
if (!meta) {
|
|
467
|
+
return {
|
|
468
|
+
content: [
|
|
469
|
+
{
|
|
470
|
+
type: "text",
|
|
471
|
+
text: `Skill "${args.skill}" not found`
|
|
472
|
+
}
|
|
473
|
+
],
|
|
474
|
+
isError: true
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
if (args.ref) {
|
|
478
|
+
const refPath = resolve2(meta.basePath, args.ref);
|
|
479
|
+
const relPath = relative2(meta.basePath, refPath);
|
|
480
|
+
if (relPath.startsWith("..")) {
|
|
481
|
+
return {
|
|
482
|
+
content: [
|
|
483
|
+
{
|
|
484
|
+
type: "text",
|
|
485
|
+
text: `Invalid ref path: ${args.ref}`
|
|
486
|
+
}
|
|
487
|
+
],
|
|
488
|
+
isError: true
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
try {
|
|
492
|
+
const content = await readFile(refPath, "utf-8");
|
|
493
|
+
return {
|
|
494
|
+
content: [
|
|
495
|
+
{
|
|
496
|
+
type: "text",
|
|
497
|
+
text: content
|
|
498
|
+
}
|
|
499
|
+
]
|
|
500
|
+
};
|
|
501
|
+
} catch {
|
|
502
|
+
return {
|
|
503
|
+
content: [
|
|
504
|
+
{
|
|
505
|
+
type: "text",
|
|
506
|
+
text: `File not found: ${args.ref}`
|
|
507
|
+
}
|
|
508
|
+
],
|
|
509
|
+
isError: true
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
try {
|
|
514
|
+
const content = await readFile(meta.filePath, "utf-8");
|
|
515
|
+
const body = extractBody(content);
|
|
516
|
+
return {
|
|
517
|
+
content: [
|
|
518
|
+
{
|
|
519
|
+
type: "text",
|
|
520
|
+
text: body
|
|
521
|
+
}
|
|
522
|
+
]
|
|
523
|
+
};
|
|
524
|
+
} catch {
|
|
525
|
+
return {
|
|
526
|
+
content: [
|
|
527
|
+
{
|
|
528
|
+
type: "text",
|
|
529
|
+
text: `Failed to load skill: ${args.skill}`
|
|
530
|
+
}
|
|
531
|
+
],
|
|
532
|
+
isError: true
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
}, {
|
|
536
|
+
internal: false
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
dispose: () => {
|
|
541
|
+
skillsMap.clear();
|
|
542
|
+
serverRef = null;
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
}
|
|
341
546
|
export {
|
|
342
547
|
createLargeResultPlugin,
|
|
343
548
|
createSearchPlugin,
|
|
549
|
+
createSkillsPlugin,
|
|
344
550
|
large_result_default as defaultLargeResultPlugin,
|
|
345
551
|
search_tool_default as defaultSearchPlugin
|
|
346
552
|
};
|
package/types/plugins.d.ts
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
* @module
|
|
38
38
|
*/ export { createSearchPlugin } from "./src/plugins/search-tool.js";
|
|
39
39
|
export { createLargeResultPlugin } from "./src/plugins/large-result.js";
|
|
40
|
+
export { createSkillsPlugin } from "./src/plugins/skills.js";
|
|
40
41
|
export { default as defaultSearchPlugin } from "./src/plugins/search-tool.js";
|
|
41
42
|
export { default as defaultLargeResultPlugin } from "./src/plugins/large-result.js";
|
|
42
43
|
export type { SearchOptions } from "./src/plugins/search-tool.js";
|
package/types/plugins.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.d.ts","sources":["../plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCC,GAGD,SAAS,kBAAkB,uCAAuC;AAClE,SAAS,uBAAuB,wCAAwC;
|
|
1
|
+
{"version":3,"file":"plugins.d.ts","sources":["../plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCC,GAGD,SAAS,kBAAkB,uCAAuC;AAClE,SAAS,uBAAuB,wCAAwC;AACxE,SAAS,kBAAkB,kCAAkC;AAG7D,SAAS,WAAW,mBAAmB,uCAAuC;AAC9E,SAAS,WAAW,wBAAwB,wCAAwC;AAGpF,cAAc,aAAa,uCAAuC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"large-result.d.ts","sources":["../../../src/plugins/large-result.ts"],"names":[],"mappings":"AAQA,SAA6B,KAAK,aAAa,2BAA2B;AAC1E,
|
|
1
|
+
{"version":3,"file":"large-result.d.ts","sources":["../../../src/plugins/large-result.ts"],"names":[],"mappings":"AAQA,SAA6B,KAAK,aAAa,2BAA2B;AAC1E,cAAmC,UAAU,6BAA6B;UAGhE;EACR,UAAU,MAAM;EAChB,cAAc,MAAM;EACpB,UAAU,MAAM;EAChB,SAAS;;AAGX;;;CAGC,GACD,OAAO,iBAAS,wBACd,UAAS,aAAkB,GAC1B;AAsHH;;CAEC,GACD,cAAM,0BAA0B;AAMhC,OAAO,cAAM,kBAAuC;AAEpD,eAAe,yBAAyB"}
|
|
@@ -9,6 +9,7 @@ import type { ToolPlugin } from "../plugin-types.js";
|
|
|
9
9
|
/** Whether search should be case sensitive (default: false) */ caseSensitive?: boolean;
|
|
10
10
|
/** Search timeout in milliseconds (default: 30000) */ timeoutMs?: number;
|
|
11
11
|
/** Custom description for the search tool (overrides default) */ toolDescription?: string;
|
|
12
|
+
/** Agent name prefix for tool naming (e.g., "my-agent" -> "my-agent__search-tool-result") */ agentName?: string;
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
14
15
|
* Create a search plugin that adds file search capability with size limits
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-tool.d.ts","sources":["../../../src/plugins/search-tool.ts"],"names":[],"mappings":"AASA,cAAc,UAAU,6BAA6B;AAIrD;;CAEC,GACD,iBAAiB;EACf,sDAAsD,GACtD,SAAS,OAAO;EAChB,6DAA6D,GAC7D,aAAa,MAAM;EACnB,sDAAsD,GACtD,gBAAgB,MAAM;EACtB,mHAAmH,GACnH,aAAa,MAAM;EACnB,6DAA6D,GAC7D,gBAAgB,OAAO;EACvB,oDAAoD,GACpD,YAAY,MAAM;EAClB,+DAA+D,GAC/D,kBAAkB,MAAM;;
|
|
1
|
+
{"version":3,"file":"search-tool.d.ts","sources":["../../../src/plugins/search-tool.ts"],"names":[],"mappings":"AASA,cAAc,UAAU,6BAA6B;AAIrD;;CAEC,GACD,iBAAiB;EACf,sDAAsD,GACtD,SAAS,OAAO;EAChB,6DAA6D,GAC7D,aAAa,MAAM;EACnB,sDAAsD,GACtD,gBAAgB,MAAM;EACtB,mHAAmH,GACnH,aAAa,MAAM;EACnB,6DAA6D,GAC7D,gBAAgB,OAAO;EACvB,oDAAoD,GACpD,YAAY,MAAM;EAClB,+DAA+D,GAC/D,kBAAkB,MAAM;EACxB,2FAA2F,GAC3F,YAAY,MAAM;;AAGpB;;CAEC,GACD,OAAO,iBAAS,mBAAmB,UAAS,aAAkB,GAAG;AAsQjE;;CAEC,GACD,cAAM,qBAAqB;AAS3B,OAAO,cAAM,kBAAkC;AAE/C,eAAe,oBAAoB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ToolPlugin } from "../plugin-types.js";
|
|
2
|
+
interface SkillsPluginOptions {
|
|
3
|
+
/** Directories to scan for skills */ paths: string[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Create a skills plugin that adds domain knowledge with lazy loading
|
|
7
|
+
*/ export declare function createSkillsPlugin(options: SkillsPluginOptions): ToolPlugin;
|
|
8
|
+
/**
|
|
9
|
+
* Factory function for parameterized usage via string path
|
|
10
|
+
*
|
|
11
|
+
* Supports query parameters:
|
|
12
|
+
* - paths: Comma-separated list of skill directories
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* // Via string path with query params
|
|
17
|
+
* plugins: [
|
|
18
|
+
* "@mcpc/core/plugins/skills?paths=./skills,./more-skills"
|
|
19
|
+
* ]
|
|
20
|
+
* ```
|
|
21
|
+
*/ export declare function createPlugin(params: Record<string, string>): ToolPlugin;
|
|
22
|
+
export default createSkillsPlugin;
|
|
23
|
+
//# sourceMappingURL=skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sources":["../../../src/plugins/skills.ts"],"names":[],"mappings":"AAOA,cAAmC,UAAU,6BAA6B;UAqBhE;EACR,mCAAmC,GACnC,OAAO,MAAM;;AAgHf;;CAEC,GACD,OAAO,iBAAS,mBAAmB,SAAS,mBAAmB,GAAG;AA0HlE;;;;;;;;;;;;;CAaC,GACD,OAAO,iBAAS,aAAa,QAAQ,OAAO,MAAM,EAAE,MAAM,CAAC,GAAG;AAM9D,eAAe,mBAAmB"}
|