@classcad/mcp 0.2.2 → 0.3.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.
- package/README.md +128 -87
- package/dist/app/SOURCE.txt +4 -0
- package/dist/app/assets/index-54a14bc0.css +8 -0
- package/dist/app/assets/index-c650c783.js +11257 -0
- package/dist/app/assets/troika-three-text.esm-188d86b5.js +220 -0
- package/dist/app/favicon.svg +98 -0
- package/dist/app/index.html +19 -0
- package/dist/app/manifest.json +14 -0
- package/dist/auth.d.ts +8 -0
- package/dist/auth.js +49 -10
- package/dist/auth.js.map +1 -1
- package/dist/backend.d.ts +8 -0
- package/dist/backend.js +39 -0
- package/dist/backend.js.map +1 -0
- package/dist/client.d.ts +48 -11
- package/dist/client.js +152 -167
- package/dist/client.js.map +1 -1
- package/dist/daemon.d.ts +0 -2
- package/dist/daemon.js +77 -62
- package/dist/daemon.js.map +1 -1
- package/dist/engine/key.d.ts +29 -0
- package/dist/engine/key.js +145 -0
- package/dist/engine/key.js.map +1 -0
- package/dist/engine/wasm-worker.js +7 -7
- package/dist/engine/wasm-worker.js.map +1 -1
- package/dist/engine/wasm.d.ts +16 -14
- package/dist/engine/wasm.js +22 -30
- package/dist/engine/wasm.js.map +1 -1
- package/dist/mcp-server.d.ts +4 -4
- package/dist/mcp-server.js +72 -28
- package/dist/mcp-server.js.map +1 -1
- package/dist/queue.js +1 -1
- package/dist/queue.js.map +1 -1
- package/dist/server.js +69 -36
- package/dist/server.js.map +1 -1
- package/dist/share/hub.d.ts +70 -0
- package/dist/share/hub.js +494 -0
- package/dist/share/hub.js.map +1 -0
- package/dist/share/protocol.d.ts +87 -0
- package/dist/share/protocol.js +54 -0
- package/dist/share/protocol.js.map +1 -0
- package/dist/share/relay.d.ts +45 -0
- package/dist/share/relay.js +230 -0
- package/dist/share/relay.js.map +1 -0
- package/dist/share/server.d.ts +27 -0
- package/dist/share/server.js +399 -0
- package/dist/share/server.js.map +1 -0
- package/dist/share/session.d.ts +120 -0
- package/dist/share/session.js +336 -0
- package/dist/share/session.js.map +1 -0
- package/dist/tools/auth.js +1 -1
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/docs.js +3 -2
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/lifecycle.js +36 -9
- package/dist/tools/lifecycle.js.map +1 -1
- package/dist/tools/share.d.ts +18 -0
- package/dist/tools/share.js +277 -0
- package/dist/tools/share.js.map +1 -0
- package/dist/tools/viewer.d.ts +1 -1
- package/dist/tools/viewer.js +11 -6
- package/dist/tools/viewer.js.map +1 -1
- package/package.json +4 -3
- package/dist/bridge/protocol.d.ts +0 -87
- package/dist/bridge/protocol.js +0 -14
- package/dist/bridge/protocol.js.map +0 -1
- package/dist/bridge/server.d.ts +0 -35
- package/dist/bridge/server.js +0 -236
- package/dist/bridge/server.js.map +0 -1
- package/dist/tools/bridge.d.ts +0 -4
- package/dist/tools/bridge.js +0 -115
- package/dist/tools/bridge.js.map +0 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// tools/share.ts — the session's company, wired into its tools.
|
|
2
|
+
//
|
|
3
|
+
// Every session can be shared (share/session.ts): the CAD app docks into it
|
|
4
|
+
// with a link, and whoever is in it shows what they have selected.
|
|
5
|
+
// attachShare() patches registerTool (like serializeTools and requireSignIn)
|
|
6
|
+
// so that the first model of a session brings the app up — and says so, once:
|
|
7
|
+
// in the user's browser, or in the host's own browser pane where it has one
|
|
8
|
+
// (then the agent opens it; see Show in share/session.ts). The tools: `view`
|
|
9
|
+
// (the link), `share` (a link for everybody else, where a relay is there to
|
|
10
|
+
// share on), `get_selection` / `set_selection` (pointing, both ways).
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { hostName } from '../auth.js';
|
|
13
|
+
import { enqueue } from '../queue.js';
|
|
14
|
+
import { openShare } from '../share/session.js';
|
|
15
|
+
/** Tools after which the model, or the engine under it, may be another one. */
|
|
16
|
+
const CHANGES_MODEL = new Set(['run_script', 'clear', 'load', 'restore', 'use_session']);
|
|
17
|
+
/** Where an agent opens a page itself. Named for the host that has one, so the agent knows which tool is meant. */
|
|
18
|
+
const OWN_PANE = 'your own browser pane, beside the conversation — in the Claude desktop app with `preview_start`, the Browser pane tool, and its `url` set to this link';
|
|
19
|
+
/** How the app reaches the user, as the agent is told from the first turn on: it depends on the host (Show in share/session.ts). */
|
|
20
|
+
const REACH = {
|
|
21
|
+
host: 'SHOWING IT IS YOUR JOB HERE: your host has a browser pane of its own, and the app belongs there — not in the user\'s other browser. The result of the first script that makes a model ends with "App: <link>". Open that link in your browser pane right then, before your next step: in the Claude desktop app with `preview_start`, the Browser pane tool, and its `url` set to the link. Open it ONCE — the pane stays live and follows every change, so never open or reload it after later scripts. Then tell the user in a few words that the model is in the browser pane; the link need not be in your answer. Only if you have no browser pane tool: call `view` with open: true, which opens the user\'s default browser, and say so. ',
|
|
22
|
+
browser: 'With the first model it opens by itself in the user\'s default browser, where there is one: the result of that script ends with "App: <link>" and says whether it did. Tell the user ONCE that the app is there, with the link on its own line. ',
|
|
23
|
+
off: 'The result of the first script that makes a model ends with "App: <link>": give the user that link ONCE, on its own line. ',
|
|
24
|
+
};
|
|
25
|
+
/** What the agent is told about the link for everybody else, from the first turn on. */
|
|
26
|
+
const SHARE_NOTE = {
|
|
27
|
+
ask: 'The app\'s link works on this machine only. When the user wants somebody else to see or work on the model — another person, or themselves on another device — call `share`: it returns a link that works from anywhere. Only when they ask: that link lets everyone who has it into the session. ',
|
|
28
|
+
always: 'ONE LINK: where the share service is reachable, the app\'s link is a share link (https://…): the same link you open is the one the user can send to somebody else, or open on another device — say so ONCE, when you tell them about the app, and that everyone who has it is let into the session. A link on 127.0.0.1 instead means the share service was not reachable: then the app works on this machine only, which you need not explain. If the share link does not load in the browser or stays without the model, do not retry it: call `view` with local: true and open the link it returns — the app on this machine, which needs no service. `share` with stop: true takes the share link back. ',
|
|
29
|
+
off: '',
|
|
30
|
+
};
|
|
31
|
+
/** The part of the server instructions about the app, written for the way this session's app reaches the user and for how it is shared. */
|
|
32
|
+
export const appNote = (show, sharing = 'off') => 'APP: every session has a live CAD app (Buerligons), docked into this session\'s engine. It shows every change as it happens, and the user can work in it too — turn the model, select, measure, edit sketches and features, export. ' +
|
|
33
|
+
REACH[show] +
|
|
34
|
+
'Do NOT repeat the link unasked in later answers: it is said once per session. When the user asks where the model is, wants to see it again or asks for the link, call `view`: it knows whether they still have the app open, brings it up again if not, and returns the link. ' +
|
|
35
|
+
SHARE_NOTE[sharing] +
|
|
36
|
+
'The user and you work on the SAME model: what they change in the app is in the tree you read next, so read it again (api.tree(), `tree`) instead of trusting ids from before their turn. ' +
|
|
37
|
+
'When the user points instead of naming ("this face", "the selected edges", "here"), call `get_selection`; `set_selection` highlights something for them.';
|
|
38
|
+
/** How a participant is named to the agent. */
|
|
39
|
+
const peerLabel = (peer) => {
|
|
40
|
+
const who = peer.identity?.name || peer.inviteName || '';
|
|
41
|
+
const app = peer.identity?.app;
|
|
42
|
+
return [app, who && who !== app ? `(${who})` : ''].filter(Boolean).join(' ') || 'a participant';
|
|
43
|
+
};
|
|
44
|
+
/** What the agent is told of a link that works from anywhere. */
|
|
45
|
+
const SHARED_WITH = 'It works from anywhere, and everyone who has it is in this session like the user at the app: they see the model live and can change it. Give it to the user, on its own line, when they want to show the model to somebody else or open it on another device — and to nobody but the user. `share` with stop: true takes it back.';
|
|
46
|
+
/** What the agent is told when the app's link is the share link. */
|
|
47
|
+
const ONE_LINK = 'This link is a share link: it works from anywhere. Give it to the user once, on its own line, and tell them that they can send it to others or open it on another device, and that everyone who has it is in this session and can change the model. ' +
|
|
48
|
+
'Should it not load, or stay without the model: call `view` with local: true and open the link it returns instead (the app on this machine).';
|
|
49
|
+
/** What the agent is told when the share link the app was offered under stopped working. */
|
|
50
|
+
const FALLBACK = 'The share link this session\'s app was opened under does not work any more (the share service cannot be reached), and nobody is left in the app. This link is the app on this machine: it needs no service. ';
|
|
51
|
+
/** What the agent is told when the app is offered: once per session (and once more, should its share link stop working). */
|
|
52
|
+
const offerText = (offer) => (offer.fallback ? FALLBACK : '') + appText(offer) + (offer.shared ? `\n${ONE_LINK}` : '');
|
|
53
|
+
const appText = (offer) => {
|
|
54
|
+
if (offer.how === 'host')
|
|
55
|
+
return (`App: ${offer.url}\n` +
|
|
56
|
+
`This session's CAD app shows this model live, and the user can work in it — but the user does not see it yet. Open this link now, before your next step, in ${OWN_PANE}. ` +
|
|
57
|
+
'Once is enough: the pane follows every change by itself. Then tell the user in a few words that the model is in the browser pane. ' +
|
|
58
|
+
'No browser pane tool? Call `view` with open: true instead: it opens the user\'s default browser. This is said once: later results do not repeat the link.');
|
|
59
|
+
if (offer.how === 'opened')
|
|
60
|
+
return (`App: ${offer.url}\n` +
|
|
61
|
+
'This session\'s CAD app has just been opened in the user\'s browser, with this model in it; it shows every change live, and the user can work in it. ' +
|
|
62
|
+
'Tell the user so once, with the link on its own line. This is said once: later results do not repeat the link, `view` returns it again.');
|
|
63
|
+
return (`App: ${offer.url}\n` +
|
|
64
|
+
'This session\'s CAD app shows this model live, and the user can work in it. Tell the user once that they can open the model there, with this link on its own line. This is said once: later results do not repeat it, `view` returns it again.');
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Opens the session's sharing and wires it into the tools registered after it:
|
|
68
|
+
* a tool that may have changed the model offers the app with the first model
|
|
69
|
+
* (once), and the sharing ends when the engine client is closed.
|
|
70
|
+
*/
|
|
71
|
+
export async function attachShare(server, client, version, show, sharing, log) {
|
|
72
|
+
const mode = process.env.CLASSCAD_VIEWER;
|
|
73
|
+
const share = await openShare({
|
|
74
|
+
client,
|
|
75
|
+
queue: work => enqueue(server, work),
|
|
76
|
+
app: mode !== 'off' && mode !== 'readonly',
|
|
77
|
+
show,
|
|
78
|
+
sharing,
|
|
79
|
+
identity: () => {
|
|
80
|
+
const host = server.server.getClientVersion()?.name;
|
|
81
|
+
return { app: 'classcad-mcp', version, kind: 'agent', ...(host ? { name: hostName(host) } : {}) };
|
|
82
|
+
},
|
|
83
|
+
log,
|
|
84
|
+
});
|
|
85
|
+
const original = server.registerTool.bind(server);
|
|
86
|
+
server.registerTool = (name, config, handler) => {
|
|
87
|
+
if (!CHANGES_MODEL.has(name))
|
|
88
|
+
return original(name, config, handler);
|
|
89
|
+
const watched = async (...args) => {
|
|
90
|
+
// Where every session is shared, the relay is asked while the tool runs: its link is there when the model is.
|
|
91
|
+
share.warm();
|
|
92
|
+
const result = await handler(...args);
|
|
93
|
+
// With the first model the app is offered, once. (Also where the script failed halfway: what it made is there to look at.)
|
|
94
|
+
const offer = await share.touch().catch(err => (log?.(`share: ${err?.message ?? err}`), null));
|
|
95
|
+
if (offer && result && Array.isArray(result.content))
|
|
96
|
+
result.content.push({ type: 'text', text: offerText(offer) });
|
|
97
|
+
return result;
|
|
98
|
+
};
|
|
99
|
+
return original(name, config, watched);
|
|
100
|
+
};
|
|
101
|
+
// The company ends with the session.
|
|
102
|
+
const close = client.close.bind(client);
|
|
103
|
+
client.close = () => {
|
|
104
|
+
share.close();
|
|
105
|
+
close();
|
|
106
|
+
};
|
|
107
|
+
return share;
|
|
108
|
+
}
|
|
109
|
+
/** `view`: the app docked into this session — or, where this build has none, the read-only 3D view. */
|
|
110
|
+
export function registerViewTool(server, share, viewer) {
|
|
111
|
+
const app = share.url !== null;
|
|
112
|
+
server.registerTool('view', {
|
|
113
|
+
title: app ? 'Open the app' : 'Open the 3D view',
|
|
114
|
+
description: app
|
|
115
|
+
? 'The live CAD app of this session (Buerligons), docked into the same engine: returns its link and says how to bring it up. Where the session is shared from the start the link is its share link (it works from anywhere); local: true returns the link that works on this machine only instead — for when the share link does not load. The app shows every change as it happens, and the user can work in it — turn and zoom, select, measure, edit sketches and features, export STEP or STL. Call it when the user asks to see, turn or edit the model, or asks for the link. If the user already has it open, nothing is opened. Otherwise it is opened in the user\'s default browser — or, when your host has a browser pane of its own, you are told to open the link there. open: false only returns the link; open: true opens the user\'s default browser whatever the host.'
|
|
116
|
+
: 'The live 3D view of this session\'s model: returns its link and brings it up in the user\'s browser. The view follows every change, turns and zooms, has light and dark, and lets the user download STEP, STL or glTF. Call it when the user wants to see or turn the model; hand them the link as a clickable link on its own line. open: false only returns the link.',
|
|
117
|
+
inputSchema: {
|
|
118
|
+
open: z.boolean().optional().describe('true: open the user\'s default browser now. false: only return the link. Default: bring it up where it belongs, unless the user already has it open.'),
|
|
119
|
+
local: z.boolean().optional().describe('Return the link of the app on this machine (127.0.0.1), which needs no share service: use it when the share link does not load.'),
|
|
120
|
+
},
|
|
121
|
+
}, async ({ open, local }) => {
|
|
122
|
+
// A guest in an app's session: the app is the view, and the user has it open.
|
|
123
|
+
if (!share.hosting) {
|
|
124
|
+
const others = share.peers().map(peerLabel);
|
|
125
|
+
return {
|
|
126
|
+
content: [{
|
|
127
|
+
type: 'text',
|
|
128
|
+
text: `This session belongs to an app the user already has open${others.length ? ` (${others.join(', ')})` : ''}: what you build shows up there as it happens. There is nothing else to open.`,
|
|
129
|
+
}],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const url = (local ? share.url : share.appUrl) ?? share.url ?? viewer?.url;
|
|
133
|
+
if (!url)
|
|
134
|
+
return { isError: true, content: [{ type: 'text', text: 'There is no view in this MCP (CLASSCAD_VIEWER=off, or its local listener could not start).' }] };
|
|
135
|
+
if (!share.url) {
|
|
136
|
+
const opened = open === false ? false : viewer.open();
|
|
137
|
+
return { content: [{ type: 'text', text: `3D view of this session: ${url}\n${opened ? 'It has been opened in the user\'s browser.' : 'Give the user this link to open it.'} It follows the model live.` }] };
|
|
138
|
+
}
|
|
139
|
+
// An app that is docked is an app somebody has open: one more tab would be one more guest, not a better look.
|
|
140
|
+
// (Asked for the link of this machine, the one they have does not work for them.)
|
|
141
|
+
const docked = local ? 0 : share.docked();
|
|
142
|
+
// The link is in this answer: the first model need not offer it again.
|
|
143
|
+
share.offered(url);
|
|
144
|
+
let state;
|
|
145
|
+
if (open === true)
|
|
146
|
+
state = share.open(url) ? 'It has been opened in the user\'s default browser.' : 'There is no browser to open here: give the user this link.';
|
|
147
|
+
else if (open === false)
|
|
148
|
+
state = 'Nothing was opened: give the user this link.';
|
|
149
|
+
else if (docked > 0)
|
|
150
|
+
state = 'The user already has it open: nothing was opened.';
|
|
151
|
+
else if (share.show === 'host')
|
|
152
|
+
state = `The user does not see it yet: open this link now in ${OWN_PANE}. No browser pane tool? Call \`view\` with open: true: it opens the user's default browser.`;
|
|
153
|
+
else if (share.show === 'browser' && share.open(url))
|
|
154
|
+
state = 'It has been opened in the user\'s default browser.';
|
|
155
|
+
else
|
|
156
|
+
state = 'Give the user this link to open it.';
|
|
157
|
+
// Which link it is: the one that works from anywhere, the one of this machine, or both where they differ.
|
|
158
|
+
const isShared = url === share.sharedUrl;
|
|
159
|
+
const reach = isShared
|
|
160
|
+
? ' This link is the session\'s share link: it works from anywhere, and everyone who has it is in the session.'
|
|
161
|
+
: share.sharedUrl
|
|
162
|
+
? `\nShared with others at: ${share.sharedUrl}`
|
|
163
|
+
: share.sharing === 'always'
|
|
164
|
+
? ' This link works on this machine only: the session is not shared right now (`share` shares it).'
|
|
165
|
+
: '';
|
|
166
|
+
return { content: [{ type: 'text', text: `App of this session: ${url}\n${state} It shows the model live, and the user can work in it.${reach}` }] };
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/** `share`: a link to this session that works beyond this machine, and taking it back. */
|
|
170
|
+
export function registerShareTool(server, share) {
|
|
171
|
+
if (share.sharing === 'off')
|
|
172
|
+
return;
|
|
173
|
+
server.registerTool('share', {
|
|
174
|
+
title: 'Share the session',
|
|
175
|
+
description: 'A link to this session\'s app that works from ANYWHERE: for another person, or for the user on another device (the link `view` returns works on this machine only). Whoever opens it is in this session like the user at the app — they see the model live as you build it, and can turn, select and edit it. ' +
|
|
176
|
+
'Call it when the user asks to share, send or show the model to somebody, or to open it somewhere else. Not on your own: the link lets EVERYONE who has it into the session, so it is a secret between the user and whom they send it to — hand it to the user, as a clickable link on its own line, and put it nowhere else. ' +
|
|
177
|
+
'The session stays on this machine; guests reach it through the ClassCAD share relay, which passes the data on and keeps none of it. The link works until the session ends or sharing is stopped. When the share service cannot be reached, nothing fails: you are told so, the app keeps working on this machine, and the session is shared as soon as the service is back. stop: true takes it back: it stops working and whoever joined with it is disconnected. Sharing again afterwards gives a new link.',
|
|
178
|
+
inputSchema: {
|
|
179
|
+
stop: z.boolean().optional().describe('Stop sharing: the link stops working and its guests are disconnected.'),
|
|
180
|
+
},
|
|
181
|
+
}, async ({ stop }) => {
|
|
182
|
+
const text = (t, isError = false) => ({ ...(isError ? { isError: true } : {}), content: [{ type: 'text', text: t }] });
|
|
183
|
+
if (stop) {
|
|
184
|
+
const stopped = share.unpublish();
|
|
185
|
+
if (!stopped)
|
|
186
|
+
return text('The session was not shared.');
|
|
187
|
+
const gone = stopped.kicked ? `, and ${stopped.kicked} app${stopped.kicked === 1 ? ' that was open under it was' : 's that were open under it were'} disconnected` : '';
|
|
188
|
+
// Where the share link is the app's link, the user had the app open under it too.
|
|
189
|
+
const after = share.sharing === 'always' ? ' The user\'s own app was one of them if it was open under that link: call `view` for the link of the app on this machine.' : ' The user\'s own app stays docked.';
|
|
190
|
+
return text(`The session is not shared any more: the link stopped working${gone}.${after}`);
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const known = share.sharedUrl !== null;
|
|
194
|
+
const link = await share.publish();
|
|
195
|
+
if (link.live)
|
|
196
|
+
return text(`Share link: ${link.url}\n${known ? (share.sharing === 'always' ? 'This is the link the app is open under: one link for the user and for everybody else. ' : 'The session is shared already: this is its link. ') : ''}${SHARED_WITH}`);
|
|
197
|
+
// No relay is not an error: the session works, on this machine, and is shared as soon as the relay takes it.
|
|
198
|
+
return text(`The session is not shared right now: the share service does not take it (${link.problem}). Nothing is wrong with the session itself` +
|
|
199
|
+
(share.url ? ` — its app works on this machine: ${share.url}` : '') +
|
|
200
|
+
'. It is offered again by itself until that works: call `share` again in a moment for the link.');
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
return text(err?.message ?? String(err), true);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/** `get_selection` / `set_selection`: pointing between the user at an app and the agent, both ways. */
|
|
208
|
+
export function registerSelectionTools(server, client, share) {
|
|
209
|
+
/** What the others have selected, with the tree's word on each object. */
|
|
210
|
+
const describe = async (selections) => {
|
|
211
|
+
let tree = {};
|
|
212
|
+
try {
|
|
213
|
+
tree = (await client.getTree());
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
/* the ids alone still say what was picked */
|
|
217
|
+
}
|
|
218
|
+
return selections.map(s => ({
|
|
219
|
+
who: peerLabel(s.peer),
|
|
220
|
+
total: s.total,
|
|
221
|
+
...(s.total > s.items.length ? { shown: s.items.length } : {}),
|
|
222
|
+
items: s.items.map(item => {
|
|
223
|
+
const node = tree[String(item.objectId)];
|
|
224
|
+
return node ? { ...item, object: { class: node.class, name: node.name } } : item;
|
|
225
|
+
}),
|
|
226
|
+
}));
|
|
227
|
+
};
|
|
228
|
+
const nobody = () => ({
|
|
229
|
+
isError: true,
|
|
230
|
+
content: [{
|
|
231
|
+
type: 'text',
|
|
232
|
+
text: JSON.stringify({
|
|
233
|
+
ok: false,
|
|
234
|
+
error: 'Nobody is docked into this session.',
|
|
235
|
+
hint: share.url
|
|
236
|
+
? 'Call `view` to bring the app up for the user, let them select, then ask again.'
|
|
237
|
+
: 'An app shares its selection once it is in the same session: join the app\'s session with use_session and its invite link.',
|
|
238
|
+
}),
|
|
239
|
+
}],
|
|
240
|
+
});
|
|
241
|
+
server.registerTool('get_selection', {
|
|
242
|
+
title: 'What the user selected',
|
|
243
|
+
description: 'What the user has selected in the app docked into this session: faces, edges and vertices of a body, or objects of the model tree (sketch entities, dimensions, features). Call it when the user points at something instead of naming it ("this face", "the selected edges", "here"). ' +
|
|
244
|
+
'Each item has `kind` (face | edge | vertex | object) and `objectId` — the tree object: the picked object itself, or the solid a picked face, edge or vertex belongs to (`object` names its class). Geometry also has `graphicId`, the id API calls take for that face, edge or vertex (e.g. v1.sketch.create({ planeId })), its `type` (plane, cylinder, line, arc, …), `containerId` and `prodRefId`. ' +
|
|
245
|
+
'Geometry ids describe the model as it is now: a feature that rebuilds the body gives its faces and edges new ids. Read the selection right before you use it.',
|
|
246
|
+
inputSchema: {},
|
|
247
|
+
}, async () => {
|
|
248
|
+
if (!share.peers().length)
|
|
249
|
+
return nobody();
|
|
250
|
+
const selections = await describe(share.selections());
|
|
251
|
+
if (!selections.length)
|
|
252
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ok: true, count: 0, selections: [], note: 'Somebody is docked, but shares no selection.' }) }] };
|
|
253
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ok: true, count: selections.reduce((n, s) => n + s.total, 0), selections }) }] };
|
|
254
|
+
});
|
|
255
|
+
server.registerTool('set_selection', {
|
|
256
|
+
title: 'Select something for the user',
|
|
257
|
+
description: 'Selects things in the app docked into this session, so the user sees what you mean: faces, edges or vertices by their `graphicId` (from getGeometryIds, pick results or get_selection), tree objects by their `objectId`. The app highlights them. Returns what is selected there afterwards. ' +
|
|
258
|
+
'Geometry ids must be current ones (see get_selection); pass `prodRefId` from a previous get_selection when the part is an instance in an assembly. An empty `items` clears the selection.',
|
|
259
|
+
inputSchema: {
|
|
260
|
+
items: z
|
|
261
|
+
.array(z.object({
|
|
262
|
+
graphicId: z.number().optional().describe('A face, edge or vertex: its element id.'),
|
|
263
|
+
objectId: z.number().optional().describe('A tree object (sketch entity, dimension, feature) — or, with graphicId, the solid that owns the element.'),
|
|
264
|
+
containerId: z.number().optional().describe('The body\'s graphic container, when known (the app finds it otherwise).'),
|
|
265
|
+
prodRefId: z.number().optional().describe('The assembly instance, when the part is one (from get_selection).'),
|
|
266
|
+
}))
|
|
267
|
+
.describe('What to select.'),
|
|
268
|
+
replace: z.boolean().optional().describe('Replace the current selection (default) or add to it (false).'),
|
|
269
|
+
},
|
|
270
|
+
}, async ({ items, replace }) => {
|
|
271
|
+
if (!share.peers().length)
|
|
272
|
+
return nobody();
|
|
273
|
+
const selections = await describe(await share.select(items, replace !== false));
|
|
274
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ok: true, requested: items.length, selections }) }] };
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=share.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share.js","sourceRoot":"","sources":["../../src/tools/share.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,4EAA4E;AAC5E,mEAAmE;AACnE,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,sEAAsE;AAGtE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,SAAS,EAAuE,MAAM,qBAAqB,CAAA;AAIpH,+EAA+E;AAC/E,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;AAExF,mHAAmH;AACnH,MAAM,QAAQ,GAAG,wJAAwJ,CAAA;AAEzK,oIAAoI;AACpI,MAAM,KAAK,GAAyB;IAClC,IAAI,EACF,ktBAAktB;IACptB,OAAO,EACL,kPAAkP;IACpP,GAAG,EAAE,4HAA4H;CAClI,CAAA;AAED,wFAAwF;AACxF,MAAM,UAAU,GAA4B;IAC1C,GAAG,EACD,mSAAmS;IACrS,MAAM,EACJ,8qBAA8qB;IAChrB,GAAG,EAAE,EAAE;CACR,CAAA;AAED,2IAA2I;AAC3I,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAU,EAAE,UAAmB,KAAK,EAAU,EAAE,CACtE,sOAAsO;IACtO,KAAK,CAAC,IAAI,CAAC;IACX,gRAAgR;IAChR,UAAU,CAAC,OAAO,CAAC;IACnB,2LAA2L;IAC3L,0JAA0J,CAAA;AAE5J,+CAA+C;AAC/C,MAAM,SAAS,GAAG,CAAC,IAAU,EAAU,EAAE;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC,UAAU,IAAI,EAAE,CAAA;IACxD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAA;IAC9B,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,eAAe,CAAA;AACjG,CAAC,CAAA;AAED,iEAAiE;AACjE,MAAM,WAAW,GACf,mUAAmU,CAAA;AAErU,oEAAoE;AACpE,MAAM,QAAQ,GACZ,sPAAsP;IACtP,6IAA6I,CAAA;AAE/I,4FAA4F;AAC5F,MAAM,QAAQ,GAAG,8MAA8M,CAAA;AAE/N,4HAA4H;AAC5H,MAAM,SAAS,GAAG,CAAC,KAAY,EAAU,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AAErI,MAAM,OAAO,GAAG,CAAC,KAAY,EAAU,EAAE;IACvC,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM;QACtB,OAAO,CACL,QAAQ,KAAK,CAAC,GAAG,IAAI;YACrB,+JAA+J,QAAQ,IAAI;YAC3K,oIAAoI;YACpI,2JAA2J,CAC5J,CAAA;IACH,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ;QACxB,OAAO,CACL,QAAQ,KAAK,CAAC,GAAG,IAAI;YACrB,uJAAuJ;YACvJ,yIAAyI,CAC1I,CAAA;IACH,OAAO,CACL,QAAQ,KAAK,CAAC,GAAG,IAAI;QACrB,gPAAgP,CACjP,CAAA;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAiB,EAAE,MAAc,EAAE,OAAe,EAAE,IAAU,EAAE,OAAgB,EAAE,GAA2B;IAC7I,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;IACxC,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC;QAC5B,MAAM;QACN,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;QACpC,GAAG,EAAE,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,UAAU;QAC1C,IAAI;QACJ,OAAO;QACP,QAAQ,EAAE,GAAG,EAAE;YACb,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,EAAE,IAAI,CAAA;YACnD,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;QACnG,CAAC;QACD,GAAG;KACJ,CAAC,CAAA;IAEF,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,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QACpE,MAAM,OAAO,GAAG,KAAK,EAAE,GAAG,IAAW,EAAE,EAAE;YACvC,8GAA8G;YAC9G,KAAK,CAAC,IAAI,EAAE,CAAA;YACZ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;YACrC,2HAA2H;YAC3H,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,UAAW,GAAa,EAAE,OAAO,IAAI,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAA;YACzG,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;gBAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YACnH,OAAO,MAAM,CAAA;QACf,CAAC,CAAA;QACD,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,CAAC,CAAA;IAED,qCAAqC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CACtC;IAAC,MAAgC,CAAC,KAAK,GAAG,GAAG,EAAE;QAC9C,KAAK,CAAC,KAAK,EAAE,CAAA;QACb,KAAK,EAAE,CAAA;IACT,CAAC,CAAA;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,gBAAgB,CAAC,MAAiB,EAAE,KAAY,EAAE,MAA4B;IAC5F,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,KAAK,IAAI,CAAA;IAC9B,MAAM,CAAC,YAAY,CACjB,MAAM,EACN;QACE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,kBAAkB;QAChD,WAAW,EAAE,GAAG;YACd,CAAC,CAAC,w1BAAw1B;YAC11B,CAAC,CAAC,yWAAyW;QAC7W,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sJAAsJ,CAAC;YAC7L,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iIAAiI,CAAC;SAC1K;KACF,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;QACxB,8EAA8E;QAC9E,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;YAC3C,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,2DAA2D,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,+EAA+E;qBAC/L,CAAC;aACH,CAAA;QACH,CAAC;QACD,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,IAAI,MAAM,EAAE,GAAG,CAAA;QAC1E,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,4FAA4F,EAAE,CAAC,EAAE,CAAA;QAC5K,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAO,CAAC,IAAI,EAAE,CAAA;YACtD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,4BAA4B,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,4CAA4C,CAAC,CAAC,CAAC,qCAAqC,6BAA6B,EAAE,CAAC,EAAE,CAAA;QACvN,CAAC;QACD,8GAA8G;QAC9G,kFAAkF;QAClF,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAA;QACzC,uEAAuE;QACvE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAClB,IAAI,KAAa,CAAA;QACjB,IAAI,IAAI,KAAK,IAAI;YAAE,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,oDAAoD,CAAC,CAAC,CAAC,4DAA4D,CAAA;aAC3J,IAAI,IAAI,KAAK,KAAK;YAAE,KAAK,GAAG,8CAA8C,CAAA;aAC1E,IAAI,MAAM,GAAG,CAAC;YAAE,KAAK,GAAG,mDAAmD,CAAA;aAC3E,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,KAAK,GAAG,uDAAuD,QAAQ,6FAA6F,CAAA;aAC/L,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,KAAK,GAAG,oDAAoD,CAAA;;YAC7G,KAAK,GAAG,qCAAqC,CAAA;QAClD,0GAA0G;QAC1G,MAAM,QAAQ,GAAG,GAAG,KAAK,KAAK,CAAC,SAAS,CAAA;QACxC,MAAM,KAAK,GAAG,QAAQ;YACpB,CAAC,CAAC,6GAA6G;YAC/G,CAAC,CAAC,KAAK,CAAC,SAAS;gBACf,CAAC,CAAC,4BAA4B,KAAK,CAAC,SAAS,EAAE;gBAC/C,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,QAAQ;oBAC1B,CAAC,CAAC,iGAAiG;oBACnG,CAAC,CAAC,EAAE,CAAA;QACV,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,wBAAwB,GAAG,KAAK,KAAK,yDAAyD,KAAK,EAAE,EAAE,CAAC,EAAE,CAAA;IAC9J,CAAC,CACF,CAAA;AACH,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,iBAAiB,CAAC,MAAiB,EAAE,KAAY;IAC/D,IAAI,KAAK,CAAC,OAAO,KAAK,KAAK;QAAE,OAAM;IACnC,MAAM,CAAC,YAAY,CACjB,OAAO,EACP;QACE,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EACT,gTAAgT;YAChT,+TAA+T;YAC/T,+eAA+e;QACjf,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uEAAuE,CAAC;SAC/G;KACF,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACjB,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,OAAO,GAAG,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;QACvI,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,EAAE,CAAA;YACjC,IAAI,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC,6BAA6B,CAAC,CAAA;YACxD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,gCAAgC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAA;YACvK,kFAAkF;YAClF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,2HAA2H,CAAC,CAAC,CAAC,oCAAoC,CAAA;YAC7M,OAAO,IAAI,CAAC,+DAA+D,IAAI,IAAI,KAAK,EAAE,CAAC,CAAA;QAC7F,CAAC;QACD,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,KAAK,IAAI,CAAA;YACtC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,CAAA;YAClC,IAAI,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC,eAAe,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,wFAAwF,CAAC,CAAC,CAAC,mDAAmD,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,WAAW,EAAE,CAAC,CAAA;YAClQ,6GAA6G;YAC7G,OAAO,IAAI,CACT,4EAA4E,IAAI,CAAC,OAAO,6CAA6C;gBACnI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,qCAAqC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnE,gGAAgG,CACnG,CAAA;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAE,GAAa,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAA;QAC3D,CAAC;IACH,CAAC,CACF,CAAA;AACH,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,sBAAsB,CAAC,MAAiB,EAAE,MAAc,EAAE,KAAY;IACpF,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,KAAK,EAAE,UAA2B,EAAE,EAAE;QACrD,IAAI,IAAI,GAAwB,EAAE,CAAA;QAClC,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,EAAE,CAAwB,CAAA;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,6CAA6C;QAC/C,CAAC;QACD,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC1B,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;YACtB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBACxB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;gBACxC,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;YAClF,CAAC,CAAC;SACH,CAAC,CAAC,CAAA;IACL,CAAC,CAAA;IACD,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC;QACpB,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,CAAC;gBACR,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,qCAAqC;oBAC5C,IAAI,EAAE,KAAK,CAAC,GAAG;wBACb,CAAC,CAAC,gFAAgF;wBAClF,CAAC,CAAC,2HAA2H;iBAChI,CAAC;aACH,CAAC;KACH,CAAC,CAAA;IAEF,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,KAAK,EAAE,wBAAwB;QAC/B,WAAW,EACT,yRAAyR;YACzR,yYAAyY;YACzY,+JAA+J;QACjK,WAAW,EAAE,EAAE;KAChB,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,MAAM;YAAE,OAAO,MAAM,EAAE,CAAA;QAC1C,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAA;QACrD,IAAI,CAAC,UAAU,CAAC,MAAM;YAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,EAAE,8CAA8C,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;QAClL,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;IAC5I,CAAC,CACF,CAAA;IAED,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,KAAK,EAAE,+BAA+B;QACtC,WAAW,EACT,gSAAgS;YAChS,2LAA2L;QAC7L,WAAW,EAAE;YACX,KAAK,EAAE,CAAC;iBACL,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;gBACP,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;gBACpF,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0GAA0G,CAAC;gBACpJ,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yEAAyE,CAAC;gBACtH,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mEAAmE,CAAC;aAC/G,CAAC,CACH;iBACA,QAAQ,CAAC,iBAAiB,CAAC;YAC9B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+DAA+D,CAAC;SAC1G;KACF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;QAC3B,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,MAAM;YAAE,OAAO,MAAM,EAAE,CAAA;QAC1C,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAA;QAC/E,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;IACjH,CAAC,CACF,CAAA;AACH,CAAC"}
|
package/dist/tools/viewer.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { Client } from '../client.js';
|
|
3
3
|
import { type ViewerSession } from '../viewer/server.js';
|
|
4
|
-
export declare const VIEWER_NOTE = "3D VIEW: every session has a live 3D view in the user's browser. It opens by itself with the first model and follows every change.
|
|
4
|
+
export declare const VIEWER_NOTE = "3D VIEW: every session has a live 3D view in the user's browser. It opens by itself with the first model and follows every change. The first result that shows a model carries its link (\"3D view: \u2026\"): give the user that link once, as a clickable link on its own line, and do NOT repeat it in later answers. `view` returns the link again and brings the view up; it also offers STEP, STL and glTF downloads to the user.";
|
|
5
5
|
export declare function attachViewer(server: McpServer, client: Client, log?: (msg: string) => void): Promise<ViewerSession | null>;
|
|
6
6
|
export declare function registerViewerTool(server: McpServer, viewer: ViewerSession | null): void;
|
package/dist/tools/viewer.js
CHANGED
|
@@ -5,20 +5,19 @@
|
|
|
5
5
|
// serializeTools and requireSignIn) so that after a tool that may have changed
|
|
6
6
|
// the model the view is told, and so that results which show a model carry
|
|
7
7
|
// the link. The first model of a session opens the view in the browser.
|
|
8
|
+
import { exportAllowed } from '../engine/key.js';
|
|
8
9
|
import { z } from 'zod';
|
|
9
10
|
import { hostName } from '../auth.js';
|
|
10
11
|
import { enqueue } from '../queue.js';
|
|
11
12
|
import { openViewerSession } from '../viewer/server.js';
|
|
12
13
|
/** Tools after which the model may be another one. */
|
|
13
14
|
const CHANGES_MODEL = new Set(['run_script', 'clear', 'load', 'restore', 'use_session']);
|
|
14
|
-
/** Results that show or change a model
|
|
15
|
+
/** Results that show or change a model: the first of them ends with the link of the view. */
|
|
15
16
|
const OFFERS_LINK = new Set(['run_script', 'snapshot', 'load', 'restore']);
|
|
16
|
-
export const VIEWER_NOTE = '3D VIEW: every session has a live 3D view in the user\'s browser. It opens by itself with the first model and follows every change.
|
|
17
|
+
export const VIEWER_NOTE = '3D VIEW: every session has a live 3D view in the user\'s browser. It opens by itself with the first model and follows every change. The first result that shows a model carries its link ("3D view: …"): give the user that link once, as a clickable link on its own line, and do NOT repeat it in later answers. `view` returns the link again and brings the view up; it also offers STEP, STL and glTF downloads to the user.';
|
|
17
18
|
const engineLabel = (client) => {
|
|
18
19
|
if (client.transport === 'wasm')
|
|
19
20
|
return 'WebAssembly · this machine';
|
|
20
|
-
if (client.transport === 'bridge')
|
|
21
|
-
return 'in an app';
|
|
22
21
|
try {
|
|
23
22
|
return `worker · ${new URL(client.url).host}`;
|
|
24
23
|
}
|
|
@@ -42,6 +41,9 @@ export async function attachViewer(server, client, log) {
|
|
|
42
41
|
return { tree, graphic };
|
|
43
42
|
}),
|
|
44
43
|
exportModel: format => enqueue(server, async () => {
|
|
44
|
+
const allowed = exportAllowed(format);
|
|
45
|
+
if (!allowed.ok)
|
|
46
|
+
throw new Error(allowed.message);
|
|
45
47
|
const args = { format, encoding: 'base64' };
|
|
46
48
|
if (format === 'STP')
|
|
47
49
|
args.stp = { version: 2 };
|
|
@@ -65,6 +67,8 @@ export async function attachViewer(server, client, log) {
|
|
|
65
67
|
log?.(`3D viewer not available: ${err?.message ?? err}`);
|
|
66
68
|
return null;
|
|
67
69
|
}
|
|
70
|
+
// The link is said once per session. Said with every result, an agent repeats it in every answer.
|
|
71
|
+
let offered = false;
|
|
68
72
|
const original = server.registerTool.bind(server);
|
|
69
73
|
server.registerTool = (name, config, handler) => {
|
|
70
74
|
if (!CHANGES_MODEL.has(name) && !OFFERS_LINK.has(name))
|
|
@@ -73,8 +77,9 @@ export async function attachViewer(server, client, log) {
|
|
|
73
77
|
const result = await handler(...args);
|
|
74
78
|
if (CHANGES_MODEL.has(name))
|
|
75
79
|
viewer.touch();
|
|
76
|
-
if (OFFERS_LINK.has(name) && result && !result.isError && Array.isArray(result.content)) {
|
|
77
|
-
|
|
80
|
+
if (!offered && OFFERS_LINK.has(name) && result && !result.isError && Array.isArray(result.content)) {
|
|
81
|
+
offered = true;
|
|
82
|
+
result.content.push({ type: 'text', text: `3D view (live, opens in the user's browser): ${viewer.url}\nThis is said once: later results do not repeat the link, \`view\` returns it again.` });
|
|
78
83
|
}
|
|
79
84
|
return result;
|
|
80
85
|
};
|
package/dist/tools/viewer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewer.js","sourceRoot":"","sources":["../../src/tools/viewer.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,2EAA2E;AAC3E,wEAAwE;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,iBAAiB,EAAsB,MAAM,qBAAqB,CAAA;AAE3E,sDAAsD;AACtD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;AACxF,
|
|
1
|
+
{"version":3,"file":"viewer.js","sourceRoot":"","sources":["../../src/tools/viewer.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classcad/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"mcpName": "io.github.awv-informatik/classcad",
|
|
5
5
|
"description": "ClassCAD MCP server: build, verify and export 3D CAD parts and assemblies with the ClassCAD engine, running locally",
|
|
6
6
|
"homepage": "https://classcad.ai",
|
|
@@ -42,11 +42,12 @@
|
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "tsc && node scripts/copy-build-assets.mjs",
|
|
45
|
+
"build:app": "node scripts/build-app.mjs",
|
|
45
46
|
"dev": "tsc --watch",
|
|
46
47
|
"start": "node dist/server.js",
|
|
47
48
|
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
48
|
-
"postbuild": "node --test test/emission.mjs test/daemon.mjs test/auth.mjs",
|
|
49
|
-
"test": "npm run build && node test/emission-live.mjs && node --test test/wasm-live.mjs"
|
|
49
|
+
"postbuild": "node --test --test-timeout=180000 test/emission.mjs test/daemon.mjs test/auth.mjs test/share.mjs",
|
|
50
|
+
"test": "npm run build && node --test test/key.mjs test/auth.mjs && node test/emission-live.mjs && node --test --test-timeout=300000 test/wasm-live.mjs"
|
|
50
51
|
},
|
|
51
52
|
"dependencies": {
|
|
52
53
|
"@classcad/skill": "^0.0.6",
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
export declare const PROTOCOL_VERSION = 1;
|
|
2
|
-
export type Capability = 'engine.execute' | 'selection.read' | 'selection.write';
|
|
3
|
-
export type RawSelection = {
|
|
4
|
-
containerId: number;
|
|
5
|
-
graphicId: number;
|
|
6
|
-
prodRefId: number;
|
|
7
|
-
};
|
|
8
|
-
export type SelectionEntity = {
|
|
9
|
-
kind: 'face' | 'edge' | 'vertex' | 'curve' | 'unknown';
|
|
10
|
-
classcadId?: number;
|
|
11
|
-
position?: {
|
|
12
|
-
x: number;
|
|
13
|
-
y: number;
|
|
14
|
-
z: number;
|
|
15
|
-
};
|
|
16
|
-
normal?: {
|
|
17
|
-
x: number;
|
|
18
|
-
y: number;
|
|
19
|
-
z: number;
|
|
20
|
-
};
|
|
21
|
-
graphicType?: string;
|
|
22
|
-
raw: RawSelection;
|
|
23
|
-
};
|
|
24
|
-
export type ShareKind = 'invite' | 'bridge';
|
|
25
|
-
export type AnnounceMessage = {
|
|
26
|
-
type: 'announce';
|
|
27
|
-
protocolVersion: number;
|
|
28
|
-
/** The share token this connection serves (one bridge connection per token). */
|
|
29
|
-
token: string;
|
|
30
|
-
kind: ShareKind;
|
|
31
|
-
drawingId: string;
|
|
32
|
-
app: string;
|
|
33
|
-
appVersion?: string;
|
|
34
|
-
capabilities: Capability[];
|
|
35
|
-
/** Stable per-connection id so the MCP can tell apart several apps for one token. */
|
|
36
|
-
clientId: string;
|
|
37
|
-
/** Legacy (pre-token) announce field; treated as the token when `token` is absent. */
|
|
38
|
-
sessionId?: string;
|
|
39
|
-
/** Legacy modeler announce field; treated as an 'invite' token when `token` is absent. */
|
|
40
|
-
invite?: string;
|
|
41
|
-
};
|
|
42
|
-
export type RequestMessage = {
|
|
43
|
-
type: 'request';
|
|
44
|
-
id: number;
|
|
45
|
-
method: BridgeMethod;
|
|
46
|
-
params?: unknown;
|
|
47
|
-
};
|
|
48
|
-
export type ResponseMessage = {
|
|
49
|
-
type: 'response';
|
|
50
|
-
id: number;
|
|
51
|
-
result?: unknown;
|
|
52
|
-
error?: {
|
|
53
|
-
code: string;
|
|
54
|
-
message: string;
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
export type EventMessage = {
|
|
58
|
-
type: 'event';
|
|
59
|
-
channel: BridgeEventChannel;
|
|
60
|
-
payload: unknown;
|
|
61
|
-
};
|
|
62
|
-
export type BridgeEnvelope = AnnounceMessage | RequestMessage | ResponseMessage | EventMessage;
|
|
63
|
-
/** Methods the MCP calls on the app. */
|
|
64
|
-
export type BridgeMethod = 'engine.execute' | 'session.attached' | 'session.detached' | 'selection.get' | 'selection.set';
|
|
65
|
-
/** engine.execute reply: the engine's text messages and inflated binary graphic packages, in emission order. */
|
|
66
|
-
export type EngineExecuteResult = {
|
|
67
|
-
messages: Record<string, any>[];
|
|
68
|
-
binaryMessages: Record<string, any>[];
|
|
69
|
-
/** Local WASM engine only: emitted messages that could not be decoded (dropped, never thrown into the engine). */
|
|
70
|
-
decodeErrors?: string[];
|
|
71
|
-
};
|
|
72
|
-
export type SessionAttachedParams = {
|
|
73
|
-
peerId: string;
|
|
74
|
-
role?: 'edit' | 'view';
|
|
75
|
-
client?: string;
|
|
76
|
-
};
|
|
77
|
-
export type SelectionGetResult = SelectionEntity[];
|
|
78
|
-
export type SelectionSetParams = {
|
|
79
|
-
items: Array<RawSelection | {
|
|
80
|
-
classcadId: number;
|
|
81
|
-
}>;
|
|
82
|
-
replace?: boolean;
|
|
83
|
-
};
|
|
84
|
-
export type BridgeEventChannel = 'selection.changed';
|
|
85
|
-
export type SelectionChangedPayload = {
|
|
86
|
-
items: SelectionEntity[];
|
|
87
|
-
};
|
package/dist/bridge/protocol.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// bridge/protocol.ts — wire protocol between a buerli app and this MCP.
|
|
2
|
-
//
|
|
3
|
-
// Mirror of @buerli.io/classcad's src/bridge/protocol.ts (source of truth
|
|
4
|
-
// there). Apps open an OUTBOUND WebSocket to the MCP's bridge listener and
|
|
5
|
-
// announce a share token. Two kinds:
|
|
6
|
-
// • 'invite' — the app runs against a ClassCAD server; the MCP joins the
|
|
7
|
-
// server session with the token (use_session) and uses the bridge for
|
|
8
|
-
// app state (selection) only.
|
|
9
|
-
// • 'bridge' — the engine runs inside the app (WASM); the MCP relays its
|
|
10
|
-
// engine commands over this link (engine.execute) — the bridge IS the
|
|
11
|
-
// engine transport.
|
|
12
|
-
// Plain TS, importable from Node and the browser.
|
|
13
|
-
export const PROTOCOL_VERSION = 1;
|
|
14
|
-
//# sourceMappingURL=protocol.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/bridge/protocol.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,EAAE;AACF,0EAA0E;AAC1E,2EAA2E;AAC3E,qCAAqC;AACrC,2EAA2E;AAC3E,0EAA0E;AAC1E,kCAAkC;AAClC,2EAA2E;AAC3E,0EAA0E;AAC1E,wBAAwB;AACxB,kDAAkD;AAElD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAA"}
|
package/dist/bridge/server.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { WebSocket } from 'ws';
|
|
2
|
-
import type { BridgeMethod, SelectionEntity, ShareKind } from './protocol.js';
|
|
3
|
-
import { PROTOCOL_VERSION } from './protocol.js';
|
|
4
|
-
export type AppConnection = {
|
|
5
|
-
clientId: string;
|
|
6
|
-
/** The share token this connection serves. */
|
|
7
|
-
token: string;
|
|
8
|
-
kind: ShareKind;
|
|
9
|
-
/** @deprecated alias of `token` (pre-token announces). */
|
|
10
|
-
sessionId: string;
|
|
11
|
-
drawingId: string;
|
|
12
|
-
app: string;
|
|
13
|
-
appVersion?: string;
|
|
14
|
-
capabilities: string[];
|
|
15
|
-
protocolVersion: number;
|
|
16
|
-
announcedAt: number;
|
|
17
|
-
request: <T = unknown>(method: BridgeMethod, params?: unknown) => Promise<T>;
|
|
18
|
-
onClose: (cb: () => void) => () => void;
|
|
19
|
-
cachedSelection: SelectionEntity[] | null;
|
|
20
|
-
socket: WebSocket;
|
|
21
|
-
};
|
|
22
|
-
export type BridgeRegistry = {
|
|
23
|
-
list: (token?: string) => AppConnection[];
|
|
24
|
-
pick: (token: string, clientId?: string) => AppConnection | null;
|
|
25
|
-
waitFor: (token: string, timeoutMs?: number) => Promise<AppConnection | null>;
|
|
26
|
-
onSelectionChanged: (sessionId: string, cb: (items: SelectionEntity[]) => void) => () => void;
|
|
27
|
-
close: () => Promise<void>;
|
|
28
|
-
readonly url: string;
|
|
29
|
-
};
|
|
30
|
-
export type StartBridgeOptions = {
|
|
31
|
-
listen?: string;
|
|
32
|
-
log?: (msg: string) => void;
|
|
33
|
-
};
|
|
34
|
-
export declare function startBridgeServer(opts?: StartBridgeOptions): Promise<BridgeRegistry>;
|
|
35
|
-
export { PROTOCOL_VERSION };
|