keyshot-mcp 0.10.0 → 0.11.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 +19 -1
- package/README.md +147 -27
- package/assets/logo-lockup.png +0 -0
- package/dist/diagnostics.js +7 -0
- package/dist/save-sync.js +203 -0
- package/dist/schemas.js +39 -0
- package/dist/server.js +6 -1
- package/dist/tools/catalog.js +11 -1
- package/dist/version.js +1 -1
- package/docs/TOOLS.md +21 -1
- package/package.json +7 -1
- package/server.json +2 -2
- package/skills/keyshot-mcp/SKILL.md +21 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to KeyShot MCP are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.11.0] - 2026-08-12
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `keyshot_sync_saved_scene`, which detects the newest saved `.bip`, copies it to
|
|
12
|
+
a collision-safe output, returns a content fingerprint, and optionally embeds a
|
|
13
|
+
preview.
|
|
14
|
+
- Machine-readable interaction-mode diagnostics so Agents can explain that stable
|
|
15
|
+
tools work on saved scenes rather than an unsaved GUI session.
|
|
16
|
+
- An original KeyShot MCP project mark and a clearer README introduction,
|
|
17
|
+
navigation, highlights, and bilingual positioning for easier recognition.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- README, Agent Skill, and MCP workflow guidance now explain the tested KeyShot
|
|
22
|
+
Script Runner limitation and recommend the saved-scene synchronization workflow.
|
|
23
|
+
|
|
7
24
|
## [0.10.0] - 2026-08-11
|
|
8
25
|
|
|
9
26
|
### Added
|
|
@@ -22,5 +39,6 @@ All notable changes to KeyShot MCP are documented in this file.
|
|
|
22
39
|
|
|
23
40
|
- Documented that scene metadata and preview images may be sent by an MCP client to its configured model provider.
|
|
24
41
|
|
|
25
|
-
[Unreleased]: https://github.com/truman-t3/keyshot-mcp/compare/v0.
|
|
42
|
+
[Unreleased]: https://github.com/truman-t3/keyshot-mcp/compare/v0.11.0...HEAD
|
|
43
|
+
[0.11.0]: https://github.com/truman-t3/keyshot-mcp/compare/v0.10.0...v0.11.0
|
|
26
44
|
[0.10.0]: https://github.com/truman-t3/keyshot-mcp/compare/v0.9.1...v0.10.0
|
package/README.md
CHANGED
|
@@ -1,35 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
>
|
|
11
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo-lockup.png" width="620" alt="KeyShot MCP">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>Turn product-rendering requests into safe, repeatable KeyShot workflows.</strong><br>
|
|
7
|
+
<strong>将产品渲染需求转化为安全、可重复执行的 KeyShot 工作流。</strong>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
Inspect scenes, prepare product views, adjust materials, cameras and environments,
|
|
12
|
+
and return Agent-visible renders through local KeyShot headless scripting.<br>
|
|
13
|
+
检查场景、准备产品视图、调整材质、相机和环境,并通过本地 KeyShot headless 脚本将渲染结果返回给 Agent。
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="https://www.npmjs.com/package/keyshot-mcp"><img src="https://img.shields.io/npm/v/keyshot-mcp.svg" alt="npm version"></a>
|
|
18
|
+
<a href="https://github.com/truman-t3/keyshot-mcp/actions/workflows/ci.yml"><img src="https://github.com/truman-t3/keyshot-mcp/actions/workflows/ci.yml/badge.svg" alt="CI status"></a>
|
|
19
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT License"></a>
|
|
20
|
+
<img src="https://img.shields.io/badge/tools-19-55E6A5" alt="19 MCP tools">
|
|
21
|
+
<img src="https://img.shields.io/badge/MCP-Model_Context_Protocol-111827" alt="Model Context Protocol">
|
|
22
|
+
<img src="https://img.shields.io/badge/KeyShot-14.1_tested-00AEEF" alt="KeyShot 14.1 tested">
|
|
23
|
+
<img src="https://img.shields.io/badge/Node.js-20%2B-339933" alt="Node.js 20 or newer">
|
|
24
|
+
<img src="https://img.shields.io/badge/TypeScript-5.9-3178C6" alt="TypeScript 5.9">
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<a href="#quick-start-for-designers">Quick start</a> ·
|
|
29
|
+
<a href="#highlights">Highlights</a> ·
|
|
30
|
+
<a href="#common-workflows">Workflows</a> ·
|
|
31
|
+
<a href="#tools">Tools</a> ·
|
|
32
|
+
<a href="#configuration">Configuration</a>
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
<p align="center"><a href="#english">English</a> · <a href="#中文">简体中文</a></p>
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<strong>Useful for your KeyShot workflow? Star the repository to help other designers discover it.</strong><br>
|
|
39
|
+
<strong>如果它改善了你的 KeyShot 工作流,欢迎点一个 Star,让更多设计师发现这个项目。</strong>
|
|
40
|
+
</p>
|
|
12
41
|
|
|
13
42
|
KeyShot MCP connects an MCP-compatible AI agent to a licensed KeyShot installation
|
|
14
|
-
on the same computer.
|
|
15
|
-
|
|
16
|
-
headless scripting. KeyShot file processing stays local; however, an MCP client may
|
|
17
|
-
send tool results, scene metadata, or embedded previews to its configured model
|
|
43
|
+
on the same computer. KeyShot file processing stays local; however, an MCP client
|
|
44
|
+
may send tool results, scene metadata, or embedded previews to its configured model
|
|
18
45
|
provider. See [Security](SECURITY.md) before using confidential work.
|
|
19
46
|
|
|
20
47
|

|
|
21
48
|
|
|
22
49
|
## English
|
|
23
50
|
|
|
51
|
+
### Highlights
|
|
52
|
+
|
|
53
|
+
- **Start with a request, not a render-settings checklist.** Use one product tool
|
|
54
|
+
for model import, object materials, camera, environment, scene copy, and output.
|
|
55
|
+
- **See before committing.** Bounded PNG previews are returned directly to the
|
|
56
|
+
Agent for composition, material, and lighting feedback.
|
|
57
|
+
- **Keep source scenes protected.** Editing workflows write controlled copies and
|
|
58
|
+
reject output paths outside the configured safe directory by default.
|
|
59
|
+
- **Continue from the latest save.** Saved-scene sync detects real file changes,
|
|
60
|
+
creates a collision-safe copy, and avoids duplicate work with fingerprints.
|
|
61
|
+
- **Use focused controls when needed.** Nineteen documented tools cover inspection,
|
|
62
|
+
presets, cameras, materials, environments, render queues, and all-camera output.
|
|
63
|
+
|
|
64
|
+
> [!NOTE]
|
|
65
|
+
> Tested on Windows 11 with KeyShot Studio 2025 / KeyShot 14.1. KeyShot and a valid
|
|
66
|
+
> license are required and are not included with this independent project.
|
|
67
|
+
|
|
24
68
|
### Quick start for designers
|
|
25
69
|
|
|
26
70
|
The easiest setup is to send this prompt to an agent that can edit your MCP
|
|
27
71
|
configuration:
|
|
28
72
|
|
|
29
73
|
```text
|
|
30
|
-
Install KeyShot MCP 0.
|
|
74
|
+
Install KeyShot MCP 0.11.0 and configure it in my MCP client.
|
|
31
75
|
|
|
32
|
-
1. Use: npx -y keyshot-mcp@0.
|
|
76
|
+
1. Use: npx -y keyshot-mcp@0.11.0
|
|
33
77
|
2. Find my local keyshot_headless.exe and set KEYSHOT_HEADLESS_EXE to its full path.
|
|
34
78
|
3. Keep outputs in the default KeyShot MCP Outputs folder unless I choose another safe folder.
|
|
35
79
|
4. Keep KEYSHOT_ALLOW_EXTERNAL_OUTPUTS disabled.
|
|
@@ -55,9 +99,41 @@ from C:\models\speaker.obj.
|
|
|
55
99
|
they are not currently verified by this project.
|
|
56
100
|
- KeyShot, its license, materials, and environments are not included.
|
|
57
101
|
|
|
102
|
+
### Why the stable release does not control the open KeyShot window
|
|
103
|
+
|
|
104
|
+
The stable server works on saved scene files through KeyShot headless scripting;
|
|
105
|
+
it does not control the currently open, unsaved KeyShot GUI session. This is a
|
|
106
|
+
boundary of the public KeyShot scripting execution model, not an omitted MCP
|
|
107
|
+
connection. KeyShot's Script Runner keeps a GUI script active until that script
|
|
108
|
+
returns. In our Live Companion prototype, keeping a bridge alive also kept the
|
|
109
|
+
Script Runner open and blocked normal interaction with the KeyShot window.
|
|
110
|
+
|
|
111
|
+
Calling `lux.sync()` can flush pending KeyShot operations, but it does not provide
|
|
112
|
+
a documented background service, GUI event callback, or plugin lifecycle that can
|
|
113
|
+
safely host a persistent MCP bridge. Calling `lux` from an arbitrary worker thread
|
|
114
|
+
would also risk unsafe access to the scene. For that reason, the project does not
|
|
115
|
+
present the experimental bridge as production-ready realtime control.
|
|
116
|
+
|
|
117
|
+
For a reliable workflow, save the scene first. The Agent can inspect or edit a
|
|
118
|
+
safe copy through headless KeyShot, return a preview, and preserve the original
|
|
119
|
+
file. True realtime GUI control can be reconsidered if KeyShot exposes a supported
|
|
120
|
+
non-blocking GUI extension or main-thread callback API.
|
|
121
|
+
|
|
122
|
+
Use `keyshot_sync_saved_scene` for the closest stable alternative to realtime
|
|
123
|
+
collaboration. Give it a saved `.bip` file or one folder containing `.bip` files.
|
|
124
|
+
It selects the newest saved scene, computes a content fingerprint, copies it to a
|
|
125
|
+
collision-safe output name, and returns an embedded preview by default. Pass the
|
|
126
|
+
returned fingerprint on the next call; if the user has not saved a new change,
|
|
127
|
+
the tool reports `changed: false` without creating another copy or render.
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
I saved my KeyShot scene in C:\projects\speaker. Sync the newest .bip from that
|
|
131
|
+
folder, show me a preview, and do not overwrite the original scene.
|
|
132
|
+
```
|
|
133
|
+
|
|
58
134
|
### Install
|
|
59
135
|
|
|
60
|
-
The current release is `0.
|
|
136
|
+
The current release is `0.11.0`.
|
|
61
137
|
|
|
62
138
|
#### Run with npx
|
|
63
139
|
|
|
@@ -68,7 +144,7 @@ No global npm installation is required:
|
|
|
68
144
|
"mcpServers": {
|
|
69
145
|
"keyshot": {
|
|
70
146
|
"command": "npx",
|
|
71
|
-
"args": ["-y", "keyshot-mcp@0.
|
|
147
|
+
"args": ["-y", "keyshot-mcp@0.11.0"],
|
|
72
148
|
"env": {
|
|
73
149
|
"KEYSHOT_HEADLESS_EXE": "C:/Program Files/KeyShot Studio/bin/keyshot_headless.exe"
|
|
74
150
|
}
|
|
@@ -80,7 +156,7 @@ No global npm installation is required:
|
|
|
80
156
|
#### Install globally
|
|
81
157
|
|
|
82
158
|
```bash
|
|
83
|
-
npm install -g keyshot-mcp@0.
|
|
159
|
+
npm install -g keyshot-mcp@0.11.0
|
|
84
160
|
```
|
|
85
161
|
|
|
86
162
|
```json
|
|
@@ -185,6 +261,7 @@ Lower-level render tools preserve their existing behavior when no preset is give
|
|
|
185
261
|
| `keyshot_status` | Diagnose local configuration, output access, presets, and KeyShot startup. |
|
|
186
262
|
| `keyshot_product_render` | Prepare, save, and render a model or scene in one process. |
|
|
187
263
|
| `keyshot_preview_render` | Return a temporary or saved PNG directly to the Agent for visual review. |
|
|
264
|
+
| `keyshot_sync_saved_scene` | Copy the newest saved scene safely, detect changes, and return a preview. |
|
|
188
265
|
| `keyshot_inspect_scene` | List scene metadata, objects, cameras, materials, model sets, and references. |
|
|
189
266
|
| `keyshot_list_cameras` | Return saved camera names before a selected-camera render. |
|
|
190
267
|
| `keyshot_render` | Render one active or named camera. |
|
|
@@ -203,7 +280,7 @@ Lower-level render tools preserve their existing behavior when no preset is give
|
|
|
203
280
|
|
|
204
281
|
The server also exposes one MCP Prompt for product rendering and one MCP Resource
|
|
205
282
|
describing the KeyShot headless workflow. The complete generated reference for
|
|
206
|
-
all
|
|
283
|
+
all 19 tools is available in [`docs/TOOLS.md`](docs/TOOLS.md). The bundled Agent Skill in
|
|
207
284
|
[`skills/keyshot-mcp`](skills/keyshot-mcp) teaches compatible agents how to install,
|
|
208
285
|
diagnose, and use the server safely.
|
|
209
286
|
|
|
@@ -293,14 +370,29 @@ bypass licensing or redistribute proprietary KeyShot software or assets.
|
|
|
293
370
|
KeyShot MCP 在本机处理 KeyShot 文件;但 MCP 客户端可能会把工具结果、场景元数据或
|
|
294
371
|
内嵌预览发送给它所配置的模型服务。处理保密项目之前请先阅读[安全说明](SECURITY.md)。
|
|
295
372
|
|
|
373
|
+
### 核心特点
|
|
374
|
+
|
|
375
|
+
- **从设计需求开始,而不是先填写一堆渲染参数。** 一个产品出图工具即可完成模型导入、
|
|
376
|
+
指定对象材质、相机、环境、场景副本和渲染输出。
|
|
377
|
+
- **正式出图前先看预览。** 预览 PNG 会直接返回给 Agent,用于检查构图、材质和光线。
|
|
378
|
+
- **保护源场景。** 编辑流程默认生成受控副本,并拒绝向安全输出目录之外写入文件。
|
|
379
|
+
- **从最近一次保存继续。** 保存同步会识别真实文件变化、创建不重名副本,并用内容指纹
|
|
380
|
+
避免重复处理。
|
|
381
|
+
- **需要精细控制时仍然可用。** 19 个工具覆盖场景检查、预设、相机、材质、环境、
|
|
382
|
+
渲染队列和全部相机出图。
|
|
383
|
+
|
|
384
|
+
> [!NOTE]
|
|
385
|
+
> 已实测 Windows 11 + KeyShot Studio 2025 / KeyShot 14.1。使用时需要另行安装并合法
|
|
386
|
+
> 授权 KeyShot;本项目是独立开源项目,不包含 KeyShot 软件或许可证。
|
|
387
|
+
|
|
296
388
|
### 设计师快速开始
|
|
297
389
|
|
|
298
390
|
最简单的安装方式,是把下面这段话发给能够修改 MCP 配置的 Agent:
|
|
299
391
|
|
|
300
392
|
```text
|
|
301
|
-
请安装 KeyShot MCP 0.
|
|
393
|
+
请安装 KeyShot MCP 0.11.0,并配置到我的 MCP 客户端。
|
|
302
394
|
|
|
303
|
-
1. 使用:npx -y keyshot-mcp@0.
|
|
395
|
+
1. 使用:npx -y keyshot-mcp@0.11.0
|
|
304
396
|
2. 查找本机 keyshot_headless.exe,并把完整路径设置为 KEYSHOT_HEADLESS_EXE。
|
|
305
397
|
3. 默认把结果保存在“文档/KeyShot MCP Outputs”,除非我明确选择其他安全目录。
|
|
306
398
|
4. 保持 KEYSHOT_ALLOW_EXTERNAL_OUTPUTS 关闭。
|
|
@@ -324,9 +416,36 @@ KeyShot MCP 在本机处理 KeyShot 文件;但 MCP 客户端可能会把工具
|
|
|
324
416
|
- 暴露相同脚本 API 的其他 KeyShot 版本可能可用,但本项目暂未完成实机验证。
|
|
325
417
|
- 本项目不包含 KeyShot、许可证、官方材质或环境资源。
|
|
326
418
|
|
|
419
|
+
### 为什么稳定版不能控制当前打开的 KeyShot 窗口
|
|
420
|
+
|
|
421
|
+
稳定版通过 KeyShot headless 脚本处理已经保存的场景文件,不能直接控制 KeyShot
|
|
422
|
+
窗口中尚未保存的当前会话。这是 KeyShot 现有公开脚本执行方式的边界,并不是 MCP
|
|
423
|
+
连接功能遗漏。
|
|
424
|
+
|
|
425
|
+
我们已经制作并实机测试过 Live Companion 原型。为了等待 Agent 指令,Bridge 脚本
|
|
426
|
+
必须长期保持运行;但 KeyShot Script Runner 会一直等待脚本结束,导致脚本窗口持续
|
|
427
|
+
占用并阻挡 KeyShot GUI 的正常交互。`lux.sync()` 只能同步待处理操作,公开文档没有
|
|
428
|
+
提供可安全承载常驻 MCP Bridge 的后台服务、GUI 事件回调或插件生命周期。让普通后台
|
|
429
|
+
线程直接调用 `lux` 也可能造成不安全的场景访问。因此,本项目没有把这个实验方案包装成
|
|
430
|
+
可用于正式工作的“实时控制”。
|
|
431
|
+
|
|
432
|
+
可靠的工作方式是先保存场景,再让 Agent 使用 headless KeyShot 检查或修改安全副本、
|
|
433
|
+
返回预览并保留原文件。如果 KeyShot 后续公开非阻塞 GUI 扩展接口或主线程回调 API,
|
|
434
|
+
本项目可以重新评估真正的实时 GUI 控制。
|
|
435
|
+
|
|
436
|
+
`keyshot_sync_saved_scene` 是目前最接近实时协作、同时保持稳定的方案。向它提供一个已
|
|
437
|
+
保存的 `.bip` 文件,或只包含当前项目场景的文件夹。工具会选择最近保存的场景、计算
|
|
438
|
+
内容指纹、复制到不会重名的安全输出路径,并默认返回内嵌预览。下一次调用时传入上次的
|
|
439
|
+
指纹;如果用户没有保存新的修改,工具会返回 `changed: false`,不会重复复制或渲染。
|
|
440
|
+
|
|
441
|
+
```text
|
|
442
|
+
我已经把 KeyShot 场景保存在 C:\projects\speaker。请同步这个文件夹中最新的 .bip,
|
|
443
|
+
给我看预览,而且不要覆盖原场景。
|
|
444
|
+
```
|
|
445
|
+
|
|
327
446
|
### 安装
|
|
328
447
|
|
|
329
|
-
当前正式版本为 `0.
|
|
448
|
+
当前正式版本为 `0.11.0`。
|
|
330
449
|
|
|
331
450
|
#### 使用 npx 免安装运行
|
|
332
451
|
|
|
@@ -335,7 +454,7 @@ KeyShot MCP 在本机处理 KeyShot 文件;但 MCP 客户端可能会把工具
|
|
|
335
454
|
"mcpServers": {
|
|
336
455
|
"keyshot": {
|
|
337
456
|
"command": "npx",
|
|
338
|
-
"args": ["-y", "keyshot-mcp@0.
|
|
457
|
+
"args": ["-y", "keyshot-mcp@0.11.0"],
|
|
339
458
|
"env": {
|
|
340
459
|
"KEYSHOT_HEADLESS_EXE": "C:/Program Files/KeyShot Studio/bin/keyshot_headless.exe"
|
|
341
460
|
}
|
|
@@ -347,7 +466,7 @@ KeyShot MCP 在本机处理 KeyShot 文件;但 MCP 客户端可能会把工具
|
|
|
347
466
|
#### 全局安装
|
|
348
467
|
|
|
349
468
|
```bash
|
|
350
|
-
npm install -g keyshot-mcp@0.
|
|
469
|
+
npm install -g keyshot-mcp@0.11.0
|
|
351
470
|
```
|
|
352
471
|
|
|
353
472
|
```json
|
|
@@ -446,6 +565,7 @@ pnpm build
|
|
|
446
565
|
| `keyshot_status` | 检查本机配置、输出目录、预设和 KeyShot 启动状态。 |
|
|
447
566
|
| `keyshot_product_render` | 在一个进程中整理、保存并渲染模型或场景。 |
|
|
448
567
|
| `keyshot_preview_render` | 将临时或保留的 PNG 直接返回给 Agent 进行视觉检查。 |
|
|
568
|
+
| `keyshot_sync_saved_scene` | 安全同步最近保存的场景、检测变化并返回预览。 |
|
|
449
569
|
| `keyshot_inspect_scene` | 查看场景、对象、相机、材质、模型集和外部引用。 |
|
|
450
570
|
| `keyshot_list_cameras` | 返回场景中的相机名称。 |
|
|
451
571
|
| `keyshot_render` | 渲染当前或指定相机。 |
|
|
@@ -463,7 +583,7 @@ pnpm build
|
|
|
463
583
|
| `keyshot_save_scene` | 将场景保存到受控输出路径。 |
|
|
464
584
|
|
|
465
585
|
服务还提供一个产品渲染 MCP Prompt,以及一个说明 headless 工作流程的 MCP Resource。
|
|
466
|
-
完整的
|
|
586
|
+
完整的 19 个工具参考由代码自动生成在 [`docs/TOOLS.md`](docs/TOOLS.md)。
|
|
467
587
|
[`skills/keyshot-mcp`](skills/keyshot-mcp) 中的 Agent Skill 会指导兼容的 Agent 安装、
|
|
468
588
|
诊断并安全使用这些工具。
|
|
469
589
|
|
|
Binary file
|
package/dist/diagnostics.js
CHANGED
|
@@ -74,6 +74,13 @@ export async function runKeyShotDiagnostics(config, runStatus = runKeyShotSerial
|
|
|
74
74
|
availableFunctionCount: availableFunctions.length,
|
|
75
75
|
capabilities: capabilitySummary(availableFunctions),
|
|
76
76
|
serverVersion: VERSION,
|
|
77
|
+
interaction: {
|
|
78
|
+
mode: "saved-scene-headless",
|
|
79
|
+
realtimeGuiControl: false,
|
|
80
|
+
requiresSavedScene: true,
|
|
81
|
+
recommendedSyncTool: "keyshot_sync_saved_scene",
|
|
82
|
+
reason: "Persistent GUI scripts keep KeyShot Script Runner active and block normal GUI interaction; the documented scripting API does not expose a supported non-blocking bridge lifecycle.",
|
|
83
|
+
},
|
|
77
84
|
ready,
|
|
78
85
|
config: {
|
|
79
86
|
keyshotHeadlessExe: config.keyshotHeadlessExe,
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import fsp from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { normalizeOutputPath } from "./output-paths.js";
|
|
6
|
+
import { renderPreview } from "./preview.js";
|
|
7
|
+
import { localFailure, toolResponse } from "./result.js";
|
|
8
|
+
import { syncSavedSceneInputSchema } from "./schemas.js";
|
|
9
|
+
export async function syncSavedScene(config, rawArgs, previewFn = renderPreview) {
|
|
10
|
+
let copiedPath;
|
|
11
|
+
try {
|
|
12
|
+
const args = syncSavedSceneInputSchema.parse(rawArgs);
|
|
13
|
+
const sourcePath = await resolveSavedScene(args.sourcePath);
|
|
14
|
+
const sourceBefore = await describeFile(sourcePath);
|
|
15
|
+
if (args.previousFingerprint === sourceBefore.fingerprint) {
|
|
16
|
+
return toolResponse({
|
|
17
|
+
ok: true,
|
|
18
|
+
data: {
|
|
19
|
+
changed: false,
|
|
20
|
+
sourcePath,
|
|
21
|
+
fingerprint: sourceBefore.fingerprint,
|
|
22
|
+
size: sourceBefore.size,
|
|
23
|
+
modifiedAt: sourceBefore.modifiedAt,
|
|
24
|
+
synchronizedScene: null,
|
|
25
|
+
previewEmbedded: false,
|
|
26
|
+
},
|
|
27
|
+
outputFiles: [],
|
|
28
|
+
warnings: [],
|
|
29
|
+
keyshotStdoutTail: "",
|
|
30
|
+
error: null,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
copiedPath = args.outputScenePath
|
|
34
|
+
? await normalizeOutputPath(config, args.outputScenePath)
|
|
35
|
+
: await allocateSyncPath(config, sourcePath);
|
|
36
|
+
if (await exists(copiedPath)) {
|
|
37
|
+
throw new Error(`Output already exists and overwrite is false: ${copiedPath}`);
|
|
38
|
+
}
|
|
39
|
+
if (samePath(sourcePath, copiedPath)) {
|
|
40
|
+
throw new Error("The synchronized scene copy must differ from sourcePath.");
|
|
41
|
+
}
|
|
42
|
+
await fsp.mkdir(path.dirname(copiedPath), { recursive: true });
|
|
43
|
+
await fsp.copyFile(sourcePath, copiedPath, fs.constants.COPYFILE_EXCL);
|
|
44
|
+
const sourceAfter = await describeFile(sourcePath);
|
|
45
|
+
if (sourceAfter.size !== sourceBefore.size ||
|
|
46
|
+
sourceAfter.modifiedMs !== sourceBefore.modifiedMs ||
|
|
47
|
+
sourceAfter.fingerprint !== sourceBefore.fingerprint) {
|
|
48
|
+
await fsp.rm(copiedPath, { force: true });
|
|
49
|
+
copiedPath = undefined;
|
|
50
|
+
throw new Error("The KeyShot scene changed while it was being copied. Wait for saving to finish, then retry.");
|
|
51
|
+
}
|
|
52
|
+
const baseData = {
|
|
53
|
+
changed: true,
|
|
54
|
+
sourcePath,
|
|
55
|
+
fingerprint: sourceBefore.fingerprint,
|
|
56
|
+
size: sourceBefore.size,
|
|
57
|
+
modifiedAt: sourceBefore.modifiedAt,
|
|
58
|
+
synchronizedScene: copiedPath,
|
|
59
|
+
};
|
|
60
|
+
if (args.includePreview === false) {
|
|
61
|
+
return toolResponse({
|
|
62
|
+
ok: true,
|
|
63
|
+
data: { ...baseData, previewEmbedded: false },
|
|
64
|
+
outputFiles: [copiedPath],
|
|
65
|
+
warnings: [],
|
|
66
|
+
keyshotStdoutTail: "",
|
|
67
|
+
error: null,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const preview = (await previewFn(config, {
|
|
71
|
+
scenePath: copiedPath,
|
|
72
|
+
camera: args.camera,
|
|
73
|
+
width: args.width,
|
|
74
|
+
height: args.height,
|
|
75
|
+
samples: args.samples,
|
|
76
|
+
maxTimeSeconds: args.maxTimeSeconds,
|
|
77
|
+
}));
|
|
78
|
+
const previewResult = preview.structuredContent;
|
|
79
|
+
const combined = {
|
|
80
|
+
...previewResult,
|
|
81
|
+
data: {
|
|
82
|
+
...baseData,
|
|
83
|
+
previewEmbedded: previewResult.ok,
|
|
84
|
+
preview: previewResult.data && typeof previewResult.data === "object"
|
|
85
|
+
? previewResult.data.preview
|
|
86
|
+
: null,
|
|
87
|
+
},
|
|
88
|
+
outputFiles: uniquePaths([copiedPath, ...previewResult.outputFiles]),
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
...preview,
|
|
92
|
+
content: [
|
|
93
|
+
{ type: "text", text: JSON.stringify(combined, null, 2) },
|
|
94
|
+
...preview.content.filter((item) => item.type === "image"),
|
|
95
|
+
],
|
|
96
|
+
structuredContent: { ...combined },
|
|
97
|
+
isError: !combined.ok,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const message = errorMessage(error);
|
|
102
|
+
const suggestions = syncSuggestions(message);
|
|
103
|
+
return toolResponse(localFailure(message, {
|
|
104
|
+
outputFiles: copiedPath ? [copiedPath] : [],
|
|
105
|
+
suggestions,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export async function resolveSavedScene(sourcePath) {
|
|
110
|
+
const resolved = path.resolve(sourcePath);
|
|
111
|
+
let stat;
|
|
112
|
+
try {
|
|
113
|
+
stat = await fsp.stat(resolved);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new Error(`Saved KeyShot scene path not found: ${resolved}`);
|
|
117
|
+
}
|
|
118
|
+
if (stat.isFile()) {
|
|
119
|
+
if (!isBip(resolved))
|
|
120
|
+
throw new Error(`Saved KeyShot scene must use the .bip extension: ${resolved}`);
|
|
121
|
+
return resolved;
|
|
122
|
+
}
|
|
123
|
+
if (!stat.isDirectory())
|
|
124
|
+
throw new Error(`Saved KeyShot scene path is not a file or directory: ${resolved}`);
|
|
125
|
+
const candidates = await Promise.all((await fsp.readdir(resolved, { withFileTypes: true }))
|
|
126
|
+
.filter((entry) => entry.isFile() && isBip(entry.name))
|
|
127
|
+
.map(async (entry) => {
|
|
128
|
+
const filePath = path.join(resolved, entry.name);
|
|
129
|
+
return { filePath, stat: await fsp.stat(filePath) };
|
|
130
|
+
}));
|
|
131
|
+
candidates.sort((left, right) => right.stat.mtimeMs - left.stat.mtimeMs ||
|
|
132
|
+
left.filePath.localeCompare(right.filePath));
|
|
133
|
+
if (!candidates[0])
|
|
134
|
+
throw new Error(`No saved .bip scene was found in directory: ${resolved}`);
|
|
135
|
+
return candidates[0].filePath;
|
|
136
|
+
}
|
|
137
|
+
async function describeFile(filePath) {
|
|
138
|
+
const stat = await fsp.stat(filePath);
|
|
139
|
+
const hash = crypto.createHash("sha256");
|
|
140
|
+
await new Promise((resolve, reject) => {
|
|
141
|
+
const input = fs.createReadStream(filePath);
|
|
142
|
+
input.on("data", (chunk) => hash.update(chunk));
|
|
143
|
+
input.on("error", reject);
|
|
144
|
+
input.on("end", resolve);
|
|
145
|
+
});
|
|
146
|
+
const sha256 = hash.digest("hex");
|
|
147
|
+
return {
|
|
148
|
+
size: stat.size,
|
|
149
|
+
modifiedMs: stat.mtimeMs,
|
|
150
|
+
modifiedAt: stat.mtime.toISOString(),
|
|
151
|
+
sha256,
|
|
152
|
+
fingerprint: `sha256:${sha256}`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async function allocateSyncPath(config, sourcePath) {
|
|
156
|
+
const stem = path.parse(sourcePath).name;
|
|
157
|
+
for (let sequence = 1; sequence < 10_000; sequence += 1) {
|
|
158
|
+
const suffix = sequence === 1 ? "" : `-${sequence}`;
|
|
159
|
+
const candidate = await normalizeOutputPath(config, `${stem}-synced${suffix}.bip`);
|
|
160
|
+
if (!(await exists(candidate)))
|
|
161
|
+
return candidate;
|
|
162
|
+
}
|
|
163
|
+
throw new Error("Could not find an available synchronized scene name after 9999 attempts.");
|
|
164
|
+
}
|
|
165
|
+
function syncSuggestions(error) {
|
|
166
|
+
const normalized = error.toLowerCase();
|
|
167
|
+
if (normalized.includes("changed while it was being copied")) {
|
|
168
|
+
return [
|
|
169
|
+
"Wait until KeyShot finishes saving the scene, then call keyshot_sync_saved_scene again.",
|
|
170
|
+
];
|
|
171
|
+
}
|
|
172
|
+
if (normalized.includes("no saved .bip") ||
|
|
173
|
+
normalized.includes("not found")) {
|
|
174
|
+
return [
|
|
175
|
+
"Save the scene as a .bip file, then provide that file path or its containing folder.",
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
return localFailure(error).suggestions ?? [];
|
|
179
|
+
}
|
|
180
|
+
function isBip(value) {
|
|
181
|
+
return path.extname(value).toLowerCase() === ".bip";
|
|
182
|
+
}
|
|
183
|
+
function samePath(left, right) {
|
|
184
|
+
const normalize = (value) => process.platform === "win32"
|
|
185
|
+
? path.resolve(value).toLowerCase()
|
|
186
|
+
: path.resolve(value);
|
|
187
|
+
return normalize(left) === normalize(right);
|
|
188
|
+
}
|
|
189
|
+
async function exists(value) {
|
|
190
|
+
try {
|
|
191
|
+
await fsp.lstat(value);
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function uniquePaths(values) {
|
|
199
|
+
return [...new Set(values)];
|
|
200
|
+
}
|
|
201
|
+
function errorMessage(error) {
|
|
202
|
+
return error instanceof Error ? error.message : String(error);
|
|
203
|
+
}
|
package/dist/schemas.js
CHANGED
|
@@ -111,6 +111,45 @@ export const previewRenderInputSchema = previewRenderSchema.superRefine((value,
|
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
});
|
|
114
|
+
export const syncSavedSceneSchema = z.object({
|
|
115
|
+
sourcePath: requiredPath("Absolute path to a saved .bip scene or a directory whose newest .bip scene should be synchronized. Directories are searched only at their top level."),
|
|
116
|
+
previousFingerprint: z
|
|
117
|
+
.string()
|
|
118
|
+
.min(1)
|
|
119
|
+
.describe("Optional fingerprint returned by an earlier sync. When it still matches, no copy or preview is created.")
|
|
120
|
+
.optional(),
|
|
121
|
+
outputScenePath: optionalPath("Optional destination for the synchronized .bip copy inside KEYSHOT_OUTPUT_DIR. Existing explicit paths are never overwritten; when omitted, a collision-safe name is generated."),
|
|
122
|
+
includePreview: z
|
|
123
|
+
.boolean()
|
|
124
|
+
.describe("Whether to render and embed a temporary PNG from the synchronized copy. Defaults to true.")
|
|
125
|
+
.optional(),
|
|
126
|
+
camera: z
|
|
127
|
+
.string()
|
|
128
|
+
.min(1)
|
|
129
|
+
.describe("Optional saved camera name for the embedded preview. Omit to use the scene's active camera.")
|
|
130
|
+
.optional(),
|
|
131
|
+
width: previewRenderSchema.shape.width,
|
|
132
|
+
height: previewRenderSchema.shape.height,
|
|
133
|
+
samples: previewRenderSchema.shape.samples,
|
|
134
|
+
maxTimeSeconds: previewRenderSchema.shape.maxTimeSeconds,
|
|
135
|
+
});
|
|
136
|
+
export const syncSavedSceneInputSchema = syncSavedSceneSchema.superRefine((value, context) => {
|
|
137
|
+
if (value.samples !== undefined && value.maxTimeSeconds !== undefined) {
|
|
138
|
+
context.addIssue({
|
|
139
|
+
code: z.ZodIssueCode.custom,
|
|
140
|
+
message: "Choose either samples or maxTimeSeconds, not both.",
|
|
141
|
+
path: ["maxTimeSeconds"],
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (value.outputScenePath !== undefined &&
|
|
145
|
+
!value.outputScenePath.toLowerCase().endsWith(".bip")) {
|
|
146
|
+
context.addIssue({
|
|
147
|
+
code: z.ZodIssueCode.custom,
|
|
148
|
+
message: "Synchronized outputScenePath must use the .bip extension.",
|
|
149
|
+
path: ["outputScenePath"],
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
});
|
|
114
153
|
export const batchRenderSchema = z.object({
|
|
115
154
|
scenePath,
|
|
116
155
|
outputDir: outputDirectory,
|
package/dist/server.js
CHANGED
|
@@ -10,7 +10,8 @@ import { VERSION } from "./version.js";
|
|
|
10
10
|
import { applyRenderQuality } from "./quality.js";
|
|
11
11
|
import { runKeyShotDiagnostics } from "./diagnostics.js";
|
|
12
12
|
import { renderPreview } from "./preview.js";
|
|
13
|
-
import {
|
|
13
|
+
import { syncSavedScene } from "./save-sync.js";
|
|
14
|
+
import { applyCameraPresetTool, applyMaterialPresetTool, applyMaterialTool, batchRenderTool, importModelTool, inspectSceneTool, listCameraPresetsTool, listCamerasTool, listMaterialPresetsTool, previewRenderTool, productRenderTool, registrationOptions, renderAllCamerasTool, renderQueueTool, renderTool, saveSceneTool, setCameraTool, setEnvironmentTool, statusTool, syncSavedSceneTool, } from "./tools/catalog.js";
|
|
14
15
|
import { applyMaterialSchema, applyMaterialPresetSchema, batchRenderInputSchema, productRenderSchema, renderQueueInputSchema, renderAllCamerasInputSchema, renderInputSchema, scenePathSchema, setCameraSchema, } from "./schemas.js";
|
|
15
16
|
function errorMessage(error) {
|
|
16
17
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -34,6 +35,9 @@ export function createKeyShotServer(config = getConfig()) {
|
|
|
34
35
|
"2. The MCP client sends a structured tool call to this server.",
|
|
35
36
|
"3. This server runs a temporary Python script through KeyShot headless.",
|
|
36
37
|
"4. KeyShot writes images or scene files and returns structured JSON results.",
|
|
38
|
+
"5. Stable tools operate on saved files; they do not attach to the currently open, unsaved KeyShot GUI session.",
|
|
39
|
+
"6. Reason: a persistent bridge tested through KeyShot Script Runner keeps the runner active and blocks normal GUI interaction, while the documented scripting API does not expose a supported non-blocking GUI service or main-thread callback lifecycle.",
|
|
40
|
+
"7. If realtime GUI control is requested, explain this boundary, ask the user to save the scene, and offer to process a safe copy and return a preview.",
|
|
37
41
|
].join("\n"),
|
|
38
42
|
},
|
|
39
43
|
],
|
|
@@ -89,6 +93,7 @@ export function createKeyShotServer(config = getConfig()) {
|
|
|
89
93
|
...args,
|
|
90
94
|
})));
|
|
91
95
|
server.registerTool("keyshot_preview_render", registrationOptions(previewRenderTool), async (args) => renderPreview(config, args));
|
|
96
|
+
server.registerTool("keyshot_sync_saved_scene", registrationOptions(syncSavedSceneTool), async (args) => syncSavedScene(config, args));
|
|
92
97
|
server.registerTool("keyshot_render", registrationOptions(renderTool), async (args) => {
|
|
93
98
|
const parsed = renderInputSchema.parse(args);
|
|
94
99
|
return toolResponse(await runKeyShotSerialized(config, {
|
package/dist/tools/catalog.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { keyShotResultSchema } from "../result.js";
|
|
2
|
-
import { applyCameraPresetSchema, applyMaterialInputSchema, applyMaterialPresetInputSchema, batchRenderSchema, importModelSchema, listCameraPresetsSchema, listCamerasSchema, listMaterialPresetsSchema, previewRenderSchema, productRenderInputSchema, renderAllCamerasSchema, renderQueueSchema, renderSchema, saveSceneSchema, scenePathSchema, setCameraInputSchema, setEnvironmentSchema, } from "../schemas.js";
|
|
2
|
+
import { applyCameraPresetSchema, applyMaterialInputSchema, applyMaterialPresetInputSchema, batchRenderSchema, importModelSchema, listCameraPresetsSchema, listCamerasSchema, listMaterialPresetsSchema, previewRenderSchema, productRenderInputSchema, renderAllCamerasSchema, renderQueueSchema, renderSchema, saveSceneSchema, scenePathSchema, setCameraInputSchema, setEnvironmentSchema, syncSavedSceneSchema, } from "../schemas.js";
|
|
3
3
|
export const readOnlyAnnotations = {
|
|
4
4
|
readOnlyHint: true,
|
|
5
5
|
destructiveHint: false,
|
|
@@ -62,6 +62,15 @@ export const previewRenderTool = defineTool({
|
|
|
62
62
|
outputSchema: keyShotResultSchema,
|
|
63
63
|
annotations: readOnly,
|
|
64
64
|
});
|
|
65
|
+
export const syncSavedSceneTool = defineTool({
|
|
66
|
+
name: "keyshot_sync_saved_scene",
|
|
67
|
+
group: "Inspection",
|
|
68
|
+
title: "Sync the latest saved KeyShot scene",
|
|
69
|
+
description: "Find a saved .bip file (or the newest .bip in one folder), detect whether it changed, copy it to a collision-safe output path, and optionally return an Agent-visible preview. This is the stable alternative to unsupported persistent GUI control.",
|
|
70
|
+
inputSchema: syncSavedSceneSchema,
|
|
71
|
+
outputSchema: keyShotResultSchema,
|
|
72
|
+
annotations: writesOutput,
|
|
73
|
+
});
|
|
65
74
|
export const renderTool = defineTool({
|
|
66
75
|
name: "keyshot_render",
|
|
67
76
|
group: "Rendering",
|
|
@@ -185,6 +194,7 @@ export const TOOL_CATALOG = [
|
|
|
185
194
|
inspectSceneTool,
|
|
186
195
|
listCamerasTool,
|
|
187
196
|
previewRenderTool,
|
|
197
|
+
syncSavedSceneTool,
|
|
188
198
|
renderTool,
|
|
189
199
|
renderQueueTool,
|
|
190
200
|
batchRenderTool,
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.11.0";
|
package/docs/TOOLS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Generated from `src/tools/catalog.ts`. Do not edit this file by hand.
|
|
4
4
|
|
|
5
|
-
KeyShot MCP exposes
|
|
5
|
+
KeyShot MCP exposes 19 tools. All tools return JSON text and structured content using the common result schema.
|
|
6
6
|
|
|
7
7
|
## Diagnostics
|
|
8
8
|
|
|
@@ -180,6 +180,26 @@ Safety: read-only; non-destructive; local only.
|
|
|
180
180
|
| ----------- | -------- | ------ | -------------------------------------------------------------------------------------------------------------------- |
|
|
181
181
|
| `scenePath` | Yes | string | Absolute path to an existing KeyShot scene file to open. Input files may be outside the configured output directory. |
|
|
182
182
|
|
|
183
|
+
### `keyshot_sync_saved_scene`
|
|
184
|
+
|
|
185
|
+
**Sync the latest saved KeyShot scene**
|
|
186
|
+
|
|
187
|
+
Find a saved .bip file (or the newest .bip in one folder), detect whether it changed, copy it to a collision-safe output path, and optionally return an Agent-visible preview. This is the stable alternative to unsupported persistent GUI control.
|
|
188
|
+
|
|
189
|
+
Safety: writes output; potentially destructive; local only.
|
|
190
|
+
|
|
191
|
+
| Parameter | Required | Type | Description |
|
|
192
|
+
| --------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
193
|
+
| `sourcePath` | Yes | string | Absolute path to a saved .bip scene or a directory whose newest .bip scene should be synchronized. Directories are searched only at their top level. |
|
|
194
|
+
| `previousFingerprint` | No | string | Optional fingerprint returned by an earlier sync. When it still matches, no copy or preview is created. |
|
|
195
|
+
| `outputScenePath` | No | string | Optional destination for the synchronized .bip copy inside KEYSHOT_OUTPUT_DIR. Existing explicit paths are never overwritten; when omitted, a collision-safe name is generated. |
|
|
196
|
+
| `includePreview` | No | boolean | Whether to render and embed a temporary PNG from the synchronized copy. Defaults to true. |
|
|
197
|
+
| `camera` | No | string | Optional saved camera name for the embedded preview. Omit to use the scene's active camera. |
|
|
198
|
+
| `width` | No | number | Preview width in pixels. Defaults to 960; allowed range is 64 to 1920. |
|
|
199
|
+
| `height` | No | number | Preview height in pixels. Defaults to 540; allowed range is 64 to 1080. |
|
|
200
|
+
| `samples` | No | number | Preview render samples. Defaults to 16 unless maxTimeSeconds is provided; maximum is 64. |
|
|
201
|
+
| `maxTimeSeconds` | No | number | Time-based preview limit in seconds. Replaces the default sample mode and cannot be combined with an explicit samples value; maximum is 60. |
|
|
202
|
+
|
|
183
203
|
## Scene editing
|
|
184
204
|
|
|
185
205
|
### `keyshot_import_model`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keyshot-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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",
|
|
@@ -32,9 +32,15 @@
|
|
|
32
32
|
"keywords": [
|
|
33
33
|
"mcp",
|
|
34
34
|
"model-context-protocol",
|
|
35
|
+
"ai-agent",
|
|
35
36
|
"keyshot",
|
|
37
|
+
"keyshot-mcp",
|
|
36
38
|
"rendering",
|
|
39
|
+
"3d-rendering",
|
|
40
|
+
"product-rendering",
|
|
41
|
+
"headless-rendering",
|
|
37
42
|
"keyshot-studio",
|
|
43
|
+
"industrial-design",
|
|
38
44
|
"product-visualization",
|
|
39
45
|
"camera-control",
|
|
40
46
|
"stdio"
|
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.11.0",
|
|
13
13
|
"packages": [
|
|
14
14
|
{
|
|
15
15
|
"registryType": "npm",
|
|
16
16
|
"identifier": "keyshot-mcp",
|
|
17
|
-
"version": "0.
|
|
17
|
+
"version": "0.11.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.11.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# KeyShot MCP
|
|
@@ -20,13 +20,22 @@ Respond in the user's language and explain errors in plain, non-technical terms.
|
|
|
20
20
|
- Do not overwrite an explicit output path unless the user clearly requests it.
|
|
21
21
|
- Use scene copies for editing workflows; do not modify customer source files in place.
|
|
22
22
|
- Do not execute arbitrary Python or simulate KeyShot GUI actions.
|
|
23
|
+
- When the user asks to control the current KeyShot window, selected GUI object,
|
|
24
|
+
or unsaved scene in realtime, explain the GUI boundary before requesting a file:
|
|
25
|
+
the stable server uses KeyShot headless on saved scenes. A persistent GUI bridge
|
|
26
|
+
was prototyped, but KeyShot Script Runner remains active for the lifetime of the
|
|
27
|
+
script and blocks normal GUI interaction. The public scripting API does not
|
|
28
|
+
provide a documented non-blocking GUI service or main-thread callback for safely
|
|
29
|
+
hosting that bridge. Do not describe this as a missing MCP feature.
|
|
30
|
+
- After explaining the boundary, ask the user to save the scene and offer to edit
|
|
31
|
+
a safe copy, return an Agent-visible preview, and preserve the source file.
|
|
23
32
|
|
|
24
33
|
## Installation workflow
|
|
25
34
|
|
|
26
35
|
1. Confirm Node.js 20 or newer is available.
|
|
27
36
|
2. Confirm KeyShot is installed and locally licensed.
|
|
28
37
|
3. Locate `keyshot_headless.exe`. Prefer its absolute path.
|
|
29
|
-
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.11.0` and set
|
|
30
39
|
`KEYSHOT_HEADLESS_EXE`.
|
|
31
40
|
5. Leave `KEYSHOT_OUTPUT_DIR` unset to use
|
|
32
41
|
`<home>/Documents/KeyShot MCP Outputs`, or set a user-approved directory.
|
|
@@ -41,7 +50,7 @@ Example configuration:
|
|
|
41
50
|
"mcpServers": {
|
|
42
51
|
"keyshot": {
|
|
43
52
|
"command": "npx",
|
|
44
|
-
"args": ["-y", "keyshot-mcp@0.
|
|
53
|
+
"args": ["-y", "keyshot-mcp@0.11.0"],
|
|
45
54
|
"env": {
|
|
46
55
|
"KEYSHOT_HEADLESS_EXE": "C:/Program Files/KeyShot Studio/bin/keyshot_headless.exe"
|
|
47
56
|
}
|
|
@@ -58,6 +67,9 @@ Example configuration:
|
|
|
58
67
|
- Use `keyshot_list_cameras` before rendering selected cameras.
|
|
59
68
|
- Use `keyshot_preview_render` after inspection so the Agent and user can review
|
|
60
69
|
composition, materials, and lighting before a standard or final render.
|
|
70
|
+
- When the user has just saved a GUI scene or asks for the newest saved work, use
|
|
71
|
+
`keyshot_sync_saved_scene`. Reuse its returned fingerprint on the next check so
|
|
72
|
+
unchanged saves do not produce duplicate copies or previews.
|
|
61
73
|
- Use `keyshot_render` for one view, `keyshot_batch_render` for selected views,
|
|
62
74
|
`keyshot_render_all_cameras` for every saved view, and `keyshot_render_queue`
|
|
63
75
|
for independent jobs from multiple scenes.
|
|
@@ -104,3 +116,9 @@ KeyShot MCP uses KeyShot headless scripting. It does not include KeyShot, a
|
|
|
104
116
|
license, proprietary materials, environments, or customer assets. Features that
|
|
105
117
|
are not exposed by the installed KeyShot headless API must be reported as
|
|
106
118
|
unsupported rather than silently ignored.
|
|
119
|
+
|
|
120
|
+
The stable tools do not attach to an open, unsaved KeyShot GUI session. If the
|
|
121
|
+
user expects realtime control, state this before running tools and attribute the
|
|
122
|
+
limitation accurately to the currently documented KeyShot GUI scripting execution
|
|
123
|
+
model. Do not imply that installing or configuring the MCP differently will enable
|
|
124
|
+
realtime GUI control.
|