Skip to content

Keeping the agent in-sync with the page's context #312

Open

Description

@yoavweiss

Playing around with WebMCP, I find myself in situations where the agent is not aware of the page's context. The human user has visuals and text that help them understand what page they are on. The agent needs to use snapshots and DOM/a11y tree walking to get similar context.

Context changes can be a result of tool calls, but can also be the result of a human user interaction with the page or time-based context changes (e.g. video ended and a new one starts playing). Tool output can help provide contexts in the first case, but not others.

It might be good to enable developers with the means to:

  • Let the agent know that the context has changed (due to a hard navigation, soft navigation or some other interaction).
  • Provide the current page context to agents, and potentially add extra details about it.

Example use cases:

  • A product page could provide high-level details about the product, allowing the agent to have more context when the user asks it basic questions about it, even before calling tools that provide more detailed descriptions.
  • A checkout page with multiple different shipping addresses can provide them as context for the agent, allowing it to offer contextual shortcut enabling the user to indicate preference for one versus the other (instead of the user typing in a full address that's already on file).

Activity

  1. beaufortfrancois commented on Sep 17, 2026

    @beaufortfrancois
    Collaborator

    I'd like to jump in and share some practical thoughts on how this could work for in-page agents. Providing both static and dynamic mechanisms for pages to declare contextual state would allow the agent runtime to prepend or inject it into its prompts.

    For static or server-rendered pages, authors could expose high-level context via <meta name="model-context-state" content="Product page: Pixel 9 Pro. Key specs: 16GB RAM, Tensor G4.">. On initial navigation/page load, the in-page agent would read the meta tag and inject the content as contextual framing into the prompt context.

    For soft navigations, client-side state transitions, or async user interactions, developers could use a registered callback to be invoked by the agent immediately before constructing a prompt turn:

    document.modelContext.registerContextProvider(async () => {
      return {
        view: "product_detail",
        selectedVariant: { sku: "G9P-128", inStock: true },
        activeTab: "tech_specs"
      };
    });
  2. Kulikowski commented on Sep 17, 2026

    @Kulikowski

    I like the proposal! In-page agent assuming its shipped by the website provider will have access to state anyway right? So it's more for 3rd party embedded agents and extension/browser-based ones?

  3. rohit-binaried commented on Sep 17, 2026

    @rohit-binaried

    Even an app-owned agent benefits from an explicit projection: access to React state does not tell it which fields are relevant, current, or appropriate to send to a model. For the shipping-address example, that projection could expose a selected address ID and approved display labels without serializing the entire account object.

    I maintain RCIP, where consumers read application snapshots and subscribe to changes. A human UI update can therefore change the consumer-visible context without a tool call. Consumer contract.

    For this proposal, I would separate “context changed” notification from “read current context,” so a consumer can coalesce updates before its next turn. RCIP is an in-process implementation, not WebMCP compatibility evidence; mapping that behavior across a browser/extension boundary remains integration work.

  4. Hronom commented on Sep 22, 2026

    @Hronom

    One useful contract might be to treat page context as a versioned observation, not only as a change event. A consumer could receive a small, allowlisted projection such as {contextId, origin, browsingContextId, documentGeneration, revision, changedAt, summary} and then explicitly read the current projection before constructing the next model turn. That separates “something changed” from “this is the state now,” and lets consumers coalesce rapid UI updates.

    For cooperative human/agent workflows, a context change should also invalidate pending proposals whose target generation no longer matches. After a soft navigation, selection change, or time-based transition, the action should stay blocked or unknown until the consumer refreshes the context and revalidates the target. The projection should be redacted by default rather than serializing the whole application state, especially for account or checkout views.

    I maintain Hronaut, a local visible Browser/MCP workspace, and this is AI-assisted design feedback—not a WebMCP compatibility or integration claim.

  5. victorhuangwq commented on Oct 1, 2026

    @victorhuangwq
    Contributor

    Here’s an idea I’ve been brainstorming for the declarative side: letting developers mark specific parts of their existing HTML as named context, similar to how we expose forms as declarative tools.

    For example, with purely illustrative syntax:

    <section contextname="product"
             contextdescription="The product currently being viewed">
      <h1>Pixel 9 Pro</h1>
      <p>Selected variant: 128GB</p>
      <p>Availability: In stock</p>
    </section>

    The benefit I’m thinking of is scoped context. Developers explicitly identify which content would be useful to agents. Agents can then request the relevant scope rather than inspecting the full DOM to discover what matters.

    I can see this being useful for browser and extension-based agents too, not just in-page agents. Instead of repeatedly taking broad DOM snapshots or screenshots for this information, they could retrieve a small, relevant piece of context. Combined with change notifications, this could be a more token-efficient way to stay in sync with the page.

    On the consumer side, we could also separate discovery from retrieval:

    getContexts() to discover available context names and descriptions, analogous to getTools().
    getContext({ name: "product" }) to retrieve the current snapshot for a particular scope.

    The browser/agent integration could expose retrieval as a read-only tool. An agent requesting the product scope could receive something like:

    {
      "name": "product",
      "description": "The product currently being viewed",
      "content": "<h1>Pixel 9 Pro</h1><p>Selected variant: 128GB</p><p>Availability: In stock</p>"
    }

    I went back and forth on whether the browser should return the scoped HTML fragment or a structured object. The fragment is zero effort and does not differ from what the user sees, so I started there. If people really want structure in markup, especially for items, our old friends itemscope have been waiting a very long time for a second job haha - such that if the html has items in them we can conver them easily.

    I also like the direction @beaufortfrancois is suggesting with the imperative registerContextProvider() proposal, particularly when developers want to construct specific context explicitly, including state that doesn’t map neatly to visible page content. Ideally, both approaches could feed the same discovery and retrieval mechanism.

    For keeping things in sync, consumers could opt into observing a named context and receive contextchange notifications when it changes. @yoavweiss I think this helps with the concern you raised: since declarative context is bound to the live DOM, the browser can detect when a scope changes regardless of whether the cause was, so the snapshot an agent retrieves would reflect what the user is currently looking at. Both of your examples (product details, on-file shipping addresses) seems like they would map naturally onto the idea of named context, potentially reusing itemscopes as wells.

    An additional side effect of this proposal is that untrustedContentHint would make a lot of sense in the declarative version too. There have been separate discussions about how sites hosting user-generated content, like reviews, could tag that content as untrusted, and a declarative annotation on the scope would give them a straightforward way to do it.

    With these proposals here, we would finally make the “C” in WebMCP real haha 😄

  6. beaufortfrancois commented on Oct 13, 2026

    @beaufortfrancois
    Collaborator

    FYI OpenAI just released their Browser Annotation API that lets websites customize what people select, what context accompanies their feedback, and which controls they use to preview changes before sending an annotation to ChatGPT. Check out https://learn.chatgpt.com/docs/annotations-extensibility

    To expose actions ChatGPT can take on your website, add Site tools (WebMCP). Annotations bring the person’s selection and feedback into the conversation; site tools let the agent act on that context through your application’s existing capabilities.

  7. victorhuangwq commented on Oct 15, 2026

    @victorhuangwq
    Contributor

    I took a closer look at OpenAI's Browser Annotation API docs that you shared, and I think there's also an interesting additional use case for markups that was brought up.

    In their model, a site can mark a card, a row, etc. as one selectable object with a name (oai-annotatable). When the user clicks it in annotation mode, the agent gets that exact object instead of whatever DOM node happened to be under the cursor.

  8. kenchris commented on Oct 20, 2026

    @kenchris

    I work on an agent frontend, and I think what this issue is circling is what we call ephemeral context. The name may not be familiar here, so let me describe it.

    A model only knows what is in the request it is sent. Some of what it needs for a turn isn't part of the conversation and shouldn't become part of it: the current time, which page is open, what the user has selected. A frontend sends that with the latest message and leaves it out of the saved history. One turn might be sent as

    [page state] {"url": "https://example.com/doc/18", "selection": "Current draft paragraph...", "revision": 18}
    Make this paragraph shorter.
    

    while the history keeps only Make this paragraph shorter. On the next turn the state is read again and replaces the old one.

    We keep it out of the history for three reasons. The history is replayed on every later turn, so anything in it is paid for again each time. It goes stale at once, and a saved selection that no longer exists is worse than none because the model may act on it. And it breaks prompt caching: serving stacks, local (llama.cpp, vLLM) and hosted, reuse the work already done for the longest identical start of a prompt, so something volatile in the middle makes everything after it be computed again. The same effect is why tool sets that change from turn to turn are costly (see #262 and #328, since the tool list sits at the start of the prompt).

    A tool doesn't fit this well, because a tool result stays in the history. A page that wants the agent to know its state today can hope the model calls a get_state tool, which costs a round trip every time, may be skipped, and leaves outdated copies behind. Or it can say nothing, and the agent has to snapshot the page or walk the DOM, which is the problem this issue starts from.

    So for the design I'd specify it as current state and not as a log: the latest value replaces the previous one, and an agent is free to keep it out of the history. I'd also separate "the context changed" from "here is the current context", like earlier in the thread, so an agent can refresh when it matters and read the state when it builds the next turn. The registerContextProvider() idea has the right shape for the second one. It should be short, with more on request like in #255, since agents will truncate anyway and it's better if the page decides what comes first. And it is page-controlled text that goes straight into the model's context, so it needs the same care as a tool result, and since it can contain personal data an agent should decide per page whether it's sent at all (compare #313 and #331).

    MCP Apps has this as ui/update-model-context: the app publishes its current state and the host puts the latest copy into the next turn.

  9. RattanPriya commented on Oct 23, 2026

    @RattanPriya

    Merchant-side requirements, from someone who builds ad-tech/commerce pipelines (and spoke on agentic commerce at Programmatic I/O 2026):

    The named-context model maps well onto storefronts, but three things need to hold for real commerce use:

    1. Live-DOM binding is the load-bearing property. Product variant, price, and availability change via SPA updates constantly. If getContext() can return a stale snapshot, an agent can quote a price that no longer exists — a worse failure mode than no context at all. contextchange notification should be a normative expectation for commerce-named contexts, not an optional extra.
    2. Untrusted content scoping matters for UGC. Reviews, Q&A, and seller descriptions on marketplaces are untrusted third-party content rendered inside the merchant's origin. A declarative annotation on the scope gives agents a clean trust boundary — worth aligning with the consequentialHint discussion in Scope of consequentialHint for high-risk read tools #331.
    3. Cross-origin reality. On real storefronts, checkout/payment tools live on different origins (merchant → payment provider). If named contexts can't be discovered across origins under the two-sided opt-in model (Document cross-origin tool exposure and two-sided opt-in in Section 6.3.4 #326), commerce agents will bypass this entirely and go back to DOM scraping. The tool-discovery session should decide whether contexts follow the same exposure rules as tools.

    One question for the F2F: is there any merchant or payments voice currently in the design conversation? The spec examples (search_products, issue_refund, add_review) are all commerce, but the thread so far is browser vendors and agent builders.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions