@dexto/tools-builtins 1.6.17 → 1.6.18

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.
@@ -94,6 +94,17 @@ function createInvokeSkillTool() {
94
94
  }
95
95
  }
96
96
  const promptDef = await promptManager.getPromptDefinition(skillKey);
97
+ const taskForker = context.services?.taskForker;
98
+ if (promptDef?.context === "fork" && !taskForker) {
99
+ return {
100
+ error: `Skill '${skill}' requires fork execution (context: fork), but agent spawning is not available.`,
101
+ skill: skillKey
102
+ };
103
+ }
104
+ const skillMcpError = await ensureSkillMcpServersConnected(skill, matchedInfo, context);
105
+ if (skillMcpError) {
106
+ return skillMcpError;
107
+ }
97
108
  if (promptDef?.context !== "fork" && promptDef?.allowedTools && promptDef.allowedTools.length > 0 && context.sessionId && context.agent?.toolManager) {
98
109
  try {
99
110
  context.agent.toolManager.addSessionAutoApproveTools(
@@ -111,8 +122,8 @@ function createInvokeSkillTool() {
111
122
  const flattened = (0, import_core.flattenPromptResult)(promptResult);
112
123
  const content = flattened.text;
113
124
  if (promptDef?.context === "fork") {
114
- const taskForker = context.services?.taskForker;
115
- if (!taskForker) {
125
+ const activeTaskForker = taskForker;
126
+ if (!activeTaskForker) {
116
127
  return {
117
128
  error: `Skill '${skill}' requires fork execution (context: fork), but agent spawning is not available.`,
118
129
  skill: skillKey
@@ -137,7 +148,7 @@ ${content}` : content;
137
148
  if (context.sessionId) {
138
149
  forkOptions.sessionId = context.sessionId;
139
150
  }
140
- const result = await taskForker.fork(forkOptions);
151
+ const result = await activeTaskForker.fork(forkOptions);
141
152
  if (result.success) {
142
153
  return result.response ?? "Task completed successfully.";
143
154
  }
@@ -167,11 +178,61 @@ Parameters:
167
178
  Execution modes:
168
179
  - **Inline skills**: Return instructions for you to follow in the current conversation
169
180
  - **Fork skills**: Automatically execute in an isolated subagent and return the result (no additional tool calls needed)
181
+ - **Bundled MCP skills**: Automatically connect any MCP servers carried inside the skill bundle when the skill is invoked
170
182
 
171
183
  Fork skills run in complete isolation without access to conversation history. They're useful for tasks that should run independently.
172
184
 
173
185
  Available skills are listed in your system prompt. Use the skill name exactly as shown.`;
174
186
  }
187
+ function getSkillBundledMcpServers(promptInfo) {
188
+ const rawServers = promptInfo?.metadata?.mcpServers;
189
+ if (!rawServers || typeof rawServers !== "object" || Array.isArray(rawServers)) {
190
+ return {};
191
+ }
192
+ return Object.fromEntries(
193
+ Object.entries(rawServers).filter(
194
+ ([, config]) => typeof config === "object" && config !== null && !Array.isArray(config)
195
+ )
196
+ );
197
+ }
198
+ async function ensureSkillMcpServersConnected(skill, promptInfo, context) {
199
+ const mcpServers = getSkillBundledMcpServers(promptInfo);
200
+ const serverNames = Object.keys(mcpServers);
201
+ if (serverNames.length === 0) {
202
+ return void 0;
203
+ }
204
+ if (!context.agent?.addMcpServer || !context.agent.getMcpServerStatus || !context.agent.enableMcpServer) {
205
+ return {
206
+ error: `Skill '${skill}' requires bundled MCP servers (${serverNames.join(", ")}), but this agent does not support dynamic MCP loading.`,
207
+ mcpServers: serverNames
208
+ };
209
+ }
210
+ try {
211
+ for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
212
+ let serverStatus = context.agent.getMcpServerStatus(serverName);
213
+ if (!serverStatus) {
214
+ await context.agent.addMcpServer(serverName, serverConfig);
215
+ serverStatus = context.agent.getMcpServerStatus(serverName);
216
+ }
217
+ if (!serverStatus?.enabled || serverStatus.status !== "connected") {
218
+ await context.agent.enableMcpServer(serverName);
219
+ serverStatus = context.agent.getMcpServerStatus(serverName);
220
+ }
221
+ if (!serverStatus || serverStatus.status !== "connected") {
222
+ return {
223
+ error: `Skill '${skill}' requires MCP server '${serverName}', but it is currently ${serverStatus?.status ?? "unavailable"}${serverStatus?.error ? `: ${serverStatus.error}` : ""}.`,
224
+ mcpServers: serverNames
225
+ };
226
+ }
227
+ }
228
+ } catch (error) {
229
+ return {
230
+ error: error instanceof Error ? error.message : "Failed to connect bundled MCP servers for skill",
231
+ mcpServers: serverNames
232
+ };
233
+ }
234
+ return void 0;
235
+ }
175
236
  // Annotate the CommonJS export names for ESM import in node:
176
237
  0 && (module.exports = {
177
238
  createInvokeSkillTool
@@ -1 +1 @@
1
- {"version":3,"file":"invoke-skill-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/invoke-skill-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAA+B,IAAI,EAAwB,MAAM,aAAa,CAAC;AAE3F,QAAA,MAAM,sBAAsB;;;;;;;;;;;;EAgBf,CAAC;AAEd;;;;;;GAMG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAAC,OAAO,sBAAsB,CAAC,CA0J3E"}
1
+ {"version":3,"file":"invoke-skill-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/invoke-skill-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAIR,IAAI,EAEP,MAAM,aAAa,CAAC;AAErB,QAAA,MAAM,sBAAsB;;;;;;;;;;;;EAgBf,CAAC;AAEd;;;;;;GAMG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAAC,OAAO,sBAAsB,CAAC,CAuK3E"}
@@ -71,6 +71,17 @@ function createInvokeSkillTool() {
71
71
  }
72
72
  }
73
73
  const promptDef = await promptManager.getPromptDefinition(skillKey);
74
+ const taskForker = context.services?.taskForker;
75
+ if (promptDef?.context === "fork" && !taskForker) {
76
+ return {
77
+ error: `Skill '${skill}' requires fork execution (context: fork), but agent spawning is not available.`,
78
+ skill: skillKey
79
+ };
80
+ }
81
+ const skillMcpError = await ensureSkillMcpServersConnected(skill, matchedInfo, context);
82
+ if (skillMcpError) {
83
+ return skillMcpError;
84
+ }
74
85
  if (promptDef?.context !== "fork" && promptDef?.allowedTools && promptDef.allowedTools.length > 0 && context.sessionId && context.agent?.toolManager) {
75
86
  try {
76
87
  context.agent.toolManager.addSessionAutoApproveTools(
@@ -88,8 +99,8 @@ function createInvokeSkillTool() {
88
99
  const flattened = flattenPromptResult(promptResult);
89
100
  const content = flattened.text;
90
101
  if (promptDef?.context === "fork") {
91
- const taskForker = context.services?.taskForker;
92
- if (!taskForker) {
102
+ const activeTaskForker = taskForker;
103
+ if (!activeTaskForker) {
93
104
  return {
94
105
  error: `Skill '${skill}' requires fork execution (context: fork), but agent spawning is not available.`,
95
106
  skill: skillKey
@@ -114,7 +125,7 @@ ${content}` : content;
114
125
  if (context.sessionId) {
115
126
  forkOptions.sessionId = context.sessionId;
116
127
  }
117
- const result = await taskForker.fork(forkOptions);
128
+ const result = await activeTaskForker.fork(forkOptions);
118
129
  if (result.success) {
119
130
  return result.response ?? "Task completed successfully.";
120
131
  }
@@ -144,11 +155,61 @@ Parameters:
144
155
  Execution modes:
145
156
  - **Inline skills**: Return instructions for you to follow in the current conversation
146
157
  - **Fork skills**: Automatically execute in an isolated subagent and return the result (no additional tool calls needed)
158
+ - **Bundled MCP skills**: Automatically connect any MCP servers carried inside the skill bundle when the skill is invoked
147
159
 
148
160
  Fork skills run in complete isolation without access to conversation history. They're useful for tasks that should run independently.
149
161
 
150
162
  Available skills are listed in your system prompt. Use the skill name exactly as shown.`;
151
163
  }
164
+ function getSkillBundledMcpServers(promptInfo) {
165
+ const rawServers = promptInfo?.metadata?.mcpServers;
166
+ if (!rawServers || typeof rawServers !== "object" || Array.isArray(rawServers)) {
167
+ return {};
168
+ }
169
+ return Object.fromEntries(
170
+ Object.entries(rawServers).filter(
171
+ ([, config]) => typeof config === "object" && config !== null && !Array.isArray(config)
172
+ )
173
+ );
174
+ }
175
+ async function ensureSkillMcpServersConnected(skill, promptInfo, context) {
176
+ const mcpServers = getSkillBundledMcpServers(promptInfo);
177
+ const serverNames = Object.keys(mcpServers);
178
+ if (serverNames.length === 0) {
179
+ return void 0;
180
+ }
181
+ if (!context.agent?.addMcpServer || !context.agent.getMcpServerStatus || !context.agent.enableMcpServer) {
182
+ return {
183
+ error: `Skill '${skill}' requires bundled MCP servers (${serverNames.join(", ")}), but this agent does not support dynamic MCP loading.`,
184
+ mcpServers: serverNames
185
+ };
186
+ }
187
+ try {
188
+ for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
189
+ let serverStatus = context.agent.getMcpServerStatus(serverName);
190
+ if (!serverStatus) {
191
+ await context.agent.addMcpServer(serverName, serverConfig);
192
+ serverStatus = context.agent.getMcpServerStatus(serverName);
193
+ }
194
+ if (!serverStatus?.enabled || serverStatus.status !== "connected") {
195
+ await context.agent.enableMcpServer(serverName);
196
+ serverStatus = context.agent.getMcpServerStatus(serverName);
197
+ }
198
+ if (!serverStatus || serverStatus.status !== "connected") {
199
+ return {
200
+ error: `Skill '${skill}' requires MCP server '${serverName}', but it is currently ${serverStatus?.status ?? "unavailable"}${serverStatus?.error ? `: ${serverStatus.error}` : ""}.`,
201
+ mcpServers: serverNames
202
+ };
203
+ }
204
+ }
205
+ } catch (error) {
206
+ return {
207
+ error: error instanceof Error ? error.message : "Failed to connect bundled MCP servers for skill",
208
+ mcpServers: serverNames
209
+ };
210
+ }
211
+ return void 0;
212
+ }
152
213
  export {
153
214
  createInvokeSkillTool
154
215
  };
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_invoke_skill_tool = require("./invoke-skill-tool.js");
4
+ function createPromptInfo() {
5
+ return {
6
+ name: "echo-custom-mcp",
7
+ displayName: "echo-custom-mcp",
8
+ commandName: "echo-custom-mcp",
9
+ source: "config",
10
+ metadata: {
11
+ type: "file",
12
+ filePath: "/tmp/skills/echo-custom-mcp/SKILL.md",
13
+ mcpServers: {
14
+ skill_echo_demo: {
15
+ type: "stdio",
16
+ command: "node",
17
+ args: ["scripts/echo-mcp-server.mjs"]
18
+ }
19
+ }
20
+ }
21
+ };
22
+ }
23
+ (0, import_vitest.describe)("invoke_skill tool", () => {
24
+ (0, import_vitest.it)("retries previously registered bundled MCP servers before returning an error", async () => {
25
+ const promptInfo = createPromptInfo();
26
+ const promptManager = {
27
+ listAutoInvocablePrompts: import_vitest.vi.fn().mockResolvedValue({ "config:echo-custom-mcp": promptInfo }),
28
+ getPromptDefinition: import_vitest.vi.fn().mockResolvedValue(void 0),
29
+ getPrompt: import_vitest.vi.fn().mockResolvedValue({
30
+ messages: [
31
+ {
32
+ content: {
33
+ type: "text",
34
+ text: "Use the bundled echo MCP tool."
35
+ }
36
+ }
37
+ ]
38
+ })
39
+ };
40
+ const addMcpServer = import_vitest.vi.fn();
41
+ const enableMcpServer = import_vitest.vi.fn().mockResolvedValue(void 0);
42
+ const getMcpServerStatus = import_vitest.vi.fn().mockReturnValueOnce({
43
+ name: "skill_echo_demo",
44
+ type: "stdio",
45
+ enabled: true,
46
+ status: "error",
47
+ error: "Request timed out"
48
+ }).mockReturnValueOnce({
49
+ name: "skill_echo_demo",
50
+ type: "stdio",
51
+ enabled: true,
52
+ status: "connected"
53
+ });
54
+ const tool = (0, import_invoke_skill_tool.createInvokeSkillTool)();
55
+ const result = await tool.execute(
56
+ {
57
+ skill: "echo-custom-mcp"
58
+ },
59
+ {
60
+ logger: {
61
+ warn: import_vitest.vi.fn()
62
+ },
63
+ services: {
64
+ prompts: promptManager
65
+ },
66
+ agent: {
67
+ addMcpServer,
68
+ getMcpServerStatus,
69
+ enableMcpServer
70
+ }
71
+ }
72
+ );
73
+ (0, import_vitest.expect)(addMcpServer).not.toHaveBeenCalled();
74
+ (0, import_vitest.expect)(enableMcpServer).toHaveBeenCalledWith("skill_echo_demo");
75
+ (0, import_vitest.expect)(getMcpServerStatus).toHaveBeenCalledTimes(2);
76
+ (0, import_vitest.expect)(result).toMatchObject({
77
+ skill: "config:echo-custom-mcp",
78
+ content: "Use the bundled echo MCP tool."
79
+ });
80
+ });
81
+ (0, import_vitest.it)("does not connect bundled MCP servers when fork execution is unavailable", async () => {
82
+ const promptInfo = createPromptInfo();
83
+ const promptManager = {
84
+ listAutoInvocablePrompts: import_vitest.vi.fn().mockResolvedValue({ "config:echo-custom-mcp": promptInfo }),
85
+ getPromptDefinition: import_vitest.vi.fn().mockResolvedValue({
86
+ name: "echo-custom-mcp",
87
+ context: "fork"
88
+ }),
89
+ getPrompt: import_vitest.vi.fn()
90
+ };
91
+ const addMcpServer = import_vitest.vi.fn();
92
+ const enableMcpServer = import_vitest.vi.fn();
93
+ const getMcpServerStatus = import_vitest.vi.fn();
94
+ const tool = (0, import_invoke_skill_tool.createInvokeSkillTool)();
95
+ const result = await tool.execute(
96
+ {
97
+ skill: "echo-custom-mcp"
98
+ },
99
+ {
100
+ logger: {
101
+ warn: import_vitest.vi.fn()
102
+ },
103
+ services: {
104
+ prompts: promptManager
105
+ },
106
+ agent: {
107
+ addMcpServer,
108
+ getMcpServerStatus,
109
+ enableMcpServer
110
+ }
111
+ }
112
+ );
113
+ (0, import_vitest.expect)(addMcpServer).not.toHaveBeenCalled();
114
+ (0, import_vitest.expect)(enableMcpServer).not.toHaveBeenCalled();
115
+ (0, import_vitest.expect)(getMcpServerStatus).not.toHaveBeenCalled();
116
+ (0, import_vitest.expect)(promptManager.getPrompt).not.toHaveBeenCalled();
117
+ (0, import_vitest.expect)(result).toEqual({
118
+ error: "Skill 'echo-custom-mcp' requires fork execution (context: fork), but agent spawning is not available.",
119
+ skill: "config:echo-custom-mcp"
120
+ });
121
+ });
122
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=invoke-skill-tool.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoke-skill-tool.test.d.ts","sourceRoot":"","sources":["../../src/implementations/invoke-skill-tool.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,121 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { createInvokeSkillTool } from "./invoke-skill-tool.js";
3
+ function createPromptInfo() {
4
+ return {
5
+ name: "echo-custom-mcp",
6
+ displayName: "echo-custom-mcp",
7
+ commandName: "echo-custom-mcp",
8
+ source: "config",
9
+ metadata: {
10
+ type: "file",
11
+ filePath: "/tmp/skills/echo-custom-mcp/SKILL.md",
12
+ mcpServers: {
13
+ skill_echo_demo: {
14
+ type: "stdio",
15
+ command: "node",
16
+ args: ["scripts/echo-mcp-server.mjs"]
17
+ }
18
+ }
19
+ }
20
+ };
21
+ }
22
+ describe("invoke_skill tool", () => {
23
+ it("retries previously registered bundled MCP servers before returning an error", async () => {
24
+ const promptInfo = createPromptInfo();
25
+ const promptManager = {
26
+ listAutoInvocablePrompts: vi.fn().mockResolvedValue({ "config:echo-custom-mcp": promptInfo }),
27
+ getPromptDefinition: vi.fn().mockResolvedValue(void 0),
28
+ getPrompt: vi.fn().mockResolvedValue({
29
+ messages: [
30
+ {
31
+ content: {
32
+ type: "text",
33
+ text: "Use the bundled echo MCP tool."
34
+ }
35
+ }
36
+ ]
37
+ })
38
+ };
39
+ const addMcpServer = vi.fn();
40
+ const enableMcpServer = vi.fn().mockResolvedValue(void 0);
41
+ const getMcpServerStatus = vi.fn().mockReturnValueOnce({
42
+ name: "skill_echo_demo",
43
+ type: "stdio",
44
+ enabled: true,
45
+ status: "error",
46
+ error: "Request timed out"
47
+ }).mockReturnValueOnce({
48
+ name: "skill_echo_demo",
49
+ type: "stdio",
50
+ enabled: true,
51
+ status: "connected"
52
+ });
53
+ const tool = createInvokeSkillTool();
54
+ const result = await tool.execute(
55
+ {
56
+ skill: "echo-custom-mcp"
57
+ },
58
+ {
59
+ logger: {
60
+ warn: vi.fn()
61
+ },
62
+ services: {
63
+ prompts: promptManager
64
+ },
65
+ agent: {
66
+ addMcpServer,
67
+ getMcpServerStatus,
68
+ enableMcpServer
69
+ }
70
+ }
71
+ );
72
+ expect(addMcpServer).not.toHaveBeenCalled();
73
+ expect(enableMcpServer).toHaveBeenCalledWith("skill_echo_demo");
74
+ expect(getMcpServerStatus).toHaveBeenCalledTimes(2);
75
+ expect(result).toMatchObject({
76
+ skill: "config:echo-custom-mcp",
77
+ content: "Use the bundled echo MCP tool."
78
+ });
79
+ });
80
+ it("does not connect bundled MCP servers when fork execution is unavailable", async () => {
81
+ const promptInfo = createPromptInfo();
82
+ const promptManager = {
83
+ listAutoInvocablePrompts: vi.fn().mockResolvedValue({ "config:echo-custom-mcp": promptInfo }),
84
+ getPromptDefinition: vi.fn().mockResolvedValue({
85
+ name: "echo-custom-mcp",
86
+ context: "fork"
87
+ }),
88
+ getPrompt: vi.fn()
89
+ };
90
+ const addMcpServer = vi.fn();
91
+ const enableMcpServer = vi.fn();
92
+ const getMcpServerStatus = vi.fn();
93
+ const tool = createInvokeSkillTool();
94
+ const result = await tool.execute(
95
+ {
96
+ skill: "echo-custom-mcp"
97
+ },
98
+ {
99
+ logger: {
100
+ warn: vi.fn()
101
+ },
102
+ services: {
103
+ prompts: promptManager
104
+ },
105
+ agent: {
106
+ addMcpServer,
107
+ getMcpServerStatus,
108
+ enableMcpServer
109
+ }
110
+ }
111
+ );
112
+ expect(addMcpServer).not.toHaveBeenCalled();
113
+ expect(enableMcpServer).not.toHaveBeenCalled();
114
+ expect(getMcpServerStatus).not.toHaveBeenCalled();
115
+ expect(promptManager.getPrompt).not.toHaveBeenCalled();
116
+ expect(result).toEqual({
117
+ error: "Skill 'echo-custom-mcp' requires fork execution (context: fork), but agent spawning is not available.",
118
+ skill: "config:echo-custom-mcp"
119
+ });
120
+ });
121
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexto/tools-builtins",
3
- "version": "1.6.17",
3
+ "version": "1.6.18",
4
4
  "description": "Built-in tools factory for Dexto agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,8 +19,8 @@
19
19
  "dependencies": {
20
20
  "undici": "^7.24.0",
21
21
  "zod": "^3.25.0",
22
- "@dexto/agent-config": "1.6.17",
23
- "@dexto/core": "1.6.17"
22
+ "@dexto/agent-config": "1.6.18",
23
+ "@dexto/core": "1.6.18"
24
24
  },
25
25
  "devDependencies": {
26
26
  "tsup": "^8.0.0",