Inspiration WebMCP is incredible for letting websites expose structured capabilities directly to AI agents. But the moment I saw it, one question immediately jumped out at me:
If an agent can discover what a website can do, who decides what it is actually allowed to do?
Capability discovery and authority are two very different things. That distinction is the entire foundation of Moirae Console.
I was already working on The Fates—a deterministic governance and control-plane project for AI systems. For the WebMCP Challenge, I wanted to pull that architecture out of the backend and turn it into something visible and tangible. Moirae Console is the human- and agent-facing surface for that idea.
I set a deliberately narrow goal: let an AI agent discover useful tools via WebMCP, but force an independent governance check before it can actually disclose protected data or trigger a host-side action.
The central idea is straightforward:
WebMCP exposes the capability. The Fates determines the authority. Moirae makes that boundary visible to the human.
What it does Moirae Console exposes exactly two WebMCP tools on a fixed demonstration document: inspect_document and publish_document.
Here is the catch: just because an agent discovers these tools doesn't mean it has permission to execute them. Instead of firing immediately, the request passes from the WebMCP browser surface to a trusted host boundary, and then hands off to Ananke, the Fates authority component.
Ananke evaluates the request and returns one of three decisions: ALLOW, REQUIRES_APPROVAL, or DENY.
For a simple inspection, Ananke returns ALLOW, permitting the host to disclose the document. For a publication request, it initially returns REQUIRES_APPROVAL. The agent cannot approve its own action—a human has to step in and perform a separate approval step before the host asks The Fates for fresh execution authority. (If you run the restricted-agent demonstration, the exact same publication attempt simply receives a DENY, and nothing happens on the host side).
By showing the governance decision separately from the actual host effect, Moirae proves that authorization and execution don't have to be mashed together into a single, insecure step. For the human, that means fewer blanket permissions and fewer interruptions: low-risk actions can proceed automatically, sensitive actions pause for approval, and forbidden actions stop cleanly. Without a structured interface like WebMCP, this kind of collaboration tends to depend on application-specific automation or prompt conventions; here, the agent discovers real web capabilities while authority remains independently governed.
How I built it The browser app is built with React, TypeScript, and WebMCP. The WebMCP surface is intentionally thin, strictly registering the two public tools via document.modelContext.registerTool(...).
I explicitly locked down the browser. It isn't allowed to choose privileged parameters like governance actions, credentials, caller authority, or publication destinations. The host completely controls those values.
The execution path looks like this:
AI agent discovers tools via WebMCP.
Moirae Console browser sends a bounded, same-origin API request.
Trusted Moirae host authenticates the request and passes it to Ananke, the authority component used by this challenge runtime, within The Fates architecture.
Ananke evaluates and returns ALLOW, REQUIRES_APPROVAL, or DENY.
The host verifies the authority and finally executes the effect.
Crucially, the host independently validates the authority returned by The Fates before doing anything. The protected document and the Fates credentials never even enter the browser bundle.
Everything runs on public, pinned source checkpoints of Moirae Console, Project Ananke, and Project Adrasteia (Runtime Contracts 0.6.2). It's all open-source and Apache-2.0 licensed.
Challenges I ran into The hardest part wasn't registering the WebMCP tools—it was preserving the strict distinction between an agent requesting something and the system having the authority to actually do it.
Once I started testing that boundary aggressively, several thorny implementation problems popped up:
Human approval leaks: Initially, there was a risk of sending too much approval context to the browser. I fixed this by giving the browser a short-lived, opaque handle. The real authoritative identifier stays strictly on the host. Clicking "Approve" just asks the backend to perform a new governed transition, rather than blindly trusting a local UI state change.
Concurrency: I had to make sure a single human approval couldn't accidentally trigger multiple effects if a bunch of requests hit the server at once. The publication path now relies on bounded atomic installation and strict replay/idempotency controls.
Canonicalisation & evidence binding: A governance system is useless if one layer authorizes a request but the next layer executes a slightly different version of it. The Console actively recomputes and verifies authority bindings rather than trusting values just because they came back in an HTTP response.
Failing closed: If The Fates goes offline, returns corrupted evidence, or spits out something the Console can't verify, the system absolutely cannot default to granting permission. In the tested failure paths, it fails closed rather than treating uncertainty as permission.
Accomplishments that I'm proud of Instead of treating this like a fragile hackathon demo, I treated it like a system that should be attacked.
I'm incredibly proud of the testing rigor. The final candidate passed a fairly brutal test suite: 186 Moirae Console tests, 266 Ananke tests, and 407 Runtime Contracts tests, alongside strict TypeScript checks, dependency audits, and live testing of the approval flows, replay behaviour, and concurrency/race conditions I designed the system to withstand.
I also verified the live Railway deployment in ChatGPT’s in-app browser, where ChatGPT discovered the two WebMCP Site Tools and successfully invoked the governed inspection flow.
Even better, I ran a repository-aware adversarial review using an independent AI model. I gave it access to the actual repos, shell, and running services, and let it try to break the system. It attempted bypasses, credential confusion, replay attacks, and evidence substitution.
The result? PASS WITH LIMITATIONS. It didn't find a single HIGH or CRITICAL blocking defect. I chose to thoroughly document the remaining known limitations rather than pretending I'd built a flawless production security system for a hackathon reference implementation.
What I learned The biggest takeaway is that capability and authority fundamentally need different abstractions.
It's fantastic that an agent knows a tool exists, and it's great that it knows how to invoke it. But neither of those facts means the resulting real-world effect should actually be allowed. WebMCP gives us a beautiful, clean way to handle the capability side. Building Moirae showed me how powerful it is to pair that with a hardened, independent authority layer.
I also learned that "human-in-the-loop" design is broken when it's just a confirmation popup bolted on as an afterthought. Human approval has to be treated as a core part of the authority model—a separate transition that the agent cannot perform through the exposed WebMCP surface.
Finally, adversarial testing proved that systems rarely fail because of a single "bad function." They fail at the boundaries: identity, freshness, canonicalisation, and replay. Hardening those boundaries changed my entire design.
What's next Moirae Console is a small demonstration, but it points toward a much safer, broader model for agentic web applications.
The agent can discover capabilities and propose useful work, while the human only steps in when their authority is actually required. Low-risk actions can continue automatically, sensitive operations can pause for a real human decision, and forbidden actions can stop cleanly at the host boundary.
That creates a model where people and agents can collaborate without giving the agent unrestricted control.
A production version would need stronger deployment identity, durable state, signed authority evidence, monitoring, and recovery guarantees. Those are deliberately documented as future work rather than hidden behind the hackathon demo.
WebMCP doesn't just give agents more things to do. It makes those capabilities structured enough that we can finally govern them properly.
Built With
- api
- codex
- css3
- github
- html5
- javascript
- node.js
- openai
- project-adrasteia
- project-ananke
- react
- rest
- the-fates-ecosystem
- typescript
- vite
- vitest
- webmcp
Log in or sign up for Devpost to join the conversation.