Writ Cloud
Read, crawl and act on websites, signed in as the user, and turn any site into an API.
Should I use this
Quality & Safety
Findings (8)
- HIGH
- LOWin writ_browser_ask_user
- LOWin writ_browser_context
- LOWin writ_browser_network
- LOWin writ_browser_cancel
- LOWin writ_browser_sessions
- LOWin writ_diagnose_http_workflow
- LOWin writ_list_webhooks
Based on automated analysis of tool definitions and protocol compliance.
Context Cost
This is the approximate number of tokens consumed each time the server's tools are loaded into a model's context. Higher counts reduce the attention available for other tasks.
Install
One-Click Install
Add this to your `claude_desktop_config.json` file:
{
"mcpServers": {
"writ-cloud": {
"command": "npx",
"args": [
"writ-mcp"
]
}
}
}Runnable packages
1.2.0stdioRemote endpoints
https://api.usewrit.app/mcpstreamable-httpWhat it can do
Tool inventory
Tools (40)
🔴writ_browser_use(url, persona_id, goal, use_residential, execution_target)
A REAL CLOUD BROWSER FOR A TASK ON A SITE: the user's signed-in account (a persona_id from writ_personas: the password stays sealed in Writ, and sessions never ask for a password), a click, form, submit or search inside an app, or a page a plain fetch cannot open (login wall, 403, CAPTCHA). Opens the browser and returns the first page; writ_browser_act drives it (Writ runs no model here). Recording is always on: writ_browser_save turns the session into a workflow that replays without a browser, writ_browser_cancel closes it (an open browser bills). The start answer carries the action vocabulary, the recording rules and, with a persona, the 2FA rules. NOT FOR: reading pages (writ_scrape) or building an API (writ_website_to_api).
Input Schema
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"persona_id": {
"type": [
"integer",
"string"
]
},
"goal": {
"type": "string"
},
"use_residential": {
"type": "boolean",
"description": "Open on the residential network (bot walls, CAPTCHAs)."
},
"execution_target": {
"type": "string",
"description": "cloud | auto | local (the user's own desktop app)."
}
},
"required": [
"url"
]
}🔴writ_record_website(goal, url, persona_id, use_residential)
Records a repeatable task on a site as a workflow (goal + start url): the same session as writ_browser_use, labelled with its goal, driven with writ_browser_act and saved with writ_browser_save. A goal that asks for an API goes to writ_website_to_api's build instead.
Input Schema
{
"type": "object",
"properties": {
"goal": {
"type": "string"
},
"url": {
"type": "string"
},
"persona_id": {
"type": [
"integer",
"string"
]
},
"use_residential": {
"type": "boolean",
"description": "Residential network (bot walls)."
}
},
"required": [
"goal",
"url"
]
}🔴writ_website_to_api(url, goal, save_as, persona_id, mode, ...)
TURNS A WEBSITE INTO A CALLABLE API (named functions with inputs, returning JSON) for a service with no practical official API. 3 calls: (1) this tool with url = the page that already shows the rows (a search-results or listing URL, not the home page) and goal = the inputs and fields wanted; (2) writ_discovery_status(build_id, wait=true); (3) on success, its run_example (writ_run_workflow with function_name). Writ builds it itself (an AI fast path in a real browser, each function live-tested) and first proposes the user's matching saved workflows and marketplace APIs (skip_existing / skip_marketplace build fresh). A sign-in wall answers persona_needed. NOT FOR: reading content (writ_scrape) or a one-off task (writ_browser_use). Modes and the guided build: writ_help('writ_website_to_api').
Input Schema
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"goal": {
"type": "string"
},
"save_as": {
"type": "string"
},
"persona_id": {
"type": "integer"
},
"mode": {
"type": "string",
"enum": [
"intelligent",
"guided",
"auto",
"fast",
"crawl",
"browser",
"regular",
"deep"
],
"description": "intelligent (default, Writ builds it) | guided (the caller drives) | auto | fast | crawl | browser."
},
"build_id": {
"type": "integer"
},
"skip_existing": {
"type": "boolean",
"description": "Skip the proposal of the user's own matching workflows."
},
"skip_marketplace": {
"type": "boolean",
"description": "Skip the marketplace proposals."
}
}
}🟢writ_discovery_status(build_id, wait, timeout_seconds)
Status of a writ_website_to_api build. wait=true is one held call (up to 75 s) that follows the build to its newest rung and returns when it is done or parked. Answers: succeeded (workflow_id, functions, run_example), failed, cancelled, or needs_guidance (the build is the caller's to finish with writ_website_to_api mode=guided build_id=<id>; its map comes with it).
Input Schema
{
"type": "object",
"properties": {
"build_id": {
"type": "integer"
},
"wait": {
"type": "boolean",
"description": "One held call until done or parked."
},
"timeout_seconds": {
"type": "integer",
"description": "Ceiling for wait=true (up to 75)."
}
},
"required": [
"build_id"
]
}🔴writ_browser_act(session_id, actions, inputs, max_chars)
Runs one batch of actions on an open session and returns the fresh page. Each action is an object, e.g. {"action":"click","selector":"#go"}. DRIVE: navigate, click, fill, type_text, select, check, hover, submit, press_key, scroll, back, wait, wait_for. SEE: query_dom, find_text, read_text, count, get_attributes, inspect, list_candidates, get_dom, get_screenshot. NETWORK: capture_network, get_request, rotate_exit. CODE: evaluate_js, search_scripts, read_script. RECORD: extract {variable,script}, api_call, login_post, probe_write, confirm_write. 2FA: twofa {challenge_method}. A value written {{name}} with its real value in `inputs` becomes a workflow input. Each action's arguments: the session's start answer, or writ_help('writ_browser_act').
Input Schema
{
"type": "object",
"properties": {
"session_id": {
"type": "string"
},
"actions": {
"type": "array",
"items": {
"type": "object"
},
"description": "Ordered action objects, e.g. [{\"action\":\"click\",\"selector\":\"#login\"}]."
},
"inputs": {
"type": "object"
},
"max_chars": {
"type": "integer",
"description": "Clip of the returned page (default 40000)."
}
},
"required": [
"session_id",
"actions"
]
}🟢writ_browser_context(session_id, section)
Re-reads an open session: section=page (url, fields, buttons, cleaned DOM), lists (the page's repeating rows with a live-tested define_function payload for writ_browser_compose), map (the evidence of the build the session is bound to).
Input Schema
{
"type": "object",
"properties": {
"session_id": {
"type": "string"
},
"section": {
"type": "string",
"enum": [
"page",
"lists",
"map",
"explorer",
"concierge_api"
],
"description": "page (default) | lists | map | explorer | concierge_api."
}
},
"required": [
"session_id"
]
}🟢writ_browser_network(session_id, operation, query, method, index)
Searches (operation=search, query, method) or reads in full (operation=detail, index) the backend requests the page made after capture_network: the site's real API. An index feeds writ_browser_compose define_function from_index.
Input Schema
{
"type": "object",
"properties": {
"session_id": {
"type": "string"
},
"operation": {
"type": "string",
"enum": [
"search",
"detail"
],
"description": "search (default) | detail."
},
"query": {
"type": "string",
"description": "Substring filter."
},
"method": {
"type": "string",
"description": "HTTP method filter."
},
"index": {
"type": "integer",
"minimum": 0,
"description": "detail: which call."
}
},
"required": [
"session_id"
]
}🔴writ_browser_compose(session_id, operation, payload)
Authors the workflow in an open session beyond a recording: named API functions (define_function from a captured call's index or a lists payload, live-tested as defined; compile_function for an authenticated request), set_inputs, add_steps, test_function, remove_function, remove_step, list. Payload shapes: writ_help('writ_browser_compose').
Input Schema
{
"type": "object",
"properties": {
"session_id": {
"type": "string"
},
"operation": {
"type": "string",
"enum": [
"list",
"define_function",
"compile_function",
"test_function",
"remove_function",
"add_steps",
"remove_step",
"set_inputs"
],
"description": "list | define_function | compile_function | test_function | remove_function | add_steps | remove_step | set_inputs."
},
"payload": {
"type": "object",
"description": "The operation's arguments."
}
},
"required": [
"session_id",
"operation"
]
}🔴writ_browser_save(session_id, name, description, allow_no_data)
Saves the open session as a replayable workflow and closes the browser. Composed functions become callable API functions, declared inputs become its parameters. The answer carries the steps, the inputs and a run_example; the workflow runs at zero AI cost through writ_run_workflow. A save that references something no run could resolve is refused with the reasons, and the browser stays open.
Input Schema
{
"type": "object",
"properties": {
"session_id": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"allow_no_data": {
"type": "boolean",
"description": "Save a workflow that returns no data, on purpose."
}
},
"required": [
"session_id"
]
}🟢writ_browser_ask_user(session_id, kind, question, wait_seconds)
Hands an open session to the Writ user: kind='captcha' (they complete a security check in the live browser), 'twofa' (they supply the one-time code and Writ types it; the code never reaches this conversation), or 'question' (their text answer). Holds up to 60 s; answers answered, waiting_for_user (the same call again) or expired.
Input Schema
{
"type": "object",
"properties": {
"session_id": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"captcha",
"question",
"twofa"
],
"description": "captcha | twofa | question."
},
"question": {
"type": "string",
"description": "kind='question': one short sentence."
},
"wait_seconds": {
"type": "integer",
"description": "Hold up to this long (1-60)."
}
},
"required": [
"session_id"
]
}🔴writ_browser_cancel(session_id, discard)
Closes an open browser session (an open cloud browser bills until it is closed). Unsaved work that did more than visit pages is auto-saved first and the reply names it; discard=true keeps nothing.
Input Schema
{
"type": "object",
"properties": {
"session_id": {
"type": "string"
},
"discard": {
"type": "boolean",
"description": "Throw away unsaved steps instead of auto-saving."
}
},
"required": [
"session_id"
]
}🟢writ_browser_sessions(limit)
Lists the account's open cloud browser sessions (each resumable by its session_id).
Input Schema
{
"type": "object",
"properties": {
"limit": {
"type": "integer"
}
}
}🟡writ_personas(action, domain, link_id, persona_id, why)
The user's own accounts on websites (personas): how Writ signs in as them. No password passes through this tool or the conversation, and its answers never ask for one: credentials are typed only into a Writ window. action='list' domain=<site> shows the personas usable there; with none it answers persona_needed: a tell_user for the user, a create_url (a small Writ window with only the sign-in form, pre-filled for the site) and a link_id. action='wait' link_id=<id> is one held call that answers when the user saves it, with its persona_id. Also get, request (a link for another account), sign_in (refresh the session) and record_login. A persona_id works on writ_scrape, writ_crawl_site, writ_browser_use and writ_run_workflow; a desktop persona ('device:...') signs in on that desktop, from its own vault.
Input Schema
{
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": [
"list",
"get",
"request",
"wait",
"sign_in",
"record_login"
],
"description": "list (default) | get | request | wait | sign_in | record_login."
},
"domain": {
"type": "string",
"description": "list: personas usable on this site. request: the site."
},
"link_id": {
"type": "string",
"description": "wait: the link_id of a persona_needed / request answer."
},
"persona_id": {
"type": "integer"
},
"why": {
"type": "string",
"description": "request: one line the user sees in the window."
}
},
"required": [
"action"
]
}🔴writ_payment(action, site, max_amount, purpose, grant_id, ...)
Pays on a site with one of the user's cards; card numbers never reach this conversation. request (site, max_amount, purpose) returns a tell_user and an open_url where the user approves; wait (grant_id or confirmation_id); checkout pauses at the order button for the user's confirmation, then Writ places the order; fill; list (card handles only). Details: writ_help('writ_payment').
Input Schema
{
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": [
"request",
"wait",
"checkout",
"fill",
"list"
],
"description": "request | wait | checkout | fill | list."
},
"site": {
"type": "string",
"description": "request: the store's domain."
},
"max_amount": {
"type": "number",
"description": "The most it may charge."
},
"purpose": {
"type": "string",
"description": "One line the user reads."
},
"grant_id": {
"type": "string"
},
"confirmation_id": {
"type": "string"
},
"session": {
"type": "string",
"description": "The browser session on the checkout page."
},
"commit_selector": {
"type": "string",
"description": "checkout: the place-order button."
}
},
"required": [
"action"
]
}⚪writ_devices(action, device)
The user's linked Writ desktops: list, use (scope this connection to one: runs, browsers, personas and monitors then target it), clear, monitors, datasets.
Input Schema
{
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": [
"list",
"use",
"clear",
"monitors",
"datasets"
],
"description": "list (default) | use | clear | monitors | datasets."
},
"device": {
"type": "string",
"description": "use: the desktop's agent_id or name."
}
}
}🟢writ_list_workflows(search)
Lists the workflows saved in the Writ account; each runs on demand without live browsing. Returns id, name, declared inputs, schedule, and whether it is pinned as its own run_<name> tool.
Input Schema
{
"type": "object",
"properties": {
"search": {
"type": "string",
"description": "Name/description filter, e.g. a site."
}
}
}🔴writ_run_workflow(workflow_id, workflow, inputs, function_name, persona_id, ...)
Runs a saved workflow by workflow_id or name and returns its extracted data (it waits by default). Inputs go as top-level fields or under `inputs`; function_name runs one function of a multi-function API (with the sign-in it needs). max_age reuses a recent result at no cost. A write function previews with mutation_mode='dry_run'. Runs sign in as the workflow's persona or persona_id; no password is ever passed. Venue, network, files and output shape: writ_help('writ_run_workflow').
Input Schema
{
"type": "object",
"properties": {
"workflow_id": {
"type": [
"integer",
"string"
],
"description": "A number, or 'local:<id>' (a workflow on the user's desktop)."
},
"workflow": {
"type": "string",
"description": "Workflow name (or workflow_id)."
},
"inputs": {
"type": "object",
"description": "Run inputs (top-level fields work too)."
},
"function_name": {
"type": "string",
"description": "One function of a multi-function workflow."
},
"persona_id": {
"type": [
"integer",
"string"
],
"description": "Run as this saved identity."
},
"max_age": {
"type": "integer",
"minimum": 0,
"description": "Reuse a result younger than this many seconds (free). 0 = fresh."
},
"mutation_mode": {
"type": "string",
"enum": [
"live",
"dry_run",
"private_test"
],
"description": "live (default) | dry_run (preview, nothing sent) | private_test."
},
"execution_target": {
"type": "string",
"description": "cloud | auto | local (the user's own desktop app)."
}
}
}🔴writ_update_workflow(workflow_id)
Inspects or edits a saved workflow: an outline, one function's source, targeted function or step edits, a settings patch. Every option: writ_help('writ_update_workflow').
Input Schema
{
"type": "object",
"properties": {
"workflow_id": {
"type": "integer"
}
}
}🔴writ_create_http_extraction(goal, workflow_id, apply)
Drafts or revises a multi-request browserless HTTP extraction (loops, GraphQL, cursor pagination) from captured requests and a plain-language goal. Every option: writ_help('writ_create_http_extraction').
Input Schema
{
"type": "object",
"properties": {
"goal": {
"type": "string"
},
"workflow_id": {
"type": "integer"
},
"apply": {
"type": "boolean",
"description": "false (default) drafts; true writes."
}
},
"required": [
"goal"
]
}🟢writ_diagnose_http_workflow(workflow_id, task_id)
Explains why a saved workflow's run returned nothing or failed: HTTP-lane readiness and, with task_id, the run's requests and responses.
Input Schema
{
"type": "object",
"properties": {
"workflow_id": {
"type": "integer"
},
"task_id": {
"type": "integer"
}
}
}⚪writ_pin_workflow_tool(workflow_id, pinned)
Pins (or unpins) a saved workflow as its own run_<name> tool on this server.
Input Schema
{
"type": "object",
"properties": {
"workflow_id": {
"type": "integer"
},
"pinned": {
"type": "boolean"
}
}
}🟢writ_workflow_data(workflow_id, q, run_id, limit, refs)
Reads a saved workflow's collected data as a table: q filters, run_id picks one run, refs returns full records.
Input Schema
{
"type": "object",
"properties": {
"workflow_id": {
"type": "integer"
},
"q": {
"type": "string"
},
"run_id": {
"type": "integer"
},
"limit": {
"type": "integer"
},
"refs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Full records by '<run_id>:<record_index>'."
}
}
}🟢writ_search_data(q, workflow_id, limit)
Searches everything the account's workflows already collected for a term, without running anything.
Input Schema
{
"type": "object",
"properties": {
"q": {
"type": "string"
},
"workflow_id": {
"type": "integer"
},
"limit": {
"type": "integer"
}
},
"required": [
"q"
]
}🟢writ_export_data(workflow_id, format)
Exports a workflow's full data table as CSV or JSON.
Input Schema
{
"type": "object",
"properties": {
"workflow_id": {
"type": "integer"
},
"format": {
"type": "string",
"description": "csv (default) | json."
}
}
}🟢writ_workflow_runs(workflow_id, status, limit)
Run history (status, timing, errors, venue) of one workflow or all of them.
Input Schema
{
"type": "object",
"properties": {
"workflow_id": {
"type": "integer"
},
"status": {
"type": "string",
"description": "Filter by status."
},
"limit": {
"type": "integer"
}
}
}🔴writ_set_schedule(workflow_id, every_minutes, kind, time, days, ...)
Schedules a saved workflow: every_minutes, or kind='daily'/'weekly' with time (HH:MM) and days. functions + inputs schedule chosen functions of a multi-function API.
Input Schema
{
"type": "object",
"properties": {
"workflow_id": {
"type": "integer"
},
"every_minutes": {
"type": "integer",
"description": "Interval in minutes."
},
"kind": {
"type": "string",
"description": "interval | daily | weekly."
},
"time": {
"type": "string",
"description": "HH:MM (daily/weekly)."
},
"days": {
"type": "array",
"items": {
"type": "integer"
},
"description": "Weekdays 1=Mon..7=Sun (weekly)."
},
"tz": {
"type": "string",
"description": "IANA timezone."
}
}
}🟡writ_expose_workflow_api(workflow_id, label)
Publishes a saved workflow as a REST endpoint (a POST URL that waits and returns its JSON). Repeated calls reuse it.
Input Schema
{
"type": "object",
"properties": {
"workflow_id": {
"type": "integer"
},
"label": {
"type": "string"
}
}
}🔴writ_crawl_site(url, intent, rank_cap, seed_urls, page_budget, ...)
COLLECTS a site or a section into a queryable, change-tracked dataset (one row per page). Scope: intent (a section, in plain words), rank_cap=N (the top N items of a listing), seed_urls (known pages), or the whole site capped by page_budget. Modes: markdown (default, no AI), extract_mode='schema' (uniform records), executor='ai' + extract_prompt (per-page understanding, 5x the page rate). A bounded crawl answers with its rows; an open one returns a crawl_id for writ_crawl_status wait=true. NOT FOR: reading a few pages now (writ_scrape). Every option: writ_help('writ_crawl_site').
Input Schema
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"intent": {
"type": "string",
"description": "The section wanted, in plain words; the server derives paths and depth."
},
"rank_cap": {
"type": "integer",
"minimum": 1,
"maximum": 50,
"description": "Top N items of a listing (one page per agent, in parallel)."
},
"seed_urls": {
"type": "array",
"items": {
"type": "string"
},
"description": "Exact pages to collect (no discovery)."
},
"page_budget": {
"type": "integer",
"description": "Cap on pages fetched."
},
"extract_mode": {
"type": "string",
"description": "markdown (default) | schema | html."
},
"executor": {
"type": "string",
"description": "regular (default, no AI) | ai (5x the page rate)."
},
"extract_prompt": {
"type": "string",
"description": "With executor=ai: what to extract from each page."
},
"persona_id": {
"type": [
"integer",
"string"
]
},
"save_as": {
"type": "string"
},
"wait": {
"type": "boolean",
"description": "Hold until done and return the rows (default true when bounded)."
}
},
"required": [
"url"
]
}🟢writ_saved_crawls(limit)
Lists the crawls saved for re-running, with their scope.
Input Schema
{
"type": "object",
"properties": {
"limit": {
"type": "integer"
}
}
}🔴writ_update_saved_crawl(crawl, settings)
Inspects or changes a saved crawl's settings in place.
Input Schema
{
"type": "object",
"properties": {
"crawl": {
"type": "string"
},
"settings": {
"type": "object",
"description": "Sparse settings patch."
}
},
"required": [
"crawl"
]
}⚪writ_run_saved_crawl(crawl, max_age, wait)
Runs a saved crawl; max_age returns its recent data at no cost instead of re-crawling.
Input Schema
{
"type": "object",
"properties": {
"crawl": {
"type": "string"
},
"max_age": {
"type": "integer",
"minimum": 0,
"description": "Reuse a run younger than this (seconds)."
},
"wait": {
"type": "boolean",
"description": "Hold until done."
}
},
"required": [
"crawl"
]
}🟢writ_saved_crawl_data(crawl, limit)
Reads the data a saved crawl collected on its last completed run, without crawling.
Input Schema
{
"type": "object",
"properties": {
"crawl": {
"type": "string"
},
"limit": {
"type": "integer"
}
},
"required": [
"crawl"
]
}🟢writ_scrape(url, urls, top_n, format, render_mode, ...)
READ PAGES NOW, no browser session: one page (url), up to 20 pages in parallel (urls), or a listing plus its top N item pages (url + top_n). Returns clean markdown in 2-10 s, including pages a plain fetch cannot read: rendered by JavaScript (render_mode='browser'; a read that only got the page's shell says so and gives the retry), behind a bot wall or 403 (an automatic residential retry), or behind the user's sign-in (persona_id). Discussion threads keep their comments. format='html' returns the raw HTML for selectors. NOT FOR: a site-wide dataset (writ_crawl_site) or clicking and typing (writ_browser_use).
Input Schema
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"urls": {
"type": "array",
"items": {
"type": "string"
},
"description": "Up to 20 known pages, read in parallel."
},
"top_n": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"description": "With url = a listing: also read its N top-ranked item pages (max 20)."
},
"format": {
"type": "string",
"enum": [
"markdown",
"html",
"both"
],
"description": "markdown (default) | html (raw HTML) | both."
},
"render_mode": {
"type": "string",
"description": "auto (default: HTTP, a browser when the page needs JS) | http | browser."
},
"persona_id": {
"type": [
"integer",
"string"
],
"description": "Read signed in as this saved identity (writ_personas)."
},
"use_residential": {
"type": "boolean",
"description": "Residential network for a bot wall (automatic retry when refused)."
}
}
}🟢writ_crawl_status(crawl_id, wait, limit)
Status of a crawl by crawl_id; wait=true holds up to 75 s and returns the collected rows when it converges.
Input Schema
{
"type": "object",
"properties": {
"crawl_id": {
"type": "integer"
},
"wait": {
"type": "boolean",
"description": "Hold until done and inline the rows."
},
"limit": {
"type": "integer"
}
},
"required": [
"crawl_id"
]
}🟢writ_crawl_files(crawl_id, crawl)
The original files (PDFs, office documents, images, CSVs) a crawl captured, with short-lived download URLs.
Input Schema
{
"type": "object",
"properties": {
"crawl_id": {
"type": "integer"
},
"crawl": {
"type": "string"
}
}
}🟡writ_create_automation(name, when, schedule, on_workflow, target_id, ...)
Creates an automation: on an event (a workflow or crawl completed, a monitor change, a webhook, a schedule) it runs a workflow, sends a notification templated over the event's data, and/or wakes an AI agent. Event, template and webhook options: writ_help('writ_create_automation').
Input Schema
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"when": {
"type": "string",
"enum": [
"workflow_completed",
"workflow_started",
"crawl_completed",
"crawl_failed",
"ai_session_completed",
"ai_session_started",
"change_detected",
"webhook_received",
"scheduled"
],
"description": "workflow_completed | crawl_completed | change_detected | webhook_received | scheduled | ..."
},
"schedule": {
"type": "object",
"description": "when='scheduled': {kind, interval_minutes | time, days, tz}."
},
"on_workflow": {
"type": "string",
"description": "Source workflow for workflow_* events."
},
"target_id": {
"type": "integer",
"description": "change_detected: the monitor_id."
},
"run_workflow": {
"type": "string",
"description": "Workflow to run."
},
"notify": {
"type": "string",
"description": "Message template, e.g. {{result.extracted_data.0.title}}."
},
"channels": {
"type": "array",
"items": {
"type": "string"
},
"description": "Notification channels."
},
"ai_prompt": {
"type": "string",
"description": "Wake an AI agent with this task."
}
},
"required": [
"name"
]
}🟢writ_list_webhooks(webhook_trigger_id)
The account's inbound webhook URLs and the automations they fire.
Input Schema
{
"type": "object",
"properties": {
"webhook_trigger_id": {
"type": "integer",
"description": "Only this webhook."
}
}
}🟡writ_create_monitor(url, interval, selector, extract, watch, ...)
WATCHES a page for changes: Writ checks it on a schedule and fires change_detected when the page, a CSS selector's text, a value read by `extract`, or a visual zone changes. Returns the monitor_id that writ_wire_monitor connects to an alert, a workflow or an AI task (a price watch takes a threshold there). Without `interval` the answer is needs_input: this plan's options and a tell_user for the user, nothing created. The page is read first: a sign-in answers needs_persona, a bot check offers use_residential.
Input Schema
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"interval": {
"type": [
"string",
"integer"
],
"description": "How often: an option id from needs_input ('5m', '1h', '24h') or seconds."
},
"selector": {
"type": "string",
"description": "CSS selector whose text is watched (unset = whole page / uptime)."
},
"extract": {
"type": [
"object",
"string"
],
"description": "Browserless read instead of a selector, e.g. {\"from\":\"html_css\",\"selector\":\".price\"}."
},
"watch": {
"type": "string",
"enum": [
"content",
"price"
],
"description": "content (default) | price (the value must hold a number)."
},
"session_id": {
"type": "string"
},
"persona_id": {
"type": [
"integer",
"string"
]
},
"use_residential": {
"type": "boolean",
"description": "Check through a residential exit (bot walls)."
}
},
"required": [
"url"
]
}🟡writ_wire_monitor(monitor_id, action, channels, recipients, threshold, ...)
Connects a monitor's change_detected event to an action: action='notify' (channels and recipients the account has configured), 'workflow' (runs a saved workflow), or 'ai_task' (an AI agent opens the page and works `prompt`). A price watch takes `threshold` (+ threshold_op): the action then runs once, when a check reads a price at or below it. Buying at a price (`buy`): writ_help('writ_wire_monitor').
Input Schema
{
"type": "object",
"properties": {
"monitor_id": {
"type": "integer"
},
"action": {
"type": "string",
"enum": [
"workflow",
"notify",
"ai_task"
],
"description": "notify | workflow | ai_task."
},
"channels": {
"type": "array",
"items": {
"type": "string"
},
"description": "Notification channels, e.g. [\"email\",\"pushover\"]."
},
"recipients": {
"type": "array",
"items": {
"type": "string"
},
"description": "e.g. [\"email:3\"]; omitted = every enabled recipient."
},
"threshold": {
"type": [
"number",
"string"
],
"description": "Price watch: act once when the price reaches this number."
},
"threshold_op": {
"type": "string",
"enum": [
"lt",
"lte",
"gt",
"gte"
],
"description": "lte (default) | lt | gte | gt."
},
"workflow": {
"type": "string"
},
"prompt": {
"type": "string",
"description": "action='ai_task': what the agent does."
},
"buy": {
"type": "object",
"description": "Run the workflow or task as a purchase (saved as a rehearsal until armed)."
}
},
"required": [
"monitor_id",
"action"
]
}🟢writ_help(topic)
The full reference of any Writ tool (every parameter and its rules) and the complete guide. topic = a tool name (e.g. 'writ_browser_compose', a 'run_<workflow>' tool), 'guide' for the manual (recurring digests, HTTP flow building, buying on a price, crawl and build modes), 'actions' / 'recording' / '2fa' for the browser rules, or empty for the list of topics.
Input Schema
{
"type": "object",
"properties": {
"topic": {
"type": "string",
"description": "A tool name, 'guide', 'actions', 'recording', '2fa', or empty."
}
}
}Community
Evidence