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.
- package/lib/browser-mcp-server.js +32 -44
- package/lib/debate-mcp-server.js +14 -31
- package/lib/mcp-local.js +31 -1
- package/lib/project-connection.js +4 -2
- package/lib/project-filesystem.js +47 -1
- package/lib/project-http.js +75 -8
- package/lib/project-mcp.js +4 -0
- package/lib/project-sessions.js +88 -51
- package/lib/project-user-message.js +12 -7
- package/lib/project.js +204 -90
- package/lib/public/app.js +123 -448
- package/lib/public/codex-avatar.png +0 -0
- package/lib/public/css/debate.css +3 -2
- package/lib/public/css/filebrowser.css +91 -1
- package/lib/public/css/icon-strip.css +21 -5
- package/lib/public/css/input.css +181 -100
- package/lib/public/css/mates.css +43 -0
- package/lib/public/css/mention.css +48 -4
- package/lib/public/css/menus.css +1 -1
- package/lib/public/css/messages.css +2 -0
- package/lib/public/css/notifications-center.css +19 -0
- package/lib/public/index.html +46 -24
- package/lib/public/modules/app-connection.js +138 -37
- package/lib/public/modules/app-cursors.js +18 -17
- package/lib/public/modules/app-debate-ui.js +9 -9
- package/lib/public/modules/app-dm.js +170 -131
- package/lib/public/modules/app-favicon.js +28 -26
- package/lib/public/modules/app-header.js +79 -68
- package/lib/public/modules/app-home-hub.js +55 -47
- package/lib/public/modules/app-loop-ui.js +34 -18
- package/lib/public/modules/app-loop-wizard.js +6 -6
- package/lib/public/modules/app-messages.js +195 -152
- package/lib/public/modules/app-misc.js +23 -12
- package/lib/public/modules/app-notifications.js +97 -3
- package/lib/public/modules/app-panels.js +203 -49
- package/lib/public/modules/app-projects.js +159 -150
- package/lib/public/modules/app-rate-limit.js +5 -4
- package/lib/public/modules/app-rendering.js +149 -101
- package/lib/public/modules/app-skills-install.js +4 -4
- package/lib/public/modules/context-sources.js +12 -41
- package/lib/public/modules/dom-refs.js +21 -0
- package/lib/public/modules/filebrowser.js +173 -2
- package/lib/public/modules/input.js +86 -0
- package/lib/public/modules/mate-sidebar.js +38 -0
- package/lib/public/modules/mention.js +24 -6
- package/lib/public/modules/scheduler.js +1 -1
- package/lib/public/modules/sidebar-mates.js +66 -34
- package/lib/public/modules/sidebar-mobile.js +34 -30
- package/lib/public/modules/sidebar-projects.js +60 -57
- package/lib/public/modules/sidebar-sessions.js +75 -69
- package/lib/public/modules/sidebar.js +12 -20
- package/lib/public/modules/skills.js +8 -9
- package/lib/public/modules/sticky-notes.js +1 -2
- package/lib/public/modules/store.js +9 -2
- package/lib/public/modules/stt.js +4 -1
- package/lib/public/modules/tools.js +14 -9
- package/lib/sdk-bridge.js +511 -1113
- package/lib/sdk-message-processor.js +123 -134
- package/lib/sdk-worker.js +4 -0
- package/lib/server-dm.js +1 -0
- package/lib/server.js +86 -1
- package/lib/sessions.js +47 -36
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +559 -0
- package/lib/yoke/adapters/claude.js +1418 -0
- package/lib/yoke/adapters/codex.js +968 -0
- package/lib/yoke/adapters/gemini.js +668 -0
- package/lib/yoke/codex-app-server.js +307 -0
- package/lib/yoke/index.js +199 -0
- package/lib/yoke/instructions.js +62 -0
- package/lib/yoke/interface.js +92 -0
- package/lib/yoke/mcp-bridge-server.js +294 -0
- package/lib/yoke/package.json +7 -0
- 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);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clay-server",
|
|
3
|
-
"version": "2.
|
|
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",
|