clay-server 2.31.0 → 2.32.0-beta.2

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 (74) hide show
  1. package/lib/browser-mcp-server.js +32 -44
  2. package/lib/debate-mcp-server.js +14 -31
  3. package/lib/mcp-local.js +31 -1
  4. package/lib/project-connection.js +4 -2
  5. package/lib/project-filesystem.js +47 -1
  6. package/lib/project-http.js +75 -8
  7. package/lib/project-mcp.js +4 -0
  8. package/lib/project-sessions.js +88 -51
  9. package/lib/project-user-message.js +12 -7
  10. package/lib/project.js +204 -90
  11. package/lib/public/app.js +123 -448
  12. package/lib/public/codex-avatar.png +0 -0
  13. package/lib/public/css/debate.css +3 -2
  14. package/lib/public/css/filebrowser.css +91 -1
  15. package/lib/public/css/icon-strip.css +21 -5
  16. package/lib/public/css/input.css +181 -100
  17. package/lib/public/css/mates.css +43 -0
  18. package/lib/public/css/mention.css +48 -4
  19. package/lib/public/css/menus.css +1 -1
  20. package/lib/public/css/messages.css +2 -0
  21. package/lib/public/css/notifications-center.css +19 -0
  22. package/lib/public/index.html +46 -24
  23. package/lib/public/modules/app-connection.js +138 -37
  24. package/lib/public/modules/app-cursors.js +18 -17
  25. package/lib/public/modules/app-debate-ui.js +9 -9
  26. package/lib/public/modules/app-dm.js +170 -131
  27. package/lib/public/modules/app-favicon.js +28 -26
  28. package/lib/public/modules/app-header.js +79 -68
  29. package/lib/public/modules/app-home-hub.js +55 -47
  30. package/lib/public/modules/app-loop-ui.js +34 -18
  31. package/lib/public/modules/app-loop-wizard.js +6 -6
  32. package/lib/public/modules/app-messages.js +195 -152
  33. package/lib/public/modules/app-misc.js +23 -12
  34. package/lib/public/modules/app-notifications.js +97 -3
  35. package/lib/public/modules/app-panels.js +203 -49
  36. package/lib/public/modules/app-projects.js +159 -150
  37. package/lib/public/modules/app-rate-limit.js +5 -4
  38. package/lib/public/modules/app-rendering.js +149 -101
  39. package/lib/public/modules/app-skills-install.js +4 -4
  40. package/lib/public/modules/context-sources.js +12 -41
  41. package/lib/public/modules/dom-refs.js +21 -0
  42. package/lib/public/modules/filebrowser.js +173 -2
  43. package/lib/public/modules/input.js +86 -0
  44. package/lib/public/modules/mate-sidebar.js +38 -0
  45. package/lib/public/modules/mention.js +24 -6
  46. package/lib/public/modules/scheduler.js +1 -1
  47. package/lib/public/modules/sidebar-mates.js +66 -34
  48. package/lib/public/modules/sidebar-mobile.js +34 -30
  49. package/lib/public/modules/sidebar-projects.js +60 -57
  50. package/lib/public/modules/sidebar-sessions.js +75 -69
  51. package/lib/public/modules/sidebar.js +12 -20
  52. package/lib/public/modules/skills.js +8 -9
  53. package/lib/public/modules/sticky-notes.js +1 -2
  54. package/lib/public/modules/store.js +9 -2
  55. package/lib/public/modules/stt.js +4 -1
  56. package/lib/public/modules/tools.js +14 -9
  57. package/lib/sdk-bridge.js +511 -1113
  58. package/lib/sdk-message-processor.js +123 -134
  59. package/lib/sdk-worker.js +4 -0
  60. package/lib/server-dm.js +1 -0
  61. package/lib/server.js +86 -1
  62. package/lib/sessions.js +47 -36
  63. package/lib/ws-schema.js +2 -0
  64. package/lib/yoke/adapters/claude-worker.js +559 -0
  65. package/lib/yoke/adapters/claude.js +1418 -0
  66. package/lib/yoke/adapters/codex.js +968 -0
  67. package/lib/yoke/adapters/gemini.js +668 -0
  68. package/lib/yoke/codex-app-server.js +307 -0
  69. package/lib/yoke/index.js +199 -0
  70. package/lib/yoke/instructions.js +62 -0
  71. package/lib/yoke/interface.js +92 -0
  72. package/lib/yoke/mcp-bridge-server.js +294 -0
  73. package/lib/yoke/package.json +7 -0
  74. package/package.json +3 -1
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env node
2
+ // mcp-bridge-server.js - Stdio MCP bridge for Codex
3
+ // --------------------------------------------------
4
+ // Codex spawns this as a native MCP server via config.mcp_servers["clay-tools"].
5
+ // It implements the MCP protocol on stdio (JSON-RPC) and proxies tool
6
+ // list/call requests to Clay's HTTP endpoint (/api/mcp-bridge).
7
+ //
8
+ // Usage: node mcp-bridge-server.js --port 2633 --slug my-project
9
+ //
10
+ // Lifecycle:
11
+ // 1. Codex spawns this process
12
+ // 2. Codex sends MCP "initialize" request on stdin
13
+ // 3. We respond, then fetch tool list from Clay
14
+ // 4. Codex sends "tools/list" -> we return cached tools
15
+ // 5. Codex sends "tools/call" -> we proxy to Clay and return result
16
+ // 6. When stdin closes (Codex exits), we exit too
17
+
18
+ var http = require("http");
19
+ var https = require("https");
20
+
21
+ // --- Parse CLI args ---
22
+ var args = process.argv.slice(2);
23
+ var clayPort = 2633;
24
+ var claySlug = "";
25
+ var clayTls = false;
26
+
27
+ for (var i = 0; i < args.length; i++) {
28
+ if (args[i] === "--port" && args[i + 1]) {
29
+ clayPort = parseInt(args[i + 1], 10) || 2633;
30
+ i++;
31
+ } else if (args[i] === "--slug" && args[i + 1]) {
32
+ claySlug = args[i + 1];
33
+ i++;
34
+ } else if (args[i] === "--tls") {
35
+ clayTls = true;
36
+ }
37
+ }
38
+
39
+ var CLAY_PROTOCOL = clayTls ? "https" : "http";
40
+ // Use global endpoint (not project-scoped) so bridge works regardless of which project was active at init
41
+ var CLAY_BASE_URL = CLAY_PROTOCOL + "://127.0.0.1:" + clayPort;
42
+
43
+ // --- Auth ---
44
+ var clayAuthToken = process.env.CLAY_AUTH_TOKEN || "";
45
+
46
+ // --- Tool cache ---
47
+ var _tools = []; // [{ server, name, description, inputSchema }]
48
+ var _toolsFetched = false;
49
+
50
+ // --- HTTP helper ---
51
+ function postJson(urlPath, body) {
52
+ return new Promise(function (resolve, reject) {
53
+ var data = JSON.stringify(body);
54
+ var url = CLAY_BASE_URL + urlPath;
55
+ var parsed = new URL(url);
56
+ var mod = parsed.protocol === "https:" ? https : http;
57
+
58
+ var headers = {
59
+ "Content-Type": "application/json",
60
+ "Content-Length": Buffer.byteLength(data),
61
+ };
62
+ // Include auth cookie if available (required for authenticated Clay servers)
63
+ if (clayAuthToken) {
64
+ headers["Cookie"] = "relay_auth=" + clayAuthToken;
65
+ }
66
+
67
+ var reqOpts = {
68
+ hostname: parsed.hostname,
69
+ port: parsed.port,
70
+ path: parsed.pathname + parsed.search,
71
+ method: "POST",
72
+ headers: headers,
73
+ // Skip TLS verification for localhost self-signed certs
74
+ rejectUnauthorized: false,
75
+ };
76
+
77
+ var req = mod.request(reqOpts, function (res) {
78
+ var chunks = [];
79
+ res.on("data", function (chunk) { chunks.push(chunk); });
80
+ res.on("end", function () {
81
+ var respBody = Buffer.concat(chunks).toString("utf8");
82
+ try {
83
+ resolve(JSON.parse(respBody));
84
+ } catch (e) {
85
+ reject(new Error("Invalid JSON response: " + respBody.substring(0, 200)));
86
+ }
87
+ });
88
+ });
89
+
90
+ req.on("error", function (err) {
91
+ reject(err);
92
+ });
93
+
94
+ // 30s timeout for tool calls
95
+ req.setTimeout(30000, function () {
96
+ req.destroy(new Error("Request timed out"));
97
+ });
98
+
99
+ req.write(data);
100
+ req.end();
101
+ });
102
+ }
103
+
104
+ // --- Fetch tools from Clay ---
105
+ function fetchTools() {
106
+ return postJson("/api/mcp-bridge", { action: "list_tools" }).then(function (resp) {
107
+ if (resp.error) {
108
+ log("Failed to fetch tools: " + resp.error);
109
+ return [];
110
+ }
111
+ _tools = resp.tools || [];
112
+ _toolsFetched = true;
113
+ var serverNames = {};
114
+ for (var i = 0; i < _tools.length; i++) {
115
+ serverNames[_tools[i].server] = (serverNames[_tools[i].server] || 0) + 1;
116
+ }
117
+ log("Fetched " + _tools.length + " tools from Clay: " + Object.keys(serverNames).map(function(s) { return s + "(" + serverNames[s] + ")"; }).join(", "));
118
+ return _tools;
119
+ }).catch(function (err) {
120
+ log("Error fetching tools: " + err.message);
121
+ return [];
122
+ });
123
+ }
124
+
125
+ // --- Call a tool via Clay ---
126
+ function callTool(serverName, toolName, args) {
127
+ return postJson("/api/mcp-bridge", {
128
+ action: "call_tool",
129
+ server: serverName,
130
+ tool: toolName,
131
+ args: args || {},
132
+ });
133
+ }
134
+
135
+ // --- JSON-RPC stdio protocol ---
136
+ var _inputBuffer = "";
137
+
138
+ function sendJsonRpc(obj) {
139
+ var line = JSON.stringify(obj) + "\n";
140
+ try {
141
+ process.stdout.write(line);
142
+ } catch (e) {
143
+ // stdout closed, exit
144
+ process.exit(0);
145
+ }
146
+ }
147
+
148
+ function sendResult(id, result) {
149
+ sendJsonRpc({ jsonrpc: "2.0", id: id, result: result });
150
+ }
151
+
152
+ function sendError(id, code, message) {
153
+ sendJsonRpc({ jsonrpc: "2.0", id: id, error: { code: code, message: message } });
154
+ }
155
+
156
+ function log(msg) {
157
+ try {
158
+ process.stderr.write("[mcp-bridge] " + msg + "\n");
159
+ } catch (e) {}
160
+ }
161
+
162
+ // --- MCP message handlers ---
163
+
164
+ function handleInitialize(id, params) {
165
+ sendResult(id, {
166
+ protocolVersion: "2024-11-05",
167
+ capabilities: {
168
+ tools: {},
169
+ },
170
+ serverInfo: {
171
+ name: "clay-tools",
172
+ version: "1.0.0",
173
+ },
174
+ });
175
+
176
+ // Pre-fetch tools after handshake
177
+ fetchTools();
178
+ }
179
+
180
+ function handleToolsList(id, params) {
181
+ function respond() {
182
+ var mcpTools = _tools.map(function (t) {
183
+ return {
184
+ name: t.server + "__" + t.name,
185
+ description: "[REMOTE MCP: " + t.server + "] " + (t.description || t.name)
186
+ + " (This tool runs on a REMOTE machine connected via browser extension, NOT on the local server.)",
187
+ inputSchema: t.inputSchema || { type: "object", properties: {} },
188
+ };
189
+ });
190
+ sendResult(id, { tools: mcpTools });
191
+ }
192
+
193
+ // Always re-fetch: remote MCP servers may connect after initial fetch
194
+ fetchTools().then(respond);
195
+ }
196
+
197
+ function handleToolsCall(id, params) {
198
+ var fullName = params.name || "";
199
+ var toolArgs = params.arguments || {};
200
+
201
+ // Parse "server__toolName" format
202
+ var sepIdx = fullName.indexOf("__");
203
+ if (sepIdx === -1) {
204
+ sendError(id, -32602, "Invalid tool name format. Expected 'server__tool': " + fullName);
205
+ return;
206
+ }
207
+
208
+ var serverName = fullName.substring(0, sepIdx);
209
+ var toolName = fullName.substring(sepIdx + 2);
210
+
211
+ callTool(serverName, toolName, toolArgs).then(function (resp) {
212
+ if (resp.error) {
213
+ sendResult(id, {
214
+ content: [{ type: "text", text: "Error: " + resp.error }],
215
+ isError: true,
216
+ });
217
+ } else {
218
+ var result = resp.result;
219
+ // Normalize result to MCP content format
220
+ if (result && result.content) {
221
+ sendResult(id, result);
222
+ } else {
223
+ sendResult(id, {
224
+ content: [{ type: "text", text: typeof result === "string" ? result : JSON.stringify(result) }],
225
+ });
226
+ }
227
+ }
228
+ }).catch(function (err) {
229
+ sendResult(id, {
230
+ content: [{ type: "text", text: "Bridge error: " + (err.message || String(err)) }],
231
+ isError: true,
232
+ });
233
+ });
234
+ }
235
+
236
+ function handleMessage(msg) {
237
+ if (!msg.method) {
238
+ // Response or notification we don't handle
239
+ return;
240
+ }
241
+
242
+ var method = msg.method;
243
+ var id = msg.id;
244
+
245
+ if (method === "initialize") {
246
+ handleInitialize(id, msg.params || {});
247
+ } else if (method === "notifications/initialized") {
248
+ // Client acknowledged init, nothing to do
249
+ } else if (method === "tools/list") {
250
+ handleToolsList(id, msg.params || {});
251
+ } else if (method === "tools/call") {
252
+ handleToolsCall(id, msg.params || {});
253
+ } else if (method === "ping") {
254
+ sendResult(id, {});
255
+ } else {
256
+ if (id !== undefined) {
257
+ sendError(id, -32601, "Method not found: " + method);
258
+ }
259
+ }
260
+ }
261
+
262
+ // --- Stdin reader ---
263
+ process.stdin.setEncoding("utf8");
264
+ process.stdin.on("data", function (chunk) {
265
+ _inputBuffer += chunk;
266
+ var lines = _inputBuffer.split("\n");
267
+ _inputBuffer = lines.pop();
268
+ for (var i = 0; i < lines.length; i++) {
269
+ var line = lines[i].trim();
270
+ if (!line) continue;
271
+ try {
272
+ var msg = JSON.parse(line);
273
+ handleMessage(msg);
274
+ } catch (e) {
275
+ log("Failed to parse JSON: " + e.message);
276
+ }
277
+ }
278
+ });
279
+
280
+ // Exit when stdin closes (Codex process exited)
281
+ process.stdin.on("end", function () {
282
+ log("stdin closed, exiting");
283
+ process.exit(0);
284
+ });
285
+
286
+ process.stdin.on("error", function () {
287
+ process.exit(0);
288
+ });
289
+
290
+ // Graceful shutdown
291
+ process.on("SIGTERM", function () { process.exit(0); });
292
+ process.on("SIGINT", function () { process.exit(0); });
293
+
294
+ log("Started: port=" + clayPort + " slug=" + claySlug);
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "yoke",
3
+ "version": "0.1.0",
4
+ "description": "YOKE - Yoke Overrides Known Engines. Vendor-independent harness abstraction for Clay.",
5
+ "main": "index.js",
6
+ "license": "MIT"
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.31.0",
3
+ "version": "2.32.0-beta.2",
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",
@@ -37,7 +37,9 @@
37
37
  "author": "Chad",
38
38
  "dependencies": {
39
39
  "@anthropic-ai/claude-agent-sdk": "^0.2.112",
40
+ "@google/genai": "^1.49.0",
40
41
  "@lydell/node-pty": "^1.2.0-beta.3",
42
+ "@openai/codex": "^0.121.0",
41
43
  "imapflow": "^1.3.1",
42
44
  "nodemailer": "^6.10.1",
43
45
  "qrcode-terminal": "^0.12.0",