3dCMS
Ce que fait ce MCP
Creates, edits, previews, and publishes interactive 3D scenes containing pages, models, text, materials, and assets.
Outils
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['sceneId', 'operations'], 'properties': {'sceneId': {'type': 'string', 'description': 'Scene id from list_scenes.'}, 'operations': {'type': 'array', 'items': {'type': 'object', 'required': ['op'], 'properties': {'id': {'type': 'string', 'description': 'Target node id from get_scene, or a tempId minted earlier in this batch. Required for duplicate/reparent; update, assignMaterial and delete take either `id` or `where`.'}, 'op': {'type': 'string', 'description': 'Which kind of edit. `scatter` makes many jittered copies of a node across an area (set-dressing: debris, foliage, props). `make_page` is a COMPOSITE that builds a whole page in one op (webpage surface + hero h1 + body paragraph + link "signs") — prefer it over hand-assembling a page from primitives. `make_diagram` is a COMPOSITE that builds a whole flowchart / mind map from `nodes` + `edges`, laid out for you — never place diagram boxes by hand. `settings` edits the SCENE rather than its nodes — the post-processing look, the sun and time of day, fog, the audio bus, editor preferences, the scene `name` and its `activeCameraId` — via `viewport`, `lighting`, `environment`, `audio` and `ui`; it is the only way to reach any of them.'}, 'ui': {'type': 'object', 'description': 'For settings: editor preferences stored on the scene. {density: comfy|compact|ultra, theme: auto|light|dark, polyWheelTheme, reduceMotion, showStatusBar, showHistoryHud, showMobileViewportHints, gizmoSize/Thickness Desktop+Mobile, longPressDurationMs, longPressMoveTolerance, tapFrameWindowMs}. These travel with the scene but change nothing a visitor sees.', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'ids': {'type': 'array', 'items': {'type': 'string'}, 'description': 'For group: the node ids to gather under a new group.'}, 'area': {'type': 'object', 'properties': {'x': {'type': 'number'}, 'z': {'type': 'number'}}, 'description': 'For scatter: half-extents (meters) of the XZ area to spread copies across, centered on the source. Default 5 each. Large = a lot strewn over a parking lot; small = a tight cluster.', 'additionalProperties': {}}, 'body': {'type': 'string', 'description': 'For make_page: a paragraph of body text placed under the hero.'}, 'data': {'type': 'object', 'description': 'Type-specific fields to merge: geometry dimensions, under the SAME keys get_scene reports as `size` (so a size you read is a size you can write back): box {width,height,depth} (default 1.5 each), plane {width,height}, sphere {radius}, cylinder {radius,height}, torus {radius,tube}; there is no `size`, `w`, `h` or `d` key and one is REFUSED by name rather than stored. A glb model is {primitive:"imported", assetUrl} (see IMPORTED MODELS). CUT a primitive (take part of it), ALL ANGLES IN RADIANS: sphere {thetaStart (down from the pole, 0..PI), thetaLength (sweep, capped at PI - thetaStart), phiStart, phiLength (around, 0..2PI)}, a hemisphere/dome is {thetaStart:0, thetaLength:1.5708}; a WALL or CEILING that must block a lamp is a THIN BOX ({width, height, depth:0.05}), not a plane: a plane shows one face and casts a shadow only from BEHIND, so a shadow-casting lamp in front of it shines straight through; cylinder {cylinderThetaStart, cylinderThetaLength (PI = half-pipe), cylinderOpenEnded:true (a tube, no caps)}; torus {torusArc (PI = horseshoe)}. A cut renders double-sided and costs the SAME triangles as the whole (only openEnded removes faces). text {text,color,fontSize, fontWeight:400|500|600|700, outline:true|false, outlineColor, outlineWidth}. webpage (a page/neighborhood surface) {pageTitle, slug, isHomePage, accentColor, clusterLabel, showPanel}. showPanel:false removes the page\'s white card (background plane, accent strip, LOD stand-in) while its text, links and children still render: use it when a page is a ROOM or an anchor rather than a billboard, instead of burying the page underground. The card is floored at 3.6 x 2.6 m otherwise. backgroundLayer (env-sphere backdrop centred on camera) {kind:gradient|color|image, color, color2, src, tint, height (world-Y offset; negative=horizon lower), coverage (band thickness), headingDeg, opacity}. light {lightType:point|spot|area, color, intensity, distance, decay, angle, penumbra, width, height, castShadow, shadowMapSize:512|1024|2048, timeMode, goboPattern, goboSrc, goboAnim, goboSpeed, goboSpin, volumetric, volumetricDensity} (see the light notes on nodeType). Web blocks: webText {text, role:h1|h2|h3|body|caption, fontWeight:400|500|600|700, outline:true|false, outlineColor, outlineWidth}; webImage {src, alt}; webLink {label, fontWeight, href|linkedPageId (jump to a page) | linkedCameraId (jump to a viewpoint) (href "#share" opens the share sheet of the visitor for the page they are on, the "send this to someone" button; a mailto may leave the recipient blank, mailto:?subject=...&body=..., so the visitor picks one), bgColor (the chip, the BUTTON colour; the accent on a primary action goes here), color (the LABEL text on the chip, not the button), textureUrl (a same-origin /assets image mapped onto the chip - use list_assets to find one, and bgColor tints it), castShadow (the chip joins the shadow pass so it reads as an object ON a surface rather than an overlay; needs a light with castShadow), roughness}; webVideo {src, poster}; any web block also accepts {anchor:world|billboard|hud}.', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'hero': {'type': 'string', 'description': 'For make_page: the hero heading text — becomes an h1 webText on the page.'}, 'name': {'type': 'string', 'description': 'Display name for a created node, or a rename. On duplicate: the copy\'s name (default "<source> copy"; with a count, "<name> 1", "<name> 2", ...).'}, 'seed': {'type': 'number', 'description': 'For scatter: integer RNG seed for reproducible placement (optional).'}, 'step': {'type': 'array', 'items': {'type': 'number'}, 'description': 'For duplicate with a count: [x,y,z] meters added per successive copy, so sixteen identical wall units 4.8 m apart are ONE op: {"op":"duplicate","id":"wall","count":16,"step":[4.8,0,0]}. Default [0,0,0] (copies stack in place).'}, 'tint': {'type': 'string', 'description': 'Hex color. On scatter, a base per-copy tint for imported/generated (assetRef) copies, with slight random variation (rust/foliage/grime). No effect on solid-color primitives.'}, 'audio': {'type': 'object', 'description': 'For settings: the scene audio bus. {masterLevel 0-1, ambientLevel 0-1, ambientMuted}.', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'color': {'type': 'string', 'description': 'Hex color like "#ff3344". On add or assignMaterial, mints-or-reuses a solid-color material and assigns it — no pre-existing material needed. A webText or webLink has no material: on add, color is the colour it shows (the text, or a link chip\'s fill; the label on a text-variant link).'}, 'count': {'type': 'number', 'description': 'For scatter (1–200) or duplicate (1–64): how many copies to make. A duplicate count lays the copies out in a straight row by `step`; scatter jitters them across `area`.'}, 'edges': {'type': 'array', 'items': {'type': 'object', 'required': ['from', 'to'], 'properties': {'to': {'type': 'string'}, 'from': {'type': 'string'}, 'label': {'type': 'string'}}}, 'description': 'For make_diagram: the connectors, each { from, to } naming node ids from `nodes`, plus an optional label drawn at the midpoint. Max 128. Cycles are fine.'}, 'links': {'type': 'array', 'items': {'type': 'object', 'properties': {'to': {'type': 'string'}, 'href': {'type': 'string'}, 'label': {'type': 'string'}}}, 'description': 'For make_page: wayfinding "sign" buttons. Each { label, and EITHER to (a page id/tempId to fly to) OR href (external URL) }.'}, 'nodes': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'label'], 'properties': {'id': {'type': 'string'}, 'body': {'type': 'string'}, 'label': {'type': 'string'}}}, 'description': 'For make_diagram: the boxes. Each { id (your own short handle, referenced by edges), label (the text on the card), body (optional smaller line under it) }. Max 64.'}, 'patch': {'type': 'object', 'description': 'For update: any of name, visible, position, rotation, scale, pivot, lookAt or lookAtNodeId (aim instead of a rotation, from the position the patch sets), plus a nested `data` object (shallow-merged). update reads them ONLY from here — a top-level position/rotation/scale/data on an update op is REJECTED, not applied.', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'pivot': {'type': 'array', 'items': {'type': 'number'}, 'description': "For add: the HINGE. The point in the primitive's own space that sits at the node's origin, so rotation swings about it: a 1 m wide door hinged on its left edge is pivot [-0.5,0,0] with position at the hinge, and a rotation track of [0,0,0] to [0,1.4,0] swings it open. Default [0,0,0] = rotate about the centre. (update takes it in patch.)"}, 'scale': {'type': 'array', 'items': {'type': 'number'}, 'description': "[x,y,z] scale. On duplicate: the copy's scale (default: the source's)."}, 'where': {'type': 'object', 'description': 'For update, assignMaterial and delete: act on MANY nodes at once instead of one by id. {type, name, nameStartsWith, nameContains, primitive, parentId (direct children only), underId (the WHOLE subtree of a node: every wall in a room group, however deep; a same-batch tempId works), materialAssetId (every node wearing that material, for a global restyle), limit} - every field given must match. Pass either `id` or `where`, never both. delete removes each match WITH its subtree and defaults to a limit of 100 (update and assignMaterial default to 500); pass where.limit to delete more on purpose. `scatter` names its copies "<source> 1", "<source> 2", so nameStartsWith is how you re-edit a scattered set without listing 60 ids. A selector matching NOTHING rejects the batch rather than quietly doing nothing, and an empty selector is refused rather than matching everything.', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'design': {'type': 'object', 'description': 'For settings: the DESIGN BRIEF, what the scene is FOR, which every design rule and design_review keys on. {intent: convert (get the visitor to do one thing) | inform | showcase | entertain | navigate, mood: calm|energetic|luxury|playful|serious|dramatic|natural, audience: one line (120 characters) on who it is for, primaryActionId: the node id or same-batch tempId of the one thing the page exists for, a sign or a tap target}. null (or "") clears a field; absent means undeclared, and the review says what it inferred instead. A webpage can carry its own data.intent and data.primaryActionId, which win for that page (add the action first, then point at it).', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'layout': {'enum': ['layered', 'radial'], 'type': 'string', 'description': 'For make_diagram: `layered` (default) stacks flowchart rows downward from the nodes nothing points at — use for processes, pipelines, org charts. `radial` puts the first root in the middle with rings of children around it — use for mind maps.'}, 'lookAt': {'type': 'array', 'items': {'type': 'number'}, 'description': "A WORLD point [x,y,z] to aim at, instead of a rotation (on add; inside patch on update). A camera looks at it with a level horizon (what the published viewer shows), a spot or area light beams at it, and a page, a web block or 3D text turns about the vertical to face it. Compose every saved viewpoint (entry, arrival, via, a sign's target) this way; never hand-compute a camera euler. With `where` on an update, every match aims at the one point: all the spots at the statue is one op."}, 'tempId': {'type': 'string', 'description': 'Batch-local handle for a node this op creates (add/duplicate/group). Later ops in the SAME batch may use it as id/parentId. On a duplicate with a count it names the FIRST copy.'}, 'spacing': {'type': 'number', 'description': 'For make_diagram: multiplier on the gaps between cards (default 1). Raise it for long labels, lower it for a compact board.'}, 'emissive': {'type': 'string', 'description': 'Beside `color`: a hex that GLOWS, added as light on top of the lit colour and visible in the dark (a lampshade, a neon tube, a screen, an ember), no texture needed. #000000 glows nothing. A lit and an unlit copy of one colour are two materials. A map set glows through materialMaps.emissive instead.'}, 'lighting': {'type': 'object', 'description': 'For settings: the SUN and the sky. {mode: time|studio, forceDaylight}; {timeOfDay: {source: live|fixed, preset: sunrise|noon|sunset|twilight|midnight|null, minutes 0-1439}}: source must be "fixed" or the preset is stored and ignored and the sky keeps following the clock on each viewer device. A preset pins the SOLAR PHASE and is re-derived for the current date on every load (sunset stays sunset all year; the sun\'s bearing still follows the season), so pass a preset alone for that; pass minutes to pin a CLOCK time instead (writing minutes clears any stored preset, or pass preset null explicitly; 18:28 is on the horizon in August and night in December); {sun: {azimuth, elevation, intensity, color, castShadow}} for studio mode; {moon: {intensity 0-4, color}} is the moonlight in time mode (1 = its natural phase-scaled strength as the key light after sunset, 0 = no moonlight while the disc still draws; the sky\'s own faint night fill stays); {env: {intensity 0-4}} is the ambient fill (0.55 default): the IBL in studio mode, and in time mode the scale of the sky\'s own fill (hemisphere plus IBL, about 0.2 by night to 0.9 by day at 0.55; never the sun or moon key). The control to reach for after pinning a low sun, since almost nothing is lit directly and a dark palette renders black (~1.25 lifts a charcoal set back); 0 leaves only the sun, the moon and the lamps, so a lamp-only night scene is a midnight pin with env 0 and moon 0.', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'nodeType': {'enum': ['geometry', 'text3d', 'nurbs', 'group', 'camera', 'particle', 'forcefield', 'light', 'shadowPool', 'cable', 'webpage', 'backgroundLayer', 'webText', 'webImage', 'webLink', 'webVideo'], 'type': 'string', 'description': 'For add: the kind of node to create. `cable` is a catenary wire sagging between data.pointA and data.pointB (node-local [x,y,z]; data.sag = midspan droop in meters, data.thickness, data.color) — power lines, festoon lights, rigging. `webpage` is a page / "neighborhood" surface (set data.pageTitle, data.slug; place pages a distance apart to make a multi-area site). `backgroundLayer` is an env-sphere backdrop centred on the camera (distant mountains, skyline, valley haze) — set data.kind gradient|color|image, colors; data.height offsets the horizon (negative=lower), data.coverage=band thickness. `light` is a placeable lamp: data.lightType point|spot|area (area = a soft glowing panel of data.width×data.height meters), data.color, data.intensity (~30), data.distance range (0=unlimited, point/spot), spot data.angle rad + data.penumbra (a DOWNWARD-ONLY bulb, the light that fills a room without washing its ceiling, is a spot with angle 1.55 and penumbra 0.6 and NO rotation: it beams straight down over a 178 deg cone, and a point light has no such flag because this is it); data.castShadow true (max 4 lights cast, scene order; on an area light it adds a soft grounding shadow since the panel casts none itself; ⚠ a shadow light is PRICED as if it re-drew the whole scene, one 1024 spot roughly doubles the geometry + draw score, so for an object that merely needs to look grounded use a `shadowPool` instead, which is nearly free); data.shadowMapSize 512|1024|2048 sets that light\'s shadow map resolution (default: spot 1024, point 512 per cube face, area 1024; a point light caps at 1024): the price is the TEXELS, so a 512 spot costs a quarter of the default and a 2048 four times it, and 512 is the right call for a soft grounding shadow under a small object while 2048 is for one crisp hero shadow; data.timeMode always|night|day gates it by the live sky (night = streetlamp). A spot and an area panel beam down local -Y — rotate the node to aim. GOBO (a projected cookie) + VOLUMETRIC lit air, on point and spot only (never area): data.goboPattern none|dapple (sun through blowing leaves)|blinds|windowpane|caustics (water light)|custom (with data.goboSrc, an image or video URL; a video makes a moving projector); data.goboAnim none|drift MOVES the pattern (slow pan + spin, the leaves stir): data.goboSpeed 0-1 uv/s (0.03), data.goboSpin -3..3 rad/s (0); data.volumetric true renders the beam as lit air (light shafts), data.volumetricDensity 0-3 (0.6). On a spot the cookie hits lit surfaces AND the cone shafts; a point light has no surface cookie, its pattern lives inside a spherical glow and shows ONLY with volumetric true. With castShadow, real geometry carves dark shafts through the beam. Dappled light through a canopy is one spot: {lightType:"spot", goboPattern:"dapple", goboAnim:"drift", volumetric:true}, aimed down at the floor. `shadowPool` is a fake contact shadow: a flat soft dark disc laid just above the floor (data.radius meters, data.opacity 0 to 1, data.softness, data.color) so a heavy object reads as resting on the ground rather than floating; it works where no light casts a real shadow. `forcefield` bends nearby particle motion and does nothing without particles in range: data.fieldType vortex|gravity|wind|turbulence|pointer|camera, data.radius meters of influence, data.strength, data.falloff constant|linear|inverseSquare; vortex and wind act along local +Y, so rotate the node to aim. `pointer` binds the field to the VISITOR\'S cursor or finger instead of to the node: dragging is a magnet plus a wind along the direction of travel, a click or tap sends a ripple expanding outward. It uses its position only to fix the depth the cursor projects onto (data.pointerPlane camera|ground), plus data.pointerMagnet, data.pointerWind, data.rippleStrength, data.rippleSpeed, data.rippleWidth and data.pointerDamping (how fast particles settle again; at 0 the effect never reaches equilibrium). `camera` makes the CAMERA the force: particles are shoved aside as the visitor moves through them and settle again when it stops, so a parked camera costs nothing. data.cameraSweep 0 shoves straight outward from the camera, 1 sweeps along the direction of travel; 0.35 (default) reads as dodging. The web* types are responsive site building-blocks. Keep in sync with server/copilotSchema.js NODE_TYPE_ENUM.'}, 'parentId': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'description': 'Parent node id/tempId, or null for the scene root. On duplicate: where the copy goes (default: beside the source, under the same parent).'}, 'position': {'type': 'array', 'items': {'type': 'number'}, 'description': "[x,y,z]. Y is up, units ≈ meters, ground at y≈0. On duplicate: the copy's position (default: the source's, i.e. exactly on top of it)."}, 'rotation': {'type': 'array', 'items': {'type': 'number'}, 'description': "[x,y,z] euler radians. On duplicate: the copy's rotation (default: the source's). To AIM a camera or a lamp, or turn a sign to face a point, pass lookAt instead."}, 'viewport': {'type': 'object', 'description': 'For settings: the published LOOK. Post effects {bloomEnabled, dofEnabled, vignetteEnabled, colorGradingEnabled, glitchEnabled, filmGrainEnabled, lensDistortionEnabled, chromaticAberrationEnabled + chromaticAberrationOffset 0.0002-0.01 + chromaticAberrationRadial, godRaysEnabled + godRaysSamples 15-120 (the COST dial) + godRaysDensity, godRaysWeight, godRaysDecay, godRaysExposure, lensFlareEnabled} each with their own numeric params; camera flights {cameraEasing: out|inOut|gentle|linear, cameraTravelScale 0.25-4 (flight length), cameraFlightArc 0-1}; neighborhood dissolve {zoneFadeMs 0-2000}; particles and cables take data.forceFieldIds: [fieldId,...] (null = every field, [] = none, the one lever that makes fields cheap) and a webpage takes data.zoneKeepDistance; {lutEnabled, lutPreset: neutral|warm|cool|teal-orange|vintage|noir|sepia|vibrant, lutIntensity}; {toneMapping: none|linear|reinhard|cineon|aces, toneMappingExposure 0.1-4}; {drawDistance 1-5000}; {showGroundPlane, showNeighborhoodPlates, showGrid}. ⚠ publishPostFx must be true or bloom/DoF/vignette/grading/glitch/grain/lens-distortion/chromatic-aberration render in the editor ONLY and every visitor sees none of them. ⚠ drawDistance is overridden by the data.far on any camera node (created at 100), so raise those too.', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'metalness': {'type': 'number', 'description': 'Optional 0–1 metalness for the minted color material (0 = non-metal, 1 = metal). Default 0.'}, 'primitive': {'enum': ['box', 'sphere', 'cylinder', 'plane', 'torus'], 'type': 'string', 'description': 'For add of a geometry node: which primitive.'}, 'roughness': {'type': 'number', 'description': 'Optional 0–1 roughness for the minted color material (0 = glossy, 1 = matte). Default 0.5.'}, 'environment': {'type': 'object', 'description': 'For settings: FOG and the background. {backgroundColor, fogColor, fogNear, fogFar, fogType: linear|exponential, fogDensity}. Fog is what makes distance read, so raising drawDistance without pushing fogFar out with it shows nothing new.', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'lookAtNodeId': {'type': 'string', 'description': 'Like lookAt, aimed at a node (its id, or a same-batch tempId): at its visible centre, so a page is aimed at its card, a pivoted door at its mesh and a room group at the middle of what is in it.'}, 'materialMaps': {'type': 'object', 'properties': {'ao': {'type': 'string'}, 'orm': {'type': 'string'}, 'ormh': {'type': 'string'}, 'height': {'type': 'string'}, 'normal': {'type': 'string'}, 'offset': {'type': 'array', 'items': {'type': 'number'}}, 'repeat': {'anyOf': [{'type': 'number'}, {'type': 'array', 'items': {'type': 'number'}}]}, 'emissive': {'type': 'string'}, 'baseColor': {'type': 'string'}, 'metalness': {'type': 'string'}, 'roughness': {'type': 'string'}, 'worldScale': {'type': 'number'}, 'uvAnimation': {'type': 'object', 'propertyNames': {'type': 'string'}, 'additionalProperties': {}}, 'offsetFromWorld': {'type': 'boolean'}, 'emissiveIntensity': {'type': 'number'}}, 'description': 'Bind a GENERATED PBR material. Pass the map URLs from get_generation (its maps.<field>.url). On add or assignMaterial, mints-or-reuses a textured material from these maps and assigns it - this is how you place a generate_material result onto a node. At least one map url required. `repeat` tiles the whole set (a number, or [x, y]) - a map wraps EXACTLY ONCE without it, which is right for a prop and useless for ground, since one wrap over a 400m plane is a single smeared ripple. `uvAnimation` moves it: { mode: "scroll", speedX, speedY } in UV per second is the flowing-water primitive - point it at a set with a normal map and the surface ripples - or { mode: "flipbook", columns, rows, fps } to step a spritesheet. Both apply to every channel at once, so the albedo and the normal cannot end up tiled differently. `offset` [x, y] shifts the whole set in UV units (1 = one full tile) so two pieces of one wall can be phase-aligned across their seam: a piece that starts 1.2 m into a 0.5 m tile pattern wants offset [2.4, 0] (distance divided by tile size). A different offset mints a different material; every piece needs its own repeat (piece size / tile size) since a box face always maps 0..1. `emissive` is a sixth map url: the pixels that GLOW (lit windows on a facade, a screen, a neon tube, a lava crack), added as light on top of the lit surface and visible in the dark; black pixels glow nothing. `emissiveIntensity` (0 to 16, default 1 = the map as painted) scales that glow; needs `emissive` beside it. A different emissive map or intensity mints a different material. `ao` is a separate ambient-occlusion map url (list_assets mapRole "ao"), the crevice shading; a packed file already carries it in R, so ao beside ormh or orm is refused. PACKED SETS: `ormh` is one RGBA file carrying AO (R), roughness (G), metalness (B) and height (A), which is how every set in the shared library ships (list_assets mapRole "ormh"); `orm` is the glTF three-channel form (R=AO, G=roughness, B=metalness, no height). Pass the file on `ormh` (or `orm`) and each channel is split out and wired for you; it REPLACES the separate roughness/metalness (and, for ormh, height) urls, so naming both is refused, and ormh and orm together is refused. A library set is baseColor + normal + ormh: {"baseColor":"<albedo url>","normal":"<normal url>","ormh":"<ormh url>","repeat":4}. Tiling, offset and animation apply to the split channels too. `worldScale` (meters) is the size of ONE tile of the set and REPLACES repeat: the engine turns it into the repeat for the node the material lands on from that primitive\'s dimensions and its WORLD scale (its own times its ancestors\'), so brick every 0.4 m reads the same on a 2 m wall and a 12 m one ({"baseColor":..., "normal":..., "ormh":..., "worldScale":0.4}). With a `where` selector each match gets the repeat for its own size (same-sized pieces still share one material). A box has three face sizes and one repeat, so its LARGEST face is sized right and a long thin box stretches on its small faces; a sphere, cylinder and torus size by their unwrapped surface. Refused on an imported model (its UVs are whatever the file baked) and beside repeat. `offsetFromWorld: true` does the seam arithmetic for you: the offset is derived from where the node stands in the world (its face\'s UV origin along the face\'s own axes, over the tile size, modulo one tile), so two pieces of one wall bound with the same set and worldScale carry the pattern straight across their join. Box and plane only; needs worldScale or repeat beside it; refused beside an explicit offset. With `where` each match gets its own phase, and pieces a whole tile apart still share one material. Each distinct phase is its own material (one texture upload is shared by every tiling of a file), so use it on the pieces of a seam, not on every brick of a city. BOTH worldScale AND offsetFromWorld ARE COMPUTED ONCE, WHEN THE MATERIAL IS BOUND: a node moved, resized or duplicated afterwards keeps the repeat and phase it was bound with (get_scene shows the material\'s repeat and offset), so re-run assignMaterial on the pieces you moved. One material source per op: materialAssetId, materialMaps, materialPreset or color, not two.', 'additionalProperties': {}}, 'materialName': {'type': 'string', 'description': 'Display name for the material minted from materialMaps or materialPreset (e.g. the generation label like "Lush Grass").'}, 'materialPreset': {'type': 'string', 'description': "On add or assignMaterial: mint-or-reuse one of the fourteen curated procedural presets, the same ones the editor's Material Assets panel offers, so a first pass is textured rather than flat (pass `color` beside it and the preset takes that colour: its tint ramp keeps the preset's own light-to-dark contrast about your hue, so brushed-metal + #8a2be2 is purple metal, neon-glass + #ff2020 glows red; without a colour a preset is exactly its stock look): matte-paper, neon-glass, brushed-metal, warm-ceramic, soft-plastic, holographic-film, dark-carbon, rough-stone, frosted-ice, worn-leather, polished-marble, lava-rock, ocean-glass, gold-leaf. Optional `color` becomes the preset's base color where the preset takes one. No image files, no weight beyond the node."}, 'materialAssetId': {'type': 'string', 'description': 'A material id from the scene summary (for add geometry or assignMaterial).'}, 'emissiveIntensity': {'type': 'number', 'description': '0 to 16, default 1 = the emissive colour as given; 2 to 4 reads as a lit lamp, higher blooms. Needs `emissive` beside it.'}}, 'additionalProperties': {}}, 'minItems': 1, 'description': 'Ordered edit operations, applied atomically.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['width', 'height'], 'properties': {'base': {'type': 'object', 'properties': {'maps': {'type': 'object', 'properties': {'ormh': {'type': 'string'}, 'normal': {'type': 'string'}, 'baseColor': {'type': 'string'}}}, 'mode': {'enum': ['tile', 'stretch'], 'type': 'string'}, 'tint': {'type': 'string'}, 'color': {'type': 'string'}, 'setId': {'type': 'string'}, 'tileSize': {'type': 'number'}, 'roughness': {'type': 'number'}}, 'description': 'The material the surface is made of.', 'additionalProperties': {}}, 'depth': {'type': 'number', 'description': 'Panel thickness in meters (default 0.03).'}, 'label': {'type': 'string'}, 'width': {'type': 'number', 'description': 'Surface width in meters.'}, 'height': {'type': 'number', 'description': 'Surface height in meters.'}, 'layers': {'type': 'array', 'items': {'type': 'object', 'required': ['type'], 'properties': {'x': {'type': 'number'}, 'y': {'type': 'number'}, 'dry': {'type': 'number'}, 'src': {'type': 'string'}, 'edge': {'type': 'number'}, 'font': {'enum': ['typewriter', 'sans', 'horror'], 'type': 'string'}, 'seed': {'type': 'number'}, 'size': {'type': 'number'}, 'text': {'type': 'string'}, 'type': {'enum': ['text', 'image', 'stains', 'spatter', 'grime', 'fill'], 'type': 'string'}, 'align': {'enum': ['left', 'center', 'right'], 'type': 'string'}, 'blend': {'enum': ['normal', 'multiply'], 'type': 'string'}, 'color': {'type': 'string'}, 'drips': {'type': 'number'}, 'scale': {'type': 'number'}, 'tooth': {'type': 'number'}, 'width': {'type': 'number'}, 'amount': {'type': 'number'}, 'jitter': {'type': 'number'}, 'weight': {'type': 'number'}, 'opacity': {'type': 'number'}, 'maxWidth': {'type': 'number'}, 'lineHeight': {'type': 'number'}}, 'additionalProperties': {}}, 'description': 'Painted on in order (max 24).'}, 'output': {'enum': ['panel', 'material'], 'type': 'string'}, 'keywords': {'type': 'array', 'items': {'type': 'string'}}, 'resolution': {'type': 'number', 'description': 'Pixels on the long side, 256 to 2048 (default 2048).'}, 'normalStrength': {'type': 'number', 'description': 'Relief strength, 0 to 4 (default 1).'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'A name for the new scene.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['sceneId'], 'properties': {'pageId': {'type': 'string', 'description': 'Review one page (a webpage node id): its brief, its content, its arrival shot. Omit for the whole scene.'}, 'sceneId': {'type': 'string', 'description': 'Scene id from list_scenes.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['sceneId'], 'properties': {'name': {'type': 'string', 'description': 'Only nodes whose name is exactly this.'}, 'type': {'type': 'string', 'description': 'Only nodes of this type (geometry, webText, webLink, webpage, light, cable, particle, group, camera, ...). Pair with the `counts` a first call returns to see what is worth asking for.'}, 'limit': {'type': 'number', 'description': 'Max nodes to return, 1-500 (default 250).'}, 'offset': {'type': 'number', 'description': 'Skip this many matching nodes - page with the `nextOffset` the previous reply gave you.'}, 'sceneId': {'type': 'string', 'description': 'Scene id from list_scenes (e.g. "src-1a2b3c4d").'}, 'parentId': {'type': 'string', 'description': 'Only the direct children of this node.'}, 'settings': {'type': 'number', 'description': 'The scene-level settings block (lighting, viewport, environment, audio, sky) rides on an unfiltered first-page call by default. Pass 1 to get it on a filtered or paged call, 0 to omit it.'}, 'materials': {'type': 'number', 'description': 'Pass 0 to omit the material library when paging a long node list you already have.'}, 'nameContains': {'type': 'string', 'description': 'Only nodes whose name contains this (case-sensitive). `scatter` names copies "<source> 1", "<source> 2", so this is how you find a scattered set.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['sceneId'], 'properties': {'views': {'type': 'array', 'items': {'anyOf': [{'type': 'string', 'description': '"page:<pageIdOrSlug>" to frame that page\'s neighborhood.'}, {'type': 'object', 'required': ['position', 'target'], 'properties': {'fov': {'type': 'number', 'description': 'Vertical FOV degrees (10-120).'}, 'target': {'type': 'array', 'items': {'type': 'number'}, 'maxItems': 3, 'minItems': 3, 'description': 'Look-at point [x,y,z].'}, 'position': {'type': 'array', 'items': {'type': 'number'}, 'maxItems': 3, 'minItems': 3, 'description': 'Camera position [x,y,z] (meters).'}}}]}, 'maxItems': 4, 'description': "Camera views to capture. Omit for the visitor's entry view."}, 'maxDim': {'type': 'number', 'description': 'Longest side of the image in pixels, 64-1920 (default 400; height is 0.625x). Smaller renders faster and is enough to judge placement; go large only for a final look.'}, 'sceneId': {'type': 'string', 'description': 'Scene id from list_scenes.'}, 'atSeconds': {'type': 'number', 'description': "Seconds into the scene's animation the shot is taken at (0-600, default 0 = frame one). Every track and motion-path rider is frozen at that time, so a door with a 4 s swing is verified mid-swing at 2 and a rider on a 12 s loop at u=0.5 at 6. Cached per time."}, 'timeoutSeconds': {'type': 'number', 'description': 'Per-view render budget in seconds, clamped 10-180 (default 45). Raise it for a big or texture-heavy scene instead of retrying a timeout at the same budget.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'properties': {'q': {'type': 'string', 'description': 'Search terms, all of which must appear in the label or keywords. Omit to list the newest.'}, 'kind': {'type': 'string', 'description': '"image" for textures, "glb" for 3D models. Omit for both.'}, 'limit': {'type': 'number', 'description': 'Max results, 1-100 (default 40).'}, 'offset': {'type': 'number', 'description': 'Skip this many results - page with the `nextOffset` the previous reply gave you.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'properties': {'q': {'type': 'string', 'description': 'Search text matched against the scene name and its published slug. Omit to list.'}, 'limit': {'type': 'number', 'description': 'Max results, 1-100 (default 40).'}, 'offset': {'type': 'number', 'description': 'Skip this many results - page with the `nextOffset` the previous reply gave you.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['sceneId'], 'properties': {'point': {'type': 'array', 'items': {'type': 'number'}, 'maxItems': 3, 'minItems': 3, 'description': 'A world point [x,y,z] to measure the clearance around.'}, 'nodeIds': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Up to 24 node ids to measure (and to pair up).'}, 'sceneId': {'type': 'string', 'description': 'Scene id from list_scenes.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['sceneId'], 'properties': {'fov': {'type': 'number', 'description': 'With position: vertical fov in degrees (default 42).'}, 'target': {'type': 'array', 'items': {'type': 'number'}, 'maxItems': 3, 'minItems': 3, 'description': 'With position: the point the eye looks at.'}, 'aspects': {'type': 'array', 'items': {'type': 'number'}, 'description': 'Screen shapes to read, width over height, 1 to 4 of them from 0.3 to 3.5 (default [1.778, 0.5625]: a desktop and a phone held upright).'}, 'sceneId': {'type': 'string', 'description': 'Scene id from list_scenes.'}, 'cameraId': {'type': 'string', 'description': 'A camera node id. Its fitWidth, its page and its role (entry, arrival, via, sign target) are all read.'}, 'position': {'type': 'array', 'items': {'type': 'number'}, 'maxItems': 3, 'minItems': 3, 'description': 'Instead of cameraId: an eye position [x,y,z], world meters.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['sceneId', 'position'], 'properties': {'nodeId': {'type': 'string', 'description': 'The node whose surface this point is on, for its albedo and room binding.'}, 'sceneId': {'type': 'string', 'description': 'Scene id from list_scenes.'}, 'position': {'type': 'array', 'items': {'type': 'number'}, 'maxItems': 3, 'minItems': 3, 'description': 'World point [x,y,z] in meters, e.g. the middle of the ceiling that looks lit.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['sceneId'], 'properties': {'confirm': {'type': 'boolean', 'description': 'Must be exactly true to publish. Omit / false to get a needsConfirm summary for the user.'}, 'sceneId': {'type': 'string', 'description': 'Scene id from list_scenes.'}}}
Schéma d’entrée
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'properties': {'url': {'type': 'string', 'description': 'A public http(s) URL to import.'}, 'path': {'type': 'string', 'description': 'A local file path (only when this MCP server runs on your own computer).'}, 'label': {'type': 'string', 'description': 'A human label for the library.'}, 'filename': {'type': 'string', 'description': 'File name WITH extension (e.g. "sign.glb", "poster.png"); required with dataBase64, optional with path.'}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Search keywords for list_assets.'}, 'dataBase64': {'type': 'string', 'description': 'The file, base64-encoded. Needs filename.'}}}
Modifications récentes des outils
Serveurs MCP similaires
GripForge
Generates, rigs, animates, analyzes, and packages game characters, weapons, armor, effects, environments, and engine-ready assets.
spriteship
Generates and manages 2D game characters, assets, animations, tilesets, UI packs, maps, and engine-ready exports for multiple gam…
Blixtworks
Offers pay-per-call utilities for text, image processing and analysis, data conversion, validation, web inspection, cryptography,…
Compose Preview Catalogs
Browses, renders, compares, audits, and edits Jetpack Compose preview catalogs and associated UI designs.
arch-tools-mcp
Provides general-purpose agent APIs for browser automation, web and PDF extraction, data conversion, email operations, fact check…
kernelcad
Authors, evaluates, reviews, simulates, edits, and exports parametric CAD assemblies and geometry from editable TypeScript-based …
floot
Creates, edits, previews, databases, and publishes full-stack web and mobile applications with hosted resources and deployment wo…
Raven
Supports coding agents with design audits, design-system guidance, and a configurable visual preference profile.