Tools
Reference for every webagent MCP tool, its arguments, what it returns and when it reports an error.
The MCP server offers 15 fixed tools and one skill_<name> tool per skill. This page lists each one. Your client also shows the exact JSON Schema of every tool in tools/list, which is the final word on argument names.
Every tool call runs with your API key, so the same limits, plans and quotas apply as on the REST API. The tools call the same services as the REST routes.
| Tool | What it does | Waits? |
|---|---|---|
create_session | Open a cloud browser session | No |
run_task | Give the browser agent a goal and wait for the answer | Up to 90 s |
get_task | Read a task, or wait for a running one | Up to 90 s |
smoke_test | Test a page or flow and return a pass/fail report | Up to 90 s (60 s by default) |
get_report | Read the test report of a run or task, or wait for it | Up to 90 s (30 s by default) |
list_sessions | List the project's sessions | No |
get_session | Read one session | No |
stop_session | End a session | No |
scrape | Load a URL and return its content | Until done |
screenshot | Load a URL and return an image | Until done |
pdf | Load a URL and return a PDF | Until done |
crawl | Crawl from a URL and return the pages | Until done |
list_skills | List the skills you can run | No |
run_skill | Start a skill run, without waiting | No |
get_skill_run | Read a skill run, or wait for its verdict | Up to 120 s |
skill_<name> | Run one skill and wait for its verdict | 30 s by default, up to 120 s |
Ids look like sess_us_… for sessions, tsk_… for tasks and run_… for skill runs.
Results and errors
Most tools return two things: content (text for the model) and structuredContent (the same data as JSON). A few return an image or a PDF instead.
A call that fails returns normally with isError: true. The text is [code] message. structuredContent.error holds { code, message, details? }. The codes are the API error codes. Argument problems come back as invalid_input, with the validation issues in details.issues. An unknown tool name is not_found.
A failed test is a result, not an error
isError means "the test could not run", not "the app has a problem". A test that ran and found a problem is a normal result. Read the verdict, not isError. See Reading results.
How each tool sets isError:
| Tool | isError: true when |
|---|---|
smoke_test, get_report | The report verdict is error: the test could not run to a result. Verdict fail is a normal result |
run_task, get_task | The call failed, or the task ended in a way that means the agent could not test: stoppedBy is captcha, blocked, error, aborted, paused, navigation_blocked, capacity_exceeded, capacity_timeout or timeout. A task that ended stuck, budget or unanswered, or finished with ok: false, is a normal result |
skill_<name>, get_skill_run | The call failed, or the run status is error, denied or cancelled. A run that is failed is a normal result. A run still running is not an error |
| every other tool | The call failed |
create_session
Opens a cloud browser session, like POST /v1/sessions. Use it when several tasks must share one browser, or when you want to keep the session open and watch it. Then pass the returned id as sessionId to run_task. For a single task, run_task takes the common options itself (recording, headers, proxy, vault login).
| Argument | Type | Required | Notes |
|---|---|---|---|
startUrl | string | no | http or https, at most 2048 characters. The browser opens it before the session is live. A blocked address fails with navigation_blocked |
device | string | no | desktop, mobile or a device id from the device list |
viewport | object | no | width and height in pixels (200 to 7680 wide, 200 to 4320 high), optional deviceScaleFactor and mobile |
record | boolean | no | Record the session as video. Kept after the session closes. See Evidence |
headers | array | no | Extra request headers sent only to the hosts you name. Up to 20 entries |
credentials | boolean or object | no | Fill login forms from the credential vault |
proxyUrl, allowHosts | string, string array | no | Reach a private host through your own proxy. allowHosts needs proxyUrl |
idleTimeoutMs | integer | no | Close after this long with no actions. 10 000 ms to 24 h. Default 5 minutes |
maxDurationMs | integer | no | Hard cap on the session's lifetime. 10 000 ms to 24 h. Default 1 hour |
engineId, engineVersion | string, integer | no | Run on a saved engine. engineVersion needs engineId |
waitForCapacityMs | integer | no | When every browser is busy, wait up to this long (0 to 30 000 ms) for a free one before failing with capacity_exceeded. Default 0 |
queueForCapacity | object | no | { "maxWaitSeconds": 1 to 900 }. When every browser is busy, keep the session queued instead of failing. See Sessions. Not together with waitForCapacityMs above 0 |
The tool takes every field of the POST /v1/sessions body. The table lists the common ones. Do not send url: it is refused with a hint to use startUrl.
Returns: the session record as JSON, with its id, status, endpoints and a short-lived session token.
{ "startUrl": "https://staging.example.com", "record": true }{
"id": "sess_us_xxxxxxxxxxxxxxxxxxxxxxxx",
"status": "live",
"engine": "chromium",
"createdAt": "2026-01-01T10:00:00.000Z",
"token": "...",
"tokenExpiresAt": "2026-01-01T10:15:00.000Z",
"endpoints": { "http": "https://...", "cdp": "wss://...", "live": "wss://..." }
}run_task
Gives the browser agent a goal in plain language. The agent looks at the page, decides, acts, and repeats until it is done or stops. The tool returns the answer, why it stopped, the steps and the cost.
Without sessionId, the tool opens a session itself and closes it when the task ends, unless keepOpen is true. With sessionId, the task runs on that open session, and the session stays open.
| Argument | Type | Required | Notes |
|---|---|---|---|
goal | string | yes | What to do, 1 to 2000 characters |
maxSteps | integer | no | 1 to 50. The agent's default is 25 |
sessionId | string | no | An open session of your project. Leave it out to let the tool open one |
startUrl | string | no | Page to open first. Only when the tool opens the session |
engineId, engineVersion | string, integer | no | Engine for the new session. Only when the tool opens the session |
record | boolean | no | Record the session as video. Only when the tool opens the session. The report then links the recording |
headers | array | no | Extra request headers for the hosts you name, for a staging site behind a gate. Same rules as in create_session. Only when the tool opens the session. See Protected sites |
proxyUrl, allowHosts | string, string array | no | Reach a private host through your own proxy. allowHosts needs proxyUrl. Only when the tool opens the session. See Test a local app |
credentials | boolean or object | no | Fill login forms from the credential vault: true, or { namespace, autoSubmit }. Only when the tool opens the session. There is no field for a plain password. See Signed-in sites |
mode | auto or full_auto | no | Default auto. See Tasks |
keepOpen | boolean | no | Keep the session open after the task, when the tool opened it |
waitSeconds | integer | no | 1 to 90. How long to wait. Default 90. A value above 90 is refused with invalid_input |
Giving startUrl, engineId, engineVersion, record, headers, proxyUrl, allowHosts or credentials together with sessionId is an invalid_input error. These options only apply when run_task opens the session.
Returns, when the task ends within the wait:
- Text:
Task finished|stopped (<stoppedBy>) in N steps, $x, S s.thenAnswer:,Reason:, a line that says whether the session was closed, and a line that namesget_reportwith the task id. structuredContent:{ sessionId, sessionClosed, task }.taskhastaskId,ok,answer,stoppedBy,reason,limit,steps,costMicros,msandtext. Each step hasop,ref,provider,ok,ms,costMicrosanderror. Costs are in millionths of a US dollar. Long strings are cut at 2000 characters.
Returns, when the wait ends first: the task keeps running and its session stays open. The result is { sessionId, taskId, status: "running", sessionClosed: false, note }. Call get_report with the taskId (or get_task), then stop_session when you are done. A task that runs longer than the wait is normal.
If the call fails before a result exists (for example session_paused), a session the tool opened is closed and the call returns isError. A task that ended because the agent could not test (a CAPTCHA, a blocked address and so on) also returns isError. See Results and errors.
run_task is for an open-ended goal. It checks no assertions: ok: true means the agent says it is done. For a repeatable pass/fail check of a page or flow, use smoke_test.
{
"goal": "Open the first product, add it to the cart and report the cart total",
"startUrl": "https://staging.example.com",
"maxSteps": 20,
"keepOpen": true
}{
"sessionId": "sess_us_xxxxxxxxxxxxxxxxxxxxxxxx",
"sessionClosed": false,
"task": {
"taskId": "tsk_xxxxxxxxxxxxxxxxxxxxxxxx",
"ok": true,
"answer": "The cart total is 24.00.",
"stoppedBy": "done",
"steps": [{ "op": "click", "ok": true, "ms": 410 }],
"costMicros": 8200,
"ms": 24000
}
}The text for the same call reads Task finished (done) in 2 steps, $0.0006, 5 s., then Answer: ..., then Session sess_us_... is still open: run another task with sessionId, or stop_session when done.
Do not put real passwords in a goal
The goal text goes to the model and into the task record. Use the credential vault for logins. See Signed-in sites.
get_task
Reads a task started by run_task. It waits for a running task, then returns the result. It never closes the session.
| Argument | Type | Required | Notes |
|---|---|---|---|
sessionId | string | yes | The session the task ran on |
taskId | string | no | Default: the session's latest task |
waitSeconds | integer | no | At least 0. Default 30, maximum 90. A larger value is lowered to 90 |
Returns, when the task is finished: text with the same summary as run_task, and structuredContent { sessionId, taskId, status: "finished", task }. Here task is the stored task record: id, goal, status, ok, stoppedBy, limit, reason, answer, steps, costMicros, ms, startedAt, finishedAt.
Returns, when it still runs: { sessionId, taskId, status: "running", note }. Call again, or use get_report with the taskId.
get_task returns the raw task record. For a verdict with console errors and failed network calls, use get_report.
A session that has no task yet, or a taskId that is not on the session, is not_found.
{ "sessionId": "sess_us_xxxxxxxxxxxxxxxxxxxxxxxx", "waitSeconds": 60 }{
"sessionId": "sess_us_xxxxxxxxxxxxxxxxxxxxxxxx",
"taskId": "tsk_us_xxxxxxxxxxxxxxxxxxxxxxxx",
"status": "finished",
"task": {
"id": "tsk_us_xxxxxxxxxxxxxxxxxxxxxxxx",
"goal": "Open the Pricing page and tell me the page heading",
"status": "finished",
"ok": true,
"stoppedBy": "done",
"answer": "The Pricing page is open. Its page heading is \"Pricing\".",
"steps": [{ "op": "click", "ref": "e2", "ok": true, "ms": 3458, "costMicros": 472 }],
"costMicros": 617,
"ms": 4753,
"startedAt": "2026-01-01T10:00:06.000Z",
"finishedAt": "2026-01-01T10:00:11.000Z"
}
}smoke_test
Tests a page or a short user flow and returns a report. This is the tool for "smoke test my app". One call opens a recorded cloud browser, loads the page, runs your plain-language steps, checks what you expect, and returns a verdict. It runs the smoke-uat skill and always records the session.
| Argument | Type | Required | Notes |
|---|---|---|---|
url | string | yes | The page to test. http or https, at most 2048 characters |
steps | string array | no | Plain-language steps run in order after the page loads, for example ["click Sign in", "type the username demo into the email field"]. Up to 30 steps of up to 500 characters. The first step that fails stops the flow. Plain steps need the browser agent to read the page. The step text is stored with the run: use a throwaway test account |
expectText | string | no | Text that must be on the page when the steps are done. Up to 500 characters. Case-sensitive |
expectUrl | string | no | Text the page address must contain when the steps are done, for example /dashboard. Up to 500 characters |
expectNoText | string | no | Text that must not be on the page when the steps are done, for example Invalid password. Up to 500 characters |
maxLinks | integer | no | How many same-origin links of the start page to check. 0 to 200. Default 50 |
strictLinks | boolean | no | A broken link fails the test. Default true. With false it is a finding only |
waitSeconds | integer | no | How long to wait for the result. 1 to 90. Default 60 |
session | object | no | Session options for this test, for example proxyUrl and allowHosts to reach a private staging site, or headers for a gate. Recording is always on. Sign-in options (credentials, profileId, persistProfile, profileName, extensionIds) are refused with invalid_input: skill runs cannot sign in yet |
The test checks, in this order: the start page loads, basic accessibility, the links of the start page, your steps, the console, then expectText, expectUrl and expectNoText, and last whether every link worked (strictLinks). Links and accessibility are checked on the start page only, not on pages your steps reach. The console check covers the whole run. A failed step does not skip the later checks, so you see a broken link and a failed flow in one report. See Skills.
Returns: the report of the run. The text has the summary, up to five failed steps (with a screenshot link when there is one) and a short-lived link to the full HTML report. structuredContent is the whole report. It has a verdict: pass, fail, error or running.
isErroristrueonly for verdicterror: the test could not run to a result, for example the run was cancelled, was denied a permission, or the browser failed. A test that ran and failed is a normal result with verdictfail.- If the test is still going when the wait ends, the verdict is
runningand the summary says to callget_reportwith theid.
{
"url": "https://staging.example.com/login",
"steps": ["type the username qa@example.com into the email field", "type the test password into the password field", "click Sign in"],
"expectText": "Welcome",
"expectNoText": "Invalid password",
"expectUrl": "/dashboard"
}The text for a failed test reads like this. It is from a run on the Test Arena sign-in page where expectText named a text that is not there (links shortened):
Smoke test of https://webagent-test-web.pages.dev/app/login FAILED in 7 s: 15 of 16 steps passed. Step 13 (assert 1 condition) failed: Expected text "Dashboard overview" is not on the page. No console errors or failed network calls.
Failed steps:
- Step 13 (assert 1 condition): Expected text "Dashboard overview" is not on the page (screenshot: https://...)
Full HTML report (short-lived link): https://...
The whole report is in structuredContent.A test that passes has a shorter text: Smoke test of https://webagent-test-web.pages.dev/app/login PASSED in 5 s: 17 of 19 steps passed. No console errors or failed network calls., then the report link line and the last line. Steps for checks you did not ask for (here expectUrl and expectNoText) are skipped: they are not counted in the first number, so it can be lower than the second. Step numbers in the text start at 1, the same as in the HTML report; a step inside a loop reads like Step 3.2. In structuredContent the index of a step starts at 0.
get_report
Reads the test report of a smoke test, a skill run or a task. It waits while the test is still running, so call it again with the same id until the verdict is not running.
| Argument | Type | Required | Notes |
|---|---|---|---|
id | string | yes | A run id (run_…) from smoke_test or run_skill, or a task id (tsk_…) from run_task |
waitSeconds | integer | no | How long to wait while it is still running. 0 to 90. Default 30 |
Returns: the same as smoke_test: text with the summary and failed steps, and the whole report in structuredContent. isError is true only for verdict error. An id that does not exist in your project is not_found.
A task report says in its notes that a task has no assertions: verdict pass means the agent says it is done. A report of a running test has few details: steps and findings appear when it ends.
The text of a task report reads like this:
Task "Open the Pricing page and tell me the page heading" finished in 3 s: the agent says it is done. 2 agent steps, all ok. Answer: The Pricing page is open at https://webagent-test-web.pages.dev/pricing/. The page heading is "Pricing". No console errors or failed network calls. Note: no assertions were checked, so `ok` only means the agent says it is done.
The whole report is in structuredContent.Console errors and failed network calls come from the session log. The log is saved when the session ends. A report read while the session is still open has empty lists and no finalUrl, and says in notes that the log is not available: "The session log is not available (it is stored when the session ends), so console errors, failed network calls and URLs are missing." Call stop_session, then read the report again: the same report then has the log data. A task that left its session open (keepOpen, or still running) has no log until you do. smoke_test and a run_task that closes its own session return after the log is saved, so their report is complete at once.
{ "id": "run_us_xxxxxxxxxxxxxxxxxxxxxxxx", "waitSeconds": 60 }For the raw record of a run or task, use get_skill_run or get_task. The report fields are in Reading results. REST: GET /v1/skill-runs/:id/report.json and GET /v1/tasks/:id/report.json return the same report.
list_sessions
Lists the project's sessions, newest first (at most 200). No arguments.
Returns: { "sessions": [ … ] }. Each entry is a session record: id, status, engine, createdAt, endedAt, endReason, title.
{}get_session
Reads one session: status, regions and pool. While the session is open, the record also has its endpoints.
| Argument | Type | Required | Notes |
|---|---|---|---|
sessionId | string | yes | The session id |
id | string | no | Deprecated alias of sessionId. Send one of them, or both with the same value |
Returns: the session record as JSON. An unknown id, or one from another project, is not_found.
{ "sessionId": "sess_us_xxxxxxxxxxxxxxxxxxxxxxxx" }stop_session
Stops a session and ends its record. Safe to repeat.
| Argument | Type | Required | Notes |
|---|---|---|---|
sessionId | string | yes | The session id |
id | string | no | Deprecated alias of sessionId |
Returns: { "deleted": true, "closed": true }. closed is true when this call stopped a running browser. Repeating the call on a session that already ended returns { "deleted": true, "closed": false }. deleted is false when the id is unknown or not yours.
{ "sessionId": "sess_us_xxxxxxxxxxxxxxxxxxxxxxxx" }scrape
Loads a URL in a cloud browser and returns the page content. Each call opens its own fresh session, so it never sees a page you are signed in to. See Sessions.
| Argument | Type | Required | Notes |
|---|---|---|---|
url | string | yes | The page |
format | array | no | Any of html, cleaned_html, markdown, readability. Default ["markdown"] |
waitUntil | string | no | domcontentloaded, load or networkidle. Default load |
waitForText | string | no | Wait until this text is on the page. At most 500 characters |
timeoutMs | integer | no | 1000 to 120 000. Default 30 000 |
screenshot, pdf, mhtml | boolean | no | Also return the page as base64 inside the JSON |
inventory | boolean | no | Also list embedded media and third-party hosts |
engine, device, viewport, block, allowHosts, proxyUrl | no | Same meaning as in create_session |
Returns: JSON as text and structuredContent: url, content (one key per requested format), metadata (title, status code, description, word count), links.
{ "url": "https://webagent-test-web.pages.dev", "format": ["markdown"] }{
"url": "https://webagent-test-web.pages.dev/",
"content": { "markdown": "[Test Arena](/)\n...\n# A practice site for browser agents\n..." },
"metadata": {
"title": "Test Arena · a practice site for browser agents",
"statusCode": 200,
"language": "en",
"wordCount": 190
},
"links": [{ "url": "https://webagent-test-web.pages.dev/pricing/", "text": "Pricing", "internal": true }]
}screenshot
Loads a URL and returns an image. The result is an MCP image content item, with no structuredContent.
| Argument | Type | Required | Notes |
|---|---|---|---|
url | string | yes | The page |
fullPage | boolean | no | Default true |
format | string | no | png, jpeg or webp. Default png |
quality | integer | no | 0 to 100 |
waitUntil, waitForText, timeoutMs | no | As in scrape | |
engine, device, viewport, block, allowHosts, proxyUrl | no | As in create_session |
{ "url": "https://example.com", "fullPage": false, "format": "jpeg", "quality": 80 }This tool takes a screenshot of a fresh page. It cannot show the state a run_task left behind.
pdf
Loads a URL and returns it as a PDF. The result is an MCP resource with uri webagent://quick/pdf, the MIME type application/pdf and the file as a base64 blob.
| Argument | Type | Required | Notes |
|---|---|---|---|
url | string | yes | The page |
paper | string | no | A4, Letter or Legal. Default A4 |
landscape | boolean | no | Default false |
printBackground | boolean | no | Default true |
waitUntil, waitForText, timeoutMs | no | As in scrape | |
engine, device, viewport, block, allowHosts, proxyUrl | no | As in create_session |
{ "url": "https://example.com", "paper": "Letter" }crawl
Crawls from a URL and returns the pages it visited.
| Argument | Type | Required | Notes |
|---|---|---|---|
url | string | yes | Where to start |
scope | string or array | no | host (default), subdomains, or a list of up to 20 hosts |
include, exclude | string array | no | URL patterns. Up to 50 each |
maxPages | integer | no | 1 to 100. Default 25 |
maxDepth | integer | no | 0 to 5. Default 2 |
delayMs | integer | no | 0 to 10 000. Default 500 |
respectRobots | boolean | no | Default true |
sitemap | boolean | no | Default false |
content | array | no | Also return page content, in these formats |
maxMs | integer | no | Limit for the whole crawl. 1000 to 600 000. Default 300 000 |
cursor | string | no | Continue a crawl that stopped. Use the cursor from the last result |
Returns: JSON with start, pages (each with url, depth, status, title, linksFound, ms), skipped, skippedTotal, stoppedBy (complete, maxPages or timeout), pending and, when the crawl stopped early, a cursor.
The crawl obeys the site's robots.txt unless you set respectRobots to false. A site that disallows everything returns no pages. Only crawl sites you own or may test.
{ "url": "https://staging.example.com", "maxPages": 10, "maxDepth": 1 }list_skills
Lists the skills your project can run, with their inputs and permissions. No arguments. Each skill also has its own skill_<name> tool.
Returns: { "skills": [ … ] }. See Skills.
{}run_skill
Starts a skill run by name and returns at once. It does not wait. Call get_report with the runId for the pass/fail report, or get_skill_run for the raw record. To test a page or flow, smoke_test is easier, because it waits and returns the report. The typed skill_<name> tool also waits.
| Argument | Type | Required | Notes |
|---|---|---|---|
name | string | yes | The skill name, for example smoke-uat |
inputs | object | no | The skill's inputs. Default {} |
version | string | no | A skill version. Default: the newest |
grant | array | no | Permissions to grant, from network_capture, active_scan, file_access. A skill that does active security probes needs ["active_scan"]. Ask the user first |
session | object | no | Session options for the run, for example proxyUrl and allowHosts |
engineId picks the engine for the run's session, as on the REST route.
Returns: { "runId": "run_…", "sessionId": "sess_us_…", "status": "running", "placement": "queued" }.
{ "name": "smoke-uat", "inputs": { "url": "https://staging.example.com" } }get_skill_run
Reads a skill run. While it runs, the tool waits up to waitSeconds for the verdict.
| Argument | Type | Required | Notes |
|---|---|---|---|
runId | string | yes | The run id |
waitSeconds | integer | no | 0 to 120. Default 0, which does not wait |
Returns: a short text summary and the run in structuredContent. See the skill result below. It sets isError only for status error, denied or cancelled. A failed run is a normal result: read status. For a verdict with failed steps, console errors and failed network calls, call get_report with the runId.
{ "runId": "run_xxxxxxxxxxxxxxxxxxxxxxxx", "waitSeconds": 60 }skill_<name>
One tool exists for each skill your project can see. A skill named smoke-uat becomes skill_smoke_uat: dashes turn into underscores. The tool starts the run, waits for the verdict and returns it with a report link.
The tool's description comes from the skill. Its arguments are the skill's own inputs, flat, plus two more:
| Argument | Type | Required | Notes |
|---|---|---|---|
| each skill input | the input's type | as the skill says | A url input is a string. A string[] input is an array of strings. A secret input takes a vault reference such as https://host#password, never the secret itself |
options.waitSeconds | integer | no | 0 to 120. Default 30. A run still going when the wait ends comes back with status: "running" and a runId for get_skill_run |
options.session | object | no | Session options for this run, for example proxyUrl and allowHosts to reach a private staging site |
confirm | boolean | yes, for active scans only | Must be true. The tool has this argument only when the skill sends active security probes. Set it only after the user confirms they may test the target |
Skill runs cannot sign in yet. If options.session has credentials, profileId, persistProfile, profileName or extensionIds, the tool returns isError with invalid_input ("Signed-in skill runs are not available yet: remove session.credentials") and starts nothing. See Skills.
Returns: the same as get_skill_run: the raw run and a report link. The call sets isError: true only when the run could not run to a result: status error, denied or cancelled. A run that failed is a normal result, and so is one still running. To get a verdict with console errors and failed network calls, use smoke_test or get_report with the runId.
{
"url": "https://staging.example.com",
"steps": ["click Sign in", "type \"qa@example.com\" into Email"],
"expectText": "Welcome",
"options": { "waitSeconds": 60 }
}The skill result
The text of the result lists, in this order: the skill name, version, run id and status; the skill's summary; the reason for a stop, if there is one; the counts; up to five failed steps; and the report link.
structuredContent is the run: runId, skill, sessionId, status, startedAt, ms, summary, counts, result, hasReport, and reportUrl once a report exists. The statuses are running, passed, failed, error, cancelled and denied.
- Screenshots and video clips are left out of
structuredContent. They are in the report. - Strings longer than 2000 characters are cut, and only the last 50 log lines are kept.
- If the result is larger than 100 000 bytes, each step is reduced to one line:
index,path,kind,label,ok,msanderror. reportUrlis a signed link that works without an API key. It stops working after a few minutes. See Evidence.
Limits that apply to tools
Waits are capped so that a single reply never stays silent for too long: 90 seconds for run_task, get_task, smoke_test and get_report, 120 seconds for the skill tools. A longer job keeps running, and you read it with get_report, get_task or get_skill_run. See Limits.