Agent Tools (WebMCP)
The card element can register a WebMCP tool, enter_card, so a browser agent can fill in the card for the shopper. The tool runs inside the Basis Theory iframe: card data the agent sends goes from the browser to Basis Theory without passing through your page, and the tool returns only whether the card is complete, its brand, and its last four digits.
WebMCP is an experimental browser API. Chrome supports it behind the chrome://flags/#enable-webmcp-testing flag and through an origin trial. In browsers without WebMCP, agentTools does nothing.
Enabling agent tools
Set agentTools: true when you create the card element.
- Web Elements
- React Elements
const card = bt.createElement('card', { agentTools: true });
await card.mount('#card');
<CardElement id="card" agentTools />
| Option | Type | Default | Description |
|---|---|---|---|
agentTools | boolean | false | Registers the enter_card tool inside the card iframe. Set it at creation: update({ agentTools: false }) removes the tool and true restores it, but update({ agentTools: true }) on a card created without it throws a ConfigurationError. The React CardElement recreates its element when the prop changes. |
The enter_card tool
enter_card takes the card as it is printed. Every field is optional, so an agent can resend only the field it needs to correct.
| Input | Type | Description |
|---|---|---|
number | string | Card number. Spaces are allowed. |
expiration | string | Expiration date, such as 12/30 or 12/2030. |
cvc | string | Security code. |
The iframe writes the values into its inputs and runs the same handlers as typing, so formatting, validation, change events, and tokenization work the same way they do for a shopper. The tool returns the card's state and never the values it was given:
{ "complete": true, "brand": "Visa", "last4": "4242", "errors": [] }
When a field is invalid, complete is false and errors lists each problem with its field, code, and message. If the element is disabled or readOnly, the tool makes no changes and returns the error code CARD_INPUT_LOCKED. Calls run one at a time, so each result reflects only the values sent in that call.
Registering your checkout tools
The card element provides enter_card. Your page registers the rest of the checkout, such as an order summary and a payment tool, with document.modelContext.registerTool.
Register submit_payment only while the card is complete, and have it run the same function as your pay button. An agent is then offered payment exactly when a shopper could pay. Return the outcome of the charge, not of tokenization: a token alone is not a payment, and the agent treats the tool's result as what happened.
const card = bt.createElement('card', { agentTools: true });
await card.mount('#card');
const charge = async () => {
const token = await bt.tokens.create({
type: 'card',
data: {
number: card.number,
expiration_month: card.expiryDate,
expiration_year: card.expiryDate,
cvc: card.cvc,
},
});
// Your backend charges the token and reports the outcome
const response = await fetch('/api/charge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tokenId: token.id }),
});
if (!response.ok) throw new Error('The payment was declined');
return response.json(); // e.g. { status: 'paid', orderId: 'ord_123' }
};
const submitPayment = {
name: 'submit_payment',
description: 'Pays for the order with the card entered in the card form.',
annotations: { consequentialHint: true },
execute: async () => ({
content: [{ type: 'text', text: JSON.stringify(await pay()) }],
}),
};
let registration = null;
let cardComplete = false;
let paying = false;
// Offer submit_payment while the card is complete, but leave it alone while a
// payment runs: tokenizing clears the card, and unregistering a tool during its
// own call makes Chrome discard the result
const syncSubmitPayment = () => {
if (paying) return;
if (cardComplete && !registration) {
registration = new AbortController();
document.modelContext?.registerTool(submitPayment, { signal: registration.signal });
} else if (!cardComplete && registration) {
registration.abort();
registration = null;
}
};
card.on('change', ({ detail }) => {
cardComplete = detail.isValid;
syncSubmitPayment();
});
const pay = async () => {
paying = true;
try {
return await charge();
} finally {
paying = false;
setTimeout(syncSubmitPayment);
}
};
payButton.addEventListener('click', pay);
Chrome discards the result of a tool that is unregistered while it is running. Tokenizing clears the card, which fires a change event with isValid: false during the call, so the example holds the registration still until pay() returns.
Adding agents beside another processor's form
If your shoppers use another processor's card form, you can add a Basis Theory card element for agents only. Mount it in a container hidden with display: none or the hidden attribute. The tool still registers, and the inputs cannot be seen, focused, or reached by assistive technology. Other ways of hiding it, such as clipping it to one pixel, leave the inputs focusable.
<div id="agent-card" hidden></div>
To pay with your processor's own reference instead of a Basis Theory token, send the card to the processor with the HTTP client from your submit_payment tool. Your page receives the processor's response and never the card.
allowHttpClient: true at initialization. A processor host that is not allowlisted is blocked, and the call rejects with status: -1, the same result as an unreachable host. Contact support to have your processor's host added before you integrate. See HTTP client requirements.const bt = BasisTheory('<PUBLIC_API_KEY>', { allowHttpClient: true });
const card = bt.createElement('card', { agentTools: true });
await card.mount('#agent-card');
const pay = () =>
bt.client.post('https://api.processor.example/payment_methods', {
card: { number: card.number, expiration: card.expiryDate, cvc: card.cvc },
});
Paying with network tokens
If you pay with a network token and cryptogram, the card can be complete without a CVC. Set cvcRequired: false when you create the element. A partly entered CVC is still invalid, and an empty one is left out of the request rather than sent as an empty string. Some processors require a CVC on a card's first use, so check before turning it off.
const card = bt.createElement('card', { agentTools: true, cvcRequired: false });
| Option | Type | Default | Description |
|---|---|---|---|
cvcRequired | boolean | true | When false, an empty CVC counts as complete. Set it at creation: update() throws a ConfigurationError if it tries to change it, and the React CardElement recreates its element when the prop changes. |
Browser support
A cross-origin iframe can register WebMCP tools only when the page grants it the tools permission. The SDK adds allow="payment *; tools" to the card iframe when agentTools is on and the browser supports the permission. In Chrome, the permission is part of the WebMCP origin trial; where the trial or flag is not enabled, the SDK leaves it out so Chrome does not log a console warning.
A page that sends Permissions-Policy: tools=() disables WebMCP for every frame, including the card iframe.
On an https page, enter_card is registered with exposedTo set to your page's origin. Agents that discover tools from the page, such as browser extensions or an agent embedded in your site, only see a cross-origin frame's tools when they are exposed this way. On an http page, such as a local development server, the tool is still registered but is visible only to the browser itself.
Security
- Card data an agent enters travels from the browser to the Basis Theory iframe. Your page's scripts never receive it, and the tool result contains only the completion state, brand, last four digits, and errors.
- The agent handles the full card number when it calls
enter_card, so the agent and its platform are responsible for how they store and transmit it. - Because the tool is exposed to your page's origin, scripts on your page can call
enter_cardtoo. They can only write card data they already have, and they receive the same masked result.
Testing
Chrome DevTools has a WebMCP pane that lists a page's tools, runs them with parameters you enter, and logs each call.
- In Chrome, enable
chrome://flags/#enable-webmcp-testingandchrome://flags/#devtools-webmcp-support, then relaunch. - Open your checkout page, open DevTools, select the Application panel, and choose WebMCP in the sidebar.
- Under Available Tools, select
enter_card, enter4242424242424242as the number,12/30as the expiration and123as the CVC, and click Run tool. The card fills, and yourchangehandler seesisValid: true. - Select your payment tool, such as
submit_payment, and click Run tool. Under Invoked Tools, the call shows Completed and its output, and your page receives a token id or your processor's response.