Skip to content

MCP: scene.* namespace (CRUD by opaque object id)#5972

Merged
Grantim merged 5 commits into
masterfrom
mcp-scene-namespace
Apr 24, 2026
Merged

MCP: scene.* namespace (CRUD by opaque object id)#5972
Grantim merged 5 commits into
masterfrom
mcp-scene-namespace

Conversation

@Grantim
Copy link
Copy Markdown
Contributor

@Grantim Grantim commented Apr 23, 2026

Summary

Adds scene.* MCP tools for domain-model access — read / write / add / remove / serialize by opaque uint64 object id (the raw Object* pointer, matching the ##<id> suffix in ImGui labels so ids are interchangeable between scene.* and ui.*).

Six tools:

  • scene.listObjectTree — flat subtree dump keyed by rootId (0 = SceneRoot).
  • scene.getObjectInfo — superset of one row plus childIds, worldBox, and the same info lines MI shows in its Information panel (plus the decomposed transform as text).
  • scene.setObjectState — unified setter; any of name / visible / selected / transform is optional; multi-field calls wrap in one SCOPED_HISTORY so one Ctrl+Z reverts them atomically.
  • scene.removeObject — detach from parent (subtree comes with it).
  • scene.addObject — load from filePath OR {bytes (base64), extension, name}.
  • scene.getObject — inverse: serialize one object via filePath (writes to disk, returns path) or extension (returns base64 bytes).

Shared infrastructure:

  • New namespace MR::Mcp.
  • New MRMcpCommon.h with skipFramesAfterInput (moved out of MRViewerMcp.cpp).
  • New Schema::Bool in MRMcp.h.
  • mcp VS filter groups MRViewerMcp.cpp, MRSceneMcp.cpp, MRMcpCommon.h.

All mutations push _t(...)-wrapped history entries via AppendHistory<T>ChangeNameAction / ChangeObjectVisibilityAction / ChangeObjectSelectedAction / ChangeXfAction / ChangeSceneAction(AddObject | RemoveObject).

Test plan

Live-verified end-to-end via MCP against a local Debug build:

  • Empty scene → listObjectTree returns [] (matches Scene panel).
  • Cube created via ui.pressButtonlistObjectTree returns 1 row; getObjectInfo returns 21 info lines (18 native + transform).
  • setObjectState compound → 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 through addObject.
  • removeObject → scene shrinks; stale id → typed error, no crash.
  • History panel shows MCP addObject / MCP setObjectState / MCP removeObject entries; Ctrl+Z unwinds.

🤖 Generated with Claude Code

Grantim and others added 3 commits April 24, 2026 00:04
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>
@Grantim Grantim requested review from Fedr and oitel April 23, 2026 20:38
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread source/MRViewer/MRMcpCommon.h Outdated
Comment thread source/MRViewer/MRSceneMcp.cpp Outdated
Comment thread source/MRViewer/MRSceneMcp.cpp Outdated
Comment on lines +66 to +68
struct DecomposedXf { Vector3f translation, rotationDeg, scale; };

static DecomposedXf decomposeXf( const AffineXf3f& xf )
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code might probably be moved to MRMesh as it's a common operation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread source/MRViewer/MRSceneMcp.cpp Outdated
const uint64_t id = args.at( "id" ).get<uint64_t>();
auto objEx = resolveId( id );
if ( !objEx )
throw std::runtime_error( objEx.error() );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've missed the exception usage in MRViewerMcp but I do think we should get rid of them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread source/MRViewer/MRViewer.vcxproj.filters Outdated
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>
@Grantim Grantim merged commit 1324745 into master Apr 24, 2026
35 checks passed
@Grantim Grantim deleted the mcp-scene-namespace branch April 24, 2026 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants