WebMCP
The tools Puni's pages register in a browser that supports WebMCP, the loop they follow, and the one thing the browser must have.
When a browser exposes document.modelContext, Puni's landing page, library and studio register tools on the page. An agent driving that browser gets the signed-in session for free: no token, no server, the scene that is open.
#Tools on the landing page
| Tool | Does |
|---|---|
puni_get_started | Where the person signs in to begin; it does not sign in or open anything |
puni_read_contract, puni_read_runtime | The command contract; the app's builds |
#Tools on the library
| Tool | Does |
|---|---|
puni_list_projects | Your scenes with their revisions |
puni_open_project | Opens one in the studio |
puni_create_composition | A new blank scene, saved and opened |
puni_search_projects | Keyword search, or semantic with mode: "semantic" |
puni_read_contract, puni_read_runtime | The command contract; the app's builds |
puni_read_project | A saved scene's structure at the revision search returned |
puni_preview_scene_index, puni_index_scene_description | What semantic search would send, then sending it on request |
puni_remove_scene_embedding, puni_refresh_scene_search | Remove a scene from semantic search; bring its entry up to date |
The search and index tools are on the studio too. Project summaries use actionCount for action definitions and clipCount for timeline clips. Legacy stored metadata has actionCount: null until resaved; counts from a loaded document are exact. Checkpoint actions aliases actionCount.
#Tools in the studio
| Tool | Does |
|---|---|
puni_read_scene | The open scene's document, selection, playhead time and save state |
puni_select_objects, puni_seek_timeline | Selection and time, the way a pointer would |
puni_apply_command | One validated, undoable authoring command, saved at once |
puni_history | Undo or redo the latest edit, then save |
puni_inspect_construction, puni_save_construction_motion | Take a structured object apart for inspection; keep that as timeline motion |
puni_save_object, puni_use_object | Keep a selection as a library object; bring one into this scene |
puni_export_composition | The scene as a project file, a GLB or a PNG |
puni_read_viewer | What the page is drawing: its load state, frame rate, draw calls and GPU resources |
puni_sculpt_object | A front picture (an https URL) with optional extra views, or a prompt, into an object; on the person's own keys, or Puni's credits where offered |
puni_get_sculpt, puni_add_sculpt | Progress and the add commands; or add the ready sculpt to the open scene |
puni_segment_sculpt | Split a ready sculpt into named parts, textures kept |
#When the tools arrive
A page's own tools register as soon as it mounts. Six retrieval tools load separately and register a moment later, on the library and in the studio alike: puni_search_projects, puni_read_project, puni_preview_scene_index, puni_index_scene_description, puni_remove_scene_embedding and puni_refresh_scene_search. An agent that lists tools the instant a page opens can see the page's tools without those six; list again.
If the six fail to load, the page tries once more after two seconds. If they are still missing it says so once in the console, the page's other tools keep working, and reloading the page brings them back. Leaving a page unregisters every tool it registered.
#The loop
puni_read_sceneandpuni_read_contract.puni_apply_commandfor each edit (its fields are inpuni_read_contract). It is immediate and saved, the way a pointer's edit is, andpuni_historyundoes it.puni_read_sceneagain, andpuni_read_viewerfor what the page is drawing.
There is nothing to commit: each edit saves a new revision. Undo it with puni_history, because a browser edit over browser work leaves no checkpoint. To look at pixels before writing, call puni_render_view from MCP tools with a commandsJson batch: it draws the saved revision with the batch applied and saves nothing.
#Brand type and geometry
Upload fonts and images through the MCP upload_asset tool. The browser tools do not expose that upload call. They can apply a text module with the retained font in module.assets and its URI in fontAsset. Record provenance, including the licence.
Leave fontAsset absent to keep the built-in typeface. Missing or invalid custom font bytes refuse without substitution. A profile can use exact SVG path data in its outline, with inset bevel and bevelSegments for its edge. If an older module rejects a new property, update its definition from the current contract first.
#The browser must draw
puni_read_viewer answers with what the page has drawn, and drawing needs WebGL. Some hosted browsers, ChatGPT's among them, have no WebGL. There the viewer reports an error and never a drawn scene, so builds are blind. Read How agents see.
#Limits
Leaving the scene unregisters the tools.
Read MCP tools for the same loop from outside the browser.
Was this useful?