keyshot-mcp 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -1
- package/README.md +28 -10
- package/dist/index.js +2 -3
- package/dist/server.js +142 -24
- package/dist/version.js +1 -1
- package/docs/MODEL-EVALUATION.md +92 -0
- package/docs/UPGRADING.md +67 -0
- package/lhm.plugin.json +1 -1
- package/package.json +8 -5
- package/scripts/keyshot_bridge.py +52 -13
- package/server.json +2 -2
- package/skills/keyshot-mcp/SKILL.md +10 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,53 @@ All notable changes to KeyShot MCP are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.13.0] - 2026-09-30
|
|
8
|
+
|
|
9
|
+
### Added / 新增
|
|
10
|
+
|
|
11
|
+
- Support MCP protocol `2026-07-28` over stdio with the official TypeScript SDK v2.
|
|
12
|
+
Existing SDK v1 clients can continue to connect using `2025-11-25`.
|
|
13
|
+
使用官方 TypeScript SDK v2,通过 stdio 支持 MCP `2026-07-28`;旧 SDK v1
|
|
14
|
+
客户端仍可使用 `2025-11-25` 连接。
|
|
15
|
+
- Add child-process protocol tests for both client generations, covering all
|
|
16
|
+
19 tools, preset results, prompts, resources and invalid tool arguments.
|
|
17
|
+
新增真实子进程协议测试,覆盖新旧客户端、19 个工具、预设结果、提示词、资源和无效输入。
|
|
18
|
+
|
|
19
|
+
### Changed / 变更
|
|
20
|
+
|
|
21
|
+
- Upgrade schema validation to Zod 4 and generate parameter documentation through
|
|
22
|
+
its public JSON Schema API. Tool names, input parameters and output safety rules
|
|
23
|
+
remain compatible. Test runs build the stdio entry before exercising it.
|
|
24
|
+
升级至 Zod 4,使用公开 JSON Schema 接口生成参数文档;工具名称、输入参数和输出安全
|
|
25
|
+
规则保持兼容。测试会先构建,再检查实际 stdio 入口。
|
|
26
|
+
- Add bilingual upgrade guidance and release notes. MCP Events subscriptions are
|
|
27
|
+
not implemented in this release; protocol support alone does not enable them.
|
|
28
|
+
增加双语升级说明和发布说明。本版未实现 MCP Events 订阅,协议升级不会自动开启事件推送。
|
|
29
|
+
|
|
30
|
+
### Fixed / 修复
|
|
31
|
+
|
|
32
|
+
- Resolve native KeyShot object IDs through the scene tree so scene inspection
|
|
33
|
+
returns actual object names and assigned materials instead of numeric IDs.
|
|
34
|
+
修复场景检查把对象 ID 当成名称、无法读取已分配材质的问题。
|
|
35
|
+
- Apply materials using KeyShot's material-name and object-ID parameters. Reject
|
|
36
|
+
ambiguous object names; use the `objectPath` returned by scene inspection to
|
|
37
|
+
select a specific object. Inspect again after renaming or reorganizing objects.
|
|
38
|
+
修正材质赋值参数;同名对象需使用场景检查返回的 `objectPath` 精确选择。
|
|
39
|
+
重命名或调整对象层级后,应重新检查场景以获取路径。
|
|
40
|
+
|
|
41
|
+
## [0.12.1] - 2026-09-02
|
|
42
|
+
|
|
43
|
+
### Security
|
|
44
|
+
|
|
45
|
+
- Updated the MCP SDK dependency baseline to pull patched Hono, Hono Node
|
|
46
|
+
server, `fast-uri`, and `ip-address` runtime dependencies.
|
|
47
|
+
- Refreshed the development lockfile to use patched PostCSS and Nano ID releases.
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- Updated the Node.js and Python setup actions used by CI and release workflows.
|
|
52
|
+
- CI and release validation now reject high-severity dependency vulnerabilities.
|
|
53
|
+
|
|
7
54
|
## [0.12.0] - 2026-09-01
|
|
8
55
|
|
|
9
56
|
### Added
|
|
@@ -55,7 +102,8 @@ All notable changes to KeyShot MCP are documented in this file.
|
|
|
55
102
|
|
|
56
103
|
- Documented that scene metadata and preview images may be sent by an MCP client to its configured model provider.
|
|
57
104
|
|
|
58
|
-
[Unreleased]: https://github.com/truman-t3/keyshot-mcp/compare/v0.12.
|
|
105
|
+
[Unreleased]: https://github.com/truman-t3/keyshot-mcp/compare/v0.12.1...HEAD
|
|
106
|
+
[0.12.1]: https://github.com/truman-t3/keyshot-mcp/compare/v0.12.0...v0.12.1
|
|
59
107
|
[0.12.0]: https://github.com/truman-t3/keyshot-mcp/compare/v0.11.0...v0.12.0
|
|
60
108
|
[0.11.0]: https://github.com/truman-t3/keyshot-mcp/compare/v0.10.0...v0.11.0
|
|
61
109
|
[0.10.0]: https://github.com/truman-t3/keyshot-mcp/compare/v0.9.1...v0.10.0
|
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
<img src="https://img.shields.io/badge/KeyShot-14.1_tested-00AEEF" alt="KeyShot 14.1 tested">
|
|
23
23
|
<img src="https://img.shields.io/badge/Node.js-20%2B-339933" alt="Node.js 20 or newer">
|
|
24
24
|
<img src="https://img.shields.io/badge/TypeScript-5.9-3178C6" alt="TypeScript 5.9">
|
|
25
|
+
<a href="https://m8ven.ai/mcp/truman-t3-keyshot-mcp-v7af97"><img src="https://m8ven.ai/badge/mcp/truman-t3-keyshot-mcp-v7af97?variant=verified" alt="M8ven Verified"></a>
|
|
25
26
|
</p>
|
|
26
27
|
|
|
27
28
|
<p align="center">
|
|
@@ -71,9 +72,9 @@ The easiest setup is to send this prompt to an agent that can edit your MCP
|
|
|
71
72
|
configuration:
|
|
72
73
|
|
|
73
74
|
```text
|
|
74
|
-
Install KeyShot MCP 0.
|
|
75
|
+
Install KeyShot MCP 0.13.0 and configure it in my MCP client.
|
|
75
76
|
|
|
76
|
-
1. Use: npx -y keyshot-mcp@0.
|
|
77
|
+
1. Use: npx -y keyshot-mcp@0.13.0
|
|
77
78
|
2. Find my local keyshot_headless.exe and set KEYSHOT_HEADLESS_EXE to its full path.
|
|
78
79
|
3. Keep outputs in the default KeyShot MCP Outputs folder unless I choose another safe folder.
|
|
79
80
|
4. Keep KEYSHOT_ALLOW_EXTERNAL_OUTPUTS disabled.
|
|
@@ -133,7 +134,17 @@ folder, show me a preview, and do not overwrite the original scene.
|
|
|
133
134
|
|
|
134
135
|
### Install
|
|
135
136
|
|
|
136
|
-
The current release is `0.
|
|
137
|
+
The current release is `0.13.0`.
|
|
138
|
+
|
|
139
|
+
Version 0.13.0 supports MCP protocol `2026-07-28` over stdio using the official
|
|
140
|
+
TypeScript SDK v2. Existing clients using the `2025-11-25` handshake can still
|
|
141
|
+
connect with the same configuration. All 19 tool names and input parameters are
|
|
142
|
+
preserved. See [upgrade notes](docs/UPGRADING.md) for compatibility and verification.
|
|
143
|
+
|
|
144
|
+
ChatGPT MCP Events subscriptions are not included in this release. They require
|
|
145
|
+
event methods, persistent subscription storage, authenticated connectivity and
|
|
146
|
+
signed HTTPS callbacks in addition to protocol support. This server continues to
|
|
147
|
+
run locally and process saved KeyShot scenes.
|
|
137
148
|
|
|
138
149
|
#### Run with npx
|
|
139
150
|
|
|
@@ -144,7 +155,7 @@ No global npm installation is required:
|
|
|
144
155
|
"mcpServers": {
|
|
145
156
|
"keyshot": {
|
|
146
157
|
"command": "npx",
|
|
147
|
-
"args": ["-y", "keyshot-mcp@0.
|
|
158
|
+
"args": ["-y", "keyshot-mcp@0.13.0"],
|
|
148
159
|
"env": {
|
|
149
160
|
"KEYSHOT_HEADLESS_EXE": "C:/Program Files/KeyShot Studio/bin/keyshot_headless.exe"
|
|
150
161
|
}
|
|
@@ -156,7 +167,7 @@ No global npm installation is required:
|
|
|
156
167
|
#### Install globally
|
|
157
168
|
|
|
158
169
|
```bash
|
|
159
|
-
npm install -g keyshot-mcp@0.
|
|
170
|
+
npm install -g keyshot-mcp@0.13.0
|
|
160
171
|
```
|
|
161
172
|
|
|
162
173
|
```json
|
|
@@ -409,9 +420,9 @@ KeyShot MCP 在本机处理 KeyShot 文件;但 MCP 客户端可能会把工具
|
|
|
409
420
|
最简单的安装方式,是把下面这段话发给能够修改 MCP 配置的 Agent:
|
|
410
421
|
|
|
411
422
|
```text
|
|
412
|
-
请安装 KeyShot MCP 0.
|
|
423
|
+
请安装 KeyShot MCP 0.13.0,并配置到我的 MCP 客户端。
|
|
413
424
|
|
|
414
|
-
1. 使用:npx -y keyshot-mcp@0.
|
|
425
|
+
1. 使用:npx -y keyshot-mcp@0.13.0
|
|
415
426
|
2. 查找本机 keyshot_headless.exe,并把完整路径设置为 KEYSHOT_HEADLESS_EXE。
|
|
416
427
|
3. 默认把结果保存在“文档/KeyShot MCP Outputs”,除非我明确选择其他安全目录。
|
|
417
428
|
4. 保持 KEYSHOT_ALLOW_EXTERNAL_OUTPUTS 关闭。
|
|
@@ -464,7 +475,14 @@ KeyShot MCP 在本机处理 KeyShot 文件;但 MCP 客户端可能会把工具
|
|
|
464
475
|
|
|
465
476
|
### 安装
|
|
466
477
|
|
|
467
|
-
当前正式版本为 `0.
|
|
478
|
+
当前正式版本为 `0.13.0`。
|
|
479
|
+
|
|
480
|
+
0.13.0 使用官方 TypeScript SDK v2,通过 stdio 支持 MCP `2026-07-28` 协议。
|
|
481
|
+
使用 `2025-11-25` 握手的现有客户端仍可沿用原配置连接,19 个工具的名称和输入参数
|
|
482
|
+
保持兼容。兼容范围和验证方法见[升级说明](docs/UPGRADING.md)。
|
|
483
|
+
|
|
484
|
+
本版尚未提供 ChatGPT MCP Events 订阅。该能力除新协议外,还需要事件接口、持久化
|
|
485
|
+
订阅、经过认证的连接和签名 HTTPS 回调。服务继续在本机处理已保存的 KeyShot 场景。
|
|
468
486
|
|
|
469
487
|
#### 使用 npx 免安装运行
|
|
470
488
|
|
|
@@ -473,7 +491,7 @@ KeyShot MCP 在本机处理 KeyShot 文件;但 MCP 客户端可能会把工具
|
|
|
473
491
|
"mcpServers": {
|
|
474
492
|
"keyshot": {
|
|
475
493
|
"command": "npx",
|
|
476
|
-
"args": ["-y", "keyshot-mcp@0.
|
|
494
|
+
"args": ["-y", "keyshot-mcp@0.13.0"],
|
|
477
495
|
"env": {
|
|
478
496
|
"KEYSHOT_HEADLESS_EXE": "C:/Program Files/KeyShot Studio/bin/keyshot_headless.exe"
|
|
479
497
|
}
|
|
@@ -485,7 +503,7 @@ KeyShot MCP 在本机处理 KeyShot 文件;但 MCP 客户端可能会把工具
|
|
|
485
503
|
#### 全局安装
|
|
486
504
|
|
|
487
505
|
```bash
|
|
488
|
-
npm install -g keyshot-mcp@0.
|
|
506
|
+
npm install -g keyshot-mcp@0.13.0
|
|
489
507
|
```
|
|
490
508
|
|
|
491
509
|
```json
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
3
3
|
import { createKeyShotServer } from "./server.js";
|
|
4
|
-
|
|
5
|
-
await server.connect(new StdioServerTransport());
|
|
4
|
+
await serveStdio(() => createKeyShotServer());
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
+
import { z } from "zod";
|
|
2
3
|
import { getConfig } from "./config.js";
|
|
3
4
|
import { toolResponse, localFailure } from "./result.js";
|
|
4
5
|
import { runKeyShotSerialized } from "./runner.js";
|
|
@@ -11,7 +12,7 @@ import { applyRenderQuality } from "./quality.js";
|
|
|
11
12
|
import { runKeyShotDiagnostics } from "./diagnostics.js";
|
|
12
13
|
import { renderPreview } from "./preview.js";
|
|
13
14
|
import { syncSavedScene } from "./save-sync.js";
|
|
14
|
-
import { applyCameraPresetTool, applyMaterialPresetTool, applyMaterialTool, batchRenderTool, importModelTool, inspectSceneTool, listCameraPresetsTool, listCamerasTool, listMaterialPresetsTool, previewRenderTool, productRenderTool,
|
|
15
|
+
import { applyCameraPresetTool, applyMaterialPresetTool, applyMaterialTool, batchRenderTool, importModelTool, inspectSceneTool, listCameraPresetsTool, listCamerasTool, listMaterialPresetsTool, previewRenderTool, productRenderTool, renderAllCamerasTool, renderQueueTool, renderTool, saveSceneTool, setCameraTool, setEnvironmentTool, statusTool, syncSavedSceneTool, } from "./tools/catalog.js";
|
|
15
16
|
import { applyMaterialSchema, applyMaterialPresetSchema, batchRenderInputSchema, productRenderSchema, renderQueueInputSchema, renderAllCamerasInputSchema, renderInputSchema, scenePathSchema, setCameraSchema, } from "./schemas.js";
|
|
16
17
|
function errorMessage(error) {
|
|
17
18
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -45,11 +46,11 @@ export function createKeyShotServer(config = getConfig()) {
|
|
|
45
46
|
server.registerPrompt("keyshot_product_render", {
|
|
46
47
|
title: "Render a KeyShot product scene",
|
|
47
48
|
description: "Create a practical prompt for rendering or batch-rendering a KeyShot product scene.",
|
|
48
|
-
argsSchema: {
|
|
49
|
+
argsSchema: z.object({
|
|
49
50
|
modelPath: scenePathSchema.shape.scenePath.optional(),
|
|
50
51
|
scenePath: scenePathSchema.shape.scenePath.optional(),
|
|
51
52
|
goal: scenePathSchema.shape.scenePath.optional(),
|
|
52
|
-
},
|
|
53
|
+
}),
|
|
53
54
|
}, async (args) => ({
|
|
54
55
|
messages: [
|
|
55
56
|
{
|
|
@@ -66,15 +67,30 @@ export function createKeyShotServer(config = getConfig()) {
|
|
|
66
67
|
args.goal
|
|
67
68
|
? `Goal: ${args.goal}`
|
|
68
69
|
: "Inspect the scene and choose a suitable camera.",
|
|
69
|
-
"
|
|
70
|
+
"Run keyshot_status first. For a model source, use keyshot_import_model to create a scene copy before inspection or preview. For an existing scene, inspect the saved scene directly.",
|
|
71
|
+
"Call keyshot_preview_render and verify that image content was returned before describing visible composition, materials, or lighting. If the image is unavailable, report that limitation rather than inventing visual observations.",
|
|
72
|
+
"For changes you propose, explain the visible issue, target object or camera, intended parameters, and expected effect; obtain approval before editing. Reuse explicit approval already provided by the user without asking again.",
|
|
73
|
+
"Apply approved edits to a scene copy, then preview the returned scene path again. Stop when the approved goal is met; do not repeatedly adjust the design without a user request. Use the approved scene for the standard or final render.",
|
|
70
74
|
"Use lower-level tools only when individual steps need manual control.",
|
|
71
75
|
].join("\n"),
|
|
72
76
|
},
|
|
73
77
|
},
|
|
74
78
|
],
|
|
75
79
|
}));
|
|
76
|
-
server.registerTool("keyshot_status",
|
|
77
|
-
|
|
80
|
+
server.registerTool("keyshot_status", {
|
|
81
|
+
title: statusTool.title,
|
|
82
|
+
description: statusTool.description,
|
|
83
|
+
inputSchema: statusTool.inputSchema,
|
|
84
|
+
outputSchema: statusTool.outputSchema,
|
|
85
|
+
annotations: statusTool.annotations,
|
|
86
|
+
}, async () => toolResponse(await runKeyShotDiagnostics(config)));
|
|
87
|
+
server.registerTool("keyshot_product_render", {
|
|
88
|
+
title: productRenderTool.title,
|
|
89
|
+
description: productRenderTool.description,
|
|
90
|
+
inputSchema: productRenderTool.inputSchema,
|
|
91
|
+
outputSchema: productRenderTool.outputSchema,
|
|
92
|
+
annotations: productRenderTool.annotations,
|
|
93
|
+
}, async (args) => {
|
|
78
94
|
try {
|
|
79
95
|
const parsed = productRenderSchema.parse(args);
|
|
80
96
|
const request = await prepareProductRenderRequest(config, parsed);
|
|
@@ -84,53 +100,119 @@ export function createKeyShotServer(config = getConfig()) {
|
|
|
84
100
|
return toolResponse(localFailure(errorMessage(error)));
|
|
85
101
|
}
|
|
86
102
|
});
|
|
87
|
-
server.registerTool("keyshot_inspect_scene",
|
|
103
|
+
server.registerTool("keyshot_inspect_scene", {
|
|
104
|
+
title: inspectSceneTool.title,
|
|
105
|
+
description: inspectSceneTool.description,
|
|
106
|
+
inputSchema: inspectSceneTool.inputSchema,
|
|
107
|
+
outputSchema: inspectSceneTool.outputSchema,
|
|
108
|
+
annotations: inspectSceneTool.annotations,
|
|
109
|
+
}, async (args) => toolResponse(await runKeyShotSerialized(config, {
|
|
88
110
|
operation: "inspect_scene",
|
|
89
111
|
...args,
|
|
90
112
|
})));
|
|
91
|
-
server.registerTool("keyshot_list_cameras",
|
|
113
|
+
server.registerTool("keyshot_list_cameras", {
|
|
114
|
+
title: listCamerasTool.title,
|
|
115
|
+
description: listCamerasTool.description,
|
|
116
|
+
inputSchema: listCamerasTool.inputSchema,
|
|
117
|
+
outputSchema: listCamerasTool.outputSchema,
|
|
118
|
+
annotations: listCamerasTool.annotations,
|
|
119
|
+
}, async (args) => toolResponse(await runKeyShotSerialized(config, {
|
|
92
120
|
operation: "list_cameras",
|
|
93
121
|
...args,
|
|
94
122
|
})));
|
|
95
|
-
server.registerTool("keyshot_preview_render",
|
|
96
|
-
|
|
97
|
-
|
|
123
|
+
server.registerTool("keyshot_preview_render", {
|
|
124
|
+
title: previewRenderTool.title,
|
|
125
|
+
description: previewRenderTool.description,
|
|
126
|
+
inputSchema: previewRenderTool.inputSchema,
|
|
127
|
+
outputSchema: previewRenderTool.outputSchema,
|
|
128
|
+
annotations: previewRenderTool.annotations,
|
|
129
|
+
}, async (args) => renderPreview(config, args));
|
|
130
|
+
server.registerTool("keyshot_sync_saved_scene", {
|
|
131
|
+
title: syncSavedSceneTool.title,
|
|
132
|
+
description: syncSavedSceneTool.description,
|
|
133
|
+
inputSchema: syncSavedSceneTool.inputSchema,
|
|
134
|
+
outputSchema: syncSavedSceneTool.outputSchema,
|
|
135
|
+
annotations: syncSavedSceneTool.annotations,
|
|
136
|
+
}, async (args) => syncSavedScene(config, args));
|
|
137
|
+
server.registerTool("keyshot_render", {
|
|
138
|
+
title: renderTool.title,
|
|
139
|
+
description: renderTool.description,
|
|
140
|
+
inputSchema: renderTool.inputSchema,
|
|
141
|
+
outputSchema: renderTool.outputSchema,
|
|
142
|
+
annotations: renderTool.annotations,
|
|
143
|
+
}, async (args) => {
|
|
98
144
|
const parsed = renderInputSchema.parse(args);
|
|
99
145
|
return toolResponse(await runKeyShotSerialized(config, {
|
|
100
146
|
operation: "render",
|
|
101
147
|
...applyRenderQuality(parsed),
|
|
102
148
|
}));
|
|
103
149
|
});
|
|
104
|
-
server.registerTool("keyshot_render_queue",
|
|
150
|
+
server.registerTool("keyshot_render_queue", {
|
|
151
|
+
title: renderQueueTool.title,
|
|
152
|
+
description: renderQueueTool.description,
|
|
153
|
+
inputSchema: renderQueueTool.inputSchema,
|
|
154
|
+
outputSchema: renderQueueTool.outputSchema,
|
|
155
|
+
annotations: renderQueueTool.annotations,
|
|
156
|
+
}, async (args) => {
|
|
105
157
|
const parsed = renderQueueInputSchema.parse(args);
|
|
106
158
|
return toolResponse(await runRenderQueue(config, parsed.jobs.map((job) => applyRenderQuality(job)), { continueOnError: parsed.continueOnError ?? false }));
|
|
107
159
|
});
|
|
108
|
-
server.registerTool("keyshot_batch_render",
|
|
160
|
+
server.registerTool("keyshot_batch_render", {
|
|
161
|
+
title: batchRenderTool.title,
|
|
162
|
+
description: batchRenderTool.description,
|
|
163
|
+
inputSchema: batchRenderTool.inputSchema,
|
|
164
|
+
outputSchema: batchRenderTool.outputSchema,
|
|
165
|
+
annotations: batchRenderTool.annotations,
|
|
166
|
+
}, async (args) => {
|
|
109
167
|
const parsed = batchRenderInputSchema.parse(args);
|
|
110
168
|
return toolResponse(await runKeyShotSerialized(config, {
|
|
111
169
|
operation: "batch_render",
|
|
112
170
|
...applyRenderQuality(parsed),
|
|
113
171
|
}));
|
|
114
172
|
});
|
|
115
|
-
server.registerTool("keyshot_render_all_cameras",
|
|
173
|
+
server.registerTool("keyshot_render_all_cameras", {
|
|
174
|
+
title: renderAllCamerasTool.title,
|
|
175
|
+
description: renderAllCamerasTool.description,
|
|
176
|
+
inputSchema: renderAllCamerasTool.inputSchema,
|
|
177
|
+
outputSchema: renderAllCamerasTool.outputSchema,
|
|
178
|
+
annotations: renderAllCamerasTool.annotations,
|
|
179
|
+
}, async (args) => {
|
|
116
180
|
const parsed = renderAllCamerasInputSchema.parse(args);
|
|
117
181
|
return toolResponse(await runKeyShotSerialized(config, {
|
|
118
182
|
operation: "render_all_cameras",
|
|
119
183
|
...applyRenderQuality(parsed),
|
|
120
184
|
}));
|
|
121
185
|
});
|
|
122
|
-
server.registerTool("keyshot_import_model",
|
|
186
|
+
server.registerTool("keyshot_import_model", {
|
|
187
|
+
title: importModelTool.title,
|
|
188
|
+
description: importModelTool.description,
|
|
189
|
+
inputSchema: importModelTool.inputSchema,
|
|
190
|
+
outputSchema: importModelTool.outputSchema,
|
|
191
|
+
annotations: importModelTool.annotations,
|
|
192
|
+
}, async (args) => toolResponse(await runKeyShotSerialized(config, {
|
|
123
193
|
operation: "import_model",
|
|
124
194
|
...args,
|
|
125
195
|
})));
|
|
126
|
-
server.registerTool("keyshot_apply_material",
|
|
196
|
+
server.registerTool("keyshot_apply_material", {
|
|
197
|
+
title: applyMaterialTool.title,
|
|
198
|
+
description: applyMaterialTool.description,
|
|
199
|
+
inputSchema: applyMaterialTool.inputSchema,
|
|
200
|
+
outputSchema: applyMaterialTool.outputSchema,
|
|
201
|
+
annotations: applyMaterialTool.annotations,
|
|
202
|
+
}, async (args) => {
|
|
127
203
|
const parsed = applyMaterialSchema.parse(args);
|
|
128
204
|
return toolResponse(await runKeyShotSerialized(config, {
|
|
129
205
|
operation: "apply_material",
|
|
130
206
|
...parsed,
|
|
131
207
|
}));
|
|
132
208
|
});
|
|
133
|
-
server.registerTool("keyshot_list_material_presets",
|
|
209
|
+
server.registerTool("keyshot_list_material_presets", {
|
|
210
|
+
title: listMaterialPresetsTool.title,
|
|
211
|
+
description: listMaterialPresetsTool.description,
|
|
212
|
+
inputSchema: listMaterialPresetsTool.inputSchema,
|
|
213
|
+
outputSchema: listMaterialPresetsTool.outputSchema,
|
|
214
|
+
annotations: listMaterialPresetsTool.annotations,
|
|
215
|
+
}, async () => {
|
|
134
216
|
try {
|
|
135
217
|
const presets = await loadMaterialPresets(config);
|
|
136
218
|
return toolResponse({
|
|
@@ -154,7 +236,13 @@ export function createKeyShotServer(config = getConfig()) {
|
|
|
154
236
|
return toolResponse(localFailure(errorMessage(error)));
|
|
155
237
|
}
|
|
156
238
|
});
|
|
157
|
-
server.registerTool("keyshot_apply_material_preset",
|
|
239
|
+
server.registerTool("keyshot_apply_material_preset", {
|
|
240
|
+
title: applyMaterialPresetTool.title,
|
|
241
|
+
description: applyMaterialPresetTool.description,
|
|
242
|
+
inputSchema: applyMaterialPresetTool.inputSchema,
|
|
243
|
+
outputSchema: applyMaterialPresetTool.outputSchema,
|
|
244
|
+
annotations: applyMaterialPresetTool.annotations,
|
|
245
|
+
}, async (args) => {
|
|
158
246
|
const parsed = applyMaterialPresetSchema.parse(args);
|
|
159
247
|
let presets;
|
|
160
248
|
try {
|
|
@@ -178,14 +266,26 @@ export function createKeyShotServer(config = getConfig()) {
|
|
|
178
266
|
outputScenePath: parsed.outputScenePath,
|
|
179
267
|
}));
|
|
180
268
|
});
|
|
181
|
-
server.registerTool("keyshot_set_camera",
|
|
269
|
+
server.registerTool("keyshot_set_camera", {
|
|
270
|
+
title: setCameraTool.title,
|
|
271
|
+
description: setCameraTool.description,
|
|
272
|
+
inputSchema: setCameraTool.inputSchema,
|
|
273
|
+
outputSchema: setCameraTool.outputSchema,
|
|
274
|
+
annotations: setCameraTool.annotations,
|
|
275
|
+
}, async (args) => {
|
|
182
276
|
const parsed = setCameraSchema.parse(args);
|
|
183
277
|
return toolResponse(await runKeyShotSerialized(config, {
|
|
184
278
|
operation: "set_camera",
|
|
185
279
|
...parsed,
|
|
186
280
|
}));
|
|
187
281
|
});
|
|
188
|
-
server.registerTool("keyshot_list_camera_presets",
|
|
282
|
+
server.registerTool("keyshot_list_camera_presets", {
|
|
283
|
+
title: listCameraPresetsTool.title,
|
|
284
|
+
description: listCameraPresetsTool.description,
|
|
285
|
+
inputSchema: listCameraPresetsTool.inputSchema,
|
|
286
|
+
outputSchema: listCameraPresetsTool.outputSchema,
|
|
287
|
+
annotations: listCameraPresetsTool.annotations,
|
|
288
|
+
}, async () => {
|
|
189
289
|
try {
|
|
190
290
|
const presets = await loadCameraPresets(config);
|
|
191
291
|
return toolResponse({
|
|
@@ -205,7 +305,13 @@ export function createKeyShotServer(config = getConfig()) {
|
|
|
205
305
|
return toolResponse(localFailure(errorMessage(error)));
|
|
206
306
|
}
|
|
207
307
|
});
|
|
208
|
-
server.registerTool("keyshot_apply_camera_preset",
|
|
308
|
+
server.registerTool("keyshot_apply_camera_preset", {
|
|
309
|
+
title: applyCameraPresetTool.title,
|
|
310
|
+
description: applyCameraPresetTool.description,
|
|
311
|
+
inputSchema: applyCameraPresetTool.inputSchema,
|
|
312
|
+
outputSchema: applyCameraPresetTool.outputSchema,
|
|
313
|
+
annotations: applyCameraPresetTool.annotations,
|
|
314
|
+
}, async (args) => {
|
|
209
315
|
let presets;
|
|
210
316
|
try {
|
|
211
317
|
presets = await loadCameraPresets(config);
|
|
@@ -245,11 +351,23 @@ export function createKeyShotServer(config = getConfig()) {
|
|
|
245
351
|
}
|
|
246
352
|
return toolResponse(result);
|
|
247
353
|
});
|
|
248
|
-
server.registerTool("keyshot_set_environment",
|
|
354
|
+
server.registerTool("keyshot_set_environment", {
|
|
355
|
+
title: setEnvironmentTool.title,
|
|
356
|
+
description: setEnvironmentTool.description,
|
|
357
|
+
inputSchema: setEnvironmentTool.inputSchema,
|
|
358
|
+
outputSchema: setEnvironmentTool.outputSchema,
|
|
359
|
+
annotations: setEnvironmentTool.annotations,
|
|
360
|
+
}, async (args) => toolResponse(await runKeyShotSerialized(config, {
|
|
249
361
|
operation: "set_environment",
|
|
250
362
|
...args,
|
|
251
363
|
})));
|
|
252
|
-
server.registerTool("keyshot_save_scene",
|
|
364
|
+
server.registerTool("keyshot_save_scene", {
|
|
365
|
+
title: saveSceneTool.title,
|
|
366
|
+
description: saveSceneTool.description,
|
|
367
|
+
inputSchema: saveSceneTool.inputSchema,
|
|
368
|
+
outputSchema: saveSceneTool.outputSchema,
|
|
369
|
+
annotations: saveSceneTool.annotations,
|
|
370
|
+
}, async (args) => toolResponse(await runKeyShotSerialized(config, {
|
|
253
371
|
operation: "save_scene",
|
|
254
372
|
...args,
|
|
255
373
|
})));
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.13.0";
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Model workflow evaluation / 模型工作流评估
|
|
2
|
+
|
|
3
|
+
This protocol evaluates an MCP client and model together. Passing unit tests does
|
|
4
|
+
not establish model quality or desktop-control support. Results are pending until
|
|
5
|
+
the tasks are run in both clients with recorded evidence.
|
|
6
|
+
|
|
7
|
+
本清单评估模型与 MCP 客户端的实际协作。单元测试通过不能证明模型表现或桌面控制能力。
|
|
8
|
+
两组任务均完成并留下记录后,才能得出对比结论。
|
|
9
|
+
|
|
10
|
+
## Setup / 准备
|
|
11
|
+
|
|
12
|
+
- Use the same MCP commit, KeyShot version, client version, and generated cube at
|
|
13
|
+
`examples/demo/keyshot-mcp-cube.obj`. Record exact model IDs and reasoning settings.
|
|
14
|
+
- Start each run in a fresh conversation with the same Skill and prompt. Use a
|
|
15
|
+
separate safe output directory per run and the same preview settings.
|
|
16
|
+
- Run each task three times per model. Record failed runs as well as successes.
|
|
17
|
+
- Use only generated assets. Review any logs before sharing them; omit credentials
|
|
18
|
+
and personal paths. Preview images are processed by the configured model provider.
|
|
19
|
+
|
|
20
|
+
- 固定 MCP 提交、KeyShot 和客户端版本,使用仓库立方体;记录完整模型名称和推理设置。
|
|
21
|
+
- 每轮使用新对话、相同 Skill 和提示词、独立安全输出目录与相同预览参数。
|
|
22
|
+
- 每个模型每项任务运行三次,失败也计入结果。
|
|
23
|
+
- 仅使用生成资产;分享记录前移除凭据和个人路径,预览会交给配置的模型服务处理。
|
|
24
|
+
|
|
25
|
+
## Tasks / 任务
|
|
26
|
+
|
|
27
|
+
Run the same language version for both models. Substitute absolute fixture paths.
|
|
28
|
+
两种模型使用同一语言版本,将路径占位符替换为测试资产的绝对路径。
|
|
29
|
+
|
|
30
|
+
| ID | Prompt / 提示词 | Pass condition / 通过条件 |
|
|
31
|
+
| --- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
32
|
+
| T1 | Check whether KeyShot MCP is ready. / 检查 KeyShot MCP 是否准备就绪。 | Reports actual diagnostic results; explains failures without claiming success. / 如实反馈诊断,失败时说明原因。 |
|
|
33
|
+
| T2 | Import `<cube.obj>` to a copy and show a preview; do not produce a final render yet. / 导入立方体并展示预览,暂不正式出图。 | Imports before preview, returns a visible image, preserves source. / 先导入再预览,保留源文件。 |
|
|
34
|
+
| T3 | Inspect this preview and propose one camera adjustment; wait before applying it. / 检查预览,提出一项相机调整,先别执行。 | Describes visible evidence and concrete parameters; makes no edit. / 依据画面提出具体参数,不提前修改。 |
|
|
35
|
+
| T4 | Apply that adjustment to a copy and show the new preview. / 把刚才的调整应用到副本并重新预览。 | Reuses approval, edits the identified camera, previews the returned copy. / 不重复索要确认,预览实际修改的副本。 |
|
|
36
|
+
| T5 | List available material presets and propose one for this object; wait for approval. / 列出可用材质预设,为该对象推荐一个,等待确认。 | Uses real preset names and scene object identifiers, never invents a material. / 使用真实预设和对象标识。 |
|
|
37
|
+
| T6 | Render the approved scene at standard quality. / 按标准质量渲染已确认的场景。 | Uses the approved scene, reports actual output and warnings, stops when complete. / 使用已确认场景,如实报告结果,完成后停止。 |
|
|
38
|
+
|
|
39
|
+
For T4, use the same approved camera adjustment in both model runs. For T6, use
|
|
40
|
+
the same approved scene. This separates execution reliability from design taste.
|
|
41
|
+
|
|
42
|
+
T4 两组使用同一项已批准的相机参数;T6 使用同一份已批准场景,以便比较执行可靠性。
|
|
43
|
+
|
|
44
|
+
## Record / 记录模板
|
|
45
|
+
|
|
46
|
+
Copy this block for each task and repetition. Pending is not a pass.
|
|
47
|
+
每项任务每轮填写一次,未执行不能标记通过。
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
Date / 日期:
|
|
51
|
+
MCP commit and version / 提交与版本:
|
|
52
|
+
Model ID and reasoning / 模型与推理设置:
|
|
53
|
+
Client and KeyShot versions / 客户端与 KeyShot 版本:
|
|
54
|
+
Task and repetition / 任务与轮次:
|
|
55
|
+
Result / 结果: pending | pass | fail | blocked
|
|
56
|
+
Elapsed seconds / 总耗时:
|
|
57
|
+
Tool calls and retries / 工具调用与重试次数:
|
|
58
|
+
Unnecessary confirmation requests / 重复确认次数:
|
|
59
|
+
Actual image received / 是否收到图片:
|
|
60
|
+
Source preserved / 源文件是否保留:
|
|
61
|
+
Evidence and error / 证据与错误:
|
|
62
|
+
Visual judgment / 视觉判断: user review required
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Compare success rate, median elapsed time, retries, unnecessary questions, and
|
|
66
|
+
unsupported visual claims. Keep subjective design preference separate. Report
|
|
67
|
+
cost only when client usage data is available; do not estimate it from elapsed time.
|
|
68
|
+
|
|
69
|
+
比较成功率、耗时中位数、重试、重复确认和无依据的画面描述;设计偏好另行记录。
|
|
70
|
+
费用仅引用客户端实际用量,不按耗时推算。
|
|
71
|
+
|
|
72
|
+
## Local baseline / 本地基线
|
|
73
|
+
|
|
74
|
+
On 2026-09-14, KeyShot 14.1.0.154 returned `ready: true` on the development
|
|
75
|
+
Windows machine. The generated cube was imported to a scene copy and rendered
|
|
76
|
+
at 960 x 540 with 16 samples. The preview response included PNG image content
|
|
77
|
+
and reported 179159 bytes. This was a direct tool smoke check, not a comparison
|
|
78
|
+
of GPT-5.6 and GPT-6. Both model evaluations and desktop interaction remain pending.
|
|
79
|
+
|
|
80
|
+
2026-09-14,开发机 KeyShot 14.1.0.154 状态检查通过,仓库立方体成功导入副本,
|
|
81
|
+
生成 960 x 540、16 samples 的预览,响应包含 PNG 图片内容,大小为 179159 字节。
|
|
82
|
+
这是直接工具验证;GPT-5.6、GPT-6 对照评估和桌面交互仍待执行。
|
|
83
|
+
|
|
84
|
+
## Desktop experiment / 桌面实验
|
|
85
|
+
|
|
86
|
+
Optional and separate: with an explicitly available desktop tool, test opening a
|
|
87
|
+
generated output folder and scene copy. Record the desktop provider and actual
|
|
88
|
+
outcome. This does not demonstrate persistent Live Bridge support or support in
|
|
89
|
+
other clients. The stable MCP workflow remains saved-scene and headless based.
|
|
90
|
+
|
|
91
|
+
可选独立实验:客户端确实提供桌面操作工具时,测试打开生成的输出目录和场景副本。
|
|
92
|
+
记录工具来源与实际结果,不据此宣称 Live Bridge 或其他客户端已经兼容。
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Upgrading KeyShot MCP / KeyShot MCP 升级说明
|
|
2
|
+
|
|
3
|
+
## 0.13.0: MCP protocol update
|
|
4
|
+
|
|
5
|
+
KeyShot MCP 0.13.0 serves MCP `2026-07-28` over stdio through the official
|
|
6
|
+
TypeScript SDK v2 `serveStdio` entry. The same entry accepts legacy
|
|
7
|
+
`2025-11-25` clients. Configuration remains `npx -y keyshot-mcp@0.13.0` with the
|
|
8
|
+
existing KeyShot and output environment variables.
|
|
9
|
+
|
|
10
|
+
1. Update the pinned package version in your MCP client configuration.
|
|
11
|
+
2. Restart the MCP server or client so it loads the new package.
|
|
12
|
+
3. Run `keyshot_status`, inspect a saved scene, and request a preview.
|
|
13
|
+
|
|
14
|
+
All 19 tools, the product-render prompt and workflow resource remain available.
|
|
15
|
+
Results retain JSON text, structured content and preview image content. Local
|
|
16
|
+
output restrictions and saved-scene workflows remain in effect. Node.js 20 or
|
|
17
|
+
later is required.
|
|
18
|
+
|
|
19
|
+
The automated suite exercises the built stdio server with an SDK v2 client pinned
|
|
20
|
+
to `2026-07-28` and a real SDK v1 client using its legacy handshake. This validates
|
|
21
|
+
the protocol paths, not every third-party client's installation or UI behavior.
|
|
22
|
+
|
|
23
|
+
### MCP Events
|
|
24
|
+
|
|
25
|
+
This release does not advertise an `events` capability or implement ChatGPT event
|
|
26
|
+
subscriptions. [OpenAI's MCP Events integration](https://developers.openai.com/plugins/build/mcp-events)
|
|
27
|
+
also requires authenticated access, event discovery/subscription methods,
|
|
28
|
+
persistent subscriptions and signed HTTPS webhook delivery. These are a separate
|
|
29
|
+
integration from a local stdio protocol upgrade. No background monitoring,
|
|
30
|
+
remote endpoint or GUI bridge is enabled by upgrading.
|
|
31
|
+
|
|
32
|
+
### Object selection
|
|
33
|
+
|
|
34
|
+
Scene inspection now resolves native KeyShot object IDs into names, materials and
|
|
35
|
+
unique `objectPath` values. Use the returned path when multiple objects share a
|
|
36
|
+
name. Inspect again after renaming or reorganizing objects. Material changes
|
|
37
|
+
continue to save an output scene rather than modifying the source file in place.
|
|
38
|
+
|
|
39
|
+
## 0.13.0:MCP 协议升级
|
|
40
|
+
|
|
41
|
+
KeyShot MCP 0.13.0 使用官方 TypeScript SDK v2 的 `serveStdio` 入口,通过 stdio
|
|
42
|
+
支持 MCP `2026-07-28`。同一入口仍接受使用 `2025-11-25` 握手的旧客户端。
|
|
43
|
+
安装配置为 `npx -y keyshot-mcp@0.13.0`,沿用原有 KeyShot 和输出目录环境变量。
|
|
44
|
+
|
|
45
|
+
1. 将 MCP 客户端配置中的包版本更新到 0.13.0。
|
|
46
|
+
2. 重启 MCP 服务或客户端,以加载新版本。
|
|
47
|
+
3. 运行 `keyshot_status`,检查已保存场景,再请求预览。
|
|
48
|
+
|
|
49
|
+
19 个工具、产品渲染 Prompt 和工作流 Resource 均保留。结果继续提供 JSON 文本、
|
|
50
|
+
结构化数据和预览图片。本地输出安全限制及已保存场景工作流保持兼容,要求 Node.js 20
|
|
51
|
+
或更高版本。
|
|
52
|
+
|
|
53
|
+
自动测试使用锁定 `2026-07-28` 的 SDK v2 客户端,以及真正的 SDK v1 客户端连接构建后
|
|
54
|
+
的 stdio 服务。测试验证协议路径,不代表所有第三方客户端的安装和界面行为均已实测。
|
|
55
|
+
|
|
56
|
+
### MCP Events 事件订阅
|
|
57
|
+
|
|
58
|
+
本版不声明 `events` 能力,也未实现 ChatGPT 事件订阅。
|
|
59
|
+
[OpenAI 官方接入要求](https://developers.openai.com/plugins/build/mcp-events)还包括经过认证的
|
|
60
|
+
连接、事件发现与订阅接口、持久化订阅和签名 HTTPS webhook 投递。这是后续需要单独
|
|
61
|
+
实现的集成。升级本地协议不会自动开启后台监控、远程接口或 GUI 实时桥接。
|
|
62
|
+
|
|
63
|
+
### 对象选择
|
|
64
|
+
|
|
65
|
+
场景检查现在能将 KeyShot 原生对象 ID 解析为名称、材质和唯一的 `objectPath`。
|
|
66
|
+
遇到同名对象时,应使用检查结果中的路径;重命名或调整对象层级后,应重新读取场景。
|
|
67
|
+
材质修改继续保存到输出场景,避免直接改写原文件。
|
package/lhm.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keyshot-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"mcpName": "io.github.truman-t3/keyshot-mcp",
|
|
5
5
|
"description": "Local MCP server for KeyShot product visualization, scene editing, camera control, and rendering.",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"prepare": "pnpm run build",
|
|
21
21
|
"start": "node ./dist/index.js",
|
|
22
22
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
23
|
-
"test": "vitest run",
|
|
23
|
+
"test": "pnpm run build && vitest run",
|
|
24
24
|
"docs:tools": "pnpm run build && node ./scripts/generate-tools-docs.mjs",
|
|
25
25
|
"docs:tools:check": "pnpm run build && node ./scripts/generate-tools-docs.mjs --check",
|
|
26
26
|
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/*.mjs\" \"*.json\" \"*.md\" \"docs/**/*.md\" \"skills/**/*.md\"",
|
|
@@ -72,13 +72,16 @@
|
|
|
72
72
|
},
|
|
73
73
|
"packageManager": "pnpm@10.17.1",
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@modelcontextprotocol/
|
|
76
|
-
"
|
|
75
|
+
"@modelcontextprotocol/core": "2.2.0",
|
|
76
|
+
"@modelcontextprotocol/server": "2.2.0",
|
|
77
|
+
"zod": "^4.5.4"
|
|
77
78
|
},
|
|
78
79
|
"devDependencies": {
|
|
80
|
+
"@modelcontextprotocol/client": "2.2.0",
|
|
81
|
+
"@modelcontextprotocol/sdk": "1.30.0",
|
|
79
82
|
"@types/node": "^22.17.2",
|
|
80
83
|
"prettier": "3.6.2",
|
|
81
84
|
"typescript": "^5.9.2",
|
|
82
|
-
"vitest": "^
|
|
85
|
+
"vitest": "^4.1.11"
|
|
83
86
|
}
|
|
84
87
|
}
|
|
@@ -85,7 +85,7 @@ def status():
|
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
def inspect_scene():
|
|
88
|
-
objects = [describe_object(obj) for obj in
|
|
88
|
+
objects = [dict(describe_object(obj), path=path) for obj, path in scene_objects()]
|
|
89
89
|
cameras = [serialize_value(camera) for camera in safe_list_call("getCameras")]
|
|
90
90
|
model_sets = [serialize_value(model_set) for model_set in safe_list_call("getModelSets")]
|
|
91
91
|
environments = [serialize_value(environment) for environment in safe_list_call("getEnvironments")]
|
|
@@ -584,11 +584,12 @@ def apply_material_current(payload, warnings):
|
|
|
584
584
|
raise RuntimeError("Object not found. Provide a valid objectName or objectPath.")
|
|
585
585
|
|
|
586
586
|
material = resolve_material(payload.get("materialName"), payload.get("materialPath"), warnings)
|
|
587
|
-
|
|
587
|
+
target_id = first_success(lambda: target.getID(), default=None)
|
|
588
|
+
call_non_false_variants(
|
|
588
589
|
"set object material",
|
|
589
|
-
lambda: lux.setObjectMaterial(
|
|
590
|
-
lambda: lux.setObjectMaterial(
|
|
591
|
-
|
|
590
|
+
lambda: lux.setObjectMaterial(mat=material, obj=target_id if target_id is not None else target),
|
|
591
|
+
lambda: (lux.setObjectMaterial(material, target_id) if target_id is not None
|
|
592
|
+
else lux.setObjectMaterial(target, material)),
|
|
592
593
|
)
|
|
593
594
|
|
|
594
595
|
return {
|
|
@@ -873,11 +874,49 @@ def safe_list_call(name):
|
|
|
873
874
|
return [value]
|
|
874
875
|
|
|
875
876
|
|
|
877
|
+
def scene_objects():
|
|
878
|
+
raw = safe_list_call("getObjects")
|
|
879
|
+
if not raw:
|
|
880
|
+
return []
|
|
881
|
+
# Older wrappers expose node objects; native KeyShot returns integer IDs.
|
|
882
|
+
if all(not isinstance(obj, int) for obj in raw):
|
|
883
|
+
return [(obj, describe_object(obj)["path"]) for obj in raw]
|
|
884
|
+
root = first_non_none(lambda: lux.getSceneTree())
|
|
885
|
+
if root is None:
|
|
886
|
+
raise RuntimeError("Scene inspection is unsupported: getSceneTree is required to resolve object IDs.")
|
|
887
|
+
wanted = set(raw)
|
|
888
|
+
found = {}
|
|
889
|
+
stack = [(root, "")]
|
|
890
|
+
visited = set()
|
|
891
|
+
while stack:
|
|
892
|
+
node, path = stack.pop()
|
|
893
|
+
node_id = node.getID()
|
|
894
|
+
if node_id in visited:
|
|
895
|
+
continue
|
|
896
|
+
visited.add(node_id)
|
|
897
|
+
if node_id in wanted:
|
|
898
|
+
found[node_id] = (node, path)
|
|
899
|
+
children = first_success(lambda: node.getChildren(), default=[]) or []
|
|
900
|
+
entries = []
|
|
901
|
+
occurrences = {}
|
|
902
|
+
for child in children:
|
|
903
|
+
name = str(child.getName()).replace("%", "%25").replace("/", "%2F")
|
|
904
|
+
name = name.replace("[", "%5B").replace("]", "%5D")
|
|
905
|
+
occurrences[name] = occurrences.get(name, 0) + 1
|
|
906
|
+
# Native IDs can change across headless launches; paths use sibling order.
|
|
907
|
+
entries.append((child, "%s/%s[%s]" % (path, name, occurrences[name])))
|
|
908
|
+
stack.extend(reversed(entries))
|
|
909
|
+
missing = wanted.difference(found)
|
|
910
|
+
if missing:
|
|
911
|
+
raise RuntimeError("Scene inspection could not resolve object IDs: %s" % sorted(missing))
|
|
912
|
+
return [found[node_id] for node_id in raw]
|
|
913
|
+
|
|
914
|
+
|
|
876
915
|
def describe_object(obj):
|
|
877
916
|
return {
|
|
878
917
|
"name": first_success(lambda: obj.getName(), lambda: obj.name(), lambda: obj.name, default=repr(obj)),
|
|
879
918
|
"path": first_success(lambda: obj.getPath(), lambda: obj.path(), lambda: obj.path, default=None),
|
|
880
|
-
"type": first_success(lambda: obj.getType(), lambda: obj.type(), lambda: obj.type, default=type(obj).__name__),
|
|
919
|
+
"type": first_success(lambda: obj.getKind(), lambda: obj.getType(), lambda: obj.type(), lambda: obj.type, default=type(obj).__name__),
|
|
881
920
|
"material": serialize_value(
|
|
882
921
|
first_success(lambda: obj.getMaterial(), lambda: obj.material(), lambda: obj.material, default=None)
|
|
883
922
|
),
|
|
@@ -886,13 +925,13 @@ def describe_object(obj):
|
|
|
886
925
|
|
|
887
926
|
|
|
888
927
|
def find_object(name, path):
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
if name and
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
return None
|
|
928
|
+
matches = []
|
|
929
|
+
for obj, object_path in scene_objects():
|
|
930
|
+
if (path and object_path == path) or (not path and name and describe_object(obj)["name"] == name):
|
|
931
|
+
matches.append(obj)
|
|
932
|
+
if len(matches) > 1:
|
|
933
|
+
raise RuntimeError("Ambiguous object name. Inspect the scene and provide a unique objectPath.")
|
|
934
|
+
return matches[0] if matches else None
|
|
896
935
|
|
|
897
936
|
|
|
898
937
|
def resolve_material(material_name, material_path, warnings):
|
package/server.json
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
"url": "https://github.com/truman-t3/keyshot-mcp",
|
|
10
10
|
"source": "github"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.
|
|
12
|
+
"version": "0.13.0",
|
|
13
13
|
"packages": [
|
|
14
14
|
{
|
|
15
15
|
"registryType": "npm",
|
|
16
16
|
"identifier": "keyshot-mcp",
|
|
17
|
-
"version": "0.
|
|
17
|
+
"version": "0.13.0",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
20
20
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: keyshot-mcp
|
|
3
3
|
author: truman-t3
|
|
4
4
|
description: Install, diagnose, and safely use KeyShot MCP for local product visualization, scene editing, camera control, material assignment, and rendering.
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.13.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# KeyShot MCP
|
|
@@ -35,7 +35,7 @@ Respond in the user's language and explain errors in plain, non-technical terms.
|
|
|
35
35
|
1. Confirm Node.js 20 or newer is available.
|
|
36
36
|
2. Confirm KeyShot is installed and locally licensed.
|
|
37
37
|
3. Locate `keyshot_headless.exe`. Prefer its absolute path.
|
|
38
|
-
4. Configure the MCP client to run `npx -y keyshot-mcp@0.
|
|
38
|
+
4. Configure the MCP client to run `npx -y keyshot-mcp@0.13.0` and set
|
|
39
39
|
`KEYSHOT_HEADLESS_EXE`.
|
|
40
40
|
5. Leave `KEYSHOT_OUTPUT_DIR` unset to use
|
|
41
41
|
`<home>/Documents/KeyShot MCP Outputs`, or set a user-approved directory.
|
|
@@ -50,7 +50,7 @@ Example configuration:
|
|
|
50
50
|
"mcpServers": {
|
|
51
51
|
"keyshot": {
|
|
52
52
|
"command": "npx",
|
|
53
|
-
"args": ["-y", "keyshot-mcp@0.
|
|
53
|
+
"args": ["-y", "keyshot-mcp@0.13.0"],
|
|
54
54
|
"env": {
|
|
55
55
|
"KEYSHOT_HEADLESS_EXE": "C:/Program Files/KeyShot Studio/bin/keyshot_headless.exe"
|
|
56
56
|
}
|
|
@@ -104,11 +104,15 @@ Every tool returns `ok`, operation-specific `data`, `outputFiles`, `warnings`,
|
|
|
104
104
|
## Recommended feedback loop
|
|
105
105
|
|
|
106
106
|
1. Run `keyshot_status`.
|
|
107
|
-
2. Inspect the scene and identify the intended camera or object.
|
|
107
|
+
2. For a model input, import it to a scene copy first. Inspect the saved scene and identify the intended camera or object.
|
|
108
108
|
3. Call `keyshot_preview_render` and describe only what is visible in the returned image.
|
|
109
|
-
4.
|
|
109
|
+
4. For proposed changes, state the visible issue, target, exact parameters, and expected effect. Obtain approval for new design decisions; reuse explicit approval already given.
|
|
110
110
|
5. Apply approved edits to a scene copy.
|
|
111
|
-
6.
|
|
111
|
+
6. Preview the returned scene copy again and compare it with the approved goal. Do not infer visual success from a file path alone. If image content is unavailable, explain that limitation.
|
|
112
|
+
7. Render the approved scene at `standard` or `final` quality. Stop rather than making unsolicited iterative adjustments.
|
|
113
|
+
|
|
114
|
+
Model comparison tasks and a reusable evaluation record are in
|
|
115
|
+
[`docs/MODEL-EVALUATION.md`](../../docs/MODEL-EVALUATION.md).
|
|
112
116
|
|
|
113
117
|
## Boundaries
|
|
114
118
|
|