Launched this week

Persona.js
Add WebMCP-native AI chat to any Frontend
558 followers
Add WebMCP-native AI chat to any Frontend
558 followers
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.







Persona.js
Hey Product Hunt 👋 I'm Nathan, cofounder at Runtype and one of the people who built Persona.
Persona is the world's first AI chat library to natively support WebMCP. It's a framework-agnostic chat UI you can drop into any existing website or app to easily add AI to your product.
Why we built it
Most AI chat libraries assume you're starting from scratch on React. Persona is built for the rest of the internet: existing sites, mixed frontend stacks, proprietary CMSes, and teams that want a modern AI experience without rebuilding in React. Drop it in with no build step, theme the whole experience through config and the built-in editor, then ship it as a chat widget, a full-screen ChatGPT/Claude-style surface with artifacts, or something in between.
It looks polished out of the box & you can make it pretty far down the path of customization with no code, but still gives developers hooks and plugins when they want to go deeper.
It’s built in Vanilla JS so it can work anywhere - WordPress, Shopify Liquid, Static HTML sites, and more.
The part we're most excited about: WebMCP 🧩
Persona is the first Agent UI framework to natively support WebMCP, which just shipped in Chrome and has polyfills available for all other browsers. With WebMCP, you can register agent-facing tools directly in your frontend, which allows Persona-based agents to directly use your frontend app with much more dexterity & token efficiency than other approaches (headless browsers, DIY frontend tools, etc).
If you already have a sophisticated web app, this can be a much faster path to enabling AI capabilities in your app vs building an entirely new backend. And since WebMCP tools can be hooked directly into frontend code, the experience feels much more like a “copilot” and less like a bolt-on, all the usual side-effects and UX are preserved while your app gets more capable.
Some cool demos:
Fullscreen Assistant → persona-chat.dev/fullscreen-assistant-demo.html
Slides → persona-chat.dev/webmcp-slides.html
Calendar → persona-chat.dev/webmcp-calendar.html
MSPaint → persona-chat.dev/webmcp-paint.html
WebMCP is maturing to the point where it makes sense to start building against it, and Persona makes that easy with a built-in polyfill.
Open source, no lock-in
Persona is MIT-licensed and open source. It's made by an AI platform company, but it's deliberately not coupled to Runtype. You deploy it on any framework and wire it to any SSE-based AI endpoint with a little glue code. Examples for popular frameworks like Flue, Eve, OpenAI Agents SDK, and more are in the repo along with docs your existing coding tools can use to implement it quickly.
If you’re like to deploy a chat agent as quickly as possible, the CLI command:
…will set you up with a WebMCP-capable chat agent on the Runtype platform in minutes.
Who it's for
Builders adding an AI experience to something that already exists, without a rewrite and who want to benefit from us obsessing over the details of frontier AI experiences so they can just deploy them.
Repo, docs, and examples: github.com/runtypelabs/persona
We'd love your feedback! If you've tried adding AI into an existing app, I'm curious what was hardest: picking the right framework, standing up a new backend, wiring up the tools, building out the right evals, or trusting what the AI actually does. Happy to go deep on WebMCP in the comments.
@runtypelabs @runtypical The WebMCP bet is the interesting part beyond the chat UI. Honest question: is WebMCP an actual emerging standard that browsers and agents will converge on, or your own convention for now? The payoff (any agent reading the page's exposed tools) only lands if it isn't Persona-only. If a future Claude or ChatGPT browser extension can read those same tool registrations, that's huge; if it's a closed loop it's a really nice chat widget. Where does it sit today?
Persona.js
@runtypelabs @david_marko we're betting that it is; right now it's shipped in Chrome with Microsoft also promising support in Edge. Polyfills are available (we use one) so you can use it in any browser already.
In terms of converging on it - in addition to its utility for on-site assistants like we're showing here with Persona, WebMCP also has utility for agentic browsers (including headless approaches, like Browserbase/Stagehand) and agents native to the browser layer itself like we're seeing from Gemini in Chrome. The utility of this in the medium term will be whenever you ask an agent to "Go accomplish X task on Y site", the agent will first check for relevant WebMCP tools it can use to accomplish that task with direct, explicit tool calls, before falling back to less-efficient strategies like mimicking clicking on things.
This makes the investment in WebMCP highly leveraged for site owners. The net effect - most websites become WebMCP-enabled and agents are able to help us with tons of tasks online without extra infrastructure.
@runtypelabs @runtypical WebMCP native is a clever play for agent adoption. What was the tipping point where you decided to go open source vs commercial? Bet that shaped your whole go-to-market.
Persona.js
@clquek the tipping point was pretty early on! We saw a split between the people we could help with our platform vs our investment into standards and UX. Most people have the problem of making their agent actually look and function the way they want, in the shape of their product, so we can help most of the market earlier in the cycle.
It also forces any platform efforts we have in the future to earn it's keep and play nice with the ecosystem, which is really important to us. The world needs more people working together.
The WebMCP-native angle is the part I keep thinking about, since you said a tool registration can invoke basically any JS function on the page. That cuts both ways. Once the agent can drive real UI, what stops it from firing a destructive or paid action it misread the user's intent on? Is there a scoping or confirmation layer for high-stakes tools, or is safety entirely on the developer to only register the safe ones? Curious how you draw that boundary.
Persona.js
@dipankar_sarkar yes! Similar to the protections that MCP tools typically have in chatbots and harnesses today, WebMCP tools can be tagged based on whether or not they are 'read-only' or mutative/potentially destructive, and Persona (in collaboration with the agent backend) can support HITL (Human-in-the-loop) approval flows for those actions.
This can be seen on demos like these:
https://www.persona-chat.dev/approval-demo.html (also has a great example of a Persona plugin)
https://www.persona-chat.dev/webmcp-demo.html ("Add to cart" requires approval)
On the general topic of WebMCP security, both Persona and Runtype have adapted Google's best practices around WebMCP security, which you can read here:
https://developer.chrome.com/docs/agents/security
https://developer.chrome.com/docs/ai/webmcp/secure-tools
That split is fair, and the server framework is genuinely where I'd want the real teeth. The part I keep snagging on is that the dangerous chain spans both halves. The client sees a getter fire, the server sees a mutation fire, but the step where one fed the other lives in the gap between them. We hit exactly this building agent tool-loops: every call looked safe on its own, the damage was in the ordering. Do you end up passing any call provenance across the seam, so the server side can see what the client already ran?
Persona.js
@dipankar_sarkar yes, in this model all the tool calls are ultimately emitted by the server and executed on the client, so there is nothing the server is not aware of; it is the initiator.
Naturally the browser is an insecure environment and the output itself needs to not be fully trusted, which is where server-side frameworks like Runtype implement (or should implement) appropriate guardrails and similarly site owners should follow best practices. The Chrome team has been appropriately conservative in their initial release and I would expect to see browsers adopt incremental improvements to CSP for example to limit the ability for arbitrary 3rd-party scripts to inject malicious tools.
Persona.js
@dipankar_sarkar to back up what @runtypical is saying on the server side, in practice we've done a few things there alongside the client tool calling in Persona to make it easier to execute safely.
1) Define tool params as 'protected' which will be filled in outside of the LLM. The model doesn't see the param ever, as we inject them server side before the tool call event is streamed back.
2) Run tool calls through an explicit "local" tool call path, which escape hatches out of the main loop like a approval step, but for tool calls you want to process specifically through another server / harness / etc. WebMCP support in Persona uses a form of this, made more generic on the client side so any platform can implement it too.
We optionally store the full input and output for every call when Runtype is involved, which can be included on the streamed events as well so your server proxying the request can use all of that data to decide on the fly. We provide a server side SDK to hook into what is streamed for those advanced cases.
The WebMCP angle is what makes this feel more than another chat widget to me.
The safety/usability detail I’d want as a builder is a clear tool-call preview: what page tool the assistant is about to use, what state it will change, and what the user can undo.
For existing apps, the scary part isn’t adding chat; it’s letting an agent touch real UI flows without turning every action into a mystery. If Persona can make those side effects visible and reversible by default, it would lower the trust barrier a lot.
Persona.js
@grace_lee26 Persona has a lot of knobs here - we support tool call approval by default (and map it to how the WebMCP tools are configured), and you can make the display of the tool call details more or less verbose based on your preference as a builder.
There's a demo here that shows more about how this works:
https://www.persona-chat.dev/approval-demo.html
There's also a fun example of a Persona plugin on this page!
That read-only vs mutative tagging plus HITL is exactly the right shape, thanks for the concrete demo. The bit I am chewing on is where the tag comes from. If the developer declares it, safety still rests on them classifying correctly, and a tool that looks read-only (a getter) can feed its result straight into a mutative call. Does the HITL gate reason about a chain that ends in a mutation, or approve per individual call? That chaining case is where I would expect intent to slip.
Persona.js
@dipankar_sarkar thank you for the nuanced question! The HITL gate is per-call from Persona's perspective, but keep in mind that Persona is only the client-side of the equation when making a chat agent w/ WebMCP capabilities - and indeed the second half of the equation is the server-side agent framework, which is where most of the defense-in-depth should be from a security perspective. When connected to Runtype, you can apply further policies on top of the registered WebMCP tools.
Ultimately site owners & platforms do have a responsibility to design tools well (or use trusted frameworks/extensions to expose tools), just as they've had responsibilities for all other matters of site security around XSRF and so forth.
I would argue that feeding the result of a read-only tool into a mutation is, most of the time, a feature! An agent that can read state and use it to inform the next call is what makes copilot experiences feel very intelligent, so they shouldn't be rejected out of hand, just designed defensively.
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.
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!