Skip to main content
Glama

Writ Cloud

Compose a workflow in a session

writ_browser_compose
Destructive

Authors the workflow being built in an open browser session: named functions, inputs and explicit steps that make what the session drove a real, complex, callable workflow rather than a replay of clicks. WHEN IT IS NEEDED: a plain recording needs none of it (there a caller input is a {{name}} value + inputs on writ_browser_act, and the data is an extract action). It exposes named functions (an API), gives an input a description/default, and adds steps the recorder cannot see. OPERATIONS: define_function {name, fn_type api|list|script|extraction, ...} · compile_function {name, from_index}: deterministic (no-LLM) capture->function that traces session tokens to an is_auth bootstrap, generates per-call ids ({{uuid()}}), and marks a write so the build never sends it (a real run does) · test_function {name, sample_inputs} · remove_function {name} · set_inputs {inputs:{name:{default?,description?,required?,example?}}} · add_steps {steps:[{type,...}], at?} · remove_step {id} · list (the draft: steps, data_steps, functions, inputs, build). Fastest paths: (a) a list / table / search-results page: writ_browser_context section=lists returns a live-tested define_function payload, and with then_save:{name} one call here defines, tests and saves it. (b) a site endpoint: once capture_network and writ_browser_network locate the call, define_function {name:'quotes.list', from_index:, request:{url:'https://site/api/quotes?page={{page}}'}, input_variables:[{name:'page',example:'1'}], response_extractions:{quotes:{from:'json',path:'quotes'}, has_next:{from:'json',path:'has_next'}}, then_save:{name:'...'}}. Every function is live-tested as it is defined (an in-session request, or a DOM read, with sample_inputs={name: value}); a failed test returns feedback and keeps nothing, and a corrected definition reuses the same name. test=false skips the proof (a real run proves it later). Saved functions run through writ_run_workflow function_name. DETAILS:

  • define_function: a named callable the saved workflow exposes. fn_type api (backed by one of the site's endpoints: from_index=<a captured call's index from writ_browser_network> seeds method/url/headers/body from the capture; overridden request fields take {{name}} placeholders, secrets {{secret:name}}, anti-CSRF echoes {{cookie:NAME}}), script (a read-only JS IIFE returning the data from the page), list (the generated-JS form for any list/table: row_selector + fields {name: sub-selector | {selector, attr}}; writ_browser_context section=lists returns this payload ready-made), or extraction (one selector's text). A list/script/extraction function reads the page it was defined on: page_url as a template (https://site/search?q={{query}}), or an example on each input_variable from which the URL is templated. then_save:true (or {name, description}) saves the workflow the moment the function passes its live test. Names of the form . (orders.list, orders.create) group functions by surface. input_variables=[{name,description,required,example}], output_fields and response_extractions declare the fields callers pass and get back. Supported specs: JSON {from:'json',path:'data.items'}, embedded JSON {from:'embedded_json',kind:'array',has:['id']}, server HTML {from:'html_css',selector:'.row',attribute:'data-id',all:true}; with fields it returns row objects, which is how a server-rendered list becomes a BROWSERLESS function: {from:'html_css',selector:'tr.athing',all:true,base_url:'',fields:{title:{selector:'.titleline > a'},url:{selector:'.titleline > a',attribute:'href'}}} on an api function that GETs the page (no browser at replay, so cheaper than a list/script function whenever the rows are in the served HTML), regex {from:'regex',pattern:'...',group:1}, header {from:'header',name:'x-next'}, body {from:'body'}, or legacy '$.json.path'. The default shape is an Auphan-style named graph: ordered is_auth functions publish dynamic token/id/origin values consumed as {{extracted:name}}, while each data function remains independently callable. flow={version:1,steps:[...]} is for request loops, recursive mapping, cross-page dedupe, cursor pagination or a composite return; it is schema-validated and live-tested at once in the current browser session with its cookies, persona and egress, using the same interpreter as the saved HTTP lane, and the later saved run remains the final engine=http parity proof. is_auth=true marks the sign-in function: it runs first on every replay and its response_extractions publish values the others consume as {{extracted:}}.

  • test_function {name, sample_inputs}: proves a defined function again.

  • remove_function {name}.

  • add_steps {steps:[...], at?}: explicit replayable steps the DOM recorder cannot see (navigate, click, fill, select, press, wait, wait_for, extract, evaluate, api_call, login_post, return, upload, wait_for_download), inserted at a position (default: append).

  • remove_step {id}.

  • set_inputs {inputs:{name:{default?,description?,required?,example?}}}: the parameters a caller passes at run time. A save is refused unless every {{name}} in a step or function is a declared input, a credential, a {{cookie:}}/{{extracted:}} runtime reference, or produced by an earlier step. Credentials are never inputs: they come from the persona or a data_key fill.

  • list: the draft so far. On writ_browser_save, api functions become api_call steps (auth first), the workflow becomes api_recorded when every step is a call, and each function is callable by name (writ_run_workflow function_name) and documented at GET /api/v1/workflows/{id}/api-docs.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
payloadNoThe operation's arguments. define_function: {name, fn_type?, description?, surface?, from_index?, request?{method,url,headers,body_template}, flow?{version,steps}, script?, selector?, input_variables?, output_fields?, response_extractions?, is_auth?, order?, sample_inputs?, test?}. compile_function {name, from_index, sibling_index?, input_variables?, response_extractions?, page_url?}: the deterministic (no-LLM) way to turn a captured authenticated request (a GraphQL/RPC POST, a form submit) into a callable function. Writ decodes the body, keeps the static parameters, traces each session-minted token (csrf/xsrf/dtsg/lsd/etc.) to where a fresh session re-reads it and emits an is_auth bootstrap that publishes it as {{extracted:}}/{{cookie:}}, replaces per-call client values (idempotence token, session id, timestamp) with runtime generators ({{uuid()}}, {{uuid(session)}}, {{timestamp_ms()}}, {{counter()}}), templates the caller inputs, and classifies a write (create/post/send/delete): the build never sends it, a real run does (mutation_mode='dry_run' previews it). It suits any authenticated mutation or token-bound endpoint better than a hand-built api function. A second capture of the same request (sibling_index, else the nearest one Writ finds) tells the pagination inputs and per-call values from stable ids: a UUID unchanged between the two is kept as captured. test_function: {name, sample_inputs?}. remove_function: {name}. add_steps: {steps:[{type, config|flat fields, description?}], at?}. remove_step: {id}. set_inputs: {inputs:{name:{default?, description?, required?, example?}}}.
operationYes
session_idYesSession id from the start tool.

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

Score is being calculated.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.