clay-server 4.8.2 → 5.0.0-beta.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.
- package/LICENSES/README.md +13 -0
- package/LICENSES/open-iconic.txt +21 -0
- package/LICENSES/plantuml-salt.txt +674 -0
- package/lib/assets/salt-assets.json +15 -0
- package/lib/assets/salt-colors.json +156 -0
- package/lib/assets/salt-font-10.bin.gz +0 -0
- package/lib/assets/salt-font-13.bin.gz +0 -0
- package/lib/assets/salt-font-14.bin.gz +0 -0
- package/lib/assets/salt-font-16.bin.gz +0 -0
- package/lib/assets/salt-font-18.bin.gz +0 -0
- package/lib/assets/salt-font-20.bin.gz +0 -0
- package/lib/assets/salt-font-metrics.bin.gz +0 -0
- package/lib/assets/salt-font-metrics.json +101 -0
- package/lib/assets/salt-font-monospace-10.bin.gz +0 -0
- package/lib/assets/salt-font-monospace-13.bin.gz +0 -0
- package/lib/assets/salt-font-monospace-14.bin.gz +0 -0
- package/lib/assets/salt-font-monospace-16.bin.gz +0 -0
- package/lib/assets/salt-font-monospace-18.bin.gz +0 -0
- package/lib/assets/salt-font-monospace-20.bin.gz +0 -0
- package/lib/assets/salt-font-monospace.bin.gz +0 -0
- package/lib/assets/salt-icons.json +671 -0
- package/lib/bundled-skills/clay-sketch/SKILL.md +198 -0
- package/lib/mcp-browser-proxy.js +37 -0
- package/lib/mcp-connection-store.js +39 -0
- package/lib/mcp-network.js +46 -0
- package/lib/mcp-oauth.js +65 -0
- package/lib/mcp-proxy.js +32 -0
- package/lib/mcp-remote-connections.js +215 -0
- package/lib/mcp-session-access.js +15 -0
- package/lib/plantuml-renderer.js +43 -0
- package/lib/project-file-http.js +2 -0
- package/lib/project-mcp.js +130 -357
- package/lib/project-plantuml.js +48 -0
- package/lib/project-session-document.js +17 -1
- package/lib/project-user-message.js +2 -0
- package/lib/project-wireframe-http.js +88 -0
- package/lib/project.js +28 -45
- package/lib/public/app.js +7 -0
- package/lib/public/css/filebrowser.css +5 -0
- package/lib/public/css/mcp-connections.css +86 -0
- package/lib/public/css/skills-workbench.css +74 -0
- package/lib/public/css/wireframe.css +95 -0
- package/lib/public/index.html +16 -15
- package/lib/public/modules/app-messages.js +10 -2
- package/lib/public/modules/filebrowser.js +29 -3
- package/lib/public/modules/markdown.js +3 -1
- package/lib/public/modules/mcp-connection-view.js +136 -0
- package/lib/public/modules/mcp-permissions.js +122 -0
- package/lib/public/modules/mcp-ui.js +85 -461
- package/lib/public/modules/plantuml-preview.js +76 -0
- package/lib/public/modules/skills-actions.js +47 -79
- package/lib/public/modules/skills-detail.js +32 -145
- package/lib/public/modules/skills-list-view.js +29 -0
- package/lib/public/modules/skills-state.js +4 -2
- package/lib/public/modules/skills.js +104 -399
- package/lib/public/modules/split-view.js +14 -0
- package/lib/public/modules/vendor-login-browser.js +16 -5
- package/lib/public/modules/wireframe-chat.js +52 -0
- package/lib/public/modules/wireframe-render.js +69 -0
- package/lib/public/modules/wireframe-style.js +6 -0
- package/lib/public/modules/wireframe-viewport.js +70 -0
- package/lib/public/modules/wireframe-workbench.js +116 -0
- package/lib/public/style.css +3 -0
- package/lib/salt-chrome.js +36 -0
- package/lib/salt-hand.js +37 -0
- package/lib/salt-images.js +87 -0
- package/lib/salt-layout.js +60 -0
- package/lib/salt-options.js +23 -0
- package/lib/salt-overlays.js +45 -0
- package/lib/salt-parser.js +153 -0
- package/lib/salt-screen.js +111 -0
- package/lib/salt-svg-shapes.js +80 -0
- package/lib/salt-svg.js +164 -0
- package/lib/salt-text.js +112 -0
- package/lib/salt-tree.js +45 -0
- package/lib/sdk-bridge.js +5 -17
- package/lib/sdk-mcp-descriptors.js +2 -1
- package/lib/session-document-mcp-server.js +9 -0
- package/lib/vendor-login-browser-worker.js +18 -8
- package/lib/vendor-login-browser.js +10 -3
- package/lib/yoke/adapters/codex.js +1 -18
- package/package.json +6 -2
package/lib/salt-tree.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Adapted from PlantUML Salt ElementTree, GPL-3.0-or-later; see LICENSES/plantuml-salt.txt.
|
|
2
|
+
function measure(node) {
|
|
3
|
+
node.columns = [];
|
|
4
|
+
node.heights = [];
|
|
5
|
+
node.rows.forEach(function (row) {
|
|
6
|
+
row.forEach(function (item, c) { node.columns[c] = Math.max(node.columns[c] || 0, item.width + (c ? 0 : (item.level || 0) * 10)); });
|
|
7
|
+
node.heights.push(Math.max(0, ...row.map(function (item) { return item.height; })));
|
|
8
|
+
});
|
|
9
|
+
node.width = node.columns.reduce(function (a, b) { return a + b; }, 0) + Math.max(0, node.columns.length - 1) * 10 + 2;
|
|
10
|
+
node.height = node.heights.reduce(function (a, b) { return a + b; }, 0);
|
|
11
|
+
}
|
|
12
|
+
function draw(node, x, y, s, drawChild) {
|
|
13
|
+
var cy = y;
|
|
14
|
+
var skeleton = [];
|
|
15
|
+
var rows = [y];
|
|
16
|
+
var cols = [x, x + (node.columns[0] || 0) + 5];
|
|
17
|
+
node.columns.slice(1).forEach(function (width) { cols.push(cols[cols.length - 1] + width + 10); });
|
|
18
|
+
node.rows.forEach(function (row, r) {
|
|
19
|
+
var cx = x;
|
|
20
|
+
row.forEach(function (item, c) {
|
|
21
|
+
drawChild(item, c ? cx : x + (item.level || 0) * 10, cy, item.width, item.height);
|
|
22
|
+
cx += node.columns[c] + 10;
|
|
23
|
+
});
|
|
24
|
+
skeleton.push({ x: x + (row[0].level || 0) * 10 - 7, y: cy + node.heights[r] / 2 - 1 });
|
|
25
|
+
cy += node.heights[r]; rows.push(cy);
|
|
26
|
+
});
|
|
27
|
+
skeleton.forEach(function (item, i) {
|
|
28
|
+
if (skeleton[i + 1] && skeleton[i + 1].x > item.x) s.rect(item.x, item.y, 2, 2, 'none', '#888');
|
|
29
|
+
var parent;
|
|
30
|
+
for (var j = 0; j < i; j++) if (skeleton[j].x < item.x) parent = skeleton[j];
|
|
31
|
+
if (parent) {
|
|
32
|
+
s.line(parent.x + 1, parent.y + 3, parent.x + 1, item.y + 1, '#888');
|
|
33
|
+
s.line(parent.x + 1, item.y + 1, item.x - 1, item.y + 1, '#888');
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
var mode = node.style.slice(1);
|
|
37
|
+
var right = cols[cols.length - 1];
|
|
38
|
+
if (mode === '+') {
|
|
39
|
+
s.line(x, y, right, y, '#888'); s.line(x, cy, right, cy, '#888');
|
|
40
|
+
s.line(x, y, x, cy, '#888'); s.line(right, y, right, cy, '#888');
|
|
41
|
+
}
|
|
42
|
+
if (/[#-]/.test(mode)) rows.forEach(function (yy) { s.line(x, yy, right, yy, '#888'); });
|
|
43
|
+
if (/[#!]/.test(mode)) cols.forEach(function (xx) { s.line(xx, y, xx, cy, '#888'); });
|
|
44
|
+
}
|
|
45
|
+
module.exports = { measure: measure, draw: draw };
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -44,7 +44,7 @@ function callMcpToolHandler(mcpServers, serverName, toolName, args) {
|
|
|
44
44
|
|
|
45
45
|
// Merge in-process MCP servers with remote (extension-bridged) MCP servers.
|
|
46
46
|
// Returns the merged object, or null if no servers exist.
|
|
47
|
-
function mergeMcpServers(localServers, getRemoteFn) {
|
|
47
|
+
function mergeMcpServers(localServers, getRemoteFn, session) {
|
|
48
48
|
var merged = {};
|
|
49
49
|
var hasAny = false;
|
|
50
50
|
if (localServers) {
|
|
@@ -58,7 +58,7 @@ function mergeMcpServers(localServers, getRemoteFn) {
|
|
|
58
58
|
console.log("[mergeMcpServers] local servers: null");
|
|
59
59
|
}
|
|
60
60
|
if (typeof getRemoteFn === "function") {
|
|
61
|
-
var remote = getRemoteFn();
|
|
61
|
+
var remote = getRemoteFn(session);
|
|
62
62
|
if (remote) {
|
|
63
63
|
var rk = Object.keys(remote);
|
|
64
64
|
console.log("[mergeMcpServers] remote servers:", rk.join(", ") || "(none)");
|
|
@@ -713,7 +713,8 @@ function createSDKBridge(opts) {
|
|
|
713
713
|
// This tool only prepares a read-only UI projection for the document the
|
|
714
714
|
// agent is about to edit. The subsequent Edit/Write keeps its own normal
|
|
715
715
|
// permission behavior.
|
|
716
|
-
if (toolName
|
|
716
|
+
if (toolName === "mcp__clay-documents__present_wireframe" || toolName === "clay-documents__present_wireframe" ||
|
|
717
|
+
toolName.indexOf("mcp__clay-documents__present_markdown_edit") === 0 ||
|
|
717
718
|
toolName.indexOf("clay-documents__present_markdown_edit") !== -1) {
|
|
718
719
|
return { behavior: "allow", updatedInput: input };
|
|
719
720
|
}
|
|
@@ -751,19 +752,6 @@ function createSDKBridge(opts) {
|
|
|
751
752
|
return { behavior: "allow", updatedInput: input };
|
|
752
753
|
}
|
|
753
754
|
|
|
754
|
-
// Auto-approve remote MCP tools that the user explicitly enabled in project settings.
|
|
755
|
-
// These are user-owned local MCP servers, so no additional permission prompt needed.
|
|
756
|
-
if (toolName.indexOf("mcp__") === 0 && getRemoteMcpServers) {
|
|
757
|
-
var _rmcp = getRemoteMcpServers();
|
|
758
|
-
if (_rmcp) {
|
|
759
|
-
var _mcpParts = toolName.split("__");
|
|
760
|
-
var _mcpServerName = _mcpParts.length >= 2 ? _mcpParts[1] : "";
|
|
761
|
-
if (_rmcp[_mcpServerName]) {
|
|
762
|
-
return { behavior: "allow", updatedInput: input };
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
755
|
// Auto-approve safe Bash commands (read-only, non-destructive)
|
|
768
756
|
// Applies to ALL sessions (mates and regular projects alike).
|
|
769
757
|
// These are purely read-only commands that cannot modify files, install
|
|
@@ -1896,7 +1884,7 @@ function createSDKBridge(opts) {
|
|
|
1896
1884
|
localMcpServers[userInputServer.name || "yoke-user-input"] = userInputServer;
|
|
1897
1885
|
}
|
|
1898
1886
|
}
|
|
1899
|
-
var mergedMcpServers = mergeMcpServers(localMcpServers, getRemoteMcpServers) || undefined;
|
|
1887
|
+
var mergedMcpServers = mergeMcpServers(localMcpServers, getRemoteMcpServers, session) || undefined;
|
|
1900
1888
|
if (selectedUserInputMode === "fallback") {
|
|
1901
1889
|
sessionToolDefs = sessionToolDefs.concat(yoke.userInput.fallbackToolDefs(sessionUserInputHandler, { maxQuestions: scheduleQuestionLimit }));
|
|
1902
1890
|
}
|
|
@@ -18,7 +18,8 @@ function extractMcpDescriptors(mcpServers) {
|
|
|
18
18
|
var toolDef = server.instance._registeredTools[toolName];
|
|
19
19
|
var inputSchema = { type: "object", properties: {} };
|
|
20
20
|
try {
|
|
21
|
-
if (toolDef.
|
|
21
|
+
if (toolDef._clayInputSchema) inputSchema = toolDef._clayInputSchema;
|
|
22
|
+
else if (toolDef.inputSchema) inputSchema = toJSONSchema(toolDef.inputSchema);
|
|
22
23
|
} catch (e) {
|
|
23
24
|
throw new Error("Cannot serialize MCP tool schema " + serverName + "/" + toolName + ": " + e.message);
|
|
24
25
|
}
|
|
@@ -10,6 +10,15 @@ function getToolDefs(handlers) {
|
|
|
10
10
|
path: { type: "string", description: "Project-relative or absolute path to the Markdown document that will be edited." },
|
|
11
11
|
}, ["path"]),
|
|
12
12
|
handler: function (args) { return handlers.present(args || {}); },
|
|
13
|
+
}, {
|
|
14
|
+
name: "present_wireframe",
|
|
15
|
+
description: "Open or update the temporary clay-sketch viewer in the right panel when the user wants to revise or discuss a wireframe. Supports nested layouts and common UI controls, not general UML or preprocessor directives. Supply complete Salt diagram source, a stable id reused across revisions, and a short title. This displays only; it never saves or edits files. Include the same source in a fenced clay-sketch response so the diagram remains in chat history.",
|
|
16
|
+
inputSchema: buildShape({
|
|
17
|
+
source: { type: "string", description: "Complete @startsalt/@endsalt source, or @startuml with a salt body (100 KB maximum)." },
|
|
18
|
+
id: { type: "string", description: "Stable design id, using letters, digits, hyphens or underscores (80 characters maximum)." },
|
|
19
|
+
title: { type: "string", description: "Short title for the viewer." },
|
|
20
|
+
}, ["source", "id"]),
|
|
21
|
+
handler: function (args) { return handlers.presentWireframe(args || {}); },
|
|
13
22
|
}];
|
|
14
23
|
}
|
|
15
24
|
|
|
@@ -3,6 +3,7 @@ var readline = require("readline");
|
|
|
3
3
|
var spawn = require("child_process").spawn;
|
|
4
4
|
var chromium = require("playwright").chromium;
|
|
5
5
|
var browser = null;
|
|
6
|
+
var loginProxy = null;
|
|
6
7
|
var page = null;
|
|
7
8
|
var installing = null;
|
|
8
9
|
var viewing = true;
|
|
@@ -22,7 +23,7 @@ async function launch() {
|
|
|
22
23
|
var channels = ["chrome", "msedge", null];
|
|
23
24
|
for (var i = 0; i < channels.length; i++) {
|
|
24
25
|
try {
|
|
25
|
-
return await chromium.launch({ channel: channels[i] || undefined, headless: true, chromiumSandbox: true, timeout: 20000 });
|
|
26
|
+
return await chromium.launch({ channel: channels[i] || undefined, headless: true, chromiumSandbox: true, timeout: 20000, proxy: loginProxy ? { server: loginProxy.server } : undefined, args: loginProxy ? ["--force-webrtc-ip-handling-policy=disable_non_proxied_udp"] : [] });
|
|
26
27
|
} catch (e) {
|
|
27
28
|
if (closing) throw e;
|
|
28
29
|
}
|
|
@@ -39,7 +40,7 @@ async function launch() {
|
|
|
39
40
|
});
|
|
40
41
|
});
|
|
41
42
|
if (closing) throw new Error("Closed");
|
|
42
|
-
return chromium.launch({ headless: true, chromiumSandbox: true, timeout: 20000 });
|
|
43
|
+
return chromium.launch({ headless: true, chromiumSandbox: true, timeout: 20000, proxy: loginProxy ? { server: loginProxy.server } : undefined, args: loginProxy ? ["--force-webrtc-ip-handling-policy=disable_non_proxied_udp"] : [] });
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
async function capture() {
|
|
@@ -55,7 +56,8 @@ async function capture() {
|
|
|
55
56
|
if (!closing) captureTimer = setTimeout(capture, 250);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
async function start(url) {
|
|
59
|
+
async function start(url, restrictedNetwork) {
|
|
60
|
+
if (restrictedNetwork) loginProxy = await require("./mcp-browser-proxy").createBrowserProxy();
|
|
59
61
|
browser = await launch();
|
|
60
62
|
if (closing) { await browser.close(); return; }
|
|
61
63
|
var session = await browser.newBrowserCDPSession();
|
|
@@ -64,12 +66,19 @@ async function start(url) {
|
|
|
64
66
|
finally { await session.detach(); }
|
|
65
67
|
// Preserve the native version/platform and replace only the headless product token.
|
|
66
68
|
var userAgent = version.userAgent.replace("HeadlessChrome/", "Chrome/");
|
|
67
|
-
var context = await browser.newContext({ viewport: viewport, acceptDownloads: false, userAgent: userAgent });
|
|
69
|
+
var context = await browser.newContext({ viewport: viewport, acceptDownloads: false, serviceWorkers: "block", userAgent: userAgent });
|
|
70
|
+
if (restrictedNetwork) await context.routeWebSocket("**/*", function (socket) { socket.close(); });
|
|
68
71
|
await context.addInitScript(require("./vendor-login-browser-passkeys").disableLoginPasskeys);
|
|
69
72
|
var callback = new URL(new URL(url).searchParams.get("redirect_uri"));
|
|
70
|
-
await context.route("**/*", function (route) {
|
|
73
|
+
await context.route("**/*", async function (route) {
|
|
71
74
|
var target = new URL(route.request().url());
|
|
72
|
-
if (target.
|
|
75
|
+
if (target.origin === callback.origin && target.pathname === callback.pathname) return route.continue();
|
|
76
|
+
if (target.protocol === "https:") {
|
|
77
|
+
if (restrictedNetwork) {
|
|
78
|
+
try { await require("./mcp-network").assertPublicUrl(target.href); } catch (error) { return route.abort(); }
|
|
79
|
+
}
|
|
80
|
+
return route.continue();
|
|
81
|
+
}
|
|
73
82
|
return route.abort();
|
|
74
83
|
});
|
|
75
84
|
context.on("page", function (opened) {
|
|
@@ -120,6 +129,7 @@ async function close() {
|
|
|
120
129
|
clearTimeout(captureTimer);
|
|
121
130
|
if (installing) installing.kill();
|
|
122
131
|
if (browser) await browser.close().catch(function () {});
|
|
132
|
+
if (loginProxy) loginProxy.close();
|
|
123
133
|
process.exit(0);
|
|
124
134
|
}
|
|
125
135
|
|
|
@@ -131,8 +141,8 @@ lines.on("line", function (line) {
|
|
|
131
141
|
try { message = JSON.parse(line); } catch (e) { return; }
|
|
132
142
|
if (message.type === "start" && !started) {
|
|
133
143
|
started = true;
|
|
134
|
-
start(message.url).catch(function () {
|
|
135
|
-
status("Unable to open the sign-in browser. The host may need browser libraries or sandbox support. Use terminal sign-in to continue.", "error");
|
|
144
|
+
start(message.url, message.restrictedNetwork).catch(function () {
|
|
145
|
+
status("Unable to open the sign-in browser. The host may need browser libraries or sandbox support." + (message.restrictedNetwork ? " Retry or use an access token." : " Use terminal sign-in to continue."), "error");
|
|
136
146
|
});
|
|
137
147
|
} else if (message.type === "viewing") {
|
|
138
148
|
viewing = !!message.value;
|
|
@@ -20,6 +20,12 @@ function loginUrl(value, vendor) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function createLoginBrowser(options) {
|
|
23
|
+
var initial = null;
|
|
24
|
+
if (options.initialUrl) {
|
|
25
|
+
initial = new URL(options.initialUrl);
|
|
26
|
+
var callback = new URL(initial.searchParams.get("redirect_uri"));
|
|
27
|
+
if (options.vendor !== "mcp" || !options.restrictedNetwork || initial.protocol !== "https:" || initial.username || initial.password || !initial.searchParams.get("state") || !initial.searchParams.get("code_challenge") || callback.protocol !== "http:" || callback.hostname !== "127.0.0.1" || callback.pathname !== "/callback") throw new Error("Invalid MCP sign-in URL");
|
|
28
|
+
}
|
|
23
29
|
var id = crypto.randomUUID();
|
|
24
30
|
var dir = fs.mkdtempSync(path.join(os.tmpdir(), "clay-login-"));
|
|
25
31
|
fs.chmodSync(dir, 0o700);
|
|
@@ -98,17 +104,17 @@ function createLoginBrowser(options) {
|
|
|
98
104
|
});
|
|
99
105
|
child.on("error", function () {
|
|
100
106
|
statusPhase = "error";
|
|
101
|
-
status = "The sign-in browser could not start. Use terminal sign-in to continue.";
|
|
107
|
+
status = "The sign-in browser could not start." + (options.vendor === "mcp" ? " Retry or use an access token." : " Use terminal sign-in to continue.");
|
|
102
108
|
send({ type: "vendor_login_browser_status", text: status, phase: statusPhase });
|
|
103
109
|
});
|
|
104
110
|
child.on("exit", function () {
|
|
105
111
|
if (closed) return;
|
|
106
112
|
statusPhase = "error";
|
|
107
|
-
status = "The sign-in browser closed. You can retry
|
|
113
|
+
status = "The sign-in browser closed. You can retry.";
|
|
108
114
|
send({ type: "vendor_login_browser_status", text: status, phase: statusPhase });
|
|
109
115
|
});
|
|
110
116
|
child.stdin.on("error", function () {});
|
|
111
|
-
write({ type: "start", url: url });
|
|
117
|
+
write({ type: "start", url: url, restrictedNetwork: !!options.restrictedNetwork });
|
|
112
118
|
}
|
|
113
119
|
function acceptUrl(value) {
|
|
114
120
|
var url = loginUrl(value, options.vendor);
|
|
@@ -123,6 +129,7 @@ function createLoginBrowser(options) {
|
|
|
123
129
|
});
|
|
124
130
|
}, 250);
|
|
125
131
|
timer.unref();
|
|
132
|
+
if (initial) start(initial.href);
|
|
126
133
|
return {
|
|
127
134
|
id: id,
|
|
128
135
|
env: { BROWSER: wrapper, CLAY_LOGIN_URL_FILE: urlFile },
|
|
@@ -1853,25 +1853,8 @@ function createCodexAdapter(opts) {
|
|
|
1853
1853
|
if (co.config) serverOpts.config = Object.assign(serverOpts.config, co.config);
|
|
1854
1854
|
}
|
|
1855
1855
|
|
|
1856
|
-
//
|
|
1857
|
-
// and inject into Codex config so Codex manages them natively.
|
|
1856
|
+
// Clay-managed external MCP runs through the owner-bound bridge.
|
|
1858
1857
|
var mcpServerConfig = Object.assign({}, serverOpts.config.mcp_servers || {});
|
|
1859
|
-
try {
|
|
1860
|
-
var mcpLocal = require("../../mcp-local");
|
|
1861
|
-
var localMcpServers = mcpLocal.readMergedServers();
|
|
1862
|
-
var mcpNames = Object.keys(localMcpServers);
|
|
1863
|
-
for (var mi = 0; mi < mcpNames.length; mi++) {
|
|
1864
|
-
var ms = localMcpServers[mcpNames[mi]];
|
|
1865
|
-
if (ms.command) {
|
|
1866
|
-
mcpServerConfig[mcpNames[mi]] = { command: ms.command, args: ms.args || [] };
|
|
1867
|
-
if (ms.env && Object.keys(ms.env).length > 0) {
|
|
1868
|
-
mcpServerConfig[mcpNames[mi]].env = ms.env;
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
} catch (e) {
|
|
1873
|
-
console.error("[codex] Failed to read local MCP config:", e.message);
|
|
1874
|
-
}
|
|
1875
1858
|
|
|
1876
1859
|
// Track 2: Add clay-tools bridge server for in-app + remote MCP tools.
|
|
1877
1860
|
var bridgePath = require("path").join(__dirname, "..", "mcp-bridge-server.js");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clay-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-beta.1",
|
|
4
4
|
"description": "Self-hosted team workspace for Claude Code and Codex. Multi-user, browser-based, with persistent AI mates.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"clay-server": "./bin/cli.js"
|
|
@@ -49,14 +49,18 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@anthropic-ai/claude-agent-sdk": "^0.3.287",
|
|
51
51
|
"@lydell/node-pty": "^1.2.0-beta.3",
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
52
53
|
"@openai/codex": "^0.160.0",
|
|
53
54
|
"@seald-io/nedb": "^4.1.2",
|
|
54
55
|
"imapflow": "^1.7.8",
|
|
56
|
+
"ipaddr.js": "^2.5.0",
|
|
55
57
|
"nodemailer": "^9.1.1",
|
|
56
58
|
"playwright": "1.63.0",
|
|
57
59
|
"qrcode-terminal": "^0.12.0",
|
|
60
|
+
"undici": "^6.13.0",
|
|
58
61
|
"web-push": "^3.6.7",
|
|
59
|
-
"ws": "^8.18.0"
|
|
62
|
+
"ws": "^8.18.0",
|
|
63
|
+
"zod": "^4.6.5"
|
|
60
64
|
},
|
|
61
65
|
"license": "AGPL-3.0-only",
|
|
62
66
|
"engines": {
|