@perstack/base 0.0.64 → 0.0.66
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/bin/server.d.ts +3 -1
- package/dist/bin/server.d.ts.map +1 -0
- package/dist/bin/server.js +15 -15
- package/dist/bin/server.js.map +1 -1
- package/dist/package.json +46 -0
- package/dist/src/index.d.ts +12 -198
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +13 -3
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/mime.d.ts +2 -0
- package/dist/src/lib/mime.d.ts.map +1 -0
- package/dist/src/lib/mime.js +13 -0
- package/dist/src/lib/mime.js.map +1 -0
- package/dist/src/lib/path.d.ts +3 -0
- package/dist/src/lib/path.d.ts.map +1 -0
- package/dist/src/lib/path.js +49 -0
- package/dist/src/lib/path.js.map +1 -0
- package/dist/src/lib/safe-file.d.ts +4 -0
- package/dist/src/lib/safe-file.d.ts.map +1 -0
- package/dist/src/lib/safe-file.js +38 -0
- package/dist/src/lib/safe-file.js.map +1 -0
- package/dist/src/lib/tool-result.d.ts +4 -0
- package/dist/src/lib/tool-result.d.ts.map +1 -0
- package/dist/src/lib/tool-result.js +9 -0
- package/dist/src/lib/tool-result.js.map +1 -0
- package/dist/src/server.d.ts +20 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +50 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/tools/attempt-completion.d.ts +11 -0
- package/dist/src/tools/attempt-completion.d.ts.map +1 -0
- package/dist/src/tools/attempt-completion.js +27 -0
- package/dist/src/tools/attempt-completion.js.map +1 -0
- package/dist/src/tools/edit-text-file.d.ts +12 -0
- package/dist/src/tools/edit-text-file.d.ts.map +1 -0
- package/dist/src/tools/edit-text-file.js +56 -0
- package/dist/src/tools/edit-text-file.js.map +1 -0
- package/dist/src/tools/exec.d.ts +16 -0
- package/dist/src/tools/exec.d.ts.map +1 -0
- package/dist/src/tools/exec.js +86 -0
- package/dist/src/tools/exec.js.map +1 -0
- package/dist/src/tools/read-image-file.d.ts +10 -0
- package/dist/src/tools/read-image-file.d.ts.map +1 -0
- package/dist/src/tools/read-image-file.js +48 -0
- package/dist/src/tools/read-image-file.js.map +1 -0
- package/dist/src/tools/read-pdf-file.d.ts +10 -0
- package/dist/src/tools/read-pdf-file.d.ts.map +1 -0
- package/dist/src/tools/read-pdf-file.js +48 -0
- package/dist/src/tools/read-pdf-file.js.map +1 -0
- package/dist/src/tools/read-text-file.d.ts +13 -0
- package/dist/src/tools/read-text-file.d.ts.map +1 -0
- package/dist/src/tools/read-text-file.js +46 -0
- package/dist/src/tools/read-text-file.js.map +1 -0
- package/dist/src/tools/skill-management.d.ts +61 -0
- package/dist/src/tools/skill-management.d.ts.map +1 -0
- package/dist/src/tools/skill-management.js +162 -0
- package/dist/src/tools/skill-management.js.map +1 -0
- package/dist/src/tools/todo.d.ts +26 -0
- package/dist/src/tools/todo.d.ts.map +1 -0
- package/dist/src/tools/todo.js +74 -0
- package/dist/src/tools/todo.js.map +1 -0
- package/dist/src/tools/write-text-file.d.ts +10 -0
- package/dist/src/tools/write-text-file.d.ts.map +1 -0
- package/dist/src/tools/write-text-file.js +41 -0
- package/dist/src/tools/write-text-file.js.map +1 -0
- package/package.json +23 -21
- package/LICENSE +0 -202
- package/dist/server-MjrduhwZ.js +0 -607
- package/dist/server-MjrduhwZ.js.map +0 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare function editTextFile(input: {
|
|
3
|
+
path: string;
|
|
4
|
+
newText: string;
|
|
5
|
+
oldText: string;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
path: string;
|
|
8
|
+
newText: string;
|
|
9
|
+
oldText: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function registerEditTextFile(server: McpServer): void;
|
|
12
|
+
//# sourceMappingURL=edit-text-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-text-file.d.ts","sourceRoot":"","sources":["../../../src/tools/edit-text-file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAMxE,wBAAsB,YAAY,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;;;GAgB3F;AAiBD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,QAqBrD"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
import { validatePath } from "../lib/path.js";
|
|
4
|
+
import { safeReadFile, safeWriteFile } from "../lib/safe-file.js";
|
|
5
|
+
import { errorToolResult, successToolResult } from "../lib/tool-result.js";
|
|
6
|
+
export async function editTextFile(input) {
|
|
7
|
+
const { path, newText, oldText } = input;
|
|
8
|
+
const validatedPath = await validatePath(path);
|
|
9
|
+
const stats = await stat(validatedPath).catch(() => null);
|
|
10
|
+
if (!stats) {
|
|
11
|
+
throw new Error(`File ${path} does not exist.`);
|
|
12
|
+
}
|
|
13
|
+
if (!(stats.mode & 0o200)) {
|
|
14
|
+
throw new Error(`File ${path} is not writable`);
|
|
15
|
+
}
|
|
16
|
+
await applyFileEdit(validatedPath, newText, oldText);
|
|
17
|
+
return {
|
|
18
|
+
path: validatedPath,
|
|
19
|
+
newText,
|
|
20
|
+
oldText,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function normalizeLineEndings(text) {
|
|
24
|
+
return text.replace(/\r\n/g, "\n");
|
|
25
|
+
}
|
|
26
|
+
async function applyFileEdit(filePath, newText, oldText) {
|
|
27
|
+
const content = normalizeLineEndings(await safeReadFile(filePath));
|
|
28
|
+
const normalizedOld = normalizeLineEndings(oldText);
|
|
29
|
+
const normalizedNew = normalizeLineEndings(newText);
|
|
30
|
+
if (!content.includes(normalizedOld)) {
|
|
31
|
+
throw new Error(`Could not find exact match for oldText in file ${filePath}`);
|
|
32
|
+
}
|
|
33
|
+
const modifiedContent = content.replace(normalizedOld, normalizedNew);
|
|
34
|
+
await safeWriteFile(filePath, modifiedContent);
|
|
35
|
+
}
|
|
36
|
+
export function registerEditTextFile(server) {
|
|
37
|
+
server.registerTool("editTextFile", {
|
|
38
|
+
title: "Edit text file",
|
|
39
|
+
description: "Replace exact text in an existing file. Normalizes line endings (CRLF → LF).",
|
|
40
|
+
inputSchema: {
|
|
41
|
+
path: z.string().describe("Target file path to edit."),
|
|
42
|
+
newText: z.string().describe("Text to replace with."),
|
|
43
|
+
oldText: z.string().describe("Exact text to find and replace."),
|
|
44
|
+
},
|
|
45
|
+
}, async (input) => {
|
|
46
|
+
try {
|
|
47
|
+
return successToolResult(await editTextFile(input));
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
if (e instanceof Error)
|
|
51
|
+
return errorToolResult(e);
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=edit-text-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-text-file.js","sourceRoot":"","sources":["../../../src/tools/edit-text-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEvC,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAE1E,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAyD;IAC1F,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,CAAA;IACxC,MAAM,aAAa,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAA;IAC9C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;IACzD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,kBAAkB,CAAC,CAAA;IACjD,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,kBAAkB,CAAC,CAAA;IACjD,CAAC;IACD,MAAM,aAAa,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACpD,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,OAAO;QACP,OAAO;KACR,CAAA;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;AACpC,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,OAAe,EAAE,OAAe;IAC7E,MAAM,OAAO,GAAG,oBAAoB,CAAC,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAA;IAClE,MAAM,aAAa,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAA;IACnD,MAAM,aAAa,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAA;IACnD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,kDAAkD,QAAQ,EAAE,CAAC,CAAA;IAC/E,CAAC;IACD,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,aAAa,CAAC,CAAA;IACrE,MAAM,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,8EAA8E;QAC3F,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;YACtD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YACrD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;SAChE;KACF,EACD,KAAK,EAAE,KAAyD,EAAE,EAAE;QAClE,IAAI,CAAC;YACH,OAAO,iBAAiB,CAAC,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC,CAAA;QACrD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,KAAK;gBAAE,OAAO,eAAe,CAAC,CAAC,CAAC,CAAA;YACjD,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
type ExecInput = {
|
|
3
|
+
command: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
env: Record<string, string>;
|
|
6
|
+
cwd: string;
|
|
7
|
+
stdout: boolean;
|
|
8
|
+
stderr: boolean;
|
|
9
|
+
timeout?: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function exec(input: ExecInput): Promise<{
|
|
12
|
+
output: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function registerExec(server: McpServer): void;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../../src/tools/exec.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAWxE,KAAK,SAAS,GAAG;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AACD,wBAAsB,IAAI,CAAC,KAAK,EAAE,SAAS;;GAmB1C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,QAqD7C"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { getFilteredEnv } from "@perstack/core";
|
|
4
|
+
import { z } from "zod/v4";
|
|
5
|
+
import { validatePath } from "../lib/path.js";
|
|
6
|
+
import { successToolResult } from "../lib/tool-result.js";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
function isExecError(error) {
|
|
9
|
+
return error instanceof Error && "code" in error;
|
|
10
|
+
}
|
|
11
|
+
export async function exec(input) {
|
|
12
|
+
const validatedCwd = await validatePath(input.cwd);
|
|
13
|
+
const { stdout, stderr } = await execFileAsync(input.command, input.args, {
|
|
14
|
+
cwd: validatedCwd,
|
|
15
|
+
env: getFilteredEnv(input.env),
|
|
16
|
+
timeout: input.timeout,
|
|
17
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
18
|
+
});
|
|
19
|
+
let output = "";
|
|
20
|
+
if (input.stdout) {
|
|
21
|
+
output += stdout;
|
|
22
|
+
}
|
|
23
|
+
if (input.stderr) {
|
|
24
|
+
output += stderr;
|
|
25
|
+
}
|
|
26
|
+
if (!output.trim()) {
|
|
27
|
+
output = "Command executed successfully, but produced no output.";
|
|
28
|
+
}
|
|
29
|
+
return { output };
|
|
30
|
+
}
|
|
31
|
+
export function registerExec(server) {
|
|
32
|
+
server.registerTool("exec", {
|
|
33
|
+
title: "Execute Command",
|
|
34
|
+
description: "Execute a system command. Returns stdout/stderr.",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
command: z.string().describe("The command to execute"),
|
|
37
|
+
args: z.array(z.string()).describe("The arguments to pass to the command"),
|
|
38
|
+
env: z.record(z.string(), z.string()).describe("The environment variables to set"),
|
|
39
|
+
cwd: z.string().describe("The working directory to execute the command in"),
|
|
40
|
+
stdout: z.boolean().describe("Whether to capture the standard output"),
|
|
41
|
+
stderr: z.boolean().describe("Whether to capture the standard error"),
|
|
42
|
+
timeout: z
|
|
43
|
+
.number()
|
|
44
|
+
.optional()
|
|
45
|
+
.default(60000)
|
|
46
|
+
.describe("Timeout in milliseconds (default: 60000)"),
|
|
47
|
+
},
|
|
48
|
+
}, async (input) => {
|
|
49
|
+
try {
|
|
50
|
+
return successToolResult(await exec(input));
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
let message;
|
|
54
|
+
let stdout;
|
|
55
|
+
let stderr;
|
|
56
|
+
if (isExecError(error)) {
|
|
57
|
+
if ((error.killed || error.signal === "SIGTERM") && typeof input.timeout === "number") {
|
|
58
|
+
message = `Command timed out after ${input.timeout}ms.`;
|
|
59
|
+
}
|
|
60
|
+
else if (error.message.includes("timeout")) {
|
|
61
|
+
message = `Command timed out after ${input.timeout}ms.`;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
message = error.message;
|
|
65
|
+
}
|
|
66
|
+
stdout = error.stdout;
|
|
67
|
+
stderr = error.stderr;
|
|
68
|
+
}
|
|
69
|
+
else if (error instanceof Error) {
|
|
70
|
+
message = error.message;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
message = "An unknown error occurred.";
|
|
74
|
+
}
|
|
75
|
+
const result = { error: message };
|
|
76
|
+
if (stdout && input.stdout) {
|
|
77
|
+
result.stdout = stdout;
|
|
78
|
+
}
|
|
79
|
+
if (stderr && input.stderr) {
|
|
80
|
+
result.stderr = stderr;
|
|
81
|
+
}
|
|
82
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/tools/exec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare function readImageFile(input: {
|
|
3
|
+
path: string;
|
|
4
|
+
}): Promise<{
|
|
5
|
+
path: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
size: number;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function registerReadImageFile(server: McpServer): void;
|
|
10
|
+
//# sourceMappingURL=read-image-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-image-file.d.ts","sourceRoot":"","sources":["../../../src/tools/read-image-file.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAOxE,wBAAsB,aAAa,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;;;;GAuB1D;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAmBtD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
|
+
import { lookupMimeType } from "../lib/mime.js";
|
|
5
|
+
import { validatePath } from "../lib/path.js";
|
|
6
|
+
import { errorToolResult, successToolResult } from "../lib/tool-result.js";
|
|
7
|
+
const MAX_IMAGE_SIZE = 15 * 1024 * 1024;
|
|
8
|
+
export async function readImageFile(input) {
|
|
9
|
+
const { path } = input;
|
|
10
|
+
const validatedPath = await validatePath(path);
|
|
11
|
+
const isFile = existsSync(validatedPath);
|
|
12
|
+
if (!isFile) {
|
|
13
|
+
throw new Error(`File ${path} does not exist.`);
|
|
14
|
+
}
|
|
15
|
+
const mimeType = lookupMimeType(validatedPath);
|
|
16
|
+
if (!mimeType || !["image/png", "image/jpeg", "image/gif", "image/webp"].includes(mimeType)) {
|
|
17
|
+
throw new Error(`File ${path} is not supported.`);
|
|
18
|
+
}
|
|
19
|
+
const fileStats = await stat(validatedPath);
|
|
20
|
+
const fileSizeMB = fileStats.size / (1024 * 1024);
|
|
21
|
+
if (fileStats.size > MAX_IMAGE_SIZE) {
|
|
22
|
+
throw new Error(`Image file too large (${fileSizeMB.toFixed(1)}MB). Maximum supported size is ${MAX_IMAGE_SIZE / (1024 * 1024)}MB. Please use a smaller image file.`);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
path: validatedPath,
|
|
26
|
+
mimeType,
|
|
27
|
+
size: fileStats.size,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function registerReadImageFile(server) {
|
|
31
|
+
server.registerTool("readImageFile", {
|
|
32
|
+
title: "Read image file",
|
|
33
|
+
description: "Read an image file as base64. Supports PNG, JPEG, GIF, WebP. Max 15MB.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
path: z.string(),
|
|
36
|
+
},
|
|
37
|
+
}, async (input) => {
|
|
38
|
+
try {
|
|
39
|
+
return successToolResult(await readImageFile(input));
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
if (e instanceof Error)
|
|
43
|
+
return errorToolResult(e);
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=read-image-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-image-file.js","sourceRoot":"","sources":["../../../src/tools/read-image-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEvC,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAE1E,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA;AACvC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAuB;IACzD,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAA;IACtB,MAAM,aAAa,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAA;IAC9C,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,CAAC,CAAA;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,kBAAkB,CAAC,CAAA;IACjD,CAAC;IACD,MAAM,QAAQ,GAAG,cAAc,CAAC,aAAa,CAAC,CAAA;IAC9C,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,oBAAoB,CAAC,CAAA;IACnD,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;IACjD,IAAI,SAAS,CAAC,IAAI,GAAG,cAAc,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACb,yBAAyB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,kCAAkC,cAAc,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,sCAAsC,CACrJ,CAAA;IACH,CAAC;IACD,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,QAAQ;QACR,IAAI,EAAE,SAAS,CAAC,IAAI;KACrB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,wEAAwE;QACrF,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;SACjB;KACF,EACD,KAAK,EAAE,KAAuB,EAAE,EAAE;QAChC,IAAI,CAAC;YACH,OAAO,iBAAiB,CAAC,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC,CAAA;QACtD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,KAAK;gBAAE,OAAO,eAAe,CAAC,CAAC,CAAC,CAAA;YACjD,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare function readPdfFile(input: {
|
|
3
|
+
path: string;
|
|
4
|
+
}): Promise<{
|
|
5
|
+
path: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
size: number;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function registerReadPdfFile(server: McpServer): void;
|
|
10
|
+
//# sourceMappingURL=read-pdf-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-pdf-file.d.ts","sourceRoot":"","sources":["../../../src/tools/read-pdf-file.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAOxE,wBAAsB,WAAW,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;;;;GAuBxD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,QAmBpD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
|
+
import { lookupMimeType } from "../lib/mime.js";
|
|
5
|
+
import { validatePath } from "../lib/path.js";
|
|
6
|
+
import { errorToolResult, successToolResult } from "../lib/tool-result.js";
|
|
7
|
+
const MAX_PDF_SIZE = 30 * 1024 * 1024;
|
|
8
|
+
export async function readPdfFile(input) {
|
|
9
|
+
const { path } = input;
|
|
10
|
+
const validatedPath = await validatePath(path);
|
|
11
|
+
const isFile = existsSync(validatedPath);
|
|
12
|
+
if (!isFile) {
|
|
13
|
+
throw new Error(`File ${path} does not exist.`);
|
|
14
|
+
}
|
|
15
|
+
const mimeType = lookupMimeType(validatedPath);
|
|
16
|
+
if (mimeType !== "application/pdf") {
|
|
17
|
+
throw new Error(`File ${path} is not a PDF file.`);
|
|
18
|
+
}
|
|
19
|
+
const fileStats = await stat(validatedPath);
|
|
20
|
+
const fileSizeMB = fileStats.size / (1024 * 1024);
|
|
21
|
+
if (fileStats.size > MAX_PDF_SIZE) {
|
|
22
|
+
throw new Error(`PDF file too large (${fileSizeMB.toFixed(1)}MB). Maximum supported size is ${MAX_PDF_SIZE / (1024 * 1024)}MB. Please use a smaller PDF file.`);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
path: validatedPath,
|
|
26
|
+
mimeType,
|
|
27
|
+
size: fileStats.size,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function registerReadPdfFile(server) {
|
|
31
|
+
server.registerTool("readPdfFile", {
|
|
32
|
+
title: "Read PDF file",
|
|
33
|
+
description: "Read a PDF file as base64. Max 30MB.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
path: z.string(),
|
|
36
|
+
},
|
|
37
|
+
}, async (input) => {
|
|
38
|
+
try {
|
|
39
|
+
return successToolResult(await readPdfFile(input));
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
if (e instanceof Error)
|
|
43
|
+
return errorToolResult(e);
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=read-pdf-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-pdf-file.js","sourceRoot":"","sources":["../../../src/tools/read-pdf-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEvC,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAE1E,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA;AACrC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAuB;IACvD,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAA;IACtB,MAAM,aAAa,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAA;IAC9C,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,CAAC,CAAA;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,kBAAkB,CAAC,CAAA;IACjD,CAAC;IACD,MAAM,QAAQ,GAAG,cAAc,CAAC,aAAa,CAAC,CAAA;IAC9C,IAAI,QAAQ,KAAK,iBAAiB,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,qBAAqB,CAAC,CAAA;IACpD,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;IACjD,IAAI,SAAS,CAAC,IAAI,GAAG,YAAY,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,uBAAuB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,kCAAkC,YAAY,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,oCAAoC,CAC/I,CAAA;IACH,CAAC;IACD,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,QAAQ;QACR,IAAI,EAAE,SAAS,CAAC,IAAI;KACrB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,sCAAsC;QACnD,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;SACjB;KACF,EACD,KAAK,EAAE,KAAuB,EAAE,EAAE;QAChC,IAAI,CAAC;YACH,OAAO,iBAAiB,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC,CAAA;QACpD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,KAAK;gBAAE,OAAO,eAAe,CAAC,CAAC,CAAC,CAAA;YACjD,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare function readTextFile(input: {
|
|
3
|
+
path: string;
|
|
4
|
+
from?: number;
|
|
5
|
+
to?: number;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
path: string;
|
|
8
|
+
content: string;
|
|
9
|
+
from: number;
|
|
10
|
+
to: number;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function registerReadTextFile(server: McpServer): void;
|
|
13
|
+
//# sourceMappingURL=read-text-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-text-file.d.ts","sourceRoot":"","sources":["../../../src/tools/read-text-file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAMxE,wBAAsB,YAAY,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;GAmBrF;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,QAqBrD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
import { validatePath } from "../lib/path.js";
|
|
4
|
+
import { safeReadFile } from "../lib/safe-file.js";
|
|
5
|
+
import { errorToolResult, successToolResult } from "../lib/tool-result.js";
|
|
6
|
+
export async function readTextFile(input) {
|
|
7
|
+
const { path, from, to } = input;
|
|
8
|
+
const validatedPath = await validatePath(path);
|
|
9
|
+
const stats = await stat(validatedPath).catch(() => null);
|
|
10
|
+
if (!stats) {
|
|
11
|
+
throw new Error(`File ${path} does not exist.`);
|
|
12
|
+
}
|
|
13
|
+
const fileContent = await safeReadFile(validatedPath);
|
|
14
|
+
const lines = fileContent.split("\n");
|
|
15
|
+
const fromLine = from ?? 0;
|
|
16
|
+
const toLine = to ?? lines.length;
|
|
17
|
+
const selectedLines = lines.slice(fromLine, toLine);
|
|
18
|
+
const content = selectedLines.join("\n");
|
|
19
|
+
return {
|
|
20
|
+
path,
|
|
21
|
+
content,
|
|
22
|
+
from: fromLine,
|
|
23
|
+
to: toLine,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function registerReadTextFile(server) {
|
|
27
|
+
server.registerTool("readTextFile", {
|
|
28
|
+
title: "Read text file",
|
|
29
|
+
description: "Read a UTF-8 text file. Supports partial reading via line ranges.",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
path: z.string(),
|
|
32
|
+
from: z.number().optional().describe("The line number to start reading from."),
|
|
33
|
+
to: z.number().optional().describe("The line number to stop reading at."),
|
|
34
|
+
},
|
|
35
|
+
}, async (input) => {
|
|
36
|
+
try {
|
|
37
|
+
return successToolResult(await readTextFile(input));
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
if (e instanceof Error)
|
|
41
|
+
return errorToolResult(e);
|
|
42
|
+
throw e;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=read-text-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-text-file.js","sourceRoot":"","sources":["../../../src/tools/read-text-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEvC,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAE1E,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAmD;IACpF,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,KAAK,CAAA;IAChC,MAAM,aAAa,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAA;IAC9C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;IACzD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,kBAAkB,CAAC,CAAA;IACjD,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,aAAa,CAAC,CAAA;IACrD,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,CAAA;IAC1B,MAAM,MAAM,GAAG,EAAE,IAAI,KAAK,CAAC,MAAM,CAAA;IACjC,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IACnD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACxC,OAAO;QACL,IAAI;QACJ,OAAO;QACP,IAAI,EAAE,QAAQ;QACd,EAAE,EAAE,MAAM;KACX,CAAA;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,mEAAmE;QAChF,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;YAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;YAC9E,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;SAC1E;KACF,EACD,KAAK,EAAE,KAAmD,EAAE,EAAE;QAC5D,IAAI,CAAC;YACH,OAAO,iBAAiB,CAAC,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC,CAAA;QACrD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,KAAK;gBAAE,OAAO,eAAe,CAAC,CAAC,CAAC,CAAA;YACjD,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export interface SkillManagementCallbacks {
|
|
3
|
+
addSkill(input: {
|
|
4
|
+
name: string;
|
|
5
|
+
type: "mcpStdioSkill" | "mcpSseSkill";
|
|
6
|
+
command?: string;
|
|
7
|
+
packageName?: string;
|
|
8
|
+
args?: string[];
|
|
9
|
+
requiredEnv?: string[];
|
|
10
|
+
endpoint?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
rule?: string;
|
|
13
|
+
pick?: string[];
|
|
14
|
+
omit?: string[];
|
|
15
|
+
}): Promise<{
|
|
16
|
+
tools: string[];
|
|
17
|
+
}>;
|
|
18
|
+
removeSkill(skillName: string): Promise<void>;
|
|
19
|
+
addDelegate(expertKey: string): Promise<{
|
|
20
|
+
delegateToolName: string;
|
|
21
|
+
}>;
|
|
22
|
+
removeDelegate(expertName: string): Promise<void>;
|
|
23
|
+
createExpert(input: {
|
|
24
|
+
key: string;
|
|
25
|
+
instruction: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
version?: string;
|
|
28
|
+
skills?: Record<string, {
|
|
29
|
+
type: "mcpStdioSkill" | "mcpSseSkill";
|
|
30
|
+
command?: string;
|
|
31
|
+
packageName?: string;
|
|
32
|
+
args?: string[];
|
|
33
|
+
requiredEnv?: string[];
|
|
34
|
+
endpoint?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
rule?: string;
|
|
37
|
+
pick?: string[];
|
|
38
|
+
omit?: string[];
|
|
39
|
+
lazyInit?: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
delegates?: string[];
|
|
42
|
+
tags?: string[];
|
|
43
|
+
providerTools?: string[];
|
|
44
|
+
}): Promise<{
|
|
45
|
+
expertKey: string;
|
|
46
|
+
}>;
|
|
47
|
+
addDelegateFromConfig(input: {
|
|
48
|
+
configPath: string;
|
|
49
|
+
delegateExpertName: string;
|
|
50
|
+
}): Promise<{
|
|
51
|
+
delegateToolName: string;
|
|
52
|
+
}>;
|
|
53
|
+
}
|
|
54
|
+
export declare function registerAddSkill(server: McpServer, callbacks: SkillManagementCallbacks): void;
|
|
55
|
+
export declare function registerRemoveSkill(server: McpServer, callbacks: SkillManagementCallbacks): void;
|
|
56
|
+
export declare function registerAddDelegate(server: McpServer, callbacks: SkillManagementCallbacks): void;
|
|
57
|
+
export declare function registerRemoveDelegate(server: McpServer, callbacks: SkillManagementCallbacks): void;
|
|
58
|
+
export declare function registerCreateExpert(server: McpServer, callbacks: SkillManagementCallbacks): void;
|
|
59
|
+
export declare function registerAddDelegateFromConfig(server: McpServer, callbacks: SkillManagementCallbacks): void;
|
|
60
|
+
export declare function registerSkillManagementTools(server: McpServer, callbacks: SkillManagementCallbacks): void;
|
|
61
|
+
//# sourceMappingURL=skill-management.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-management.d.ts","sourceRoot":"","sources":["../../../src/tools/skill-management.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import { errorToolResult, successToolResult } from "../lib/tool-result.js";
|
|
3
|
+
export function registerAddSkill(server, callbacks) {
|
|
4
|
+
server.registerTool("addSkill", {
|
|
5
|
+
title: "Add skill",
|
|
6
|
+
description: "Dynamically add an MCP skill. Returns the list of tool names provided by the new skill.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
name: z.string().describe("Unique skill name"),
|
|
9
|
+
type: z.enum(["mcpStdioSkill", "mcpSseSkill"]).describe("Skill transport type"),
|
|
10
|
+
command: z.string().optional().describe("Command to execute (for stdio skills)"),
|
|
11
|
+
packageName: z.string().optional().describe("Package name for npx/uvx (for stdio skills)"),
|
|
12
|
+
args: z.array(z.string()).optional().describe("Additional command arguments"),
|
|
13
|
+
requiredEnv: z.array(z.string()).optional().describe("Required environment variable names"),
|
|
14
|
+
endpoint: z.string().optional().describe("SSE endpoint URL (for SSE skills)"),
|
|
15
|
+
description: z.string().optional().describe("Human-readable description"),
|
|
16
|
+
rule: z.string().optional().describe("Usage rules for the LLM"),
|
|
17
|
+
pick: z.array(z.string()).optional().describe("Tool names to include (whitelist)"),
|
|
18
|
+
omit: z.array(z.string()).optional().describe("Tool names to exclude (blacklist)"),
|
|
19
|
+
},
|
|
20
|
+
}, async (input) => {
|
|
21
|
+
try {
|
|
22
|
+
return successToolResult(await callbacks.addSkill(input));
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
if (e instanceof Error)
|
|
26
|
+
return errorToolResult(e);
|
|
27
|
+
throw e;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function registerRemoveSkill(server, callbacks) {
|
|
32
|
+
server.registerTool("removeSkill", {
|
|
33
|
+
title: "Remove skill",
|
|
34
|
+
description: "Dynamically remove an MCP skill by name. Disconnects and removes the skill.",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
skillName: z.string().describe("Name of the skill to remove"),
|
|
37
|
+
},
|
|
38
|
+
}, async (input) => {
|
|
39
|
+
try {
|
|
40
|
+
await callbacks.removeSkill(input.skillName);
|
|
41
|
+
return successToolResult({ removed: input.skillName });
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
if (e instanceof Error)
|
|
45
|
+
return errorToolResult(e);
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export function registerAddDelegate(server, callbacks) {
|
|
51
|
+
server.registerTool("addDelegate", {
|
|
52
|
+
title: "Add delegate",
|
|
53
|
+
description: "Dynamically add a delegate expert. Returns the delegate tool name so you know what to call.",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
expertKey: z.string().describe("Key of the expert to add as a delegate"),
|
|
56
|
+
},
|
|
57
|
+
}, async (input) => {
|
|
58
|
+
try {
|
|
59
|
+
return successToolResult(await callbacks.addDelegate(input.expertKey));
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
if (e instanceof Error)
|
|
63
|
+
return errorToolResult(e);
|
|
64
|
+
throw e;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export function registerRemoveDelegate(server, callbacks) {
|
|
69
|
+
server.registerTool("removeDelegate", {
|
|
70
|
+
title: "Remove delegate",
|
|
71
|
+
description: "Dynamically remove a delegate expert by name.",
|
|
72
|
+
inputSchema: {
|
|
73
|
+
expertName: z.string().describe("Name of the delegate expert to remove"),
|
|
74
|
+
},
|
|
75
|
+
}, async (input) => {
|
|
76
|
+
try {
|
|
77
|
+
await callbacks.removeDelegate(input.expertName);
|
|
78
|
+
return successToolResult({ removed: input.expertName });
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
if (e instanceof Error)
|
|
82
|
+
return errorToolResult(e);
|
|
83
|
+
throw e;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
export function registerCreateExpert(server, callbacks) {
|
|
88
|
+
server.registerTool("createExpert", {
|
|
89
|
+
title: "Create expert",
|
|
90
|
+
description: "Dynamically create an expert definition in memory. Returns the expert key so you can add it as a delegate.",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
key: z.string().describe("Unique expert key (kebab-case)"),
|
|
93
|
+
instruction: z.string().describe("System instruction for the expert"),
|
|
94
|
+
description: z.string().optional().describe("Human-readable description"),
|
|
95
|
+
version: z.string().optional().describe("Semantic version (defaults to 1.0.0)"),
|
|
96
|
+
skills: z
|
|
97
|
+
.record(z.string(), z.object({
|
|
98
|
+
type: z.enum(["mcpStdioSkill", "mcpSseSkill"]).describe("Skill transport type"),
|
|
99
|
+
command: z.string().optional().describe("Command to execute (for stdio skills)"),
|
|
100
|
+
packageName: z
|
|
101
|
+
.string()
|
|
102
|
+
.optional()
|
|
103
|
+
.describe("Package name for npx/uvx (for stdio skills)"),
|
|
104
|
+
args: z.array(z.string()).optional().describe("Additional command arguments"),
|
|
105
|
+
requiredEnv: z
|
|
106
|
+
.array(z.string())
|
|
107
|
+
.optional()
|
|
108
|
+
.describe("Required environment variable names"),
|
|
109
|
+
endpoint: z.string().optional().describe("SSE endpoint URL (for SSE skills)"),
|
|
110
|
+
description: z.string().optional().describe("Human-readable description"),
|
|
111
|
+
rule: z.string().optional().describe("Usage rules for the LLM"),
|
|
112
|
+
pick: z.array(z.string()).optional().describe("Tool names to include (whitelist)"),
|
|
113
|
+
omit: z.array(z.string()).optional().describe("Tool names to exclude (blacklist)"),
|
|
114
|
+
lazyInit: z.boolean().optional().describe("Lazy initialization"),
|
|
115
|
+
}))
|
|
116
|
+
.optional()
|
|
117
|
+
.describe("Skills map (defaults to @perstack/base)"),
|
|
118
|
+
delegates: z.array(z.string()).optional().describe("Expert keys to delegate to"),
|
|
119
|
+
tags: z.array(z.string()).optional().describe("Tags for categorization"),
|
|
120
|
+
providerTools: z.array(z.string()).optional().describe("Provider-specific tool names"),
|
|
121
|
+
},
|
|
122
|
+
}, async (input) => {
|
|
123
|
+
try {
|
|
124
|
+
return successToolResult(await callbacks.createExpert(input));
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
if (e instanceof Error)
|
|
128
|
+
return errorToolResult(e);
|
|
129
|
+
throw e;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export function registerAddDelegateFromConfig(server, callbacks) {
|
|
134
|
+
server.registerTool("addDelegateFromConfig", {
|
|
135
|
+
title: "Add delegate from config",
|
|
136
|
+
description: "Load all experts from a perstack.toml config file and add the specified one as a delegate. This is a shortcut that combines reading the config, creating the expert, and adding it as a delegate in a single step.",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
configPath: z.string().describe("Path to the perstack.toml config file"),
|
|
139
|
+
delegateExpertName: z
|
|
140
|
+
.string()
|
|
141
|
+
.describe("Name of the expert in the config to add as a delegate"),
|
|
142
|
+
},
|
|
143
|
+
}, async (input) => {
|
|
144
|
+
try {
|
|
145
|
+
return successToolResult(await callbacks.addDelegateFromConfig(input));
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
if (e instanceof Error)
|
|
149
|
+
return errorToolResult(e);
|
|
150
|
+
throw e;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
export function registerSkillManagementTools(server, callbacks) {
|
|
155
|
+
registerAddSkill(server, callbacks);
|
|
156
|
+
registerRemoveSkill(server, callbacks);
|
|
157
|
+
registerAddDelegate(server, callbacks);
|
|
158
|
+
registerRemoveDelegate(server, callbacks);
|
|
159
|
+
registerCreateExpert(server, callbacks);
|
|
160
|
+
registerAddDelegateFromConfig(server, callbacks);
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=skill-management.js.map
|