← WebMCP Directory

Tool tree

traces.ribdsp.com

Developer ToolsTools verified
Website
A session replay player an AI agent can interrogate through WebMCP: bisect the timeline, read the DOM at any moment, and file a verifiable bug report.
Explore capabilities
    read_session_metaAnswer

    Get the shape of the recording before anything else: duration, viewport, browser, pages visited, and counts of clicks, inputs, console errors and failed requests. Call this first, because every other tool takes times in ms relative to the start of the recording.

    View tool JSON
    {
      "name": "read_session_meta",
      "kind": "answer",
      "impl": "imperative",
      "description": "Get the shape of the recording before anything else: duration, viewport, browser, pages visited, and counts of clicks, inputs, console errors and failed requests. Call this first, because every other tool takes times in ms relative to the start of the recording.",
      "inputSchema": {
        "type": "object",
        "properties": {},
        "additionalProperties": false
      }
    }
    list_eventsAnswer

    List the interesting things that happened in the recording, in time order: clicks, typing, navigations, console errors and warnings, failed network requests, and runs of repeated clicking on one element (rageClick). Mouse movement, scrolling and plain DOM mutations are deliberately left out — this is the orienting call you make after read_session_meta to decide where to look. At most 40 entries come back, earliest first; narrow the window or the kinds rather than asking for more.

    View tool JSON
    {
      "name": "list_events",
      "kind": "answer",
      "impl": "imperative",
      "description": "List the interesting things that happened in the recording, in time order: clicks, typing, navigations, console errors and warnings, failed network requests, and runs of repeated clicking on one element (rageClick). Mouse movement, scrolling and plain DOM mutations are deliberately left out — this is the orienting call you make after read_session_meta to decide where to look. At most 40 entries come back, earliest first; narrow the window or the kinds rather than asking for more.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "kinds": {
            "type": "array",
            "description": "Only return these kinds of event. Omit for all of them. Example: [\"consoleError\", \"failedRequest\"] to look for things that went wrong.",
            "items": {
              "type": "string",
              "enum": [
                "click",
                "input",
                "navigation",
                "consoleError",
                "consoleWarn",
                "failedRequest",
                "rageClick"
              ],
              "description": "One event kind. rageClick is three or more clicks on the same element within a second, which usually marks the moment a user realised something was broken."
            }
          },
          "from": {
            "type": "number",
            "description": "Start of the window, in ms from the start of the recording. Defaults to 0. Example: 20000 to look at everything after the 20-second mark."
          },
          "to": {
            "type": "number",
            "description": "End of the window, in ms from the start of the recording. Defaults to the end of the recording (durationMs from read_session_meta)."
          }
        },
        "additionalProperties": false
      }
    }
    find_elementAnswer

    Locate elements in the recorded page by CSS selector, by visible text, by ARIA role, or any combination of the three, and get back a selector for each that stays valid as the page changes, plus the window of time the element existed. Call this before bisect or measure_layout: those tools take a selector and re-resolve it at other timestamps, so a selector from here is what keeps them pointing at the same element. At most 5 matches come back.

    View tool JSON
    {
      "name": "find_element",
      "kind": "answer",
      "impl": "imperative",
      "description": "Locate elements in the recorded page by CSS selector, by visible text, by ARIA role, or any combination of the three, and get back a selector for each that stays valid as the page changes, plus the window of time the element existed. Call this before bisect or measure_layout: those tools take a selector and re-resolve it at other timestamps, so a selector from here is what keeps them pointing at the same element. At most 5 matches come back.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "selector": {
            "type": "string",
            "description": "CSS selector to match, e.g. \"select[name=province]\" or \"#checkout button\". Combine with text or role to narrow it further."
          },
          "text": {
            "type": "string",
            "description": "Case-insensitive text the element must contain, e.g. \"Pay\" or \"is required\". Only the innermost matching element is returned, not its wrappers."
          },
          "role": {
            "type": "string",
            "description": "ARIA role, matched against both an explicit role attribute and the implicit role of the tag: \"button\" also finds <button>, \"combobox\" finds <select>, \"link\" finds <a href>. Example: \"button\"."
          },
          "timestamp": {
            "type": "number",
            "description": "The moment to look at, in ms from the start of the recording. Defaults to where the human's playhead currently is. Pass a value when the element only exists for part of the recording."
          }
        },
        "additionalProperties": false
      }
    }
    read_dom_atAnswer

    Read the state of the recorded page at one moment as a compact, indented list of the interactive and state-bearing elements: form values, disabled and required flags, aria state, option counts, error text. This is not HTML — layout wrappers, classes and styling are dropped, and the output is capped at 60 lines / 1200 characters. Use it once you know which moment matters (from list_events or bisect); pass "scope" to zoom into one container when the whole page is too broad. It cannot tell you where things are on screen or what covers what — that is measure_layout.

    View tool JSON
    {
      "name": "read_dom_at",
      "kind": "answer",
      "impl": "imperative",
      "description": "Read the state of the recorded page at one moment as a compact, indented list of the interactive and state-bearing elements: form values, disabled and required flags, aria state, option counts, error text. This is not HTML — layout wrappers, classes and styling are dropped, and the output is capped at 60 lines / 1200 characters. Use it once you know which moment matters (from list_events or bisect); pass \"scope\" to zoom into one container when the whole page is too broad. It cannot tell you where things are on screen or what covers what — that is measure_layout.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "timestamp": {
            "type": "number",
            "description": "The moment to read, in ms from the start of the recording. Example: 28412 for 28.412 seconds in. Must be between 0 and durationMs from read_session_meta."
          },
          "scope": {
            "type": "string",
            "description": "Optional CSS selector for a container to read instead of the whole page, e.g. \"#checkout\" or \"form[name=payment]\". Use this when the full-page read comes back truncated."
          }
        },
        "required": [
          "timestamp"
        ],
        "additionalProperties": false
      }
    }
    read_consoleAnswer

    Read the console errors and warnings the recorded page logged inside a time window, in chronological order, each message truncated to 200 characters. Use it to find the exception behind a visible failure, and to get the timestamp to hand to bisect or read_dom_at. Only error- and warning-level output is captured, so an empty result means no errors or warnings were logged — not that the page logged nothing at all.

    View tool JSON
    {
      "name": "read_console",
      "kind": "answer",
      "impl": "imperative",
      "description": "Read the console errors and warnings the recorded page logged inside a time window, in chronological order, each message truncated to 200 characters. Use it to find the exception behind a visible failure, and to get the timestamp to hand to bisect or read_dom_at. Only error- and warning-level output is captured, so an empty result means no errors or warnings were logged — not that the page logged nothing at all.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "from": {
            "type": "number",
            "description": "Start of the window, in ms from the start of the recording. Defaults to 0. Example: pass 27000 and to: 30000 to read around a failure you located at 28412 ms."
          },
          "to": {
            "type": "number",
            "description": "End of the window, in ms from the start of the recording. Defaults to the end of the recording (durationMs from read_session_meta)."
          }
        },
        "additionalProperties": false
      }
    }
    read_networkAnswer

    List the network requests the recorded page made inside a time window — method, URL, status code, duration — including the ones that succeeded, which is what makes it useful for "the request went out and came back empty" as well as "the request failed". Response bodies are never returned; when the recorder captured one you get a one-line summary such as "array, 0 items", which is often exactly the evidence that an empty dropdown was the server's fault rather than the page's.

    View tool JSON
    {
      "name": "read_network",
      "kind": "answer",
      "impl": "imperative",
      "description": "List the network requests the recorded page made inside a time window — method, URL, status code, duration — including the ones that succeeded, which is what makes it useful for \"the request went out and came back empty\" as well as \"the request failed\". Response bodies are never returned; when the recorder captured one you get a one-line summary such as \"array, 0 items\", which is often exactly the evidence that an empty dropdown was the server's fault rather than the page's.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "from": {
            "type": "number",
            "description": "Start of the window, in ms from the start of the recording. Defaults to 0. Example: 20000 to see only what was requested after the 20-second mark."
          },
          "to": {
            "type": "number",
            "description": "End of the window, in ms from the start of the recording. Defaults to the end of the recording (durationMs from read_session_meta)."
          },
          "filter": {
            "type": "string",
            "description": "Case-insensitive substring the URL must contain, e.g. \"provinces\" or \"/api/\". Omit to see every request in the window."
          }
        },
        "additionalProperties": false
      }
    }
    read_markersAnswer

    List the markers pinned on the timeline inside a time window — what each one says, when it is, who made it, and whether a human rejected it. Use it to see what the person watching has already pointed at before you start looking, and to check what you pinned earlier survived: markers are the shared notes on this recording, and yours are only half of them. Pass a marker's atMs to seek to watch that moment.

    View tool JSON
    {
      "name": "read_markers",
      "kind": "answer",
      "impl": "imperative",
      "description": "List the markers pinned on the timeline inside a time window — what each one says, when it is, who made it, and whether a human rejected it. Use it to see what the person watching has already pointed at before you start looking, and to check what you pinned earlier survived: markers are the shared notes on this recording, and yours are only half of them. Pass a marker's atMs to seek to watch that moment.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "from": {
            "type": "number",
            "description": "Start of the window, in ms from the start of the recording. Defaults to 0, which is usually what you want here — there are rarely many markers."
          },
          "to": {
            "type": "number",
            "description": "End of the window, in ms from the start of the recording. Defaults to the end of the recording (durationMs from read_session_meta)."
          }
        },
        "additionalProperties": false
      }
    }
    bisectAction

    Find the first moment in the recording when a condition became true about an element, using a binary search over the replay timeline. Use this instead of reading the DOM at many timestamps: it takes about ten replays instead of dozens, and it returns a millisecond, not a guess. Typical use: locating exactly when a button became disabled, when a dropdown lost its options, or when an error message first appeared. The condition is assumed to change once and stay changed. Call read_session_meta first to learn the recording duration.

    View tool JSON
    {
      "name": "bisect",
      "kind": "act",
      "impl": "imperative",
      "description": "Find the first moment in the recording when a condition became true about an element, using a binary search over the replay timeline. Use this instead of reading the DOM at many timestamps: it takes about ten replays instead of dozens, and it returns a millisecond, not a guess. Typical use: locating exactly when a button became disabled, when a dropdown lost its options, or when an error message first appeared. The condition is assumed to change once and stay changed. Call read_session_meta first to learn the recording duration.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "selector": {
            "type": "string",
            "description": "CSS selector for the element to watch, e.g. \"#checkout button[type=submit]\". Use find_element first if you are not sure it exists."
          },
          "predicate": {
            "type": "object",
            "description": "The condition to search for. A structured object from a fixed set — expressions and code are not accepted.",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "propertyEquals",
                  "attributeExists",
                  "attributeEquals",
                  "optionCount",
                  "visible",
                  "textContains",
                  "exists"
                ],
                "description": "propertyEquals: a live DOM property. attributeExists / attributeEquals: an attribute. optionCount: number of <option> children, use 0 to find an empty dropdown. visible: rendered and non-zero-sized. textContains: substring of the text content. exists: the element is in the document."
              },
              "property": {
                "type": "string",
                "enum": [
                  "disabled",
                  "checked",
                  "readOnly",
                  "value"
                ],
                "description": "For propertyEquals only."
              },
              "attribute": {
                "type": "string",
                "description": "For attributeExists and attributeEquals. Letters and hyphens, up to 30 characters."
              },
              "text": {
                "type": "string",
                "description": "For textContains. Up to 100 characters, matched case-insensitively."
              },
              "equals": {
                "description": "The value to compare against: boolean for visible and exists, integer for optionCount, string or boolean for propertyEquals, string for attributeEquals."
              }
            },
            "required": [
              "kind"
            ]
          },
          "from": {
            "type": "number",
            "description": "Start of the search window, in ms from the beginning of the recording."
          },
          "to": {
            "type": "number",
            "description": "End of the search window, in ms. Usually durationMs from read_session_meta."
          },
          "precisionMs": {
            "type": "number",
            "description": "How close the answer needs to be, in ms. Defaults to 250. Larger is faster."
          }
        },
        "required": [
          "selector",
          "predicate",
          "from",
          "to"
        ],
        "additionalProperties": false
      }
    }
    diff_domAnswer

    Compare the recorded page at two moments and get back a short list of what was added, removed, and what attributes or text changed. Use it straight after bisect: bisect tells you when something changed, this tells you what else changed at the same time, which is usually where the cause is. Only elements read_dom_at would show are compared, and at most 30 changes come back, so keep the interval tight — a second either side of a transition beats the whole recording.

    View tool JSON
    {
      "name": "diff_dom",
      "kind": "answer",
      "impl": "imperative",
      "description": "Compare the recorded page at two moments and get back a short list of what was added, removed, and what attributes or text changed. Use it straight after bisect: bisect tells you when something changed, this tells you what else changed at the same time, which is usually where the cause is. Only elements read_dom_at would show are compared, and at most 30 changes come back, so keep the interval tight — a second either side of a transition beats the whole recording.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "from": {
            "type": "number",
            "description": "The earlier moment, in ms from the start of the recording. Example: 27900, just before a transition bisect found at 28412."
          },
          "to": {
            "type": "number",
            "description": "The later moment, in ms from the start of the recording. Example: 28900, just after that transition. Must be different from \"from\"."
          }
        },
        "required": [
          "from",
          "to"
        ],
        "additionalProperties": false
      }
    }
    measure_layoutAnswer

    Measure where elements were on screen at one moment — position, size, computed visibility, display, z-index — and which of them overlapped each other. This is how you diagnose a click that never landed: pass the element the user aimed at *and* the elements you suspect were on top of it in the same call, because overlap is only reported between elements measured together. Everything else about an element (its value, whether it is disabled, its text) comes from read_dom_at instead.

    View tool JSON
    {
      "name": "measure_layout",
      "kind": "answer",
      "impl": "imperative",
      "description": "Measure where elements were on screen at one moment — position, size, computed visibility, display, z-index — and which of them overlapped each other. This is how you diagnose a click that never landed: pass the element the user aimed at *and* the elements you suspect were on top of it in the same call, because overlap is only reported between elements measured together. Everything else about an element (its value, whether it is disabled, its text) comes from read_dom_at instead.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "selectors": {
            "type": "array",
            "description": "The elements to measure, as CSS selectors, e.g. [\"#checkout button[type=submit]\", \".modal-overlay\"]. Pass every element you want compared in one call: overlaps are only computed between elements in the same array.",
            "items": {
              "type": "string",
              "description": "One CSS selector. Use find_element first to get selectors that stay valid across the recording."
            }
          },
          "timestamp": {
            "type": "number",
            "description": "The moment to measure, in ms from the start of the recording. Example: 28412. Geometry changes as the page reflows, so this is not optional."
          }
        },
        "required": [
          "selectors",
          "timestamp"
        ],
        "additionalProperties": false
      }
    }
    seekAction

    Move the human's playhead to a moment in the recording so they are looking at what you are talking about. Call this before asking them a question.

    View tool JSON
    {
      "name": "seek",
      "kind": "act",
      "impl": "imperative",
      "description": "Move the human's playhead to a moment in the recording so they are looking at what you are talking about. Call this before asking them a question.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "timestamp": {
            "type": "number",
            "description": "Where to put the playhead, in milliseconds from the start of the recording, e.g. 28412 for 28.412s. Clamped into the recording if it is past the end."
          },
          "play": {
            "type": "number",
            "description": "How long to play for from that point, in milliseconds, e.g. 1500 to play a second and a half. Omit to park the playhead without playing. Capped at 5000; this call does not return until playback stops.",
            "minimum": 0,
            "maximum": 5000
          }
        },
        "required": [
          "timestamp"
        ],
        "additionalProperties": false
      }
    }
    annotateAction

    Put a labelled marker on the human's timeline at a given moment. Markers you create are shown as yours, and the human can accept, reject or undo each one individually.

    View tool JSON
    {
      "name": "annotate",
      "kind": "act",
      "impl": "imperative",
      "description": "Put a labelled marker on the human's timeline at a given moment. Markers you create are shown as yours, and the human can accept, reject or undo each one individually.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "timestamp": {
            "type": "number",
            "description": "The moment to mark, in milliseconds from the start of the recording, e.g. 28412 for 28.412s."
          },
          "label": {
            "type": "string",
            "description": "What is true at that moment, in a few words — this is the text shown under the pin, e.g. \"province dropdown has 0 options\". At most 80 characters; put the reasoning in propose_hypotheses instead."
          },
          "severity": {
            "type": "string",
            "enum": [
              "info",
              "warn",
              "error"
            ],
            "description": "How the marker is coloured: \"error\" for something broken, \"warn\" for something suspicious, \"info\" for a moment worth finding again."
          }
        },
        "required": [
          "timestamp",
          "label",
          "severity"
        ],
        "additionalProperties": false
      }
    }
    ask_human_visualSensitive Action

    Ask the human to look at the replay and answer a question you cannot answer yourself. You cannot see rendered output: you have no screenshots and no pixels, only the compressed DOM from read_dom_at. Use this only for questions that genuinely require eyes — whether something looked visibly broken, whether an element was covered, whether the user appeared confused — and not for anything you could establish with read_dom_at, bisect, or measure_layout. The human answers by clicking a moment on the player, so you get back their choice and the exact timestamp they marked. This call blocks until they respond; if they are slow you receive a ticket and should call again with it rather than starting a new question.

    View tool JSON
    {
      "name": "ask_human_visual",
      "kind": "transact",
      "impl": "imperative",
      "description": "Ask the human to look at the replay and answer a question you cannot answer yourself. You cannot see rendered output: you have no screenshots and no pixels, only the compressed DOM from read_dom_at. Use this only for questions that genuinely require eyes — whether something looked visibly broken, whether an element was covered, whether the user appeared confused — and not for anything you could establish with read_dom_at, bisect, or measure_layout. The human answers by clicking a moment on the player, so you get back their choice and the exact timestamp they marked. This call blocks until they respond; if they are slow you receive a ticket and should call again with it rather than starting a new question.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "question": {
            "type": "string",
            "description": "What you need a person to look at. State what you already know and what you cannot determine, e.g. \"The province dropdown has zero options from 28.4s. I cannot tell whether it looked visibly broken or just empty.\""
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Two to four short answers to choose from. Concrete options get answered in seconds; open questions get skipped."
          },
          "hintAtMs": {
            "type": "number",
            "description": "Where you suggest they look, in ms. The player seeks here. They may mark elsewhere."
          },
          "ticket": {
            "type": "string",
            "description": "Only when retrying a call that previously returned status \"pending\". Do not invent one."
          }
        },
        "required": [
          "question",
          "choices"
        ],
        "additionalProperties": false
      }
    }
    propose_hypothesesAction

    Propose two to five ranked explanations, each with the timestamps that support it, then wait for the human to promote or reject them. This call blocks until they decide.

    View tool JSON
    {
      "name": "propose_hypotheses",
      "kind": "act",
      "impl": "imperative",
      "description": "Propose two to five ranked explanations, each with the timestamps that support it, then wait for the human to promote or reject them. This call blocks until they decide.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "hypotheses": {
            "type": "array",
            "description": "Your ranked explanations, best first: 2 to 5 of them. The page rescales the confidences so they sum to 1.",
            "items": {
              "type": "object",
              "properties": {
                "text": {
                  "type": "string",
                  "description": "The explanation in one sentence, e.g. \"The province list is fetched after the form renders and the request fails, so the select stays empty.\" At most 240 characters."
                },
                "confidence": {
                  "type": "number",
                  "description": "How strongly you believe it, 0 to 1, e.g. 0.6. Rescaled with the others; shown as a bar, never as a percentage.",
                  "minimum": 0,
                  "maximum": 1
                },
                "evidence": {
                  "type": "array",
                  "description": "The moments that support it — at least one, at most 5. Clicking the card highlights all of them on the timeline, so a hypothesis with no evidence is rejected.",
                  "items": {
                    "type": "object",
                    "properties": {
                      "atMs": {
                        "type": "number",
                        "description": "When it happened, in milliseconds from the start of the recording, e.g. 28412. Must be inside the recording."
                      },
                      "note": {
                        "type": "string",
                        "description": "What that moment shows, in a few words, e.g. \"GET /provinces returned 500\". At most 120 characters."
                      },
                      "markerId": {
                        "type": "string",
                        "description": "Optional. The id annotate returned, if this evidence is a marker you already put on the timeline. Do not invent one."
                      }
                    },
                    "required": [
                      "atMs"
                    ],
                    "additionalProperties": false
                  }
                }
              },
              "required": [
                "text",
                "confidence",
                "evidence"
              ],
              "additionalProperties": false
            }
          },
          "ticket": {
            "type": "string",
            "description": "Only when retrying a call that previously returned status \"pending\". Do not invent one — pass back exactly the ticket you were given, e.g. \"ask-1a2b3c4d\"."
          }
        },
        "required": [
          "hypotheses"
        ],
        "additionalProperties": false
      }
    }
    propose_reportAction

    Propose a bug report and wait for the human to approve or edit it. Reproduction steps are validated against the recorded events, and any step no event supports is marked unverified rather than quietly dropped.

    View tool JSON
    {
      "name": "propose_report",
      "kind": "act",
      "impl": "imperative",
      "description": "Propose a bug report and wait for the human to approve or edit it. Reproduction steps are validated against the recorded events, and any step no event supports is marked unverified rather than quietly dropped.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "description": "One line naming the bug, e.g. \"Province dropdown is empty, so checkout cannot be completed\". At most 120 characters."
          },
          "summary": {
            "type": "string",
            "description": "A short paragraph on what happened and why it matters. At most 600 characters."
          },
          "steps": {
            "type": "array",
            "description": "The reproduction steps, in order, at most 12. Pass an empty array to have the page reconstruct them from the recorded events instead of writing them yourself.",
            "items": {
              "type": "object",
              "properties": {
                "text": {
                  "type": "string",
                  "description": "One action, e.g. \"Open the province dropdown\". At most 200 characters."
                },
                "atMs": {
                  "type": "number",
                  "description": "When the recording shows this step happening, in milliseconds from the start, e.g. 28412. Give it whenever you know it: a step with no timestamp cannot be matched against the recorded events and is shown to the human as unverified."
                }
              },
              "required": [
                "text"
              ],
              "additionalProperties": false
            }
          },
          "expected": {
            "type": "string",
            "description": "What should have happened, e.g. \"The dropdown lists the provinces for the selected country.\""
          },
          "actual": {
            "type": "string",
            "description": "What did happen, e.g. \"The dropdown opened with no options and the Pay button stayed disabled.\""
          },
          "rootCause": {
            "type": "string",
            "description": "Your explanation of the cause, in one or two sentences, based on what you found in the recording."
          },
          "evidence": {
            "type": "array",
            "description": "The moments that back the report, at most 10. Each becomes a clickable timestamp in the report.",
            "items": {
              "type": "object",
              "properties": {
                "atMs": {
                  "type": "number",
                  "description": "When it happened, in milliseconds from the start of the recording, e.g. 28412."
                },
                "note": {
                  "type": "string",
                  "description": "What that moment shows, in a few words, e.g. \"GET /provinces returned 500\"."
                }
              },
              "required": [
                "atMs"
              ],
              "additionalProperties": false
            }
          },
          "ticket": {
            "type": "string",
            "description": "Only when retrying a call that previously returned status \"pending\". Do not invent one — pass back exactly the ticket you were given, e.g. \"ask-1a2b3c4d\"."
          }
        },
        "required": [
          "title",
          "steps",
          "rootCause"
        ],
        "additionalProperties": false
      }
    }
    claim_next_taskAction

    Take the next task the human has put in the agent lane. This call blocks until a task exists, so you can use it to wait for work instead of polling.

    View tool JSON
    {
      "name": "claim_next_task",
      "kind": "act",
      "impl": "imperative",
      "description": "Take the next task the human has put in the agent lane. This call blocks until a task exists, so you can use it to wait for work instead of polling.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "ticket": {
            "type": "string",
            "description": "Only when retrying a call that previously returned status \"pending\". Do not invent one — pass back exactly the ticket you were given, e.g. \"ask-1a2b3c4d\"."
          }
        },
        "additionalProperties": false
      }
    }
    snapshot_findingAction

    Save the current findings, meaning markers, hypotheses and the report draft, so they survive a reload and can be copied out.

    View tool JSON
    {
      "name": "snapshot_finding",
      "kind": "act",
      "impl": "imperative",
      "description": "Save the current findings, meaning markers, hypotheses and the report draft, so they survive a reload and can be copied out.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "A short name for this snapshot, e.g. \"empty province dropdown\". Used to label it and to build its storage key; at most 60 characters. Omit it and the recording id plus the time is used."
          }
        },
        "additionalProperties": false
      }
    }