freeform-modeling-mcp 1.0.26

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 (81) hide show
  1. package/README.md +61 -0
  2. package/index.cjs +21 -0
  3. package/package.json +48 -0
  4. package/src/cli.ts +47 -0
  5. package/src/commands/start.ts +88 -0
  6. package/src/commands/status.ts +81 -0
  7. package/src/dev.ts +24 -0
  8. package/src/lib/fileproxy/index.ts +505 -0
  9. package/src/lib/fileproxy/proxy-server.js +347 -0
  10. package/src/lib/gltf2obj/compress.js +33 -0
  11. package/src/lib/gltf2obj/engine/core/core.js +34 -0
  12. package/src/lib/gltf2obj/engine/core/eventnotifier.js +39 -0
  13. package/src/lib/gltf2obj/engine/core/taskrunner.js +88 -0
  14. package/src/lib/gltf2obj/engine/export/exporter.js +38 -0
  15. package/src/lib/gltf2obj/engine/export/exporterbase.js +86 -0
  16. package/src/lib/gltf2obj/engine/export/exportermodel.js +117 -0
  17. package/src/lib/gltf2obj/engine/export/exporterobj.js +147 -0
  18. package/src/lib/gltf2obj/engine/geometry/box3d.js +60 -0
  19. package/src/lib/gltf2obj/engine/geometry/coord2d.js +35 -0
  20. package/src/lib/gltf2obj/engine/geometry/coord3d.js +126 -0
  21. package/src/lib/gltf2obj/engine/geometry/coord4d.js +15 -0
  22. package/src/lib/gltf2obj/engine/geometry/geometry.js +56 -0
  23. package/src/lib/gltf2obj/engine/geometry/matrix.js +440 -0
  24. package/src/lib/gltf2obj/engine/geometry/octree.js +160 -0
  25. package/src/lib/gltf2obj/engine/geometry/quaternion.js +83 -0
  26. package/src/lib/gltf2obj/engine/geometry/transformation.js +63 -0
  27. package/src/lib/gltf2obj/engine/geometry/tween.js +31 -0
  28. package/src/lib/gltf2obj/engine/import/importer.js +270 -0
  29. package/src/lib/gltf2obj/engine/import/importerbase.js +115 -0
  30. package/src/lib/gltf2obj/engine/import/importerfiles.js +139 -0
  31. package/src/lib/gltf2obj/engine/import/importergltf.js +1045 -0
  32. package/src/lib/gltf2obj/engine/import/importerutils.js +102 -0
  33. package/src/lib/gltf2obj/engine/io/binaryreader.js +93 -0
  34. package/src/lib/gltf2obj/engine/io/binarywriter.js +92 -0
  35. package/src/lib/gltf2obj/engine/io/bufferutils.js +85 -0
  36. package/src/lib/gltf2obj/engine/io/externallibs.js +42 -0
  37. package/src/lib/gltf2obj/engine/io/fileutils.js +120 -0
  38. package/src/lib/gltf2obj/engine/io/textwriter.js +41 -0
  39. package/src/lib/gltf2obj/engine/main.js +19 -0
  40. package/src/lib/gltf2obj/engine/model/color.js +130 -0
  41. package/src/lib/gltf2obj/engine/model/generator.js +433 -0
  42. package/src/lib/gltf2obj/engine/model/material.js +243 -0
  43. package/src/lib/gltf2obj/engine/model/mesh.js +173 -0
  44. package/src/lib/gltf2obj/engine/model/meshbuffer.js +233 -0
  45. package/src/lib/gltf2obj/engine/model/meshinstance.js +128 -0
  46. package/src/lib/gltf2obj/engine/model/meshutils.js +64 -0
  47. package/src/lib/gltf2obj/engine/model/model.js +195 -0
  48. package/src/lib/gltf2obj/engine/model/modelfinalization.js +377 -0
  49. package/src/lib/gltf2obj/engine/model/modelutils.js +117 -0
  50. package/src/lib/gltf2obj/engine/model/node.js +178 -0
  51. package/src/lib/gltf2obj/engine/model/object.js +90 -0
  52. package/src/lib/gltf2obj/engine/model/property.js +86 -0
  53. package/src/lib/gltf2obj/engine/model/quantities.js +46 -0
  54. package/src/lib/gltf2obj/engine/model/topology.js +139 -0
  55. package/src/lib/gltf2obj/engine/model/triangle.js +99 -0
  56. package/src/lib/gltf2obj/engine/threejs/threemodelloader.js +85 -0
  57. package/src/lib/gltf2obj/handler.js +63 -0
  58. package/src/lib/gltf2obj/index.js +47 -0
  59. package/src/lib/logger.ts +304 -0
  60. package/src/lib/print.ts +37 -0
  61. package/src/lib/utils.ts +34 -0
  62. package/src/lib/wsbridge/bridge.ts +639 -0
  63. package/src/lib/wsbridge/lock.ts +258 -0
  64. package/src/llmClient.ts +245 -0
  65. package/src/prompt.ts +187 -0
  66. package/src/server.ts +125 -0
  67. package/src/tools/assets-tool.ts +314 -0
  68. package/src/tools/basic-tools.ts +531 -0
  69. package/src/tools/index.ts +2 -0
  70. package/src/tools/material-tools.ts +88 -0
  71. package/src/tools/modeling/auxiliary-curve-tool.ts +44 -0
  72. package/src/tools/modeling/find-face.ts +38 -0
  73. package/src/tools/modeling/sweep-tool.ts +60 -0
  74. package/src/tools/modeling/utils.ts +181 -0
  75. package/src/tools/modeling-tools.ts +135 -0
  76. package/src/tools/screenshot-tools.ts +107 -0
  77. package/src/tools/tools-info.ts +107 -0
  78. package/src/tools/tripo3d-tools.ts +496 -0
  79. package/src/types/index.ts +25 -0
  80. package/src/types/tripo3d.ts +38 -0
  81. package/tsconfig.json +22 -0
package/src/server.ts ADDED
@@ -0,0 +1,125 @@
1
+ // src/server.ts
2
+ import { FastMCP } from 'fastmcp';
3
+ import { SessionAuth } from './types';
4
+ import {
5
+ getSceneInfoTool,
6
+ getObjectsInfoTool,
7
+ createObjectTool,
8
+ modifyObjectTool,
9
+ deleteObjectsTool,
10
+ copyObjectTool,
11
+ getSelectionTool,
12
+ } from './tools/basic-tools';
13
+ import {
14
+ createTripo3DJobTool,
15
+ pollTripo3DJobStatusTool,
16
+ importGeneratedAssetTool,
17
+ pollTripo3DImportStatusTool,
18
+ getTripo3DStatusTool
19
+ } from './tools/tripo3d-tools'
20
+ import logger from './lib/logger';
21
+ import { addFacesTool, getFacePathTool, pullFacesTool } from './tools/modeling-tools';
22
+ import { getToolsInfoTool } from './tools/tools-info';
23
+ import { getMaterialNames, setFaceMaterialTool, setGroupMaterialTool } from './tools/material-tools';
24
+ import { MainStrategy } from './prompt';
25
+ import { batchGetKjlAssetSnapshotTool, batchSearchKjlAssetsTool, getKjlAssetSnapshotTool, placeKjlAssetTool, replaceKjlAssetTool, searchKjlAssetsTool } from './tools/assets-tool';
26
+ import { getObjectsScreenshotTool, getSceneScreenshotTool } from './tools/screenshot-tools';
27
+ import { createAuxiliaryCurve } from './tools/modeling/auxiliary-curve-tool';
28
+ import { sweepTool } from './tools/modeling/sweep-tool';
29
+ import { findFaceByMultiplePointsTool } from './tools/modeling/find-face';
30
+ // ... 其他工具导入
31
+
32
+ // 创建 FastMCP 服务器
33
+ export function createServer(): FastMCP<SessionAuth> {
34
+
35
+ // 服务器选项
36
+ const server = new FastMCP({
37
+ name: "my-mcp-server",
38
+ version: "1.0.0", // 格式必须是 x.y.z
39
+ });
40
+
41
+ // 注册所有工具
42
+ server.addTool(getToolsInfoTool);
43
+ server.addTool(getObjectsScreenshotTool);
44
+ server.addTool(getSceneScreenshotTool);
45
+
46
+ //#region basic
47
+ server.addTool(getSceneInfoTool);
48
+ server.addTool(getObjectsInfoTool);
49
+ server.addTool(createObjectTool);
50
+ server.addTool(modifyObjectTool);
51
+ server.addTool(deleteObjectsTool);
52
+ server.addTool(copyObjectTool);
53
+ server.addTool(getSelectionTool);
54
+ //#region
55
+
56
+ //#region material
57
+ server.addTool(getMaterialNames);
58
+ server.addTool(setFaceMaterialTool);
59
+ server.addTool(setGroupMaterialTool);
60
+ //#endregion
61
+
62
+ //#region assets
63
+ // server.addTool(searchKjlAssetsTool);
64
+ server.addTool(batchSearchKjlAssetsTool);
65
+ server.addTool(batchGetKjlAssetSnapshotTool);
66
+ // server.addTool(getKjlAssetSnapshotTool);
67
+ server.addTool(placeKjlAssetTool);
68
+ server.addTool(replaceKjlAssetTool);
69
+ //#endregion
70
+
71
+ //#region modeling
72
+ server.addTool(addFacesTool);
73
+ server.addTool(getFacePathTool);
74
+ server.addTool(findFaceByMultiplePointsTool);
75
+ server.addTool(pullFacesTool);
76
+ server.addTool(sweepTool);
77
+ server.addTool(createAuxiliaryCurve)
78
+ //#endregion
79
+
80
+ //#region tripo3d
81
+ server.addTool(getTripo3DStatusTool);
82
+ server.addTool(createTripo3DJobTool);
83
+ server.addTool(pollTripo3DJobStatusTool);
84
+ server.addTool(importGeneratedAssetTool);
85
+ server.addTool(pollTripo3DImportStatusTool);
86
+ //#endregion
87
+
88
+ server.addPrompt({
89
+ name: 'KooMaster(酷大师) 3D Scene Builder',
90
+ description: 'A professional 3D scene creation and modeling tool that helps users build detailed interior designs, architectural models, and 3D scenes. Perfect for designing rooms, creating furniture layouts, or building custom 3D environments. Uses the KuJiaLe(酷家乐) ecosystem for high-quality assets and precise spatial planning. Call get_tools_info() first to understand available functions and coordinate system details.',
91
+ arguments: [],
92
+ load: async () => {
93
+ return MainStrategy;
94
+ }
95
+ });
96
+
97
+ server.on('connect', (event) => {
98
+ logger.info('Client connected');
99
+ });
100
+
101
+ server.on('disconnect', (event) => {
102
+ logger.info('Client disconnected');
103
+
104
+ // 检查 WebSocket 服务器状态
105
+ // if (!connectionManager.isRunning()) {
106
+ // logger.info('WebSocket server not running, initializing...');
107
+ // connectionManager.initialize().catch(err => {
108
+ // logger.error('Failed to initialize WebSocket server:', err);
109
+ // });
110
+ // }
111
+ });
112
+
113
+
114
+
115
+
116
+
117
+ return server;
118
+ }
119
+
120
+ // 用于处理服务器关闭的函数
121
+ export async function shutdownServer(): Promise<void> {
122
+ // 断开与 Koomaster 的连接
123
+ // connectionManager.shutdown();
124
+ logger.info('Disconnected from Koomaster');
125
+ }
@@ -0,0 +1,314 @@
1
+ import { Context } from '../types';
2
+ import { z } from 'zod';
3
+ import logger from '../lib/logger';
4
+ import { llmClient } from '../llmClient';
5
+ import { imageContent, ContentResult, UserError, ImageContent } from 'fastmcp';
6
+
7
+ export const searchKjlAssetsTool = {
8
+ name: 'search_kjl_assets',
9
+ description: 'Searches for 3D models and assets in the KJL(酷家乐) database based on a text description. Returns a list of models that match the search criteria, including their names, IDs, categories, and dimensions.',
10
+ parameters: z.object({
11
+ description: z.string().describe('用于搜索的中文文本描述。必须使用中文关键词进行搜索以获得最佳结果。可以是物体类型(如"椅子"、"桌子"、"灯")、风格(如"现代"、"传统")或其他描述性术语。越具体的中文查询词(如"现代皮革办公椅"而非简单的"椅子")会产生更精确的结果。')
12
+ }),
13
+ execute: async (args, context: Context): Promise<ContentResult> => {
14
+ try {
15
+ if (!llmClient.hasConnectedKoomasterClients()) {
16
+ logger.warn(`No clients connected when attempting to search for assets`);
17
+ throw new UserError("No modeling software clients connected. Please start the software and connect to this server.");
18
+ }
19
+ // context.reportProgress({
20
+ // progress: 10,
21
+ // total: 100,
22
+ // });
23
+ const result = await llmClient.sendCommand("search_kjl_assets", args);
24
+
25
+ // context.reportProgress({
26
+ // progress: 100,
27
+ // total: 100,
28
+ // });
29
+ return result;
30
+ } catch (e) {
31
+ const errorMessage = `Error searching for KJL assets: ${e instanceof Error ? e.message : String(e)}`;
32
+ logger.error(errorMessage);
33
+ context.log.error(errorMessage);
34
+ return {
35
+ content: [
36
+ {
37
+ text: errorMessage,
38
+ type: "text"
39
+ }
40
+ ],
41
+ isError: true
42
+ };
43
+ }
44
+ }
45
+ };
46
+
47
+ export const batchSearchKjlAssetsTool = {
48
+ name: 'batch_search_kjl_assets',
49
+ description: 'Performs a concurrent batch search for multiple 3D models and assets in the KJL(酷家乐) database. Returns a combined list of models that match each search term, including their names, IDs, categories, and dimensions. Use this when you need to search for multiple different types of models at once.',
50
+ parameters: z.object({
51
+ descriptions: z.array(z.string()).describe('An array of Chinese text descriptions to search for. Each description must use Chinese keywords for best results. Can include object types (like "椅子", "桌子", "灯"), styles (like "现代", "传统"), or other descriptive terms. More specific Chinese queries (like "现代皮革办公椅" instead of just "椅子") will yield more precise results.')
52
+ }),
53
+ execute: async (args, context: Context): Promise<any> => {
54
+ try {
55
+ if (!llmClient.hasConnectedKoomasterClients()) {
56
+ logger.warn(`No clients connected when attempting to batch search for assets`);
57
+ throw new UserError("No modeling software clients connected. Please start the software and connect to this server.");
58
+ }
59
+ // Create an array of promises for concurrent execution
60
+ const searchPromises = args.descriptions.map(description =>
61
+ llmClient.sendCommand("search_kjl_assets", { description })
62
+ .then(result => ({
63
+ searchTerm: description,
64
+ results: result
65
+ }))
66
+ );
67
+
68
+ // Execute all searches concurrently
69
+ const results = await Promise.all(searchPromises);
70
+
71
+ // Combine the results into a single response
72
+ const combinedResults = results.map(item =>
73
+ `=== Search Results for "${item.searchTerm}" ===\n${item.results}\n`
74
+ ).join('\n');
75
+
76
+ const note = `Note about dimensions: The dimensions (x, y, z) show the model's default orientation. For example, a cabinet with larger x than y typically has its front facing the y direction and is wider along x. Consider these proportions when rotating models for proper placement. Use place_kjl_asset with the model ID to add items to your scene.`;
77
+
78
+ // context.reportProgress({
79
+ // progress: 100,
80
+ // total: 100,
81
+ // });
82
+
83
+ return `${combinedResults}\n\n${note}`;
84
+ } catch (e) {
85
+ const errorMessage = `Error performing batch search for KJL assets: ${e instanceof Error ? e.message : String(e)}`;
86
+ logger.error(errorMessage);
87
+ context.log.error(errorMessage);
88
+ return {
89
+ content: [
90
+ {
91
+ text: errorMessage,
92
+ type: "text"
93
+ }
94
+ ],
95
+ isError: true
96
+ };
97
+ }
98
+ }
99
+ };
100
+
101
+ export const getKjlAssetSnapshotTool = {
102
+ name: 'get_kjl_asset_snapshot',
103
+ description: 'Retrieves a preview image for a specific KJL(酷家乐) asset model based on its model ID. This helps visualize the asset before placing it in the scene. Use this tool to examine the appearance and style of a model to ensure it matches your design requirements.',
104
+ parameters: z.object({
105
+ modelId: z.number().describe('The unique ID of the KJL asset model for which to retrieve the preview image. This ID can be obtained from the batch_search_kjl_assets function results.')
106
+ }),
107
+ execute: async (args, context: Context): Promise<ImageContent | ContentResult> => {
108
+ try {
109
+ if (!llmClient.hasConnectedKoomasterClients()) {
110
+ logger.warn(`No clients connected when attempting to get KJL asset snapshot`);
111
+ throw new UserError("No modeling software clients connected. Please start the software and connect to this server.");
112
+ }
113
+ const result = await llmClient.sendCommand("get_kjl_asset_snapshot", args);
114
+
115
+ // If the result is a URL, return a formatted message with the URL
116
+ // if (typeof result === 'string' && (result.startsWith('http://') || result.startsWith('https://'))) {
117
+ // return `Preview image for model ID ${args.modelId} is available at: ${result}`;
118
+ // }
119
+
120
+ return await imageContent({
121
+ url: result
122
+ });
123
+ } catch (e) {
124
+ const errorMessage = `Error retrieving KJL asset snapshot: ${e instanceof Error ? e.message : String(e)}`;
125
+ logger.error(errorMessage);
126
+ context.log.error(errorMessage);
127
+ return {
128
+ content: [
129
+ {
130
+ text: errorMessage,
131
+ type: "text"
132
+ }
133
+ ],
134
+ isError: true
135
+ };
136
+ }
137
+ }
138
+ };
139
+
140
+ export const batchGetKjlAssetSnapshotTool = {
141
+ name: 'batch_get_kjl_asset_snapshot',
142
+ description: 'Retrieves preview images for multiple KJL(酷家乐) asset models concurrently based on their model IDs. This helps visualize multiple assets before placing them in the scene. Use this tool to efficiently examine the appearance and style of several models at once to ensure they match your design requirements.',
143
+ parameters: z.object({
144
+ modelIds: z.array(z.number()).describe('An array of unique IDs of the KJL asset models for which to retrieve preview images. These IDs can be obtained from the batch_search_kjl_assets function results.')
145
+ }),
146
+ execute: async (args, context: Context): Promise<ContentResult> => {
147
+ try {
148
+ if (!llmClient.hasConnectedKoomasterClients()) {
149
+ logger.warn(`No clients connected when attempting to batch get KJL asset snapshots`);
150
+ throw new UserError("No modeling software clients connected. Please start the software and connect to this server.");
151
+ }
152
+
153
+ // Create an array of promises for concurrent execution
154
+ const snapshotPromises = args.modelIds.map(modelId =>
155
+ llmClient.sendCommand("get_kjl_asset_snapshot", { modelId })
156
+ .then(async (result) => {
157
+ try {
158
+ // Create image content for each snapshot
159
+ const content = await imageContent({
160
+ url: result
161
+ });
162
+
163
+ return {
164
+ modelId,
165
+ imageContent: content,
166
+ success: true
167
+ };
168
+ } catch (error) {
169
+ return {
170
+ modelId,
171
+ error: `Failed to get image for model ID ${modelId}: ${error.message}`,
172
+ success: false
173
+ };
174
+ }
175
+ })
176
+ );
177
+
178
+ // Execute all snapshot retrievals concurrently
179
+ const results = await Promise.all(snapshotPromises);
180
+
181
+ // Convert results into content array
182
+ const contents = [];
183
+
184
+ // First add a header text
185
+ contents.push({
186
+ text: `Preview images for ${args.modelIds.length} model(s):`,
187
+ type: "text"
188
+ });
189
+
190
+ // Add all successful images and error messages
191
+ for (const result of results) {
192
+ if (result.success) {
193
+ // Add a label for the image
194
+ contents.push({
195
+ text: `Model ID: ${result.modelId}`,
196
+ type: "text"
197
+ });
198
+
199
+ // Add the image content
200
+ contents.push(result.imageContent);
201
+ } else {
202
+ // Add error message
203
+ contents.push({
204
+ text: result.error,
205
+ type: "text"
206
+ });
207
+ }
208
+ }
209
+
210
+ return {
211
+ content: contents
212
+ };
213
+ } catch (e) {
214
+ const errorMessage = `Error batch retrieving KJL asset snapshots: ${e instanceof Error ? e.message : String(e)}`;
215
+ logger.error(errorMessage);
216
+ context.log.error(errorMessage);
217
+ return {
218
+ content: [
219
+ {
220
+ text: errorMessage,
221
+ type: "text"
222
+ }
223
+ ],
224
+ isError: true
225
+ };
226
+ }
227
+ }
228
+ };
229
+
230
+ export const placeKjlAssetTool = {
231
+ name: 'place_kjl_asset',
232
+ description: 'Places a 3D model from the KJL(酷家乐) asset library into the current scene with specified position, rotation, and scale. Before placing, analyze the model\'s bounding box dimensions to determine proper orientation - elongated objects like wall panels may require 90° rotation based on intended function. Ensure bottoms align with the ground plane (z=0) by setting z-value to half the model\'s height. After placement, verify no clipping with existing objects and confirm dimensions match real-world proportions.',
233
+ parameters: z.object({
234
+ modelId: z.number().describe('The unique ID of the model to place. This ID can be obtained from the batch_search_kjl_assets function results. Use the exact ID value returned by the search.'),
235
+ name: z.string().describe('Specify a name for the placed model. This name will be used for subsequent identification and reference to the model, such as when applying materials or performing transformation operations. Use descriptive names for easy identification, such as "dining_table", "office_chair", etc.'),
236
+ originalDimensions: z.array(z.number()).describe('The original [width, height, depth] dimensions of the model as provided in the batch_search_kjl_assets results. These values must be obtained from the asset search results.'),
237
+
238
+ // 添加变换参数
239
+ location: z.array(z.number()).describe('[x, y, z] center position coordinates for the imported model'),
240
+ targetDimensions: z.array(z.number()).describe('The target [width, height, depth] dimensions for the model after importing. These values specify the absolute size the model should be scaled to in the scene.'),
241
+ rotation: z.array(z.number()).optional().describe('Optional [x, y, z] rotation in degrees. Following the Z-up coordinate system: rotation around X-axis tilts the object left/right (like nodding sideways), rotation around Y-axis tilts the object forward/backward (like nodding), and rotation around Z-axis rotates the object in the horizontal plane (like spinning on the ground). Positive values follow the right-hand rule: point thumb in positive axis direction, fingers show positive rotation direction.'),
242
+ }),
243
+ execute: async (args, context: Context): Promise<ContentResult> => {
244
+ try {
245
+ if (!llmClient.hasConnectedKoomasterClients()) {
246
+ logger.warn(`No clients connected when attempting to place KJL asset`);
247
+ throw new UserError("No modeling software clients connected. Please start the software and connect to this server.");
248
+ }
249
+
250
+ // context.reportProgress({
251
+ // progress: 30,
252
+ // total: 100,
253
+ // });
254
+
255
+ const result = await llmClient.sendCommand("place_kjl_asset", args);
256
+
257
+ // context.reportProgress({
258
+ // progress: 100,
259
+ // total: 100,
260
+ // });
261
+ return result;
262
+ } catch (e) {
263
+ const errorMessage = `Error placing KJL asset: ${e instanceof Error ? e.message : String(e)}`;
264
+ logger.error(errorMessage);
265
+ context.log.error(errorMessage);
266
+ return {
267
+ content: [
268
+ {
269
+ text: errorMessage,
270
+ type: "text"
271
+ }
272
+ ],
273
+ isError: true
274
+ };
275
+ }
276
+ }
277
+ };
278
+
279
+ export const replaceKjlAssetTool = {
280
+ name: 'replace_kjl_asset',
281
+ description: 'Replaces an existing object in the scene with a new 3D model from the KJL(酷家乐) asset library. The replacement maintains the position, rotation, aligning the centers of both models. After replacement, review the scene to ensure no intersections or clipping with other objects.',
282
+ parameters: z.object({
283
+ targetName: z.string().describe('The name of the existing object in the scene that will be replaced. This must exactly match the name of a placed object.'),
284
+ newModelId: z.number().describe('The unique ID of the new model that will replace the existing object. This ID should be obtained from the batch_search_kjl_assets function results.'),
285
+ newName: z.string().describe('Specify a name for the replacement model. This name will be used for subsequent identification and reference, such as when applying materials or performing transformation operations. Use descriptive names for easy identification.'),
286
+ originalDimensions: z.array(z.number()).describe('The original [width, height, depth] dimensions of the new model as provided in the batch_search_kjl_assets results. These values must be obtained from the asset search results.'),
287
+ targetDimensions: z.array(z.number()).optional().describe('The target [width, height, depth] dimensions for the model after importing. These values specify the absolute size the model should be scaled to in the scene.'),
288
+ }),
289
+ execute: async (args, context: Context): Promise<ContentResult> => {
290
+ try {
291
+ if (!llmClient.hasConnectedKoomasterClients()) {
292
+ logger.warn(`No clients connected when attempting to replace KJL asset`);
293
+ throw new UserError("No modeling software clients connected. Please start the software and connect to this server.");
294
+ }
295
+
296
+ const result = await llmClient.sendCommand("replace_kjl_asset", args);
297
+
298
+ return result;
299
+ } catch (e) {
300
+ const errorMessage = `Error replacing KJL asset: ${e instanceof Error ? e.message : String(e)}`;
301
+ logger.error(errorMessage);
302
+ context.log.error(errorMessage);
303
+ return {
304
+ content: [
305
+ {
306
+ text: errorMessage,
307
+ type: "text"
308
+ }
309
+ ],
310
+ isError: true
311
+ };
312
+ }
313
+ }
314
+ };