@kolbo/mcp 1.87.10 → 1.87.11
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/package.json +1 -1
- package/src/tools/blender.js +62 -57
package/package.json
CHANGED
package/src/tools/blender.js
CHANGED
|
@@ -56,20 +56,25 @@ const idempotencyKey = z.string()
|
|
|
56
56
|
.regex(/^[A-Za-z0-9._:-]+$/)
|
|
57
57
|
.optional()
|
|
58
58
|
.describe('Optional replay-safe key. Reuse it only when retrying the same command.');
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
// Every shared piece below is a FACTORY, not a shared instance. zod-to-json-schema
|
|
60
|
+
// de-duplicates repeated zod instances into JSON-pointer `$ref`s, and this tool
|
|
61
|
+
// reuses name/vector/color dozens of times inside one discriminated union, so the
|
|
62
|
+
// emitted inputSchema carried 54 `$ref`s, draft-07 tuple `items: [...]` arrays and
|
|
63
|
+
// `const` discriminators. ChatGPT's app "Scan Tools" rejects all three
|
|
64
|
+
// ("Invalid MCP tool schema for tool 'blender_apply_operations'", 2026-09-06),
|
|
65
|
+
// which blocks re-scanning the whole Kolbo app. Fresh instances + fixed-length
|
|
66
|
+
// arrays + single-value enums keep the schema inside the plain subset every host
|
|
67
|
+
// accepts. Validation semantics are unchanged.
|
|
68
|
+
const name = () => noControls(128);
|
|
69
|
+
const num = () => z.number().finite().min(-MAX_ABSOLUTE_NUMBER).max(MAX_ABSOLUTE_NUMBER);
|
|
70
|
+
const vector = () => z.array(num()).length(3);
|
|
71
|
+
const color = () => z.array(z.number().finite().min(0).max(1)).min(3).max(4);
|
|
72
|
+
const propertyScalar = () => z.union([
|
|
68
73
|
noControls(256),
|
|
69
|
-
|
|
74
|
+
num(),
|
|
70
75
|
z.boolean(),
|
|
71
76
|
]);
|
|
72
|
-
const properties = z.record(z.union([propertyScalar, z.array(propertyScalar).max(16)]))
|
|
77
|
+
const properties = () => z.record(z.union([propertyScalar(), z.array(propertyScalar()).max(16)]))
|
|
73
78
|
.refine((value) => Object.keys(value).length <= 30, 'A modifier may contain at most 30 properties.')
|
|
74
79
|
.refine(
|
|
75
80
|
(value) => Object.keys(value).every((key) => SAFE_PROPERTY_KEY.test(key) && !BLOCKED_PROPERTY_KEYS.has(key)),
|
|
@@ -79,81 +84,81 @@ const properties = z.record(z.union([propertyScalar, z.array(propertyScalar).max
|
|
|
79
84
|
(value) => Buffer.byteLength(JSON.stringify(value), 'utf8') <= 16 * 1024,
|
|
80
85
|
'Modifier properties must be at most 16 KiB as JSON.',
|
|
81
86
|
);
|
|
82
|
-
const transform = {
|
|
83
|
-
location: vector.optional(),
|
|
84
|
-
rotation_euler: vector.optional(),
|
|
85
|
-
scale: vector.optional(),
|
|
86
|
-
};
|
|
87
|
+
const transform = () => ({
|
|
88
|
+
location: vector().optional(),
|
|
89
|
+
rotation_euler: vector().optional(),
|
|
90
|
+
scale: vector().optional(),
|
|
91
|
+
});
|
|
92
|
+
const op = (value) => z.enum([value]);
|
|
87
93
|
const operation = z.discriminatedUnion('op', [
|
|
88
94
|
z.object({
|
|
89
|
-
op:
|
|
90
|
-
name: name.optional(),
|
|
95
|
+
op: op('object.create'),
|
|
96
|
+
name: name().optional(),
|
|
91
97
|
type: z.enum(['CUBE', 'SPHERE', 'CYLINDER', 'CONE', 'PLANE', 'EMPTY', 'CAMERA', 'LIGHT']),
|
|
92
98
|
light_type: z.enum(['POINT', 'SUN', 'SPOT', 'AREA']).optional(),
|
|
93
99
|
energy: z.number().finite().min(0).max(MAX_ABSOLUTE_NUMBER).optional(),
|
|
94
|
-
color: color.optional(),
|
|
100
|
+
color: color().optional(),
|
|
95
101
|
shadow_soft_size: z.number().finite().min(0).max(MAX_ABSOLUTE_NUMBER).optional(),
|
|
96
102
|
lens: z.number().finite().min(1).max(10_000).optional(),
|
|
97
|
-
collection: name.optional(),
|
|
98
|
-
...transform,
|
|
103
|
+
collection: name().optional(),
|
|
104
|
+
...transform(),
|
|
99
105
|
}).strict(),
|
|
100
|
-
z.object({ op:
|
|
101
|
-
z.object({ op:
|
|
102
|
-
z.object({ op:
|
|
106
|
+
z.object({ op: op('object.transform'), object: name(), ...transform() }).strict(),
|
|
107
|
+
z.object({ op: op('object.rename'), object: name(), new_name: name() }).strict(),
|
|
108
|
+
z.object({ op: op('object.delete'), object: name() }).strict(),
|
|
103
109
|
z.object({
|
|
104
|
-
op:
|
|
105
|
-
object: name,
|
|
106
|
-
new_name: name.optional(),
|
|
107
|
-
collection: name.optional(),
|
|
108
|
-
...transform,
|
|
110
|
+
op: op('object.duplicate'),
|
|
111
|
+
object: name(),
|
|
112
|
+
new_name: name().optional(),
|
|
113
|
+
collection: name().optional(),
|
|
114
|
+
...transform(),
|
|
109
115
|
}).strict(),
|
|
110
|
-
z.object({ op:
|
|
111
|
-
z.object({ op:
|
|
112
|
-
z.object({ op:
|
|
113
|
-
z.object({ op:
|
|
116
|
+
z.object({ op: op('object.set_parent'), object: name(), parent: name().nullable().optional() }).strict(),
|
|
117
|
+
z.object({ op: op('collection.create'), name: name(), parent: name().nullable().optional() }).strict(),
|
|
118
|
+
z.object({ op: op('collection.delete'), collection: name() }).strict(),
|
|
119
|
+
z.object({ op: op('collection.link_object'), collection: name(), object: name() }).strict(),
|
|
114
120
|
z.object({
|
|
115
|
-
op:
|
|
116
|
-
|
|
117
|
-
base_color: color.optional(),
|
|
121
|
+
op: op('material.create'), name: name(),
|
|
122
|
+
base_color: color().optional(),
|
|
118
123
|
roughness: z.number().min(0).max(1).optional(),
|
|
119
124
|
metallic: z.number().min(0).max(1).optional(),
|
|
120
125
|
alpha: z.number().min(0).max(1).optional(),
|
|
121
|
-
emission_color: color.optional(),
|
|
126
|
+
emission_color: color().optional(),
|
|
122
127
|
emission_strength: z.number().finite().min(0).max(MAX_ABSOLUTE_NUMBER).optional(),
|
|
123
128
|
}).strict(),
|
|
124
|
-
z.object({ op:
|
|
129
|
+
z.object({ op: op('material.assign'), object: name(), material: name(), replace: z.boolean().optional() }).strict(),
|
|
125
130
|
z.object({
|
|
126
|
-
op:
|
|
127
|
-
material: name,
|
|
128
|
-
base_color: color.optional(),
|
|
131
|
+
op: op('material.set_principled'),
|
|
132
|
+
material: name(),
|
|
133
|
+
base_color: color().optional(),
|
|
129
134
|
roughness: z.number().min(0).max(1).optional(),
|
|
130
135
|
metallic: z.number().min(0).max(1).optional(),
|
|
131
136
|
alpha: z.number().min(0).max(1).optional(),
|
|
132
|
-
emission_color: color.optional(),
|
|
137
|
+
emission_color: color().optional(),
|
|
133
138
|
emission_strength: z.number().finite().min(0).max(MAX_ABSOLUTE_NUMBER).optional(),
|
|
134
139
|
}).strict(),
|
|
135
|
-
z.object({ op:
|
|
140
|
+
z.object({ op: op('world.set_color'), color: color() }).strict(),
|
|
136
141
|
z.object({
|
|
137
|
-
op:
|
|
138
|
-
object: name,
|
|
142
|
+
op: op('modifier.add'),
|
|
143
|
+
object: name(),
|
|
139
144
|
type: z.string().regex(SAFE_MODIFIER_TYPE),
|
|
140
|
-
name: name.optional(),
|
|
141
|
-
properties: properties.optional(),
|
|
145
|
+
name: name().optional(),
|
|
146
|
+
properties: properties().optional(),
|
|
142
147
|
}).strict(),
|
|
143
|
-
z.object({ op:
|
|
144
|
-
z.object({ op:
|
|
145
|
-
z.object({ op:
|
|
148
|
+
z.object({ op: op('modifier.configure'), object: name(), modifier: name(), properties: properties() }).strict(),
|
|
149
|
+
z.object({ op: op('modifier.remove'), object: name(), modifier: name() }).strict(),
|
|
150
|
+
z.object({ op: op('camera.set_active'), object: name() }).strict(),
|
|
146
151
|
z.object({
|
|
147
|
-
op:
|
|
148
|
-
object: name,
|
|
149
|
-
data_path: name,
|
|
152
|
+
op: op('animation.keyframe_insert'),
|
|
153
|
+
object: name(),
|
|
154
|
+
data_path: name(),
|
|
150
155
|
frame: z.number().int().min(-1_048_574).max(1_048_574),
|
|
151
156
|
index: z.number().int().min(-1).max(1024).optional(),
|
|
152
157
|
}).strict(),
|
|
153
158
|
z.object({
|
|
154
|
-
op:
|
|
155
|
-
object: name,
|
|
156
|
-
data_path: name,
|
|
159
|
+
op: op('animation.delete_keyframe'),
|
|
160
|
+
object: name(),
|
|
161
|
+
data_path: name(),
|
|
157
162
|
frame: z.number().int().min(-1_048_574).max(1_048_574),
|
|
158
163
|
index: z.number().int().min(-1).max(1024).optional(),
|
|
159
164
|
}).strict(),
|
|
@@ -316,7 +321,7 @@ function registerBlenderTools(server, client) {
|
|
|
316
321
|
.optional(),
|
|
317
322
|
kind: z.enum(['model', '3d', 'glb', 'image', 'video']).optional(),
|
|
318
323
|
import_mode: z.enum(['plane', 'active_material', 'world', 'sequencer', 'collection']).optional(),
|
|
319
|
-
name: name.optional(),
|
|
324
|
+
name: name().optional(),
|
|
320
325
|
idempotency_key: idempotencyKey,
|
|
321
326
|
},
|
|
322
327
|
async (args) => {
|