गाइड
Forms and sign-in inside the chat
Ask for missing details as a card with real fields instead of a list to type, and let visitors sign in to your site without leaving the conversation.
इस पेज पर
Busymate AI can ask for what it is missing as a form card — labelled fields and one button, inside the conversation — instead of writing "I'll need: 1. your name 2. your phone number". On a phone that difference is the whole experience: the right keyboard per field, the visitor's own autofill, and nothing to remember. The same card is how a visitor signs in to your site without leaving the chat.
1. Decide which action needs details
Any action the visitor asks for that you cannot complete from what they said: a booking that needs a name and a phone number, an order lookup that needs the order number, a return that needs a reason. Write down the fields, their types, and which are required.
2. Answer with a card instead of a result
A tool asks for a form by returning one, in place of its answer:
BusymateAI.registerPageTools([
{
name: "book_table",
description: "Book a table. Call with no arguments to show the booking form.",
inputSchema: { type: "object", properties: {} },
annotations: { readOnlyHint: false },
async execute(input) {
// No details yet? Ask for them as a CARD, not as a sentence.
if (!input?.name) {
return {
$bmForm: 1,
title: "Book a table",
description: "Two minutes and you're done.",
fields: [
{ name: "name", label: "Your name", type: "text", required: true, autocomplete: "name" },
{ name: "phone", label: "Phone", type: "tel", required: true, autocomplete: "tel" },
{ name: "party", label: "People", type: "number", min: 1, max: 12 },
{ name: "time", label: "Time", type: "time" },
],
submit: { label: "Book it", tool: "book_table" },
cancel: { label: "Not now" },
};
}
// Submitted: the same tool, now with the visitor's values.
const response = await fetch("/api/bookings", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(input),
});
return response.ok ? await response.json() : { error: "could_not_book" };
},
},
]);Answer with $bmForm: 1 and a fields array. submit.tool names where the values go — usually the same tool, now with arguments. A field type is text, textarea, tel, email, number, date, time, select or password, with optional required, placeholder, help, autocomplete, min/max, and options for a select. Twelve fields at most.
3. Or declare it from your MCP server
A connected MCP server needs no page: attach the identical object at _meta.ui.form on the tool result. That is the same declaration channel as _meta.ui.resourceUri — one says "mount my document", the other "draw these fields" — and both render on the visitor's surface, the embedded widget and your hosted chat, not only in the Console.
If you write neither, the assistant still shows a card: it asks for the details it is missing with the platform's own form rather than a list. Your own tool is better, because it knows its fields and completes the action as well as collecting it.
4. Sign a visitor in without leaving the chat
If what they asked for needs their account, the honest answer is not "go and find the Sign in button". Register a page tool called sign_in that returns a sign-in card:
BusymateAI.registerPageTools([
{
name: "sign_in",
description:
"Show the sign-in form in the chat when something needs the visitor's account.",
inputSchema: { type: "object", properties: {} },
// Showing a form changes nothing, so no confirmation stands in front of it.
annotations: { readOnlyHint: true },
execute: () => ({
$bmForm: 1,
title: "Sign in",
description: "You'll stay right here in this conversation.",
fields: [
{ name: "email", label: "Email", type: "email", required: true, autocomplete: "username" },
{ name: "password", label: "Password", type: "password", required: true, autocomplete: "current-password" },
],
submit: { label: "Sign in", tool: "sign_in_submit" },
cancel: { label: "Not now" },
}),
},
{
name: "sign_in_submit",
description: "Complete the sign-in the card collected.",
inputSchema: { type: "object", properties: {} },
annotations: { readOnlyHint: false },
async execute({ email, password }) {
const response = await fetch("/api/login", {
method: "POST",
credentials: "same-origin",
headers: { "content-type": "application/json", "x-csrf-token": window.CSRF_TOKEN },
body: JSON.stringify({ email, password }),
});
// `signedIn: true` is the ONE thing the chat reads. On it, the widget
// re-asks your page for an identity token and the SAME conversation
// continues signed in — no reload, nothing retyped.
if (!response.ok) return { signedIn: false, error: "invalid_credentials" };
return { signedIn: true };
},
},
]);A password field is accepted only on a card that submits back into your own page, so its value goes from the input straight to your execute — never to the assistant, the transcript or a log, and the settled card shows ••••• rather than the value or its length. Send your CSRF token and rate-limit the endpoint as you already do: this is a form on your page.
5. Let the conversation carry on
When your sign-in tool answers { signedIn: true }, the widget asks your page for a fresh identity token — the getIdentity handoff in Recognize signed-in customers — and re-mints the session in place. Nothing reloads, the visitor retypes nothing, and what they asked for before signing in is answered straight after. The token is verified against your registered JWKS exactly as at launch, so signing in here grants nothing a normal sign-in would not.
Verify
- Ask for something your tool needs details for. A card appears with your fields — not a numbered list in a message.
- On a phone, tap the phone field: the dial pad opens. Tap the email field: the @ row does.
- Leave a required field empty and submit: it says so and nothing is sent.
- Submit the form. Your tool runs with the values and the assistant answers from what it returned.
- Signed out, ask for something needing an account: the sign-in card appears in the chat, not a link away.
- Sign in from the card. The same conversation continues, now identified, and your original request is answered without repeating it.
- Open the tool call's details: the password is
•••••, never the value.
Next
- Let the assistant use your page — registering the tools a card belongs to.
- Recognize signed-in customers — the identity handoff a sign-in card completes.
- Connect your MCP server as assistant tools — declaring a card from a server instead of a page.
सवाल
Does the assistant see what the visitor types?
Only for an ordinary form, where the values become a visible message — which is what a booking or an order number should be. A card that submits into your own page never shows its values to the assistant, and a
passwordfield is only accepted on that kind of card.What if my tool returns something that is not a form?
Nothing changes. The card only renders for a result that positively declares
$bmForm: 1with at least one usable field; everything else shows as it always has.Can I use it without WebMCP page tools?
Yes. A connected MCP server declares the same object at
_meta.ui.form, and the platform's own card needs nothing from you at all.Why not just link to my login page?
A link ends the conversation. The visitor signs in somewhere else, comes back to a chat that may have moved on, and retypes their question. The card keeps the thread.