MCP: add viewer.* namespace (camera, screenshots, input events)#5980
Merged
Conversation
… MRUiMcp.cpp Prep for viewer.* namespace: the existing MRViewerMcp.cpp hosts ui.* tools, rename to MRUiMcp.cpp so the new viewer.* TU can take the old name. resolveId is about to get a second caller (viewer.fit), so lift it and idOf to a shared MRMcpCommon.h/.cpp.
- viewer.fit takes optional objectIds and/or 3D points and unions their boxes; falls back to whole-scene fitData when neither is given. - viewer.setupCamera takes mandatory forwardDir + upDir, orthogonalizes upDir against forwardDir, and always refits after reorientation.
One tool, two modes: default captures just the 3D viewport via the sync captureSceneScreenShot path; `includeUi: true` captures the whole window via captureUIScreenShot, blocking on a promise until the async callback fires on the next frame. Dual output: `filePath` -> write server-side and return the path; omitted -> return inline base64 PNG bytes.
preciseFitDataToScreenBorder / preciseFitBoxToScreenBorder give exact framing to the viewport edges. Both default to snapView=false, so the camera angle is preserved — setupCamera's direction is no longer silently snapped to canonical. Drops the snapView tool param entirely.
Unified per-domain event injection. `sendMouseEvent` dispatches on `type` (down/up/click/move/scroll); `click` = down+up with an optional pre-hover move. `sendKeyboardEvent` takes a string key — single printable char or a named key like `Escape` / `ArrowUp` / `F5`. Modifiers are string arrays (`["ctrl","shift"]`). All events go through Viewer::emplaceEvent (the same path Python UI tests use) with skipFramesAfterInput between steps to give plugins a frame to react.
Fedr
approved these changes
Apr 24, 2026
oitel
approved these changes
Apr 24, 2026
…creenshots # Conflicts: # source/MRViewer/MRViewerMcp.cpp
This was referenced Apr 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the
viewer.*MCP namespace: camera control, screenshots, and input-event injection. Extends issue MeshInspector/MeshInspectorCode#7205.New tools:
viewer.fit— frame objects/points/whole scene (usespreciseFit*ToScreenBorder, preserves camera angle)viewer.setupCamera— set camera forward/up direction; auto-orthogonalizes upDir via Gram-Schmidtviewer.captureScreenshot— 3D viewport (sync) or whole window (async, blocks on promise/future); dual output mode (server-sidefilePathor inline base64 bytes)viewer.sendMouseEvent— unified mouse injection (down/up/click/move/scroll)viewer.sendKeyboardEvent— unified keyboard injection (down/up/press/repeat); string key names (\"a\",\"Escape\",\"F5\",\"ArrowUp\"); modifiers as string arrayAlso extracts
resolveId/idOffromMRSceneMcp.cppinto a sharedMRMcpCommon.h/.cppnow that a second caller (viewer.fit) needs them, and renames the existingMRViewerMcp.cpp(which hostsui.*) toMRUiMcp.cppso the new viewer-namespace TU can take the clearer name.Commits
resolveId/idOfintoMRMcpCommon.h/.cpp; renameMRViewerMcp.cpp→MRUiMcp.cpp.viewer.fit+viewer.setupCamera.viewer.captureScreenshot.preciseFit*ToScreenBorder; drop thesnapViewparam (always false now).viewer.sendMouseEvent+viewer.sendKeyboardEvent— all events go throughViewer::emplaceEvent(same pattern astest_ui/scenarios/scenario_helpers.py), withskipFramesAfterInputbetween steps.Test plan
All 6 tools verified live via MCP:
viewer.captureScreenshotfile-mode → PNG on disk with89 50 4E 47magicviewer.captureScreenshotbytes-mode at custom resolution → base64 PNGviewer.captureScreenshotwithincludeUi: true→ full window including ribbon/scene tree/panels (async promise path)viewer.setupCameratop/left/isometric (forwardDir=[-1,-1,-1]) directions — camera angle preserved, navigation cube matchesviewer.setupCamerarejects zeroforwardDirandupDirparallel toforwardDirwith typed errorsviewer.fitwhole-scene, per-object, per-point, mixed — all frame correctly; unknown object id → typed errorviewer.sendMouseEventtype=scroll → view zoom changed; error paths (unknown type, missing button fordown, missingscrollDeltaforscroll, unknown modifier)viewer.sendKeyboardEventCtrl+Z → undoes last scene mutation; unknown key name → typed error🤖 Generated with Claude Code