@wingman-ai/gateway 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +29 -111
  2. package/dist/agent/config/agentConfig.cjs +2 -0
  3. package/dist/agent/config/agentConfig.d.ts +6 -0
  4. package/dist/agent/config/agentConfig.js +2 -0
  5. package/dist/agent/config/agentLoader.cjs +21 -18
  6. package/dist/agent/config/agentLoader.js +22 -19
  7. package/dist/agent/config/mcpClientManager.cjs +48 -9
  8. package/dist/agent/config/mcpClientManager.d.ts +12 -0
  9. package/dist/agent/config/mcpClientManager.js +48 -9
  10. package/dist/agent/config/toolRegistry.cjs +19 -0
  11. package/dist/agent/config/toolRegistry.d.ts +4 -0
  12. package/dist/agent/config/toolRegistry.js +17 -1
  13. package/dist/agent/middleware/additional-messages.cjs +115 -11
  14. package/dist/agent/middleware/additional-messages.d.ts +9 -0
  15. package/dist/agent/middleware/additional-messages.js +115 -11
  16. package/dist/agent/tests/agentLoader.test.cjs +45 -0
  17. package/dist/agent/tests/agentLoader.test.js +45 -0
  18. package/dist/agent/tests/mcpClientManager.test.cjs +50 -0
  19. package/dist/agent/tests/mcpClientManager.test.js +50 -0
  20. package/dist/agent/tests/toolRegistry.test.cjs +2 -0
  21. package/dist/agent/tests/toolRegistry.test.js +2 -0
  22. package/dist/agent/tools/node_invoke.cjs +146 -0
  23. package/dist/agent/tools/node_invoke.d.ts +86 -0
  24. package/dist/agent/tools/node_invoke.js +109 -0
  25. package/dist/cli/commands/gateway.cjs +1 -1
  26. package/dist/cli/commands/gateway.js +1 -1
  27. package/dist/cli/commands/skill.cjs +12 -4
  28. package/dist/cli/commands/skill.js +12 -4
  29. package/dist/cli/config/jsonSchema.cjs +55 -0
  30. package/dist/cli/config/jsonSchema.d.ts +2 -0
  31. package/dist/cli/config/jsonSchema.js +18 -0
  32. package/dist/cli/config/loader.cjs +33 -1
  33. package/dist/cli/config/loader.js +33 -1
  34. package/dist/cli/config/schema.cjs +119 -2
  35. package/dist/cli/config/schema.d.ts +40 -0
  36. package/dist/cli/config/schema.js +119 -2
  37. package/dist/cli/core/agentInvoker.cjs +25 -4
  38. package/dist/cli/core/agentInvoker.d.ts +13 -0
  39. package/dist/cli/core/agentInvoker.js +25 -4
  40. package/dist/cli/services/skillRepository.cjs +138 -20
  41. package/dist/cli/services/skillRepository.d.ts +10 -2
  42. package/dist/cli/services/skillRepository.js +138 -20
  43. package/dist/cli/services/skillSecurityScanner.cjs +158 -0
  44. package/dist/cli/services/skillSecurityScanner.d.ts +28 -0
  45. package/dist/cli/services/skillSecurityScanner.js +121 -0
  46. package/dist/cli/services/skillService.cjs +44 -12
  47. package/dist/cli/services/skillService.d.ts +2 -0
  48. package/dist/cli/services/skillService.js +46 -14
  49. package/dist/cli/types/skill.d.ts +9 -0
  50. package/dist/gateway/http/nodes.cjs +247 -0
  51. package/dist/gateway/http/nodes.d.ts +20 -0
  52. package/dist/gateway/http/nodes.js +210 -0
  53. package/dist/gateway/node.cjs +10 -1
  54. package/dist/gateway/node.d.ts +10 -1
  55. package/dist/gateway/node.js +10 -1
  56. package/dist/gateway/server.cjs +418 -27
  57. package/dist/gateway/server.d.ts +34 -0
  58. package/dist/gateway/server.js +412 -27
  59. package/dist/gateway/types.d.ts +15 -1
  60. package/dist/gateway/validation.cjs +2 -0
  61. package/dist/gateway/validation.d.ts +4 -0
  62. package/dist/gateway/validation.js +2 -0
  63. package/dist/tests/additionalMessageMiddleware.test.cjs +92 -0
  64. package/dist/tests/additionalMessageMiddleware.test.js +92 -0
  65. package/dist/tests/cli-config-loader.test.cjs +33 -1
  66. package/dist/tests/cli-config-loader.test.js +33 -1
  67. package/dist/tests/config-json-schema.test.cjs +25 -0
  68. package/dist/tests/config-json-schema.test.d.ts +1 -0
  69. package/dist/tests/config-json-schema.test.js +19 -0
  70. package/dist/tests/gateway-http-security.test.cjs +277 -0
  71. package/dist/tests/gateway-http-security.test.d.ts +1 -0
  72. package/dist/tests/gateway-http-security.test.js +271 -0
  73. package/dist/tests/gateway-node-mode.test.cjs +174 -0
  74. package/dist/tests/gateway-node-mode.test.d.ts +1 -0
  75. package/dist/tests/gateway-node-mode.test.js +168 -0
  76. package/dist/tests/gateway-origin-policy.test.cjs +60 -0
  77. package/dist/tests/gateway-origin-policy.test.d.ts +1 -0
  78. package/dist/tests/gateway-origin-policy.test.js +54 -0
  79. package/dist/tests/gateway.test.cjs +1 -0
  80. package/dist/tests/gateway.test.js +1 -0
  81. package/dist/tests/node-tools.test.cjs +77 -0
  82. package/dist/tests/node-tools.test.d.ts +1 -0
  83. package/dist/tests/node-tools.test.js +71 -0
  84. package/dist/tests/nodes-api.test.cjs +86 -0
  85. package/dist/tests/nodes-api.test.d.ts +1 -0
  86. package/dist/tests/nodes-api.test.js +80 -0
  87. package/dist/tests/skill-repository.test.cjs +106 -0
  88. package/dist/tests/skill-repository.test.d.ts +1 -0
  89. package/dist/tests/skill-repository.test.js +100 -0
  90. package/dist/tests/skill-security-scanner.test.cjs +126 -0
  91. package/dist/tests/skill-security-scanner.test.d.ts +1 -0
  92. package/dist/tests/skill-security-scanner.test.js +120 -0
  93. package/dist/tests/uv.test.cjs +47 -0
  94. package/dist/tests/uv.test.d.ts +1 -0
  95. package/dist/tests/uv.test.js +41 -0
  96. package/dist/utils/uv.cjs +64 -0
  97. package/dist/utils/uv.d.ts +3 -0
  98. package/dist/utils/uv.js +24 -0
  99. package/dist/webui/assets/{index-DHbfLOUR.js → index-BMekSELC.js} +106 -106
  100. package/dist/webui/index.html +1 -1
  101. package/package.json +2 -1
  102. package/skills/gog/SKILL.md +36 -0
  103. package/skills/weather/SKILL.md +49 -0
package/README.md CHANGED
@@ -123,128 +123,46 @@ wingman provider login ollama # Optional
123
123
  wingman agent --local --agent <id> "prompt"
124
124
  ```
125
125
 
126
- ## Gateway Configuration (All the Ways + Why)
126
+ ## Secure Skills + MCP Proxy (TL;DR)
127
127
 
128
- Gateway behavior can be configured in three layers (higher priority wins): runtime flags, environment variables, and `wingman.config.json`. Use the config file for persistent defaults, then override per run when needed.
128
+ Main point: skill scanning and MCP proxy are separate toggles, both explicit, and `uv` checks only happen when the feature is enabled.
129
129
 
130
- ### 1) `wingman.config.json` (persistent defaults)
130
+ Key CLI commands:
131
131
 
132
- - `gateway.host` / `gateway.port` - bind address + port. Use `0.0.0.0` for LAN access, or change the port to avoid conflicts.
133
- - `gateway.stateDir` - where durable sessions and gateway state live. Point to fast local storage or a shared volume.
134
- - `gateway.fsRoots` - allowlist for Control UI working folders and output paths. Keep this tight for safety.
135
- - `gateway.auth.mode` / `gateway.auth.token` / `gateway.auth.password` - gateway auth strategy (token, password, or none) for remote access.
136
- - `gateway.auth.allowTailscale` - trust Tailscale identity headers so Tailnet users can access without tokens.
137
- - `gateway.controlUi.enabled` / `gateway.controlUi.port` - enable/disable Control UI and choose its port.
138
- - `gateway.controlUi.pairingRequired` - require pairing for Control UI clients (recommended).
139
- - `gateway.controlUi.allowInsecureAuth` - only for local dev when testing auth flows.
140
- - `gateway.adapters.discord.*` - Discord output adapter:
141
- - `enabled`, `token`, `mentionOnly`, `allowBots`, `allowedGuilds`, `allowedChannels`
142
- - `channelSessions` to pin channels to a session (or `agent:<id>:` to force routing)
143
- - `sessionCommand` for ad-hoc session overrides
144
- - `responseChunkSize` to fit Discord message limits
145
- - Optional `gatewayUrl`, `gatewayToken`, `gatewayPassword` to point the adapter at a remote gateway
146
-
147
- ### 2) Runtime flags (`wingman gateway start` / `run`)
148
-
149
- - `--host`, `--port` - override bind address + port for this run.
150
- - `--auth`, `--auth-mode`, `--token`, `--password` - enable auth without editing config.
151
- - `--discovery mdns|tailscale`, `--name` - advertise your gateway for LAN or Tailnet discovery.
152
- - `--max-nodes`, `--ping-interval`, `--ping-timeout` - tune scale and heartbeat behavior.
153
- - `--log-level` - dial verbosity for debugging or production.
154
-
155
- ### 3) Environment overrides
156
-
157
- - `WINGMAN_GATEWAY_TOKEN` - supply a token at runtime so you don't store secrets in config.
158
-
159
- ### Related gateway behavior (configured elsewhere)
160
-
161
- - `agents.bindings` - deterministic routing rules used by the gateway to select an agent per inbound channel/message.
162
- - `voice` - gateway TTS defaults (provider + settings), with optional per-agent overrides for voice-enabled UIs.
163
-
164
- ### Example configs (common setups)
132
+ ```bash
133
+ # gateway auth token
134
+ wingman gateway token --generate
135
+ export WINGMAN_GATEWAY_TOKEN="<token>"
165
136
 
166
- #### 1) Local dev (single user, no auth)
137
+ # gateway runtime
138
+ wingman gateway start
167
139
 
168
- ```json
169
- {
170
- "gateway": {
171
- "host": "127.0.0.1",
172
- "port": 18789,
173
- "auth": { "mode": "none" },
174
- "controlUi": { "enabled": true, "port": 18790 }
175
- }
176
- }
140
+ # skills
141
+ wingman skill browse
142
+ wingman skill install <skill-name>
143
+ wingman skill list
144
+ wingman skill remove <skill-name>
177
145
  ```
178
146
 
179
- #### 2) Shared LAN gateway (token auth + restricted outputs)
180
-
181
- ```json
182
- {
183
- "gateway": {
184
- "host": "0.0.0.0",
185
- "port": 18789,
186
- "fsRoots": ["~/Projects", "~/.wingman/outputs"],
187
- "auth": { "mode": "token" },
188
- "controlUi": { "enabled": true, "port": 18790, "pairingRequired": true }
189
- }
190
- }
191
- ```
147
+ - Skill scan runs on each `wingman skill install` only when `skills.security.scanOnInstall` is enabled.
148
+ - MCP proxy runs at agent runtime only when `gateway.mcpProxy.enabled` is enabled.
149
+ - If `uv` is missing for an enabled feature, Wingman fails with an error (no interactive prompt).
150
+ - Full config examples: `../docs-website/docs/configuration/skills.mdx`, `../docs-website/docs/configuration/gateway.mdx`, `../docs-website/docs/configuration/wingman-config.mdx`.
192
151
 
193
- Tip: set `WINGMAN_GATEWAY_TOKEN` at runtime so you do not store tokens in config.
194
-
195
- #### 3) Headless gateway + Discord output adapter
196
-
197
- ```json
198
- {
199
- "gateway": {
200
- "host": "0.0.0.0",
201
- "port": 18789,
202
- "auth": { "mode": "token" },
203
- "controlUi": { "enabled": false },
204
- "adapters": {
205
- "discord": {
206
- "enabled": true,
207
- "token": "DISCORD_BOT_TOKEN",
208
- "mentionOnly": true,
209
- "allowedGuilds": ["123456789012345678"],
210
- "allowedChannels": ["987654321098765432"],
211
- "channelSessions": {
212
- "987654321098765432": "agent:support:discord:channel:987654321098765432"
213
- }
214
- }
215
- }
216
- }
217
- }
218
- ```
152
+ ## Configuration
219
153
 
220
- #### 4) Remote access over Tailscale + voice TTS
221
-
222
- ```json
223
- {
224
- "gateway": {
225
- "host": "0.0.0.0",
226
- "port": 18789,
227
- "auth": { "mode": "token", "allowTailscale": true },
228
- "controlUi": { "enabled": true, "port": 18790, "pairingRequired": true }
229
- },
230
- "voice": {
231
- "provider": "elevenlabs",
232
- "defaultPolicy": "off",
233
- "elevenlabs": {
234
- "voiceId": "VOICE_ID",
235
- "modelId": "eleven_multilingual_v2",
236
- "stability": 0.4,
237
- "similarityBoost": 0.7
238
- }
239
- }
240
- }
241
- ```
154
+ Where to configure:
242
155
 
243
- Start discovery at runtime:
156
+ - Runtime flags: `wingman gateway start --help`
157
+ - Environment secret: `WINGMAN_GATEWAY_TOKEN`
158
+ - Persistent config: `.wingman/wingman.config.json`
159
+ - JSON schema: `https://getwingmanai.com/schemas/wingman.config.schema.json`
244
160
 
245
- ```bash
246
- wingman gateway start --discovery tailscale --name "Work Gateway"
247
- ```
161
+ Docs (full examples):
162
+
163
+ - Gateway: `../docs-website/docs/configuration/gateway.mdx`
164
+ - Skills: `../docs-website/docs/configuration/skills.mdx`
165
+ - Full config: `../docs-website/docs/configuration/wingman-config.mdx`
248
166
 
249
167
  ## Core Concepts
250
168
 
@@ -42,6 +42,8 @@ const AvailableToolNames = external_zod_namespaceObject["enum"]([
42
42
  "browser_control",
43
43
  "command_execute",
44
44
  "background_terminal",
45
+ "node_notify",
46
+ "node_run",
45
47
  "think",
46
48
  "code_search",
47
49
  "git_status",
@@ -9,6 +9,8 @@ export declare const AvailableToolNames: z.ZodEnum<{
9
9
  browser_control: "browser_control";
10
10
  command_execute: "command_execute";
11
11
  background_terminal: "background_terminal";
12
+ node_notify: "node_notify";
13
+ node_run: "node_run";
12
14
  think: "think";
13
15
  code_search: "code_search";
14
16
  git_status: "git_status";
@@ -44,6 +46,8 @@ export declare const AgentConfigSchema: z.ZodObject<{
44
46
  browser_control: "browser_control";
45
47
  command_execute: "command_execute";
46
48
  background_terminal: "background_terminal";
49
+ node_notify: "node_notify";
50
+ node_run: "node_run";
47
51
  think: "think";
48
52
  code_search: "code_search";
49
53
  git_status: "git_status";
@@ -148,6 +152,8 @@ export declare const AgentConfigSchema: z.ZodObject<{
148
152
  browser_control: "browser_control";
149
153
  command_execute: "command_execute";
150
154
  background_terminal: "background_terminal";
155
+ node_notify: "node_notify";
156
+ node_run: "node_run";
151
157
  think: "think";
152
158
  code_search: "code_search";
153
159
  git_status: "git_status";
@@ -9,6 +9,8 @@ const AvailableToolNames = external_zod_enum([
9
9
  "browser_control",
10
10
  "command_execute",
11
11
  "background_terminal",
12
+ "node_notify",
13
+ "node_run",
12
14
  "think",
13
15
  "code_search",
14
16
  "git_status",
@@ -45,6 +45,10 @@ function _define_property(obj, key, value) {
45
45
  }
46
46
  const logger = (0, external_logger_cjs_namespaceObject.createLogger)();
47
47
  const PROMPT_REFINEMENT_MARKER = "[[wingman:prompt-refinement]]";
48
+ const ALWAYS_ON_TOOL_NAMES = [
49
+ ...external_toolRegistry_cjs_namespaceObject.UI_TOOL_NAMES,
50
+ ...external_toolRegistry_cjs_namespaceObject.NODE_TOOL_NAMES
51
+ ];
48
52
  const normalizePromptRefinementPath = (agentName, rawPath)=>{
49
53
  const fallback = `/memories/agents/${agentName}/instructions.md`;
50
54
  if (!rawPath) return fallback;
@@ -244,16 +248,23 @@ class AgentLoader {
244
248
  ...this.runtimeToolOptions
245
249
  };
246
250
  };
251
+ const addAlwaysOnTools = async (existingTools, source)=>{
252
+ const alwaysOnTools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)([
253
+ ...ALWAYS_ON_TOOL_NAMES
254
+ ], buildToolOptions(source));
255
+ if (0 === alwaysOnTools.length) return existingTools || [];
256
+ if (existingTools && existingTools.length > 0) {
257
+ const existing = new Set(existingTools.map((tool)=>tool.name));
258
+ const uniqueAlwaysOnTools = alwaysOnTools.filter((tool)=>!existing.has(tool.name));
259
+ return [
260
+ ...existingTools,
261
+ ...uniqueAlwaysOnTools
262
+ ];
263
+ }
264
+ return alwaysOnTools;
265
+ };
247
266
  if (config.tools && config.tools.length > 0) agent.tools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)(config.tools, buildToolOptions(config));
248
- const uiTools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)(external_toolRegistry_cjs_namespaceObject.UI_TOOL_NAMES, buildToolOptions(config));
249
- if (uiTools.length > 0) if (agent.tools && agent.tools.length > 0) {
250
- const existing = new Set(agent.tools.map((tool)=>tool.name));
251
- const uniqueUiTools = uiTools.filter((tool)=>!existing.has(tool.name));
252
- agent.tools = [
253
- ...agent.tools,
254
- ...uniqueUiTools
255
- ];
256
- } else agent.tools = uiTools;
267
+ agent.tools = await addAlwaysOnTools(agent.tools, config);
257
268
  if (config.mcp) agent.mcpConfig = config.mcp;
258
269
  if (config.mcpUseGlobal) agent.mcpUseGlobal = true;
259
270
  if (config.model) try {
@@ -280,15 +291,7 @@ class AgentLoader {
280
291
  sub.systemPrompt = applyPromptRefinement(sub.systemPrompt, subagent.name, subagent.promptRefinement);
281
292
  }
282
293
  if (subagent.tools && subagent.tools.length > 0) sub.tools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)(subagent.tools, buildToolOptions(subagent));
283
- const subUiTools = await (0, external_toolRegistry_cjs_namespaceObject.createTools)(external_toolRegistry_cjs_namespaceObject.UI_TOOL_NAMES, buildToolOptions(subagent));
284
- if (subUiTools.length > 0) if (sub.tools && sub.tools.length > 0) {
285
- const existing = new Set(sub.tools.map((tool)=>tool.name));
286
- const uniqueUiTools = subUiTools.filter((tool)=>!existing.has(tool.name));
287
- sub.tools = [
288
- ...sub.tools,
289
- ...uniqueUiTools
290
- ];
291
- } else sub.tools = subUiTools;
294
+ sub.tools = await addAlwaysOnTools(sub.tools, subagent);
292
295
  if (subagent.model) try {
293
296
  sub.model = external_modelFactory_cjs_namespaceObject.ModelFactory.createModel(subagent.model, {
294
297
  reasoningEffort: subagent.reasoningEffort,
@@ -4,7 +4,7 @@ import { load } from "js-yaml";
4
4
  import { createLogger } from "../../logger.js";
5
5
  import { WingmanDirectory, validateAgentConfig } from "./agentConfig.js";
6
6
  import { ModelFactory } from "./modelFactory.js";
7
- import { UI_TOOL_NAMES, createTools } from "./toolRegistry.js";
7
+ import { NODE_TOOL_NAMES, UI_TOOL_NAMES, createTools } from "./toolRegistry.js";
8
8
  function _define_property(obj, key, value) {
9
9
  if (key in obj) Object.defineProperty(obj, key, {
10
10
  value: value,
@@ -17,6 +17,10 @@ function _define_property(obj, key, value) {
17
17
  }
18
18
  const logger = createLogger();
19
19
  const PROMPT_REFINEMENT_MARKER = "[[wingman:prompt-refinement]]";
20
+ const ALWAYS_ON_TOOL_NAMES = [
21
+ ...UI_TOOL_NAMES,
22
+ ...NODE_TOOL_NAMES
23
+ ];
20
24
  const normalizePromptRefinementPath = (agentName, rawPath)=>{
21
25
  const fallback = `/memories/agents/${agentName}/instructions.md`;
22
26
  if (!rawPath) return fallback;
@@ -216,16 +220,23 @@ class AgentLoader {
216
220
  ...this.runtimeToolOptions
217
221
  };
218
222
  };
223
+ const addAlwaysOnTools = async (existingTools, source)=>{
224
+ const alwaysOnTools = await createTools([
225
+ ...ALWAYS_ON_TOOL_NAMES
226
+ ], buildToolOptions(source));
227
+ if (0 === alwaysOnTools.length) return existingTools || [];
228
+ if (existingTools && existingTools.length > 0) {
229
+ const existing = new Set(existingTools.map((tool)=>tool.name));
230
+ const uniqueAlwaysOnTools = alwaysOnTools.filter((tool)=>!existing.has(tool.name));
231
+ return [
232
+ ...existingTools,
233
+ ...uniqueAlwaysOnTools
234
+ ];
235
+ }
236
+ return alwaysOnTools;
237
+ };
219
238
  if (config.tools && config.tools.length > 0) agent.tools = await createTools(config.tools, buildToolOptions(config));
220
- const uiTools = await createTools(UI_TOOL_NAMES, buildToolOptions(config));
221
- if (uiTools.length > 0) if (agent.tools && agent.tools.length > 0) {
222
- const existing = new Set(agent.tools.map((tool)=>tool.name));
223
- const uniqueUiTools = uiTools.filter((tool)=>!existing.has(tool.name));
224
- agent.tools = [
225
- ...agent.tools,
226
- ...uniqueUiTools
227
- ];
228
- } else agent.tools = uiTools;
239
+ agent.tools = await addAlwaysOnTools(agent.tools, config);
229
240
  if (config.mcp) agent.mcpConfig = config.mcp;
230
241
  if (config.mcpUseGlobal) agent.mcpUseGlobal = true;
231
242
  if (config.model) try {
@@ -252,15 +263,7 @@ class AgentLoader {
252
263
  sub.systemPrompt = applyPromptRefinement(sub.systemPrompt, subagent.name, subagent.promptRefinement);
253
264
  }
254
265
  if (subagent.tools && subagent.tools.length > 0) sub.tools = await createTools(subagent.tools, buildToolOptions(subagent));
255
- const subUiTools = await createTools(UI_TOOL_NAMES, buildToolOptions(subagent));
256
- if (subUiTools.length > 0) if (sub.tools && sub.tools.length > 0) {
257
- const existing = new Set(sub.tools.map((tool)=>tool.name));
258
- const uniqueUiTools = subUiTools.filter((tool)=>!existing.has(tool.name));
259
- sub.tools = [
260
- ...sub.tools,
261
- ...uniqueUiTools
262
- ];
263
- } else sub.tools = subUiTools;
266
+ sub.tools = await addAlwaysOnTools(sub.tools, subagent);
264
267
  if (subagent.model) try {
265
268
  sub.model = ModelFactory.createModel(subagent.model, {
266
269
  reasoningEffort: subagent.reasoningEffort,
@@ -52,15 +52,7 @@ class MCPClientManager {
52
52
  for (const [key, value] of Object.entries(stdioServer.env || {}))resolvedEnv[key] = resolveEnvValue(value);
53
53
  const runtimeEnv = this.applyRuntimeEnv(resolvedEnv);
54
54
  const defaultToolTimeout = getDefaultToolTimeout(stdioServer);
55
- mcpServers[server.name] = {
56
- transport: "stdio",
57
- command: stdioServer.command,
58
- args: stdioServer.args || [],
59
- env: runtimeEnv,
60
- ...void 0 !== defaultToolTimeout ? {
61
- defaultToolTimeout
62
- } : {}
63
- };
55
+ mcpServers[server.name] = this.buildStdioServerConfig(stdioServer, runtimeEnv, defaultToolTimeout);
64
56
  } else if ("sse" === server.transport) {
65
57
  const sseServer = server;
66
58
  const defaultToolTimeout = getDefaultToolTimeout(sseServer);
@@ -83,6 +75,51 @@ class MCPClientManager {
83
75
  }
84
76
  };
85
77
  }
78
+ buildStdioServerConfig(server, env, defaultToolTimeout) {
79
+ const baseConfig = {
80
+ transport: "stdio",
81
+ command: server.command,
82
+ args: server.args || [],
83
+ env,
84
+ ...void 0 !== defaultToolTimeout ? {
85
+ defaultToolTimeout
86
+ } : {}
87
+ };
88
+ if (!this.proxyConfig?.enabled) return baseConfig;
89
+ const proxyCommand = this.proxyConfig.command?.trim() || "uvx";
90
+ const proxyBaseArgs = this.proxyConfig.baseArgs && this.proxyConfig.baseArgs.length > 0 ? this.proxyConfig.baseArgs : [
91
+ "invariant-gateway@latest",
92
+ "mcp"
93
+ ];
94
+ const proxyEnv = {
95
+ ...env
96
+ };
97
+ if (this.proxyConfig.apiKey) proxyEnv.INVARIANT_API_KEY = this.proxyConfig.apiKey;
98
+ if (this.proxyConfig.apiUrl) {
99
+ proxyEnv.INVARIANT_API_URL = this.proxyConfig.apiUrl;
100
+ proxyEnv.GUARDRAILS_API_URL = this.proxyConfig.apiUrl;
101
+ }
102
+ const proxyArgs = [
103
+ ...proxyBaseArgs,
104
+ "--project-name",
105
+ this.proxyConfig.projectName || "wingman-gateway",
106
+ ...this.proxyConfig.pushExplorer ? [
107
+ "--push-explorer"
108
+ ] : [],
109
+ "--exec",
110
+ baseConfig.command,
111
+ ...baseConfig.args || []
112
+ ];
113
+ return {
114
+ transport: "stdio",
115
+ command: proxyCommand,
116
+ args: proxyArgs,
117
+ env: proxyEnv,
118
+ ...void 0 !== defaultToolTimeout ? {
119
+ defaultToolTimeout
120
+ } : {}
121
+ };
122
+ }
86
123
  applyRuntimeEnv(env) {
87
124
  if (!this.executionWorkspace) return env;
88
125
  const next = {
@@ -176,9 +213,11 @@ class MCPClientManager {
176
213
  _define_property(this, "logger", void 0);
177
214
  _define_property(this, "serverConfigs", void 0);
178
215
  _define_property(this, "executionWorkspace", void 0);
216
+ _define_property(this, "proxyConfig", void 0);
179
217
  this.logger = logger;
180
218
  this.serverConfigs = this.mergeConfigs(configs);
181
219
  this.executionWorkspace = options?.executionWorkspace?.trim() || null;
220
+ this.proxyConfig = options?.proxyConfig;
182
221
  }
183
222
  }
184
223
  function resolveEnvValue(value) {
@@ -1,6 +1,15 @@
1
1
  import type { StructuredTool } from "@langchain/core/tools";
2
2
  import type { Logger } from "@/logger.js";
3
3
  import type { MCPServersConfig } from "@/types/mcp.js";
4
+ export type MCPProxyConfig = {
5
+ enabled?: boolean;
6
+ command?: string;
7
+ baseArgs?: string[];
8
+ projectName?: string;
9
+ pushExplorer?: boolean;
10
+ apiKey?: string;
11
+ apiUrl?: string;
12
+ };
4
13
  /**
5
14
  * Manages MCP server connections and tool retrieval
6
15
  * Handles server lifecycle: initialization, tool loading, and cleanup
@@ -10,8 +19,10 @@ export declare class MCPClientManager {
10
19
  private logger;
11
20
  private serverConfigs;
12
21
  private executionWorkspace;
22
+ private proxyConfig;
13
23
  constructor(configs: MCPServersConfig[], logger: Logger, options?: {
14
24
  executionWorkspace?: string | null;
25
+ proxyConfig?: MCPProxyConfig;
15
26
  });
16
27
  /**
17
28
  * Merge multiple MCP configurations (global + agent-specific)
@@ -22,6 +33,7 @@ export declare class MCPClientManager {
22
33
  * Convert Wingman MCP config to MultiServerMCPClient format
23
34
  */
24
35
  private buildClientConfig;
36
+ private buildStdioServerConfig;
25
37
  private applyRuntimeEnv;
26
38
  /**
27
39
  * Initialize MCP client and connect to servers
@@ -24,15 +24,7 @@ class MCPClientManager {
24
24
  for (const [key, value] of Object.entries(stdioServer.env || {}))resolvedEnv[key] = resolveEnvValue(value);
25
25
  const runtimeEnv = this.applyRuntimeEnv(resolvedEnv);
26
26
  const defaultToolTimeout = getDefaultToolTimeout(stdioServer);
27
- mcpServers[server.name] = {
28
- transport: "stdio",
29
- command: stdioServer.command,
30
- args: stdioServer.args || [],
31
- env: runtimeEnv,
32
- ...void 0 !== defaultToolTimeout ? {
33
- defaultToolTimeout
34
- } : {}
35
- };
27
+ mcpServers[server.name] = this.buildStdioServerConfig(stdioServer, runtimeEnv, defaultToolTimeout);
36
28
  } else if ("sse" === server.transport) {
37
29
  const sseServer = server;
38
30
  const defaultToolTimeout = getDefaultToolTimeout(sseServer);
@@ -55,6 +47,51 @@ class MCPClientManager {
55
47
  }
56
48
  };
57
49
  }
50
+ buildStdioServerConfig(server, env, defaultToolTimeout) {
51
+ const baseConfig = {
52
+ transport: "stdio",
53
+ command: server.command,
54
+ args: server.args || [],
55
+ env,
56
+ ...void 0 !== defaultToolTimeout ? {
57
+ defaultToolTimeout
58
+ } : {}
59
+ };
60
+ if (!this.proxyConfig?.enabled) return baseConfig;
61
+ const proxyCommand = this.proxyConfig.command?.trim() || "uvx";
62
+ const proxyBaseArgs = this.proxyConfig.baseArgs && this.proxyConfig.baseArgs.length > 0 ? this.proxyConfig.baseArgs : [
63
+ "invariant-gateway@latest",
64
+ "mcp"
65
+ ];
66
+ const proxyEnv = {
67
+ ...env
68
+ };
69
+ if (this.proxyConfig.apiKey) proxyEnv.INVARIANT_API_KEY = this.proxyConfig.apiKey;
70
+ if (this.proxyConfig.apiUrl) {
71
+ proxyEnv.INVARIANT_API_URL = this.proxyConfig.apiUrl;
72
+ proxyEnv.GUARDRAILS_API_URL = this.proxyConfig.apiUrl;
73
+ }
74
+ const proxyArgs = [
75
+ ...proxyBaseArgs,
76
+ "--project-name",
77
+ this.proxyConfig.projectName || "wingman-gateway",
78
+ ...this.proxyConfig.pushExplorer ? [
79
+ "--push-explorer"
80
+ ] : [],
81
+ "--exec",
82
+ baseConfig.command,
83
+ ...baseConfig.args || []
84
+ ];
85
+ return {
86
+ transport: "stdio",
87
+ command: proxyCommand,
88
+ args: proxyArgs,
89
+ env: proxyEnv,
90
+ ...void 0 !== defaultToolTimeout ? {
91
+ defaultToolTimeout
92
+ } : {}
93
+ };
94
+ }
58
95
  applyRuntimeEnv(env) {
59
96
  if (!this.executionWorkspace) return env;
60
97
  const next = {
@@ -148,9 +185,11 @@ class MCPClientManager {
148
185
  _define_property(this, "logger", void 0);
149
186
  _define_property(this, "serverConfigs", void 0);
150
187
  _define_property(this, "executionWorkspace", void 0);
188
+ _define_property(this, "proxyConfig", void 0);
151
189
  this.logger = logger;
152
190
  this.serverConfigs = this.mergeConfigs(configs);
153
191
  this.executionWorkspace = options?.executionWorkspace?.trim() || null;
192
+ this.proxyConfig = options?.proxyConfig;
154
193
  }
155
194
  }
156
195
  function resolveEnvValue(value) {
@@ -27,6 +27,7 @@ __webpack_require__.d(__webpack_exports__, {
27
27
  createTools: ()=>createTools,
28
28
  UI_TOOL_NAMES: ()=>UI_TOOL_NAMES,
29
29
  getAvailableTools: ()=>getAvailableTools,
30
+ NODE_TOOL_NAMES: ()=>NODE_TOOL_NAMES,
30
31
  createTool: ()=>createTool
31
32
  });
32
33
  const external_logger_cjs_namespaceObject = require("../../logger.cjs");
@@ -36,6 +37,7 @@ const code_search_cjs_namespaceObject = require("../tools/code_search.cjs");
36
37
  const command_execute_cjs_namespaceObject = require("../tools/command_execute.cjs");
37
38
  const git_status_cjs_namespaceObject = require("../tools/git_status.cjs");
38
39
  const internet_search_cjs_namespaceObject = require("../tools/internet_search.cjs");
40
+ const node_invoke_cjs_namespaceObject = require("../tools/node_invoke.cjs");
39
41
  const terminal_session_manager_cjs_namespaceObject = require("../tools/terminal_session_manager.cjs");
40
42
  const think_cjs_namespaceObject = require("../tools/think.cjs");
41
43
  const ui_registry_cjs_namespaceObject = require("../tools/ui_registry.cjs");
@@ -47,6 +49,10 @@ const UI_TOOL_NAMES = [
47
49
  "ui_registry_get",
48
50
  "ui_present"
49
51
  ];
52
+ const NODE_TOOL_NAMES = [
53
+ "node_notify",
54
+ "node_run"
55
+ ];
50
56
  function createTool(name, options = {}) {
51
57
  const { workspace = process.cwd(), executionWorkspace, blockedCommands, allowScriptExecution = true, timeout = 300000, terminalOwnerId = "default", terminalSessionManager = (0, terminal_session_manager_cjs_namespaceObject.getSharedTerminalSessionManager)(), searchConfig = {
52
58
  provider: "duckduckgo",
@@ -93,6 +99,16 @@ function createTool(name, options = {}) {
93
99
  allowScriptExecution,
94
100
  commandTimeout: timeout
95
101
  });
102
+ case "node_notify":
103
+ return (0, node_invoke_cjs_namespaceObject.createNodeNotifyTool)({
104
+ nodeInvoker: options.nodeInvoker,
105
+ defaultTargetClientId: options.nodeDefaultTargetClientId
106
+ });
107
+ case "node_run":
108
+ return (0, node_invoke_cjs_namespaceObject.createNodeRunTool)({
109
+ nodeInvoker: options.nodeInvoker,
110
+ defaultTargetClientId: options.nodeDefaultTargetClientId
111
+ });
96
112
  case "think":
97
113
  return (0, think_cjs_namespaceObject.createThinkingTool)();
98
114
  case "code_search":
@@ -140,17 +156,20 @@ function getAvailableTools() {
140
156
  "browser_control",
141
157
  "command_execute",
142
158
  "background_terminal",
159
+ ...NODE_TOOL_NAMES,
143
160
  "think",
144
161
  "code_search",
145
162
  "git_status",
146
163
  ...UI_TOOL_NAMES
147
164
  ];
148
165
  }
166
+ exports.NODE_TOOL_NAMES = __webpack_exports__.NODE_TOOL_NAMES;
149
167
  exports.UI_TOOL_NAMES = __webpack_exports__.UI_TOOL_NAMES;
150
168
  exports.createTool = __webpack_exports__.createTool;
151
169
  exports.createTools = __webpack_exports__.createTools;
152
170
  exports.getAvailableTools = __webpack_exports__.getAvailableTools;
153
171
  for(var __rspack_i in __webpack_exports__)if (-1 === [
172
+ "NODE_TOOL_NAMES",
154
173
  "UI_TOOL_NAMES",
155
174
  "createTool",
156
175
  "createTools",
@@ -1,6 +1,7 @@
1
1
  import type { StructuredTool } from "@langchain/core/tools";
2
2
  import type { MCPServersConfig } from "@/types/mcp.js";
3
3
  import type { SearchConfig } from "../../cli/config/schema.js";
4
+ import { type NodeInvokeRequest, type NodeInvokeResult } from "../tools/node_invoke.js";
4
5
  import { type TerminalSessionManager } from "../tools/terminal_session_manager.js";
5
6
  import type { AvailableToolName } from "./agentConfig.js";
6
7
  export interface ToolOptions {
@@ -30,8 +31,11 @@ export interface ToolOptions {
30
31
  mcpConfigs?: MCPServersConfig[];
31
32
  skillsDirectory?: string;
32
33
  dynamicUiEnabled?: boolean;
34
+ nodeInvoker?: (request: NodeInvokeRequest) => Promise<NodeInvokeResult>;
35
+ nodeDefaultTargetClientId?: string;
33
36
  }
34
37
  export declare const UI_TOOL_NAMES: AvailableToolName[];
38
+ export declare const NODE_TOOL_NAMES: AvailableToolName[];
35
39
  /**
36
40
  * Create a tool by name with optional configuration
37
41
  */
@@ -5,6 +5,7 @@ import { createCodeSearchTool } from "../tools/code_search.js";
5
5
  import { createCommandExecuteTool } from "../tools/command_execute.js";
6
6
  import { createGitStatusTool } from "../tools/git_status.js";
7
7
  import { createInternetSearchTool } from "../tools/internet_search.js";
8
+ import { createNodeNotifyTool, createNodeRunTool } from "../tools/node_invoke.js";
8
9
  import { getSharedTerminalSessionManager } from "../tools/terminal_session_manager.js";
9
10
  import { createThinkingTool } from "../tools/think.js";
10
11
  import { createUiPresentTool, createUiRegistryGetTool, createUiRegistryListTool } from "../tools/ui_registry.js";
@@ -16,6 +17,10 @@ const UI_TOOL_NAMES = [
16
17
  "ui_registry_get",
17
18
  "ui_present"
18
19
  ];
20
+ const NODE_TOOL_NAMES = [
21
+ "node_notify",
22
+ "node_run"
23
+ ];
19
24
  function createTool(name, options = {}) {
20
25
  const { workspace = process.cwd(), executionWorkspace, blockedCommands, allowScriptExecution = true, timeout = 300000, terminalOwnerId = "default", terminalSessionManager = getSharedTerminalSessionManager(), searchConfig = {
21
26
  provider: "duckduckgo",
@@ -62,6 +67,16 @@ function createTool(name, options = {}) {
62
67
  allowScriptExecution,
63
68
  commandTimeout: timeout
64
69
  });
70
+ case "node_notify":
71
+ return createNodeNotifyTool({
72
+ nodeInvoker: options.nodeInvoker,
73
+ defaultTargetClientId: options.nodeDefaultTargetClientId
74
+ });
75
+ case "node_run":
76
+ return createNodeRunTool({
77
+ nodeInvoker: options.nodeInvoker,
78
+ defaultTargetClientId: options.nodeDefaultTargetClientId
79
+ });
65
80
  case "think":
66
81
  return createThinkingTool();
67
82
  case "code_search":
@@ -109,10 +124,11 @@ function getAvailableTools() {
109
124
  "browser_control",
110
125
  "command_execute",
111
126
  "background_terminal",
127
+ ...NODE_TOOL_NAMES,
112
128
  "think",
113
129
  "code_search",
114
130
  "git_status",
115
131
  ...UI_TOOL_NAMES
116
132
  ];
117
133
  }
118
- export { UI_TOOL_NAMES, createTool, createTools, getAvailableTools };
134
+ export { NODE_TOOL_NAMES, UI_TOOL_NAMES, createTool, createTools, getAvailableTools };