@classcad/mcp 0.1.0

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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +361 -0
  3. package/dist/auth.d.ts +32 -0
  4. package/dist/auth.js +272 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/bridge/protocol.d.ts +87 -0
  7. package/dist/bridge/protocol.js +14 -0
  8. package/dist/bridge/protocol.js.map +1 -0
  9. package/dist/bridge/server.d.ts +35 -0
  10. package/dist/bridge/server.js +236 -0
  11. package/dist/bridge/server.js.map +1 -0
  12. package/dist/client.d.ts +98 -0
  13. package/dist/client.js +879 -0
  14. package/dist/client.js.map +1 -0
  15. package/dist/daemon.d.ts +38 -0
  16. package/dist/daemon.js +390 -0
  17. package/dist/daemon.js.map +1 -0
  18. package/dist/engine/wasm-worker.d.ts +1 -0
  19. package/dist/engine/wasm-worker.js +176 -0
  20. package/dist/engine/wasm-worker.js.map +1 -0
  21. package/dist/engine/wasm.d.ts +60 -0
  22. package/dist/engine/wasm.js +212 -0
  23. package/dist/engine/wasm.js.map +1 -0
  24. package/dist/mcp-server.d.ts +23 -0
  25. package/dist/mcp-server.js +141 -0
  26. package/dist/mcp-server.js.map +1 -0
  27. package/dist/ports.d.ts +2 -0
  28. package/dist/ports.js +5 -0
  29. package/dist/ports.js.map +1 -0
  30. package/dist/queue.d.ts +9 -0
  31. package/dist/queue.js +50 -0
  32. package/dist/queue.js.map +1 -0
  33. package/dist/server.d.ts +2 -0
  34. package/dist/server.js +423 -0
  35. package/dist/server.js.map +1 -0
  36. package/dist/tools/auth.d.ts +7 -0
  37. package/dist/tools/auth.js +63 -0
  38. package/dist/tools/auth.js.map +1 -0
  39. package/dist/tools/bridge.d.ts +4 -0
  40. package/dist/tools/bridge.js +115 -0
  41. package/dist/tools/bridge.js.map +1 -0
  42. package/dist/tools/docs.d.ts +13 -0
  43. package/dist/tools/docs.js +180 -0
  44. package/dist/tools/docs.js.map +1 -0
  45. package/dist/tools/lifecycle.d.ts +3 -0
  46. package/dist/tools/lifecycle.js +156 -0
  47. package/dist/tools/lifecycle.js.map +1 -0
  48. package/dist/tools/script.d.ts +3 -0
  49. package/dist/tools/script.js +102 -0
  50. package/dist/tools/script.js.map +1 -0
  51. package/dist/tools/snapshot.d.ts +3 -0
  52. package/dist/tools/snapshot.js +174 -0
  53. package/dist/tools/snapshot.js.map +1 -0
  54. package/dist/tools/state.d.ts +3 -0
  55. package/dist/tools/state.js +95 -0
  56. package/dist/tools/state.js.map +1 -0
  57. package/dist/types.d.ts +46 -0
  58. package/dist/types.js +3 -0
  59. package/dist/types.js.map +1 -0
  60. package/package.json +44 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** Daemon HTTP port. Not 9095: a ClassCAD worker listens on 9094 (ws) AND 9095 (wss). */
2
+ export declare const DEFAULT_DAEMON_PORT = 9097;
package/dist/ports.js ADDED
@@ -0,0 +1,5 @@
1
+ // Default ports, in a leaf module so tool docs can name them without importing
2
+ // the daemon (which imports the MCP server, which imports the tool docs).
3
+ /** Daemon HTTP port. Not 9095: a ClassCAD worker listens on 9094 (ws) AND 9095 (wss). */
4
+ export const DEFAULT_DAEMON_PORT = 9097;
5
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,0EAA0E;AAE1E,yFAAyF;AACzF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAA"}
@@ -0,0 +1,9 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function setDrawingBusy(server: McpServer, check: () => boolean): void;
3
+ export declare const UNQUEUED_TOOLS: Set<string>;
4
+ /**
5
+ * Patches `server.registerTool` so every handler registered afterwards runs
6
+ * through the shared queue (except the tools in `exclude`). Call it once,
7
+ * before the first registerTool call.
8
+ */
9
+ export declare function serializeTools(server: McpServer, exclude?: Set<string>): void;
package/dist/queue.js ADDED
@@ -0,0 +1,50 @@
1
+ // queue.ts — one tool call at a time.
2
+ //
3
+ // Every MCP tool of this server shares ONE engine connection and ONE
4
+ // drawing. The MCP SDK dispatches tool calls concurrently, and hosts do issue
5
+ // them in parallel (Claude Code sends independent tool calls of one turn at
6
+ // once; subagents spawned by a session talk to the SAME server process and
7
+ // therefore the same drawing). Without serialization a `snapshot` issued next
8
+ // to a `run_script` renders whatever the drawing looked like when its GetTree
9
+ // happened to run - possibly before the script created anything - and two
10
+ // scripts interleave their commands on one connection.
11
+ //
12
+ // So tool executions are queued: each call starts after the previous one
13
+ // settled (resolved OR rejected - a failed call never blocks the queue).
14
+ // Pure lookups that never touch the engine (docs, method index) are exempt
15
+ // and keep answering immediately.
16
+ //
17
+ // Subagents: they share this process and this queue with their parent. A
18
+ // parent's tool call issued while a subagent runs a long script simply waits
19
+ // for it - it would have to anyway, since both act on the same drawing. No
20
+ // tool waits on ANOTHER tool's completion inside its own handler, so the
21
+ // queue cannot deadlock. Separate Claude sessions run separate MCP processes
22
+ // and are unaffected.
23
+ /** Tools that only read package data (no engine access) and stay unqueued. */
24
+ const busyChecks = new WeakMap();
25
+ export function setDrawingBusy(server, check) { busyChecks.set(server, check); }
26
+ export const UNQUEUED_TOOLS = new Set(['list_methods', 'describe_method', 'docs', 'session_info']);
27
+ /**
28
+ * Patches `server.registerTool` so every handler registered afterwards runs
29
+ * through the shared queue (except the tools in `exclude`). Call it once,
30
+ * before the first registerTool call.
31
+ */
32
+ export function serializeTools(server, exclude = UNQUEUED_TOOLS) {
33
+ // The tail of the chain: resolves when the most recently queued call settled.
34
+ let tail = Promise.resolve();
35
+ const original = server.registerTool.bind(server);
36
+ server.registerTool = (name, config, handler) => {
37
+ if (exclude.has(name))
38
+ return original(name, config, handler);
39
+ const queued = (...args) => {
40
+ const run = tail.then(() => busyChecks.get(server)?.()
41
+ ? { isError: true, content: [{ type: 'text', text: 'Session busy: timed-out work is unresolved. Wait before changing or inspecting the drawing.' }] }
42
+ : handler(...args));
43
+ // Whatever happens to this call, the next one may start afterwards.
44
+ tail = run.then(() => undefined, () => undefined);
45
+ return run;
46
+ };
47
+ return original(name, config, queued);
48
+ };
49
+ }
50
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,qEAAqE;AACrE,8EAA8E;AAC9E,4EAA4E;AAC5E,2EAA2E;AAC3E,8EAA8E;AAC9E,8EAA8E;AAC9E,0EAA0E;AAC1E,uDAAuD;AACvD,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,2EAA2E;AAC3E,kCAAkC;AAClC,EAAE;AACF,yEAAyE;AACzE,6EAA6E;AAC7E,2EAA2E;AAC3E,yEAAyE;AACzE,6EAA6E;AAC7E,sBAAsB;AAItB,8EAA8E;AAC9E,MAAM,UAAU,GAAG,IAAI,OAAO,EAA4B,CAAA;AAC1D,MAAM,UAAU,cAAc,CAAC,MAAiB,EAAE,KAAoB,IAAU,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA,CAAC,CAAC;AAE/G,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,iBAAiB,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;AAElG;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAiB,EAAE,UAAuB,cAAc;IACrF,8EAA8E;IAC9E,IAAI,IAAI,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAA;IAC9C,MAAM,QAAQ,GAAI,MAAM,CAAC,YAAwC,CAAC,IAAI,CAAC,MAAM,CAAC,CAC7E;IAAC,MAAc,CAAC,YAAY,GAAG,CAAC,IAAY,EAAE,MAAe,EAAE,OAAgC,EAAE,EAAE;QAClG,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QAC7D,MAAM,MAAM,GAAG,CAAC,GAAG,IAAW,EAAE,EAAE;YAChC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE;gBACpD,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,6FAA6F,EAAE,CAAC,EAAE;gBACrJ,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;YACrB,oEAAoE;YACpE,IAAI,GAAG,GAAG,CAAC,IAAI,CACb,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CAChB,CAAA;YACD,OAAO,GAAG,CAAA;QACZ,CAAC,CAAA;QACD,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/server.js ADDED
@@ -0,0 +1,423 @@
1
+ #!/usr/bin/env node
2
+ // server.ts — the `classcad-mcp` entry point hosts spawn over stdio.
3
+ //
4
+ // It is a thin shim: it finds the ClassCAD MCP daemon on this machine (or
5
+ // starts it, detached) and forwards its host's JSON-RPC to the daemon over
6
+ // HTTP (MCP Streamable HTTP). Every tab therefore gets its own session in the
7
+ // ONE daemon process that owns the bridge listener — see daemon.ts for why.
8
+ //
9
+ // If no daemon can be reached at all (the port is held by a foreign program
10
+ // and cannot be freed), the shim serves the MCP in-process as before, minus
11
+ // the bridge listener, so a Drogon-only user is never blocked.
12
+ import { spawn } from 'node:child_process';
13
+ import { connect as tcpConnect } from 'node:net';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { dirname, join } from 'node:path';
16
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
18
+ import { createMcpServer, DEFAULT_WS_URL, VERSION } from './mcp-server.js';
19
+ import { DAEMON_HOST, DEFAULT_DAEMON_PORT, defaultDaemonLogFile, daemonBuildStamp } from './daemon.js';
20
+ import { wasmOptionsFromEnv } from './engine/wasm.js';
21
+ import { authStatus, beginLogin, logout, waitForLogin } from './auth.js';
22
+ const WS_URL = process.env.CLASSCAD_WS_URL ?? DEFAULT_WS_URL;
23
+ /** Engine policy for this tab's session: auto (worker, else local WASM) | drogon | wasm. */
24
+ const ENGINE = process.env.CLASSCAD_ENGINE ?? 'auto';
25
+ const WASM = wasmOptionsFromEnv();
26
+ const DAEMON_PORT = Number(process.env.CLASSCAD_MCP_PORT ?? DEFAULT_DAEMON_PORT);
27
+ /** Full daemon base URL override (tests, unusual setups). */
28
+ const DAEMON_URL = process.env.CLASSCAD_MCP_URL ?? `http://${DAEMON_HOST}:${DAEMON_PORT}`;
29
+ const SPAWN_TIMEOUT_MS = 10_000;
30
+ const log = (msg) => process.stderr.write(`[classcad-mcp] ${msg}\n`);
31
+ async function health() {
32
+ try {
33
+ const res = await fetch(`${DAEMON_URL}/health`, { signal: AbortSignal.timeout(1500) });
34
+ if (!res.ok)
35
+ return null;
36
+ const j = (await res.json());
37
+ return j && j.name === 'classcad-mcp' ? j : null;
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ }
43
+ /** True when something accepts TCP connections on the daemon port (a foreign program, if /health said no). */
44
+ function portInUse() {
45
+ const { hostname, port } = new URL(DAEMON_URL);
46
+ return new Promise(resolve => {
47
+ const sock = tcpConnect({ host: hostname, port: Number(port) }, () => {
48
+ sock.destroy();
49
+ resolve(true);
50
+ });
51
+ sock.once('error', () => resolve(false));
52
+ sock.setTimeout(1000, () => {
53
+ sock.destroy();
54
+ resolve(false);
55
+ });
56
+ });
57
+ }
58
+ const sleep = (ms) => new Promise(r => setTimeout(r, ms));
59
+ /** Spawns the daemon detached (survives this shim) and waits for /health. */
60
+ async function spawnDaemon() {
61
+ const daemonPath = join(dirname(fileURLToPath(import.meta.url)), 'daemon.js');
62
+ const child = spawn(process.execPath, [daemonPath], {
63
+ detached: true,
64
+ stdio: 'ignore',
65
+ windowsHide: true,
66
+ env: {
67
+ ...process.env,
68
+ CLASSCAD_MCP_DAEMON: '1',
69
+ CLASSCAD_MCP_PORT: String(DAEMON_PORT),
70
+ // The first shim's worker URL becomes the daemon default; each session
71
+ // still names its own worker via the x-classcad-ws-url header below.
72
+ CLASSCAD_WS_URL: WS_URL,
73
+ },
74
+ });
75
+ child.unref();
76
+ const deadline = Date.now() + SPAWN_TIMEOUT_MS;
77
+ while (Date.now() < deadline) {
78
+ await sleep(200);
79
+ const h = await health();
80
+ if (h)
81
+ return h;
82
+ }
83
+ return null;
84
+ }
85
+ /**
86
+ * Finds a daemon of this version, or starts one. Returns null when neither is
87
+ * possible (foreign program on the port) — the caller then serves in-process.
88
+ */
89
+ /** True when the running daemon is another version or another build of this package. */
90
+ function stale(h) {
91
+ if (h.version !== VERSION)
92
+ return true;
93
+ const mine = daemonBuildStamp();
94
+ if (mine === 'unknown')
95
+ return false;
96
+ // No stamp at all = a daemon from before stamps existed = older.
97
+ return !h.build || h.build !== mine;
98
+ }
99
+ async function ensureDaemon() {
100
+ let h = await health();
101
+ if (h && stale(h)) {
102
+ // An older daemon (previous install or previous build) — ask it to leave when idle.
103
+ log(`daemon ${h.version} (build ${h.build ?? '?'}) running, this MCP is ${VERSION} (build ${daemonBuildStamp()}): asking it to shut down`);
104
+ try {
105
+ const res = await fetch(`${DAEMON_URL}/shutdown`, { method: 'POST', signal: AbortSignal.timeout(1500) });
106
+ if (res.ok) {
107
+ for (let i = 0; i < 25 && (await health()); i++)
108
+ await sleep(200);
109
+ h = await health();
110
+ }
111
+ }
112
+ catch {
113
+ /* fall through: use it as is */
114
+ }
115
+ if (h && stale(h)) {
116
+ // Busy with other tabs. Tell it to drain (newer daemons stop taking
117
+ // sessions and exit after the last one) and run THIS tab on the new
118
+ // code in-process — without the bridge listener, which the old daemon
119
+ // still holds. The next tab after the old one is gone starts a fresh daemon.
120
+ try {
121
+ await fetch(`${DAEMON_URL}/shutdown?drain=1`, { method: 'POST', signal: AbortSignal.timeout(1500) });
122
+ }
123
+ catch {
124
+ /* an old daemon without drain support: it leaves when idle */
125
+ }
126
+ log(`daemon ${h.version} (build ${h.build ?? '?'}) still serves ${h.sessions ?? '?'} other tab(s) — this tab runs the current build in-process (no bridge listener until that daemon is gone; restart the other tabs to move them)`);
127
+ return null;
128
+ }
129
+ }
130
+ if (h?.draining) {
131
+ log('daemon is draining; this tab runs in-process');
132
+ return null;
133
+ }
134
+ if (h)
135
+ return h;
136
+ if (await portInUse()) {
137
+ log(`port ${DAEMON_PORT} answers but is not a classcad-mcp daemon — set CLASSCAD_MCP_PORT to a free port`);
138
+ return null;
139
+ }
140
+ h = await spawnDaemon();
141
+ if (!h) {
142
+ // Lost the race to another shim, or the port was taken meanwhile: one more look.
143
+ h = await health();
144
+ }
145
+ return h;
146
+ }
147
+ /** Id of the initialize request the shim replays on a new daemon; its response is not the host's. */
148
+ const REPLAY_ID = 'classcad-shim-reinitialize';
149
+ /**
150
+ * Forward stdio (host) ⇄ HTTP (daemon) message by message.
151
+ *
152
+ * The daemon may go away under a live tab (`classcad-mcp stop --force`, a
153
+ * crash). The host initialized once and will not do it again, so the shim
154
+ * keeps the host's initialize request: when a send fails it finds or starts a
155
+ * daemon, replays the handshake there and resends. The tab continues on a
156
+ * fresh session (an empty drawing) instead of hanging on unanswered requests.
157
+ */
158
+ async function proxyToDaemon() {
159
+ const stdio = new StdioServerTransport();
160
+ let upstream;
161
+ let hostInitialize = null;
162
+ const replayWaiters = new Set();
163
+ const connect = async () => {
164
+ const t = new StreamableHTTPClientTransport(new URL(`${DAEMON_URL}/mcp`), {
165
+ // Per-session settings travel as loopback headers: the daemon may have
166
+ // been started by another tab with a different configuration.
167
+ requestInit: {
168
+ headers: {
169
+ 'x-classcad-ws-url': WS_URL,
170
+ 'x-classcad-engine': ENGINE,
171
+ ...(WASM ? { 'x-classcad-wasm-key': WASM.key, ...(WASM.origin ? { 'x-classcad-wasm-origin': WASM.origin } : {}) } : {}),
172
+ },
173
+ },
174
+ });
175
+ t.onmessage = m => {
176
+ if ('id' in m && m.id === REPLAY_ID) {
177
+ for (const resolve of replayWaiters)
178
+ resolve();
179
+ replayWaiters.clear();
180
+ return;
181
+ }
182
+ stdio.send(m).catch(err => log(`stdio send failed: ${err?.message ?? err}`));
183
+ };
184
+ t.onerror = err => log(`daemon transport error: ${err.message}`);
185
+ t.onclose = () => {
186
+ if (t === upstream)
187
+ void finish('daemon closed the session');
188
+ };
189
+ await t.start();
190
+ return t;
191
+ };
192
+ /** New daemon session for this tab, handshake replayed. False when no daemon can be reached. */
193
+ const reconnect = async () => {
194
+ const h = await ensureDaemon();
195
+ if (!h || !hostInitialize || !('method' in hostInitialize))
196
+ return false;
197
+ const previous = upstream;
198
+ upstream = await connect();
199
+ previous.close().catch(() => { });
200
+ const answered = new Promise(resolve => replayWaiters.add(resolve));
201
+ await upstream.send({ ...hostInitialize, id: REPLAY_ID });
202
+ await Promise.race([answered, sleep(15_000).then(() => Promise.reject(new Error('initialize not answered')))]);
203
+ await upstream.send({ jsonrpc: '2.0', method: 'notifications/initialized' });
204
+ log(`reconnected to daemon ${h.version} (pid ${h.pid}) — new session, previous drawing state is gone`);
205
+ return true;
206
+ };
207
+ const forward = async (m) => {
208
+ if ('method' in m && m.method === 'initialize')
209
+ hostInitialize = m;
210
+ try {
211
+ await upstream.send(m);
212
+ return;
213
+ }
214
+ catch (err) {
215
+ log(`daemon send failed: ${err?.message ?? err} — reconnecting`);
216
+ }
217
+ try {
218
+ if (hostInitialize && m !== hostInitialize && (await reconnect())) {
219
+ await upstream.send(m);
220
+ return;
221
+ }
222
+ }
223
+ catch (err) {
224
+ log(`reconnect failed: ${err?.message ?? err}`);
225
+ }
226
+ // Unrecoverable: answer the host instead of leaving the request pending, then end the tab's MCP.
227
+ if ('method' in m && 'id' in m) {
228
+ await stdio
229
+ .send({ jsonrpc: '2.0', id: m.id, error: { code: -32000, message: 'ClassCAD MCP daemon unreachable — restart the MCP' } })
230
+ .catch(() => { });
231
+ }
232
+ void finish('daemon unreachable');
233
+ };
234
+ // Serial: a reconnect must finish before the next host message goes out.
235
+ let queue = Promise.resolve();
236
+ stdio.onmessage = m => {
237
+ queue = queue.then(() => forward(m));
238
+ };
239
+ upstream = await connect();
240
+ let done = false;
241
+ const finish = async (why) => {
242
+ if (done)
243
+ return;
244
+ done = true;
245
+ log(`session ended (${why})`);
246
+ try {
247
+ await upstream.terminateSession();
248
+ }
249
+ catch {
250
+ /* daemon may be gone */
251
+ }
252
+ try {
253
+ await upstream.close();
254
+ }
255
+ catch { }
256
+ try {
257
+ await stdio.close();
258
+ }
259
+ catch { }
260
+ process.exit(0);
261
+ };
262
+ stdio.onclose = () => void finish('host closed stdio');
263
+ process.on('SIGINT', () => void finish('SIGINT'));
264
+ process.on('SIGTERM', () => void finish('SIGTERM'));
265
+ // The SDK transport only reads stdin; it does not notice when the host closes it.
266
+ // A host that ends our stdin (instead of killing us) must still end the daemon session.
267
+ process.stdin.on('end', () => void finish('host closed stdin'));
268
+ process.stdin.on('close', () => void finish('host closed stdin'));
269
+ await stdio.start();
270
+ }
271
+ /** Last resort: the MCP in this process, without a bridge listener. */
272
+ async function serveInProcess() {
273
+ const { server, client } = await createMcpServer({ wsUrl: WS_URL, bridge: () => null, engine: ENGINE, wasm: WASM, log });
274
+ const shutdown = () => {
275
+ try {
276
+ client.close();
277
+ }
278
+ catch { }
279
+ process.exit(0);
280
+ };
281
+ process.on('SIGINT', shutdown);
282
+ process.on('SIGTERM', shutdown);
283
+ process.stdin.on('end', shutdown);
284
+ process.stdin.on('close', shutdown);
285
+ await server.connect(new StdioServerTransport());
286
+ }
287
+ async function main() {
288
+ const h = await ensureDaemon();
289
+ if (h) {
290
+ log(`using daemon ${h.version} (pid ${h.pid}, ${h.sessions} session(s), bridge ${h.bridge ?? 'none'}) at ${DAEMON_URL}; daemon log: ${h.logFile ?? defaultDaemonLogFile()}`);
291
+ if (!h.bridge)
292
+ log(`WARNING: the daemon has no bridge listener (${h.bridgeListen ?? 'unknown'} is taken, probably by an MCP of an older build in another tab) — in-app engines cannot attach until that process is gone; the daemon retries by itself`);
293
+ await proxyToDaemon();
294
+ return;
295
+ }
296
+ log('serving in-process (no bridge listener: in-app engines cannot attach; the worker and the local WASM engine work as usual)');
297
+ await serveInProcess();
298
+ }
299
+ const USAGE = `classcad-mcp — ClassCAD MCP server (stdio). Hosts start it without arguments.
300
+
301
+ Daemon commands:
302
+ classcad-mcp status show the running daemon (pid, build, sessions, apps, log file)
303
+ classcad-mcp stop stop the daemon if no session is active
304
+ classcad-mcp stop --force terminate it now: EVERY connected tab loses its drawing and
305
+ reconnects to a fresh daemon on its next tool call
306
+
307
+ Account commands (the MCP works for signed-in classcad.ch accounts):
308
+ classcad-mcp login print a sign-in link and wait until it is used
309
+ classcad-mcp whoami show this machine's sign-in
310
+ classcad-mcp logout sign this machine out
311
+ `;
312
+ const pidAlive = (pid) => {
313
+ try {
314
+ process.kill(pid, 0);
315
+ return true;
316
+ }
317
+ catch (err) {
318
+ return err.code === 'EPERM';
319
+ }
320
+ };
321
+ /** Waits until the daemon neither answers /health nor has a live pid. */
322
+ async function waitGone(pid, ms) {
323
+ const deadline = Date.now() + ms;
324
+ while (Date.now() < deadline) {
325
+ if (!(await health()) && !(pid && pidAlive(pid)))
326
+ return true;
327
+ await sleep(100);
328
+ }
329
+ return false;
330
+ }
331
+ async function stopDaemon(force) {
332
+ const h = await health();
333
+ if (!h) {
334
+ console.log(`no classcad-mcp daemon at ${DAEMON_URL}`);
335
+ return 0;
336
+ }
337
+ if (!force) {
338
+ const res = await fetch(`${DAEMON_URL}/shutdown`, { method: 'POST', signal: AbortSignal.timeout(1500) });
339
+ if (res.status === 409) {
340
+ console.error(`daemon pid ${h.pid} has ${h.sessions} active session(s) — not stopped.\n` +
341
+ `\`classcad-mcp stop --force\` ends them: every connected tab loses its drawing and reconnects on its next tool call.`);
342
+ return 1;
343
+ }
344
+ if (!res.ok) {
345
+ console.error(`daemon refused to stop: HTTP ${res.status}`);
346
+ return 1;
347
+ }
348
+ }
349
+ else {
350
+ if (!h.pid) {
351
+ console.error('daemon did not report its pid — cannot force it');
352
+ return 1;
353
+ }
354
+ // SIGTERM runs the daemon's own shutdown: sessions closed, bridge and port released.
355
+ process.kill(h.pid, 'SIGTERM');
356
+ }
357
+ if (await waitGone(h.pid, 5000)) {
358
+ console.log(`stopped daemon pid ${h.pid}${force ? ` (${h.sessions ?? 0} session(s) ended)` : ''}`);
359
+ return 0;
360
+ }
361
+ if (force && h.pid) {
362
+ process.kill(h.pid, 'SIGKILL');
363
+ if (await waitGone(h.pid, 2000)) {
364
+ console.log(`killed daemon pid ${h.pid} (did not exit on SIGTERM)`);
365
+ return 0;
366
+ }
367
+ }
368
+ console.error(`daemon pid ${h.pid} is still running`);
369
+ return 1;
370
+ }
371
+ async function cli(command, args) {
372
+ if (command === 'status') {
373
+ const h = await health();
374
+ if (!h) {
375
+ console.log(`no classcad-mcp daemon at ${DAEMON_URL}`);
376
+ return 1;
377
+ }
378
+ console.log(JSON.stringify(h, null, 2));
379
+ return 0;
380
+ }
381
+ if (command === 'stop')
382
+ return stopDaemon(args.includes('--force'));
383
+ if (command === 'whoami' || command === 'login') {
384
+ const status = await authStatus();
385
+ if (status.signedIn) {
386
+ console.log(`signed in as ${status.account.email ?? status.account.name ?? status.account.uid}${status.offline ? ' (offline, not re-checked)' : ''}`);
387
+ return 0;
388
+ }
389
+ if (command === 'whoami') {
390
+ console.log(status.reason);
391
+ return 1;
392
+ }
393
+ const { url } = await beginLogin('this terminal');
394
+ console.log(`Open this link to sign in:\n\n ${url}\n\nWaiting …`);
395
+ const account = await waitForLogin(15 * 60 * 1000).catch(() => null);
396
+ if (!account) {
397
+ console.error('the sign-in link expired');
398
+ return 1;
399
+ }
400
+ console.log(`signed in as ${account.email ?? account.name ?? account.uid}`);
401
+ return 0;
402
+ }
403
+ if (command === 'logout') {
404
+ console.log(logout() ? 'signed out' : 'was not signed in');
405
+ return 0;
406
+ }
407
+ console.log(USAGE);
408
+ return 0;
409
+ }
410
+ const [command, ...args] = process.argv.slice(2);
411
+ if (['status', 'stop', 'login', 'logout', 'whoami', 'help', '--help'].includes(command)) {
412
+ cli(command, args).then(code => process.exit(code), err => {
413
+ process.stderr.write(`[classcad-mcp] ${err?.message ?? err}\n`);
414
+ process.exit(1);
415
+ });
416
+ }
417
+ else {
418
+ main().catch(err => {
419
+ process.stderr.write(`[classcad-mcp] FATAL: ${err?.message ?? err}\n`);
420
+ process.exit(1);
421
+ });
422
+ }
423
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ /** Tools that work without a sign-in: the sign-in itself, status, and the public docs. */
3
+ export declare const OPEN_TOOLS: Set<string>;
4
+ export declare const SIGN_IN_NOTE = "SIGN-IN: the ClassCAD MCP needs a free classcad.ch account, once per machine. When a tool answers \"Sign-in required\", show the user the link it gives (as a clickable link, on its own line), then call `login` \u2014 it waits and returns as soon as they have signed in; then repeat the call. Docs tools work without a sign-in.";
5
+ /** Wraps every tool registered after this call, except OPEN_TOOLS, in the sign-in check. */
6
+ export declare function requireSignIn(server: McpServer, open?: Set<string>): void;
7
+ export declare function registerAuthTool(server: McpServer): void;
@@ -0,0 +1,63 @@
1
+ // tools/auth.ts — the sign-in gate and the `login` tool.
2
+ //
3
+ // requireSignIn() patches registerTool (like serializeTools) so every tool
4
+ // registered afterwards first checks the machine's sign-in. Not signed in,
5
+ // the call starts a sign-in and answers with its link instead of running;
6
+ // the agent shows the link and calls `login`, which returns the moment the
7
+ // browser hands the sign-in back.
8
+ import { z } from 'zod';
9
+ import { authStatus, beginLogin, logout, pendingLogin, waitForLogin } from '../auth.js';
10
+ /** Tools that work without a sign-in: the sign-in itself, status, and the public docs. */
11
+ export const OPEN_TOOLS = new Set(['login', 'session_info', 'list_methods', 'describe_method', 'docs']);
12
+ /** How long one `login` call waits (under Codex's 60 s default tool timeout). */
13
+ const WAIT_MS = 50_000;
14
+ export const SIGN_IN_NOTE = 'SIGN-IN: the ClassCAD MCP needs a free classcad.ch account, once per machine. When a tool answers "Sign-in required", show the user the link it gives (as a clickable link, on its own line), then call `login` — it waits and returns as soon as they have signed in; then repeat the call. Docs tools work without a sign-in.';
15
+ const clientName = (server) => server.server.getClientVersion()?.name;
16
+ function linkText(url) {
17
+ return ('Sign-in required: the ClassCAD MCP works with a free classcad.ch account (Google, GitHub or email; one click if they already have one).\n\n' +
18
+ `Show the user this link, as a clickable link on its own line, and ask them to sign in:\n\n${url}\n\n` +
19
+ 'Then call the `login` tool: it waits and returns as soon as the sign-in has arrived. After that, repeat the call that needed it.');
20
+ }
21
+ /** Wraps every tool registered after this call, except OPEN_TOOLS, in the sign-in check. */
22
+ export function requireSignIn(server, open = OPEN_TOOLS) {
23
+ const original = server.registerTool.bind(server);
24
+ server.registerTool = (name, config, handler) => {
25
+ if (open.has(name))
26
+ return original(name, config, handler);
27
+ const gated = async (...args) => {
28
+ const status = await authStatus();
29
+ if (status.signedIn)
30
+ return handler(...args);
31
+ const { url } = await beginLogin(clientName(server));
32
+ return { isError: true, content: [{ type: 'text', text: (status.reason === 'not signed in' ? '' : `(${status.reason}.) `) + linkText(url) }] };
33
+ };
34
+ return original(name, config, gated);
35
+ };
36
+ }
37
+ export function registerAuthTool(server) {
38
+ server.registerTool('login', {
39
+ title: 'Sign in to ClassCAD',
40
+ description: 'Sign this machine in to ClassCAD (a free classcad.ch account; Google, GitHub or email). Not signed in: the first call returns a link — show it to the user as a clickable link — and the next call WAITS (up to ~50 s per call) until they have signed in in the browser, then returns their account. Call again while it reports "still waiting". Already signed in: returns the account at once. logout: true signs the machine out.',
41
+ inputSchema: {
42
+ logout: z.boolean().optional().describe('Sign this machine out instead.'),
43
+ },
44
+ }, async ({ logout: out }) => {
45
+ const text = (t) => ({ content: [{ type: 'text', text: t }] });
46
+ if (out)
47
+ return text(logout() ? 'Signed out. The next ClassCAD call asks for a new sign-in.' : 'Was not signed in.');
48
+ const status = await authStatus();
49
+ if (status.signedIn)
50
+ return text(`Signed in as ${status.account.email ?? status.account.name ?? status.account.uid}.`);
51
+ const waiting = pendingLogin();
52
+ if (!waiting)
53
+ return text(linkText((await beginLogin(clientName(server))).url));
54
+ const account = await waitForLogin(WAIT_MS);
55
+ if (account)
56
+ return text(`Signed in as ${account.email ?? account.name ?? account.uid}. Repeat the call that needed it.`);
57
+ const still = pendingLogin();
58
+ if (!still)
59
+ return text(linkText((await beginLogin(clientName(server))).url));
60
+ return text(`Still waiting for the user to sign in at:\n\n${still.url}\n\nCall \`login\` again to keep waiting.`);
61
+ });
62
+ }
63
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/tools/auth.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,kCAAkC;AAGlC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEvF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAA;AAEvG,iFAAiF;AACjF,MAAM,OAAO,GAAG,MAAM,CAAA;AAEtB,MAAM,CAAC,MAAM,YAAY,GACvB,iUAAiU,CAAA;AAEnU,MAAM,UAAU,GAAG,CAAC,MAAiB,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,EAAE,IAAI,CAAA;AAEhF,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,CACL,6IAA6I;QAC7I,6FAA6F,GAAG,MAAM;QACtG,kIAAkI,CACnI,CAAA;AACH,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,aAAa,CAAC,MAAiB,EAAE,OAAoB,UAAU;IAC7E,MAAM,QAAQ,GAAI,MAAM,CAAC,YAAwC,CAAC,IAAI,CAAC,MAAM,CAAC,CAC7E;IAAC,MAAc,CAAC,YAAY,GAAG,CAAC,IAAY,EAAE,MAAe,EAAE,OAAgC,EAAE,EAAE;QAClG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QAC1D,MAAM,KAAK,GAAG,KAAK,EAAE,GAAG,IAAW,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAA;YACjC,IAAI,MAAM,CAAC,QAAQ;gBAAE,OAAO,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;YAC5C,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;YACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAA;QAChJ,CAAC,CAAA;QACD,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;IACtC,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,MAAM,CAAC,YAAY,CACjB,OAAO,EACP;QACE,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EACT,waAAwa;QAC1a,WAAW,EAAE;YACX,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;SAC1E;KACF,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE;QACxB,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;QAC/E,IAAI,GAAG;YAAE,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,4DAA4D,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAA;QACpH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAA;QACjC,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,gBAAgB,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAA;QACtH,MAAM,OAAO,GAAG,YAAY,EAAE,CAAA;QAC9B,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QAC/E,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,CAAA;QAC3C,IAAI,OAAO;YAAE,OAAO,IAAI,CAAC,gBAAgB,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,mCAAmC,CAAC,CAAA;QACzH,MAAM,KAAK,GAAG,YAAY,EAAE,CAAA;QAC5B,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7E,OAAO,IAAI,CAAC,gDAAgD,KAAK,CAAC,GAAG,2CAA2C,CAAC,CAAA;IACnH,CAAC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Client } from '../client.js';
3
+ import type { BridgeRegistry } from '../bridge/server.js';
4
+ export declare function registerBridgeTools(server: McpServer, client: Client, registry: BridgeRegistry): void;