@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,115 @@
1
+ // Bridge tools: route requests to a CC app's bridge connection.
2
+ //
3
+ // All tools are scoped by the share token the cc MCP is currently attached
4
+ // with (use_session: an invite for a server session, or a bridge token for
5
+ // an in-app engine). If no app has connected a bridge for that token, tools
6
+ // return a clean "no bridge connected" error and the user can keep using the
7
+ // rest of the cc MCP unaffected.
8
+ import { z } from 'zod';
9
+ function noBridgeError(sessionId) {
10
+ return {
11
+ isError: true,
12
+ content: [{
13
+ type: 'text',
14
+ text: JSON.stringify({
15
+ ok: false,
16
+ error: 'no bridge connected',
17
+ sessionId,
18
+ hint: sessionId
19
+ ? `No CC app has registered a bridge for token "${sessionId}". Create the share in the app (Session Management) and start the app with the bridge enabled (e.g. ?mcpBridge=on).`
20
+ : 'No shared session is attached. Call use_session with the app\'s share link first, then try again.',
21
+ }),
22
+ }],
23
+ };
24
+ }
25
+ function pickConn(client, registry, clientId) {
26
+ // The token the MCP joined with (invite or bridge) selects the app.
27
+ const sid = client.shareToken;
28
+ if (!sid)
29
+ return { conn: null, sid: null };
30
+ return { conn: registry.pick(sid, clientId), sid };
31
+ }
32
+ export function registerBridgeTools(server, client, registry) {
33
+ server.registerTool('bridge.list_clients', {
34
+ title: 'List bridge clients',
35
+ description: 'List CC apps that have opened a bridge connection for the share token the MCP is attached with (use_session with an app share link; both ?invite= and ?bridge= links). Returns connection metadata and capabilities (engine.execute = the app hosts the engine). Empty list = no bridge available; the cc MCP still works for ClassCAD-only operations.',
36
+ inputSchema: {},
37
+ }, async () => {
38
+ const sid = client.shareToken;
39
+ const clients = registry.list(sid ?? undefined).map(c => ({
40
+ clientId: c.clientId,
41
+ token: c.token,
42
+ kind: c.kind,
43
+ drawingId: c.drawingId,
44
+ app: c.app,
45
+ appVersion: c.appVersion,
46
+ capabilities: c.capabilities,
47
+ protocolVersion: c.protocolVersion,
48
+ announcedAt: c.announcedAt,
49
+ }));
50
+ return {
51
+ content: [{
52
+ type: 'text',
53
+ text: JSON.stringify({ shareToken: sid, transport: client.transport, count: clients.length, clients, bridgeUrl: registry.url }),
54
+ }],
55
+ };
56
+ });
57
+ server.registerTool('bridge.get_selection', {
58
+ title: 'Get app selection',
59
+ description: 'Return the currently selected entities in the connected CC app. Each entity carries both a high-level kind/position/normal AND the raw {containerId, graphicId, prodRefId} triplet — pass `raw.graphicId` straight into ClassCAD APIs like v1.sketch.create({planeId: ...}).\n\n' +
60
+ 'STALENESS WARNING — the `raw.containerId` reflects the part\'s brep container at the time of the pick. Once any feature mutation (fillet, chamfer, boolean, extrusion, ...) creates a new solid, the part\'s active container id rotates (it tracks `inspect(partId).solids[0]`). Reusing an older containerId in bridge.set_selection round-trips fine but mis-renders: the viewer highlights the wrong graphic or nothing. After any mutation, re-fetch via `inspect(partId).solids[0]` instead of trusting an older raw triplet.',
61
+ inputSchema: {
62
+ clientId: z.string().optional()
63
+ .describe('Specific bridge client id (from bridge.list_clients). Omit to use the most recently announced client for the session.'),
64
+ cached: z.boolean().optional()
65
+ .describe('If true, return the cached selection (updated by push events). If false (default), round-trip to the app.'),
66
+ },
67
+ }, async ({ clientId, cached }) => {
68
+ const { conn, sid } = pickConn(client, registry, clientId);
69
+ if (!conn)
70
+ return noBridgeError(sid);
71
+ try {
72
+ const items = cached && conn.cachedSelection
73
+ ? conn.cachedSelection
74
+ : await conn.request('selection.get');
75
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, count: items.length, items }) }] };
76
+ }
77
+ catch (err) {
78
+ const msg = err instanceof Error ? err.message : String(err);
79
+ return { isError: true, content: [{ type: 'text', text: JSON.stringify({ ok: false, error: msg }) }] };
80
+ }
81
+ });
82
+ server.registerTool('bridge.set_selection', {
83
+ title: 'Set app selection',
84
+ description: "Set the CC app's current selection. Each item is the raw {containerId, graphicId, prodRefId} triplet. The app highlights these entities in its UI.\n\n" +
85
+ 'CRITICAL — `containerId` must be the part\'s **current** brep container id, i.e. `inspect(partId).solids[0]` at call time. Each feature that creates a new solid (fillet, chamfer, boolean, extrusion, ...) rotates the part\'s `solids[0]` to a new id, and the bridge\'s canonical containerId rotates with it. Do NOT copy `containerId` from an earlier bridge.get_selection / bridge.pick if any feature has run since — old containerIds round-trip without error but mis-render in the viewer (the bridge accepts the triplet, but graphicIds created by later features do not exist in the old container, and the viewer either highlights the wrong graphic or nothing).\n\n' +
86
+ 'Construction recipe from API state alone:\n' +
87
+ ' • containerId = `inspect(partId).solids[0]` (re-fetch every time, do not cache across mutations)\n' +
88
+ ' • graphicId = brep element id (from `getBrepGeometryByIndex` / `getGeometryIds`)\n' +
89
+ ' • prodRefId = the value echoed by a recent bridge.get_selection / bridge.pick on the same part (this differs from the part id when the part is instanced through an assembly — copy it from a pick rather than guessing).',
90
+ inputSchema: {
91
+ items: z.array(z.object({
92
+ containerId: z.number(),
93
+ graphicId: z.number(),
94
+ prodRefId: z.number(),
95
+ })).describe('Entities to select.'),
96
+ replace: z.boolean().optional()
97
+ .describe('If true (default), replace current selection. If false, add to it.'),
98
+ clientId: z.string().optional(),
99
+ },
100
+ }, async ({ items, replace, clientId }) => {
101
+ const { conn, sid } = pickConn(client, registry, clientId);
102
+ if (!conn)
103
+ return noBridgeError(sid);
104
+ try {
105
+ const params = { items, replace: replace !== false };
106
+ await conn.request('selection.set', params);
107
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, count: items.length }) }] };
108
+ }
109
+ catch (err) {
110
+ const msg = err instanceof Error ? err.message : String(err);
111
+ return { isError: true, content: [{ type: 'text', text: JSON.stringify({ ok: false, error: msg }) }] };
112
+ }
113
+ });
114
+ }
115
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/tools/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ /**
3
+ * Daemon lifecycle for agents. Shared by the instructions and session_info:
4
+ * hosts may cut long instructions, a tool description always arrives.
5
+ */
6
+ export declare const DAEMON_NOTE: string;
7
+ /**
8
+ * Server instructions for the MCP initialize handshake — hosts put this into
9
+ * the agent's context, so it knows the FULL method surface from turn one
10
+ * (mirrors the method index buerli-ai injects into its system prompt).
11
+ */
12
+ export declare function serverInstructions(): string;
13
+ export declare function registerDocsTools(server: McpServer): void;
@@ -0,0 +1,180 @@
1
+ // Discovery tools: list_methods, describe_method — thin MCP wrappers around the
2
+ // shared @classcad/skill/discovery module (the SAME search/describe logic as
3
+ // buerli-ai): CAD-synonym-expanded ranked search, fuzzy method resolution, and
4
+ // whole-document serving (skill bundle + @classcad/script data-contract docs).
5
+ //
6
+ // Dev override: with CLASSCAD_SKILL_PATH set, docs are read live from that
7
+ // skill checkout instead of the built bundle — edits show up without a rebuild.
8
+ import { z } from 'zod';
9
+ import { existsSync, readFileSync } from 'fs';
10
+ import { createRequire } from 'module';
11
+ import { dirname, join } from 'path';
12
+ import { fileURLToPath } from 'url';
13
+ import { DEFAULT_DAEMON_PORT } from '../ports.js';
14
+ import { RECIPES_POINTER, REFERENCE_IMAGE_POINTER } from '@classcad/skill/prompts';
15
+ import { createDiscovery, DOCS_MAX_KEYS, DOCS_TOOL } from '@classcad/skill/discovery';
16
+ import { docs as scriptDocs } from '@classcad/script/docs';
17
+ // Loaded at runtime, NOT as static imports: tsc otherwise ingests the multi-MB
18
+ // bundle.json as a literal type — observed to OOM the compiler on some setups.
19
+ const requireJson = createRequire(import.meta.url);
20
+ const registry = requireJson('@classcad/skill/method-registry.json');
21
+ const bundle = requireJson('@classcad/skill/bundle.json');
22
+ // Live-doc override (development): resolve doc keys against a skill checkout.
23
+ function diskResolver() {
24
+ const override = process.env.CLASSCAD_SKILL_PATH;
25
+ const root = override ??
26
+ (() => {
27
+ try {
28
+ return dirname(createRequire(import.meta.url).resolve('@classcad/skill/package.json'));
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ })();
34
+ if (!override || !root)
35
+ return undefined; // no override → serve the built bundle
36
+ return (key) => {
37
+ const safe = key.replace(/[^a-zA-Z0-9/_-]/g, '');
38
+ // Keys map to files: references/<domain>/<method>.md, or top-level dirs for
39
+ // prefixed keys (recipes/<name>.md lives at the package root).
40
+ for (const c of [
41
+ join(root, 'references', `${safe}.md`),
42
+ join(root, `${safe}.md`),
43
+ join(root, 'references', `${safe.toUpperCase()}.md`),
44
+ ]) {
45
+ if (existsSync(c))
46
+ return readFileSync(c, 'utf8');
47
+ }
48
+ return null;
49
+ };
50
+ }
51
+ const discovery = createDiscovery({
52
+ registry: registry,
53
+ bundle: bundle,
54
+ extraDocs: scriptDocs,
55
+ resolveDoc: diskResolver(),
56
+ });
57
+ /** This install's CLI, so agents get a command that runs as written. */
58
+ const CLI = `node "${fileURLToPath(new URL('../server.js', import.meta.url))}"`;
59
+ /**
60
+ * Daemon lifecycle for agents. Shared by the instructions and session_info:
61
+ * hosts may cut long instructions, a tool description always arrives.
62
+ */
63
+ export const DAEMON_NOTE = `DAEMON: every tab of this MCP is a session in ONE shared daemon process per machine (127.0.0.1:${DEFAULT_DAEMON_PORT}); it exits a minute after its last session. ` +
64
+ 'Rebuilt MCP/renderer code only runs in a new daemon — an idle one is replaced automatically. Shell commands: ' +
65
+ `\`${CLI} status\` (pid, build, sessions); ` +
66
+ `\`${CLI} stop\` (stops it only when no session is active); ` +
67
+ `\`${CLI} stop --force\` (terminates it NOW — every connected tab, this one included, loses its drawing: ask the user first. ` +
68
+ 'Tabs reconnect to a fresh daemon on their next tool call; tabs started before this MCP version need a host restart). Never kill daemon processes by hand.';
69
+ /**
70
+ * Server instructions for the MCP initialize handshake — hosts put this into
71
+ * the agent's context, so it knows the FULL method surface from turn one
72
+ * (mirrors the method index buerli-ai injects into its system prompt).
73
+ */
74
+ export function serverInstructions() {
75
+ return [
76
+ 'ClassCAD MCP. run_script is the ONLY way to execute API calls (JavaScript against the live CAD session; ' +
77
+ 'state persists between scripts — follow-up scripts ATTACH via api.tree(), never part.create twice). ' +
78
+ 'PLAN FIRST, THEN FETCH ONCE: decide the whole build, pick every method you will need from the index below, ' +
79
+ 'then request ALL their docs in ONE docs([...]) call, recipes first — include "DATA" whenever a script reads api.tree()/api.graphic(). ' +
80
+ 'A docs response is size-capped: keys listed under "NOT included yet" at its top must be requested in a follow-up call before building. ' +
81
+ RECIPES_POINTER +
82
+ ' After that, build in a FEW substantial staged scripts — ' +
83
+ 'not one method per round. Verify with numbers (calculateMassProperties) and snapshot renders. ' +
84
+ 'REFERENCE IMAGES: ' +
85
+ REFERENCE_IMAGE_POINTER,
86
+ '',
87
+ 'ENGINES: the MCP runs on a ClassCAD worker (Drogon server), inside a buerli app (share link with ?bridge=), or on its OWN local ' +
88
+ 'WASM engine (no server, no browser). A share link/URL decides by itself; otherwise use_session(engine="auto"|"drogon"|"wasm") — ' +
89
+ 'auto = worker if reachable else local WASM; a worker that comes up later takes over while the local drawing is still empty (a modeled session stays put). ' +
90
+ '"use WASM / local / offline" → engine "wasm"; "my Drogon/ClassCAD server" → "drogon". ' +
91
+ 'session_info shows what is in use.',
92
+ '',
93
+ DAEMON_NOTE,
94
+ '',
95
+ 'Method Index (v1) — every method, one line. Pick directly from here; use docs([...]) for exact parameters ' +
96
+ 'and trap notes, list_methods to filter. Never conclude an operation does not exist without checking this index:',
97
+ '',
98
+ discovery.methodIndex(),
99
+ '',
100
+ 'Document Index — recipes (composed workflows) and guides (cross-cutting behavior), key — title. ' +
101
+ 'Fetch with docs([...]); list_methods({ search }) also ranks these by topic:',
102
+ '',
103
+ discovery.docIndex(),
104
+ ].join('\n');
105
+ }
106
+ export function registerDocsTools(server) {
107
+ server.registerTool('list_methods', {
108
+ title: 'List API methods',
109
+ description: 'Search/list the v1.<domain>.<method> surface. `search` as a string: one BM25-ranked list over method name + summary (whole words), ' +
110
+ 'CAD synonyms expanded (split→slice, hole→bore, round→fillet, …). `search` as an ARRAY with ONE CONCEPT PER ENTRY ' +
111
+ '(e.g. ["expression", "box", "fastened constraint", "mass properties"]): one group per entry, each ranked on its own, ' +
112
+ 'so every concept gets its own top matches (a group note flags entries that are likely workflows, not methods). ' +
113
+ 'PLUS `docs`: the recipes/guides whose title, headings or text match (e.g. "shared parameters assembly" → ' +
114
+ 'recipes/assembly-parameters) — search by what you want to build, then fetch those docs. ' +
115
+ 'Without `search`: the full method listing plus `documents` (every recipe and guide, key — title). ' +
116
+ 'withSummaries=false returns bare names (token-cheap). ' +
117
+ 'A no-hit result does NOT mean the operation is missing — browse the domain instead.',
118
+ inputSchema: {
119
+ domain: z
120
+ .enum(['assembly', 'common', 'curve', 'drawing2d', 'part', 'sketch', 'solid'])
121
+ .optional()
122
+ .describe('Restrict to one domain.'),
123
+ search: z
124
+ .union([z.string(), z.array(z.string())])
125
+ .optional()
126
+ .describe('Keyword(s) to rank against name + summary (case-insensitive, synonyms expanded). Array = one group per entry — put one concept in each.'),
127
+ withSummaries: z.boolean().optional().describe('Include one-line summaries (default true).'),
128
+ limit: z.number().int().min(1).max(300).optional().describe('Max ranked results: total for a string search (default 25), per group for an array (default 8).'),
129
+ },
130
+ }, async ({ domain, search, withSummaries, limit }) => {
131
+ const result = discovery.searchMethods({ domain, search, withSummaries, limit });
132
+ const hasSearch = search != null && (Array.isArray(search) ? search.length > 0 : String(search).trim() !== '');
133
+ const docs = hasSearch ? discovery.searchDocs({ search }) : null;
134
+ const payload = hasSearch
135
+ ? docs && docs.count > 0
136
+ ? { ...result, docs: docs.docs, docsNote: docs.note }
137
+ : result
138
+ : domain
139
+ ? result
140
+ : { ...result, documents: discovery.docIndex().split('\n') };
141
+ return { content: [{ type: 'text', text: JSON.stringify(payload) }] };
142
+ });
143
+ // Bulk documentation — the PRIMARY doc tool. Contract AND implementation are
144
+ // the shared single source in @classcad/skill/discovery (DOCS_TOOL/bulkDocs);
145
+ // buerli-ai registers the identical tool from the same source.
146
+ server.registerTool(DOCS_TOOL.name, {
147
+ title: 'Fetch documentation (bulk)',
148
+ description: DOCS_TOOL.description,
149
+ inputSchema: {
150
+ keys: z
151
+ .array(z.string())
152
+ .min(1)
153
+ .max(DOCS_MAX_KEYS)
154
+ .describe('Documentation keys, e.g. ["DATA", "v1.part.extrusion", "v1.part.chamfer", "recipes/parametric-part"].'),
155
+ },
156
+ }, async ({ keys }) => {
157
+ const res = await discovery.bulkDocs(keys);
158
+ return { content: [{ type: 'text', text: res.text }] };
159
+ });
160
+ server.registerTool('describe_method', {
161
+ title: 'Describe API method',
162
+ description: 'Full documentation for ONE key — prefer docs([...]) to fetch everything you need in a single round; ' +
163
+ 'use this only for a single follow-up lookup. Accepts full ("v1.part.box") or bare ("box") names — ' +
164
+ 'ambiguous bare names list the candidates. Also serves whole documents ("DATA", "api/part", ' +
165
+ '"recipes/parametric-part"). Same size cap and paging as docs: a large document answers with page 1 and ' +
166
+ 'names the next page ("api/part#2").',
167
+ inputSchema: {
168
+ method: z
169
+ .string()
170
+ .describe('Method name ("v1.part.box", "box"), topic doc ("DATA") or recipe ("recipes/constrained-sketching", "recipes/parametric-part").'),
171
+ },
172
+ }, async ({ method }) => {
173
+ const res = await discovery.bulkDocs([method]);
174
+ if (res.found.length === 0) {
175
+ return { isError: true, content: [{ type: 'text', text: res.text }] };
176
+ }
177
+ return { content: [{ type: 'text', text: res.text }] };
178
+ });
179
+ }
180
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/tools/docs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Client } from '../client.js';
3
+ export declare function registerLifecycleTools(server: McpServer, client: Client): void;
@@ -0,0 +1,156 @@
1
+ // Lifecycle tools: clear, save, load.
2
+ import { z } from 'zod';
3
+ export function registerLifecycleTools(server, client) {
4
+ server.registerTool('clear', {
5
+ title: 'Clear drawing',
6
+ description: 'Delete all objects in the current drawing. Wraps v1.common.clear.',
7
+ inputSchema: {
8
+ keepIds: z.array(z.union([z.string(), z.number()])).optional()
9
+ .describe('Object IDs to preserve. Omit to wipe everything.'),
10
+ },
11
+ }, async ({ keepIds }) => {
12
+ const arg = keepIds ? { keepIds } : {};
13
+ const r = await client.execute({ 'v1.common.clear': [arg] });
14
+ return {
15
+ content: [{
16
+ type: 'text',
17
+ text: JSON.stringify({ ok: r.maxLevel <= 31, maxLevel: r.maxLevel, messages: r.messages }),
18
+ }],
19
+ };
20
+ });
21
+ server.registerTool('save', {
22
+ title: 'Save drawing',
23
+ description: 'Serialize the current drawing. Returns base64-encoded content. Formats: STP (STEP), STL, JSON. (OFB export is not available in this release.)',
24
+ inputSchema: {
25
+ format: z.enum(['STP', 'STL', 'JSON']).describe('Output format.'),
26
+ },
27
+ }, async ({ format }) => {
28
+ const args = { format, encoding: 'base64' };
29
+ if (format === 'STP')
30
+ args.stp = { version: 2 };
31
+ if (format === 'STL')
32
+ args.stl = { binary: true, facetingTol: 0.1, angleTol: 6 };
33
+ const r = await client.execute({
34
+ 'v1.common.save': [args],
35
+ });
36
+ return {
37
+ content: [{
38
+ type: 'text',
39
+ text: JSON.stringify({
40
+ format,
41
+ success: r.result?.success ?? false,
42
+ bytes: r.result?.content ? Buffer.from(r.result.content, 'base64').length : 0,
43
+ content: r.result?.content ?? null,
44
+ maxLevel: r.maxLevel,
45
+ }),
46
+ }],
47
+ };
48
+ });
49
+ server.registerTool('load', {
50
+ title: 'Load drawing',
51
+ description: 'Load a previously saved drawing from base64-encoded content. Format must match what save produced.',
52
+ inputSchema: {
53
+ format: z.enum(['OFB', 'STP', 'STL', 'JSON']).describe('Input format.'),
54
+ content: z.string().describe('Base64-encoded payload.'),
55
+ },
56
+ }, async ({ format, content }) => {
57
+ // NOTE: the engine's load parameter is `data` (a prior save's payload) —
58
+ // `content` is silently ignored (error 1004 "Either data, file or url…").
59
+ const r = await client.execute({
60
+ 'v1.common.load': [{ format, encoding: 'base64', data: content, doClear: 1 }],
61
+ });
62
+ return {
63
+ content: [{
64
+ type: 'text',
65
+ text: JSON.stringify({ ok: r.maxLevel <= 31, maxLevel: r.maxLevel, messages: r.messages }),
66
+ }],
67
+ };
68
+ });
69
+ // ── checkpoint / restore — cheap rollback (same semantics as buerli-ai) ─────
70
+ // A failed multi-feature attempt leaves half-built features and consumed
71
+ // bodies behind; recovering by delete-archaeology is error-prone. checkpoint
72
+ // = in-memory OFB save held in THIS server process (never enters the model
73
+ // context), restore = clear + reload. Keyed per reconnect generation — a
74
+ // use_session switch invalidates checkpoints from the previous session.
75
+ const MAX_CHECKPOINTS = 5;
76
+ const checkpoints = new Map();
77
+ let checkpointGen = -1;
78
+ function bucket() {
79
+ if (checkpointGen !== client.generation) {
80
+ checkpoints.clear();
81
+ checkpointGen = client.generation;
82
+ }
83
+ return checkpoints;
84
+ }
85
+ server.registerTool('checkpoint', {
86
+ title: 'Checkpoint drawing',
87
+ description: 'Save the WHOLE drawing state under a label (in-memory, server-side — cheap and instant). ' +
88
+ 'Use BEFORE risky sequences (large booleans, trims, experiments): if the attempt goes wrong, ' +
89
+ 'restore({label}) brings the drawing back exactly instead of delete-archaeology. ' +
90
+ 'Re-using a label overwrites it; at most 5 checkpoints are kept (oldest dropped). ' +
91
+ 'Checkpoints die with the server process and with a use_session switch.',
92
+ inputSchema: {
93
+ label: z.string().optional().describe('Checkpoint name, e.g. "before-boolean". Default "checkpoint".'),
94
+ },
95
+ }, async ({ label }) => {
96
+ const name = (label || 'checkpoint').trim().slice(0, 60) || 'checkpoint';
97
+ // The one OFB save this release makes: held in this process, never returned.
98
+ const r = await client.execute({
99
+ 'v1.common.save': [{ format: 'OFB', encoding: 'base64' }],
100
+ }, { internalOfb: true });
101
+ if (!r.result?.content) {
102
+ return { isError: true, content: [{ type: 'text', text: JSON.stringify({ error: 'Checkpoint failed: the engine returned no OFB data.', maxLevel: r.maxLevel }) }] };
103
+ }
104
+ const m = bucket();
105
+ m.delete(name);
106
+ m.set(name, { data: r.result.content, createdAt: Date.now() });
107
+ while (m.size > MAX_CHECKPOINTS) {
108
+ const oldest = m.keys().next().value;
109
+ m.delete(oldest);
110
+ }
111
+ return {
112
+ content: [{
113
+ type: 'text',
114
+ text: JSON.stringify({
115
+ label: name,
116
+ size: Math.floor((r.result.content.length * 3) / 4),
117
+ stored: [...m.keys()],
118
+ note: `Drawing state saved server-side. restore({ label: "${name}" }) brings it back exactly.`,
119
+ }),
120
+ }],
121
+ };
122
+ });
123
+ server.registerTool('restore', {
124
+ title: 'Restore checkpoint',
125
+ description: 'Replace the drawing with a previously checkpointed state (clear + reload). Without a label, ' +
126
+ 'the MOST RECENT checkpoint is restored. All ids from after the checkpoint are stale afterwards — ' +
127
+ 're-read them (tree/find) before further operations.',
128
+ inputSchema: {
129
+ label: z.string().optional().describe('Checkpoint name. Default: the most recently created one.'),
130
+ },
131
+ }, async ({ label }) => {
132
+ const m = bucket();
133
+ if (m.size === 0) {
134
+ return { isError: true, content: [{ type: 'text', text: JSON.stringify({ error: 'No checkpoints exist for this session. Create one with checkpoint first.' }) }] };
135
+ }
136
+ const name = (label && label.trim()) ||
137
+ [...m.entries()].sort((a, b) => b[1].createdAt - a[1].createdAt)[0][0];
138
+ const snap = m.get(name);
139
+ if (!snap) {
140
+ return { isError: true, content: [{ type: 'text', text: JSON.stringify({ error: `No checkpoint named "${name}".`, available: [...m.keys()] }) }] };
141
+ }
142
+ const r = await client.execute({ 'v1.common.load': [{ format: 'OFB', encoding: 'base64', data: snap.data, doClear: 1 }] });
143
+ return {
144
+ content: [{
145
+ type: 'text',
146
+ text: JSON.stringify({
147
+ restored: name,
148
+ ok: r.maxLevel <= 31,
149
+ maxLevel: r.maxLevel,
150
+ note: 'Drawing replaced with the checkpointed state. All ids from after the checkpoint are stale — re-read them (tree/find) before further operations.',
151
+ }),
152
+ }],
153
+ };
154
+ });
155
+ }
156
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/tools/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Client } from '../client.js';
3
+ export declare function registerScriptTool(server: McpServer, client: Client): void;
@@ -0,0 +1,102 @@
1
+ import { setDrawingBusy } from '../queue.js';
2
+ // run_script — execute model-written JavaScript against the live session.
3
+ //
4
+ // Powered by @classcad/script: the same script medium as buerli-ai and
5
+ // headless Node. Scripts using the guaranteed surface (api.v1.*,
6
+ // api.tree(), api.graphic(), api.env) run unchanged in all of them.
7
+ import { z } from 'zod';
8
+ import { createRequire } from 'module';
9
+ import { runScript, isSessionBusy } from '@classcad/script';
10
+ // Runtime require — keeps the registry JSON out of tsc's type space (OOM risk).
11
+ const REGISTRY = createRequire(import.meta.url)('@classcad/skill/method-registry.json');
12
+ /**
13
+ * Adapt the MCP's WS client to the @classcad/script session contract. Tree and
14
+ * graphic come from the client's cached, pull-on-demand accessors; the
15
+ * emission config pair lets runScript suppress payloads for the duration of
16
+ * the script (and restore the connection's previous flags afterwards), so a
17
+ * 100-command script costs 100 small Results while the MCP's connection
18
+ * otherwise keeps the engine defaults - important when it is docked into a
19
+ * session shared with an interactive app.
20
+ */
21
+ const sessions = new WeakMap();
22
+ function sessionFor(client) {
23
+ const previous = sessions.get(client);
24
+ if (previous)
25
+ return previous;
26
+ const session = {
27
+ executionKey: client,
28
+ env: 'node',
29
+ execute: (task) => client.execute(task),
30
+ getTree: (o) => client.getTree(o),
31
+ getGraphic: (o) => client.getGraphic(o),
32
+ getEmissionConfig: () => client.getEmissionConfig(),
33
+ setEmissionConfig: (partial) => client.setEmissionConfig(partial),
34
+ };
35
+ sessions.set(client, session);
36
+ return session;
37
+ }
38
+ export function registerScriptTool(server, client) {
39
+ setDrawingBusy(server, () => isSessionBusy(sessionFor(client)));
40
+ server.registerTool('run_script', {
41
+ title: 'Run script',
42
+ description: 'Execute JavaScript against the CAD session — THE tool for any build involving computation, ' +
43
+ 'repetition, or more than a handful of operations. The script runs as an async function body with:\n' +
44
+ '• api.v1.<domain>.<method>(params) — ClassCAD calls, await-able, → { result, maxLevel, messages }. ' +
45
+ 'Typos throw immediately with suggestions. Strict: a call that ends at maxLevel ≥ 51 THROWS (the message carries ' +
46
+ 'the engine error) — method docs describing "null/[] with maxLevel 51" are the non-script result; wrap intentional ' +
47
+ 'probes (e.g. getGeometryIds where a miss is expected) in try/catch.\n' +
48
+ '• await api.tree({ refresh? }) — the structure tree (id → node): find parts, features, sketches by class/name. ' +
49
+ 'Returns a Promise: without await, Object.values(api.tree()) is silently [] (looks like an empty drawing).\n' +
50
+ '• await api.graphic({ recalc? }) — the graphic payload (containers with face meshes, edges, vertices): ' +
51
+ 'find and FILTER GEOMETRY yourself — e.g. locate a bore wall by vertex radius, collect edge ids for a chamfer. ' +
52
+ 'Exact shapes + selection idioms: describe_method("DATA").\n' +
53
+ '• Math, full JS (variables, loops, functions); console.log/log(...) captured and returned.\n' +
54
+ '• return <small summary> — results are size-capped; keep big data in the drawing, not the return value.\n' +
55
+ 'Compute coordinates IN the script (trigonometry, loops) instead of inlining hand-evaluated numbers. ' +
56
+ 'Pass recalc:false to api.graphic() in solid.*/entity-injection sessions (a recalc invalidates curve shape ids and can destroy injected bodies in complex cases). ' +
57
+ 'No DOM/network/filesystem access; awaited work times out (default 60s). ' +
58
+ 'Prefer several small verified scripts over one huge one — STATE PERSISTS in the drawing between ' +
59
+ 'scripts: a follow-up script ATTACHES to the existing model (re-discover via await api.tree(); tree ids' +
60
+ 'are stable; NEVER part.create when a part already exists). Even a single operation is a run_script.',
61
+ inputSchema: {
62
+ script: z
63
+ .string()
64
+ .describe('JavaScript source. Executed as an async function body — use await directly, return a (small) summary value.'),
65
+ label: z.string().optional().describe('Short label describing what this script does.'),
66
+ timeoutMs: z.number().int().min(1000).max(300000).optional().describe('Timeout for awaited work in ms (default 60000).'),
67
+ },
68
+ }, async ({ script, timeoutMs }, extra) => {
69
+ let operationCount = 0;
70
+ const session = sessionFor(client);
71
+ const res = await runScript(script, session, { registry: REGISTRY, timeoutMs, strict: true, signal: extra.signal,
72
+ onOperation: event => {
73
+ if (extra._meta?.progressToken !== undefined && event.phase === 'start') {
74
+ void extra.sendNotification({ method: 'notifications/progress', params: {
75
+ progressToken: extra._meta.progressToken, progress: ++operationCount,
76
+ message: `${event.method}${event.name ? ': ' + event.name : ''}`,
77
+ } }).catch(() => { });
78
+ }
79
+ },
80
+ });
81
+ // The script ran with payloads suppressed and the connection's flags
82
+ // are restored again. One GetTree now: its Result carries the full
83
+ // structure and graphic, refreshes our caches, and - broadcast by the
84
+ // server - brings a shared session's other clients up to date with
85
+ // everything the script changed while they received nothing.
86
+ try {
87
+ if (!isSessionBusy(session))
88
+ await client.pull();
89
+ }
90
+ catch {
91
+ /* the tools pull again on demand */
92
+ }
93
+ if (!res.ok) {
94
+ const tail = res.logs.length ? `\nConsole output before the error:\n${res.logs.slice(-20).join('\n')}` : '';
95
+ return { content: [{ type: 'text', text: `${res.error}${tail}` }], isError: true };
96
+ }
97
+ return {
98
+ content: [{ type: 'text', text: JSON.stringify({ returned: res.returned, logs: res.logs }) }],
99
+ };
100
+ });
101
+ }
102
+ //# sourceMappingURL=script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script.js","sourceRoot":"","sources":["../../src/tools/script.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Client } from '../client.js';
3
+ export declare function registerSnapshotTool(server: McpServer, client: Client): void;