clay-server 2.34.0-beta.4 → 2.34.0-beta.6

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/lib/sdk-bridge.js CHANGED
@@ -1227,11 +1227,13 @@ function createSDKBridge(opts) {
1227
1227
  }
1228
1228
 
1229
1229
  var codexConfig = getCodexConfig(sm);
1230
+ var mergedMcpServers = mergeMcpServers(getMcpServers(), getRemoteMcpServers) || undefined;
1230
1231
  var queryOpts = {
1231
1232
  cwd: cwd,
1232
1233
  model: queryModel,
1233
1234
  effort: ls.effort || sm.currentEffort || undefined,
1234
- toolServers: mergeMcpServers(getMcpServers(), getRemoteMcpServers) || undefined,
1235
+ toolServers: mergedMcpServers,
1236
+ toolServerDescriptors: extractMcpDescriptors(mergedMcpServers) || undefined,
1235
1237
  resumeSessionId: session.cliSessionId || undefined,
1236
1238
  abortController: linuxUser ? undefined : session.abortController,
1237
1239
  canUseTool: function(toolName, input, toolOpts) {
@@ -1240,6 +1242,9 @@ function createSDKBridge(opts) {
1240
1242
  onElicitation: function(request, elicitOpts) {
1241
1243
  return handleElicitation(session, request, elicitOpts);
1242
1244
  },
1245
+ callMcpTool: function(serverName, toolName, args) {
1246
+ return callMcpToolHandler(mergedMcpServers, serverName, toolName, args);
1247
+ },
1243
1248
  adapterOptions: {
1244
1249
  CLAUDE: claudeOpts,
1245
1250
  CODEX: {
@@ -31,6 +31,7 @@ var abortController = null;
31
31
  var pendingPermissions = {}; // requestId -> resolve
32
32
  var pendingAskUser = {}; // toolUseId -> resolve
33
33
  var pendingElicitations = {}; // requestId -> resolve
34
+ var pendingMcpToolCalls = {}; // requestId -> { resolve, reject }
34
35
  var conn = null;
35
36
  var buffer = "";
36
37
 
@@ -81,6 +82,90 @@ function getSDK() {
81
82
  return sdkModule;
82
83
  }
83
84
 
85
+ function buildZodShape(z, inputSchema) {
86
+ if (!inputSchema || !inputSchema.properties) return {};
87
+ var shape = {};
88
+ var props = inputSchema.properties;
89
+ var required = inputSchema.required || [];
90
+ var keys = Object.keys(props);
91
+
92
+ for (var i = 0; i < keys.length; i++) {
93
+ var key = keys[i];
94
+ var prop = props[key];
95
+ var field;
96
+
97
+ if (prop.type === "number" || prop.type === "integer") {
98
+ field = z.number();
99
+ } else if (prop.type === "boolean") {
100
+ field = z.boolean();
101
+ } else if (prop.type === "array") {
102
+ field = z.array(z.any());
103
+ } else if (prop.type === "object") {
104
+ field = z.record(z.any());
105
+ } else if (prop.enum) {
106
+ field = z.enum(prop.enum);
107
+ } else {
108
+ field = z.string();
109
+ }
110
+
111
+ if (prop.description) field = field.describe(prop.description);
112
+ if (required.indexOf(key) === -1) field = field.optional();
113
+ shape[key] = field;
114
+ }
115
+
116
+ return shape;
117
+ }
118
+
119
+ function createWorkerMcpToolHandler(serverName, toolName) {
120
+ return function(args) {
121
+ var requestId = crypto.randomUUID();
122
+ sendToDaemon({
123
+ type: "mcp_tool_call",
124
+ requestId: requestId,
125
+ serverName: serverName,
126
+ toolName: toolName,
127
+ args: args || {},
128
+ });
129
+ return new Promise(function(resolve, reject) {
130
+ pendingMcpToolCalls[requestId] = { resolve: resolve, reject: reject };
131
+ });
132
+ };
133
+ }
134
+
135
+ function buildMcpServersFromDescriptors(descriptors, sdk) {
136
+ if (!descriptors || !descriptors.length) return null;
137
+ var z;
138
+ try { z = require("zod").z; } catch (e) {
139
+ try { z = require("zod"); } catch (e2) { return null; }
140
+ }
141
+
142
+ var servers = {};
143
+ for (var i = 0; i < descriptors.length; i++) {
144
+ var descriptor = descriptors[i];
145
+ if (!descriptor || !descriptor.serverName || !descriptor.tools || !descriptor.tools.length) continue;
146
+ var tools = [];
147
+ for (var j = 0; j < descriptor.tools.length; j++) {
148
+ var toolDescriptor = descriptor.tools[j];
149
+ if (!toolDescriptor || !toolDescriptor.name) continue;
150
+ tools.push(sdk.tool(
151
+ toolDescriptor.name,
152
+ toolDescriptor.description || toolDescriptor.name,
153
+ buildZodShape(z, toolDescriptor.inputSchema),
154
+ createWorkerMcpToolHandler(descriptor.serverName, toolDescriptor.name)
155
+ ));
156
+ }
157
+ if (tools.length > 0) {
158
+ servers[descriptor.serverName] = sdk.createSdkMcpServer({
159
+ name: descriptor.serverName,
160
+ version: "1.0.0",
161
+ tools: tools,
162
+ });
163
+ }
164
+ }
165
+
166
+ return Object.keys(servers).length > 0 ? servers : null;
167
+ }
168
+
84
169
  // --- IPC helpers ---
85
170
  function sendToDaemon(msg) {
86
171
  if (!conn || conn.destroyed) return;
@@ -127,6 +212,9 @@ function handleMessage(msg) {
127
212
  case "elicitation_response":
128
213
  handleElicitationResponse(msg);
129
214
  break;
215
+ case "mcp_tool_result":
216
+ handleMcpToolResult(msg);
217
+ break;
130
218
  case "warmup":
131
219
  handleWarmup(msg);
132
220
  break;
@@ -208,6 +296,17 @@ function handleElicitationResponse(msg) {
208
296
  }
209
297
  }
210
298
 
299
+ function handleMcpToolResult(msg) {
300
+ var pending = pendingMcpToolCalls[msg.requestId];
301
+ if (!pending) return;
302
+ delete pendingMcpToolCalls[msg.requestId];
303
+ if (msg.error) {
304
+ pending.reject(new Error(msg.error));
305
+ return;
306
+ }
307
+ pending.resolve(msg.result);
308
+ }
309
+
211
310
  // --- Query handling ---
212
311
  async function handleQueryStart(msg) {
213
312
  var t0 = msg._perfT0 || Date.now();
@@ -238,6 +337,15 @@ async function handleQueryStart(msg) {
238
337
  options.abortController = abortController;
239
338
  options.debug = true;
240
339
  options.debugFile = "/tmp/clay-cli-debug-" + process.pid + ".log";
340
+ if (options.mcpServerDescriptors && options.mcpServerDescriptors.length) {
341
+ try {
342
+ var mcpServers = buildMcpServersFromDescriptors(options.mcpServerDescriptors, sdk);
343
+ if (mcpServers) options.mcpServers = mcpServers;
344
+ } catch (e) {
345
+ console.error("[sdk-worker] Failed to build MCP servers:", e.message || e);
346
+ }
347
+ delete options.mcpServerDescriptors;
348
+ }
241
349
  // Override CLI subprocess spawn to inject NODE_OPTIONS for IPv4-first DNS.
242
350
  // The SDK constructs its own env for the CLI process, so worker env vars
243
351
  // like NODE_OPTIONS are not inherited. We intercept the spawn to fix this.
@@ -600,7 +600,7 @@ function spawnWorker(linuxUser, workerScriptPath, cwd) {
600
600
  worker.send = function(msg) {
601
601
  if (!worker.connection || worker.connection.destroyed) return;
602
602
  try {
603
- worker.connection.write(JSON.stringify(msg) + "\n");
603
+ worker.connection.write(JSON.stringify(serializeWorkerValue(msg)) + "\n");
604
604
  } catch (e) {
605
605
  console.error("[yoke/claude] Failed to send to worker:", e.message);
606
606
  }
@@ -643,12 +643,45 @@ function cleanupWorker(worker) {
643
643
  worker.ready = false;
644
644
  }
645
645
 
646
+ function serializeWorkerValue(value, seen) {
647
+ if (value == null) return value;
648
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
649
+ if (typeof value === "bigint") return String(value);
650
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "undefined") return undefined;
651
+ if (value instanceof Date) return value.toISOString();
652
+ if (Buffer.isBuffer(value)) return value.toString("base64");
653
+
654
+ if (!seen) seen = new WeakSet();
655
+ if (typeof value === "object") {
656
+ if (seen.has(value)) return undefined;
657
+ seen.add(value);
658
+ }
659
+
660
+ if (Array.isArray(value)) {
661
+ var arr = [];
662
+ for (var i = 0; i < value.length; i++) {
663
+ var item = serializeWorkerValue(value[i], seen);
664
+ if (item !== undefined) arr.push(item);
665
+ }
666
+ return arr;
667
+ }
668
+
669
+ var out = {};
670
+ var keys = Object.keys(value);
671
+ for (var j = 0; j < keys.length; j++) {
672
+ var key = keys[j];
673
+ var child = serializeWorkerValue(value[key], seen);
674
+ if (child !== undefined) out[key] = child;
675
+ }
676
+ return out;
677
+ }
678
+
646
679
  // --- Worker QueryHandle ---
647
680
  // Wraps worker IPC into the same async iterable + control interface as the
648
681
  // in-process QueryHandle. This allows processQueryStream to iterate a worker
649
682
  // query identically to an in-process query.
650
683
 
651
- function createWorkerQueryHandle(worker, canUseTool, onElicitation) {
684
+ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool) {
652
685
  // Async iterable state
653
686
  var iterQueue = [];
654
687
  var iterWaiting = null;
@@ -741,6 +774,20 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation) {
741
774
  }
742
775
  break;
743
776
 
777
+ case "mcp_tool_call":
778
+ if (callMcpTool) {
779
+ callMcpTool(msg.serverName, msg.toolName, msg.args || {}).then(function(result) {
780
+ worker.send({ type: "mcp_tool_result", requestId: msg.requestId, result: result });
781
+ }).catch(function(e) {
782
+ worker.send({
783
+ type: "mcp_tool_result",
784
+ requestId: msg.requestId,
785
+ error: (e && e.message) ? e.message : String(e),
786
+ });
787
+ });
788
+ }
789
+ break;
790
+
744
791
  case "context_usage":
745
792
  case "model_changed":
746
793
  case "effort_changed":
@@ -1231,7 +1278,7 @@ function createClaudeAdapter(opts) {
1231
1278
  }
1232
1279
 
1233
1280
  // Create the worker query handle (sets up message handler on worker)
1234
- var handle = createWorkerQueryHandle(worker, queryOpts.canUseTool, queryOpts.onElicitation);
1281
+ var handle = createWorkerQueryHandle(worker, queryOpts.canUseTool, queryOpts.onElicitation, queryOpts.callMcpTool);
1235
1282
 
1236
1283
  // Wait for worker to be ready before sending query_start
1237
1284
  if (!reusingWorker) {
@@ -1254,7 +1301,7 @@ function createClaudeAdapter(opts) {
1254
1301
  if (claudeOpts.allowDangerouslySkipPermissions) queryOptions.allowDangerouslySkipPermissions = true;
1255
1302
  if (claudeOpts.settings) queryOptions.settings = claudeOpts.settings;
1256
1303
 
1257
- if (queryOpts.toolServers) queryOptions.mcpServers = queryOpts.toolServers;
1304
+ if (queryOpts.toolServerDescriptors) queryOptions.mcpServerDescriptors = queryOpts.toolServerDescriptors;
1258
1305
  if (queryOpts.model) queryOptions.model = queryOpts.model;
1259
1306
  if (queryOpts.effort) queryOptions.effort = queryOpts.effort;
1260
1307
  if (queryOpts.resumeSessionId) queryOptions.resume = queryOpts.resumeSessionId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.34.0-beta.4",
3
+ "version": "2.34.0-beta.6",
4
4
  "description": "Self-hosted Claude Code in your browser. Multi-session, multi-user, push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",