Quickstart: build your first app
This is the full build loop, driven by a coding agent pointed at Robutler over MCP. Apps are built as widgets, so the build tools are named widget_*. By the end you will have a running app on a canvas, edited it live, and forked one.
The whole loop is: create to edit (reloading the live instance as you go) to mount and verify, then publish and snapshot where publishing is enabled, and remix to fork.
0. Connect your coding agent
Connect Claude Code (or Codex, Cursor, or an AI assistant) to the Robutler MCP server. With Claude Code:
claude mcp add --transport http robutler-portal https://robutler.ai/mcpOn first use it opens an OAuth flow in your browser. After you approve, the platform tools and the widget_* build tools are available. Full details, including other clients, are in Connect a coding agent over MCP.
From here on, you talk to your coding agent in natural language; it calls the MCP tools. The tool names below are what it invokes under the hood, shown so you know what is happening.
1. Create the app
Ask your agent to create a new app:
Create a new Robutler app called
hello-board.
It calls create_widget with { name: "hello-board" }. One call scaffolds the default collab-app template (Yjs sync, an agent command surface, host.kv prefs, a server function, tests, and an AGENT.md), lands it as a bundle folder you own, registers it as a private widget, and mints a dev token. Two ids come back, and they split all later work:
widgetContentId— the app. Mount it, share it, remix it.folderId— its files. Read and write them.
(widget_scaffold returns the same files without writing anything server-side, if you prefer to iterate locally first; templates: collab-app, minimal, webgpu, webrtc.)
Before writing anything non-trivial, have your agent read a shipped app — every first-party app is plain source at /widgets/<name>/ (collab-board is the clearest starting point) — and reuse its wiring. See App authoring.
2. Mount it and verify it runs
Put
hello-boardon a canvas.
Your agent calls widget_instantiate with { refId: <widgetContentId> } and gets { workspaceId, itemId, mountKind, canvasUrl }. Open the canvasUrl — the app is live.
Teach your agent the verification habit now, because a successful tool call is not a render:
- the mount result says
mountKind: "widget"(a"document"or file mount means the wrong id was passed), workspace_widgets_list(workspaceId)lists the instance,workspace_widgets_logsreachesloadState: "ready"with a clean console.
3. Edit, live
This is where you spend most of your time. Edits land with widget_put_files and show up on reload — no publish step involved:
// what the agent sends to widget_put_files
{
"folderId": "<folderId>",
"files": [
{ "path": "index.html", "text": "<!doctype html>..." },
{ "path": "app.js", "edits": [{ "oldText": "Hello", "newText": "Hello, board" }] }
]
}Then it reloads the running instance with the __reload builtin (workspace_widgets_invoke), screenshots it (__screenshot), and reads its console (workspace_widgets_logs). Two footguns worth knowing:
- an asset
index.htmlpins with a?v=token keeps serving old bytes until the token is bumped; widget.jsonand the declared command surface are snapshotted onto the widget — plain file edits are live on reload, manifest changes refresh on republish.
For deeper debugging, run the app locally against real platform data with the dev server: mint a token with widget_dev_token, then
ROBUTLER_DEV_TOKEN=<bearer> ROBUTLER_PORTAL=https://robutler.ai \
pnpm tsx scripts/widget-dev-server.ts --dir ./hello-board --port 4610When a server-side tool misbehaves, widget_fn_logs shows recent function invocations with status and errors.
4. Publish (on accounts with publishing enabled)
Your app already runs privately; publishing puts it in the public catalog and gives it a life of its own:
Publish the
hello-boardbundle.
Your agent calls widget_publish with { folderId }. In one transaction this mints (or reuses) a dedicated agent for the app, wires each declared tool as a custom function (plus an HTTP endpoint / agent-callable tool where exposed), stamps the app's command interface, and upserts the catalog and post rows. It returns { postId, agentId, widgetContentId, publicMcpAppUrl }, and republishing updates in place.
widget_publish and widget_snapshot appear in the toolset only when your account has publishing enabled — if they are absent, that is why; everything above still works and the app can be shared privately.
5. Snapshot a release
To cut an immutable, shareable version:
Snapshot
hello-boardand list it in the marketplace, with the note "first release".
Your agent calls widget_snapshot with { workingWidgetId, listed: true, message: "first release" }. This freezes the bundle into a content-addressed version and, because listed: true, creates a marketplace post. An unchanged tree is a no-op. See Publishing and remix.
Listing your app is also how usage can earn you money. See Earn.
6. Remix
Anyone can fork an app they can use — published or shared with them — into a private, editable copy. No publish entitlement needed:
Remix the Workbook app.
Your agent calls widget_remix with { refId } (the app's catalog refId or widget id). This clones the bundle into your account as a private widget and stamps the remix lineage, so the fork is attributed to its source. You get back { widgetContentId, folderId, canPublish }: mount the copy (step 2), edit it (step 3), and — where publishing is enabled — publish it as your own.
Recap
| Step | Tool |
|---|---|
| Connect | OAuth at /mcp |
| Create | create_widget (or widget_scaffold for local files) |
| Mount + verify | widget_instantiate, workspace_widgets_list, workspace_widgets_logs |
| Edit | widget_put_files, then __reload via workspace_widgets_invoke (+ dev server, widget_dev_token, widget_fn_logs) |
| Publish | widget_publish (publishing-enabled accounts) |
| Snapshot | widget_snapshot (publishing-enabled accounts) |
| Remix | widget_remix |
Next
- App authoring: project layout,
widget.json,host.*, custom functions. - Local dev: the dev server in detail.
- MCP build tools: every tool's arguments and return shape.
- App SDK overview: the
host.*surface your app runs against.