Skip to content

What Purview is

Purview implements the MCP GUI Bridge Protocol v0.1.0. It sits between your real GUI and an MCP client, serving a projection of your application: a set of windows, each carrying semantic blocks (fields, tables, lists, notices, media…) and actions.

You never build protocol messages. You describe windows and keep them current; the library serves them.

Why "Purview"

A purview is the range of what someone is allowed to see and to decide — as in "within the purview of". That is precisely what a projection is: the agent's purview over your application. What you put in it, the agent can read; the actions you expose, the agent may invoke. Everything else is not hidden behind a permission check — it never reaches the wire at all.

The word also carries view inside it, which is the other half of the story: what the agent holds is a view of your GUI, never the GUI itself. It cannot click your buttons or reach past the projection into your widgets; it can only ask, through an action you defined, for something to happen.

The three ideas

Framework-agnostic core. The purview crate links no GUI toolkit. It reaches your main thread through one closure you register at startup.

Semantic, not pixels. An agent reads structured blocks — labelled fields, typed rows — not a screenshot it has to interpret.

The library owns the protocol. Dynamic tools/list, modal chains, optimistic concurrency, redaction, notifications: all derived from the projection you maintain.

Which crate do I use?

CrateUse when
purviewAlways. It is the core: projection, protocol, server.
purview-gpuiYour UI is gpui. It removes the thread-marshaling boilerplate — see Using purview-gpui.

Mental model

Two flows, and keeping them separate is the single most important habit:

Read side (GUI → library). Your UI's change events push values into the projection through the typed handles you kept. This is the only place blocks change.

Write side (agent → GUI). Each action becomes an MCP tool. Its handler triggers real business logic or GUI operations; it does not edit the projection directly — the change flows back through the read side.

Why it matters

If a handler wrote to the projection directly, an agent-driven change and a human-driven change would take different paths and eventually disagree. Funnel every mutation through one method — the examples all use a single sync() or bump() — and the projection cannot drift.

The three things you hold

HandleFromFor
AppHandle<Cx>build()Opening windows; reading the snapshot
Window<Cx>app.open(title)Summary, blocks, actions, modals, close
Block handleswin.blocks().fields(..) etc.Updating that block later

All of them are cheap to clone, Send + Sync, and hold a weak reference to the shared core — so you can store them in GUI components and call them from any thread. Once the core is dropped, their operations become safe no-ops.