kagent-ts 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/agent.d.ts +53 -10
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +45 -12
- package/dist/core/agent.js.map +1 -1
- package/dist/core/plan-solve-agent.d.ts.map +1 -1
- package/dist/core/plan-solve-agent.js +2 -0
- package/dist/core/plan-solve-agent.js.map +1 -1
- package/dist/core/react-agent.d.ts.map +1 -1
- package/dist/core/react-agent.js +2 -0
- package/dist/core/react-agent.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcp-client-manager.d.ts +64 -0
- package/dist/mcp/mcp-client-manager.d.ts.map +1 -0
- package/dist/mcp/mcp-client-manager.js +224 -0
- package/dist/mcp/mcp-client-manager.js.map +1 -0
- package/dist/mcp/mcp-types.d.ts +58 -0
- package/dist/mcp/mcp-types.d.ts.map +1 -0
- package/dist/mcp/mcp-types.js +20 -0
- package/dist/mcp/mcp-types.js.map +1 -0
- package/dist/skills/file-skill-loader.d.ts +5 -14
- package/dist/skills/file-skill-loader.d.ts.map +1 -1
- package/dist/skills/file-skill-loader.js +28 -146
- package/dist/skills/file-skill-loader.js.map +1 -1
- package/dist/skills/skill-manager.d.ts +6 -23
- package/dist/skills/skill-manager.d.ts.map +1 -1
- package/dist/skills/skill-manager.js +9 -53
- package/dist/skills/skill-manager.js.map +1 -1
- package/dist/skills/types.d.ts +1 -8
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/trace/trace-logger.js +1 -1
- package/dist/trace/trace-logger.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.McpClientManager = void 0;
|
|
4
|
+
const mcp_types_1 = require("./mcp-types");
|
|
5
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
6
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
7
|
+
const sse_js_1 = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
8
|
+
// ─── Config resolver ────────────────────────────────────────────────────────
|
|
9
|
+
function resolveTransportConfig(config) {
|
|
10
|
+
if (config.command && config.url) {
|
|
11
|
+
throw new mcp_types_1.McpConnectionError("MCP server config must specify either 'command' (stdio) or 'url' (SSE), not both.");
|
|
12
|
+
}
|
|
13
|
+
if (config.command) {
|
|
14
|
+
return { type: "stdio", command: config.command, args: config.args, env: config.env };
|
|
15
|
+
}
|
|
16
|
+
if (config.url) {
|
|
17
|
+
return { type: "sse", url: config.url };
|
|
18
|
+
}
|
|
19
|
+
throw new mcp_types_1.McpConnectionError("MCP server config must specify either 'command' (stdio) or 'url' (SSE).");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Manages connections to MCP (Model Context Protocol) servers.
|
|
23
|
+
*
|
|
24
|
+
* Lifecycle:
|
|
25
|
+
* 1. `connectToServer()` / `connectAll()` — connect, discover tools,
|
|
26
|
+
* register adapted tools in ToolRegistry
|
|
27
|
+
* 2. Tools execute via the adapted `Tool` wrapper (delegates to MCP client)
|
|
28
|
+
* 3. `disconnect()` / `disconnectAll()` — unregister tools, close connections
|
|
29
|
+
*/
|
|
30
|
+
class McpClientManager {
|
|
31
|
+
/** Active connections keyed by server name. */
|
|
32
|
+
connections = new Map();
|
|
33
|
+
/** The ToolRegistry where adapted tools are registered. */
|
|
34
|
+
toolRegistry;
|
|
35
|
+
constructor(toolRegistry) {
|
|
36
|
+
this.toolRegistry = toolRegistry;
|
|
37
|
+
}
|
|
38
|
+
// ─── Connection Management ────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* Connect to a single MCP server, discover its tools, and register them
|
|
41
|
+
* in the ToolRegistry with a `{serverName}_` prefix.
|
|
42
|
+
*
|
|
43
|
+
* @throws McpConnectionError if the config is invalid or the
|
|
44
|
+
* connection/tool-discovery fails.
|
|
45
|
+
*/
|
|
46
|
+
async connectToServer(serverName, config) {
|
|
47
|
+
if (this.connections.has(serverName)) {
|
|
48
|
+
throw new mcp_types_1.McpConnectionError(`MCP server "${serverName}" is already connected.`);
|
|
49
|
+
}
|
|
50
|
+
const transportConfig = resolveTransportConfig(config);
|
|
51
|
+
// Build transport
|
|
52
|
+
let transport;
|
|
53
|
+
if (transportConfig.type === "stdio") {
|
|
54
|
+
transport = new stdio_js_1.StdioClientTransport({
|
|
55
|
+
command: transportConfig.command,
|
|
56
|
+
args: transportConfig.args,
|
|
57
|
+
env: transportConfig.env,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
transport = new sse_js_1.SSEClientTransport(new URL(transportConfig.url));
|
|
62
|
+
}
|
|
63
|
+
// Create client and connect
|
|
64
|
+
const client = new index_js_1.Client({ name: "kagent-ts", version: "0.1.3" }, { capabilities: {} });
|
|
65
|
+
try {
|
|
66
|
+
await client.connect(transport);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
throw new mcp_types_1.McpConnectionError(`Failed to connect to MCP server "${serverName}": ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
+
}
|
|
71
|
+
// Discover tools
|
|
72
|
+
let mcpToolDescriptors;
|
|
73
|
+
try {
|
|
74
|
+
const result = await client.listTools();
|
|
75
|
+
mcpToolDescriptors = result.tools;
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
await client.close().catch(() => { });
|
|
79
|
+
throw new mcp_types_1.McpConnectionError(`Failed to list tools from MCP server "${serverName}": ${err instanceof Error ? err.message : String(err)}`);
|
|
80
|
+
}
|
|
81
|
+
// Adapt and register tools
|
|
82
|
+
const adaptedTools = [];
|
|
83
|
+
for (const mcpTool of mcpToolDescriptors) {
|
|
84
|
+
const adapted = this.adaptTool(serverName, mcpTool, client);
|
|
85
|
+
if (this.toolRegistry.has(adapted.name)) {
|
|
86
|
+
console.warn(`[MCP] Tool "${adapted.name}" is already registered. ` +
|
|
87
|
+
`Skipping MCP tool "${serverName}/${mcpTool.name}".`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
adaptedTools.push(adapted);
|
|
91
|
+
}
|
|
92
|
+
if (adaptedTools.length === 0) {
|
|
93
|
+
console.warn(`[MCP] Server "${serverName}" exposed no usable tools.`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.toolRegistry.registerMany(adaptedTools);
|
|
97
|
+
console.log(`[MCP] Registered ${adaptedTools.length} tool(s) from server "${serverName}".`);
|
|
98
|
+
}
|
|
99
|
+
this.connections.set(serverName, { serverName, client, tools: adaptedTools });
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Connect to multiple MCP servers. Failures for individual servers are
|
|
103
|
+
* collected and logged; other servers still connect.
|
|
104
|
+
*
|
|
105
|
+
* @returns An array of connection error reports (empty if all succeeded).
|
|
106
|
+
*/
|
|
107
|
+
async connectAll(servers) {
|
|
108
|
+
const errors = [];
|
|
109
|
+
await Promise.allSettled(Object.entries(servers).map(async ([name, config]) => {
|
|
110
|
+
try {
|
|
111
|
+
await this.connectToServer(name, config);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
115
|
+
errors.push({ serverName: name, error: message });
|
|
116
|
+
console.error(`[MCP] Failed to connect to server "${name}": ${message}`);
|
|
117
|
+
}
|
|
118
|
+
}));
|
|
119
|
+
return errors;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Disconnect from a single server and unregister its tools.
|
|
123
|
+
*/
|
|
124
|
+
async disconnect(serverName) {
|
|
125
|
+
const conn = this.connections.get(serverName);
|
|
126
|
+
if (!conn)
|
|
127
|
+
return;
|
|
128
|
+
// Unregister tools
|
|
129
|
+
for (const tool of conn.tools) {
|
|
130
|
+
this.toolRegistry.remove(tool.name);
|
|
131
|
+
}
|
|
132
|
+
// Close the client
|
|
133
|
+
try {
|
|
134
|
+
await conn.client.close();
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.warn(`[MCP] Error while closing connection to "${serverName}": ${err instanceof Error ? err.message : String(err)}`);
|
|
138
|
+
}
|
|
139
|
+
this.connections.delete(serverName);
|
|
140
|
+
console.log(`[MCP] Disconnected from server "${serverName}".`);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Disconnect from all MCP servers and unregister all MCP-provided tools.
|
|
144
|
+
*/
|
|
145
|
+
async disconnectAll() {
|
|
146
|
+
const names = Array.from(this.connections.keys());
|
|
147
|
+
await Promise.allSettled(names.map((name) => this.disconnect(name)));
|
|
148
|
+
}
|
|
149
|
+
// ─── Queries ──────────────────────────────────────────────────────────────
|
|
150
|
+
/**
|
|
151
|
+
* Get connection status for all servers.
|
|
152
|
+
*/
|
|
153
|
+
getStatus() {
|
|
154
|
+
return Array.from(this.connections.entries()).map(([name, conn]) => ({
|
|
155
|
+
serverName: name,
|
|
156
|
+
connected: true,
|
|
157
|
+
toolCount: conn.tools.length,
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Whether at least one server is connected.
|
|
162
|
+
*/
|
|
163
|
+
get isConnected() {
|
|
164
|
+
return this.connections.size > 0;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get the number of connected servers.
|
|
168
|
+
*/
|
|
169
|
+
get connectedCount() {
|
|
170
|
+
return this.connections.size;
|
|
171
|
+
}
|
|
172
|
+
// ─── Tool Adapter ─────────────────────────────────────────────────────────
|
|
173
|
+
/**
|
|
174
|
+
* Adapt an MCP SDK tool descriptor into the framework's Tool interface.
|
|
175
|
+
*
|
|
176
|
+
* The adapted tool's name is `{serverName}_{mcpToolName}` to avoid
|
|
177
|
+
* collisions between servers and with locally-registered tools.
|
|
178
|
+
*
|
|
179
|
+
* The `execute` function closes over the client instance and handles
|
|
180
|
+
* result content parsing (extracting text from MCP's Content[] array).
|
|
181
|
+
*/
|
|
182
|
+
adaptTool(serverName, mcpTool, client) {
|
|
183
|
+
const prefixedName = `${serverName}_${mcpTool.name}`;
|
|
184
|
+
return {
|
|
185
|
+
name: prefixedName,
|
|
186
|
+
description: mcpTool.description ?? "",
|
|
187
|
+
parameters: (mcpTool.inputSchema ?? { type: "object", properties: {} }),
|
|
188
|
+
execute: async (args) => {
|
|
189
|
+
// Check connection is still alive
|
|
190
|
+
if (!this.connections.has(serverName)) {
|
|
191
|
+
return `Error: MCP server "${serverName}" is no longer connected. Tool "${prefixedName}" is unavailable.`;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const result = await client.callTool({
|
|
195
|
+
name: mcpTool.name,
|
|
196
|
+
arguments: args,
|
|
197
|
+
});
|
|
198
|
+
// MCP results carry an array of content items (text, image, etc.)
|
|
199
|
+
let output = "(MCP tool returned no content)";
|
|
200
|
+
if (result.content && Array.isArray(result.content)) {
|
|
201
|
+
const textParts = result.content
|
|
202
|
+
.map((c) => (c.text ? c.text : JSON.stringify(c)))
|
|
203
|
+
.filter(Boolean);
|
|
204
|
+
if (textParts.length > 0) {
|
|
205
|
+
output = textParts.join("\n");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Prefix with "Error:" so the framework's circuit-breaker /
|
|
209
|
+
// retry-guidance path handles it correctly.
|
|
210
|
+
if (result.isError) {
|
|
211
|
+
return `Error: ${output}`;
|
|
212
|
+
}
|
|
213
|
+
return output;
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
217
|
+
return `Error executing MCP tool "${serverName}/${mcpTool.name}": ${message}`;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
exports.McpClientManager = McpClientManager;
|
|
224
|
+
//# sourceMappingURL=mcp-client-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-client-manager.js","sourceRoot":"","sources":["../../src/mcp/mcp-client-manager.ts"],"names":[],"mappings":";;;AAEA,2CAKqB;AAErB,wEAAmE;AACnE,wEAAiF;AACjF,oEAA6E;AAE7E,+EAA+E;AAE/E,SAAS,sBAAsB,CAAC,MAAuB;IAGrD,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;QACjC,MAAM,IAAI,8BAAkB,CAC1B,mFAAmF,CACpF,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;IACxF,CAAC;IACD,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;IAC1C,CAAC;IACD,MAAM,IAAI,8BAAkB,CAC1B,yEAAyE,CAC1E,CAAC;AACJ,CAAC;AAUD;;;;;;;;GAQG;AACH,MAAa,gBAAgB;IAC3B,+CAA+C;IACvC,WAAW,GAAqC,IAAI,GAAG,EAAE,CAAC;IAElE,2DAA2D;IACnD,YAAY,CAAe;IAEnC,YAAY,YAA0B;QACpC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED,6EAA6E;IAE7E;;;;;;OAMG;IACH,KAAK,CAAC,eAAe,CAAC,UAAkB,EAAE,MAAuB;QAC/D,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,8BAAkB,CAAC,eAAe,UAAU,yBAAyB,CAAC,CAAC;QACnF,CAAC;QAED,MAAM,eAAe,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAEvD,kBAAkB;QAClB,IAAI,SAAoD,CAAC;QACzD,IAAI,eAAe,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACrC,SAAS,GAAG,IAAI,+BAAoB,CAAC;gBACnC,OAAO,EAAE,eAAe,CAAC,OAAO;gBAChC,IAAI,EAAE,eAAe,CAAC,IAAI;gBAC1B,GAAG,EAAE,eAAe,CAAC,GAAG;aACzB,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,SAAS,GAAG,IAAI,2BAAkB,CAAC,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;QACnE,CAAC;QAED,4BAA4B;QAC5B,MAAM,MAAM,GAAG,IAAI,iBAAM,CACvB,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,EACvC,EAAE,YAAY,EAAE,EAAE,EAAE,CACrB,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,8BAAkB,CAC1B,oCAAoC,UAAU,MAC5C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACJ,CAAC;QAED,iBAAiB;QACjB,IAAI,kBAAuC,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;YACxC,kBAAkB,GAAG,MAAM,CAAC,KAA4B,CAAC;QAC3D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACrC,MAAM,IAAI,8BAAkB,CAC1B,yCAAyC,UAAU,MACjD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACJ,CAAC;QAED,2BAA2B;QAC3B,MAAM,YAAY,GAAW,EAAE,CAAC;QAChC,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5D,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxC,OAAO,CAAC,IAAI,CACV,eAAe,OAAO,CAAC,IAAI,2BAA2B;oBACtD,sBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,IAAI,CACrD,CAAC;gBACF,SAAS;YACX,CAAC;YACD,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,iBAAiB,UAAU,4BAA4B,CAAC,CAAC;QACxE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,oBAAoB,YAAY,CAAC,MAAM,yBAAyB,UAAU,IAAI,CAAC,CAAC;QAC9F,CAAC;QAED,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IAChF,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU,CAAC,OAAwC;QACvD,MAAM,MAAM,GAA+B,EAAE,CAAC;QAE9C,MAAM,OAAO,CAAC,UAAU,CACtB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE;YACnD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC3C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,MAAM,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;gBAClD,OAAO,CAAC,KAAK,CAAC,sCAAsC,IAAI,MAAM,OAAO,EAAE,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC,CAAC,CACH,CAAC;QAEF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,UAAkB;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,mBAAmB;QACnB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QAED,mBAAmB;QACnB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CACV,4CAA4C,UAAU,MACpD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,mCAAmC,UAAU,IAAI,CAAC,CAAC;IACjE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa;QACjB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,6EAA6E;IAE7E;;OAEG;IACH,SAAS;QACP,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACnE,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM;SAC7B,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IAC/B,CAAC;IAED,6EAA6E;IAE7E;;;;;;;;OAQG;IACK,SAAS,CACf,UAAkB,EAClB,OAA0B,EAC1B,MAAc;QAEd,MAAM,YAAY,GAAG,GAAG,UAAU,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QAErD,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;YACtC,UAAU,EAAE,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,CAA4B;YAElG,OAAO,EAAE,KAAK,EAAE,IAA6B,EAAmB,EAAE;gBAChE,kCAAkC;gBAClC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;oBACtC,OAAO,sBAAsB,UAAU,mCAAmC,YAAY,mBAAmB,CAAC;gBAC5G,CAAC;gBAED,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC;wBACnC,IAAI,EAAE,OAAO,CAAC,IAAI;wBAClB,SAAS,EAAE,IAAI;qBAChB,CAAC,CAAC;oBAEH,kEAAkE;oBAClE,IAAI,MAAM,GAAG,gCAAgC,CAAC;oBAC9C,IAAI,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;wBACpD,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO;6BAC7B,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;6BACtD,MAAM,CAAC,OAAO,CAAC,CAAC;wBACnB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BACzB,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBAChC,CAAC;oBACH,CAAC;oBAED,4DAA4D;oBAC5D,4CAA4C;oBAC5C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;wBACnB,OAAO,UAAU,MAAM,EAAE,CAAC;oBAC5B,CAAC;oBACD,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAAC,OAAO,GAAY,EAAE,CAAC;oBACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBACjE,OAAO,6BAA6B,UAAU,IAAI,OAAO,CAAC,IAAI,MAAM,OAAO,EAAE,CAAC;gBAChF,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF;AA9OD,4CA8OC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP (Model Context Protocol) type definitions.
|
|
3
|
+
*
|
|
4
|
+
* Users define MCP servers via `McpServerConfig`. The framework
|
|
5
|
+
* connects, discovers tools, adapts them, and registers with ToolRegistry.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for connecting to an MCP server.
|
|
9
|
+
*
|
|
10
|
+
* Two mutually exclusive modes:
|
|
11
|
+
* - stdio: set `command` (optionally `args` and `env`)
|
|
12
|
+
* - SSE: set `url`
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* // stdio: local filesystem server
|
|
17
|
+
* { command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "."] }
|
|
18
|
+
*
|
|
19
|
+
* // SSE: remote server
|
|
20
|
+
* { url: "http://localhost:3001/sse" }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export interface McpServerConfig {
|
|
24
|
+
/** Command for stdio transport (e.g., "npx", "uvx"). Exclusive with `url`. */
|
|
25
|
+
command?: string;
|
|
26
|
+
/** Arguments passed to the stdio command. */
|
|
27
|
+
args?: string[];
|
|
28
|
+
/** Environment variables injected into the stdio process. */
|
|
29
|
+
env?: Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* URL for SSE transport (e.g., "http://localhost:3000/sse").
|
|
32
|
+
* Exclusive with `command`.
|
|
33
|
+
*/
|
|
34
|
+
url?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Snapshot of a single MCP server's connection state.
|
|
38
|
+
*/
|
|
39
|
+
export interface McpConnectionStatus {
|
|
40
|
+
serverName: string;
|
|
41
|
+
connected: boolean;
|
|
42
|
+
toolCount: number;
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Report from a failed connection attempt.
|
|
47
|
+
*/
|
|
48
|
+
export interface McpConnectionErrorReport {
|
|
49
|
+
serverName: string;
|
|
50
|
+
error: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Specialized error for MCP connection/issues.
|
|
54
|
+
*/
|
|
55
|
+
export declare class McpConnectionError extends Error {
|
|
56
|
+
constructor(message: string);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=mcp-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-types.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP (Model Context Protocol) type definitions.
|
|
4
|
+
*
|
|
5
|
+
* Users define MCP servers via `McpServerConfig`. The framework
|
|
6
|
+
* connects, discovers tools, adapts them, and registers with ToolRegistry.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.McpConnectionError = void 0;
|
|
10
|
+
/**
|
|
11
|
+
* Specialized error for MCP connection/issues.
|
|
12
|
+
*/
|
|
13
|
+
class McpConnectionError extends Error {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "McpConnectionError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.McpConnectionError = McpConnectionError;
|
|
20
|
+
//# sourceMappingURL=mcp-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-types.js","sourceRoot":"","sources":["../../src/mcp/mcp-types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAkDH;;GAEG;AACH,MAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AALD,gDAKC"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Skill } from "./types";
|
|
2
|
-
import { Tool } from "../tools/types";
|
|
3
2
|
/**
|
|
4
3
|
* Parse YAML-like frontmatter from a Markdown file.
|
|
5
4
|
*
|
|
@@ -32,15 +31,14 @@ export declare function parseKeywords(raw?: string): string[] | undefined;
|
|
|
32
31
|
* skills/
|
|
33
32
|
* ├── <skill-name>/
|
|
34
33
|
* │ ├── SKILL.md # Frontmatter (metadata) + body (system prompt)
|
|
35
|
-
* │
|
|
36
|
-
* │ └── scripts/ # Optional executable scripts
|
|
34
|
+
* │ └── reference/ # Optional reference docs (*.md, *.txt)
|
|
37
35
|
* ├── <another-skill>/
|
|
38
36
|
* │ └── ...
|
|
39
37
|
* ```
|
|
40
38
|
*
|
|
41
39
|
* Loading is two-phase:
|
|
42
40
|
* 1. `scan()` — reads only frontmatter, returns metadata-only `Skill[]`
|
|
43
|
-
* 2. `loadSystemPrompt()`
|
|
41
|
+
* 2. `loadSystemPrompt()` — full content (body + reference docs), called
|
|
44
42
|
* lazily on activation.
|
|
45
43
|
*/
|
|
46
44
|
export declare class FileSkillLoader {
|
|
@@ -55,14 +53,15 @@ export declare class FileSkillLoader {
|
|
|
55
53
|
getDirectory(): string;
|
|
56
54
|
/**
|
|
57
55
|
* Get the absolute path to a named skill's subdirectory.
|
|
56
|
+
* Throws if the skill name contains path traversal characters.
|
|
58
57
|
*/
|
|
59
58
|
getSkillDir(name: string): string;
|
|
60
59
|
/**
|
|
61
60
|
* Scan the skills directory and return metadata-only Skill objects.
|
|
62
61
|
*
|
|
63
62
|
* Reads only the frontmatter from each SKILL.md — the body (systemPrompt)
|
|
64
|
-
*
|
|
65
|
-
*
|
|
63
|
+
* is NOT loaded at this stage. It is loaded lazily when the skill is
|
|
64
|
+
* activated.
|
|
66
65
|
*
|
|
67
66
|
* Subdirectories without a valid SKILL.md (or without a `name` in
|
|
68
67
|
* frontmatter) are skipped with a warning.
|
|
@@ -76,13 +75,5 @@ export declare class FileSkillLoader {
|
|
|
76
75
|
* Throws if the skill directory does not exist.
|
|
77
76
|
*/
|
|
78
77
|
loadSystemPrompt(name: string): string;
|
|
79
|
-
/**
|
|
80
|
-
* Create Tool objects from scripts in the scripts/ subdirectory.
|
|
81
|
-
*
|
|
82
|
-
* Each recognized script file becomes a Tool named `{skillName}_{scriptName}`.
|
|
83
|
-
*
|
|
84
|
-
* Returns an empty array if the scripts/ directory is missing or empty.
|
|
85
|
-
*/
|
|
86
|
-
loadScriptsAsTools(name: string): Tool[];
|
|
87
78
|
}
|
|
88
79
|
//# sourceMappingURL=file-skill-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-skill-loader.d.ts","sourceRoot":"","sources":["../../src/skills/file-skill-loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-skill-loader.d.ts","sourceRoot":"","sources":["../../src/skills/file-skill-loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAIhC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;CACd,CAuBA;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAOhE;AAwBD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAS;IAE1B;;OAEG;gBACS,SAAS,CAAC,EAAE,MAAM;IAI9B;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAKjC;;;;;;;;;OASG;IACH,IAAI,IAAI,KAAK,EAAE;IA0Df;;;;;;OAMG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAsCvC"}
|
|
@@ -38,7 +38,6 @@ exports.parseFrontmatter = parseFrontmatter;
|
|
|
38
38
|
exports.parseKeywords = parseKeywords;
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
|
-
const child_process_1 = require("child_process");
|
|
42
41
|
// ─── Frontmatter Parsing ───────────────────────────────────────────────────
|
|
43
42
|
/**
|
|
44
43
|
* Parse YAML-like frontmatter from a Markdown file.
|
|
@@ -69,7 +68,12 @@ function parseFrontmatter(raw) {
|
|
|
69
68
|
if (colonIdx <= 0)
|
|
70
69
|
continue;
|
|
71
70
|
const key = trimmed.slice(0, colonIdx).trim();
|
|
72
|
-
|
|
71
|
+
let value = trimmed.slice(colonIdx + 1).trim();
|
|
72
|
+
// Strip surrounding quotes if both ends match
|
|
73
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
74
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
75
|
+
value = value.slice(1, -1).trim();
|
|
76
|
+
}
|
|
73
77
|
if (key)
|
|
74
78
|
frontmatter[key] = value;
|
|
75
79
|
}
|
|
@@ -89,78 +93,19 @@ function parseKeywords(raw) {
|
|
|
89
93
|
return keywords.length > 0 ? keywords : undefined;
|
|
90
94
|
}
|
|
91
95
|
// ─── Script Execution ──────────────────────────────────────────────────────
|
|
92
|
-
const SUPPORTED_SCRIPT_EXTENSIONS = new Set([
|
|
93
|
-
".sh",
|
|
94
|
-
".bat",
|
|
95
|
-
".cmd",
|
|
96
|
-
".ps1",
|
|
97
|
-
".js",
|
|
98
|
-
".py",
|
|
99
|
-
]);
|
|
100
96
|
const REFERENCE_EXTENSIONS = new Set([".md", ".txt"]);
|
|
101
97
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*/
|
|
105
|
-
function getInterpreter(filePath) {
|
|
106
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
107
|
-
switch (ext) {
|
|
108
|
-
case ".sh":
|
|
109
|
-
return ["bash", filePath];
|
|
110
|
-
case ".bat":
|
|
111
|
-
case ".cmd":
|
|
112
|
-
return ["cmd.exe", "/c", filePath];
|
|
113
|
-
case ".ps1":
|
|
114
|
-
return ["powershell.exe", "-File", filePath];
|
|
115
|
-
case ".js":
|
|
116
|
-
return ["node", filePath];
|
|
117
|
-
case ".py":
|
|
118
|
-
// Try python3 first, fall back to python
|
|
119
|
-
try {
|
|
120
|
-
(0, child_process_1.execFileSync)("python3", ["--version"], { timeout: 5000, stdio: "ignore" });
|
|
121
|
-
return ["python3", filePath];
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
return ["python", filePath];
|
|
125
|
-
}
|
|
126
|
-
default:
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Extract a description for a script tool from the first comment/line
|
|
132
|
-
* of the file, or fall back to a generic description.
|
|
98
|
+
* Validate a skill name to prevent path traversal.
|
|
99
|
+
* Only allows alphanumeric characters, hyphens, underscores, and dots.
|
|
133
100
|
*/
|
|
134
|
-
function
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const firstLine = content.split("\n")[0]?.trim();
|
|
138
|
-
if (firstLine) {
|
|
139
|
-
// Strip common comment markers and shebang
|
|
140
|
-
const clean = firstLine
|
|
141
|
-
.replace(/^#!\s*/, "")
|
|
142
|
-
.replace(/^#\s*/, "")
|
|
143
|
-
.replace(/^\/\/\s*/, "")
|
|
144
|
-
.replace(/^--\s*/, "")
|
|
145
|
-
.trim();
|
|
146
|
-
if (clean && !clean.startsWith("/"))
|
|
147
|
-
return clean;
|
|
148
|
-
}
|
|
101
|
+
function validateSkillName(name) {
|
|
102
|
+
if (!name) {
|
|
103
|
+
throw new Error(`Skill name must not be empty.`);
|
|
149
104
|
}
|
|
150
|
-
|
|
151
|
-
|
|
105
|
+
// Prevent path traversal: reject names with slashes, backslashes, "..", or null bytes
|
|
106
|
+
if (/[/\\]|\.\.|\0/.test(name)) {
|
|
107
|
+
throw new Error(`Invalid skill name "${name}": contains path traversal characters.`);
|
|
152
108
|
}
|
|
153
|
-
return `Execute the ${path.basename(filePath)} script`;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Sanitize a string for use as a tool name (alphanumeric + underscores only).
|
|
157
|
-
*/
|
|
158
|
-
function sanitizeToolName(name) {
|
|
159
|
-
return name
|
|
160
|
-
.toLowerCase()
|
|
161
|
-
.replace(/[^a-z0-9_]/g, "_")
|
|
162
|
-
.replace(/_+/g, "_")
|
|
163
|
-
.replace(/^_|_$/g, "");
|
|
164
109
|
}
|
|
165
110
|
// ─── FileSkillLoader ───────────────────────────────────────────────────────
|
|
166
111
|
/**
|
|
@@ -171,15 +116,14 @@ function sanitizeToolName(name) {
|
|
|
171
116
|
* skills/
|
|
172
117
|
* ├── <skill-name>/
|
|
173
118
|
* │ ├── SKILL.md # Frontmatter (metadata) + body (system prompt)
|
|
174
|
-
* │
|
|
175
|
-
* │ └── scripts/ # Optional executable scripts
|
|
119
|
+
* │ └── reference/ # Optional reference docs (*.md, *.txt)
|
|
176
120
|
* ├── <another-skill>/
|
|
177
121
|
* │ └── ...
|
|
178
122
|
* ```
|
|
179
123
|
*
|
|
180
124
|
* Loading is two-phase:
|
|
181
125
|
* 1. `scan()` — reads only frontmatter, returns metadata-only `Skill[]`
|
|
182
|
-
* 2. `loadSystemPrompt()`
|
|
126
|
+
* 2. `loadSystemPrompt()` — full content (body + reference docs), called
|
|
183
127
|
* lazily on activation.
|
|
184
128
|
*/
|
|
185
129
|
class FileSkillLoader {
|
|
@@ -198,16 +142,18 @@ class FileSkillLoader {
|
|
|
198
142
|
}
|
|
199
143
|
/**
|
|
200
144
|
* Get the absolute path to a named skill's subdirectory.
|
|
145
|
+
* Throws if the skill name contains path traversal characters.
|
|
201
146
|
*/
|
|
202
147
|
getSkillDir(name) {
|
|
148
|
+
validateSkillName(name);
|
|
203
149
|
return path.join(this.directory, name);
|
|
204
150
|
}
|
|
205
151
|
/**
|
|
206
152
|
* Scan the skills directory and return metadata-only Skill objects.
|
|
207
153
|
*
|
|
208
154
|
* Reads only the frontmatter from each SKILL.md — the body (systemPrompt)
|
|
209
|
-
*
|
|
210
|
-
*
|
|
155
|
+
* is NOT loaded at this stage. It is loaded lazily when the skill is
|
|
156
|
+
* activated.
|
|
211
157
|
*
|
|
212
158
|
* Subdirectories without a valid SKILL.md (or without a `name` in
|
|
213
159
|
* frontmatter) are skipped with a warning.
|
|
@@ -244,12 +190,19 @@ class FileSkillLoader {
|
|
|
244
190
|
console.warn(`[Skills] Skipping "${entry.name}": SKILL.md has no "name" in frontmatter`);
|
|
245
191
|
continue;
|
|
246
192
|
}
|
|
193
|
+
try {
|
|
194
|
+
validateSkillName(name);
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
198
|
+
console.warn(`[Skills] Skipping "${entry.name}": ${message}`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
247
201
|
skills.push({
|
|
248
202
|
name,
|
|
249
203
|
description: frontmatter.description?.trim() ?? "",
|
|
250
204
|
systemPrompt: "", // Loaded lazily on activation
|
|
251
205
|
keywords: parseKeywords(frontmatter.keywords),
|
|
252
|
-
tools: [], // Loaded lazily on activation
|
|
253
206
|
});
|
|
254
207
|
}
|
|
255
208
|
return skills;
|
|
@@ -289,77 +242,6 @@ class FileSkillLoader {
|
|
|
289
242
|
}
|
|
290
243
|
return parts.join("").trim();
|
|
291
244
|
}
|
|
292
|
-
/**
|
|
293
|
-
* Create Tool objects from scripts in the scripts/ subdirectory.
|
|
294
|
-
*
|
|
295
|
-
* Each recognized script file becomes a Tool named `{skillName}_{scriptName}`.
|
|
296
|
-
*
|
|
297
|
-
* Returns an empty array if the scripts/ directory is missing or empty.
|
|
298
|
-
*/
|
|
299
|
-
loadScriptsAsTools(name) {
|
|
300
|
-
const scriptsDir = path.join(this.getSkillDir(name), "scripts");
|
|
301
|
-
const tools = [];
|
|
302
|
-
let entries;
|
|
303
|
-
try {
|
|
304
|
-
entries = fs.readdirSync(scriptsDir, { withFileTypes: true });
|
|
305
|
-
}
|
|
306
|
-
catch {
|
|
307
|
-
// No scripts/ directory — return empty
|
|
308
|
-
return [];
|
|
309
|
-
}
|
|
310
|
-
for (const entry of entries) {
|
|
311
|
-
if (!entry.isFile())
|
|
312
|
-
continue;
|
|
313
|
-
const ext = path.extname(entry.name).toLowerCase();
|
|
314
|
-
if (!SUPPORTED_SCRIPT_EXTENSIONS.has(ext))
|
|
315
|
-
continue;
|
|
316
|
-
const scriptPath = path.join(scriptsDir, entry.name);
|
|
317
|
-
const scriptName = path.basename(entry.name, ext);
|
|
318
|
-
const toolName = sanitizeToolName(`${name}_${scriptName}`);
|
|
319
|
-
const toolDescription = getScriptDescription(scriptPath);
|
|
320
|
-
const tool = {
|
|
321
|
-
name: toolName,
|
|
322
|
-
description: toolDescription,
|
|
323
|
-
parameters: {
|
|
324
|
-
type: "object",
|
|
325
|
-
properties: {
|
|
326
|
-
args: {
|
|
327
|
-
type: "string",
|
|
328
|
-
description: `Arguments to pass to the ${entry.name} script. ` +
|
|
329
|
-
`Use shell-style syntax (space-separated).`,
|
|
330
|
-
},
|
|
331
|
-
},
|
|
332
|
-
required: ["args"],
|
|
333
|
-
},
|
|
334
|
-
async execute(params) {
|
|
335
|
-
const argsStr = String(params.args ?? "");
|
|
336
|
-
const interpreter = getInterpreter(scriptPath);
|
|
337
|
-
if (!interpreter) {
|
|
338
|
-
return `Error: Unsupported script type "${ext}" for ${entry.name}`;
|
|
339
|
-
}
|
|
340
|
-
const [cmd, ...cmdArgs] = interpreter;
|
|
341
|
-
// Append the script argument after the interpreter args
|
|
342
|
-
const allArgs = argsStr
|
|
343
|
-
? [...cmdArgs, ...argsStr.split(/\s+/).filter(Boolean)]
|
|
344
|
-
: cmdArgs;
|
|
345
|
-
return new Promise((resolve) => {
|
|
346
|
-
const child = (0, child_process_1.execFile)(cmd, allArgs, { timeout: 30000, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
|
|
347
|
-
if (err) {
|
|
348
|
-
const details = stderr
|
|
349
|
-
? `\nstderr:\n${stderr.trim()}`
|
|
350
|
-
: "";
|
|
351
|
-
resolve(`Error executing ${entry.name}: ${err.message}${details}`);
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
resolve(stdout.trim() || "(no output)");
|
|
355
|
-
});
|
|
356
|
-
});
|
|
357
|
-
},
|
|
358
|
-
};
|
|
359
|
-
tools.push(tool);
|
|
360
|
-
}
|
|
361
|
-
return tools;
|
|
362
|
-
}
|
|
363
245
|
}
|
|
364
246
|
exports.FileSkillLoader = FileSkillLoader;
|
|
365
247
|
//# sourceMappingURL=file-skill-loader.js.map
|