Inspiration
AI agents are becoming capable of acting directly on websites, but many useful workflows involve information that should not automatically become part of the agent's context.
A rental application is a simple example: an agent may need to know whether an applicant satisfies an income requirement, but it does not need the applicant's actual income. The same applies to identity data, age checks, financial values, health information, and many other private facts.
Sealed explores a simple idea:
The agent gets the decision — not the secret.
What it does
Sealed is a reusable WebMCP privacy boundary for agentic web applications.
The page exposes a small, state-aware set of WebMCP tools. Public information can move normally, while sensitive facts are evaluated locally inside the page and only allowlisted results are returned to the agent.
The primary rental demo shows the full pattern:
- Public applicant information can be updated by the agent.
- A private
income_3x_rentpredicate is evaluated locally. - The agent receives only
satisfiedornot satisfied; the income itself is returned asvalue: "withheld". - A passport verification requires explicit human approval.
- Approval creates a random, session-bound opaque reference that does not encode the passport number.
- Every private crossing is recorded in a visible Boundary Ledger.
- Human review returns
submitted: false; Sealed has no final-submission tool. - After review, the WebMCP capability surface contracts from five tools to only two safe read/flag operations.
A second Membership scenario reuses the same primitives to evaluate age_18_plus without exposing the user's date of birth.
Why WebMCP
WebMCP is load-bearing in Sealed.
Without structured site tools, an agent would have to infer meaning from DOM content, accessibility trees, or screenshots. That is especially problematic around sensitive data because the agent may end up observing more information than the task actually requires.
With WebMCP, the page defines the capability boundary explicitly.
Sealed registers tools through document.modelContext.registerTool(...), including:
get_application_contextset_public_fieldsflag_uncertainrequest_private_bindingevaluate_private_requirementrequest_review
The available tool surface changes with workflow state. The agent can ask for a useful decision, but no tool exists that returns the underlying private value.
How I built it
Sealed is built with Next.js, React, TypeScript, and WebMCP and is deployed on Vercel.
The privacy boundary has several layers:
Fixed private predicates
The agent selects from predefined checks such asincome_3x_rentorage_18_plus; it cannot construct arbitrary queries over private values.Local execution
Private values are generated and retained page-locally. Predicate evaluation happens inside the application.Guarded egress
Private operations pass through a single allowlisted response path that releases only approved fields such as status, requirement, andvalue: "withheld".Human-approved opaque bindings
Consequential credential binding requires explicit user approval and returns only a random session-bound reference.Boundary Ledger
Each private operation records the exact sanitized payload released across the boundary.State-dependent capabilities
Once human review is requested, Sealed removes write/private tools and leaves only two safe operations.
Challenges I ran into
The hardest part was separating three different things that initially looked like the same problem: page registration, browser discovery, and agent invocation.
A page can successfully register WebMCP tools while the current ChatGPT browser session has not yet attached those tools to the conversation. I reproduced cases where the page showed five registered tools while the agent still reported that site tools were unavailable.
Testing against the official Chrome Labs WebMCP demo helped isolate the difference between application behavior and client-session discovery.
That led to an important UI correction: Sealed now says “Site tools registered”, rather than claiming that an agent is connected simply because local registration succeeded.
I also had to guard against stale sessions, repeat probing of private predicates, response/ledger drift, and accidental leakage through seemingly harmless response fields.
Accomplishments I am proud of
The final implementation demonstrates a complete human-agent privacy workflow rather than a single tool call.
The strongest result is that the same private operation produces both:
- the agent-visible sanitized response, and
- the visible Boundary Ledger entry
from the same guarded source of truth.
The rental flow demonstrates public writes, private decision-only evaluation, explicit human approval, opaque binding, human review without submission, and dynamic capability reduction.
The membership scenario shows that the pattern is reusable rather than hard-coded to one application.
What I learned
WebMCP is not only useful for making agents more capable. It can also make the boundary between a web application and an agent more explicit.
The most useful question became:
What is the minimum information the agent needs to complete this task?
For many workflows, the answer is not the private value itself. It is a decision, status, capability, or reference derived from that value.
I also learned that real browser behavior matters as much as implementing the API correctly. Registration, discovery, invocation, approvals, and session lifecycle all have to be tested end to end.
What's next
Sealed is intentionally narrow for this challenge.
Future work could explore additional private decision primitives, external verifier-backed references, and reusable SDK-level abstractions for application developers.
For now, the project focuses on one core principle:
Useful decisions can cross the boundary. Raw private values do not have to.
Built With
- next.js
- react
- typescript
- vercel
- webmcp
Log in or sign up for Devpost to join the conversation.