Persona.js - Add WebMCP-native AI chat to any Frontend
by•
Persona is a lightweight, open-source AI chat UI library that embeds into any website, from modern apps to static HTML. Unlike React-based chat frameworks, Persona is framework-free, backend-agnostic, and WebMCP-native, so your assistant can discover and execute tools exposed by the parent page. Add streaming chat, voice, theming, and interactive copilot experiences without rebuilding your frontend or writing bespoke APIs.


Replies
The boundary I’d want to inspect in every WebMCP app is the receipt after a mutative step, not just the approval before it.
Tool name, page/app context, inputs used, approval, result, and whether it can be undone. That’s what makes the action feel debuggable instead of magical.
Persona.js
@blah_mad sounds like you've built something real before :)
So while Persona does have built-in event debugging in the UI you can optionally turn on (screenshots attached), there is also a deeper set of JS events you can listen to for messages, results, approvals etc in a lot more detail: https://github.com/runtypelabs/persona/blob/main/packages/widget/docs/PROGRAMMATIC-CONTROL.md?plain=1#L562
The messages in particular seem to be right up your alley for the deep debugging use case:
You'd get that info by hooking into Persona init and watching for the assistant messages:
Persona.js
@blah_mad your question made me realize we could have docs specifically related to tracking the mutations via these events, so I added it: https://github.com/runtypelabs/persona/blob/main/packages/widget/docs/PROGRAMMATIC-CONTROL.md?plain=1#L758
Thanks again!
Lancepilot
Persona.js
@odeth_negapatan1 thanks! And yeah, we have tested on WordPress and Shopify. Persona drops in great there because you can use the script tag (CDN) deployment which doesn’t need a build step. All the init and theme configuration can be set in script tag data params.
We spent a lot of time determining the right loading methods for each type of site. Getting the different methods working in the same library while keeping the bundle size tiny, without regressions in functionality, was not easy.
I had been toying around with the idea of writing an internal tool for our implementation team to quickly spin up, configure and iterate on accounts with a promptable interface. Our platform is highly customizable and composable so this is typically a pretty heavy lift to even get to a demoable state for a customer, much less to a go-live state. Persona arrived at the perfect time and I was able to get it working within a day, and I expect it to start accelerating our implementation work immediately. WebMCP makes this extremely powerful. Haven't had to lean on the polyfill as we use Chrome, so I can't speak to that part, but I imagine there are plenty of people and companies out there with use cases similar to ours that this would work perfectly for even without that.
Persona.js
@erik_christensen really appreciate your real-world validation, it was great to see how cleanly Persona's capabilities mapped to the needs of your team. I think you'll be a great real-world success story very soon as you get to production with it - can't wait to see what you build.
Persona.js
@erik_christensen exactly. I love the emergent benefit you get from thinking about the frontend as a source of tools. Many times the logic you already built there is 1:1 with what you’d want an agent to perform.
The "discover and execute tools exposed by the parent page" part is the interesting bit, and also where I'd want to hear your boundary story. Once the in-page assistant can call real parent-page tools, the chat input becomes an execution surface: a pasted block of text, or third-party content rendered in the thread, can try to talk the model into calling a sensitive tool (submit, delete, pay) the user never actually asked for.
Does the host page get a way to mark which exposed tools are model-callable vs require an explicit user gesture, and is there anything in between "tool is discoverable" and "tool just ran"? That separation is what would make me comfortable dropping this into a page that already has real write actions.
Persona.js
@tang_weigang some of what you are talking about is backend related, so it's up to the AI framework or platform you use.
The actual SSE spec Person is using has an "await" event that can be implemented which includes information on the reason (LLM generated) and user decision (approve, always approve, deny, timeout). The UI automatically shows when using that event and is extendable with your own approval UX.
On the WebMCP front, you are in control over what is registered on the front end, which usually means you have already built something to respect permissions (say, if the session is trying to be hijacked with a link, you'd verify the request on the backend powering the JS function registered for WebMCP)
Runtype is the reference implementation of the SSE spec and includes more control on the backend for which tools are approved both at the model level and WebMCP tool level (so, you can have registered tools that are "blocked" for a certain agent). The SSE events are enriched with provenance as well, so you can track the whole chain.
In the repo the most thorough example beyond that is the AI SDK WebMCP example, which includes a supporting doc detailing a lot of what I'm talking about here for those building in another stack: https://github.com/runtypelabs/persona/blob/main/docs/webmcp-without-runtype.md
the "rest of the internet" framing is the right one. most ai chat libraries assume you're shipping a new react app on a clean slate. that's not where 90% of the actual web lives. shipping into someone's existing wordpress, a 2018 next.js install, or a php shop is where real adoption happens.
webmcp-native is the part i keep thinking about. once an agent can actually act inside your ui through a standard protocol, the chat stops being a help widget and starts being an interface. curious how you're thinking about discovery. letting other agents know what a persona instance can do without each integration being a custom contract.
Persona.js
@thenameisarian thanks! In terms of discovery, the WebMCP register tool methods that Persona looks for are the same ones other agents that interact with the front end would read from in the future. So the standard helps there.
We also have built into A2A and MCP as standards on the server side at Runtype to help other agents hook into what is powering Persona too.
This is great, just at the right time. This could make chat widgets as easy to add to existing websites as including a new Google Font or FontAwesome. And the WebMCP is great so other agents can use the site. Makes it extremely useful. Quick question, can you set up token limits, per visitor/user?
Persona.js
@jn263 thanks! The token limits could technically be set up by listening events Persona fires and preventing messages from being sent in a session past a certain amount.
That of course would need to be enforced in the backend as well if you wanted to really prevent it!
@_becomevocal_ That's one of the things I think can really make or break a product like this. Some sort of guardrail that will keep the conversation on topic and prevent skyrocketing API costs.
Protected params is the one that moves the needle for me, taking the value out of the model's hands beats asking it to behave. The gap I'd still watch: that locks the WHAT, but the model still picks WHICH tool and WHEN off whatever a prior getter handed it. We hit a loop where every arg was clean but a poisoned read steered the model to fire a legit mutation at the wrong moment. Do you scope which tools are even available by app state, or does the model always see the full set?
Persona.js
@dipankar_sarkar for WebMCP the default is Persona reads the active tools so you’ll get the active state of the app if, however you can override that functionality. On the Runtype side we have more server tooling to lock down tools, implement guardrails, and log requests that we found the FE alone couldn’t do.
Persona in a way just accelerates you to the point you know more about what your server needs to do, to get the right blend of UX and security.
Macaly
framework free + webmcp native is a smart bet 👏 the standard is gonna matter alot
Persona.js
@petrkovacik Thanks! The team leading the standards effort is really cool as well.
Active-tools-reflecting-app-state is a cleaner answer than I expected, since the menu shrinks with the app instead of sitting wide open. The thing I'd watch from building agent loops: that same liveness means a tool the model planned to call two steps ago can quietly vanish when state changes under it, so a half-formed plan goes invalid mid-run. Do you surface that disappearance back to the model as a signal it can re-plan on, or does the call just fail when it reaches for the tool?
Persona.js
@dipankar_sarkar within a run, the model plans against a snapshot taken at dispatch, so there's no mid-run "tool vanished" notification, but if a tool unregisters before execution, the bridge re-reads the live registry and returns a structured isError result like:
Then the model can re-plan on in the same run without aborting.
Every client-side failure mode (unregistered, allowlist-excluded, user-declined, timeout) resolves this way, and the next turn re-snapshots the registry so the advertised toolset re-converges with app state within one turn.
Mailwarm
How does tool discovery work safely, like can the page expose only certain actions and require a user confirmation before anything runs?
Persona.js
@thamibenjelloun Yep, you have control over the tools registered on the FE and the "await" SSE event handles the user confirmation UX. That last piece is automatically handled in two scenarios within Persona: when you use Runtype and the AI SDK example. However the spec is reasonable easy to add to any AI framework or platform with approvals.