@yukino.js/yukino 0.0.1
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/README.md +320 -0
- package/dist/acp-IVY5HZR4.js +14 -0
- package/dist/agent-QANDY7FO.js +4 -0
- package/dist/anthropic-6YLT4JZR.js +4 -0
- package/dist/bwrap-MLSZLWHD.js +4 -0
- package/dist/chunk-2LHY3FND.js +67 -0
- package/dist/chunk-3KKA6537.js +4 -0
- package/dist/chunk-3QL446GD.js +4 -0
- package/dist/chunk-4GE22WHI.js +4 -0
- package/dist/chunk-5VOCXSSX.js +5 -0
- package/dist/chunk-77W2UPS2.js +4 -0
- package/dist/chunk-7MKA3SHV.js +4 -0
- package/dist/chunk-AGSYLE4E.js +16 -0
- package/dist/chunk-CU3IISCC.js +4 -0
- package/dist/chunk-GYBJR2RB.js +65 -0
- package/dist/chunk-HLE7243Q.js +4 -0
- package/dist/chunk-HMZQYFYG.js +4 -0
- package/dist/chunk-JFKC4VCA.js +4 -0
- package/dist/chunk-K4BYVVW7.js +5 -0
- package/dist/chunk-KCMBAHWU.js +4 -0
- package/dist/chunk-L2BWSPCF.js +10 -0
- package/dist/chunk-MDPSQ6L7.js +44 -0
- package/dist/chunk-OFC2GD5K.js +4 -0
- package/dist/chunk-RAS3ORX3.js +164 -0
- package/dist/chunk-RCXKUD3Y.js +4 -0
- package/dist/chunk-SAJCBGHL.js +4 -0
- package/dist/chunk-SOO5OXX2.js +237 -0
- package/dist/chunk-T4O4GQVL.js +607 -0
- package/dist/chunk-UPNNYP7M.js +60 -0
- package/dist/chunk-VCIRD267.js +4 -0
- package/dist/chunk-VU3JWAJR.js +4 -0
- package/dist/chunk-VWUGGYUP.js +6 -0
- package/dist/chunk-XO7ITAWR.js +4 -0
- package/dist/devtools-YWVUN4HX.js +7 -0
- package/dist/esm-57CJEGGK.js +4 -0
- package/dist/esm-6L5BJ5GT.js +4 -0
- package/dist/esm-DIYJ5HX6.js +4 -0
- package/dist/esm-HW2JMFYC.js +4 -0
- package/dist/esm-RPR2GBJ2.js +4 -0
- package/dist/esm-YKAO4ES6.js +4 -0
- package/dist/esm-ZTIYTMTS.js +229 -0
- package/dist/getMachineId-bsd-E27XJQLX.js +4 -0
- package/dist/getMachineId-darwin-35UPAIXP.js +5 -0
- package/dist/getMachineId-linux-MKXIYT6A.js +4 -0
- package/dist/getMachineId-unsupported-ZXVGGFX2.js +4 -0
- package/dist/getMachineId-win-RNVIYJG5.js +4 -0
- package/dist/lib/agent-6FZX4P2Y.js +16 -0
- package/dist/lib/anthropic-IP23YZ3P.js +21 -0
- package/dist/lib/bwrap-2OL7VPSO.js +7 -0
- package/dist/lib/chunk-2VF3XTGH.js +3382 -0
- package/dist/lib/chunk-5DVQ64YL.js +619 -0
- package/dist/lib/chunk-5TGXH7BX.js +624 -0
- package/dist/lib/chunk-6OMQD7MK.js +591 -0
- package/dist/lib/chunk-AZRC67RW.js +512 -0
- package/dist/lib/chunk-F2YJG72O.js +150 -0
- package/dist/lib/chunk-FFM2LB4C.js +132 -0
- package/dist/lib/chunk-FLKB4BK7.js +50 -0
- package/dist/lib/chunk-I34MAQRE.js +255 -0
- package/dist/lib/chunk-KA6POJKI.js +889 -0
- package/dist/lib/chunk-MLKGABMK.js +9 -0
- package/dist/lib/chunk-O7ELJUEK.js +262 -0
- package/dist/lib/chunk-QYBQT2DM.js +655 -0
- package/dist/lib/chunk-YIWNQ7OA.js +47 -0
- package/dist/lib/index.d.ts +8581 -0
- package/dist/lib/index.js +15905 -0
- package/dist/lib/openai-EMJ6U4FW.js +15 -0
- package/dist/lib/permissions-KZ5ACZAX.js +19 -0
- package/dist/lib/providers-2CGZPBFI.js +9 -0
- package/dist/lib/sandbox-runtime-CCQ3X3SM.js +9 -0
- package/dist/lib/seatbelt-WO7YWKLR.js +7 -0
- package/dist/lib/tool-filter-CKHZUUBQ.js +21 -0
- package/dist/main.js +332 -0
- package/dist/node-634LOHDX.js +15 -0
- package/dist/openai-YXYO5OW6.js +34 -0
- package/dist/permissions-VCDMNX3L.js +4 -0
- package/dist/providers-HNMBGGW6.js +4 -0
- package/dist/sandbox-runtime-ELKAYRIL.js +4 -0
- package/dist/seatbelt-JOCANLYS.js +4 -0
- package/dist/server-ZHQILOBP.js +4 -0
- package/dist/src-43CPCEK2.js +12 -0
- package/dist/src-JBRNH4FA.js +4 -0
- package/dist/src-R4BSSIJJ.js +4 -0
- package/dist/src-X6JWWHHT.js +4 -0
- package/dist/tool-filter-BQZDV3TF.js +4 -0
- package/dist/turndown.es-NQ3KDXM7.js +83 -0
- package/package.json +146 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-MLKGABMK.js";
|
|
4
|
+
|
|
5
|
+
// src/sandbox/sandbox-runtime.ts
|
|
6
|
+
var sandbox_runtime_exports = {};
|
|
7
|
+
__export(sandbox_runtime_exports, {
|
|
8
|
+
SandboxRuntimeSandbox: () => SandboxRuntimeSandbox,
|
|
9
|
+
createSandboxRuntimeConfig: () => createSandboxRuntimeConfig
|
|
10
|
+
});
|
|
11
|
+
import { isAbsolute, resolve } from "path";
|
|
12
|
+
async function loadSandboxRuntime() {
|
|
13
|
+
return await import("@anthropic-ai/sandbox-runtime");
|
|
14
|
+
}
|
|
15
|
+
function resolvePaths(paths, cwd) {
|
|
16
|
+
return paths.map((path) => isAbsolute(path) ? path : resolve(cwd, path));
|
|
17
|
+
}
|
|
18
|
+
function createSandboxRuntimeConfig(config, cwd) {
|
|
19
|
+
return {
|
|
20
|
+
network: {
|
|
21
|
+
allowedDomains: [],
|
|
22
|
+
deniedDomains: config.networkEnabled ? [] : ["*"],
|
|
23
|
+
strictAllowlist: !config.networkEnabled
|
|
24
|
+
},
|
|
25
|
+
filesystem: {
|
|
26
|
+
denyRead: [],
|
|
27
|
+
allowWrite: resolvePaths(config.allowWrite, cwd),
|
|
28
|
+
denyWrite: resolvePaths(config.denyWrite, cwd),
|
|
29
|
+
allowGitConfig: false
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
var SandboxRuntimeSandbox = class {
|
|
34
|
+
implementation = "sandbox-runtime";
|
|
35
|
+
availabilityError;
|
|
36
|
+
availabilityPromise;
|
|
37
|
+
configKey;
|
|
38
|
+
initializationPromise;
|
|
39
|
+
runtimePromise;
|
|
40
|
+
available() {
|
|
41
|
+
return this.availabilityPromise ??= this.checkAvailability();
|
|
42
|
+
}
|
|
43
|
+
async prepare(command, config, context) {
|
|
44
|
+
if (!await this.available()) {
|
|
45
|
+
throw new Error(this.availabilityError ?? "sandbox runtime is unavailable");
|
|
46
|
+
}
|
|
47
|
+
const runtime = await this.loadRuntime();
|
|
48
|
+
const runtimeConfig = runtime.SandboxRuntimeConfigSchema.parse(
|
|
49
|
+
createSandboxRuntimeConfig(config, context.cwd)
|
|
50
|
+
);
|
|
51
|
+
const configKey = JSON.stringify(runtimeConfig);
|
|
52
|
+
if (this.initializationPromise) {
|
|
53
|
+
await this.initializationPromise;
|
|
54
|
+
}
|
|
55
|
+
if (this.configKey !== configKey || !runtime.SandboxManager.isSandboxingEnabled()) {
|
|
56
|
+
this.initializationPromise = (async () => {
|
|
57
|
+
if (runtime.SandboxManager.isSandboxingEnabled()) {
|
|
58
|
+
await runtime.SandboxManager.reset();
|
|
59
|
+
}
|
|
60
|
+
await runtime.SandboxManager.initialize(
|
|
61
|
+
runtimeConfig,
|
|
62
|
+
config.networkEnabled ? () => Promise.resolve(true) : void 0
|
|
63
|
+
);
|
|
64
|
+
this.configKey = configKey;
|
|
65
|
+
})();
|
|
66
|
+
try {
|
|
67
|
+
await this.initializationPromise;
|
|
68
|
+
} finally {
|
|
69
|
+
this.initializationPromise = void 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const commandId = context.commandId ?? command;
|
|
73
|
+
const prepared = await runtime.SandboxManager.wrapWithSandboxArgv(
|
|
74
|
+
command,
|
|
75
|
+
"/bin/bash",
|
|
76
|
+
void 0,
|
|
77
|
+
context.abortSignal,
|
|
78
|
+
context.cwd,
|
|
79
|
+
{ commandId, commandText: command }
|
|
80
|
+
);
|
|
81
|
+
const [executable, ...args] = prepared.argv;
|
|
82
|
+
if (!executable) {
|
|
83
|
+
throw new Error("sandbox runtime returned an empty command");
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
executable,
|
|
87
|
+
args,
|
|
88
|
+
env: prepared.env,
|
|
89
|
+
annotateStderr: (stderr) => runtime.SandboxManager.annotateStderrWithSandboxFailures(commandId, stderr),
|
|
90
|
+
cleanup: () => {
|
|
91
|
+
runtime.SandboxManager.cleanupAfterCommand();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async dispose() {
|
|
96
|
+
if (!this.runtimePromise) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
await this.initializationPromise;
|
|
100
|
+
const runtime = await this.runtimePromise;
|
|
101
|
+
await runtime.SandboxManager.reset();
|
|
102
|
+
this.configKey = void 0;
|
|
103
|
+
}
|
|
104
|
+
async checkAvailability() {
|
|
105
|
+
try {
|
|
106
|
+
const runtime = await this.loadRuntime();
|
|
107
|
+
if (!runtime.SandboxManager.isSupportedPlatform()) {
|
|
108
|
+
this.availabilityError = "sandbox runtime does not support this platform";
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const dependencies = await runtime.SandboxManager.checkDependenciesAsync();
|
|
112
|
+
if (dependencies.errors.length > 0) {
|
|
113
|
+
this.availabilityError = dependencies.errors.join("; ");
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
this.availabilityError = void 0;
|
|
117
|
+
return true;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
this.availabilityError = error instanceof Error ? error.message : String(error);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
loadRuntime() {
|
|
124
|
+
return this.runtimePromise ??= loadSandboxRuntime();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
createSandboxRuntimeConfig,
|
|
130
|
+
SandboxRuntimeSandbox,
|
|
131
|
+
sandbox_runtime_exports
|
|
132
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-MLKGABMK.js";
|
|
4
|
+
|
|
5
|
+
// src/sandbox/seatbelt.ts
|
|
6
|
+
var seatbelt_exports = {};
|
|
7
|
+
__export(seatbelt_exports, {
|
|
8
|
+
SeatbeltSandbox: () => SeatbeltSandbox
|
|
9
|
+
});
|
|
10
|
+
import { existsSync, statSync } from "fs";
|
|
11
|
+
var SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
|
|
12
|
+
var SeatbeltSandbox = class {
|
|
13
|
+
implementation = "seatbelt";
|
|
14
|
+
available() {
|
|
15
|
+
return existsSync(SANDBOX_EXEC_PATH);
|
|
16
|
+
}
|
|
17
|
+
prepare(command, config) {
|
|
18
|
+
return {
|
|
19
|
+
executable: SANDBOX_EXEC_PATH,
|
|
20
|
+
args: ["-p", buildProfile(config), "bash", "-c", command]
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
function buildProfile(config) {
|
|
25
|
+
const lines = [];
|
|
26
|
+
lines.push("(version 1)");
|
|
27
|
+
lines.push("(deny default)");
|
|
28
|
+
lines.push("(allow process-exec)");
|
|
29
|
+
lines.push("(allow process-fork)");
|
|
30
|
+
lines.push("(allow sysctl-read)");
|
|
31
|
+
lines.push('(allow file-read* (subpath "/"))');
|
|
32
|
+
for (const path of config.allowWrite) {
|
|
33
|
+
lines.push(`(allow file-write* (subpath "${path}"))`);
|
|
34
|
+
}
|
|
35
|
+
for (const path of config.denyWrite) {
|
|
36
|
+
const matcher = existsSync(path) && statSync(path).isDirectory() ? "subpath" : "literal";
|
|
37
|
+
lines.push(`(deny file-write* (${matcher} "${path}"))`);
|
|
38
|
+
}
|
|
39
|
+
if (config.networkEnabled) {
|
|
40
|
+
lines.push("(allow network*)");
|
|
41
|
+
} else {
|
|
42
|
+
lines.push("(deny network*)");
|
|
43
|
+
}
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
SeatbeltSandbox,
|
|
49
|
+
seatbelt_exports
|
|
50
|
+
};
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asErrorString,
|
|
3
|
+
asRecord,
|
|
4
|
+
createChildLogger,
|
|
5
|
+
strArg
|
|
6
|
+
} from "./chunk-6OMQD7MK.js";
|
|
7
|
+
import {
|
|
8
|
+
__export
|
|
9
|
+
} from "./chunk-MLKGABMK.js";
|
|
10
|
+
|
|
11
|
+
// src/tools/mcp-call.ts
|
|
12
|
+
var mcp_call_exports = {};
|
|
13
|
+
__export(mcp_call_exports, {
|
|
14
|
+
McpCallTool: () => McpCallTool,
|
|
15
|
+
coerceBySchema: () => coerceBySchema,
|
|
16
|
+
isMcpToolLike: () => isMcpToolLike,
|
|
17
|
+
mcpCallPermissionContent: () => mcpCallPermissionContent
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// src/mcp/tool-wrapper.ts
|
|
21
|
+
var tool_wrapper_exports = {};
|
|
22
|
+
__export(tool_wrapper_exports, {
|
|
23
|
+
MCPToolWrapper: () => MCPToolWrapper,
|
|
24
|
+
MCP_NAME_SEP: () => MCP_NAME_SEP,
|
|
25
|
+
MCP_TOOL_PREFIX: () => MCP_TOOL_PREFIX,
|
|
26
|
+
buildMcpToolName: () => buildMcpToolName,
|
|
27
|
+
mcpToolNamePrefix: () => mcpToolNamePrefix,
|
|
28
|
+
sanitizeSegment: () => sanitizeSegment
|
|
29
|
+
});
|
|
30
|
+
var log = createChildLogger({ module: "mcp" });
|
|
31
|
+
var MCP_TOOL_PREFIX = "mcp__";
|
|
32
|
+
var MCP_NAME_SEP = "__";
|
|
33
|
+
function sanitizeSegment(s) {
|
|
34
|
+
return s.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
35
|
+
}
|
|
36
|
+
function mcpToolNamePrefix(serverName) {
|
|
37
|
+
return MCP_TOOL_PREFIX + sanitizeSegment(serverName) + MCP_NAME_SEP;
|
|
38
|
+
}
|
|
39
|
+
function buildMcpToolName(serverName, toolName) {
|
|
40
|
+
return mcpToolNamePrefix(serverName) + sanitizeSegment(toolName);
|
|
41
|
+
}
|
|
42
|
+
var MCPToolWrapper = class {
|
|
43
|
+
name;
|
|
44
|
+
description;
|
|
45
|
+
category = "command";
|
|
46
|
+
// MCP tools are lazily loaded by default to avoid cramming all schemas into the prompt
|
|
47
|
+
deferred = true;
|
|
48
|
+
mcpServerName;
|
|
49
|
+
client;
|
|
50
|
+
originalName;
|
|
51
|
+
inputSchema;
|
|
52
|
+
constructor(client, serverName, tool) {
|
|
53
|
+
this.name = buildMcpToolName(serverName, tool.name);
|
|
54
|
+
this.description = tool.description;
|
|
55
|
+
this.originalName = tool.name;
|
|
56
|
+
this.client = client;
|
|
57
|
+
this.inputSchema = tool.inputSchema;
|
|
58
|
+
this.mcpServerName = serverName;
|
|
59
|
+
}
|
|
60
|
+
/** Original JSON schema. McpCall's argument coercion walks it layer by layer. */
|
|
61
|
+
mcpInputSchema() {
|
|
62
|
+
return this.inputSchema ?? {};
|
|
63
|
+
}
|
|
64
|
+
/** In eager mode the defer flag is cleared so MCP tools go straight into tools[]. */
|
|
65
|
+
setDeferLoading(on) {
|
|
66
|
+
this.deferred = on;
|
|
67
|
+
}
|
|
68
|
+
schema() {
|
|
69
|
+
return {
|
|
70
|
+
name: this.name,
|
|
71
|
+
description: this.description,
|
|
72
|
+
input_schema: this.inputSchema
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async execute(ctx, args) {
|
|
76
|
+
try {
|
|
77
|
+
return await this.client.callTool(this.originalName, args, ctx.abortSignal);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
log.error({ err }, "mcp operation failed");
|
|
80
|
+
return {
|
|
81
|
+
output: `MCP tool error: ${asErrorString(err)}`,
|
|
82
|
+
isError: true
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/tools/mcp-call.ts
|
|
89
|
+
function coerceScalar(value, want) {
|
|
90
|
+
if (want === "string" && typeof value === "number" && Number.isFinite(value)) {
|
|
91
|
+
return String(value);
|
|
92
|
+
}
|
|
93
|
+
if ((want === "integer" || want === "number") && typeof value === "string") {
|
|
94
|
+
const text = value.trim();
|
|
95
|
+
if (text === "") {
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
const shape = want === "integer" ? /^[+-]?\d+$/ : /^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/;
|
|
99
|
+
if (!shape.test(text)) {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
const parsed = want === "integer" ? Number.parseInt(text, 10) : Number.parseFloat(text);
|
|
103
|
+
return Number.isFinite(parsed) ? parsed : value;
|
|
104
|
+
}
|
|
105
|
+
if (want === "boolean" && typeof value === "string") {
|
|
106
|
+
const low = value.trim().toLowerCase();
|
|
107
|
+
if (low === "true") {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
if (low === "false") {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
function coerceBySchema(value, schema) {
|
|
117
|
+
if (typeof schema !== "object" || schema === null) {
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
const schemaObj = asRecord(schema);
|
|
121
|
+
const want = strArg(schemaObj, "type", "");
|
|
122
|
+
if (want === "object" && typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
123
|
+
const props = asRecord(schemaObj.properties ?? {});
|
|
124
|
+
const out = {};
|
|
125
|
+
for (const [key, item] of Object.entries(value)) {
|
|
126
|
+
out[key] = key in props ? coerceBySchema(item, props[key]) : item;
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
if (want === "array") {
|
|
131
|
+
const itemSchema = schemaObj.items ?? {};
|
|
132
|
+
let working = value;
|
|
133
|
+
if (typeof working === "object" && working !== null && !Array.isArray(working)) {
|
|
134
|
+
const entries = Object.values(working);
|
|
135
|
+
if (entries.length === 1 && Array.isArray(entries[0])) {
|
|
136
|
+
working = entries[0];
|
|
137
|
+
}
|
|
138
|
+
} else if (typeof working === "string") {
|
|
139
|
+
working = working.split(",").map((p) => p.trim()).filter((p) => p !== "");
|
|
140
|
+
}
|
|
141
|
+
if (Array.isArray(working)) {
|
|
142
|
+
return working.map((item) => coerceBySchema(item, itemSchema));
|
|
143
|
+
}
|
|
144
|
+
return working;
|
|
145
|
+
}
|
|
146
|
+
if (want !== "") {
|
|
147
|
+
return coerceScalar(value, want);
|
|
148
|
+
}
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
function mcpCallPermissionContent(server, tool) {
|
|
152
|
+
if (tool.startsWith(MCP_TOOL_PREFIX)) {
|
|
153
|
+
const rest = tool.slice(MCP_TOOL_PREFIX.length);
|
|
154
|
+
const idx = rest.indexOf(MCP_NAME_SEP);
|
|
155
|
+
if (idx >= 0) {
|
|
156
|
+
return sanitizeSegment(rest.slice(0, idx)) + MCP_NAME_SEP + sanitizeSegment(rest.slice(idx + MCP_NAME_SEP.length));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return sanitizeSegment(server) + MCP_NAME_SEP + sanitizeSegment(tool);
|
|
160
|
+
}
|
|
161
|
+
function isMcpToolLike(tool) {
|
|
162
|
+
return "mcpInputSchema" in tool && typeof tool.mcpInputSchema === "function";
|
|
163
|
+
}
|
|
164
|
+
var McpCallTool = class {
|
|
165
|
+
constructor(registry) {
|
|
166
|
+
this.registry = registry;
|
|
167
|
+
}
|
|
168
|
+
registry;
|
|
169
|
+
name = "McpCall";
|
|
170
|
+
description = "Invoke a tool on a connected MCP server. Call ToolSearch first to load the tool's schema, then pass its arguments here exactly as that schema requires, using the same JSON types.";
|
|
171
|
+
category = "command";
|
|
172
|
+
// This tool must stay in tools[] itself, otherwise the model has no entry point
|
|
173
|
+
deferred = false;
|
|
174
|
+
schema() {
|
|
175
|
+
return {
|
|
176
|
+
name: this.name,
|
|
177
|
+
description: this.description,
|
|
178
|
+
input_schema: {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
server: {
|
|
182
|
+
type: "string",
|
|
183
|
+
description: "MCP server name, e.g. 'linear'."
|
|
184
|
+
},
|
|
185
|
+
tool: {
|
|
186
|
+
type: "string",
|
|
187
|
+
description: "Full tool name as returned by ToolSearch, e.g. 'mcp__linear__create_issue'."
|
|
188
|
+
},
|
|
189
|
+
arguments: {
|
|
190
|
+
type: "object",
|
|
191
|
+
description: "The target tool's arguments. Must match that tool's input_schema exactly, including JSON types: bare numbers for integer fields, bare true/false for boolean fields, quoted strings for string fields, and plain JSON arrays for array fields."
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
required: ["server", "tool", "arguments"]
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
resolveTarget(args) {
|
|
199
|
+
const server = strArg(args, "server");
|
|
200
|
+
const name = strArg(args, "tool");
|
|
201
|
+
if (!server || !name) {
|
|
202
|
+
return void 0;
|
|
203
|
+
}
|
|
204
|
+
const fullName = name.startsWith(MCP_TOOL_PREFIX) ? name : buildMcpToolName(server, name);
|
|
205
|
+
const target = this.registry.get(fullName);
|
|
206
|
+
return target && isMcpToolLike(target) && sanitizeSegment(target.mcpServerName) === sanitizeSegment(server) ? target : void 0;
|
|
207
|
+
}
|
|
208
|
+
availableNames() {
|
|
209
|
+
return this.registry.listTools().filter((t) => t.name.startsWith(MCP_TOOL_PREFIX)).map((t) => t.name).sort();
|
|
210
|
+
}
|
|
211
|
+
async execute(ctx, args) {
|
|
212
|
+
const server = strArg(args, "server", "");
|
|
213
|
+
const tool = strArg(args, "tool", "");
|
|
214
|
+
if (tool === "") {
|
|
215
|
+
return { output: "McpCall requires a 'tool' name", isError: true };
|
|
216
|
+
}
|
|
217
|
+
const target = this.resolveTarget(args);
|
|
218
|
+
if (!target) {
|
|
219
|
+
const names = this.availableNames();
|
|
220
|
+
const hint = names.length > 0 ? names.join(", ") : "(none connected)";
|
|
221
|
+
return {
|
|
222
|
+
output: `Unknown MCP tool '${tool}' on server '${server}'. Available tools: ${hint}`,
|
|
223
|
+
isError: true
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (typeof args.arguments !== "object" || args.arguments === null || Array.isArray(args.arguments)) {
|
|
227
|
+
return {
|
|
228
|
+
output: "McpCall requires an 'arguments' object matching the target schema",
|
|
229
|
+
isError: true
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
let inner = asRecord(args.arguments);
|
|
233
|
+
if (isMcpToolLike(target)) {
|
|
234
|
+
const schema = target.mcpInputSchema();
|
|
235
|
+
if (Object.keys(schema).length > 0) {
|
|
236
|
+
const fixed = coerceBySchema(inner, schema);
|
|
237
|
+
if (typeof fixed === "object" && fixed !== null && !Array.isArray(fixed)) {
|
|
238
|
+
inner = asRecord(fixed);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
ctx.abortSignal?.throwIfAborted();
|
|
243
|
+
return target.execute(ctx, inner);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export {
|
|
248
|
+
MCP_TOOL_PREFIX,
|
|
249
|
+
MCPToolWrapper,
|
|
250
|
+
tool_wrapper_exports,
|
|
251
|
+
mcpCallPermissionContent,
|
|
252
|
+
isMcpToolLike,
|
|
253
|
+
McpCallTool,
|
|
254
|
+
mcp_call_exports
|
|
255
|
+
};
|