@pascal-app/mcp 0.2.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 (249) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +330 -0
  3. package/dist/bin/pascal-mcp.d.ts +3 -0
  4. package/dist/bin/pascal-mcp.d.ts.map +1 -0
  5. package/dist/bin/pascal-mcp.js +91 -0
  6. package/dist/bridge/index.d.ts +3 -0
  7. package/dist/bridge/index.d.ts.map +1 -0
  8. package/dist/bridge/index.js +1 -0
  9. package/dist/bridge/node-shims.d.ts +16 -0
  10. package/dist/bridge/node-shims.d.ts.map +1 -0
  11. package/dist/bridge/node-shims.js +25 -0
  12. package/dist/bridge/scene-bridge.d.ts +158 -0
  13. package/dist/bridge/scene-bridge.d.ts.map +1 -0
  14. package/dist/bridge/scene-bridge.js +482 -0
  15. package/dist/index.d.ts +5 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +4 -0
  18. package/dist/lib/rehydrate-site-children.d.ts +12 -0
  19. package/dist/lib/rehydrate-site-children.d.ts.map +1 -0
  20. package/dist/lib/rehydrate-site-children.js +40 -0
  21. package/dist/lib/safe-fetch.d.ts +19 -0
  22. package/dist/lib/safe-fetch.d.ts.map +1 -0
  23. package/dist/lib/safe-fetch.js +221 -0
  24. package/dist/operations/index.d.ts +2 -0
  25. package/dist/operations/index.d.ts.map +1 -0
  26. package/dist/operations/index.js +1 -0
  27. package/dist/operations/scene-operations.d.ts +67 -0
  28. package/dist/operations/scene-operations.d.ts.map +1 -0
  29. package/dist/operations/scene-operations.js +192 -0
  30. package/dist/prompts/from-brief.d.ts +11 -0
  31. package/dist/prompts/from-brief.d.ts.map +1 -0
  32. package/dist/prompts/from-brief.js +48 -0
  33. package/dist/prompts/index.d.ts +10 -0
  34. package/dist/prompts/index.d.ts.map +1 -0
  35. package/dist/prompts/index.js +14 -0
  36. package/dist/prompts/iterate-on-feedback.d.ts +11 -0
  37. package/dist/prompts/iterate-on-feedback.d.ts.map +1 -0
  38. package/dist/prompts/iterate-on-feedback.js +41 -0
  39. package/dist/prompts/renovation-from-photos.d.ts +24 -0
  40. package/dist/prompts/renovation-from-photos.d.ts.map +1 -0
  41. package/dist/prompts/renovation-from-photos.js +135 -0
  42. package/dist/prompts/scene-guidance.d.ts +2 -0
  43. package/dist/prompts/scene-guidance.d.ts.map +1 -0
  44. package/dist/prompts/scene-guidance.js +24 -0
  45. package/dist/resources/agent-guide.d.ts +5 -0
  46. package/dist/resources/agent-guide.d.ts.map +1 -0
  47. package/dist/resources/agent-guide.js +85 -0
  48. package/dist/resources/catalog-items.d.ts +10 -0
  49. package/dist/resources/catalog-items.d.ts.map +1 -0
  50. package/dist/resources/catalog-items.js +29 -0
  51. package/dist/resources/constraints.d.ts +9 -0
  52. package/dist/resources/constraints.d.ts.map +1 -0
  53. package/dist/resources/constraints.js +69 -0
  54. package/dist/resources/index.d.ts +15 -0
  55. package/dist/resources/index.d.ts.map +1 -0
  56. package/dist/resources/index.js +23 -0
  57. package/dist/resources/scene-current.d.ts +9 -0
  58. package/dist/resources/scene-current.d.ts.map +1 -0
  59. package/dist/resources/scene-current.js +20 -0
  60. package/dist/resources/scene-summary.d.ts +10 -0
  61. package/dist/resources/scene-summary.d.ts.map +1 -0
  62. package/dist/resources/scene-summary.js +196 -0
  63. package/dist/server.d.ts +14 -0
  64. package/dist/server.d.ts.map +1 -0
  65. package/dist/server.js +18 -0
  66. package/dist/storage/index.d.ts +13 -0
  67. package/dist/storage/index.d.ts.map +1 -0
  68. package/dist/storage/index.js +14 -0
  69. package/dist/storage/slug.d.ts +21 -0
  70. package/dist/storage/slug.d.ts.map +1 -0
  71. package/dist/storage/slug.js +62 -0
  72. package/dist/storage/sqlite-driver.d.ts +18 -0
  73. package/dist/storage/sqlite-driver.d.ts.map +1 -0
  74. package/dist/storage/sqlite-driver.js +32 -0
  75. package/dist/storage/sqlite-scene-store.d.ts +51 -0
  76. package/dist/storage/sqlite-scene-store.d.ts.map +1 -0
  77. package/dist/storage/sqlite-scene-store.js +526 -0
  78. package/dist/storage/types.d.ts +136 -0
  79. package/dist/storage/types.d.ts.map +1 -0
  80. package/dist/storage/types.js +28 -0
  81. package/dist/templates/empty-studio.d.ts +8 -0
  82. package/dist/templates/empty-studio.d.ts.map +1 -0
  83. package/dist/templates/empty-studio.js +230 -0
  84. package/dist/templates/garden-house.d.ts +8 -0
  85. package/dist/templates/garden-house.d.ts.map +1 -0
  86. package/dist/templates/garden-house.js +242 -0
  87. package/dist/templates/index.d.ts +23 -0
  88. package/dist/templates/index.d.ts.map +1 -0
  89. package/dist/templates/index.js +20 -0
  90. package/dist/templates/two-bedroom.d.ts +8 -0
  91. package/dist/templates/two-bedroom.d.ts.map +1 -0
  92. package/dist/templates/two-bedroom.js +270 -0
  93. package/dist/tools/apply-patch.d.ts +25 -0
  94. package/dist/tools/apply-patch.d.ts.map +1 -0
  95. package/dist/tools/apply-patch.js +59 -0
  96. package/dist/tools/asset-catalog.d.ts +16 -0
  97. package/dist/tools/asset-catalog.d.ts.map +1 -0
  98. package/dist/tools/asset-catalog.js +308 -0
  99. package/dist/tools/check-collisions.d.ts +15 -0
  100. package/dist/tools/check-collisions.d.ts.map +1 -0
  101. package/dist/tools/check-collisions.js +61 -0
  102. package/dist/tools/construction-tools.d.ts +93 -0
  103. package/dist/tools/construction-tools.d.ts.map +1 -0
  104. package/dist/tools/construction-tools.js +369 -0
  105. package/dist/tools/create-level.d.ts +14 -0
  106. package/dist/tools/create-level.d.ts.map +1 -0
  107. package/dist/tools/create-level.js +48 -0
  108. package/dist/tools/create-wall.d.ts +15 -0
  109. package/dist/tools/create-wall.d.ts.map +1 -0
  110. package/dist/tools/create-wall.js +50 -0
  111. package/dist/tools/cut-opening.d.ts +18 -0
  112. package/dist/tools/cut-opening.d.ts.map +1 -0
  113. package/dist/tools/cut-opening.js +57 -0
  114. package/dist/tools/delete-node.d.ts +12 -0
  115. package/dist/tools/delete-node.d.ts.map +1 -0
  116. package/dist/tools/delete-node.js +37 -0
  117. package/dist/tools/describe-node.d.ts +17 -0
  118. package/dist/tools/describe-node.d.ts.map +1 -0
  119. package/dist/tools/describe-node.js +86 -0
  120. package/dist/tools/duplicate-level.d.ts +12 -0
  121. package/dist/tools/duplicate-level.d.ts.map +1 -0
  122. package/dist/tools/duplicate-level.js +56 -0
  123. package/dist/tools/errors.d.ts +20 -0
  124. package/dist/tools/errors.d.ts.map +1 -0
  125. package/dist/tools/errors.js +25 -0
  126. package/dist/tools/export-glb.d.ts +10 -0
  127. package/dist/tools/export-glb.d.ts.map +1 -0
  128. package/dist/tools/export-glb.js +24 -0
  129. package/dist/tools/export-json.d.ts +11 -0
  130. package/dist/tools/export-json.d.ts.map +1 -0
  131. package/dist/tools/export-json.js +23 -0
  132. package/dist/tools/find-nodes.d.ts +32 -0
  133. package/dist/tools/find-nodes.d.ts.map +1 -0
  134. package/dist/tools/find-nodes.js +105 -0
  135. package/dist/tools/geometry.d.ts +24 -0
  136. package/dist/tools/geometry.d.ts.map +1 -0
  137. package/dist/tools/geometry.js +94 -0
  138. package/dist/tools/get-node.d.ts +11 -0
  139. package/dist/tools/get-node.d.ts.map +1 -0
  140. package/dist/tools/get-node.js +27 -0
  141. package/dist/tools/get-scene.d.ts +11 -0
  142. package/dist/tools/get-scene.d.ts.map +1 -0
  143. package/dist/tools/get-scene.js +26 -0
  144. package/dist/tools/index.d.ts +12 -0
  145. package/dist/tools/index.d.ts.map +1 -0
  146. package/dist/tools/index.js +64 -0
  147. package/dist/tools/live-sync.d.ts +11 -0
  148. package/dist/tools/live-sync.d.ts.map +1 -0
  149. package/dist/tools/live-sync.js +62 -0
  150. package/dist/tools/measure.d.ts +14 -0
  151. package/dist/tools/measure.d.ts.map +1 -0
  152. package/dist/tools/measure.js +137 -0
  153. package/dist/tools/photo-to-scene/index.d.ts +11 -0
  154. package/dist/tools/photo-to-scene/index.d.ts.map +1 -0
  155. package/dist/tools/photo-to-scene/index.js +11 -0
  156. package/dist/tools/photo-to-scene/photo-to-scene.d.ts +26 -0
  157. package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -0
  158. package/dist/tools/photo-to-scene/photo-to-scene.js +323 -0
  159. package/dist/tools/place-item.d.ts +15 -0
  160. package/dist/tools/place-item.d.ts.map +1 -0
  161. package/dist/tools/place-item.js +84 -0
  162. package/dist/tools/redo.d.ts +11 -0
  163. package/dist/tools/redo.d.ts.map +1 -0
  164. package/dist/tools/redo.js +25 -0
  165. package/dist/tools/room-tools.d.ts +97 -0
  166. package/dist/tools/room-tools.d.ts.map +1 -0
  167. package/dist/tools/room-tools.js +510 -0
  168. package/dist/tools/scene-lifecycle/create-project.d.ts +33 -0
  169. package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -0
  170. package/dist/tools/scene-lifecycle/create-project.js +70 -0
  171. package/dist/tools/scene-lifecycle/delete-scene.d.ts +12 -0
  172. package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -0
  173. package/dist/tools/scene-lifecycle/delete-scene.js +42 -0
  174. package/dist/tools/scene-lifecycle/get-project-status.d.ts +31 -0
  175. package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -0
  176. package/dist/tools/scene-lifecycle/get-project-status.js +69 -0
  177. package/dist/tools/scene-lifecycle/index.d.ts +17 -0
  178. package/dist/tools/scene-lifecycle/index.d.ts.map +1 -0
  179. package/dist/tools/scene-lifecycle/index.js +29 -0
  180. package/dist/tools/scene-lifecycle/list-scenes.d.ts +27 -0
  181. package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -0
  182. package/dist/tools/scene-lifecycle/list-scenes.js +49 -0
  183. package/dist/tools/scene-lifecycle/load-scene.d.ts +31 -0
  184. package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -0
  185. package/dist/tools/scene-lifecycle/load-scene.js +55 -0
  186. package/dist/tools/scene-lifecycle/metadata.d.ts +49 -0
  187. package/dist/tools/scene-lifecycle/metadata.d.ts.map +1 -0
  188. package/dist/tools/scene-lifecycle/metadata.js +68 -0
  189. package/dist/tools/scene-lifecycle/rename-scene.d.ts +22 -0
  190. package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -0
  191. package/dist/tools/scene-lifecycle/rename-scene.js +63 -0
  192. package/dist/tools/scene-lifecycle/save-scene.d.ts +42 -0
  193. package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -0
  194. package/dist/tools/scene-lifecycle/save-scene.js +130 -0
  195. package/dist/tools/scene-lifecycle/test-utils.d.ts +39 -0
  196. package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -0
  197. package/dist/tools/scene-lifecycle/test-utils.js +211 -0
  198. package/dist/tools/scene-query.d.ts +57 -0
  199. package/dist/tools/scene-query.d.ts.map +1 -0
  200. package/dist/tools/scene-query.js +645 -0
  201. package/dist/tools/schemas.d.ts +52 -0
  202. package/dist/tools/schemas.d.ts.map +1 -0
  203. package/dist/tools/schemas.js +42 -0
  204. package/dist/tools/set-zone.d.ts +14 -0
  205. package/dist/tools/set-zone.d.ts.map +1 -0
  206. package/dist/tools/set-zone.js +48 -0
  207. package/dist/tools/templates/create-from-template.d.ts +54 -0
  208. package/dist/tools/templates/create-from-template.d.ts.map +1 -0
  209. package/dist/tools/templates/create-from-template.js +145 -0
  210. package/dist/tools/templates/create-house-from-brief.d.ts +35 -0
  211. package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -0
  212. package/dist/tools/templates/create-house-from-brief.js +170 -0
  213. package/dist/tools/templates/index.d.ts +14 -0
  214. package/dist/tools/templates/index.d.ts.map +1 -0
  215. package/dist/tools/templates/index.js +18 -0
  216. package/dist/tools/templates/list-templates.d.ts +18 -0
  217. package/dist/tools/templates/list-templates.d.ts.map +1 -0
  218. package/dist/tools/templates/list-templates.js +36 -0
  219. package/dist/tools/undo.d.ts +11 -0
  220. package/dist/tools/undo.d.ts.map +1 -0
  221. package/dist/tools/undo.js +25 -0
  222. package/dist/tools/validate-scene.d.ts +14 -0
  223. package/dist/tools/validate-scene.d.ts.map +1 -0
  224. package/dist/tools/validate-scene.js +24 -0
  225. package/dist/tools/variants/generate-variants.d.ts +30 -0
  226. package/dist/tools/variants/generate-variants.d.ts.map +1 -0
  227. package/dist/tools/variants/generate-variants.js +160 -0
  228. package/dist/tools/variants/index.d.ts +9 -0
  229. package/dist/tools/variants/index.d.ts.map +1 -0
  230. package/dist/tools/variants/index.js +9 -0
  231. package/dist/tools/variants/mutations.d.ts +18 -0
  232. package/dist/tools/variants/mutations.d.ts.map +1 -0
  233. package/dist/tools/variants/mutations.js +324 -0
  234. package/dist/tools/vision/analyze-floorplan-image.d.ts +33 -0
  235. package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -0
  236. package/dist/tools/vision/analyze-floorplan-image.js +146 -0
  237. package/dist/tools/vision/analyze-room-photo.d.ts +29 -0
  238. package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -0
  239. package/dist/tools/vision/analyze-room-photo.js +130 -0
  240. package/dist/tools/vision/index.d.ts +12 -0
  241. package/dist/tools/vision/index.d.ts.map +1 -0
  242. package/dist/tools/vision/index.js +14 -0
  243. package/dist/transports/http.d.ts +36 -0
  244. package/dist/transports/http.d.ts.map +1 -0
  245. package/dist/transports/http.js +203 -0
  246. package/dist/transports/stdio.d.ts +14 -0
  247. package/dist/transports/stdio.d.ts.map +1 -0
  248. package/dist/transports/stdio.js +16 -0
  249. package/package.json +86 -0
@@ -0,0 +1,196 @@
1
+ /** Shoelace polygon area (absolute, square meters). */
2
+ function polygonArea(poly) {
3
+ if (!Array.isArray(poly) || poly.length < 3)
4
+ return 0;
5
+ let sum = 0;
6
+ for (let i = 0; i < poly.length; i++) {
7
+ const a = poly[i];
8
+ const b = poly[(i + 1) % poly.length];
9
+ if (!(a && b))
10
+ continue;
11
+ sum += a[0] * b[1] - b[0] * a[1];
12
+ }
13
+ return Math.abs(sum) / 2;
14
+ }
15
+ function emptyBBox() {
16
+ return {
17
+ min: [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
18
+ max: [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
19
+ empty: true,
20
+ };
21
+ }
22
+ function expandBBox(bbox, x, y, z) {
23
+ if (!(Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z)))
24
+ return;
25
+ bbox.empty = false;
26
+ if (x < bbox.min[0])
27
+ bbox.min[0] = x;
28
+ if (y < bbox.min[1])
29
+ bbox.min[1] = y;
30
+ if (z < bbox.min[2])
31
+ bbox.min[2] = z;
32
+ if (x > bbox.max[0])
33
+ bbox.max[0] = x;
34
+ if (y > bbox.max[1])
35
+ bbox.max[1] = y;
36
+ if (z > bbox.max[2])
37
+ bbox.max[2] = z;
38
+ }
39
+ /** Fold a node's world-relevant points into the running bbox. */
40
+ function foldNodeIntoBBox(node, bbox) {
41
+ // Walls / fences: 2D start/end. Treat missing y as 0.
42
+ if (node.type === 'wall' || node.type === 'fence') {
43
+ const anyNode = node;
44
+ if (anyNode.start)
45
+ expandBBox(bbox, anyNode.start[0], 0, anyNode.start[1]);
46
+ if (anyNode.end)
47
+ expandBBox(bbox, anyNode.end[0], 0, anyNode.end[1]);
48
+ return;
49
+ }
50
+ // Zone / slab / ceiling: polygon + optional holes. Treat ground plane y=0.
51
+ if (node.type === 'zone' || node.type === 'slab' || node.type === 'ceiling') {
52
+ const poly = node.polygon;
53
+ if (Array.isArray(poly)) {
54
+ for (const p of poly) {
55
+ if (Array.isArray(p) && p.length >= 2)
56
+ expandBBox(bbox, p[0], 0, p[1]);
57
+ }
58
+ }
59
+ return;
60
+ }
61
+ // Positioned nodes (building/item/roof/stair/scan/guide/...):
62
+ const pos = node.position;
63
+ if (Array.isArray(pos) && pos.length >= 3) {
64
+ expandBBox(bbox, pos[0], pos[1], pos[2]);
65
+ }
66
+ }
67
+ function countByType(nodes) {
68
+ const out = {};
69
+ for (const n of nodes) {
70
+ out[n.type] = (out[n.type] ?? 0) + 1;
71
+ }
72
+ return out;
73
+ }
74
+ /** Build the markdown summary. Pure over the SceneGraph snapshot. */
75
+ export function buildSceneSummaryMarkdown(snapshot) {
76
+ const { nodes, rootNodeIds } = snapshot;
77
+ const allNodes = Object.values(nodes);
78
+ const sites = allNodes.filter((n) => n.type === 'site');
79
+ const buildings = allNodes.filter((n) => n.type === 'building');
80
+ const levels = allNodes.filter((n) => n.type === 'level');
81
+ const bbox = emptyBBox();
82
+ for (const n of allNodes)
83
+ foldNodeIntoBBox(n, bbox);
84
+ const lines = [];
85
+ lines.push('# Scene summary');
86
+ lines.push('');
87
+ lines.push(`- Sites: ${sites.length} Buildings: ${buildings.length} Levels: ${levels.length}`);
88
+ lines.push(`- Root nodes: ${rootNodeIds.length}`);
89
+ lines.push(`- Total nodes: ${allNodes.length}`);
90
+ lines.push('');
91
+ // Hierarchy table
92
+ lines.push('## Hierarchy');
93
+ lines.push('');
94
+ lines.push('| Site | Building | Level |');
95
+ lines.push('| --- | --- | --- |');
96
+ if (sites.length === 0 && buildings.length === 0 && levels.length === 0) {
97
+ lines.push('| _(empty scene)_ | | |');
98
+ }
99
+ else {
100
+ for (const site of sites) {
101
+ const sName = site.name ?? site.id;
102
+ const siteBuildings = allNodes.filter((n) => n.type === 'building' && n.parentId === site.id);
103
+ if (siteBuildings.length === 0) {
104
+ lines.push(`| ${sName} | _(none)_ | |`);
105
+ continue;
106
+ }
107
+ for (const b of siteBuildings) {
108
+ const bName = b.name ?? b.id;
109
+ const bLevels = allNodes.filter((n) => n.type === 'level' && n.parentId === b.id);
110
+ if (bLevels.length === 0) {
111
+ lines.push(`| ${sName} | ${bName} | _(none)_ |`);
112
+ continue;
113
+ }
114
+ for (const l of bLevels) {
115
+ const lName = l.name ?? l.id;
116
+ lines.push(`| ${sName} | ${bName} | ${lName} |`);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ lines.push('');
122
+ // Per-level detail
123
+ if (levels.length > 0) {
124
+ lines.push('## Per level');
125
+ lines.push('');
126
+ for (const level of levels) {
127
+ const lName = level.name ?? level.id;
128
+ // Nodes whose ancestry includes this level.
129
+ const levelNodes = allNodes.filter((n) => n.id !== level.id && walkToLevel(n, nodes) === level.id);
130
+ const counts = countByType(levelNodes);
131
+ const countKeys = Object.keys(counts).sort();
132
+ // Estimated floor area = sum of zone polygon areas on this level.
133
+ const zones = levelNodes.filter((n) => n.type === 'zone');
134
+ let floorAreaSq = 0;
135
+ for (const z of zones) {
136
+ floorAreaSq += polygonArea(z.polygon);
137
+ }
138
+ lines.push(`### ${lName}`);
139
+ lines.push('');
140
+ if (countKeys.length === 0) {
141
+ lines.push('- _(no descendants)_');
142
+ }
143
+ else {
144
+ const parts = countKeys.map((k) => `${k}=${counts[k]}`);
145
+ lines.push(`- Node counts: ${parts.join(', ')}`);
146
+ }
147
+ lines.push(`- Estimated floor area (zones): ${floorAreaSq.toFixed(2)} m^2`);
148
+ lines.push('');
149
+ }
150
+ }
151
+ // BBox
152
+ lines.push('## Scene bbox (meters)');
153
+ lines.push('');
154
+ if (bbox.empty) {
155
+ lines.push('- _(no positioned nodes)_');
156
+ }
157
+ else {
158
+ lines.push(`- min: [${bbox.min.map((v) => v.toFixed(3)).join(', ')}]`);
159
+ lines.push(`- max: [${bbox.max.map((v) => v.toFixed(3)).join(', ')}]`);
160
+ }
161
+ return lines.join('\n');
162
+ }
163
+ /** Walk up parentId until we find a level; return its id or null. */
164
+ function walkToLevel(node, nodes) {
165
+ const seen = new Set();
166
+ let current = node;
167
+ while (current && !seen.has(current.id)) {
168
+ seen.add(current.id);
169
+ if (current.type === 'level')
170
+ return current.id;
171
+ const pid = current.parentId;
172
+ if (!pid)
173
+ return null;
174
+ current = nodes[pid];
175
+ }
176
+ return null;
177
+ }
178
+ /**
179
+ * `pascal://scene/current/summary` — human-readable scene overview.
180
+ * MIME `text/markdown`.
181
+ */
182
+ export function registerSceneSummary(server, bridge) {
183
+ server.registerResource('scene-summary', 'pascal://scene/current/summary', {
184
+ title: 'Scene summary (markdown)',
185
+ description: 'Markdown overview: sites/buildings/levels, per-level node counts, zone floor areas, scene bbox.',
186
+ mimeType: 'text/markdown',
187
+ }, async (uri) => ({
188
+ contents: [
189
+ {
190
+ uri: uri.href,
191
+ mimeType: 'text/markdown',
192
+ text: buildSceneSummaryMarkdown(bridge.exportJSON()),
193
+ },
194
+ ],
195
+ }));
196
+ }
@@ -0,0 +1,14 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { SceneBridge } from './bridge/scene-bridge';
3
+ import { type SceneOperations } from './operations';
4
+ import type { SceneStore } from './storage/types';
5
+ export type CreatePascalMcpServerOptions = {
6
+ bridge: SceneBridge;
7
+ operations?: SceneOperations;
8
+ /** Required for persistence tools. Hosted apps and CLIs inject their own store. */
9
+ store?: SceneStore;
10
+ name?: string;
11
+ version?: string;
12
+ };
13
+ export declare function createPascalMcpServer(opts: CreatePascalMcpServerOptions): McpServer;
14
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAG1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAIjD,MAAM,MAAM,4BAA4B,GAAG;IACzC,MAAM,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,mFAAmF;IACnF,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,4BAA4B,GAAG,SAAS,CAYnF"}
package/dist/server.js ADDED
@@ -0,0 +1,18 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { createSceneOperations } from './operations';
3
+ import { registerPrompts } from './prompts';
4
+ import { registerResources } from './resources';
5
+ import { registerTools } from './tools';
6
+ import { registerVisionTools } from './tools/vision';
7
+ export function createPascalMcpServer(opts) {
8
+ const server = new McpServer({
9
+ name: opts.name ?? 'pascal-mcp',
10
+ version: opts.version ?? '0.1.0',
11
+ });
12
+ const operations = opts.operations ?? createSceneOperations({ bridge: opts.bridge, store: opts.store });
13
+ registerTools(server, operations);
14
+ registerVisionTools(server, operations);
15
+ registerResources(server, operations);
16
+ registerPrompts(server, operations);
17
+ return server;
18
+ }
@@ -0,0 +1,13 @@
1
+ import type { SceneStore } from './types';
2
+ export * from './slug';
3
+ export * from './sqlite-scene-store';
4
+ export * from './types';
5
+ /**
6
+ * Factory for Pascal's local-first scene store.
7
+ *
8
+ * The store is backed by the runtime's built-in SQLite driver. By default it
9
+ * writes to `~/.pascal/data/pascal.db`; set `PASCAL_DB_PATH` for an exact file
10
+ * path or `PASCAL_DATA_DIR` for a directory containing `pascal.db`.
11
+ */
12
+ export declare function createSceneStore(env?: NodeJS.ProcessEnv): Promise<SceneStore>;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,cAAc,QAAQ,CAAA;AACtB,cAAc,sBAAsB,CAAA;AACpC,cAAc,SAAS,CAAA;AAEvB;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAGnF"}
@@ -0,0 +1,14 @@
1
+ export * from './slug';
2
+ export * from './sqlite-scene-store';
3
+ export * from './types';
4
+ /**
5
+ * Factory for Pascal's local-first scene store.
6
+ *
7
+ * The store is backed by the runtime's built-in SQLite driver. By default it
8
+ * writes to `~/.pascal/data/pascal.db`; set `PASCAL_DB_PATH` for an exact file
9
+ * path or `PASCAL_DATA_DIR` for a directory containing `pascal.db`.
10
+ */
11
+ export async function createSceneStore(env) {
12
+ const mod = await import('./sqlite-scene-store');
13
+ return new mod.SqliteSceneStore({ env });
14
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Normalizes a raw string into a slug:
3
+ * - lowercase
4
+ * - spaces → hyphen
5
+ * - strip non [a-z0-9-]
6
+ * - collapse consecutive hyphens
7
+ * - trim hyphens from ends
8
+ * - enforce ≤ 64 chars
9
+ *
10
+ * Throws if the result is empty.
11
+ */
12
+ export declare function sanitizeSlug(raw: string): string;
13
+ /**
14
+ * Checks if a string is already a valid slug (no sanitization performed).
15
+ */
16
+ export declare function isValidSlug(s: string): boolean;
17
+ /**
18
+ * Generates a fresh 12-char lowercase alphanumeric slug using crypto randomness.
19
+ */
20
+ export declare function generateSlug(): string;
21
+ //# sourceMappingURL=slug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../../src/storage/slug.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAehD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAI9C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAYrC"}
@@ -0,0 +1,62 @@
1
+ const MAX_SLUG_LENGTH = 64;
2
+ const GENERATED_SLUG_LENGTH = 12;
3
+ const SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4
+ const ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';
5
+ /**
6
+ * Normalizes a raw string into a slug:
7
+ * - lowercase
8
+ * - spaces → hyphen
9
+ * - strip non [a-z0-9-]
10
+ * - collapse consecutive hyphens
11
+ * - trim hyphens from ends
12
+ * - enforce ≤ 64 chars
13
+ *
14
+ * Throws if the result is empty.
15
+ */
16
+ export function sanitizeSlug(raw) {
17
+ const sanitized = raw
18
+ .toLowerCase()
19
+ .replace(/\s+/g, '-')
20
+ .replace(/[^a-z0-9-]/g, '')
21
+ .replace(/-+/g, '-')
22
+ .replace(/^-+|-+$/g, '')
23
+ .slice(0, MAX_SLUG_LENGTH)
24
+ .replace(/-+$/g, '');
25
+ if (sanitized.length === 0) {
26
+ throw new Error('Slug cannot be empty after sanitization');
27
+ }
28
+ return sanitized;
29
+ }
30
+ /**
31
+ * Checks if a string is already a valid slug (no sanitization performed).
32
+ */
33
+ export function isValidSlug(s) {
34
+ if (typeof s !== 'string')
35
+ return false;
36
+ if (s.length === 0 || s.length > MAX_SLUG_LENGTH)
37
+ return false;
38
+ return SLUG_PATTERN.test(s);
39
+ }
40
+ /**
41
+ * Generates a fresh 12-char lowercase alphanumeric slug using crypto randomness.
42
+ */
43
+ export function generateSlug() {
44
+ const raw = globalThis.crypto?.randomUUID?.().replace(/-/g, '') ?? fallbackRandom();
45
+ const base = raw.toLowerCase().replace(/[^a-z0-9]/g, '');
46
+ if (base.length >= GENERATED_SLUG_LENGTH) {
47
+ return base.slice(0, GENERATED_SLUG_LENGTH);
48
+ }
49
+ // Pad with additional random chars if for any reason the base is short.
50
+ let out = base;
51
+ while (out.length < GENERATED_SLUG_LENGTH) {
52
+ out += ALPHABET[Math.floor(Math.random() * ALPHABET.length)];
53
+ }
54
+ return out.slice(0, GENERATED_SLUG_LENGTH);
55
+ }
56
+ function fallbackRandom() {
57
+ let out = '';
58
+ for (let i = 0; i < GENERATED_SLUG_LENGTH * 2; i++) {
59
+ out += ALPHABET[Math.floor(Math.random() * ALPHABET.length)];
60
+ }
61
+ return out;
62
+ }
@@ -0,0 +1,18 @@
1
+ type SqliteBinding = string | number | bigint | boolean | null | Uint8Array;
2
+ export interface SqliteRunResult {
3
+ changes: number;
4
+ lastInsertRowid: number | bigint;
5
+ }
6
+ export interface SqliteStatement {
7
+ all(...params: SqliteBinding[]): unknown[];
8
+ get(...params: SqliteBinding[]): unknown;
9
+ run(...params: SqliteBinding[]): SqliteRunResult;
10
+ }
11
+ export interface SqliteDatabase {
12
+ exec(sql: string): void;
13
+ query(sql: string): SqliteStatement;
14
+ close(): void;
15
+ }
16
+ export declare function openSqliteDatabase(filename: string): Promise<SqliteDatabase>;
17
+ export {};
18
+ //# sourceMappingURL=sqlite-driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-driver.d.ts","sourceRoot":"","sources":["../../src/storage/sqlite-driver.ts"],"names":[],"mappings":"AAAA,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,UAAU,CAAA;AAE3E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAA;IAC1C,GAAG,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAA;IACxC,GAAG,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,GAAG,eAAe,CAAA;CACjD;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAA;IACnC,KAAK,IAAI,IAAI,CAAA;CACd;AAyBD,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAelF"}
@@ -0,0 +1,32 @@
1
+ export async function openSqliteDatabase(filename) {
2
+ if ('Bun' in globalThis) {
3
+ const mod = (await import('bun:sqlite'));
4
+ return new mod.Database(filename, { create: true, readwrite: true });
5
+ }
6
+ try {
7
+ const mod = (await import('node:sqlite'));
8
+ return adaptNodeDatabase(new mod.DatabaseSync(filename));
9
+ }
10
+ catch (error) {
11
+ const reason = error instanceof Error ? error.message : String(error);
12
+ throw new Error(`SQLite requires Bun or a Node runtime with node:sqlite support. Failed to open ${filename}: ${reason}`);
13
+ }
14
+ }
15
+ function adaptNodeDatabase(db) {
16
+ return {
17
+ exec(sql) {
18
+ db.exec(sql);
19
+ },
20
+ query(sql) {
21
+ const stmt = db.prepare(sql);
22
+ return {
23
+ all: (...params) => stmt.all(...params),
24
+ get: (...params) => stmt.get(...params),
25
+ run: (...params) => stmt.run(...params),
26
+ };
27
+ },
28
+ close() {
29
+ db.close();
30
+ },
31
+ };
32
+ }
@@ -0,0 +1,51 @@
1
+ import { type ProjectCreateOptions, type ProjectStatus, type SceneEvent, type SceneEventAppendOptions, type SceneEventListOptions, type SceneListOptions, type SceneMeta, type SceneMutateOptions, type SceneSaveOptions, type SceneStore, type SceneWithGraph } from './types';
2
+ export interface SqliteSceneStoreOptions {
3
+ /** Exact SQLite database file path. If omitted, resolved from env. */
4
+ databasePath?: string;
5
+ /** Optional env override for default path and size-limit resolution. */
6
+ env?: NodeJS.ProcessEnv;
7
+ /** Maximum UTF-8 byte length of graph JSON. Defaults to 10 MB. */
8
+ maxSceneBytes?: number;
9
+ }
10
+ /**
11
+ * Resolves Pascal's local SQLite database path.
12
+ *
13
+ * Precedence:
14
+ * 1. `PASCAL_DB_PATH`
15
+ * 2. `PASCAL_DATA_DIR/pascal.db`
16
+ * 3. On Windows: `%APPDATA%/Pascal/data/pascal.db`
17
+ * 4. `$XDG_DATA_HOME/pascal/data/pascal.db`
18
+ * 5. `$HOME/.pascal/data/pascal.db`
19
+ */
20
+ export declare function resolveDefaultDatabasePath(env?: NodeJS.ProcessEnv): string;
21
+ /**
22
+ * SQLite-backed implementation of `SceneStore`.
23
+ *
24
+ * Uses one local database file, WAL mode, and transaction-scoped version checks
25
+ * so a local editor and MCP process can safely share scenes on one machine.
26
+ */
27
+ export declare class SqliteSceneStore implements SceneStore {
28
+ readonly backend: "sqlite";
29
+ readonly databasePath: string;
30
+ private readonly maxSceneBytes;
31
+ private readonly projectPlaceholders;
32
+ private db;
33
+ private dbPromise;
34
+ constructor(opts?: SqliteSceneStoreOptions);
35
+ createProject(opts: ProjectCreateOptions): Promise<ProjectStatus>;
36
+ getProjectStatus(id: string): Promise<ProjectStatus | null>;
37
+ save(opts: SceneSaveOptions): Promise<SceneMeta>;
38
+ load(id: string): Promise<SceneWithGraph | null>;
39
+ list(opts?: SceneListOptions): Promise<SceneMeta[]>;
40
+ delete(id: string, opts?: SceneMutateOptions): Promise<boolean>;
41
+ rename(id: string, newName: string, opts?: SceneMutateOptions): Promise<SceneMeta>;
42
+ appendSceneEvent(opts: SceneEventAppendOptions): Promise<SceneEvent>;
43
+ listSceneEvents(sceneId: string, opts?: SceneEventListOptions): Promise<SceneEvent[]>;
44
+ close(): void;
45
+ private database;
46
+ private migrate;
47
+ private withWriteTransaction;
48
+ private getRow;
49
+ private generateUniqueId;
50
+ }
51
+ //# sourceMappingURL=sqlite-scene-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-scene-store.d.ts","sourceRoot":"","sources":["../../src/storage/sqlite-scene-store.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAE1B,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAGf,KAAK,cAAc,EACpB,MAAM,SAAS,CAAA;AAOhB,MAAM,WAAW,uBAAuB;IACtC,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAwCD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAmBvF;AAiKD;;;;;GAKG;AACH,qBAAa,gBAAiB,YAAW,UAAU;IACjD,QAAQ,CAAC,OAAO,EAAG,QAAQ,CAAS;IAEpC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAE7B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAwC;IAC5E,OAAO,CAAC,EAAE,CAA8B;IACxC,OAAO,CAAC,SAAS,CAAuC;gBAE5C,IAAI,GAAE,uBAA4B;IAMxC,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IAuBjE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAS3D,IAAI,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC;IAyHhD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAUhD,IAAI,CAAC,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAiCvD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;IAenE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;IAsCtF,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC;IA6BpE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAmB/F,KAAK,IAAI,IAAI;YAMC,QAAQ;IAiBtB,OAAO,CAAC,OAAO;YAgDD,oBAAoB;IAiBlC,OAAO,CAAC,MAAM;IAad,OAAO,CAAC,gBAAgB;CAOzB"}