Concepts

Elements, roles and states, ids and instances, locations, discovery, security, versions, and what a host must do.

This page explains the ideas the protocol is built from. The specification gives the exact shapes and rules.

Elements

An element is one thing the app shows: the app itself, a window, a document, a list, a row in it, a run of text. Everything a provider reports is an element, and the elements form one tree rooted at the app. Containment says how things relate: a selected sentence is inside the document it was selected in, which is inside its window.

The answer to current has exactly three fields: protocol, the protocol version; instance, a random value the provider mints each time it starts; and root, the app as an element.

The answer to current: an editor with two files open and a sentence selected
{
  "protocol": "0.2.0",
  "instance": "k3f9x2",
  "root": {
    "id": "app",
    "role": "Application",
    "title": "Editor",
    "children": [
      {
        "id": "win-1",
        "role": "Window",
        "focused": true,
        "children": [
          {
            "id": "doc-1",
            "role": "Document",
            "subrole": "markdown",
            "title": "draft.md",
            "url": "file:///Users/ada/essays/draft.md",
            "focused": true,
            "children": [
              {
                "id": "sel-23",
                "role": "Text",
                "selected": true,
                "content": [
                  { "type": "text", "text": "The command line lasted thirty years as the main way people used computers." }
                ],
                "attributes": { "lines": [31, 31] }
              }
            ]
          },
          {
            "id": "doc-2",
            "role": "Document",
            "subrole": "markdown",
            "title": "notes.md",
            "url": "file:///Users/ada/essays/notes.md",
            "visible": false
          }
        ]
      }
    ]
  }
}

Every element has an id and a role. It may also carry a subrole, a title, content, a url, the four states, children and attributes.

  • role says how the thing is shown. It is one of thirteen words: Application, Window, Group, List, Table, Row, Cell, Text, Heading, Link, Image, Page and Document. The root is the only Application. When no role plainly fits, a provider uses Group, and a receiver treats a role it does not know as Group.
  • subrole says what the thing is, in the app's own word: task, comment, pane, markdown. A task in a list is a Row with subrole task; the same task on a board is a Cell with subrole task.
  • content is a list of MCP content blocks, unchanged from the MCP specification, so a host can hand them to an agent without translating them. An element that is text sends a text block. An element that is not text sends an image, a resource_link, or both. A provider keeps content small.
  • attributes holds anything else the app wants an agent to have, by name: line numbers, a due date, a status. Two names are reserved, author and related.

A provider reports what the user has open and is pointing at, not its whole interface. It does not report buttons, menus, scroll bars or toolbars, and it leaves out anything the user would be surprised to see handed to an agent. The elements reference has the full field table and the role mapping.

States

Four states say what the user is pointing at and what is on screen. Each is stated once, on the element it describes. There is no separate focus field in the answer.

StateMeaningWhen absent
selectedThe user selected this element. Any number of elements may be selected.False
focusedThe keyboard or cursor is in this element.False
visibleThe element is drawn on screen now.The parent's value; the root is visible
hoveredThe pointer is over this element now. At most one element is hovered.False

focused is a path: it is set on every element from a child of the root down to the user's position, with no gaps. In the example the path is win-1, then doc-1. hovered goes on the innermost reported element under the pointer and not on its ancestors. It is optional, and a receiver must not infer anything when it is absent everywhere.

Selected text is a Text element with selected true, placed inside the element it was selected in.

Ids and instances

An id is a short string minted by the provider, such as sel-23: 1 to 128 letters, digits, hyphens and underscores. It is opaque. The host and the agent pass it back unchanged.

Within one run of the provider, an id keeps referring to the thing it was minted for, and the provider does not reuse it. An id for a text selection keeps referring to that passage after the user selects something else, and follows the passage through edits where it can. When the thing no longer exists, the provider answers gone.

An id means something only together with the provider that issued it and that provider's instance. Two providers may both issue sel-23, and a provider that restarts may issue it again for something else. Keeping the three together is the host's job; the agent sees only the id.

Open

How long an id must stay valid after the app quits and reopens, if at all, is not settled.

Locations

Given an id, a provider returns a location: where the element is on screen at that moment.

  • state is visible, offscreen or gone. These are separate from an element's visible state, which describes the moment current was answered.
  • rects is a list of rectangles covering the element, each with x, y, w and h. A wrapped text selection has one per line; most elements have one.
  • window is the system window number of the window the element is in.
  • avoid and edge are optional placement hints: a region the provider would prefer the host not cover, and the side it prefers things attached to. A host may ignore both.

Rectangles are in global display coordinates, in points, with the origin at the top-left corner of the primary display and y increasing downward. A location describes one moment; a host that needs to know whether an element moved asks again.

Whether an element can be located is asked, not declared in advance. An id the provider never issued in this run, or an element it cannot place, is an error. A provider that cannot report positions at all does not offer the operation. No message names a host's interface, and a provider cannot ask a host to draw anything.

Discovery

A running provider writes a descriptor file into a known per-user folder and removes it when it quits. The file gives the protocol version, the app's bundle identifier, name and version, the process id, the instance, the endpoint of the provider's MCP server, and the bearer token for this run.

The endpoint is http://127.0.0.1:<port><path>. The path is the provider's choice: a provider may open its own listener or serve the endpoint from a local server the app already has. A host uses the path as given and does not assume /mcp.

To find the front app's provider, a host takes the front app's process id from the operating system and looks for a descriptor with that process id. It ignores a descriptor whose process is no longer running, whose bundle identifier does not match the process, or whose protocol it does not support. Before using the endpoint it checks that the address is 127.0.0.1 and that the process listening on the port is the process the descriptor names. It ignores an answer whose instance or protocol differs from the descriptor's.

When the user has turned the provider off in the app, it writes no descriptor and opens no listener.

Open

A sandboxed app cannot write to the folder, and a web page cannot listen at all. Discovery for those is not designed yet.

Security

Only a local process that can read the user's descriptor can read from a provider.

  • The provider listens on 127.0.0.1 only.
  • Every request must carry Authorization: Bearer <token> with the token from the descriptor. The token is random, made at launch, published only in the descriptor and used for this endpoint only.
  • Any request with an Origin header is refused with HTTP 403, before the token is checked. Browsers attach that header to requests made by web pages, and no web page should be calling a provider. A host must not send it.
  • The providers folder has permissions 0700 and each descriptor 0600, which keeps other users of the machine out.
  • A provider does not write its token, or anything from an answer, to a log.

None of this defends against a hostile program already running as the user: it can read the token, and it can read the user's documents without this protocol. The token is also not consent. It shows that a caller can read the user's files, not that the user approved that caller. A host can additionally check the provider's code signature from its process id without any change to the descriptor format.

Versions

The protocol version has three parts and is sent as a string: 0.2.0. A provider gives the same value in the descriptor, in its extension declaration and in the answer to current. While the major version is 0, a host and a provider work together only if they agree on the major and minor parts; a new patch version only adds things a receiver may ignore. A host that does not support a provider's version treats the app as if it were not a provider. A receiver must not fail on a field, a subrole or an attributes name it does not recognize.

Version 0.2.0 replaced the flat list and separate focus list of the first draft with the element tree. The versioning page has the changelog, and the RFCs record why.

What a host must do

The protocol cannot enforce what a host does with context once it has it. These are rules a host follows; the rules page has the full list for both sides.

  • Ask on the user's action. Read current when the user invokes the agent, not on a timer.
  • Ask the front app only. Which app is in front is the host's knowledge, from the operating system. A user asking about a paragraph in an editor does not expect their mail to go along.
  • Show what was collected. Before a message is sent, the user must be able to see which context will go with it, and remove it. If they remove it while an answer is still on its way, the late answer is discarded.
  • Pass ids through unchanged. When an agent names an element, use exactly the id the provider issued.
  • Keep the provider with the id. Record which provider and which instance issued every id. Ask that provider only, and only if its instance is unchanged. If the provider has quit or restarted, treat the element as gone.
  • Treat hover as weaker than selection. The pointer may rest on something the user never meant to indicate. Present hovered as where the pointer is, and do not send more of an element's content because it is hovered.
  • Never wait on a provider. Set a time limit. If a provider does not answer, or answers offscreen or gone, carry on without the context or the placement.

On this page