Skip to content
PalmaPlan — the wiki ← PalmaPlan

The embedded view

show_schedule shows a live schedule inside the conversation. It is not an image or an export: it is PalmaPlan's Gantt chart, drawn by the same code as the screen. It is editable if your role allows it, and it keeps up when someone else writes.

show_schedule { schedule: "S/4HANA Migration", view: "gantt", critical_path: true }

Options:

The user's actual role is always the ceiling: editable never grants more than a role allows.

What the view is: a native MCP App

PalmaPlan serves an MCP App: the resource ui://palmaplan/planning, of type text/html;profile=mcp-app. It is a single page: its HTML, JavaScript and CSS are inline, and nothing loads from elsewhere. show_schedule, what_if and diff_versions point to it (_meta.ui.resourceUri). A host that renders MCP Apps shows it in the conversation, in an isolated frame. It opens inline, then full screen on request, in the host's theme colours (light or dark), language and time zone.

The view only talks to the host. Its CSP opens no domain: no connectDomains, no frameDomains, no resourceDomains. It reads and writes by calling the server's tools (tools/call), which the host relays under the connector's token or OAuth. So the view has no token of its own, no nested frame and no direct access to PalmaPlan. Three tools are reserved for it (_meta.ui.visibility: ["app"]); the host hides them from the model:

ToolWhat it returns
get_schedule_pagethe schedule in pages, each response under 120,000 characters (Claude caps a tool result at about 150,000)
get_changes_sincethe journal entries after a rank, to stay up to date
get_schedule_absencesthe agenda: the members' names and the dates of their absences, never the reason or the e-mail

The three views, and the before/after

"Accept" applies exactly what the before/after shows, and nothing else.

Gestures

In the Gantt chart, when the role allows it:

Each gesture calls an existing tool (move_periods, resize_period, set_action_status, undo…), as the agent would. So the journal, undo and real time are the screen's. A refused write (role, lock, conflict) shows in the view. A viewer gets a read-only view.

Live updates: the view rereads the journal through get_changes_since, every two seconds at first, then less and less often while nothing moves (up to fifteen seconds). It stops while the frame is hidden. Each show_schedule call mounts a new frame; an older view of the same schedule withdraws on its own and says a newer view is open further down.

Talking to the model

Negotiation: who sees what

An MCP Apps host announces the io.modelcontextprotocol/ui extension with the type text/html;profile=mcp-app. The server reads three routes, and the first that announces it is enough:

  1. capabilities.extensions["io.modelcontextprotocol/ui"] at initialize (the stable spec's route);
  2. capabilities.experimental["io.modelcontextprotocol/ui"] (the older route);
  3. _meta["io.modelcontextprotocol/clientCapabilities"] on a request: the stateless protocol's route. The server's SDK (1.x) does not speak that protocol yet, but the route is read.

What depends on it:

An MCP Apps hostA client without MCP Apps (Claude Code)
tools/listthe 101 tools; the host hides the three view-only tools from the modelthe 98 model tools: the three view-only tools are not listed (nothing there would hide them from the model, and get_schedule_page returns up to 120,000 characters)
show_schedulethe view; a text that sums it up; no link and no tokenthe text and the one-hour /embed link
what_if, diff_versionsthe before/after, and their texttheir text

Calling a view-only tool still works for everyone. Visibility is not a permission boundary: the same data reads through palmaplan://schedule/{id}/json, under the same rights. And a host that rendered the view without announcing the extension does not find it empty.

What Claude Code shows: text

Claude Code does not render MCP Apps. There, show_schedule returns a text that stands on its own: the name, the period, the number of groups, rows, bars and milestones, the risks (violated links, bars at risk of absence) and the one-hour link:

Planning « S/4HANA Migration » — du 2026-11-02 au 2026-12-18 ; 2 groupes, 6 lignes, 6 blocs, 1 jalon.
Risques : 1 bloc à risque d absence.
Cet hôte n'affiche pas les vues MCP Apps : cette adresse ouvre le planning, valable une heure, en lecture seule : http://127.0.0.1:4785/embed/<id>?t=…

(The tools' texts are in French.) Its structuredContent is compact: { schedule_id, name, view, options, rank, read_only, shown_at, url, expires_at }. It never carries the model, because Claude Code gives it to the model instead of the text. A test checks all 98 model tools the same way: each returns a useful text (a result, or a refusal that says what to fix) and a compact structuredContent.

The /embed link, for hosts without MCP Apps

The link opens PalmaPlan's screen, reduced to the schedule, in a browser or in an iframe on another site. It carries a signed token valid for one hour, capped to the schedule and to the actual role. This link:

It is not a named token: it does not appear under Settings › Connections and agents › Connected applications (see Tokens and scopes).

Limits

What is proven, and what is not

Proven, in the repository:

Not proven: no real host has displayed this view yet. Not Claude Desktop, not claude.ai, not any other MCP Apps host. The test host follows the documentation, not Claude's code: its exact CSP, the way it announces the MCP Apps capability at initialize, its mobile WebView may differ. In particular, if a real host displays the view without announcing the extension, the model there will only see the 98 model tools, and the view will keep working, since view-only calls are still served. The steps for the real trial are in Connecting Claude Desktop, section "Trying the view in Claude Desktop". Until it has been run, treat the view as proven at the protocol level, not as proven in Claude.