llmist 17.3.0 → 17.5.0
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/README.md +21 -0
- package/dist/chunk-HM7PUGPA.js +2252 -0
- package/dist/chunk-HM7PUGPA.js.map +1 -0
- package/dist/index.cjs +1416 -331
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +448 -13
- package/dist/index.d.ts +448 -13
- package/dist/index.js +486 -1678
- package/dist/index.js.map +1 -1
- package/dist/runtime-GKQ6QIQP.js +187 -0
- package/dist/runtime-GKQ6QIQP.js.map +1 -0
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -358,15 +358,15 @@ var init_execution_tree = __esm({
|
|
|
358
358
|
const parentId = params.parentId ?? this.parentNodeId;
|
|
359
359
|
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
360
360
|
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
361
|
-
const
|
|
361
|
+
const path4 = parent ? [...parent.path] : [];
|
|
362
362
|
const id = this.generateLLMCallId(params.iteration, parentId);
|
|
363
|
-
|
|
363
|
+
path4.push(id);
|
|
364
364
|
const node = {
|
|
365
365
|
id,
|
|
366
366
|
type: "llm_call",
|
|
367
367
|
parentId,
|
|
368
368
|
depth,
|
|
369
|
-
path:
|
|
369
|
+
path: path4,
|
|
370
370
|
createdAt: Date.now(),
|
|
371
371
|
completedAt: null,
|
|
372
372
|
iteration: params.iteration,
|
|
@@ -477,15 +477,15 @@ var init_execution_tree = __esm({
|
|
|
477
477
|
const parentId = params.parentId ?? this.getCurrentLLMCallId() ?? this.parentNodeId;
|
|
478
478
|
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
479
479
|
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
480
|
-
const
|
|
480
|
+
const path4 = parent ? [...parent.path] : [];
|
|
481
481
|
const id = this.generateGadgetId(params.invocationId);
|
|
482
|
-
|
|
482
|
+
path4.push(id);
|
|
483
483
|
const node = {
|
|
484
484
|
id,
|
|
485
485
|
type: "gadget",
|
|
486
486
|
parentId,
|
|
487
487
|
depth,
|
|
488
|
-
path:
|
|
488
|
+
path: path4,
|
|
489
489
|
createdAt: Date.now(),
|
|
490
490
|
completedAt: null,
|
|
491
491
|
invocationId: params.invocationId,
|
|
@@ -1424,8 +1424,8 @@ ${this.endPrefix}`
|
|
|
1424
1424
|
});
|
|
1425
1425
|
if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
|
|
1426
1426
|
const idRefs = media.map((m, i) => {
|
|
1427
|
-
const
|
|
1428
|
-
const pathInfo =
|
|
1427
|
+
const path4 = storedMedia?.[i]?.path;
|
|
1428
|
+
const pathInfo = path4 ? ` \u2192 saved to: ${path4}` : "";
|
|
1429
1429
|
return `[Media: ${mediaIds[i]} (${m.kind})${pathInfo}]`;
|
|
1430
1430
|
}).join("\n");
|
|
1431
1431
|
const textWithIds = `Result (${invocationId}): ${result}
|
|
@@ -3071,6 +3071,16 @@ var init_conversation_manager = __esm({
|
|
|
3071
3071
|
getBaseMessages() {
|
|
3072
3072
|
return [...this.baseMessages, ...this.initialMessages];
|
|
3073
3073
|
}
|
|
3074
|
+
/**
|
|
3075
|
+
* Replace the base (system + gadget catalog) messages.
|
|
3076
|
+
*
|
|
3077
|
+
* Used when async setup (e.g. MCP server connect-and-list) discovers
|
|
3078
|
+
* additional gadgets after the agent was constructed. Conversation history
|
|
3079
|
+
* is preserved; only the leading system block is swapped.
|
|
3080
|
+
*/
|
|
3081
|
+
replaceBaseMessages(newBase) {
|
|
3082
|
+
this.baseMessages = newBase;
|
|
3083
|
+
}
|
|
3074
3084
|
replaceHistory(newHistory) {
|
|
3075
3085
|
this.historyBuilder = new LLMMessageBuilder();
|
|
3076
3086
|
if (this.startPrefix && this.endPrefix) {
|
|
@@ -4088,29 +4098,29 @@ function schemaToJSONSchema(schema, options) {
|
|
|
4088
4098
|
}
|
|
4089
4099
|
function detectDescriptionMismatch(schema, jsonSchema) {
|
|
4090
4100
|
const mismatches = [];
|
|
4091
|
-
function checkSchema(zodSchema, json,
|
|
4101
|
+
function checkSchema(zodSchema, json, path4) {
|
|
4092
4102
|
if (!zodSchema || typeof zodSchema !== "object") return;
|
|
4093
4103
|
const def = zodSchema._def;
|
|
4094
4104
|
const jsonObj = json;
|
|
4095
4105
|
if (def?.description && !jsonObj?.description) {
|
|
4096
|
-
mismatches.push(
|
|
4106
|
+
mismatches.push(path4 || "root");
|
|
4097
4107
|
}
|
|
4098
4108
|
if (def?.typeName === "ZodObject" && def?.shape) {
|
|
4099
4109
|
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
4100
4110
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
4101
4111
|
const properties = jsonObj?.properties;
|
|
4102
4112
|
const jsonProp = properties?.[key];
|
|
4103
|
-
checkSchema(fieldSchema, jsonProp,
|
|
4113
|
+
checkSchema(fieldSchema, jsonProp, path4 ? `${path4}.${key}` : key);
|
|
4104
4114
|
}
|
|
4105
4115
|
}
|
|
4106
4116
|
if (def?.typeName === "ZodArray" && def?.type) {
|
|
4107
|
-
checkSchema(def.type, jsonObj?.items,
|
|
4117
|
+
checkSchema(def.type, jsonObj?.items, path4 ? `${path4}[]` : "[]");
|
|
4108
4118
|
}
|
|
4109
4119
|
if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
|
|
4110
|
-
checkSchema(def.innerType, json,
|
|
4120
|
+
checkSchema(def.innerType, json, path4);
|
|
4111
4121
|
}
|
|
4112
4122
|
if (def?.typeName === "ZodDefault" && def?.innerType) {
|
|
4113
|
-
checkSchema(def.innerType, json,
|
|
4123
|
+
checkSchema(def.innerType, json, path4);
|
|
4114
4124
|
}
|
|
4115
4125
|
}
|
|
4116
4126
|
checkSchema(schema, jsonSchema, "");
|
|
@@ -4203,7 +4213,7 @@ Example fixes:
|
|
|
4203
4213
|
);
|
|
4204
4214
|
}
|
|
4205
4215
|
}
|
|
4206
|
-
function findUnknownTypes(schema,
|
|
4216
|
+
function findUnknownTypes(schema, path4 = []) {
|
|
4207
4217
|
const issues = [];
|
|
4208
4218
|
if (!schema || typeof schema !== "object") {
|
|
4209
4219
|
return issues;
|
|
@@ -4215,7 +4225,7 @@ function findUnknownTypes(schema, path3 = []) {
|
|
|
4215
4225
|
}
|
|
4216
4226
|
if (schema.properties) {
|
|
4217
4227
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
4218
|
-
const propPath = [...
|
|
4228
|
+
const propPath = [...path4, propName];
|
|
4219
4229
|
if (hasNoType(propSchema)) {
|
|
4220
4230
|
issues.push(propPath.join(".") || propName);
|
|
4221
4231
|
}
|
|
@@ -4223,7 +4233,7 @@ function findUnknownTypes(schema, path3 = []) {
|
|
|
4223
4233
|
}
|
|
4224
4234
|
}
|
|
4225
4235
|
if (schema.items) {
|
|
4226
|
-
const itemPath = [...
|
|
4236
|
+
const itemPath = [...path4, "[]"];
|
|
4227
4237
|
if (hasNoType(schema.items)) {
|
|
4228
4238
|
issues.push(itemPath.join("."));
|
|
4229
4239
|
}
|
|
@@ -4231,17 +4241,17 @@ function findUnknownTypes(schema, path3 = []) {
|
|
|
4231
4241
|
}
|
|
4232
4242
|
if (schema.anyOf) {
|
|
4233
4243
|
schema.anyOf.forEach((subSchema, index) => {
|
|
4234
|
-
issues.push(...findUnknownTypes(subSchema, [...
|
|
4244
|
+
issues.push(...findUnknownTypes(subSchema, [...path4, `anyOf[${index}]`]));
|
|
4235
4245
|
});
|
|
4236
4246
|
}
|
|
4237
4247
|
if (schema.oneOf) {
|
|
4238
4248
|
schema.oneOf.forEach((subSchema, index) => {
|
|
4239
|
-
issues.push(...findUnknownTypes(subSchema, [...
|
|
4249
|
+
issues.push(...findUnknownTypes(subSchema, [...path4, `oneOf[${index}]`]));
|
|
4240
4250
|
});
|
|
4241
4251
|
}
|
|
4242
4252
|
if (schema.allOf) {
|
|
4243
4253
|
schema.allOf.forEach((subSchema, index) => {
|
|
4244
|
-
issues.push(...findUnknownTypes(subSchema, [...
|
|
4254
|
+
issues.push(...findUnknownTypes(subSchema, [...path4, `allOf[${index}]`]));
|
|
4245
4255
|
});
|
|
4246
4256
|
}
|
|
4247
4257
|
return issues;
|
|
@@ -13232,6 +13242,7 @@ var init_builder = __esm({
|
|
|
13232
13242
|
subagents;
|
|
13233
13243
|
policies;
|
|
13234
13244
|
skills;
|
|
13245
|
+
mcp;
|
|
13235
13246
|
constructor(client) {
|
|
13236
13247
|
this.core = { client, initialMessages: [] };
|
|
13237
13248
|
this.gadgets = { gadgets: [] };
|
|
@@ -13239,6 +13250,7 @@ var init_builder = __esm({
|
|
|
13239
13250
|
this.subagents = {};
|
|
13240
13251
|
this.policies = {};
|
|
13241
13252
|
this.skills = { preActivated: [], skillDirs: [] };
|
|
13253
|
+
this.mcp = { servers: [] };
|
|
13242
13254
|
}
|
|
13243
13255
|
/** Set the model to use. Supports aliases like "sonnet", "flash". */
|
|
13244
13256
|
withModel(model) {
|
|
@@ -13285,6 +13297,45 @@ var init_builder = __esm({
|
|
|
13285
13297
|
this.gadgets.gadgets.push(...gadgets);
|
|
13286
13298
|
return this;
|
|
13287
13299
|
}
|
|
13300
|
+
/**
|
|
13301
|
+
* Attach a Model Context Protocol (MCP) server.
|
|
13302
|
+
*
|
|
13303
|
+
* The agent connects to the server lazily at the start of `run()`,
|
|
13304
|
+
* discovers its tools, and registers them as native gadgets so the LLM
|
|
13305
|
+
* can call them through the standard streaming block format.
|
|
13306
|
+
*
|
|
13307
|
+
* Calling this multiple times accumulates servers. Tools across servers
|
|
13308
|
+
* are merged into a single registry; in plan 1, conflicting tool names
|
|
13309
|
+
* raise a registration warning. Plan 2 introduces deterministic
|
|
13310
|
+
* `<server>__<tool>` prefixing for collisions.
|
|
13311
|
+
*
|
|
13312
|
+
* STDIO commands are gated by an allowlist (see allowlist.ts) — pass
|
|
13313
|
+
* `trust: true` on the spec to opt in for non-allowlisted binaries.
|
|
13314
|
+
*
|
|
13315
|
+
* Zero-overhead invariant: if you never call this method, the MCP
|
|
13316
|
+
* runtime module is never loaded. Agents without MCP pay nothing.
|
|
13317
|
+
*
|
|
13318
|
+
* @example
|
|
13319
|
+
* ```typescript
|
|
13320
|
+
* const agent = LLMist.createAgent()
|
|
13321
|
+
* .withModel("sonnet")
|
|
13322
|
+
* .withMcpServer({
|
|
13323
|
+
* name: "filesystem",
|
|
13324
|
+
* transport: "stdio",
|
|
13325
|
+
* command: "npx",
|
|
13326
|
+
* args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
|
|
13327
|
+
* })
|
|
13328
|
+
* .ask("list files in /tmp");
|
|
13329
|
+
* ```
|
|
13330
|
+
*/
|
|
13331
|
+
withMcpServer(spec) {
|
|
13332
|
+
this.mcp.servers.push(spec);
|
|
13333
|
+
return this;
|
|
13334
|
+
}
|
|
13335
|
+
/** Inspect the configured MCP server specs. Useful for tests. */
|
|
13336
|
+
getMcpServerSpecs() {
|
|
13337
|
+
return this.mcp.servers;
|
|
13338
|
+
}
|
|
13288
13339
|
/** Add conversation history messages. */
|
|
13289
13340
|
withHistory(messages) {
|
|
13290
13341
|
this.core.initialMessages.push(...normalizeHistory(messages));
|
|
@@ -13605,7 +13656,8 @@ ${preActivatedBlock}` : preActivatedBlock;
|
|
|
13605
13656
|
parentObservers: this.subagents.parentObservers
|
|
13606
13657
|
},
|
|
13607
13658
|
sharedRateLimitTracker: this.subagents.sharedRateLimitTracker,
|
|
13608
|
-
sharedRetryConfig: this.retry.sharedRetryConfig
|
|
13659
|
+
sharedRetryConfig: this.retry.sharedRetryConfig,
|
|
13660
|
+
mcpSpecs: this.mcp.servers.length > 0 ? [...this.mcp.servers] : void 0
|
|
13609
13661
|
};
|
|
13610
13662
|
}
|
|
13611
13663
|
/** Create agent and start with a user prompt. */
|
|
@@ -14251,8 +14303,8 @@ var init_error_formatter = __esm({
|
|
|
14251
14303
|
const parts = [];
|
|
14252
14304
|
parts.push(`Error: Invalid parameters for '${gadgetName}':`);
|
|
14253
14305
|
for (const issue of zodError.issues) {
|
|
14254
|
-
const
|
|
14255
|
-
parts.push(` - ${
|
|
14306
|
+
const path4 = issue.path.join(".") || "root";
|
|
14307
|
+
parts.push(` - ${path4}: ${issue.message}`);
|
|
14256
14308
|
}
|
|
14257
14309
|
parts.push("");
|
|
14258
14310
|
parts.push("Gadget Usage:");
|
|
@@ -16612,184 +16664,1081 @@ var init_stream_processor_factory = __esm({
|
|
|
16612
16664
|
}
|
|
16613
16665
|
});
|
|
16614
16666
|
|
|
16615
|
-
// src/
|
|
16616
|
-
var
|
|
16617
|
-
var
|
|
16618
|
-
"src/
|
|
16667
|
+
// src/mcp/errors.ts
|
|
16668
|
+
var McpError, McpUntrustedCommandError, McpConnectError, McpToolCallError, McpTimeoutError, JsonSchemaConversionError;
|
|
16669
|
+
var init_errors = __esm({
|
|
16670
|
+
"src/mcp/errors.ts"() {
|
|
16619
16671
|
"use strict";
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
|
|
16624
|
-
|
|
16625
|
-
|
|
16626
|
-
|
|
16627
|
-
|
|
16628
|
-
|
|
16629
|
-
|
|
16630
|
-
|
|
16631
|
-
|
|
16632
|
-
|
|
16633
|
-
|
|
16634
|
-
|
|
16635
|
-
|
|
16636
|
-
|
|
16637
|
-
|
|
16638
|
-
|
|
16639
|
-
|
|
16640
|
-
|
|
16641
|
-
|
|
16642
|
-
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16648
|
-
|
|
16649
|
-
|
|
16650
|
-
|
|
16651
|
-
|
|
16652
|
-
|
|
16653
|
-
|
|
16654
|
-
|
|
16655
|
-
|
|
16656
|
-
|
|
16657
|
-
|
|
16658
|
-
|
|
16659
|
-
|
|
16660
|
-
|
|
16661
|
-
|
|
16662
|
-
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16666
|
-
|
|
16667
|
-
|
|
16668
|
-
|
|
16669
|
-
|
|
16670
|
-
|
|
16671
|
-
|
|
16672
|
-
|
|
16673
|
-
|
|
16674
|
-
|
|
16675
|
-
|
|
16676
|
-
|
|
16677
|
-
|
|
16678
|
-
|
|
16679
|
-
|
|
16680
|
-
|
|
16681
|
-
|
|
16682
|
-
|
|
16683
|
-
|
|
16684
|
-
|
|
16685
|
-
|
|
16686
|
-
|
|
16687
|
-
|
|
16672
|
+
McpError = class extends Error {
|
|
16673
|
+
serverName;
|
|
16674
|
+
constructor(message, serverName) {
|
|
16675
|
+
super(message);
|
|
16676
|
+
this.name = "McpError";
|
|
16677
|
+
this.serverName = serverName;
|
|
16678
|
+
}
|
|
16679
|
+
};
|
|
16680
|
+
McpUntrustedCommandError = class extends McpError {
|
|
16681
|
+
command;
|
|
16682
|
+
constructor(command, serverName) {
|
|
16683
|
+
super(
|
|
16684
|
+
`Refusing to spawn MCP stdio command "${command}" because its basename is not in the default allowlist. To opt in, set { trust: true } on the server spec (library), or trust = true in your TOML mcp.servers block, or pass --mcp-trust ${serverName ?? "<name>"} on the CLI. See https://llmist.dev/library/advanced/mcp-security/ for context (CVE-2026-30623).`,
|
|
16685
|
+
serverName
|
|
16686
|
+
);
|
|
16687
|
+
this.name = "McpUntrustedCommandError";
|
|
16688
|
+
this.command = command;
|
|
16689
|
+
}
|
|
16690
|
+
};
|
|
16691
|
+
McpConnectError = class extends McpError {
|
|
16692
|
+
cause;
|
|
16693
|
+
constructor(message, opts) {
|
|
16694
|
+
super(message, opts?.serverName);
|
|
16695
|
+
this.name = "McpConnectError";
|
|
16696
|
+
this.cause = opts?.cause;
|
|
16697
|
+
}
|
|
16698
|
+
};
|
|
16699
|
+
McpToolCallError = class extends McpError {
|
|
16700
|
+
toolName;
|
|
16701
|
+
cause;
|
|
16702
|
+
constructor(toolName, message, opts) {
|
|
16703
|
+
super(message, opts?.serverName);
|
|
16704
|
+
this.name = "McpToolCallError";
|
|
16705
|
+
this.toolName = toolName;
|
|
16706
|
+
this.cause = opts?.cause;
|
|
16707
|
+
}
|
|
16708
|
+
};
|
|
16709
|
+
McpTimeoutError = class extends McpError {
|
|
16710
|
+
operation;
|
|
16711
|
+
timeoutMs;
|
|
16712
|
+
constructor(operation, timeoutMs, serverName) {
|
|
16713
|
+
super(
|
|
16714
|
+
`MCP operation "${operation}" on server "${serverName ?? "<unknown>"}" timed out after ${timeoutMs}ms`,
|
|
16715
|
+
serverName
|
|
16716
|
+
);
|
|
16717
|
+
this.name = "McpTimeoutError";
|
|
16718
|
+
this.operation = operation;
|
|
16719
|
+
this.timeoutMs = timeoutMs;
|
|
16720
|
+
}
|
|
16721
|
+
};
|
|
16722
|
+
JsonSchemaConversionError = class extends Error {
|
|
16723
|
+
schemaFragment;
|
|
16724
|
+
reason;
|
|
16725
|
+
constructor(reason, schemaFragment) {
|
|
16726
|
+
super(`JSON Schema \u2192 Zod conversion failed: ${reason}`);
|
|
16727
|
+
this.name = "JsonSchemaConversionError";
|
|
16728
|
+
this.reason = reason;
|
|
16729
|
+
this.schemaFragment = schemaFragment;
|
|
16730
|
+
}
|
|
16731
|
+
};
|
|
16732
|
+
}
|
|
16733
|
+
});
|
|
16734
|
+
|
|
16735
|
+
// src/mcp/allowlist.ts
|
|
16736
|
+
function assertCommandAllowed(command, trusted, customAllowlist) {
|
|
16737
|
+
if (!command || typeof command !== "string") {
|
|
16738
|
+
throw new McpUntrustedCommandError(String(command));
|
|
16739
|
+
}
|
|
16740
|
+
if (WHITESPACE_OR_META_RE.test(command)) {
|
|
16741
|
+
throw new McpUntrustedCommandError(command);
|
|
16742
|
+
}
|
|
16743
|
+
if (trusted) return;
|
|
16744
|
+
const allowlist = customAllowlist ?? DEFAULT_MCP_COMMAND_ALLOWLIST;
|
|
16745
|
+
const base = import_node_path6.default.basename(command);
|
|
16746
|
+
if (!allowlist.has(base)) {
|
|
16747
|
+
throw new McpUntrustedCommandError(command);
|
|
16748
|
+
}
|
|
16749
|
+
}
|
|
16750
|
+
var import_node_path6, DEFAULT_MCP_COMMAND_ALLOWLIST, WHITESPACE_OR_META_RE;
|
|
16751
|
+
var init_allowlist = __esm({
|
|
16752
|
+
"src/mcp/allowlist.ts"() {
|
|
16753
|
+
"use strict";
|
|
16754
|
+
import_node_path6 = __toESM(require("path"), 1);
|
|
16755
|
+
init_errors();
|
|
16756
|
+
DEFAULT_MCP_COMMAND_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
16757
|
+
"npx",
|
|
16758
|
+
"node",
|
|
16759
|
+
"uvx",
|
|
16760
|
+
"uv",
|
|
16761
|
+
"python",
|
|
16762
|
+
"python3",
|
|
16763
|
+
"deno",
|
|
16764
|
+
"bun"
|
|
16765
|
+
]);
|
|
16766
|
+
WHITESPACE_OR_META_RE = /[\s;|&`$<>()'"\\]/;
|
|
16767
|
+
}
|
|
16768
|
+
});
|
|
16769
|
+
|
|
16770
|
+
// src/mcp/client.ts
|
|
16771
|
+
async function loadSdk() {
|
|
16772
|
+
if (!cachedSdk) {
|
|
16773
|
+
cachedSdk = (async () => {
|
|
16774
|
+
const [client, stdio, http] = await Promise.all([
|
|
16775
|
+
import("@modelcontextprotocol/sdk/client/index.js"),
|
|
16776
|
+
import("@modelcontextprotocol/sdk/client/stdio.js"),
|
|
16777
|
+
import("@modelcontextprotocol/sdk/client/streamableHttp.js")
|
|
16778
|
+
]);
|
|
16779
|
+
return {
|
|
16780
|
+
Client: client.Client,
|
|
16781
|
+
StdioClientTransport: stdio.StdioClientTransport,
|
|
16782
|
+
StreamableHTTPClientTransport: http.StreamableHTTPClientTransport
|
|
16783
|
+
};
|
|
16784
|
+
})();
|
|
16785
|
+
}
|
|
16786
|
+
return cachedSdk;
|
|
16787
|
+
}
|
|
16788
|
+
var cachedSdk, DEFAULT_CLIENT_INFO, McpClient;
|
|
16789
|
+
var init_client2 = __esm({
|
|
16790
|
+
"src/mcp/client.ts"() {
|
|
16791
|
+
"use strict";
|
|
16792
|
+
init_allowlist();
|
|
16793
|
+
init_errors();
|
|
16794
|
+
cachedSdk = null;
|
|
16795
|
+
DEFAULT_CLIENT_INFO = { name: "llmist", version: "0.0.0" };
|
|
16796
|
+
McpClient = class {
|
|
16797
|
+
constructor(spec, opts) {
|
|
16798
|
+
this.spec = spec;
|
|
16799
|
+
this.injectedTransport = opts?.transport;
|
|
16800
|
+
this.clientInfo = opts?.clientInfo ?? DEFAULT_CLIENT_INFO;
|
|
16801
|
+
}
|
|
16802
|
+
sdkClient = null;
|
|
16803
|
+
spawnedPid = null;
|
|
16804
|
+
closed = false;
|
|
16805
|
+
injectedTransport;
|
|
16806
|
+
clientInfo;
|
|
16807
|
+
get serverName() {
|
|
16808
|
+
return this.spec.name;
|
|
16809
|
+
}
|
|
16810
|
+
get pid() {
|
|
16811
|
+
return this.spawnedPid;
|
|
16812
|
+
}
|
|
16813
|
+
get serverCapabilities() {
|
|
16814
|
+
if (!this.sdkClient) return null;
|
|
16815
|
+
return this.sdkClient.getServerCapabilities() ?? null;
|
|
16816
|
+
}
|
|
16817
|
+
async connect() {
|
|
16818
|
+
if (this.sdkClient) return;
|
|
16819
|
+
let transport;
|
|
16820
|
+
if (this.injectedTransport) {
|
|
16821
|
+
transport = this.injectedTransport;
|
|
16822
|
+
} else if (this.spec.transport === "stdio") {
|
|
16823
|
+
assertCommandAllowed(this.spec.command, this.spec.trust === true);
|
|
16824
|
+
const { StdioClientTransport } = await loadSdk();
|
|
16825
|
+
const stdioTransport = new StdioClientTransport({
|
|
16826
|
+
command: this.spec.command,
|
|
16827
|
+
args: this.spec.args,
|
|
16828
|
+
env: this.spec.env
|
|
16829
|
+
});
|
|
16830
|
+
transport = stdioTransport;
|
|
16831
|
+
this.spawnedPid = null;
|
|
16832
|
+
} else {
|
|
16833
|
+
const { StreamableHTTPClientTransport } = await loadSdk();
|
|
16834
|
+
let url;
|
|
16835
|
+
try {
|
|
16836
|
+
url = new URL(this.spec.url);
|
|
16837
|
+
} catch (err) {
|
|
16838
|
+
throw new McpConnectError(
|
|
16839
|
+
`MCP server "${this.spec.name}" has an invalid URL: ${err.message}`,
|
|
16840
|
+
{ serverName: this.spec.name, cause: err }
|
|
16841
|
+
);
|
|
16842
|
+
}
|
|
16843
|
+
transport = new StreamableHTTPClientTransport(url, {
|
|
16844
|
+
requestInit: this.spec.headers ? { headers: this.spec.headers } : void 0
|
|
16845
|
+
});
|
|
16846
|
+
}
|
|
16847
|
+
const { Client } = await loadSdk();
|
|
16848
|
+
const client = new Client(this.clientInfo, { capabilities: {} });
|
|
16849
|
+
try {
|
|
16850
|
+
await this.withTimeout(() => client.connect(transport), "connect");
|
|
16851
|
+
} catch (err) {
|
|
16852
|
+
throw new McpConnectError(
|
|
16853
|
+
`Failed to connect to MCP server "${this.spec.name}": ${err.message}`,
|
|
16854
|
+
{ serverName: this.spec.name, cause: err }
|
|
16688
16855
|
);
|
|
16689
16856
|
}
|
|
16690
|
-
this.
|
|
16691
|
-
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
this.temperature = options.temperature;
|
|
16695
|
-
this.logger = options.logger ?? createLogger({ name: "llmist:agent" });
|
|
16696
|
-
this.registry = options.registry;
|
|
16697
|
-
this.prefixConfig = options.prefixConfig;
|
|
16698
|
-
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
16699
|
-
const outputLimitConfig = {
|
|
16700
|
-
enabled: options.outputLimitConfig?.enabled,
|
|
16701
|
-
limitPercent: options.outputLimitConfig?.limitPercent
|
|
16702
|
-
};
|
|
16703
|
-
this.outputLimitManager = new OutputLimitManager(
|
|
16704
|
-
this.client,
|
|
16705
|
-
this.model,
|
|
16706
|
-
outputLimitConfig,
|
|
16707
|
-
this.registry,
|
|
16708
|
-
this.logger
|
|
16709
|
-
);
|
|
16710
|
-
this.mediaStore = new MediaStore();
|
|
16711
|
-
this.hooks = this.outputLimitManager.getHooks(options.hooks);
|
|
16712
|
-
const baseBuilder = new LLMMessageBuilder(options.promptConfig);
|
|
16713
|
-
if (options.systemPrompt) {
|
|
16714
|
-
baseBuilder.addSystem(options.systemPrompt);
|
|
16857
|
+
this.sdkClient = client;
|
|
16858
|
+
const maybePid = transport.pid;
|
|
16859
|
+
if (typeof maybePid === "number") {
|
|
16860
|
+
this.spawnedPid = maybePid;
|
|
16715
16861
|
}
|
|
16716
|
-
|
|
16717
|
-
|
|
16718
|
-
|
|
16719
|
-
|
|
16720
|
-
|
|
16721
|
-
|
|
16722
|
-
|
|
16723
|
-
|
|
16724
|
-
content: message.content
|
|
16862
|
+
}
|
|
16863
|
+
async listTools() {
|
|
16864
|
+
const client = this.requireClient();
|
|
16865
|
+
const res = await this.withTimeout(() => client.listTools(), "tools/list");
|
|
16866
|
+
return res.tools.map((t) => ({
|
|
16867
|
+
name: t.name,
|
|
16868
|
+
description: t.description,
|
|
16869
|
+
inputSchema: t.inputSchema
|
|
16725
16870
|
}));
|
|
16726
|
-
|
|
16727
|
-
|
|
16728
|
-
|
|
16729
|
-
|
|
16730
|
-
|
|
16731
|
-
|
|
16732
|
-
|
|
16733
|
-
|
|
16871
|
+
}
|
|
16872
|
+
async callTool(name, args) {
|
|
16873
|
+
const client = this.requireClient();
|
|
16874
|
+
try {
|
|
16875
|
+
const res = await this.withTimeout(
|
|
16876
|
+
() => client.callTool({
|
|
16877
|
+
name,
|
|
16878
|
+
arguments: args ?? {}
|
|
16879
|
+
}),
|
|
16880
|
+
`tools/call ${name}`
|
|
16881
|
+
);
|
|
16882
|
+
return {
|
|
16883
|
+
content: res.content ?? [],
|
|
16884
|
+
isError: res.isError
|
|
16885
|
+
};
|
|
16886
|
+
} catch (err) {
|
|
16887
|
+
throw new McpToolCallError(
|
|
16888
|
+
name,
|
|
16889
|
+
`MCP tool call "${name}" on server "${this.spec.name}" failed: ${err.message}`,
|
|
16890
|
+
{ serverName: this.spec.name, cause: err }
|
|
16891
|
+
);
|
|
16734
16892
|
}
|
|
16735
|
-
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
|
|
16739
|
-
|
|
16740
|
-
|
|
16741
|
-
const
|
|
16742
|
-
|
|
16743
|
-
|
|
16744
|
-
|
|
16745
|
-
|
|
16746
|
-
|
|
16747
|
-
|
|
16893
|
+
}
|
|
16894
|
+
async listPrompts() {
|
|
16895
|
+
const client = this.requireClient();
|
|
16896
|
+
if (!client.listPrompts) {
|
|
16897
|
+
return [];
|
|
16898
|
+
}
|
|
16899
|
+
const listPrompts = client.listPrompts.bind(client);
|
|
16900
|
+
const res = await this.withTimeout(() => listPrompts(), "prompts/list");
|
|
16901
|
+
return res.prompts.map((p) => ({
|
|
16902
|
+
name: p.name,
|
|
16903
|
+
description: p.description,
|
|
16904
|
+
arguments: p.arguments
|
|
16905
|
+
}));
|
|
16906
|
+
}
|
|
16907
|
+
async getPrompt(name, args) {
|
|
16908
|
+
const client = this.requireClient();
|
|
16909
|
+
if (!client.getPrompt) {
|
|
16910
|
+
throw new McpToolCallError(name, "Server has no getPrompt method", {
|
|
16911
|
+
serverName: this.spec.name
|
|
16912
|
+
});
|
|
16913
|
+
}
|
|
16914
|
+
const getPrompt = client.getPrompt.bind(client);
|
|
16915
|
+
try {
|
|
16916
|
+
const res = await this.withTimeout(
|
|
16917
|
+
() => getPrompt({ name, arguments: args ?? {} }),
|
|
16918
|
+
`prompts/get ${name}`
|
|
16919
|
+
);
|
|
16920
|
+
return {
|
|
16921
|
+
description: res.description,
|
|
16922
|
+
messages: res.messages.map((m) => ({
|
|
16923
|
+
role: m.role,
|
|
16924
|
+
content: m.content
|
|
16925
|
+
}))
|
|
16926
|
+
};
|
|
16927
|
+
} catch (err) {
|
|
16928
|
+
throw new McpToolCallError(
|
|
16929
|
+
name,
|
|
16930
|
+
`MCP prompts/get "${name}" on server "${this.spec.name}" failed: ${err.message}`,
|
|
16931
|
+
{ serverName: this.spec.name, cause: err }
|
|
16748
16932
|
);
|
|
16749
16933
|
}
|
|
16750
|
-
|
|
16751
|
-
|
|
16752
|
-
this.
|
|
16753
|
-
this.
|
|
16754
|
-
if (
|
|
16755
|
-
|
|
16756
|
-
|
|
16757
|
-
|
|
16758
|
-
if (rateLimitConfig.enabled) {
|
|
16759
|
-
this.rateLimitTracker = new RateLimitTracker(options.rateLimitConfig);
|
|
16934
|
+
}
|
|
16935
|
+
async close() {
|
|
16936
|
+
if (this.closed) return;
|
|
16937
|
+
this.closed = true;
|
|
16938
|
+
if (this.sdkClient) {
|
|
16939
|
+
try {
|
|
16940
|
+
await this.sdkClient.close();
|
|
16941
|
+
} catch {
|
|
16760
16942
|
}
|
|
16943
|
+
this.sdkClient = null;
|
|
16761
16944
|
}
|
|
16762
|
-
|
|
16763
|
-
|
|
16764
|
-
this.
|
|
16765
|
-
|
|
16766
|
-
|
|
16767
|
-
|
|
16768
|
-
|
|
16769
|
-
|
|
16770
|
-
|
|
16771
|
-
|
|
16772
|
-
|
|
16773
|
-
|
|
16774
|
-
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
|
|
16778
|
-
|
|
16779
|
-
|
|
16780
|
-
|
|
16781
|
-
|
|
16782
|
-
|
|
16783
|
-
|
|
16784
|
-
|
|
16785
|
-
|
|
16945
|
+
}
|
|
16946
|
+
requireClient() {
|
|
16947
|
+
if (!this.sdkClient) {
|
|
16948
|
+
throw new McpConnectError(
|
|
16949
|
+
`MCP client for server "${this.spec.name}" is not connected. Call connect() first.`,
|
|
16950
|
+
{ serverName: this.spec.name }
|
|
16951
|
+
);
|
|
16952
|
+
}
|
|
16953
|
+
return this.sdkClient;
|
|
16954
|
+
}
|
|
16955
|
+
async withTimeout(fn, operation) {
|
|
16956
|
+
const timeoutMs = this.spec.timeoutMs;
|
|
16957
|
+
if (timeoutMs === void 0 || timeoutMs <= 0) {
|
|
16958
|
+
return fn();
|
|
16959
|
+
}
|
|
16960
|
+
return new Promise((resolve2, reject) => {
|
|
16961
|
+
let settled = false;
|
|
16962
|
+
const timeoutId = setTimeout(() => {
|
|
16963
|
+
if (settled) return;
|
|
16964
|
+
settled = true;
|
|
16965
|
+
reject(new McpTimeoutError(operation, timeoutMs, this.spec.name));
|
|
16966
|
+
}, timeoutMs);
|
|
16967
|
+
fn().then((result) => {
|
|
16968
|
+
if (settled) return;
|
|
16969
|
+
settled = true;
|
|
16970
|
+
clearTimeout(timeoutId);
|
|
16971
|
+
resolve2(result);
|
|
16972
|
+
}).catch((err) => {
|
|
16973
|
+
if (settled) return;
|
|
16974
|
+
settled = true;
|
|
16975
|
+
clearTimeout(timeoutId);
|
|
16976
|
+
reject(err);
|
|
16977
|
+
});
|
|
16786
16978
|
});
|
|
16787
|
-
|
|
16788
|
-
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
|
|
16792
|
-
|
|
16979
|
+
}
|
|
16980
|
+
};
|
|
16981
|
+
}
|
|
16982
|
+
});
|
|
16983
|
+
|
|
16984
|
+
// src/mcp/lifecycle.ts
|
|
16985
|
+
var McpLifecycle;
|
|
16986
|
+
var init_lifecycle = __esm({
|
|
16987
|
+
"src/mcp/lifecycle.ts"() {
|
|
16988
|
+
"use strict";
|
|
16989
|
+
init_logger();
|
|
16990
|
+
McpLifecycle = class {
|
|
16991
|
+
clients = [];
|
|
16992
|
+
closing = null;
|
|
16993
|
+
signalHandlersInstalled = false;
|
|
16994
|
+
sigtermHandler = null;
|
|
16995
|
+
sigintHandler = null;
|
|
16996
|
+
get size() {
|
|
16997
|
+
return this.clients.length;
|
|
16998
|
+
}
|
|
16999
|
+
register(client) {
|
|
17000
|
+
this.clients.push(client);
|
|
17001
|
+
}
|
|
17002
|
+
/**
|
|
17003
|
+
* Attach SIGTERM/SIGINT handlers that close every registered client when
|
|
17004
|
+
* the parent process is asked to exit. Idempotent (double install is a
|
|
17005
|
+
* no-op) and removable via `removeSignalHandlers()`.
|
|
17006
|
+
*/
|
|
17007
|
+
installSignalHandlers() {
|
|
17008
|
+
if (this.signalHandlersInstalled) return;
|
|
17009
|
+
this.signalHandlersInstalled = true;
|
|
17010
|
+
this.sigtermHandler = () => {
|
|
17011
|
+
void this.closeAll();
|
|
17012
|
+
};
|
|
17013
|
+
this.sigintHandler = () => {
|
|
17014
|
+
void this.closeAll();
|
|
17015
|
+
};
|
|
17016
|
+
process.on("SIGTERM", this.sigtermHandler);
|
|
17017
|
+
process.on("SIGINT", this.sigintHandler);
|
|
17018
|
+
}
|
|
17019
|
+
removeSignalHandlers() {
|
|
17020
|
+
if (!this.signalHandlersInstalled) return;
|
|
17021
|
+
if (this.sigtermHandler) process.off("SIGTERM", this.sigtermHandler);
|
|
17022
|
+
if (this.sigintHandler) process.off("SIGINT", this.sigintHandler);
|
|
17023
|
+
this.sigtermHandler = null;
|
|
17024
|
+
this.sigintHandler = null;
|
|
17025
|
+
this.signalHandlersInstalled = false;
|
|
17026
|
+
}
|
|
17027
|
+
/**
|
|
17028
|
+
* Close every registered client in parallel. Errors from individual close()
|
|
17029
|
+
* calls are swallowed (logged via console.warn) — a teardown path must not
|
|
17030
|
+
* throw because that would mask the original reason the agent is shutting
|
|
17031
|
+
* down. Idempotent: concurrent calls all return the same in-flight promise.
|
|
17032
|
+
*/
|
|
17033
|
+
async closeAll() {
|
|
17034
|
+
if (this.closing) return this.closing;
|
|
17035
|
+
const toClose = this.clients;
|
|
17036
|
+
this.clients = [];
|
|
17037
|
+
this.closing = (async () => {
|
|
17038
|
+
const results = await Promise.allSettled(toClose.map((c) => c.close()));
|
|
17039
|
+
for (const r of results) {
|
|
17040
|
+
if (r.status === "rejected") {
|
|
17041
|
+
defaultLogger.debug("MCP client close failed during teardown:", r.reason);
|
|
17042
|
+
}
|
|
17043
|
+
}
|
|
17044
|
+
})();
|
|
17045
|
+
try {
|
|
17046
|
+
await this.closing;
|
|
17047
|
+
} finally {
|
|
17048
|
+
this.closing = null;
|
|
17049
|
+
this.removeSignalHandlers();
|
|
17050
|
+
}
|
|
17051
|
+
}
|
|
17052
|
+
};
|
|
17053
|
+
}
|
|
17054
|
+
});
|
|
17055
|
+
|
|
17056
|
+
// src/mcp/multi-server.ts
|
|
17057
|
+
function resolveToolNames(input) {
|
|
17058
|
+
const counts = /* @__PURE__ */ new Map();
|
|
17059
|
+
for (const s of input) {
|
|
17060
|
+
for (const t of s.tools) {
|
|
17061
|
+
counts.set(t.name, (counts.get(t.name) ?? 0) + 1);
|
|
17062
|
+
}
|
|
17063
|
+
}
|
|
17064
|
+
const collidingServers = /* @__PURE__ */ new Set();
|
|
17065
|
+
for (const s of input) {
|
|
17066
|
+
if (s.tools.some((t) => (counts.get(t.name) ?? 0) > 1)) {
|
|
17067
|
+
collidingServers.add(s.server.name);
|
|
17068
|
+
}
|
|
17069
|
+
}
|
|
17070
|
+
return input.map((s) => ({
|
|
17071
|
+
...s,
|
|
17072
|
+
prefix: collidingServers.has(s.server.name) ? `${s.server.name}__` : void 0
|
|
17073
|
+
}));
|
|
17074
|
+
}
|
|
17075
|
+
var init_multi_server = __esm({
|
|
17076
|
+
"src/mcp/multi-server.ts"() {
|
|
17077
|
+
"use strict";
|
|
17078
|
+
}
|
|
17079
|
+
});
|
|
17080
|
+
|
|
17081
|
+
// src/mcp/prompt-adapter.ts
|
|
17082
|
+
function mcpPromptToSkill(descriptor, client, opts) {
|
|
17083
|
+
return new McpPromptSkill(descriptor, client, opts);
|
|
17084
|
+
}
|
|
17085
|
+
var McpPromptSkill;
|
|
17086
|
+
var init_prompt_adapter = __esm({
|
|
17087
|
+
"src/mcp/prompt-adapter.ts"() {
|
|
17088
|
+
"use strict";
|
|
17089
|
+
McpPromptSkill = class {
|
|
17090
|
+
name;
|
|
17091
|
+
description;
|
|
17092
|
+
metadata;
|
|
17093
|
+
isUserInvocable = true;
|
|
17094
|
+
isModelInvocable = true;
|
|
17095
|
+
client;
|
|
17096
|
+
mcpToolName;
|
|
17097
|
+
constructor(descriptor, client, opts) {
|
|
17098
|
+
const prefix = opts?.prefix ?? "";
|
|
17099
|
+
this.name = prefix + descriptor.name;
|
|
17100
|
+
this.description = descriptor.description ?? `MCP prompt "${descriptor.name}" from server "${client.serverName}"`;
|
|
17101
|
+
this.metadata = {
|
|
17102
|
+
name: this.name,
|
|
17103
|
+
description: this.description,
|
|
17104
|
+
...descriptor.arguments ? { arguments: descriptor.arguments } : {}
|
|
17105
|
+
};
|
|
17106
|
+
this.client = client;
|
|
17107
|
+
this.mcpToolName = descriptor.name;
|
|
17108
|
+
}
|
|
17109
|
+
/**
|
|
17110
|
+
* Render the prompt by calling the MCP server's prompts/get with the
|
|
17111
|
+
* supplied arguments and joining the resulting message text.
|
|
17112
|
+
*/
|
|
17113
|
+
async getInstructions(args) {
|
|
17114
|
+
const result = await this.client.getPrompt(this.mcpToolName, args ?? {});
|
|
17115
|
+
const parts = [];
|
|
17116
|
+
for (const m of result.messages) {
|
|
17117
|
+
const c = m.content;
|
|
17118
|
+
if (c.type === "text" && typeof c.text === "string") {
|
|
17119
|
+
parts.push(c.text);
|
|
17120
|
+
} else {
|
|
17121
|
+
try {
|
|
17122
|
+
parts.push(JSON.stringify(c));
|
|
17123
|
+
} catch {
|
|
17124
|
+
parts.push(String(c));
|
|
17125
|
+
}
|
|
17126
|
+
}
|
|
17127
|
+
}
|
|
17128
|
+
return parts.join("\n");
|
|
17129
|
+
}
|
|
17130
|
+
};
|
|
17131
|
+
}
|
|
17132
|
+
});
|
|
17133
|
+
|
|
17134
|
+
// src/gadgets/helpers.ts
|
|
17135
|
+
function gadgetSuccess(data = {}) {
|
|
17136
|
+
return JSON.stringify({ success: true, ...data });
|
|
17137
|
+
}
|
|
17138
|
+
function gadgetError(message, details) {
|
|
17139
|
+
return JSON.stringify({ error: message, ...details });
|
|
17140
|
+
}
|
|
17141
|
+
function getErrorMessage(error) {
|
|
17142
|
+
return error instanceof Error ? error.message : String(error);
|
|
17143
|
+
}
|
|
17144
|
+
function withErrorHandling(execute) {
|
|
17145
|
+
return async (params, ctx) => {
|
|
17146
|
+
try {
|
|
17147
|
+
return await execute(params, ctx);
|
|
17148
|
+
} catch (error) {
|
|
17149
|
+
return gadgetError(getErrorMessage(error));
|
|
17150
|
+
}
|
|
17151
|
+
};
|
|
17152
|
+
}
|
|
17153
|
+
function createMediaOutput(kind, data, mimeType, options) {
|
|
17154
|
+
const buffer = data instanceof Buffer ? data : Buffer.from(data);
|
|
17155
|
+
return {
|
|
17156
|
+
kind,
|
|
17157
|
+
data: buffer.toString("base64"),
|
|
17158
|
+
mimeType,
|
|
17159
|
+
description: options?.description,
|
|
17160
|
+
metadata: options?.metadata,
|
|
17161
|
+
fileName: options?.fileName
|
|
17162
|
+
};
|
|
17163
|
+
}
|
|
17164
|
+
function resultWithMedia(result, media, cost) {
|
|
17165
|
+
if (media.length === 0) {
|
|
17166
|
+
throw new Error("resultWithMedia: media array cannot be empty");
|
|
17167
|
+
}
|
|
17168
|
+
return {
|
|
17169
|
+
result,
|
|
17170
|
+
media,
|
|
17171
|
+
cost
|
|
17172
|
+
};
|
|
17173
|
+
}
|
|
17174
|
+
function resultWithImage(result, imageData, options) {
|
|
17175
|
+
const buffer = imageData instanceof Buffer ? imageData : Buffer.from(imageData);
|
|
17176
|
+
const mimeType = options?.mimeType ?? detectImageMimeType(buffer);
|
|
17177
|
+
if (!mimeType) {
|
|
17178
|
+
throw new Error(
|
|
17179
|
+
"Could not detect image MIME type. Please provide mimeType explicitly in options."
|
|
17180
|
+
);
|
|
17181
|
+
}
|
|
17182
|
+
return {
|
|
17183
|
+
result,
|
|
17184
|
+
media: [
|
|
17185
|
+
{
|
|
17186
|
+
kind: "image",
|
|
17187
|
+
data: buffer.toString("base64"),
|
|
17188
|
+
mimeType,
|
|
17189
|
+
description: options?.description,
|
|
17190
|
+
metadata: options?.metadata,
|
|
17191
|
+
fileName: options?.fileName
|
|
17192
|
+
}
|
|
17193
|
+
],
|
|
17194
|
+
cost: options?.cost
|
|
17195
|
+
};
|
|
17196
|
+
}
|
|
17197
|
+
function resultWithImages(result, images, cost) {
|
|
17198
|
+
if (images.length === 0) {
|
|
17199
|
+
throw new Error("resultWithImages: images array cannot be empty");
|
|
17200
|
+
}
|
|
17201
|
+
const media = images.map((img, index) => {
|
|
17202
|
+
const buffer = img.data instanceof Buffer ? img.data : Buffer.from(img.data);
|
|
17203
|
+
const mimeType = img.mimeType ?? detectImageMimeType(buffer);
|
|
17204
|
+
if (!mimeType) {
|
|
17205
|
+
throw new Error(
|
|
17206
|
+
`Could not detect MIME type for image at index ${index}. Please provide mimeType explicitly.`
|
|
17207
|
+
);
|
|
17208
|
+
}
|
|
17209
|
+
return {
|
|
17210
|
+
kind: "image",
|
|
17211
|
+
data: buffer.toString("base64"),
|
|
17212
|
+
mimeType,
|
|
17213
|
+
description: img.description,
|
|
17214
|
+
metadata: img.metadata,
|
|
17215
|
+
fileName: img.fileName
|
|
17216
|
+
};
|
|
17217
|
+
});
|
|
17218
|
+
return { result, media, cost };
|
|
17219
|
+
}
|
|
17220
|
+
function resultWithAudio(result, audioData, options) {
|
|
17221
|
+
const buffer = audioData instanceof Buffer ? audioData : Buffer.from(audioData);
|
|
17222
|
+
const mimeType = options?.mimeType ?? detectAudioMimeType(buffer);
|
|
17223
|
+
if (!mimeType) {
|
|
17224
|
+
throw new Error(
|
|
17225
|
+
"Could not detect audio MIME type. Please provide mimeType explicitly in options."
|
|
17226
|
+
);
|
|
17227
|
+
}
|
|
17228
|
+
const metadata = options?.durationMs ? { durationMs: options.durationMs } : void 0;
|
|
17229
|
+
return {
|
|
17230
|
+
result,
|
|
17231
|
+
media: [
|
|
17232
|
+
{
|
|
17233
|
+
kind: "audio",
|
|
17234
|
+
data: buffer.toString("base64"),
|
|
17235
|
+
mimeType,
|
|
17236
|
+
description: options?.description,
|
|
17237
|
+
metadata,
|
|
17238
|
+
fileName: options?.fileName
|
|
17239
|
+
}
|
|
17240
|
+
],
|
|
17241
|
+
cost: options?.cost
|
|
17242
|
+
};
|
|
17243
|
+
}
|
|
17244
|
+
function resultWithFile(result, fileData, mimeType, options) {
|
|
17245
|
+
const buffer = fileData instanceof Buffer ? fileData : Buffer.from(fileData);
|
|
17246
|
+
return {
|
|
17247
|
+
result,
|
|
17248
|
+
media: [
|
|
17249
|
+
{
|
|
17250
|
+
kind: "file",
|
|
17251
|
+
data: buffer.toString("base64"),
|
|
17252
|
+
mimeType,
|
|
17253
|
+
description: options?.description,
|
|
17254
|
+
fileName: options?.fileName
|
|
17255
|
+
}
|
|
17256
|
+
],
|
|
17257
|
+
cost: options?.cost
|
|
17258
|
+
};
|
|
17259
|
+
}
|
|
17260
|
+
var init_helpers = __esm({
|
|
17261
|
+
"src/gadgets/helpers.ts"() {
|
|
17262
|
+
"use strict";
|
|
17263
|
+
init_input_content();
|
|
17264
|
+
}
|
|
17265
|
+
});
|
|
17266
|
+
|
|
17267
|
+
// src/mcp/json-schema-to-zod.ts
|
|
17268
|
+
function jsonSchemaToZod(schema) {
|
|
17269
|
+
if (!schema || typeof schema !== "object") {
|
|
17270
|
+
return import_zod4.z.unknown();
|
|
17271
|
+
}
|
|
17272
|
+
if (schema.$ref) {
|
|
17273
|
+
throw new JsonSchemaConversionError("$ref is not supported in MCP tool schemas", schema);
|
|
17274
|
+
}
|
|
17275
|
+
if (schema.allOf) {
|
|
17276
|
+
throw new JsonSchemaConversionError(
|
|
17277
|
+
"allOf is not supported (MCP tools should use a single composed schema)",
|
|
17278
|
+
schema
|
|
17279
|
+
);
|
|
17280
|
+
}
|
|
17281
|
+
const union = schema.oneOf ?? schema.anyOf;
|
|
17282
|
+
if (union) {
|
|
17283
|
+
if (!Array.isArray(union) || union.length < 2) {
|
|
17284
|
+
throw new JsonSchemaConversionError("oneOf/anyOf must have at least two members", schema);
|
|
17285
|
+
}
|
|
17286
|
+
const branches = union.map((m) => jsonSchemaToZod(m));
|
|
17287
|
+
return applyDecorators(import_zod4.z.union(branches), schema);
|
|
17288
|
+
}
|
|
17289
|
+
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
17290
|
+
if (type === void 0 && schema.enum && Array.isArray(schema.enum)) {
|
|
17291
|
+
return applyDecorators(buildEnum(schema.enum), schema);
|
|
17292
|
+
}
|
|
17293
|
+
if (type === void 0) {
|
|
17294
|
+
return applyDecorators(import_zod4.z.unknown(), schema);
|
|
17295
|
+
}
|
|
17296
|
+
switch (type) {
|
|
17297
|
+
case "string": {
|
|
17298
|
+
let s;
|
|
17299
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
17300
|
+
s = buildEnum(schema.enum);
|
|
17301
|
+
} else {
|
|
17302
|
+
s = import_zod4.z.string();
|
|
17303
|
+
}
|
|
17304
|
+
return applyDecorators(s, schema);
|
|
17305
|
+
}
|
|
17306
|
+
case "number":
|
|
17307
|
+
return applyDecorators(import_zod4.z.number(), schema);
|
|
17308
|
+
case "integer":
|
|
17309
|
+
return applyDecorators(import_zod4.z.number().int(), schema);
|
|
17310
|
+
case "boolean":
|
|
17311
|
+
return applyDecorators(import_zod4.z.boolean(), schema);
|
|
17312
|
+
case "null":
|
|
17313
|
+
return applyDecorators(import_zod4.z.null(), schema);
|
|
17314
|
+
case "array": {
|
|
17315
|
+
const items = schema.items;
|
|
17316
|
+
if (Array.isArray(items)) {
|
|
17317
|
+
throw new JsonSchemaConversionError("tuple-style items arrays are not supported", schema);
|
|
17318
|
+
}
|
|
17319
|
+
const inner = items ? jsonSchemaToZod(items) : import_zod4.z.unknown();
|
|
17320
|
+
return applyDecorators(import_zod4.z.array(inner), schema);
|
|
17321
|
+
}
|
|
17322
|
+
case "object": {
|
|
17323
|
+
const props = schema.properties ?? {};
|
|
17324
|
+
const required = new Set(schema.required ?? []);
|
|
17325
|
+
const keys = Object.keys(props);
|
|
17326
|
+
if (keys.length === 0) {
|
|
17327
|
+
return applyDecorators(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()), schema);
|
|
17328
|
+
}
|
|
17329
|
+
const shape = {};
|
|
17330
|
+
for (const key of keys) {
|
|
17331
|
+
const inner = jsonSchemaToZod(props[key]);
|
|
17332
|
+
shape[key] = required.has(key) ? inner : inner.optional();
|
|
17333
|
+
}
|
|
17334
|
+
return applyDecorators(import_zod4.z.object(shape), schema);
|
|
17335
|
+
}
|
|
17336
|
+
default:
|
|
17337
|
+
throw new JsonSchemaConversionError(`unknown JSON Schema type "${type}"`, schema);
|
|
17338
|
+
}
|
|
17339
|
+
}
|
|
17340
|
+
function buildEnum(values) {
|
|
17341
|
+
if (values.every((v) => typeof v === "string")) {
|
|
17342
|
+
const literals2 = values;
|
|
17343
|
+
if (literals2.length === 0) {
|
|
17344
|
+
throw new JsonSchemaConversionError("enum cannot be empty", values);
|
|
17345
|
+
}
|
|
17346
|
+
return import_zod4.z.enum(literals2);
|
|
17347
|
+
}
|
|
17348
|
+
const literals = values.map((v) => import_zod4.z.literal(v));
|
|
17349
|
+
if (literals.length === 0) {
|
|
17350
|
+
throw new JsonSchemaConversionError("enum cannot be empty", values);
|
|
17351
|
+
}
|
|
17352
|
+
if (literals.length === 1) {
|
|
17353
|
+
return literals[0];
|
|
17354
|
+
}
|
|
17355
|
+
return import_zod4.z.union(literals);
|
|
17356
|
+
}
|
|
17357
|
+
function applyDecorators(base, schema) {
|
|
17358
|
+
let s = base;
|
|
17359
|
+
if (schema.nullable === true) {
|
|
17360
|
+
s = s.nullable();
|
|
17361
|
+
}
|
|
17362
|
+
if (schema.description) {
|
|
17363
|
+
s = s.describe(schema.description);
|
|
17364
|
+
}
|
|
17365
|
+
if (schema.default !== void 0) {
|
|
17366
|
+
s = s.default(schema.default);
|
|
17367
|
+
}
|
|
17368
|
+
return s;
|
|
17369
|
+
}
|
|
17370
|
+
var import_zod4;
|
|
17371
|
+
var init_json_schema_to_zod = __esm({
|
|
17372
|
+
"src/mcp/json-schema-to-zod.ts"() {
|
|
17373
|
+
"use strict";
|
|
17374
|
+
import_zod4 = require("zod");
|
|
17375
|
+
init_errors();
|
|
17376
|
+
}
|
|
17377
|
+
});
|
|
17378
|
+
|
|
17379
|
+
// src/mcp/tool-adapter.ts
|
|
17380
|
+
function mcpToolToGadget(tool, client, opts) {
|
|
17381
|
+
const gadgetName = (opts?.prefix ?? "") + tool.name;
|
|
17382
|
+
const schema = buildSchema(tool.inputSchema);
|
|
17383
|
+
const description = tool.description ?? `MCP tool "${tool.name}" from server "${client.serverName}"`;
|
|
17384
|
+
return createGadget({
|
|
17385
|
+
name: gadgetName,
|
|
17386
|
+
description,
|
|
17387
|
+
schema,
|
|
17388
|
+
execute: async (params) => {
|
|
17389
|
+
const result = await client.callTool(tool.name, params);
|
|
17390
|
+
return mcpResultToGadgetReturn(result, tool.name);
|
|
17391
|
+
}
|
|
17392
|
+
});
|
|
17393
|
+
}
|
|
17394
|
+
function buildSchema(inputSchema) {
|
|
17395
|
+
if (!inputSchema) {
|
|
17396
|
+
return import_zod5.z.object({});
|
|
17397
|
+
}
|
|
17398
|
+
const converted = jsonSchemaToZod(inputSchema);
|
|
17399
|
+
if (!(converted instanceof import_zod5.z.ZodObject) && !(converted instanceof import_zod5.z.ZodRecord)) {
|
|
17400
|
+
return import_zod5.z.object({}).passthrough();
|
|
17401
|
+
}
|
|
17402
|
+
return converted;
|
|
17403
|
+
}
|
|
17404
|
+
function mcpResultToGadgetReturn(result, toolName) {
|
|
17405
|
+
const blocks = result.content ?? [];
|
|
17406
|
+
const textParts = [];
|
|
17407
|
+
const media = [];
|
|
17408
|
+
for (const block of blocks) {
|
|
17409
|
+
const kind = block.type;
|
|
17410
|
+
if (kind === "text" && typeof block.text === "string") {
|
|
17411
|
+
textParts.push(block.text);
|
|
17412
|
+
} else if (kind === "image") {
|
|
17413
|
+
const b = block;
|
|
17414
|
+
media.push({ kind: "image", data: b.data, mimeType: b.mimeType });
|
|
17415
|
+
} else if (kind === "audio") {
|
|
17416
|
+
const b = block;
|
|
17417
|
+
media.push({ kind: "audio", data: b.data, mimeType: b.mimeType });
|
|
17418
|
+
} else {
|
|
17419
|
+
try {
|
|
17420
|
+
textParts.push(JSON.stringify(block));
|
|
17421
|
+
} catch {
|
|
17422
|
+
textParts.push(String(block));
|
|
17423
|
+
}
|
|
17424
|
+
}
|
|
17425
|
+
}
|
|
17426
|
+
const text3 = textParts.join("\n");
|
|
17427
|
+
if (result.isError) {
|
|
17428
|
+
throw new Error(
|
|
17429
|
+
text3 ? text3 : `MCP tool "${toolName}" returned an error result with no text content`
|
|
17430
|
+
);
|
|
17431
|
+
}
|
|
17432
|
+
if (media.length === 0) {
|
|
17433
|
+
return text3;
|
|
17434
|
+
}
|
|
17435
|
+
if (media.length === 1 && media[0].kind === "image") {
|
|
17436
|
+
const img = media[0];
|
|
17437
|
+
return resultWithImage(text3, Buffer.from(img.data, "base64"), {
|
|
17438
|
+
mimeType: img.mimeType
|
|
17439
|
+
});
|
|
17440
|
+
}
|
|
17441
|
+
return {
|
|
17442
|
+
result: text3,
|
|
17443
|
+
media: media.map((m) => ({
|
|
17444
|
+
kind: m.kind,
|
|
17445
|
+
data: m.data,
|
|
17446
|
+
mimeType: m.mimeType
|
|
17447
|
+
}))
|
|
17448
|
+
};
|
|
17449
|
+
}
|
|
17450
|
+
var import_zod5;
|
|
17451
|
+
var init_tool_adapter = __esm({
|
|
17452
|
+
"src/mcp/tool-adapter.ts"() {
|
|
17453
|
+
"use strict";
|
|
17454
|
+
import_zod5 = require("zod");
|
|
17455
|
+
init_create_gadget();
|
|
17456
|
+
init_helpers();
|
|
17457
|
+
init_json_schema_to_zod();
|
|
17458
|
+
}
|
|
17459
|
+
});
|
|
17460
|
+
|
|
17461
|
+
// src/mcp/runtime.ts
|
|
17462
|
+
var runtime_exports = {};
|
|
17463
|
+
__export(runtime_exports, {
|
|
17464
|
+
setupMcpServers: () => setupMcpServers
|
|
17465
|
+
});
|
|
17466
|
+
async function setupMcpServers(opts) {
|
|
17467
|
+
const { specs, registry, conversation, prefixConfig, systemPrompt, logger: logger2, onPromptDiscovered } = opts;
|
|
17468
|
+
const lifecycle = new McpLifecycle();
|
|
17469
|
+
lifecycle.installSignalHandlers();
|
|
17470
|
+
const connected = [];
|
|
17471
|
+
await Promise.all(
|
|
17472
|
+
specs.map(async (spec) => {
|
|
17473
|
+
const client = new McpClient(spec);
|
|
17474
|
+
try {
|
|
17475
|
+
await client.connect();
|
|
17476
|
+
} catch (err) {
|
|
17477
|
+
logger2.warn(
|
|
17478
|
+
`MCP server "${spec.name}" failed to connect \u2014 skipping. Reason: ${err.message}`
|
|
17479
|
+
);
|
|
17480
|
+
return;
|
|
17481
|
+
}
|
|
17482
|
+
lifecycle.register(client);
|
|
17483
|
+
const caps = client.serverCapabilities;
|
|
17484
|
+
const hasTools = caps?.tools !== void 0;
|
|
17485
|
+
let tools = [];
|
|
17486
|
+
if (hasTools) {
|
|
17487
|
+
try {
|
|
17488
|
+
tools = await client.listTools();
|
|
17489
|
+
} catch (err) {
|
|
17490
|
+
logger2.warn(
|
|
17491
|
+
`MCP server "${spec.name}" listTools failed \u2014 skipping. Reason: ${err.message}`
|
|
17492
|
+
);
|
|
17493
|
+
return;
|
|
17494
|
+
}
|
|
17495
|
+
} else {
|
|
17496
|
+
logger2.debug(
|
|
17497
|
+
`MCP server "${spec.name}" did not advertise tools capability \u2014 skipping listTools.`
|
|
17498
|
+
);
|
|
17499
|
+
}
|
|
17500
|
+
if (caps?.resources !== void 0) {
|
|
17501
|
+
logger2.debug(
|
|
17502
|
+
`MCP server "${spec.name}" advertises 'resources' capability \u2014 not yet implemented in llmist (deferred to v1.5).`
|
|
17503
|
+
);
|
|
17504
|
+
}
|
|
17505
|
+
if (caps?.prompts !== void 0 && onPromptDiscovered) {
|
|
17506
|
+
try {
|
|
17507
|
+
const prompts = await client.listPrompts();
|
|
17508
|
+
for (const p of prompts) {
|
|
17509
|
+
onPromptDiscovered(mcpPromptToSkill(p, client));
|
|
17510
|
+
}
|
|
17511
|
+
} catch (err) {
|
|
17512
|
+
logger2.debug(
|
|
17513
|
+
`MCP server "${spec.name}" listPrompts failed \u2014 skipping prompts. Reason: ${err.message}`
|
|
17514
|
+
);
|
|
17515
|
+
}
|
|
17516
|
+
}
|
|
17517
|
+
connected.push({ client, serverToolList: { server: spec, tools } });
|
|
17518
|
+
})
|
|
17519
|
+
);
|
|
17520
|
+
const resolved = resolveToolNames(connected.map((c) => c.serverToolList));
|
|
17521
|
+
for (const r of resolved) {
|
|
17522
|
+
const cs = connected.find((c) => c.serverToolList.server.name === r.server.name);
|
|
17523
|
+
if (!cs) continue;
|
|
17524
|
+
for (const tool of r.tools) {
|
|
17525
|
+
const gadget = mcpToolToGadget(tool, cs.client, { prefix: r.prefix });
|
|
17526
|
+
try {
|
|
17527
|
+
registry.register(gadget.name ?? tool.name, gadget);
|
|
17528
|
+
} catch (err) {
|
|
17529
|
+
logger2.warn(
|
|
17530
|
+
`MCP server "${r.server.name}" tool "${tool.name}" was not registered: ${err.message}`
|
|
17531
|
+
);
|
|
17532
|
+
}
|
|
17533
|
+
}
|
|
17534
|
+
}
|
|
17535
|
+
const builder = new LLMMessageBuilder();
|
|
17536
|
+
if (typeof systemPrompt === "string" && systemPrompt.length > 0) {
|
|
17537
|
+
builder.addSystem(systemPrompt);
|
|
17538
|
+
}
|
|
17539
|
+
builder.addGadgets(registry.getAll(), {
|
|
17540
|
+
startPrefix: prefixConfig?.gadgetStartPrefix,
|
|
17541
|
+
endPrefix: prefixConfig?.gadgetEndPrefix,
|
|
17542
|
+
argPrefix: prefixConfig?.gadgetArgPrefix
|
|
17543
|
+
});
|
|
17544
|
+
conversation.replaceBaseMessages(builder.build());
|
|
17545
|
+
return lifecycle;
|
|
17546
|
+
}
|
|
17547
|
+
var init_runtime = __esm({
|
|
17548
|
+
"src/mcp/runtime.ts"() {
|
|
17549
|
+
"use strict";
|
|
17550
|
+
init_messages();
|
|
17551
|
+
init_client2();
|
|
17552
|
+
init_lifecycle();
|
|
17553
|
+
init_multi_server();
|
|
17554
|
+
init_prompt_adapter();
|
|
17555
|
+
init_tool_adapter();
|
|
17556
|
+
}
|
|
17557
|
+
});
|
|
17558
|
+
|
|
17559
|
+
// src/agent/agent.ts
|
|
17560
|
+
var OVERFLOW_RECOVERY_MIN_HISTORY, Agent;
|
|
17561
|
+
var init_agent = __esm({
|
|
17562
|
+
"src/agent/agent.ts"() {
|
|
17563
|
+
"use strict";
|
|
17564
|
+
init_execution_tree();
|
|
17565
|
+
init_messages();
|
|
17566
|
+
init_model_shortcuts();
|
|
17567
|
+
init_rate_limit();
|
|
17568
|
+
init_retry();
|
|
17569
|
+
init_exceptions();
|
|
17570
|
+
init_media_store();
|
|
17571
|
+
init_logger();
|
|
17572
|
+
init_agent_internal_key();
|
|
17573
|
+
init_manager();
|
|
17574
|
+
init_conversation_manager();
|
|
17575
|
+
init_conversation_updater();
|
|
17576
|
+
init_event_handlers();
|
|
17577
|
+
init_llm_call_lifecycle();
|
|
17578
|
+
init_output_limit_manager();
|
|
17579
|
+
init_retry_orchestrator();
|
|
17580
|
+
init_safe_observe();
|
|
17581
|
+
init_stream_processor_factory();
|
|
17582
|
+
init_tree_hook_bridge();
|
|
17583
|
+
OVERFLOW_RECOVERY_MIN_HISTORY = 4;
|
|
17584
|
+
Agent = class {
|
|
17585
|
+
client;
|
|
17586
|
+
model;
|
|
17587
|
+
maxIterations;
|
|
17588
|
+
budget;
|
|
17589
|
+
temperature;
|
|
17590
|
+
logger;
|
|
17591
|
+
hooks;
|
|
17592
|
+
conversation;
|
|
17593
|
+
registry;
|
|
17594
|
+
prefixConfig;
|
|
17595
|
+
conversationUpdater;
|
|
17596
|
+
defaultMaxTokens;
|
|
17597
|
+
hasUserPrompt;
|
|
17598
|
+
// Gadget output limiting
|
|
17599
|
+
outputLimitManager;
|
|
17600
|
+
// Context compaction
|
|
17601
|
+
compactionManager;
|
|
17602
|
+
// Media storage (for gadgets returning images, audio, etc.)
|
|
17603
|
+
mediaStore;
|
|
17604
|
+
// Cancellation
|
|
17605
|
+
signal;
|
|
17606
|
+
reasoning;
|
|
17607
|
+
caching;
|
|
17608
|
+
// Retry configuration (shared reference also passed to StreamProcessorFactory)
|
|
17609
|
+
retryConfig;
|
|
17610
|
+
// Rate limit tracker for proactive throttling
|
|
17611
|
+
rateLimitTracker;
|
|
17612
|
+
// Cross-iteration dependency tracking - allows gadgets to depend on results from prior iterations
|
|
17613
|
+
completedInvocationIds = /* @__PURE__ */ new Set();
|
|
17614
|
+
failedInvocationIds = /* @__PURE__ */ new Set();
|
|
17615
|
+
// Queue for user messages injected during agent execution (REPL mid-session input)
|
|
17616
|
+
pendingUserMessages = [];
|
|
17617
|
+
// Execution Tree - first-class model for nested subagent support
|
|
17618
|
+
tree;
|
|
17619
|
+
parentNodeId;
|
|
17620
|
+
// StreamProcessor factory - encapsulates all pass-through StreamProcessor config
|
|
17621
|
+
streamProcessorFactory;
|
|
17622
|
+
// LLM call lifecycle helper (encapsulates prepareLLMCall, completeLLMCall, notifyLLMError)
|
|
17623
|
+
llmCallLifecycle;
|
|
17624
|
+
// MCP integration — populated only when mcpSpecs were provided.
|
|
17625
|
+
mcpSpecs;
|
|
17626
|
+
mcpLifecycle = null;
|
|
17627
|
+
mcpDiscoveredPrompts = [];
|
|
17628
|
+
/**
|
|
17629
|
+
* Creates a new Agent instance.
|
|
17630
|
+
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
17631
|
+
*/
|
|
17632
|
+
constructor(key, options) {
|
|
17633
|
+
if (!isValidAgentKey(key)) {
|
|
17634
|
+
throw new Error(
|
|
17635
|
+
"Agent cannot be instantiated directly. Use LLMist.createAgent() or new AgentBuilder() instead."
|
|
17636
|
+
);
|
|
17637
|
+
}
|
|
17638
|
+
this.client = options.client;
|
|
17639
|
+
this.model = resolveModel(options.model);
|
|
17640
|
+
this.maxIterations = options.maxIterations ?? 10;
|
|
17641
|
+
this.budget = options.budget;
|
|
17642
|
+
this.temperature = options.temperature;
|
|
17643
|
+
this.logger = options.logger ?? createLogger({ name: "llmist:agent" });
|
|
17644
|
+
this.registry = options.registry;
|
|
17645
|
+
this.prefixConfig = options.prefixConfig;
|
|
17646
|
+
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
17647
|
+
const outputLimitConfig = {
|
|
17648
|
+
enabled: options.outputLimitConfig?.enabled,
|
|
17649
|
+
limitPercent: options.outputLimitConfig?.limitPercent
|
|
17650
|
+
};
|
|
17651
|
+
this.outputLimitManager = new OutputLimitManager(
|
|
17652
|
+
this.client,
|
|
17653
|
+
this.model,
|
|
17654
|
+
outputLimitConfig,
|
|
17655
|
+
this.registry,
|
|
17656
|
+
this.logger
|
|
17657
|
+
);
|
|
17658
|
+
this.mediaStore = new MediaStore();
|
|
17659
|
+
this.hooks = this.outputLimitManager.getHooks(options.hooks);
|
|
17660
|
+
const baseBuilder = new LLMMessageBuilder(options.promptConfig);
|
|
17661
|
+
if (options.systemPrompt) {
|
|
17662
|
+
baseBuilder.addSystem(options.systemPrompt);
|
|
17663
|
+
}
|
|
17664
|
+
baseBuilder.addGadgets(this.registry.getAll(), {
|
|
17665
|
+
startPrefix: this.prefixConfig?.gadgetStartPrefix,
|
|
17666
|
+
endPrefix: this.prefixConfig?.gadgetEndPrefix,
|
|
17667
|
+
argPrefix: this.prefixConfig?.gadgetArgPrefix
|
|
17668
|
+
});
|
|
17669
|
+
const baseMessages = baseBuilder.build();
|
|
17670
|
+
const initialMessages = (options.initialMessages ?? []).map((message) => ({
|
|
17671
|
+
role: message.role,
|
|
17672
|
+
content: message.content
|
|
17673
|
+
}));
|
|
17674
|
+
this.conversation = new ConversationManager(baseMessages, initialMessages, {
|
|
17675
|
+
startPrefix: this.prefixConfig?.gadgetStartPrefix,
|
|
17676
|
+
endPrefix: this.prefixConfig?.gadgetEndPrefix,
|
|
17677
|
+
argPrefix: this.prefixConfig?.gadgetArgPrefix
|
|
17678
|
+
});
|
|
17679
|
+
this.hasUserPrompt = !!options.userPrompt;
|
|
17680
|
+
if (options.userPrompt) {
|
|
17681
|
+
this.conversation.addUserMessage(options.userPrompt);
|
|
17682
|
+
}
|
|
17683
|
+
this.conversationUpdater = new ConversationUpdater(
|
|
17684
|
+
this.conversation,
|
|
17685
|
+
options.textOnlyHandler ?? "terminate",
|
|
17686
|
+
options.textWithGadgetsHandler,
|
|
17687
|
+
this.logger
|
|
17688
|
+
);
|
|
17689
|
+
const compactionEnabled = options.compactionConfig?.enabled ?? true;
|
|
17690
|
+
if (compactionEnabled) {
|
|
17691
|
+
this.compactionManager = new CompactionManager(
|
|
17692
|
+
this.client,
|
|
17693
|
+
this.model,
|
|
17694
|
+
options.compactionConfig,
|
|
17695
|
+
this.logger
|
|
17696
|
+
);
|
|
17697
|
+
}
|
|
17698
|
+
this.signal = options.signal;
|
|
17699
|
+
this.mcpSpecs = options.mcpSpecs ?? [];
|
|
17700
|
+
this.reasoning = options.reasoning;
|
|
17701
|
+
this.caching = options.caching;
|
|
17702
|
+
this.retryConfig = options.sharedRetryConfig ?? resolveRetryConfig(options.retryConfig);
|
|
17703
|
+
if (options.sharedRateLimitTracker) {
|
|
17704
|
+
this.rateLimitTracker = options.sharedRateLimitTracker;
|
|
17705
|
+
} else {
|
|
17706
|
+
const rateLimitConfig = resolveRateLimitConfig(options.rateLimitConfig);
|
|
17707
|
+
if (rateLimitConfig.enabled) {
|
|
17708
|
+
this.rateLimitTracker = new RateLimitTracker(options.rateLimitConfig);
|
|
17709
|
+
}
|
|
17710
|
+
}
|
|
17711
|
+
const treeConfig = options.treeConfig;
|
|
17712
|
+
this.tree = treeConfig?.tree ?? new ExecutionTree();
|
|
17713
|
+
this.parentNodeId = treeConfig?.parentNodeId ?? null;
|
|
17714
|
+
this.streamProcessorFactory = new StreamProcessorFactory({
|
|
17715
|
+
registry: this.registry,
|
|
17716
|
+
prefixConfig: this.prefixConfig,
|
|
17717
|
+
hooks: this.hooks,
|
|
17718
|
+
logger: this.logger,
|
|
17719
|
+
requestHumanInput: options.requestHumanInput,
|
|
17720
|
+
defaultGadgetTimeoutMs: options.defaultGadgetTimeoutMs,
|
|
17721
|
+
gadgetExecutionMode: options.gadgetExecutionMode ?? "parallel",
|
|
17722
|
+
client: this.client,
|
|
17723
|
+
mediaStore: this.mediaStore,
|
|
17724
|
+
agentContextConfig: {
|
|
17725
|
+
model: this.model,
|
|
17726
|
+
temperature: this.temperature
|
|
17727
|
+
},
|
|
17728
|
+
subagentConfig: options.subagentConfig,
|
|
17729
|
+
tree: this.tree,
|
|
17730
|
+
baseDepth: treeConfig?.baseDepth ?? 0,
|
|
17731
|
+
parentObservers: treeConfig?.parentObservers,
|
|
17732
|
+
rateLimitTracker: this.rateLimitTracker,
|
|
17733
|
+
retryConfig: this.retryConfig,
|
|
17734
|
+
maxGadgetsPerResponse: options.maxGadgetsPerResponse ?? 0
|
|
17735
|
+
});
|
|
17736
|
+
this.llmCallLifecycle = new LLMCallLifecycle({
|
|
17737
|
+
client: this.client,
|
|
17738
|
+
conversation: this.conversation,
|
|
17739
|
+
tree: this.tree,
|
|
17740
|
+
hooks: this.hooks,
|
|
17741
|
+
logger: this.logger,
|
|
16793
17742
|
rateLimitTracker: this.rateLimitTracker,
|
|
16794
17743
|
signal: this.signal,
|
|
16795
17744
|
temperature: this.temperature,
|
|
@@ -17007,6 +17956,18 @@ var init_agent = __esm({
|
|
|
17007
17956
|
);
|
|
17008
17957
|
}
|
|
17009
17958
|
const unsubscribeBridge = bridgeTreeToHooks(this.tree, this.hooks, this.logger);
|
|
17959
|
+
if (this.mcpSpecs.length > 0) {
|
|
17960
|
+
const { setupMcpServers: setupMcpServers2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
|
|
17961
|
+
this.mcpLifecycle = await setupMcpServers2({
|
|
17962
|
+
specs: this.mcpSpecs,
|
|
17963
|
+
registry: this.registry,
|
|
17964
|
+
conversation: this.conversation,
|
|
17965
|
+
prefixConfig: this.prefixConfig,
|
|
17966
|
+
systemPrompt: this.conversation.getBaseMessages()[0]?.role === "system" ? this.conversation.getBaseMessages()[0].content : void 0,
|
|
17967
|
+
logger: this.logger,
|
|
17968
|
+
onPromptDiscovered: (skill) => this.mcpDiscoveredPrompts.push(skill)
|
|
17969
|
+
});
|
|
17970
|
+
}
|
|
17010
17971
|
let currentIteration = 0;
|
|
17011
17972
|
this.logger.info("Starting agent loop", {
|
|
17012
17973
|
model: this.model,
|
|
@@ -17206,6 +18167,14 @@ var init_agent = __esm({
|
|
|
17206
18167
|
}
|
|
17207
18168
|
}
|
|
17208
18169
|
unsubscribeBridge();
|
|
18170
|
+
if (this.mcpLifecycle) {
|
|
18171
|
+
try {
|
|
18172
|
+
await this.mcpLifecycle.closeAll();
|
|
18173
|
+
} catch (err) {
|
|
18174
|
+
this.logger.debug("MCP lifecycle teardown error (suppressed):", err);
|
|
18175
|
+
}
|
|
18176
|
+
this.mcpLifecycle = null;
|
|
18177
|
+
}
|
|
17209
18178
|
}
|
|
17210
18179
|
}
|
|
17211
18180
|
/**
|
|
@@ -17405,6 +18374,7 @@ __export(index_exports, {
|
|
|
17405
18374
|
ConversationManager: () => ConversationManager,
|
|
17406
18375
|
DEFAULT_COMPACTION_CONFIG: () => DEFAULT_COMPACTION_CONFIG,
|
|
17407
18376
|
DEFAULT_HINTS: () => DEFAULT_HINTS,
|
|
18377
|
+
DEFAULT_MCP_COMMAND_ALLOWLIST: () => DEFAULT_MCP_COMMAND_ALLOWLIST,
|
|
17408
18378
|
DEFAULT_PROMPTS: () => DEFAULT_PROMPTS,
|
|
17409
18379
|
DEFAULT_RATE_LIMIT_CONFIG: () => DEFAULT_RATE_LIMIT_CONFIG,
|
|
17410
18380
|
DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
|
|
@@ -17424,10 +18394,17 @@ __export(index_exports, {
|
|
|
17424
18394
|
HuggingFaceProvider: () => HuggingFaceProvider,
|
|
17425
18395
|
HumanInputRequiredException: () => HumanInputRequiredException,
|
|
17426
18396
|
HybridStrategy: () => HybridStrategy,
|
|
18397
|
+
JsonSchemaConversionError: () => JsonSchemaConversionError,
|
|
17427
18398
|
LLMMessageBuilder: () => LLMMessageBuilder,
|
|
17428
18399
|
LLMist: () => LLMist,
|
|
17429
18400
|
LOAD_SKILL_GADGET_NAME: () => LOAD_SKILL_GADGET_NAME,
|
|
17430
18401
|
MODEL_ALIASES: () => MODEL_ALIASES,
|
|
18402
|
+
McpClient: () => McpClient,
|
|
18403
|
+
McpConnectError: () => McpConnectError,
|
|
18404
|
+
McpError: () => McpError,
|
|
18405
|
+
McpLifecycle: () => McpLifecycle,
|
|
18406
|
+
McpToolCallError: () => McpToolCallError,
|
|
18407
|
+
McpUntrustedCommandError: () => McpUntrustedCommandError,
|
|
17431
18408
|
MediaStore: () => MediaStore,
|
|
17432
18409
|
ModelIdentifierParser: () => ModelIdentifierParser,
|
|
17433
18410
|
ModelRegistry: () => ModelRegistry,
|
|
@@ -17443,6 +18420,7 @@ __export(index_exports, {
|
|
|
17443
18420
|
SummarizationStrategy: () => SummarizationStrategy,
|
|
17444
18421
|
TaskCompletionSignal: () => TaskCompletionSignal,
|
|
17445
18422
|
TimeoutException: () => TimeoutException,
|
|
18423
|
+
assertCommandAllowed: () => assertCommandAllowed,
|
|
17446
18424
|
audioFromBase64: () => audioFromBase64,
|
|
17447
18425
|
audioFromBuffer: () => audioFromBuffer,
|
|
17448
18426
|
collectEvents: () => collectEvents,
|
|
@@ -17457,6 +18435,7 @@ __export(index_exports, {
|
|
|
17457
18435
|
createHuggingFaceProviderFromEnv: () => createHuggingFaceProviderFromEnv,
|
|
17458
18436
|
createLoadSkillGadget: () => createLoadSkillGadget,
|
|
17459
18437
|
createLogger: () => createLogger,
|
|
18438
|
+
createMcpServer: () => createMcpServer,
|
|
17460
18439
|
createMediaOutput: () => createMediaOutput,
|
|
17461
18440
|
createOpenAIProviderFromEnv: () => createOpenAIProviderFromEnv,
|
|
17462
18441
|
createOpenRouterProviderFromEnv: () => createOpenRouterProviderFromEnv,
|
|
@@ -17479,7 +18458,9 @@ __export(index_exports, {
|
|
|
17479
18458
|
formatLLMError: () => formatLLMError,
|
|
17480
18459
|
formatLlmRequest: () => formatLlmRequest,
|
|
17481
18460
|
gadgetError: () => gadgetError,
|
|
18461
|
+
gadgetResultToMcpContent: () => gadgetResultToMcpContent,
|
|
17482
18462
|
gadgetSuccess: () => gadgetSuccess,
|
|
18463
|
+
gadgetToMcpTool: () => gadgetToMcpTool,
|
|
17483
18464
|
getErrorMessage: () => getErrorMessage,
|
|
17484
18465
|
getHostExports: () => getHostExports2,
|
|
17485
18466
|
getModelId: () => getModelId,
|
|
@@ -17507,9 +18488,11 @@ __export(index_exports, {
|
|
|
17507
18488
|
isSubagentEvent: () => isSubagentEvent,
|
|
17508
18489
|
isTextPart: () => isTextPart,
|
|
17509
18490
|
iterationProgressHint: () => iterationProgressHint,
|
|
18491
|
+
jsonSchemaToZod: () => jsonSchemaToZod,
|
|
17510
18492
|
listPresets: () => listPresets,
|
|
17511
18493
|
listSubagents: () => listSubagents,
|
|
17512
18494
|
loadSkillsFromDirectory: () => loadSkillsFromDirectory,
|
|
18495
|
+
mcpToolToGadget: () => mcpToolToGadget,
|
|
17513
18496
|
normalizeMessageContent: () => normalizeMessageContent,
|
|
17514
18497
|
parallelGadgetHint: () => parallelGadgetHint,
|
|
17515
18498
|
parseDataUrl: () => parseDataUrl,
|
|
@@ -17520,6 +18503,7 @@ __export(index_exports, {
|
|
|
17520
18503
|
parseSkillContent: () => parseSkillContent,
|
|
17521
18504
|
parseSkillFile: () => parseSkillFile,
|
|
17522
18505
|
randomDelay: () => randomDelay,
|
|
18506
|
+
renderSkillForMcpPrompt: () => renderSkillForMcpPrompt,
|
|
17523
18507
|
resetFileLoggingState: () => resetFileLoggingState,
|
|
17524
18508
|
resolveConfig: () => resolveConfig,
|
|
17525
18509
|
resolveHintTemplate: () => resolveHintTemplate,
|
|
@@ -17537,9 +18521,11 @@ __export(index_exports, {
|
|
|
17537
18521
|
resultWithImage: () => resultWithImage,
|
|
17538
18522
|
resultWithImages: () => resultWithImages,
|
|
17539
18523
|
resultWithMedia: () => resultWithMedia,
|
|
18524
|
+
runGadgetForMcp: () => runGadgetForMcp,
|
|
17540
18525
|
runWithHandlers: () => runWithHandlers,
|
|
17541
18526
|
scanResources: () => scanResources,
|
|
17542
18527
|
schemaToJSONSchema: () => schemaToJSONSchema,
|
|
18528
|
+
skillToMcpPrompt: () => skillToMcpPrompt,
|
|
17543
18529
|
stream: () => stream,
|
|
17544
18530
|
stripProviderPrefix: () => stripProviderPrefix,
|
|
17545
18531
|
substituteArguments: () => substituteArguments,
|
|
@@ -17555,10 +18541,10 @@ __export(index_exports, {
|
|
|
17555
18541
|
withErrorHandling: () => withErrorHandling,
|
|
17556
18542
|
withRetry: () => withRetry,
|
|
17557
18543
|
withTimeout: () => withTimeout,
|
|
17558
|
-
z: () =>
|
|
18544
|
+
z: () => import_zod6.z
|
|
17559
18545
|
});
|
|
17560
18546
|
module.exports = __toCommonJS(index_exports);
|
|
17561
|
-
var
|
|
18547
|
+
var import_zod6 = require("zod");
|
|
17562
18548
|
init_agent();
|
|
17563
18549
|
init_builder();
|
|
17564
18550
|
init_event_handlers();
|
|
@@ -17741,140 +18727,254 @@ init_create_gadget();
|
|
|
17741
18727
|
init_exceptions();
|
|
17742
18728
|
init_executor();
|
|
17743
18729
|
init_gadget();
|
|
18730
|
+
init_helpers();
|
|
18731
|
+
init_output_viewer();
|
|
18732
|
+
init_parser2();
|
|
18733
|
+
init_registry();
|
|
18734
|
+
init_typed_gadget();
|
|
17744
18735
|
|
|
17745
|
-
// src/
|
|
17746
|
-
|
|
17747
|
-
|
|
17748
|
-
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
|
|
17754
|
-
|
|
17755
|
-
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
|
|
17759
|
-
|
|
17760
|
-
}
|
|
17761
|
-
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
const
|
|
18736
|
+
// src/mcp/index.ts
|
|
18737
|
+
init_allowlist();
|
|
18738
|
+
init_client2();
|
|
18739
|
+
init_errors();
|
|
18740
|
+
|
|
18741
|
+
// src/mcp/gadget-exporter.ts
|
|
18742
|
+
init_schema_to_json();
|
|
18743
|
+
|
|
18744
|
+
// src/gadgets/validation.ts
|
|
18745
|
+
function validateAndApplyDefaults(schema, params) {
|
|
18746
|
+
const result = schema.safeParse(params);
|
|
18747
|
+
if (result.success) {
|
|
18748
|
+
return {
|
|
18749
|
+
success: true,
|
|
18750
|
+
data: result.data
|
|
18751
|
+
};
|
|
18752
|
+
}
|
|
18753
|
+
const issues = result.error.issues.map((issue) => ({
|
|
18754
|
+
path: issue.path.join(".") || "root",
|
|
18755
|
+
message: issue.message
|
|
18756
|
+
}));
|
|
18757
|
+
const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
|
|
17767
18758
|
return {
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
description: options?.description,
|
|
17772
|
-
metadata: options?.metadata,
|
|
17773
|
-
fileName: options?.fileName
|
|
18759
|
+
success: false,
|
|
18760
|
+
error: formattedError,
|
|
18761
|
+
issues
|
|
17774
18762
|
};
|
|
17775
18763
|
}
|
|
17776
|
-
function
|
|
17777
|
-
if (
|
|
17778
|
-
|
|
18764
|
+
function validateGadgetParams(gadget, params) {
|
|
18765
|
+
if (!gadget.parameterSchema) {
|
|
18766
|
+
return {
|
|
18767
|
+
success: true,
|
|
18768
|
+
data: params
|
|
18769
|
+
};
|
|
17779
18770
|
}
|
|
17780
|
-
return
|
|
17781
|
-
result,
|
|
17782
|
-
media,
|
|
17783
|
-
cost
|
|
17784
|
-
};
|
|
18771
|
+
return validateAndApplyDefaults(gadget.parameterSchema, params);
|
|
17785
18772
|
}
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
);
|
|
18773
|
+
|
|
18774
|
+
// src/mcp/gadget-exporter.ts
|
|
18775
|
+
function gadgetToMcpTool(gadget) {
|
|
18776
|
+
const description = gadget.description && gadget.description.length > 0 ? gadget.description : `Native llmist gadget "${gadget.name ?? "unnamed"}"`;
|
|
18777
|
+
let inputSchema;
|
|
18778
|
+
if (gadget.parameterSchema) {
|
|
18779
|
+
inputSchema = schemaToJSONSchema(gadget.parameterSchema);
|
|
18780
|
+
} else {
|
|
18781
|
+
inputSchema = { type: "object", properties: {} };
|
|
17793
18782
|
}
|
|
17794
18783
|
return {
|
|
17795
|
-
|
|
17796
|
-
|
|
17797
|
-
|
|
17798
|
-
kind: "image",
|
|
17799
|
-
data: buffer.toString("base64"),
|
|
17800
|
-
mimeType,
|
|
17801
|
-
description: options?.description,
|
|
17802
|
-
metadata: options?.metadata,
|
|
17803
|
-
fileName: options?.fileName
|
|
17804
|
-
}
|
|
17805
|
-
],
|
|
17806
|
-
cost: options?.cost
|
|
18784
|
+
name: gadget.name ?? "unnamed-gadget",
|
|
18785
|
+
description,
|
|
18786
|
+
inputSchema
|
|
17807
18787
|
};
|
|
17808
18788
|
}
|
|
17809
|
-
function
|
|
17810
|
-
if (
|
|
17811
|
-
|
|
18789
|
+
function gadgetResultToMcpContent(ret) {
|
|
18790
|
+
if (typeof ret === "string") {
|
|
18791
|
+
return [{ type: "text", text: ret }];
|
|
18792
|
+
}
|
|
18793
|
+
if (ret && typeof ret === "object" && "result" in ret) {
|
|
18794
|
+
const r = ret;
|
|
18795
|
+
const blocks = [];
|
|
18796
|
+
if (typeof r.result === "string") {
|
|
18797
|
+
blocks.push({ type: "text", text: r.result });
|
|
18798
|
+
} else {
|
|
18799
|
+
blocks.push({ type: "text", text: JSON.stringify(r.result) });
|
|
18800
|
+
}
|
|
18801
|
+
if (r.media) {
|
|
18802
|
+
for (const m of r.media) {
|
|
18803
|
+
if (m.kind === "image" || m.kind === "audio") {
|
|
18804
|
+
blocks.push({
|
|
18805
|
+
type: m.kind,
|
|
18806
|
+
data: m.data,
|
|
18807
|
+
mimeType: m.mimeType
|
|
18808
|
+
});
|
|
18809
|
+
}
|
|
18810
|
+
}
|
|
18811
|
+
}
|
|
18812
|
+
return blocks;
|
|
17812
18813
|
}
|
|
17813
|
-
|
|
17814
|
-
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17819
|
-
|
|
18814
|
+
return [{ type: "text", text: JSON.stringify(ret) }];
|
|
18815
|
+
}
|
|
18816
|
+
async function runGadgetForMcp(gadget, rawParams) {
|
|
18817
|
+
if (gadget.parameterSchema) {
|
|
18818
|
+
const validation = validateAndApplyDefaults(
|
|
18819
|
+
gadget.parameterSchema,
|
|
18820
|
+
rawParams ?? {}
|
|
18821
|
+
);
|
|
18822
|
+
if (!validation.success) {
|
|
18823
|
+
return {
|
|
18824
|
+
isError: true,
|
|
18825
|
+
content: [
|
|
18826
|
+
{
|
|
18827
|
+
type: "text",
|
|
18828
|
+
text: `Invalid arguments for gadget "${gadget.name}": ${validation.error}`
|
|
18829
|
+
}
|
|
18830
|
+
]
|
|
18831
|
+
};
|
|
17820
18832
|
}
|
|
18833
|
+
rawParams = validation.data;
|
|
18834
|
+
}
|
|
18835
|
+
try {
|
|
18836
|
+
const result = await gadget.execute(rawParams);
|
|
17821
18837
|
return {
|
|
17822
|
-
|
|
17823
|
-
data: buffer.toString("base64"),
|
|
17824
|
-
mimeType,
|
|
17825
|
-
description: img.description,
|
|
17826
|
-
metadata: img.metadata,
|
|
17827
|
-
fileName: img.fileName
|
|
18838
|
+
content: gadgetResultToMcpContent(result)
|
|
17828
18839
|
};
|
|
17829
|
-
})
|
|
17830
|
-
|
|
18840
|
+
} catch (err) {
|
|
18841
|
+
return {
|
|
18842
|
+
isError: true,
|
|
18843
|
+
content: [
|
|
18844
|
+
{
|
|
18845
|
+
type: "text",
|
|
18846
|
+
text: `Gadget "${gadget.name}" failed: ${err.message}`
|
|
18847
|
+
}
|
|
18848
|
+
]
|
|
18849
|
+
};
|
|
18850
|
+
}
|
|
17831
18851
|
}
|
|
17832
|
-
|
|
17833
|
-
|
|
17834
|
-
|
|
17835
|
-
|
|
17836
|
-
|
|
17837
|
-
|
|
17838
|
-
|
|
18852
|
+
|
|
18853
|
+
// src/mcp/index.ts
|
|
18854
|
+
init_json_schema_to_zod();
|
|
18855
|
+
init_lifecycle();
|
|
18856
|
+
|
|
18857
|
+
// src/mcp/skill-exporter.ts
|
|
18858
|
+
function skillToMcpPrompt(skill) {
|
|
18859
|
+
const description = skill.description && skill.description.length > 0 ? skill.description : `Native llmist skill "${skill.name}"`;
|
|
18860
|
+
const args = [];
|
|
18861
|
+
if (skill.metadata.argumentHint) {
|
|
18862
|
+
args.push({
|
|
18863
|
+
name: "arguments",
|
|
18864
|
+
description: skill.metadata.argumentHint,
|
|
18865
|
+
required: false
|
|
18866
|
+
});
|
|
17839
18867
|
}
|
|
17840
|
-
const metadata = options?.durationMs ? { durationMs: options.durationMs } : void 0;
|
|
17841
18868
|
return {
|
|
17842
|
-
|
|
17843
|
-
|
|
18869
|
+
name: skill.name,
|
|
18870
|
+
description,
|
|
18871
|
+
...args.length > 0 ? { arguments: args } : {}
|
|
18872
|
+
};
|
|
18873
|
+
}
|
|
18874
|
+
async function renderSkillForMcpPrompt(skill, args) {
|
|
18875
|
+
const argString = typeof args.arguments === "string" ? args.arguments : Object.values(args).filter((v) => typeof v === "string").join(" ");
|
|
18876
|
+
const activation = await skill.activate({
|
|
18877
|
+
arguments: argString || void 0
|
|
18878
|
+
});
|
|
18879
|
+
return {
|
|
18880
|
+
description: skill.description,
|
|
18881
|
+
messages: [
|
|
17844
18882
|
{
|
|
17845
|
-
|
|
17846
|
-
|
|
17847
|
-
mimeType,
|
|
17848
|
-
description: options?.description,
|
|
17849
|
-
metadata,
|
|
17850
|
-
fileName: options?.fileName
|
|
18883
|
+
role: "user",
|
|
18884
|
+
content: { type: "text", text: activation.resolvedInstructions }
|
|
17851
18885
|
}
|
|
17852
|
-
]
|
|
17853
|
-
cost: options?.cost
|
|
18886
|
+
]
|
|
17854
18887
|
};
|
|
17855
18888
|
}
|
|
17856
|
-
|
|
17857
|
-
|
|
18889
|
+
|
|
18890
|
+
// src/mcp/server.ts
|
|
18891
|
+
var DEFAULT_SERVER_INFO = { name: "llmist", version: "0.0.0" };
|
|
18892
|
+
function createMcpServer(opts) {
|
|
18893
|
+
const { gadgets, skills } = opts;
|
|
18894
|
+
const hasTools = gadgets.getAll().length > 0;
|
|
18895
|
+
const hasPrompts = !!skills && skills.size > 0;
|
|
18896
|
+
const capabilities = {};
|
|
18897
|
+
if (hasTools) capabilities.tools = {};
|
|
18898
|
+
if (hasPrompts) capabilities.prompts = {};
|
|
18899
|
+
let sdkServer = null;
|
|
18900
|
+
let running = false;
|
|
18901
|
+
async function ensureServer() {
|
|
18902
|
+
if (sdkServer) return sdkServer;
|
|
18903
|
+
const [serverMod, typesMod] = await Promise.all([
|
|
18904
|
+
import("@modelcontextprotocol/sdk/server/index.js"),
|
|
18905
|
+
import("@modelcontextprotocol/sdk/types.js")
|
|
18906
|
+
]);
|
|
18907
|
+
const ServerClass = serverMod.Server;
|
|
18908
|
+
const server = new ServerClass(opts.serverInfo ?? DEFAULT_SERVER_INFO, {
|
|
18909
|
+
capabilities
|
|
18910
|
+
});
|
|
18911
|
+
if (hasTools) {
|
|
18912
|
+
server.setRequestHandler(typesMod.ListToolsRequestSchema, async () => ({
|
|
18913
|
+
tools: gadgets.getAll().map(gadgetToMcpTool)
|
|
18914
|
+
}));
|
|
18915
|
+
server.setRequestHandler(
|
|
18916
|
+
typesMod.CallToolRequestSchema,
|
|
18917
|
+
async (req) => {
|
|
18918
|
+
const gadget = gadgets.get(req.params.name);
|
|
18919
|
+
if (!gadget) {
|
|
18920
|
+
return {
|
|
18921
|
+
isError: true,
|
|
18922
|
+
content: [
|
|
18923
|
+
{
|
|
18924
|
+
type: "text",
|
|
18925
|
+
text: `Unknown tool "${req.params.name}". Call tools/list first.`
|
|
18926
|
+
}
|
|
18927
|
+
]
|
|
18928
|
+
};
|
|
18929
|
+
}
|
|
18930
|
+
return runGadgetForMcp(gadget, req.params.arguments ?? {});
|
|
18931
|
+
}
|
|
18932
|
+
);
|
|
18933
|
+
}
|
|
18934
|
+
if (hasPrompts && skills) {
|
|
18935
|
+
server.setRequestHandler(typesMod.ListPromptsRequestSchema, async () => ({
|
|
18936
|
+
prompts: Array.from(skills.getAll()).map(skillToMcpPrompt)
|
|
18937
|
+
}));
|
|
18938
|
+
server.setRequestHandler(
|
|
18939
|
+
typesMod.GetPromptRequestSchema,
|
|
18940
|
+
async (req) => {
|
|
18941
|
+
const skill = skills.get(req.params.name);
|
|
18942
|
+
if (!skill) {
|
|
18943
|
+
throw new Error(`Unknown prompt "${req.params.name}"`);
|
|
18944
|
+
}
|
|
18945
|
+
const result = await renderSkillForMcpPrompt(skill, req.params.arguments ?? {});
|
|
18946
|
+
return result;
|
|
18947
|
+
}
|
|
18948
|
+
);
|
|
18949
|
+
}
|
|
18950
|
+
sdkServer = server;
|
|
18951
|
+
return server;
|
|
18952
|
+
}
|
|
17858
18953
|
return {
|
|
17859
|
-
|
|
17860
|
-
|
|
17861
|
-
|
|
17862
|
-
|
|
17863
|
-
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
18954
|
+
get running() {
|
|
18955
|
+
return running;
|
|
18956
|
+
},
|
|
18957
|
+
async connect(transport) {
|
|
18958
|
+
const server = await ensureServer();
|
|
18959
|
+
await server.connect(transport);
|
|
18960
|
+
running = true;
|
|
18961
|
+
},
|
|
18962
|
+
async stop() {
|
|
18963
|
+
if (!sdkServer) return;
|
|
18964
|
+
try {
|
|
18965
|
+
await sdkServer.close();
|
|
18966
|
+
} catch {
|
|
17867
18967
|
}
|
|
17868
|
-
|
|
17869
|
-
|
|
18968
|
+
sdkServer = null;
|
|
18969
|
+
running = false;
|
|
18970
|
+
}
|
|
17870
18971
|
};
|
|
17871
18972
|
}
|
|
17872
18973
|
|
|
18974
|
+
// src/mcp/index.ts
|
|
18975
|
+
init_tool_adapter();
|
|
18976
|
+
|
|
17873
18977
|
// src/index.ts
|
|
17874
|
-
init_output_viewer();
|
|
17875
|
-
init_parser2();
|
|
17876
|
-
init_registry();
|
|
17877
|
-
init_typed_gadget();
|
|
17878
18978
|
init_constants2();
|
|
17879
18979
|
|
|
17880
18980
|
// src/utils/config-resolver.ts
|
|
@@ -17983,38 +19083,6 @@ function hasHostExports(ctx) {
|
|
|
17983
19083
|
init_media_store();
|
|
17984
19084
|
init_schema_to_json();
|
|
17985
19085
|
init_schema_validator();
|
|
17986
|
-
|
|
17987
|
-
// src/gadgets/validation.ts
|
|
17988
|
-
function validateAndApplyDefaults(schema, params) {
|
|
17989
|
-
const result = schema.safeParse(params);
|
|
17990
|
-
if (result.success) {
|
|
17991
|
-
return {
|
|
17992
|
-
success: true,
|
|
17993
|
-
data: result.data
|
|
17994
|
-
};
|
|
17995
|
-
}
|
|
17996
|
-
const issues = result.error.issues.map((issue) => ({
|
|
17997
|
-
path: issue.path.join(".") || "root",
|
|
17998
|
-
message: issue.message
|
|
17999
|
-
}));
|
|
18000
|
-
const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
|
|
18001
|
-
return {
|
|
18002
|
-
success: false,
|
|
18003
|
-
error: formattedError,
|
|
18004
|
-
issues
|
|
18005
|
-
};
|
|
18006
|
-
}
|
|
18007
|
-
function validateGadgetParams(gadget, params) {
|
|
18008
|
-
if (!gadget.parameterSchema) {
|
|
18009
|
-
return {
|
|
18010
|
-
success: true,
|
|
18011
|
-
data: params
|
|
18012
|
-
};
|
|
18013
|
-
}
|
|
18014
|
-
return validateAndApplyDefaults(gadget.parameterSchema, params);
|
|
18015
|
-
}
|
|
18016
|
-
|
|
18017
|
-
// src/index.ts
|
|
18018
19086
|
init_logger();
|
|
18019
19087
|
|
|
18020
19088
|
// src/package/manifest.ts
|
|
@@ -18313,6 +19381,7 @@ function getHostExports2(ctx) {
|
|
|
18313
19381
|
ConversationManager,
|
|
18314
19382
|
DEFAULT_COMPACTION_CONFIG,
|
|
18315
19383
|
DEFAULT_HINTS,
|
|
19384
|
+
DEFAULT_MCP_COMMAND_ALLOWLIST,
|
|
18316
19385
|
DEFAULT_PROMPTS,
|
|
18317
19386
|
DEFAULT_RATE_LIMIT_CONFIG,
|
|
18318
19387
|
DEFAULT_RETRY_CONFIG,
|
|
@@ -18332,10 +19401,17 @@ function getHostExports2(ctx) {
|
|
|
18332
19401
|
HuggingFaceProvider,
|
|
18333
19402
|
HumanInputRequiredException,
|
|
18334
19403
|
HybridStrategy,
|
|
19404
|
+
JsonSchemaConversionError,
|
|
18335
19405
|
LLMMessageBuilder,
|
|
18336
19406
|
LLMist,
|
|
18337
19407
|
LOAD_SKILL_GADGET_NAME,
|
|
18338
19408
|
MODEL_ALIASES,
|
|
19409
|
+
McpClient,
|
|
19410
|
+
McpConnectError,
|
|
19411
|
+
McpError,
|
|
19412
|
+
McpLifecycle,
|
|
19413
|
+
McpToolCallError,
|
|
19414
|
+
McpUntrustedCommandError,
|
|
18339
19415
|
MediaStore,
|
|
18340
19416
|
ModelIdentifierParser,
|
|
18341
19417
|
ModelRegistry,
|
|
@@ -18351,6 +19427,7 @@ function getHostExports2(ctx) {
|
|
|
18351
19427
|
SummarizationStrategy,
|
|
18352
19428
|
TaskCompletionSignal,
|
|
18353
19429
|
TimeoutException,
|
|
19430
|
+
assertCommandAllowed,
|
|
18354
19431
|
audioFromBase64,
|
|
18355
19432
|
audioFromBuffer,
|
|
18356
19433
|
collectEvents,
|
|
@@ -18365,6 +19442,7 @@ function getHostExports2(ctx) {
|
|
|
18365
19442
|
createHuggingFaceProviderFromEnv,
|
|
18366
19443
|
createLoadSkillGadget,
|
|
18367
19444
|
createLogger,
|
|
19445
|
+
createMcpServer,
|
|
18368
19446
|
createMediaOutput,
|
|
18369
19447
|
createOpenAIProviderFromEnv,
|
|
18370
19448
|
createOpenRouterProviderFromEnv,
|
|
@@ -18387,7 +19465,9 @@ function getHostExports2(ctx) {
|
|
|
18387
19465
|
formatLLMError,
|
|
18388
19466
|
formatLlmRequest,
|
|
18389
19467
|
gadgetError,
|
|
19468
|
+
gadgetResultToMcpContent,
|
|
18390
19469
|
gadgetSuccess,
|
|
19470
|
+
gadgetToMcpTool,
|
|
18391
19471
|
getErrorMessage,
|
|
18392
19472
|
getHostExports,
|
|
18393
19473
|
getModelId,
|
|
@@ -18415,9 +19495,11 @@ function getHostExports2(ctx) {
|
|
|
18415
19495
|
isSubagentEvent,
|
|
18416
19496
|
isTextPart,
|
|
18417
19497
|
iterationProgressHint,
|
|
19498
|
+
jsonSchemaToZod,
|
|
18418
19499
|
listPresets,
|
|
18419
19500
|
listSubagents,
|
|
18420
19501
|
loadSkillsFromDirectory,
|
|
19502
|
+
mcpToolToGadget,
|
|
18421
19503
|
normalizeMessageContent,
|
|
18422
19504
|
parallelGadgetHint,
|
|
18423
19505
|
parseDataUrl,
|
|
@@ -18428,6 +19510,7 @@ function getHostExports2(ctx) {
|
|
|
18428
19510
|
parseSkillContent,
|
|
18429
19511
|
parseSkillFile,
|
|
18430
19512
|
randomDelay,
|
|
19513
|
+
renderSkillForMcpPrompt,
|
|
18431
19514
|
resetFileLoggingState,
|
|
18432
19515
|
resolveConfig,
|
|
18433
19516
|
resolveHintTemplate,
|
|
@@ -18445,9 +19528,11 @@ function getHostExports2(ctx) {
|
|
|
18445
19528
|
resultWithImage,
|
|
18446
19529
|
resultWithImages,
|
|
18447
19530
|
resultWithMedia,
|
|
19531
|
+
runGadgetForMcp,
|
|
18448
19532
|
runWithHandlers,
|
|
18449
19533
|
scanResources,
|
|
18450
19534
|
schemaToJSONSchema,
|
|
19535
|
+
skillToMcpPrompt,
|
|
18451
19536
|
stream,
|
|
18452
19537
|
stripProviderPrefix,
|
|
18453
19538
|
substituteArguments,
|