Skip to main content
Look up any of the 29 Agent Vault MCP tools, plus get_pending_result on the hosted server: what it does, what it takes, what it returns and when it refuses.

Overview

Tools are grouped in the order a run uses them:

Conventions

  • Needs an open page. Page tools act on the browser that open_page launches. Called first, they refuse and tell you to call open_page. The discovery and account tools need no browser.
  • Errors. A refused call returns an MCP tool error (isError: true) whose text is the proxy’s message. The underlying codes and whether to retry are on Error codes.
  • Hosted server. On the hosted server, every tool that uses your account is refused until the account holder chooses what the connection may use on Connected apps. Only get_checkout_recipes works before then. Every tool also takes an optional session argument, which the parameter lists below leave out. A result from a call that ran on a session ends with a line naming its handle, such as session: s_8f3k.... Pass that handle as session on every later call in the task. The server sends no Mcp-Session-Id header, so the argument is the only way back to the session. Without it, a call after open_page cannot reach the open browser: a page tool is refused with “call open_page first”, and another open_page starts a new browser. See On the hosted server and Hosted MCP server errors.
  • Selectors. Any selector Playwright accepts, unless a tool says otherwise. A frame parameter is the CSS selector of the iframe the element lives in.
  • Timeouts are in milliseconds.
  • During a handoff, page tools are refused with challenge_required while the account owner has the page. close_session still works, and get_run_report touches no page.

Status line

Every prose answer’s first text block, content[0].text, starts with one short, machine-readable line, then a newline, then the answer as before. A client that reads only the start of the first text block still gets what it must act on. The line is key=value pairs separated by spaces, with status first, and it is at most 120 characters:
An answer whose text is a JSON object (observe_page, get_run_report, close_session, count, list_frames, detect_challenge, get_session_outcome, get_token_catalog, get_checkout_recipes and the others that answer JSON) has no line before it, so JSON.parse(content[0].text) works as it is. It carries the same fields as its first keys instead, compact or indented as the JSON already was:
When the object already has a key of the same name, it keeps its own value and the status field goes in with a leading underscore. close_session already has a status key ("Session closed"), so its answer starts {"_status":"ok","status":"Session closed",...}. A JSON array (list_page_hint_kinds) is not wrapped in an object: the status line is a text block of its own, content[0], and the array is content[1]. get_pending_result returns the finished call’s own status, as a line or as keys. The hosted server also gives a schema refusal (code=invalid_params) a status line. The local stdio server does not, because the MCP SDK answers those before a tool runs.

On the hosted server

The hosted server has the same 29 tools as the local one, with the same parameters, results and errors, plus get_pending_result for a call that is still running. Two things differ: every tool takes one more parameter, and a browser belongs to a session you name on each call, not to the server process. The hosted browser never tunnels through your machine: its traffic leaves from the deployment’s residential egress where one is configured, and from the proxy’s address otherwise. A residential egress session is HTTPS only.

The session argument

string
The session handle an earlier result in this task returned, such as s_8f3k.... At most 200 characters. Pass it on every call so the call reaches the same browser. Leave it out only to start a new session.
A result from a call that ran on a session ends with one more text item:
The handle is only in the result text. The server sends no Mcp-Session-Id response header: an MCP client takes that header as the id of its own connection, and ends it with an HTTP DELETE when it reconnects, which used to close the browser in the middle of a task. A call without a session argument still uses a handle in the Mcp-Session-Id request header, from a client that kept one from before. A handle belongs to the connection that started it. On any other connection it reads as a session that has ended.

Calls without a session

Read the catalog on the session you fill on. The session keeps what get_token_catalog returned and uses it to check your fills, and it records login writes for get_run_report. So start a task with get_token_catalog (or a login tool) and pass its handle to open_page, rather than letting open_page start a second session. A first call that fails starts no session, with one exception: when a login tool itself returns an error, its session is kept and the handle returned, because a write may have landed before the error.

When a session ends

A session ends when you call close_session, after 13 minutes with no call that reaches its browser, 56 minutes after its browser opened, a minute before its credential lapses, when the gateway closes it to make room at the account’s session limit, or when the gateway restarts. The age check skips a session while a call is in flight, so a long call can still be running at the proxy’s 60-minute limit and fail with hard_timeout. The credential check does not: the session ends then, and its browser closes once the running call finishes. See Limits. A call naming an ended session is refused, and nothing runs. The message is one of two: Both end with “Call open_page without session to start a new one.” The reasons the first message names are:
  • it was idle too long
  • its browser was near the proxy's one-hour limit for a session
  • its credential was about to lapse
  • the account was at its browser session limit and another session of this connection needed the slot
  • no call had used it in the 2 minutes since it started, and this account's hosted apps needed its browser session slot
  • it was asked to (close_session)
When the close could read nothing, what it found is a note: “No report could be read for that session. Do NOT read this as nothing having been filled.”
A gateway restart forgets every session and every record of one. A later call gets only the second message, with no report. Before you repeat anything the ended session may have done, check the account holder’s Activity and Logins pages, or the merchant’s own order state.
When the handle came only from the Mcp-Session-Id header, and the tool can start a session (see Calls without a session), the call starts a new session instead and returns the new handle. A client that kept a handle from before keeps sending it, so this keeps it from being stuck on a dead one. If the gateway had a record of the old session, the result adds a line beginning “The earlier session”, with what its close found.

Closing a hosted session

close_session needs the handle. It takes the session out of use at once, so no new call can enter it, then closes the browser and gives back the session’s credential. Giving it back is best effort: if Agent Vault cannot be reached at that moment, the credential stays counted toward the connection’s 50 until it expires, at most 75 minutes after it was issued. A call that failed with unavailable can also leave a credential that no session owns, which close_session cannot give back. See Limits.
  • Sent again for the same handle, it returns the first answer again.
  • A call still running on the session is ended. If it finishes after the close, the reply adds a line beginning “Calls still running when the session closed finished afterwards”, with what the final close found.
  • Refused for its arguments, it still closes the session, and the reply adds “The session is closed anyway.”
  • If the close itself fails, the reply reads “close_session hit an internal error, but the session is closed.” followed by what the close found.
An HTTP DELETE closes nothing: the server answers it 405, and a session ends only with close_session, after being idle, or at one of the limits above. See Protocol responses.

Repeated and retried calls

  • The same call at the same time. While a call is running, a second call with the same tool and the same arguments on the same session joins it and gets the same answer, instead of running twice. A first call without a session joins the same way on one connection. Once the first has finished, the same call runs again, so polling works.
  • A retransmitted call. Calls that change something are kept for 2 minutes: open_page, click, press, fill_checkout, fill_login, declare_page_hint, close_popup, wait_for_popup, request_challenge_handoff, create_login, set_login_username, rotate_login_password, submit_checkout_recipe, and get_session_outcome with confirmsCheckout: true. The same call again (same tool, arguments, session and JSON-RPC id) within those 2 minutes gets the first answer back and does not run. A new id runs it again.
  • A retransmitted first call. One that started a session never starts a second. get_token_catalog, get_signup_request and wait_for_signup_approval run again on the session the first call started, so they read the current state. The login writes get the first answer back.
  • What is not kept. A refusal made before the tool ran: a sign-in or credential refusal, arguments that fail the schema, a missing or ended session, or a restarting gateway. See Gateway messages. Sending that call again runs it.
  • How many are kept. The latest 200 answers per connection. A call that started a session counts twice. Past 200 within 2 minutes, the oldest are dropped early, and a retransmission of a dropped call runs again. A gateway restart drops them all. After a burst of calls or a restart, check the page or get_run_report before you resend a call that changes something.

Call deadlines

A call still running after 25 seconds, or as soon as the proxy reports that it is waiting on the owner (a signup wait keeps the 25 seconds), answers pending instead of waiting, with a wait id, and keeps running on the server. Fetch its answer with get_pending_result. Do not repeat the call: that starts a second one. A call this slow can be waiting on the account owner, for example the first fill on a new site, which waits up to 15 minutes for them to approve the site. The pending answer names the wait once the proxy reports it, and waiting_on_owner is true only then. When it names one, tell the user right away rather than polling in silence. When it does not, no owner wait is being reported for this call: do not tell the user a new approval is waiting (one they were already told about may still be open in their email or dashboard). Either way, keep calling get_pending_result in the same turn: an agent that ends its turn is never called back, so the answer goes uncollected and the checkout stalls. A hosted call gets 4 minutes, and request_challenge_handoff gets 12. Past that the call answers with the tool’s name, for example “click did not finish within 4 minutes. Its outcome is unknown: check before repeating it.” The call may still finish on the server. Check the page or get_run_report before you repeat it.

get_pending_result

Hosted server only. Get the answer to a call that came back pending.
string
required
The wait id from the pending answer.
Waits up to about 25 seconds, and answers early when the proxy reports a new wait on the owner. If the call has finished, you get its answer. If it is still running, you get pending again, with the same wait id: call get_pending_result again. When the pending answer names a wait on the account owner, tell the user what they need to approve, and where: the email Agent Vault sent them or the banner on their dashboard. Then keep polling in the same turn; do not end it to wait for them. A pending answer that names no wait reports no owner wait for this call: do not tell the user a new approval is waiting. When the reason is known, the pending answer names it. For example, a first fill_checkout on a site the account has not authorized answers: “fill_checkout is waiting for the account owner to approve a new site (www.site.com) … they can approve it from the email titled ‘Authorize new site: www.site.com’ or the banner on their Agent Vault dashboard. It expires at 2026-10-14T14:32:00Z.” Each wait has its own sentence: a new site, a purchase over the card’s approval threshold, a card security code to enter, a challenge to complete, or a signup to approve. The same facts are in structuredContent: Until the proxy reports a wait, the answer says no request to the owner has been reported, waiting_on_owner is false, and the other fields are absent. Once the wait ends and the call keeps running, the answer says the call stopped waiting on the owner and its answer will say what came of the request.

Discover

These tools need no open page.

get_token_catalog

List the mock tokens this key may use: identity, addresses, cards and logins.
string
Narrows cards and logins to one site, for example https://shop.example.com/checkout. Identity and addresses always come back unfiltered.
Returns the catalog: identity, addresses, cards, logins, and, under a url, relatedLogins. It never contains a real card number or CVV. Full shape: Token catalog. How the url filter behaves:
  • A card marked anySite: true is not scoped to a list of sites. It is always included, after any card registered for that site. An empty cards under a url means no card for that site is visible to this key: a key limited to Only selected cards drops every other card before the url filter runs. Ask the account holder to check the key’s card access on API keys before giving up on the checkout.
  • A url that does not parse matches no site. Site-scoped cards and every login drop out, and the anySite cards are still returned.
  • A returned card is a candidate, not a guarantee. The owner’s rules still apply at fill time and can refuse it with merchant_blocked, or with destination_unauthorized on a site that is not an authorized destination.
  • A login appears only for its own site. A login on a sibling host of the same site (us.example.com while you are on www.example.com) is listed in relatedLogins, hosts only. It will not fill here: navigate to that website to sign in. An empty logins beside an empty relatedLogins means no login for this site is visible to this key. A key limited to Only selected logins does not see logins outside its selection, so ask the account holder to check Logins, and widen the key’s access if one exists, before calling create_login.
Errors: invalid_api_key, rate_limited (wait for Retry-After).
Example arguments

get_checkout_recipes

Look up the open checkout recipe registry for a page before improvising a checkout. Each registry call here and in submit_checkout_recipe and get_recipe_submission gets 20 seconds, then fails with “The recipe registry did not answer in time.”
string
required
The absolute product or checkout URL, such as https://shop.example.com/product/thing. The query, fragment and any hosted-checkout token are stripped before the lookup. A string that does not parse as a URL is sent as is, so never pass one that contains a session token.
Returns four slots, each null when nobody has recorded coverage for that host yet:
object | null
The recipe for this host: selector overrides, gotchas, preconditions, and a deadEnd classification when unattended checkout is impossible there. A deadEnd means do not attempt the checkout, and says why.
object | null
The platform recipe the merchant names: detection, cardSurface, step-by-step fill instructions and, where recorded, frame selectors under selectors.
object | null
How this host’s signup form behaves.
object | null
The identity provider the signup recipe names (for example Auth0, Okta, Cognito or Shopify customer accounts).
Read preconditions first. Each is a step to take, not a reason to stop: A signup recipe’s ownerSteps lists what the account owner does after submit. For each email_code, sms_code or captcha, call request_challenge_handoff. For an email_link, stop and tell the owner which site is waiting. A repeated step is the site asking again, not a rejected code.
A signup recipe describes a form. It is not permission to open an account, which always goes through create_login and the owner’s approval, given per request or in advance on their Pre-approved signups list.
Example arguments

submit_checkout_recipe

Contribute what you learned about a site to the registry, after solving a new site or hitting a dead end the registry does not know about.
object
required
The recipe body. A merchant recipe carries id (the primary host), kind: "merchant", hosts, platform and status, plus optional cardSurface, overrides, gotchas, notes, deadEnd, exampleProductUrl and preconditions. Signup and signup platform recipes are accepted too. See Recipe format.
string
Free text for the maintainer, such as how you tested it. Never personal data. With verificationRequested, an approved note is published inside the recipe as the evidence, word for word.
boolean
true when a real purchase completed (or, for a signup recipe, an account was actually created) and note holds the sanitized evidence.
Returns the submission id, its status and its verdict. The call waits up to about 45 seconds for screening. If screening has not finished by then, status is still pending and verdict is empty: poll the same id with get_recipe_submission. Do not resubmit. Rules the registry enforces:
  • Never claim status: "verified". That status is maintainer gated. Use partial, unverified or dead-end, and request verification instead.
  • preconditions is a closed vocabulary. A merchant recipe accepts exactly delivery_location, region_select and account_required, with no repeats. A signup recipe accepts exactly region_select, consent_gate and mode_toggle. Omit the field rather than sending an empty array.
  • A submission for the same store under another host is combined with the existing recipe.
Errors: invalid_recipe (fix it, do not resend unchanged), rate_limited (the budget of 20 in any 24 hours: stop submitting until the oldest submission in the last 24 hours turns 24 hours old).
Example arguments

get_recipe_submission

Read the verdict of a submission that was still pending. Poll this rather than resubmitting.
string
required
The submission id submit_checkout_recipe returned.
Returns the submission id, its status and its verdict. status is one of:
  • pending: screening is still running, even after submit_checkout_recipe gave up its 45 second wait. verdict is empty. Poll the same id again shortly, and do not resubmit.
  • approved: the recipe is live.
  • merged: it was combined with an existing recipe for the same store.
  • rejected: verdict names the rule it broke.
  • needs_human: a maintainer will decide. No action needed.
Errors: not_found (the id will not start existing).
Example arguments

Open an account

create_login, set_login_username and rotate_login_password write to the credential store, so they need a key the owner has allowed to create logins. That permission is off by default per key; without it those tools refuse with signup_not_enabled. get_signup_request and wait_for_signup_approval only read, and answer only the key that filed the request. None needs an open page.

create_login

Open an account on a site. Returns a request handle, not a credential. Usually the request is pending until the account owner approves it. If the owner has put this site and this exact email on their pre-approved signup list, the login is created at once and the handle comes back already approved.
string
required
The site’s registrable domain, for example doordash.com. Not the signup URL. The credential is locked to this domain and its subdomains, so a sign-in that later moves to identity.doordash.com still works. A bare suffix such as com or co.uk is refused.
string
required
A mailbox the agent controls. Never invent one. It is stored readable, and the owner sees it before deciding.
string
The site’s separate handle. Pass it only when the site has already accepted that exact handle. Approval stores whatever you pass, and only the first handle sticks. Otherwise omit it and record one later with set_login_username.
integer
The site’s stated minimum password length, when longer than the default of 24. At most 64, the longest password the vault generates. A higher value is refused with invalid_policy: on such a site, ask the account holder to add the login themselves.
integer
The site’s stated maximum password length. Below 12 is refused with password_too_short.
boolean
true only when the site rejects punctuation. It weakens the password, so do not set it speculatively.
string
The exact punctuation the site accepts. Applied as a filter over the vault’s own safe set, !@#$*+-_=.?: characters outside that set are ignored. If none of them is in the set, the call is refused with invalid_policy. Pass noSymbols instead when the site takes no punctuation at all.
boolean
true when the site rejects three identical characters in a row.
Pass only rules the form actually states. Each rule narrows the password. The generated password is 12 to 64 characters long, 24 by default. It always holds at least one lowercase letter, one uppercase letter and one digit, plus one symbol unless noSymbols is set. A minLength greater than maxLength is refused with invalid_policy. Returns requestId, status, expiresAt and a reminder note. Nothing in it is safe to type into a form. status is one of: Agent Vault still tries to notify the owner of every account opened from the pre-approved list (best effort). Pre-approval completes at once for at most 2 signups per listed site and, by default, 60 per account in a 24-hour window. Past either ceiling, or when another approval check fails, the request falls back to pending, so handle both statuses. See Limits. The 500-login ceiling is the exception: an account that already holds 500 logins gets login_limit straight away, pre-approved site or not, and no request is created.
A pending result stops create_login, not the signup. Wait with wait_for_signup_approval or poll get_signup_request on the same requestId. Calling create_login again opens a second, unrelated request and spends another of the account’s five outstanding-request slots.
Errors: signup_not_enabled, too_many_pending, login_limit (the account already holds 500 logins, counting ones the account holder added; no request is created), invalid_website, invalid_email, invalid_username, invalid_policy, password_too_short, rate_limited (over 20 credential writes this minute for this key; wait, then call again), write_outcome_unknown (never retry blind).
Example arguments

get_signup_request

Check a signup request once, without blocking.
string
required
The requestId create_login returned.
Returns the request’s status: On approved the reply is { status, login, passwordPolicy, passwordOwnerManaged }:
object
The new login’s catalog entry, exactly as get_token_catalog will list it. Its fields:
object
What the vault generated (length, character classes, symbol alphabet), so you can answer a “must contain” validator without knowing the value.
boolean
true when the owner has since replaced the password from their dashboard. passwordPolicy then describes a password that no longer exists, and rotate_login_password is permanently refused for this login. The token still fills the owner’s password. If the site rejects it, tell the owner.
Errors: request_not_found (re-read the id; do not guess), scope_denied (the owner has since removed the approved login from this key’s scope).
Example arguments

wait_for_signup_approval

Block until the owner decides, instead of polling yourself.
string
required
The requestId create_login returned.
number
default:"45000"
How long to wait. Must be greater than 0: 0 or a negative value is refused before the wait starts, with a tool error beginning MCP error -32602. Anything over 45000 is clamped to 45000, because most MCP hosts abandon a tool call at about 60 seconds. Call again to keep waiting.
Returns a status and a note for every decision, and for a wait that ran out: An owner usually takes longer than one wait, so expect to call this several times. A wait that ends leaves the request untouched. On no result should you call create_login again for the same site. Errors: a refusal no further poll can change ends the wait at once as a tool error, not a status:
  • request_not_found: the id is unknown, malformed or belongs to another key, or (rarely) the approved login was since deleted. Re-read the id; do not guess.
  • scope_denied: the request was approved, but the owner has since removed that login from this key’s or connection’s access. Ask the owner.
  • A key refusal such as invalid_api_key or key_revoked. See Error codes.
A dropped connection, a 429 or a 5xx on one poll does not end the wait. The next poll asks again, and if none answers before the wait runs out the status is unknown.
Example arguments

set_login_username

Record the handle a site has already accepted on a login opened with only an email.
string
required
The login’s label: login.label from the approved request, or the label in the token catalog.
string
required
The handle the site accepted.
Returns an ordinary success.
Call this last: type the handle into the site’s form, submit, and confirm the site accepted it. This sets a username once and cannot be undone by the agent. Re-sending the same handle is idempotent and is the safe recovery when an answer went missing. A different handle is refused with username_already_set; tell the owner which handle the site accepted.
Only the API key that created the login can set its username, and only within seven days of the login being created. Seven days is the default; a deployment can set a different window. Any other key, or a later call, gets not_found, the same answer as an unknown label. Re-sending the handle already stored still succeeds after the seven days. Errors: username_already_set, not_found (no such login, or not one this key may change), invalid_username, signup_not_enabled, daily_limit (the account’s 25 changes in a 24-hour window are spent; it resets 24 hours after the first counted change), rate_limited (over 20 credential writes this minute; wait, then call again), write_outcome_unknown (re-send the same handle).
Example arguments

rotate_login_password

Mint a replacement password for a login an agent created, when the site rejected the generated one at signup, or on a change form that asks only for a new password.
string
required
The login’s label.
integer
The site’s stated minimum length. At most 64: a higher value is refused with invalid_policy, and the account holder has to set the password themselves.
integer
The site’s stated maximum length. Below 12 is refused with password_too_short.
boolean
true only when the site rejects punctuation.
string
The punctuation the site accepts, filtered to the vault’s safe set !@#$*+-_=.?. None of them in that set is refused with invalid_policy.
boolean
true when the site rejects three identical characters in a row.
The new password follows the same length and character rules as for create_login: 12 to 64 characters, 24 by default. Returns success. The mock tokens do not change: re-fill the same password token and the new password goes in. The password is generated on the server, stored encrypted and never returned.
Do not use this on a form that asks for the current password. There is one password token per login, so after rotating it fills the new value into both fields, the site refuses the change, and the vault holds a password the site does not know. A forced change on a live account is the owner’s to handle.
Only the API key that created the login can rotate its password, and only within seven days of the login being created. Seven days is the default; a deployment can set a different window. Errors: not_rotatable (a login the owner saved, one whose password they replaced, one another key created, or one created more than seven days ago; this tells you nothing about whether such a login exists), not_found, invalid_policy, password_too_short, signup_not_enabled, daily_limit, rate_limited (over 20 credential writes this minute; wait, then call again), write_outcome_unknown (stop and hand it to the owner).
Example arguments

Drive the page

open_page

Launch the proxied browser on the first call, then navigate. Later calls reuse the same page, so you can walk a multi-step checkout.
string
required
Absolute URL, for example https://shop.example.com/checkout.
Returns Opened <url>. If the proxy connection had dropped, open_page launches a replacement session; the run report then covers only what happened after the restart (see get_run_report). On the hosted server, an open_page without session starts a new browser every time. To go to another page, pass the session you already have. When the connection already has other browsers open, the answer names them, so you can reuse or close them rather than load the site in several at once. In a residential egress session, which includes every session on the hosted server where the deployment has one, an http:// address is opened as https:// on the same host, and so is any http:// page a link, form or popup leads to. Errors:
  • insecure_site. The site would not load over HTTPS, or it redirected to an http:// address. The message begins “This site doesn’t support HTTPS” or “This site redirected to a plain http:// address”, and ends “Nothing was sent over plain http.” The page did not change: the tab is still where it was, and what you filled there is still there. Do not call open_page on the same address again, since it fails the same way. Choose another site, or tell the user this one cannot be used. When a click or press leads to such a page, it returns the browser’s own navigation error instead, such as net::ERR_SSL_PROTOCOL_ERROR, and the tab stays where it was.
  • egress_unavailable. The first open_page could not start a session, because residential egress is required (SELF_AGENT_PAY_RESIDENTIAL_EGRESS=1) and the deployment has none, or its provider is not answering for the account’s country. Nothing was opened. The message says how many seconds to wait before trying again.
  • egress_lost. The session’s residential egress stopped working. Every later call that reaches the browser is refused with it, including get_session_outcome. get_run_report and close_session still work. Read the report, call close_session, then open_page again.
See Error codes.
Example arguments

click

Click an element, for example the place-order button.
string
required
The element to click.
Returns Clicked <selector>. When the click opened a new tab, the reply says so: call wait_for_popup before anything else, because every tool still targets the page you were on. A reply without that notice does not prove no tab opened. The proxy reports only a tab that arrives within a short window after the click. A tab the site opens later, for example a 3-D Secure or wallet window after a server round trip, is still queued for wait_for_popup with no notice. When the checkout plainly moved to another tab, call wait_for_popup anyway. When a click times out because something covers the target (an expanded payment iframe, a cookie wall, a modal scrim), the error says so and names the fix. Usually that is press with Enter, or Space for a checkbox or radio. Where no keypress activates the target, the error asks you to clear the overlay.
<tag> intercepts pointer events in the call log is not that signal on its own. When performing click action or click action done follows it, the click landed and the timeout was the wait after it. The order may already be placed: read the page instead of clicking again.
Example arguments

press

Send a keypress to an element instead of clicking it. Use it when an overlay swallows pointer events, or when a form only submits on Enter.
string
required
The element to press the key on.
string
required
Playwright key syntax: Enter, Tab, Escape, ArrowDown, or a character.
string
The iframe the element is inside, for a hosted checkout that submits on Enter.
Returns Pressed <key> on <selector>, and a new-tab notice like click. Errors: clipboard_blocked for copy, cut and paste chords (Control+c, Control+v, Control+x and their Meta spellings) once any token has been filled. Click, or press a plain key, instead.
Example arguments

wait_for_selector

Wait for an element to reach a state.
string
required
The element to wait for.
string
One of attached, detached, visible, hidden. Defaults to the page’s default (appear).
number
default:"30000"
How long to wait, in milliseconds.
string
Wait inside a hosted iframe, for example to check a card field is there before filling it.
Returns Found <selector>.
A wait that returns counts as an action between a card number and its security code. Do your waiting before the number goes in. See Card order.
Example arguments

count

How many elements match a selector right now, and how many of those are visible. Never waits, so probing several candidate selectors costs no timeout.
string
required
The selector to count.
string
Count inside a hosted iframe.
Returns { count, visible }. A nonzero visible is not a promise the fill will work: a visible input can still be disabled or read-only. Set fill_checkout’s timeout regardless. count is the one probe that is safe between a card number and its security code. After any token fill, a selector that tests a field’s value, or puts a digit in a text match, is refused with sensitive_data_blocked. Point with an id, class, [name=…], [type=…] or text without digits. Text without digits is allowed whatever value the page is showing, so the non-digit characters of any value a page echoes as text stay testable. See What the agent can see.
Example arguments

wait_for_popup

Wait for a popup or new tab this page opened (a 3-D Secure window, a wallet sign-in, a hosted checkout) and make it the current page.
number
default:"10000"
How long to wait. Anything over 60000 is clamped to 60000, because this wait holds the session. Call again to keep waiting.
Returns a confirmation. From here, fill_checkout, click, press, wait_for_selector and open_page all target the popup. If the popup closed itself and had opened a tab of its own, you land on that child tab and the reply says so. It is a different document, so read it before acting. Errors: fails if no popup opens within the timeout. A late popup is queued, so one retry is worthwhile.
Example arguments

close_popup

Close the popup and make the page that opened it current again. Safe even if the popup already closed itself. This tool takes no parameters. Returns where you landed: the opener; a sibling tab queued under it; a child tab the popup opened; or, if every page that opened it had closed, a blank page in the same browser with cookies intact (navigate back with open_page). Read the page before assuming your checkout is still on screen.

Fill

fill_checkout

Fill an ordered list of fields with mock tokens or real values.
array
required
The fields to fill, in order.
number
default:"30000"
How long each field gets to become fillable before the call fails with the reason. Must be 1 or more: 0 means no timeout, not an immediate failure. It does not count time spent waiting on the account owner (a purchase approval or a live CVV entry).
Returns Filled N field(s). When the account owner was pulled in to approve the purchase or type the security code, the reply says so. A plain reply means nobody was asked. When the key is verification-only (see API keys) and the batch filled a card field, the reply also carries a card_fields_verified note. The proxy typed a public test card, not the owner’s card. Nothing was reserved or charged, and the owner was not alerted for that card fill. The test card does not force a merchant to refuse the order, so submit only on a checkout meant for testing. Treat the fill as a probe of the form, not a purchase.
Set timeout whenever a miss is possible. With the 30 second default, a field that never becomes fillable times out at the same moment as most MCP clients, and the proxy’s explanation (for example locator resolved to hidden <input>) is lost. A few seconds gets the explanation back. The budget is per field, so keep fields multiplied by timeout under your client’s deadline.

Card order

Fill expiry and any billing field first, then the card number, then the security code immediately after it. Filling the number authorizes the security code that follows it exactly once. The next action spends that authorization: another field, a click, or a wait that returns. The account owner is then asked to approve the same purchase a second time. Reads do not spend it: observe_page, detect_challenge and count leave it in place, and a wait that timed out observed nothing. The tool enforces this:
  • A batch that puts any other field straight after the card number is refused before anything is typed. Reorder it. Only cardField fields move.
  • A vault value between the number and the code is refused, so fill billing first.
  • When a batch ends on the card number, the next fill_checkout is refused unless it starts with that card’s security code.
Filling the number is also where the order total is read and checked against the owner’s limits. Put country or postcode edits that recompute the total in an earlier call, so the page has settled. Errors: see Spending, limits and approval and Card and merchant refusals. cvv_entry_required means the security code was not supplied. If the owner did not enter it in time, or the prompt could not be started, report it and let the user retry. If the message says “Live CVV entry is not yet available”, every retry fails the same way: stop and tell the user that this card cannot be used on this deployment for a checkout that asks for the security code. See Live CVV.
Example arguments
Take the expiry from the same catalog card whose number and cvv tokens you fill: its expiryMonth and expiryYear, not fixed values like the ones above. Both are numbers, with a four-digit year. Match the format the control expects, for example 11 and 2029, or 1129 for a single MMYY field.

fill_login

Fill a sign-in or signup form’s identifier and password with their mock tokens. Each half is optional.
string
The identifier field. Pass with usernameToken.
string
Choose by the field’s visible label and type, not its name. For an email field, tokens.email. If the entry has no tokens.email, ask the account holder rather than filling tokens.username, which may be a handle. For a field that asks for the site’s own handle in so many words, tokens.username. See Pick the identifier token.
string
The password field. Pass with passwordToken.
string
The login’s password token.
string
A repeat-password field on a signup form, filled with the same password token. Needs the password pair in the same call.
Returns which halves were filled, for example Filled username and password.
  • Email-first sign-ins (Microsoft, Okta, Google) take two calls: the identifier pair, submit, wait for the password field, then the password pair. Passing both before the password field exists fills nothing and says so. Each field gets 3 seconds to appear and be visible before the call refuses with “Nothing was filled”.
  • Do not choose the token from the field’s name. A field named username (in its name, id, data-testid or autocomplete) is very often the email field. Read its type and visible label instead.
  • Authenticator codes. If this sign-in then asks for an authenticator-app code and the login has tokens.totp, fill that token with fill_checkout and submit promptly. Use it only for this login’s authenticator step.
Example arguments

declare_page_hint

Tell the vault where something is on the current page when it cannot find it by convention. The main use is the order total on a capped card.
string
required
The hint kind, for example order.total. See list_page_hint_kinds.
string
required
Plain CSS only. No text=, :has-text(), >>, XPath, _react= or :visible: a hint points at an element, it does not search for one. At most 512 characters. An order.total selector matching more than 8 elements, or an element over 4,000 characters, adds nothing and answers accepted: false.
string
The iframe the element lives in. The only way to reach a total inside an overlay checkout such as Paddle or Lemon Squeezy. Plain CSS, at most 512 characters. For order.total the vault also reads the frame’s whole text and every other frame on the page. Over 20,000 characters in the frame, over 16,384 counting the page’s own text, or more than 32 frames on the page, the hint is dropped and answers accepted: false even though the selector matched. Point at a total outside the frame if there is one. Otherwise a card with an approval threshold asks the account holder, and a card with spending limits is refused with checkout_total_unknown.
string
default:"text"
text or attribute. An attribute’s value must name a currency: a bare 49.99 is refused.
string
Which attribute to read when read is attribute, for example aria-label, content or data-total.
string
For login.totp_secret: the label of the stored login the secret belongs to.
Returns { kind, accepted, checked }, plus resolved, amount and currency when checked is true, or untrusted naming why a figure was refused. A login.totp_secret hint returns stored, digits and period. After any token fill the reply is { accepted: true, checked: false }: not a rejection, because the hint is kept and used at the card fill. That applies only to a selector and frame built from ids, classes, element names and presence-only attribute tests such as [data-total]. One that compares an attribute value, such as [data-testid="total"], is refused with invalid_hint after a fill, and one declared before the fill is skipped when the card fill reads the total. accepted: false means the hint was dropped, so declare it again. See What changes after the first fill. You supply a location, never a value. Declare order.total before filling the card, and again after a navigation. Errors: invalid_hint, hint_unresolved, hint_uncertain. A selector or frame over 512 characters is refused with invalid_hint before anything is recorded. A page holds at most 4 hints of each kind: a fifth distinct hint of the same kind on that page is refused with invalid_hint. Repeating a hint does not count, and any navigation of the page clears the count. A login.totp_secret hint can also fail its preconditions first: sensitive_data_blocked (this session has not signed in as that login) or insecure_page (the page is not on https://, and is not a loopback http:// page). Full guide: Page hints.
Example arguments

list_page_hint_kinds

List the hint kinds this deployment accepts, what each is for and which attributes it may read. Kinds are added server side, so ask rather than assume. This tool takes no parameters. It needs the session open_page starts, because the list comes from the vault.

Look at the page

None of these tools return a filled value, and none spends the card authorization.

observe_page

A fixed set of proxy-computed answers about the page. This is how you tell a page that rejected your input from one that moved on, including after a fill, when screenshots are blacked out. This tool takes no parameters. Returns:
boolean
A password field is on screen.
boolean
A field the page declares as its sign-in identifier.
boolean
An email or phone input. A shipping form has one too.
boolean
The page says a code was sent.
boolean
A validation or error message is showing.
boolean
The page is refusing further attempts.
string
Which signals matched. Value free.
string[]
Short strings saying why the page rejected you, in its own words, for example ["Enter a valid postcode"]. At most 5, each cut to 200 characters, drawn only from elements the page marks as validation output and scrubbed of filled values and anything card, email, phone or token shaped.
true
Present only when the scan did not finish.
  • Read errorText whenever it is non-empty, not only when error is true, then fix your input and fill again. Do not branch on the wording or paste it to the user.
  • An empty errorText does not mean nothing was rejected. Some merchants style errors without marking them up. It is also withheld for the rest of a session in which the owner drove the page during a handoff.
  • A live region also announces success. Read what it says.
With inconclusive: true, every false means unknown, not absent, and a true may be an off-screen panel. Look again before acting. A finished scan omits the flag, so test for its absence, not for false.

snapshot_page

The accessibility tree as YAML: every field, its accessible name and a [ref=eN] handle, reaching into iframes including cross-origin ones. Call it first on a page you have not seen.
number
default:"12"
How deep to walk. The proxy clamps it to 30.
number
Line offset for the next page of a truncated result. Pass it with the depth the truncation note names.
Returns YAML:
  • Pre-fill only. Once any token is filled it is refused with sensitive_data_blocked for the rest of the session, because the page chooses accessible names. Snapshot while orienting, then use observe_page and count.
  • A ref is a selector. Pass aria-ref=e7 to count, wait_for_selector, click or press. Refs survive DOM changes but not a navigation. A fill refuses a ref: fill by CSS selector.
  • Field selectors. The snapshot ends with a # Field selectors comment that names, for each field ref, the CSS selector to pass to fill_checkout, and the iframe’s frame selector for a field inside one. Use those rather than guessing.
  • Values are never in it. A blank-looking field says nothing about whether a fill landed.
  • Paging. A result holds at most 24,000 characters. If it was cut, its last line names a cursor and depth. Pass both: a cursor is an offset into a capture at that depth. Lower the depth instead (and drop the cursor) only when what overflowed is nesting, not width.
Example arguments

list_frames

This page’s frames as { name, url }, to find which iframe holds the payment form. Never frame content. This tool takes no parameters. Returns { frames: [{ name, url }] }. A cross-origin frame may report an empty name or URL. Errors: sensitive_data_blocked once any token has been filled, for the rest of the session. Use it when a page already shows its payment surface and you have filled nothing. On a multi-page checkout the card frame usually appears only after sign-in, so plan on targeting it by CSS instead: get the selector from get_checkout_recipes, or use the known Stripe titles: Settle every card frame before the number goes in.

detect_challenge

Whether the page is showing a 3-D Secure prompt, a one-time-code step, a CAPTCHA or bot wall, or a silent block. Call it first whenever a click or fill seems to do nothing: an invisible bot wall looks exactly like a stuck overlay. This tool takes no parameters. Returns { detected, type, types, reason, signature?, deliveredCode?, ambiguousOnly?, inconclusive? }. type is three_ds, otp, captcha or blocked. Never page text. blocked means the site stopped answering without saying why. signature names the shape: stalled_search (a search that never loads), search_bounce (a click bounced back to the previous page with an error) or error_reference (an error dialog with a reference code). deliveredCode: true on an otp means the page says the code was sent by SMS or email.
ambiguousOnly means the page has a field that might be a code input, flagged only because this sign-in’s windows (this page, its opener, or a popup it opened) recently took a login password and no checkout token since. request_challenge_handoff refuses it with handoff_not_applicable.

get_network_log

A diagnostic that is off unless the deployment enables it, typically on staging. Lists the current page’s last 25 document, xhr and fetch requests: method, host, path (cut to 200 characters), status, and how long ago. This tool takes no parameters. Returns one line per request, oldest first, or a message saying it is not available on this deployment. No bodies, headers or query strings are ever returned. After a vault value has been filled, the host is only this site or another site and no path is shown. Use it when a submit appears to do nothing: a silent server refusal shows as a 4xx on the form’s request.

Hand off a challenge

request_challenge_handoff

Hand the page to the account owner so they complete a challenge you must not, then resume. This tool takes no parameters. The proxy re-detects before summoning anyone, then asks the owner with a dashboard banner, plus an email, desktop or background alert on each channel they turned on while their A checkout needs me to clear a bank or verification step switch is on, with a link that streams the live page. The call blocks until they finish, the wait times out (about 10 minutes), or the session ends. Returns { outcome, challengeType, durationMs }, never what the owner typed. Errors: handoff_not_applicable (an ambiguousOnly detection; the page is still yours), challenge_scan_incomplete (nobody was notified; detect again), challenge_required for other page tools while the handoff is parked, and unknown_target in two variants. Unknown pageId means the page was gone before the handoff started: nobody was notified, so call close_popup or move to another live page and detect again. No challenge is present on this page means the proxy re-detected on a live page and found nothing: carry on.
Most MCP hosts cancel a tool call at 60 seconds. That does not cancel the handoff: the owner still has the page. Re-orient with wait_for_selector or get_session_outcome once they are done, rather than calling this again.

Confirm and finish

get_session_outcome

After submitting, race a success selector against an optional failure selector. Use this instead of scraping a confirmation page, which may echo card digits.
string
required
Appears on success, for example #order-success.
string
Appears on failure, for example .checkout-error.
number
default:"15000"
Maximum 30000; larger values are clamped. 0 or a negative value means the default, not “no timeout”. A handoff waits for this check, so it is also the longest the owner can be kept from the page. To watch longer, call again.
boolean
true only when the success selector confirms a completed checkout, so the session is recorded as confirmed. Leave it off for logins.
string
Required with confirmsCheckout. The control you clicked or pressed (with Enter) to place the order, exactly as you passed it, and the latest click or press on the page. Call this right after the submit.
Returns outcome (success, failure, timeout or cancelled) and matched, the selector that decided it. timeout means neither selector appeared yet, not that the checkout failed. cancelled means a handoff took the page over mid-check: ask again once it returns. With confirmsCheckout: true, the confirmation is recorded only when it is corroborated:
  • the submit control has left the screen,
  • no tab this page opened is queued or waiting for wait_for_popup,
  • the page shows no error, rate limit, sign-in step, sent-code notice or challenge step,
  • the success selector is still there and the failure selector is not.
The reply then says corroborated: true and the recording is started (best effort; get_run_report is the record). Otherwise it says corroborated: false with notCorroboratedBecause, and nothing is recorded. Do not report an order on it.
A redacted receipt image is captured only for a session that filled no mock tokens, such as a saved-payment checkout. A normal token-filled checkout is marked confirmed with no receipt image.
Example arguments

get_run_report

What the session actually did, from the proxy’s own record. Read this before telling anyone whether a card was charged. This tool takes no parameters. It needs an open session, and is safe to call while a handoff is parked. Returns:
string[]
The distinct token kinds filled, for example ["identity", "address", "card", "cvv"].
number
How many fills the proxy refused (paused card, merchant block, scope, URL lock). A burst of rate_limited refusals counts once a minute, not once each.
boolean
Whether a checkout confirmation was recorded.
string
The total on that confirmation page, when there was one.
string[]
What the session changed in the vault, as label:change, for example ["doordash:signup_requested", "doordash:created", "doordash:totp_enrolled"]. These outlive the session. Tell the owner about them even when the checkout failed.
boolean
false when open_page had to start a new proxy session partway through the run, for example after the connection dropped. The numbers then cover only what happened after the restart, and a note says so. A short record is not evidence of a quiet run.
object[]
Only when open_page replaced this run’s proxy session partway through (a lost tab, say) and the old session’s record could still be read. One object per earlier proxy session, oldest first, each with the same filledFieldKinds, deniedCount, confirmationDetected, orderTotal and credentialWrites fields as the report. An earlierReportsNote comes with it.
timeout from get_session_outcome plus card in filledFieldKinds means the card was entered and you did not see the confirmation. Say that.

After a restart

With earlierReports, the run is split into segments: each earlier proxy session, then the current one. The top-level fields describe only the current one. Check every segment before you tell anyone nothing was charged. card in any segment’s filledFieldKinds, or confirmationDetected: true in any segment, holds for the whole run. When the current proxy session cannot be read but earlier segments were kept, the reply has no top-level report fields. It carries earlierReports, currentReport: null, coversWholeRun: false, a currentReportNote, and credentialWrites when the run changed the vault:
currentReport: null does not mean nothing was filled since the restart. It means Agent Vault could not ask. The earlier segments still count. Kinds and counts only, never a filled value. A read it cannot complete, with no earlier segments to report, refuses and says so rather than answering with zeros. The proxy gets 5 seconds to answer this read, here and in close_session.

close_session

Close the browser and end the proxy session. Call it when the task is done. This tool takes no parameters. Returns status and report, the same object get_run_report returns, so your last act is reading what the run did. If the browser did not close cleanly, status says so and teardownError is set. When open_page replaced a proxy session partway through, the reply also carries earlierReports and earlierReportsNote, as in get_run_report. Count every segment with report. If no session was ever opened, or its connection had already died, there is no report and the reply says so. Do not read that as nothing having been filled. Any credentialWrites made through the server are still listed, and so are any earlierReports, with coversWholeRun: false.

Rules of engagement

The rules your agent follows when it uses these tools.

Error codes

Every refusal and whether retrying is safe.

Handling challenges

3-D Secure, one-time codes and CAPTCHAs.

Confirming outcomes

What counts as proof an order went through.