@pascal-app/mcp 1.0.6 → 1.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 (88) hide show
  1. package/README.md +32 -11
  2. package/dist/bin/pascal-mcp.js +2 -0
  3. package/dist/bridge/agent-session.d.ts +2 -1
  4. package/dist/bridge/agent-session.d.ts.map +1 -1
  5. package/dist/bridge/agent-session.js +0 -1
  6. package/dist/bridge/scene-bridge.d.ts +22 -14
  7. package/dist/bridge/scene-bridge.d.ts.map +1 -1
  8. package/dist/bridge/scene-bridge.js +180 -198
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/lib/safe-fetch.d.ts +6 -0
  12. package/dist/lib/safe-fetch.d.ts.map +1 -1
  13. package/dist/lib/safe-fetch.js +135 -41
  14. package/dist/operations/scene-operations.d.ts +7 -0
  15. package/dist/operations/scene-operations.d.ts.map +1 -1
  16. package/dist/operations/scene-operations.js +4 -0
  17. package/dist/prompts/from-brief.js +1 -1
  18. package/dist/prompts/iterate-on-feedback.js +1 -1
  19. package/dist/prompts/renovation-from-photos.js +1 -1
  20. package/dist/prompts/scene-guidance.js +2 -2
  21. package/dist/resources/agent-guide.d.ts.map +1 -1
  22. package/dist/resources/agent-guide.js +8 -3
  23. package/dist/resources/catalog-items.d.ts +4 -1
  24. package/dist/resources/catalog-items.d.ts.map +1 -1
  25. package/dist/resources/catalog-items.js +14 -6
  26. package/dist/server.d.ts +9 -5
  27. package/dist/server.d.ts.map +1 -1
  28. package/dist/server.js +21 -5
  29. package/dist/templates/empty-studio.d.ts.map +1 -1
  30. package/dist/templates/empty-studio.js +2 -3
  31. package/dist/templates/garden-house.d.ts.map +1 -1
  32. package/dist/templates/garden-house.js +2 -3
  33. package/dist/templates/two-bedroom.d.ts.map +1 -1
  34. package/dist/templates/two-bedroom.js +2 -3
  35. package/dist/tools/add-column.d.ts.map +1 -1
  36. package/dist/tools/add-column.js +9 -6
  37. package/dist/tools/add-object.d.ts +44 -5
  38. package/dist/tools/add-object.d.ts.map +1 -1
  39. package/dist/tools/add-object.js +75 -21
  40. package/dist/tools/apply-patch.js +1 -1
  41. package/dist/tools/asset-catalog.d.ts +5 -6
  42. package/dist/tools/asset-catalog.d.ts.map +1 -1
  43. package/dist/tools/asset-catalog.js +1 -9
  44. package/dist/tools/author-scene.d.ts +8 -5
  45. package/dist/tools/author-scene.d.ts.map +1 -1
  46. package/dist/tools/author-scene.js +302 -51
  47. package/dist/tools/clear-scene.js +2 -2
  48. package/dist/tools/collections.d.ts.map +1 -1
  49. package/dist/tools/collections.js +8 -5
  50. package/dist/tools/honest-patch-guard.js +6 -5
  51. package/dist/tools/hosted-services.d.ts.map +1 -1
  52. package/dist/tools/hosted-services.js +2 -2
  53. package/dist/tools/index.d.ts +7 -2
  54. package/dist/tools/index.d.ts.map +1 -1
  55. package/dist/tools/index.js +3 -9
  56. package/dist/tools/local-geometry-scripts.d.ts.map +1 -1
  57. package/dist/tools/local-geometry-scripts.js +7 -2
  58. package/dist/tools/patch-guards.d.ts.map +1 -1
  59. package/dist/tools/patch-guards.js +32 -15
  60. package/dist/tools/project-files.d.ts.map +1 -1
  61. package/dist/tools/project-files.js +2 -2
  62. package/dist/tools/room-tools.d.ts +1 -0
  63. package/dist/tools/room-tools.d.ts.map +1 -1
  64. package/dist/tools/room-tools.js +11 -8
  65. package/dist/tools/scene-scope.d.ts +4 -0
  66. package/dist/tools/scene-scope.d.ts.map +1 -0
  67. package/dist/tools/scene-scope.js +4 -0
  68. package/dist/tools/shared-tools.d.ts +0 -2
  69. package/dist/tools/shared-tools.d.ts.map +1 -1
  70. package/dist/tools/shared-tools.js +146 -31
  71. package/dist/tools/templates/create-house-from-brief.js +1 -1
  72. package/dist/tools/view-scene.js +2 -2
  73. package/examples/coordinate-conventions-demo.md +1 -0
  74. package/examples/photo-to-scene.md +1 -0
  75. package/examples/renovate-from-photos.md +2 -0
  76. package/package.json +10 -10
  77. package/dist/tools/create-unit.d.ts +0 -29
  78. package/dist/tools/create-unit.d.ts.map +0 -1
  79. package/dist/tools/create-unit.js +0 -36
  80. package/dist/tools/list-units.d.ts +0 -38
  81. package/dist/tools/list-units.d.ts.map +0 -1
  82. package/dist/tools/list-units.js +0 -61
  83. package/dist/tools/set-unit-members.d.ts +0 -22
  84. package/dist/tools/set-unit-members.d.ts.map +0 -1
  85. package/dist/tools/set-unit-members.js +0 -43
  86. package/dist/tools/unit-members.d.ts +0 -4
  87. package/dist/tools/unit-members.d.ts.map +0 -1
  88. package/dist/tools/unit-members.js +0 -24
package/dist/server.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { McpServer, ProtocolError, ProtocolErrorCode, } from '@modelcontextprotocol/server';
2
2
  import { recordMade, } from '@pascal-app/core/agent-operations';
3
+ import { untracked } from '@pascal-app/core/runtime';
3
4
  import { createSceneOperations } from './operations/index.js';
4
5
  import { registerPrompts } from './prompts/index.js';
5
6
  import { registerResources } from './resources/index.js';
6
7
  import { registerTools } from './tools/index.js';
7
- import { cachedCatalog } from './tools/asset-catalog.js';
8
8
  import { registerHostedServiceTools } from './tools/hosted-services.js';
9
9
  import { normalizeToolSchemaDialect } from './tools/normalize-schema-dialect.js';
10
10
  import { registerProjectFileTools } from './tools/project-files.js';
@@ -21,10 +21,12 @@ export function createPascalMcpServer(opts) {
21
21
  const executeTool = recordingWhatToolsMake(operations, opts.executeTool);
22
22
  if (executeTool)
23
23
  installToolExecutor(server, executeTool);
24
- const catalog = opts.catalog ? cachedCatalog(opts.catalog) : undefined;
24
+ installSceneScope(server, operations);
25
25
  registerTools(server, operations, {
26
- catalog,
26
+ catalog: opts.catalog,
27
27
  geometryScripts: opts.geometryScripts,
28
+ objectMakers: opts.objectMakers,
29
+ readFile: opts.readFile,
28
30
  sceneViews: opts.sceneViews,
29
31
  assetViews: opts.assetViews,
30
32
  });
@@ -33,7 +35,7 @@ export function createPascalMcpServer(opts) {
33
35
  registerHostedServiceTools(server, opts.services);
34
36
  if (opts.files)
35
37
  registerProjectFileTools(server, opts.files);
36
- registerResources(server, operations, catalog);
38
+ registerResources(server, operations, opts.catalog);
37
39
  registerPrompts(server, operations);
38
40
  normalizeToolSchemaDialect(server);
39
41
  answerRetiredTools(server);
@@ -73,12 +75,26 @@ function recordingWhatToolsMake(operations, outer) {
73
75
  if (!session)
74
76
  return outer;
75
77
  const recording = (input) => recordMade({
76
- getNodes: () => operations.getNodes(),
78
+ // What the tool made is bookkeeping, not what it read: the scene as stored.
79
+ getNodes: () => untracked(operations.getNodes()),
77
80
  readSession: () => session.read(),
78
81
  keep: (keep) => session.keep(keep),
79
82
  }, input.execute);
80
83
  return outer ? (input) => outer({ ...input, execute: () => recording(input) }) : recording;
81
84
  }
85
+ /**
86
+ * Every handler starts in the scene's scope (`SceneOperations.runInScene`): ids it mints and
87
+ * schema defaults it fills before its first await come from the scene's allocator, so a detached
88
+ * scene recomputed from the same snapshot and effect mints the same ones. Work after an await
89
+ * re-enters the scope itself (add_object, add_column).
90
+ */
91
+ function installSceneScope(server, operations) {
92
+ const runInScene = operations.runInScene?.bind(operations);
93
+ if (!runInScene)
94
+ return;
95
+ const registerTool = server.registerTool.bind(server);
96
+ server.registerTool = ((name, config, callback) => registerTool(name, config, (...args) => runInScene(() => Reflect.apply(callback, undefined, args))));
97
+ }
82
98
  function installToolExecutor(server, executeTool) {
83
99
  const registerTool = server.registerTool.bind(server);
84
100
  server.registerTool = ((name, config, callback) => {
@@ -1 +1 @@
1
- {"version":3,"file":"empty-studio.d.ts","sourceRoot":"","sources":["../../src/templates/empty-studio.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAmPpE,eAAO,MAAM,QAAQ,EAAE,UAA4B,CAAA;AAEnD,eAAO,MAAM,QAAQ;aACnB,EAAE,EAAE,cAAc;aAClB,IAAI,EAAE,cAAc;aACpB,WAAW,EACT,6FAA6F;CACvF,CAAA"}
1
+ {"version":3,"file":"empty-studio.d.ts","sourceRoot":"","sources":["../../src/templates/empty-studio.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAiPpE,eAAO,MAAM,QAAQ,EAAE,UAA4B,CAAA;AAEnD,eAAO,MAAM,QAAQ;aACnB,EAAE,EAAE,cAAc;aAClB,IAAI,EAAE,cAAc;aACpB,WAAW,EACT,6FAA6F;CACvF,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { DEFAULT_LEVEL_HEIGHT } from '@pascal-app/core';
2
+ import { NEW_DOOR_FRAME_THICKNESS } from '@pascal-app/core/building';
2
3
  /**
3
4
  * 40 m² studio apartment — a single open room with one window, one front door
4
5
  * and a single "Living/Kitchen" zone. Used as a starting point for small unit
@@ -55,8 +56,7 @@ function buildNodes() {
55
56
  rotation: [0, 0, 0],
56
57
  width: 0.9,
57
58
  height: 2.1,
58
- frameThickness: 0.05,
59
- frameDepth: 0.07,
59
+ frameThickness: NEW_DOOR_FRAME_THICKNESS,
60
60
  threshold: true,
61
61
  thresholdHeight: 0.02,
62
62
  hingesSide: 'left',
@@ -100,7 +100,6 @@ function buildNodes() {
100
100
  width: 1.5,
101
101
  height: 1.2,
102
102
  frameThickness: 0.05,
103
- frameDepth: 0.07,
104
103
  columnRatios: [1],
105
104
  rowRatios: [1],
106
105
  columnDividerThickness: 0.03,
@@ -1 +1 @@
1
- {"version":3,"file":"garden-house.d.ts","sourceRoot":"","sources":["../../src/templates/garden-house.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAkRpE,eAAO,MAAM,QAAQ,EAAE,UAA4B,CAAA;AAEnD,eAAO,MAAM,QAAQ;aACnB,EAAE,EAAE,cAAc;aAClB,IAAI,EAAE,cAAc;aACpB,WAAW,EACT,4GAA4G;CACtG,CAAA"}
1
+ {"version":3,"file":"garden-house.d.ts","sourceRoot":"","sources":["../../src/templates/garden-house.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAgRpE,eAAO,MAAM,QAAQ,EAAE,UAA4B,CAAA;AAEnD,eAAO,MAAM,QAAQ;aACnB,EAAE,EAAE,cAAc;aAClB,IAAI,EAAE,cAAc;aACpB,WAAW,EACT,4GAA4G;CACtG,CAAA"}
@@ -1,3 +1,4 @@
1
+ import { NEW_DOOR_FRAME_THICKNESS } from '@pascal-app/core/building';
1
2
  /**
2
3
  * "Garden house" — a simplified take on the Casa del Sol layout used in the
3
4
  * MCP research fixtures.
@@ -50,8 +51,7 @@ function door(id, parentWallId, width = 0.9) {
50
51
  rotation: [0, 0, 0],
51
52
  width,
52
53
  height: 2.1,
53
- frameThickness: 0.05,
54
- frameDepth: 0.07,
54
+ frameThickness: NEW_DOOR_FRAME_THICKNESS,
55
55
  threshold: true,
56
56
  thresholdHeight: 0.02,
57
57
  hingesSide: 'left',
@@ -97,7 +97,6 @@ function makeWindow(id, parentWallId, width = 1.2) {
97
97
  width,
98
98
  height: 1.2,
99
99
  frameThickness: 0.05,
100
- frameDepth: 0.07,
101
100
  columnRatios: [1],
102
101
  rowRatios: [1],
103
102
  columnDividerThickness: 0.03,
@@ -1 +1 @@
1
- {"version":3,"file":"two-bedroom.d.ts","sourceRoot":"","sources":["../../src/templates/two-bedroom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AA2SpE,eAAO,MAAM,QAAQ,EAAE,UAA4B,CAAA;AAEnD,eAAO,MAAM,QAAQ;aACnB,EAAE,EAAE,aAAa;aACjB,IAAI,EAAE,uBAAuB;aAC7B,WAAW,EACT,qGAAqG;CAC/F,CAAA"}
1
+ {"version":3,"file":"two-bedroom.d.ts","sourceRoot":"","sources":["../../src/templates/two-bedroom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAySpE,eAAO,MAAM,QAAQ,EAAE,UAA4B,CAAA;AAEnD,eAAO,MAAM,QAAQ;aACnB,EAAE,EAAE,aAAa;aACjB,IAAI,EAAE,uBAAuB;aAC7B,WAAW,EACT,qGAAqG;CAC/F,CAAA"}
@@ -1,3 +1,4 @@
1
+ import { NEW_DOOR_FRAME_THICKNESS } from '@pascal-app/core/building';
1
2
  /**
2
3
  * 80 m² two-bedroom apartment.
3
4
  *
@@ -50,8 +51,7 @@ function door(id, parentWallId) {
50
51
  rotation: [0, 0, 0],
51
52
  width: 0.8,
52
53
  height: 2.1,
53
- frameThickness: 0.05,
54
- frameDepth: 0.07,
54
+ frameThickness: NEW_DOOR_FRAME_THICKNESS,
55
55
  threshold: true,
56
56
  thresholdHeight: 0.02,
57
57
  hingesSide: 'left',
@@ -97,7 +97,6 @@ function makeWindow(id, parentWallId, width = 1.2) {
97
97
  width,
98
98
  height: 1.2,
99
99
  frameThickness: 0.05,
100
- frameDepth: 0.07,
101
100
  columnRatios: [1],
102
101
  rowRatios: [1],
103
102
  columnDividerThickness: 0.03,
@@ -1 +1 @@
1
- {"version":3,"file":"add-column.d.ts","sourceRoot":"","sources":["../../src/tools/add-column.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAW7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAmB,KAAK,kBAAkB,EAAc,MAAM,cAAc,CAAA;AAMnF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,IAAI,CAAC,EAAE,kBAAkB,QAqD1B"}
1
+ {"version":3,"file":"add-column.d.ts","sourceRoot":"","sources":["../../src/tools/add-column.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAgB7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAmB,KAAK,kBAAkB,EAAc,MAAM,cAAc,CAAA;AAOnF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,IAAI,CAAC,EAAE,kBAAkB,QAyD1B"}
@@ -1,17 +1,18 @@
1
1
  import { isScriptedNode, scriptedObjectMeta } from '@pascal-app/core';
2
2
  import { addColumn, columnScriptParams, editedScriptLanguage, } from '@pascal-app/core/agent-operations';
3
- import { addColumnTool, isAgentRefusal, refuse } from '@pascal-app/core/agent-tools';
4
- import { GeometryReuseFields } from '@pascal-app/core/schema';
3
+ import { addColumnTool, isAgentRefusal, refuse, surfaceDescription, } from '@pascal-app/core/agent-tools';
4
+ import { GeometryReuseFields, generateId } from '@pascal-app/core/schema';
5
5
  import { z } from 'zod';
6
6
  import { compileAndStore, readScript } from './add-object.js';
7
7
  import { DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations.js';
8
8
  import { refusalResult, toolError } from './errors.js';
9
9
  import { persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
10
+ import { inScene } from './scene-scope.js';
10
11
  import { toPatches } from './shared-tools.js';
11
12
  export function registerAddColumn(server, bridge, host) {
12
13
  server.registerTool(addColumnTool.name, {
13
14
  title: addColumnTool.title,
14
- description: addColumnTool.description,
15
+ description: surfaceDescription(addColumnTool, 'mcp'),
15
16
  inputSchema: z.object(addColumnTool.input),
16
17
  annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
17
18
  }, async (input) => {
@@ -26,17 +27,19 @@ export function registerAddColumn(server, bridge, host) {
26
27
  const scene = bridge.getActiveScene();
27
28
  if (!scene)
28
29
  refuse('no_active_scene', 'Open or save a scene first.');
29
- const code = input.code ?? (await readScript(host, scene.id, bridge, input.nodeId));
30
+ // A new column's id is the scene's, minted before anything is awaited.
31
+ const nodeId = input.nodeId ?? inScene(bridge, () => generateId('column'));
30
32
  const language = editedScriptLanguage(previous, input);
33
+ const code = input.code ?? (await readScript(host, scene.id, bridge, input.nodeId));
31
34
  compiled = await compileAndStore(host, scene.id, { code, language, params }, 'column', {
32
- nodeId: input.nodeId,
35
+ nodeId,
33
36
  metadata: {
34
37
  ...(isScriptedNode(previous) ? scriptedObjectMeta(previous) : {}),
35
38
  ...GeometryReuseFields.parse(input),
36
39
  },
37
40
  });
38
41
  }
39
- const outcome = addColumn(nodes, { ...input, compiled }, { activeLevelId: bridge.activeLevelId?.() ?? null });
42
+ const outcome = inScene(bridge, () => addColumn(nodes, { ...input, compiled }, { activeLevelId: bridge.activeLevelId?.() ?? null }));
40
43
  if (outcome.changes)
41
44
  bridge.applyPatch(toPatches(outcome.changes));
42
45
  const payload = {
@@ -1,5 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/server';
2
- import { type AddObjectInput } from '@pascal-app/core/agent-operations';
2
+ import { type AddObjectInput, type PlannedPiece } from '@pascal-app/core/agent-operations';
3
3
  import { type CompiledGeometryScript, GeometryArtifactMetadata, type GeometryScriptLanguage, type GeometryScriptParamValue } from '@pascal-app/core/schema';
4
4
  import type { SceneOperations } from '../operations/index.js';
5
5
  /**
@@ -15,7 +15,7 @@ export type GeometryScriptHost = {
15
15
  code: string;
16
16
  language: GeometryScriptLanguage;
17
17
  params?: Record<string, GeometryScriptParamValue>;
18
- /** What the host builds it for (a room's floor, a wall's face: `floorScriptInputs`, `wallFaceScriptInputs`), kept in the host's frame. */
18
+ /** What the host builds it for (a room's floor or a slab's top, y = 0 its surface; a wall's face: `floorScriptInputs`, `slabSurfaceScriptInputs`, `wallFaceScriptInputs`), kept in the host's frame. */
19
19
  inputs?: Record<string, unknown>;
20
20
  }): Promise<CompiledGeometryScript & {
21
21
  glb: Uint8Array;
@@ -33,6 +33,16 @@ export type GeometryScriptHost = {
33
33
  sceneId: string;
34
34
  sha256: string;
35
35
  }): Promise<Uint8Array | null>;
36
+ /**
37
+ * Runs a design's adapter (`author_scene`'s), model code, where the host runs modules: the
38
+ * payload it returns. Throws with the adapter's own error.
39
+ */
40
+ runAdapter?(input: {
41
+ adapter: string;
42
+ design: string;
43
+ }): Promise<{
44
+ payload: Record<string, unknown>;
45
+ }>;
36
46
  /**
37
47
  * Compiles and stores in one step, for a host whose compiler keeps the artifacts itself (the
38
48
  * hosted MCP's user editor tab); preferred over `compile` + `storeArtifact` when present.
@@ -49,6 +59,34 @@ export type GeometryScriptHost = {
49
59
  metadata?: GeometryArtifactMetadata;
50
60
  }): Promise<CompiledGeometryScript>;
51
61
  };
62
+ /** A piece as the host that makes it knows it. */
63
+ export type PieceState = {
64
+ status: 'made';
65
+ code: string;
66
+ } | {
67
+ status: 'making';
68
+ } | {
69
+ status: 'failed';
70
+ error: string;
71
+ };
72
+ /**
73
+ * Makes objects from words (`add_object`'s `piece`) with the host's own models, in the background:
74
+ * the host bills them, writes each module under add_object's conventions, and when one lands places
75
+ * it where the call asked, and writes again the conversion whose items wait for it.
76
+ */
77
+ export type ObjectMakerHost = {
78
+ /** Records the piece and starts its maker; answers long before the module lands. */
79
+ start(input: {
80
+ sceneId: string;
81
+ piece: PlannedPiece;
82
+ metadata: GeometryArtifactMetadata;
83
+ }): Promise<void>;
84
+ /** The pieces by id; an id never started is absent. */
85
+ pieces(input: {
86
+ sceneId: string;
87
+ ids: readonly string[];
88
+ }): Promise<Record<string, PieceState>>;
89
+ };
52
90
  export type ScriptedKind = 'object' | 'window' | 'door' | 'column' | 'floor' | 'wall-face';
53
91
  /** Compiles a module on the host and stores its GLB and text for the scene: the step every scripted tool shares. */
54
92
  export declare function compileAndStore(host: GeometryScriptHost, sceneId: string, { code, language, params, inputs, }: {
@@ -64,13 +102,14 @@ export declare function readScript(host: GeometryScriptHost, sceneId: string, br
64
102
  /**
65
103
  * One object's module compiled and stored by the host: a new one from its code, an edit from its
66
104
  * code or its stored script, with the params it keeps. The step a lone add_object and each
67
- * add_object of a run_batch share.
105
+ * add_object of a run_batch share. What it reads from the scene it reads before its first await;
106
+ * `nodeId` is a new object's id, minted by the caller in the scene's scope.
68
107
  */
69
- export declare function compileObject(host: GeometryScriptHost, sceneId: string, bridge: SceneOperations, args: Omit<AddObjectInput, 'compiled'>): Promise<CompiledGeometryScript>;
108
+ export declare function compileObject(host: GeometryScriptHost, sceneId: string, bridge: SceneOperations, args: Omit<AddObjectInput, 'compiled'>, nodeId?: string | undefined): Promise<CompiledGeometryScript>;
70
109
  /** What a batch's add_object builds with: the host's compile, on the scene open now. */
71
110
  export declare function compileObjectOnHost(host: GeometryScriptHost | undefined, bridge: SceneOperations, args: Omit<AddObjectInput, 'compiled'>): Promise<CompiledGeometryScript>;
72
111
  /** `add_object` on the MCP: the shared contract and operation, with the host's compile in front. */
73
- export declare function registerAddObject(server: McpServer, bridge: SceneOperations, host: GeometryScriptHost | undefined): void;
112
+ export declare function registerAddObject(server: McpServer, bridge: SceneOperations, host: GeometryScriptHost | undefined, makers?: ObjectMakerHost): void;
74
113
  /** `get_source` on the MCP: the object's module text, read back through the host's store. */
75
114
  export declare function registerGetSource(server: McpServer, bridge: SceneOperations, host: GeometryScriptHost | undefined): void;
76
115
  //# sourceMappingURL=add-object.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"add-object.d.ts","sourceRoot":"","sources":["../../src/tools/add-object.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"add-object.d.ts","sourceRoot":"","sources":["../../src/tools/add-object.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  import { isScriptedNode, scriptedObjectMeta } from '@pascal-app/core';
2
- import { achievedChanges, addObject, authoredObject, editedScriptLanguage, editedScriptParams, furnishingRefusal, readableScript, readSourceResult, requireAddObjectReason, } from '@pascal-app/core/agent-operations';
3
- import { addObjectTool, CATALOG_SOURCE, getSourceTool, isAgentRefusal, refuse, } from '@pascal-app/core/agent-tools';
2
+ import { achievedChanges, addObject, authoredObject, editedScriptLanguage, editedScriptParams, furnishingRefusal, planPiece, readableScript, readSourceResult, requireAddObjectReason, } from '@pascal-app/core/agent-operations';
3
+ import { addObjectTool, CATALOG_SOURCE, getSourceTool, isAgentRefusal, refuse, surfaceDescription, } from '@pascal-app/core/agent-tools';
4
+ import { untracked } from '@pascal-app/core/runtime';
4
5
  import { GEOMETRY_SCRIPT_MIME_TYPES, GeometryArtifactMetadata, GeometryReuseFields, generateId, } from '@pascal-app/core/schema';
5
6
  import { z } from 'zod';
6
7
  import { DESTRUCTIVE_TOOL_ANNOTATIONS, READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
7
8
  import { refusalResult, toolError } from './errors.js';
8
9
  import { persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
10
+ import { inScene } from './scene-scope.js';
9
11
  import { briefInForce, toPatches } from './shared-tools.js';
10
12
  /** The kind an artifact records; a room's floor finish and a wall face's are none of the node kinds. */
11
13
  const ARTIFACT_KIND = {
@@ -31,13 +33,20 @@ export async function compileAndStore(host, sceneId, { code, language, params, i
31
33
  const { glb, ...compiled } = await host.compile({ code, language, params, inputs });
32
34
  metadata.mount = compiled.mount;
33
35
  const [sha256] = await Promise.all([
34
- host.storeArtifact({
36
+ host
37
+ .storeArtifact({
35
38
  sceneId,
36
39
  nodeId,
37
40
  metadata,
38
41
  sha256: compiled.sha256,
39
42
  bytes: glb,
40
43
  mimeType: 'model/gltf-binary',
44
+ })
45
+ .catch((error) => {
46
+ // 413, Payload Too Large: the host's store refuses a build this size.
47
+ if (error.status !== 413)
48
+ throw error;
49
+ throw new Error(`${error instanceof Error ? error.message : String(error)}: the build is ${(glb.byteLength / 1048576).toFixed(1)} MB for ${compiled.manifest.triangles.toLocaleString('en')} triangles. Build it with fewer triangles: fewer segments on curves, spheres and tubes, fewer repeated tufts, slats or links, a flat back where it stands against a wall.`);
41
50
  }),
42
51
  host.storeArtifact({
43
52
  sceneId,
@@ -62,22 +71,21 @@ export function readScript(host, sceneId, bridge, nodeId) {
62
71
  /**
63
72
  * One object's module compiled and stored by the host: a new one from its code, an edit from its
64
73
  * code or its stored script, with the params it keeps. The step a lone add_object and each
65
- * add_object of a run_batch share.
74
+ * add_object of a run_batch share. What it reads from the scene it reads before its first await;
75
+ * `nodeId` is a new object's id, minted by the caller in the scene's scope.
66
76
  */
67
- export async function compileObject(host, sceneId, bridge, args) {
68
- const code = args.code ??
69
- (args.nodeId ? await readScript(host, sceneId, bridge, args.nodeId) : refuseMissingCode());
77
+ export async function compileObject(host, sceneId, bridge, args, nodeId = args.nodeId) {
70
78
  const nodes = bridge.getNodes();
71
79
  const previous = args.nodeId ? nodes[args.nodeId] : undefined;
72
80
  const language = editedScriptLanguage(previous, args);
73
81
  const params = editedScriptParams(previous, args.params);
74
- return compileAndStore(host, sceneId, { code, language, params }, 'object', {
75
- nodeId: args.nodeId,
76
- metadata: {
77
- ...(isScriptedNode(previous) ? scriptedObjectMeta(previous) : {}),
78
- ...GeometryReuseFields.parse(args),
79
- },
80
- });
82
+ const metadata = {
83
+ ...(isScriptedNode(previous) ? scriptedObjectMeta(previous) : {}),
84
+ ...GeometryReuseFields.parse(args),
85
+ };
86
+ const code = args.code ??
87
+ (args.nodeId ? await readScript(host, sceneId, bridge, args.nodeId) : refuseMissingCode());
88
+ return compileAndStore(host, sceneId, { code, language, params }, 'object', { nodeId, metadata });
81
89
  }
82
90
  /** What a batch's add_object builds with: the host's compile, on the scene open now. */
83
91
  export async function compileObjectOnHost(host, bridge, args) {
@@ -86,13 +94,14 @@ export async function compileObjectOnHost(host, bridge, args) {
86
94
  const scene = bridge.getActiveScene();
87
95
  if (!scene)
88
96
  refuse('no_active_scene', 'Open or save a scene first: authored objects are stored with a scene.');
89
- return compileObject(host, scene.id, bridge, args);
97
+ const nodeId = args.nodeId ?? inScene(bridge, () => generateId('item'));
98
+ return compileObject(host, scene.id, bridge, args, nodeId);
90
99
  }
91
100
  /** `add_object` on the MCP: the shared contract and operation, with the host's compile in front. */
92
- export function registerAddObject(server, bridge, host) {
101
+ export function registerAddObject(server, bridge, host, makers) {
93
102
  server.registerTool(addObjectTool.name, {
94
103
  title: addObjectTool.title,
95
- description: addObjectTool.description,
104
+ description: surfaceDescription(addObjectTool, 'mcp'),
96
105
  inputSchema: z.object(addObjectTool.input),
97
106
  annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
98
107
  }, async (input) => {
@@ -114,10 +123,14 @@ export function registerAddObject(server, bridge, host) {
114
123
  });
115
124
  }
116
125
  const args = input;
126
+ if (args.piece)
127
+ return startPiece(bridge, scene.id, makers, args);
128
+ // A new object's id is the scene's, minted before anything is awaited.
129
+ const nodeId = args.nodeId ?? inScene(bridge, () => generateId('item'));
117
130
  let compiled;
118
131
  try {
119
132
  requireAddObjectReason(args);
120
- compiled = await compileObject(host, scene.id, bridge, args);
133
+ compiled = await compileObject(host, scene.id, bridge, args, nodeId);
121
134
  }
122
135
  catch (error) {
123
136
  if (isAgentRefusal(error))
@@ -126,10 +139,17 @@ export function registerAddObject(server, bridge, host) {
126
139
  }
127
140
  // A copy: the hosted bridge writes its map in place, and a "before" that grows with the call
128
141
  // reads every creation as unchanged.
129
- const before = { ...bridge.getNodes() };
142
+ // Copying is not a read: on a detached scene the operation records what it selects.
143
+ const nodes = bridge.getNodes();
144
+ const before = { ...untracked(nodes) };
130
145
  let outcome;
131
146
  try {
132
- outcome = addObject(before, { ...args, compiled }, { activeLevelId: bridge.activeLevelId?.() ?? null });
147
+ outcome = inScene(bridge, () => addObject(nodes, { ...args, compiled }, {
148
+ activeLevelId: bridge.activeLevelId?.() ?? null,
149
+ ...(bridge.supportsMaterialUpserts && bridge.getMaterials
150
+ ? { materials: bridge.getMaterials() }
151
+ : {}),
152
+ }));
133
153
  }
134
154
  catch (error) {
135
155
  return refusalResult(error);
@@ -149,6 +169,40 @@ export function registerAddObject(server, bridge, host) {
149
169
  };
150
170
  });
151
171
  }
172
+ /** add_object from words: the piece planned, its maker started on the host, answered at once. */
173
+ async function startPiece(bridge, sceneId, makers, args) {
174
+ if (!makers)
175
+ return toolError('This Pascal server cannot make objects from words: pass the module as code.', {
176
+ code: 'pieces_unavailable',
177
+ });
178
+ let piece;
179
+ try {
180
+ piece = planPiece(args);
181
+ if (piece.place?.parentId && !(piece.place.parentId in bridge.getNodes()))
182
+ refuse('parent_not_found', `No node ${piece.place.parentId} to place the object on.`);
183
+ await makers.start({ sceneId, piece, metadata: GeometryReuseFields.parse(args) });
184
+ }
185
+ catch (error) {
186
+ if (isAgentRefusal(error))
187
+ return refusalResult(error);
188
+ return toolError(error instanceof Error ? error.message : String(error), {
189
+ code: 'piece_not_started',
190
+ });
191
+ }
192
+ const payload = {
193
+ piece: piece.id,
194
+ status: 'pending',
195
+ size: piece.size,
196
+ mount: piece.mount,
197
+ placed: piece.place
198
+ ? 'Where you asked, once its module lands.'
199
+ : `Not placed: author_scene items place it by its id (piece: "${piece.id}").`,
200
+ };
201
+ return {
202
+ content: [{ type: 'text', text: JSON.stringify(payload) }],
203
+ structuredContent: payload,
204
+ };
205
+ }
152
206
  function refuseMissingCode() {
153
207
  refuse('code_required', 'Pass code to build a new object; params alone rebuild an existing one (nodeId).');
154
208
  }
@@ -156,7 +210,7 @@ function refuseMissingCode() {
156
210
  export function registerGetSource(server, bridge, host) {
157
211
  server.registerTool(getSourceTool.name, {
158
212
  title: getSourceTool.title,
159
- description: getSourceTool.description,
213
+ description: surfaceDescription(getSourceTool, 'mcp'),
160
214
  inputSchema: z.object(getSourceTool.input),
161
215
  annotations: READ_ONLY_TOOL_ANNOTATIONS,
162
216
  }, async ({ nodeId, face }) => {
@@ -22,7 +22,7 @@ export const applyPatchOutput = {
22
22
  export function registerApplyPatch(server, bridge) {
23
23
  server.registerTool('apply_patch', {
24
24
  title: 'Apply patch',
25
- description: "Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step. Batch-first is the default: prefer one apply_patch call containing all create/update/delete ops for a build step, in stable order so later ops can reference ids created by earlier ops. A single call is atomic (all or nothing) and pays the snapshot and save cost once; do not loop one-op calls. A create whose id already exists fails the whole patch with node_exists; to replace a node, delete it earlier in the same patch. An update cannot change id or type (identity_change) or object or children (immutable_field); restating the current value is fine. A parentId change must name an existing node that holds children (invalid_parent). An update cannot add schema issues to a node (invalid_update). After a delete or a roof update that regenerates a roof's default gutters, those gutters and their downspouts are addressable only in a later call (regenerated_default). A roof created or moved onto the storey whose walls carry it goes to the level above them (the roof level over a top storey), said in notes; storey content (walls, zones, slabs, ceilings, items, stairs) on a roof level (roof_level), a walled level declared the roof level (roof_level_holds_storey) and floor indexes putting a roof level under a storey (roof_level_on_top) are refused. Refusals come back as a tool error whose text is JSON {code, patchIndex, id, message}.",
25
+ description: "Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step. Batch-first is the default: prefer one apply_patch call containing all create/update/delete ops for a build step, in stable order so later ops can reference ids created by earlier ops. A single call is atomic (all or nothing) and pays the snapshot and save cost once; do not loop one-op calls. A create whose id already exists fails the whole patch with node_exists; to replace a node, delete it earlier in the same patch. An update cannot change id or type (identity_change) or object or children (immutable_field); restating the current value is fine. A parentId change must name an existing node that holds children and cannot point to the node or one of its descendants (invalid_parent). An update cannot add schema issues to a node (invalid_update). After a delete or a roof update that regenerates a roof's default gutters, those gutters and their downspouts are addressable only in a later call (regenerated_default). A roof created or moved onto the storey whose walls carry it goes to the level above them (the roof level over a top storey), said in notes; storey content (walls, zones, slabs, ceilings, items, stairs) on a roof level (roof_level), a walled level declared the roof level (roof_level_holds_storey) and floor indexes putting a roof level under a storey (roof_level_on_top) are refused. Refusals come back as a tool error whose text is JSON {code, patchIndex, id, message}.",
26
26
  inputSchema: z.object(applyPatchInput),
27
27
  outputSchema: z.object(applyPatchOutput),
28
28
  annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
@@ -9,12 +9,11 @@ import type { AssetInput } from '@pascal-app/core/schema';
9
9
  */
10
10
  export declare const MCP_CATALOG_ITEMS: AssetInput[];
11
11
  /**
12
- * The items search_assets, place_items and furnish_room draw from. A host passes its own (the hosted
13
- * app: its published library, as the chat searches); a standalone server keeps the built-in list.
12
+ * The items search_assets, place_items and furnish_room draw from: a host passes its own (the
13
+ * hosted app: a source over its published library, as the chat searches it); a standalone server
14
+ * keeps the built-in list.
14
15
  */
15
- export type AssetCatalog = () => Promise<readonly AssetInput[]>;
16
- export declare const builtInCatalog: AssetCatalog;
17
- /** A host's catalog, read once by the first call that needs it; a failed read is tried again. */
18
- export declare function cachedCatalog(load: AssetCatalog): AssetCatalog;
16
+ export type { AssetCatalog } from '@pascal-app/core/agent-operations';
17
+ export declare const builtInCatalog: readonly AssetInput[];
19
18
  export declare function findCatalogItem(items: readonly AssetInput[], id: string): AssetInput | undefined;
20
19
  //# sourceMappingURL=asset-catalog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"asset-catalog.d.ts","sourceRoot":"","sources":["../../src/tools/asset-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAEzD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,UAAU,EAoTzC,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAA;AAE/D,eAAO,MAAM,cAAc,EAAE,YAA4C,CAAA;AAEzE,iGAAiG;AACjG,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAO9D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAEhG"}
1
+ {"version":3,"file":"asset-catalog.d.ts","sourceRoot":"","sources":["../../src/tools/asset-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAEzD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,UAAU,EAoTzC,CAAA;AAED;;;;GAIG;AACH,YAAY,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAA;AAErE,eAAO,MAAM,cAAc,EAAE,SAAS,UAAU,EAAsB,CAAA;AAEtE,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAEhG"}
@@ -315,15 +315,7 @@ export const MCP_CATALOG_ITEMS = [
315
315
  scale: [1, 1, 1],
316
316
  },
317
317
  ];
318
- export const builtInCatalog = async () => MCP_CATALOG_ITEMS;
319
- /** A host's catalog, read once by the first call that needs it; a failed read is tried again. */
320
- export function cachedCatalog(load) {
321
- let items = null;
322
- return () => (items ??= load().catch((error) => {
323
- items = null;
324
- throw error;
325
- }));
326
- }
318
+ export const builtInCatalog = MCP_CATALOG_ITEMS;
327
319
  export function findCatalogItem(items, id) {
328
320
  return items.find((item) => item.id === id);
329
321
  }
@@ -1,10 +1,13 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/server';
2
+ import { type ProjectFileReader } from '@pascal-app/core/agent-operations';
2
3
  import type { SceneOperations } from '../operations/index.js';
3
- import { type GeometryScriptHost } from './add-object.js';
4
+ import { type GeometryScriptHost, type ObjectMakerHost } from './add-object.js';
4
5
  /**
5
- * `author_scene` on the MCP: every item's module compiled and stored through the host, the
6
- * module and the adapter kept as the scene's provenance, the operation applied, Pascal's checks
7
- * run, and the gate report answered. Replay is the same payload through the same operation.
6
+ * `author_scene` on the MCP: the payload given, or the one the adapter answers on the module (the
7
+ * stored module and adapter, changed by edits, when left out), or the stored conversion again;
8
+ * items naming a piece written with its module, those still being made left for the host to write
9
+ * when they land; every module compiled and stored through the host, the operation applied,
10
+ * Pascal's checks run, and the gate report answered.
8
11
  */
9
- export declare function registerAuthorScene(server: McpServer, bridge: SceneOperations, host: GeometryScriptHost | undefined): void;
12
+ export declare function registerAuthorScene(server: McpServer, bridge: SceneOperations, host: GeometryScriptHost | undefined, makers?: ObjectMakerHost, readFile?: ProjectFileReader): void;
10
13
  //# sourceMappingURL=author-scene.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"author-scene.d.ts","sourceRoot":"","sources":["../../src/tools/author-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAe7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAevE;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,kBAAkB,GAAG,SAAS,GACnC,IAAI,CA0HN"}
1
+ {"version":3,"file":"author-scene.d.ts","sourceRoot":"","sources":["../../src/tools/author-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAE7D,OAAO,EAML,KAAK,iBAAiB,EAGvB,MAAM,mCAAmC,CAAA;AAY1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAmB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAoC7F;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,kBAAkB,GAAG,SAAS,EACpC,MAAM,CAAC,EAAE,eAAe,EACxB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,IAAI,CAkMN"}