MCP: scene.* namespace (CRUD by opaque object id)#5972
Conversation
Read-only scene access keyed by `uint64` id = raw `Object*` (matches ImGui's `##<id>`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| struct DecomposedXf { Vector3f translation, rotationDeg, scale; }; | ||
|
|
||
| static DecomposedXf decomposeXf( const AffineXf3f& xf ) |
There was a problem hiding this comment.
This code might probably be moved to MRMesh as it's a common operation.
There was a problem hiding this comment.
Agreed in principle, but keeping DecomposedXf / decomposeXf / composeXf local to this TU for now — no second caller yet. Happy to promote to a MRAffineXf3Decompose.h in MRMesh the moment something else needs it.
| const uint64_t id = args.at( "id" ).get<uint64_t>(); | ||
| auto objEx = resolveId( id ); | ||
| if ( !objEx ) | ||
| throw std::runtime_error( objEx.error() ); |
There was a problem hiding this comment.
I've missed the exception usage in MRViewerMcp but I do think we should get rid of them.
There was a problem hiding this comment.
Matches the existing handler pattern in MRViewerMcp.cpp, and the ToolFunc contract in MRMcp.h explicitly documents throwing as the error channel (/// Those functions are allowed to throw, that's how you report errors to the MCP.). I'd rather not change ToolFunc's signature mid-PR — happy to open a separate issue/PR migrating all MCP handlers to Expected<nlohmann::json> at once if we decide to go that way.
Move skipFramesAfterInput to MRCommandLoop; use PI_F constants; rename mcp filter to MCP; derive info-line transform text from transformToJson output. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Adds
scene.*MCP tools for domain-model access — read / write / add / remove / serialize by opaqueuint64object id (the rawObject*pointer, matching the##<id>suffix in ImGui labels so ids are interchangeable betweenscene.*andui.*).Six tools:
scene.listObjectTree— flat subtree dump keyed byrootId(0 = SceneRoot).scene.getObjectInfo— superset of one row pluschildIds,worldBox, and the same info lines MI shows in its Information panel (plus the decomposed transform as text).scene.setObjectState— unified setter; any ofname/visible/selected/transformis optional; multi-field calls wrap in oneSCOPED_HISTORYso one Ctrl+Z reverts them atomically.scene.removeObject— detach from parent (subtree comes with it).scene.addObject— load fromfilePathOR{bytes (base64), extension, name}.scene.getObject— inverse: serialize one object viafilePath(writes to disk, returns path) orextension(returns base64 bytes).Shared infrastructure:
namespace MR::Mcp.MRMcpCommon.hwithskipFramesAfterInput(moved out ofMRViewerMcp.cpp).Schema::BoolinMRMcp.h.mcpVS filter groupsMRViewerMcp.cpp,MRSceneMcp.cpp,MRMcpCommon.h.All mutations push
_t(...)-wrapped history entries viaAppendHistory<T>—ChangeNameAction/ChangeObjectVisibilityAction/ChangeObjectSelectedAction/ChangeXfAction/ChangeSceneAction(AddObject | RemoveObject).Test plan
Live-verified end-to-end via MCP against a local Debug build:
listObjectTreereturns[](matches Scene panel).ui.pressButton→listObjectTreereturns 1 row;getObjectInforeturns 21 info lines (18 native + transform).setObjectStatecompound → all fields applied atomically; one Ctrl+Z reverts.addObject({filePath})/addObject({bytes, extension, name})→ both load correctly.getObject({filePath})/getObject({extension})→ both forms serialize; bytes round-trip throughaddObject.removeObject→ scene shrinks; stale id → typed error, no crash.MCP addObject/MCP setObjectState/MCP removeObjectentries; Ctrl+Z unwinds.🤖 Generated with Claude Code