Skip to content

MCP Tool Reference

probe-mcp exposes 117 tools, all prefixed probe_…. They're grouped here by feature. For each tool: a one-line description, the input schema, and what comes back. Every result is wrapped in MCP's content: [{type: "text", text: "..."}] envelope; for tools that return structured data, the text is compact JSON from the underlying REST API.

Need wiring? See MCP Setup.

  • Required parameters are marked. Everything else is optional.
  • Type is the JSON Schema type accepted by the MCP inputSchema.
  • Returns describes what the AI sees in content[0].text after the call resolves.
  • IDs returned by probe_list_traffic are integers; collection / session / script-rule / flow / remote-fs connection IDs are strings.
  • max_body_chars — tools that return bodies accept this optional integer: probe_get_request, probe_export_curl, probe_export_raw, probe_replay_request, probe_diff_traffic, probe_get_session_tab, probe_get_saved_session, probe_get_collection, probe_send_compose_request, probe_send_request, probe_list_compose_history, probe_get_flow, probe_run_flow, probe_ws_get_frames, probe_export_collection and probe_execute_script. Every body or other long string in the result is cut after that many characters (default 8000) and ends with a …[truncated: showing N of M chars; …] marker; JSON results stay valid JSON. Pass 0 for no limit (a captured body can be up to 512 KB). It isn't repeated in the tables below. A write tool refuses any argument that still carries that marker (writing it back would save the marker instead of the real content): read the value again with max_body_chars: 0 before you send it.
  • Result size — a whole result is capped at 200,000 characters after the per-string cut. A JSON result over it keeps the head of its largest list (at any depth) and says how many items were left out (result_truncated in an object, a final …[N item(s) left out …] string in a list). A cut page stays complete: its next_cursor / next_offset is moved back to the first item left out (a tool that takes offset but returns no continuation gets a next_offset), so following it skips nothing and never moves back. The paged list is the page's own list (entries), even when a list beside it is bigger. An item kept only in part (a list inside it cut) counts as left out and comes again on the next page — except the page's first item: it is moved past and not sent again, and the note says it was cut short. The count names items of the paged list, and items cut from other or nested lists are counted apart. When not even one item fits, the continuation stays as it was and the note asks for smaller items. Several big lists are cut in turn, none emptied for the others. A result that is one JSON string is cut inside the string, so it stays valid JSON. Otherwise narrow the call with the tool's filters or limit; a tool that takes max_body_chars can also pass 0 (lifts both caps) — the note only names arguments the tool accepts.
  • Compact results — JSON results come without pretty-printing (no indentation or spaces), in a stable key order, to keep token cost down. Tool descriptions in tools/list are kept short; this page holds the longer explanations and examples.
  • Strict arguments — every call is checked against the tool's inputSchema before it runs: an argument the tool doesn't have (Unknown argument x … Valid arguments: …), a value of the wrong JSON type (Argument limit must be integer, got string.), a value outside an enum, minimum or maximum, a negative integer, or a missing required argument is an error (isError: true) that names the argument — also inside objects and lists, by path (Argument variables.token must be string, got integer., headers[0][1], minItems / maxItems). A nested object whose members the schema lists refuses any other member (Unknown member paths[0].dir. Valid members: …), so a misspelled flag is never dropped silently; the value itself is never echoed. null for an optional argument means "not given". Calling a tool that doesn't exist is a JSON-RPC -32602 error.
  • Annotations — every tool carries MCP readOnlyHint and openWorldHint, and every tool that changes something also carries destructiveHint. The rule: a write is destructive unless it only adds, or the opposite call with the same arguments undoes it. So deletes, clears (turning the throttle off included), updates that overwrite saved content (every update tool, the throttle setting and creating a breakpoint for a pattern that already has one), cancelling a Files transfer, installing the CA (it changes the computer's trust store and removes retired Probe CAs from it) and the Files rename/upload/download/delete tools are marked destructive, and so are probe_execute_script, probe_replay_request, probe_send_compose_request, probe_send_request, probe_run_flow and probe_resume_intercept, which can send a request of any method (DELETE included) to a real server. Those six and the Files tools that reach a remote server are marked open-world. Creates, adds, making a Files folder, blocking and unblocking a host or client, adding and removing an intercept host, and enable/disable switches are not marked destructive.
  • Where writes go — with the Probe app open, rule and setting writes (Map Local, Map Remote, Mock rules and server, script rules, breakpoints, ignored hosts and clients, the global throttle) are handed to the app, which applies them as if you had made them in its UI and shows them there. Rules, master switches, breakpoints and ignored lists are saved; the global throttle, the scripting switch and the Mock server's running state reset on restart, as they do in the app. The intercept-host allowlist is the exception: it lives in the proxy for the current run only.

List recent HTTP/HTTPS traffic, newest first. Use filters to narrow the result. CONNECT tunnel rows (status 0, no response) are left out unless include_connect is true.

ParameterTypeDescription
limitintegerMax entries (default 20, at most 500 — more is refused).
offsetintegerPagination offset.
hoststringCase-insensitive substring match against host.
methodstringHTTP method filter (e.g. GET).
statusintegerExact status code (overrides status_min / status_max).
status_minintegerMinimum status code.
status_maxintegerMaximum status code.
pathstringSubstring match against the whole URL.
include_connectbooleanAlso list CONNECT tunnel rows (default false).
has_errorbooleanOnly entries with status ≥ 400 (requests still waiting for a response have status 0 and aren't counted).
since_idintegerOnly entries with id > since_id (poll for new traffic).
sinceintegerOnly entries with timestamp >= since (ms since epoch).
fieldsstring[]Keep only these row keys, e.g. ["id","status_code"]. Any name outside the row keys below is refused with the valid list.
summarybooleantrue returns counts over every match instead of rows (see below). Can't be combined with fields, limit or offset.
test_run_idstringOnly the requests of this test run (see Test runs); the result gains a test_run object with the run's stats.
clientstringSubstring of the client label (client_addr: process or IP), case-insensitive.
headerstringRequest header Name:value (exact value) or Name (present).

Returns: {items, total, offset, limit} (plus capture_note when no new traffic is being captured — proxy stopped or capture off) where items[] are summaries — id, method, url, host, status_code, content_type, duration_ms, timestamp, client_addr, has_response. With fields, each row holds only the keys you named.

With summary: true: {total, counted, by_host, by_status, by_method} — counts per host (the 100 biggest; the rest are counted in other_hosts), per status code and per method, over every entry the filters match (the API copy holds up to 10,000). No rows are returned.

Paging 5,000 captured requests costs about 1.34 MB (~336k tokens) with full rows, about 0.15 MB (~37k tokens) with fields: ["id","status_code"], and a summary of all of them is about 110 bytes.

Full detail of a single entry, including headers and bodies. A 502 row with an x-probe-upstream-error header means Probe could not reach the server: that header and the error-text body are Probe's own diagnostics, not wire data (the client received an empty 502).

ParameterTypeRequiredDescription
idintegeryesTraffic entry id from probe_list_traffic.

Returns: the full entry — request and response headers as [[key, value], …], request and response bodies as strings (or null), timing, content type, client address. Bodies longer than max_body_chars are cut. A captured body over 512 KB is stored as [body too large: N bytes, not shown], a binary one as [binary N bytes], and a response the proxy streamed to the client without keeping it as [binary <type>, <size>, not captured] or [streaming <type>, not captured]: placeholders, not content, which the write tools refuse.

Search by URL path or body content.

ParameterTypeRequiredDescription
querystringyesSubstring matched against URL and body.
limitintegerDefault 20, at most 500 (more is refused).
offsetintegerPagination offset (see total).
fieldsstring[]Keep only these row keys (as in probe_list_traffic).
summarybooleanCounts by host / status / method over every match, no rows (as in probe_list_traffic).
test_run_idstringOnly this test run's requests (as in probe_list_traffic).
clientstringClient label substring (as in probe_list_traffic).
headerstringRequest header Name:value or Name (as in probe_list_traffic).

Returns: the same shape as probe_list_traffic.

Quick health snapshot — uptime, entry count, capture status, API port, version. No parameters.

Returns: {status, version, capturing, entry_count, uptime_secs, api_port}.

Clear captured traffic: the Probe app's own log list (the current recording tab, as its Clear button does) and the copy the REST API and these tools read. No parameters. Returns: a sentence saying whether the app's list was cleared too — without the app UI connected only the API's copy is cleared.

Delete one entry by id from the API traffic copy. The app's own log keeps it.

ParameterTypeRequired
idintegeryes

Returns: "Traffic entry <id> deleted.". An unknown id is an error.

Whether new traffic is stored in the API copy these tools read (see probe_toggle_capture) and how many entries that copy holds. app_connected says whether the Probe app UI is connected (then probe_toggle_capture and probe_clear_traffic act on the app too). No parameters.

Returns: {capturing, entry_count, proxy_running, app_connected}. With proxy_running false nothing new arrives even while capturing is true; the REST API and every non-traffic tool keep working.

Start or stop capturing — the same as the Start/Stop button in the Probe app: stopping stops the proxy (and turns off the system proxy if Probe turned it on), starting starts it again, and the app shows it. Without the app UI connected only the REST API's copy of the traffic is paused (traffic still flows, unrecorded).

ParameterTypeRequired
enabledbooleanyes

Returns: a sentence saying whether the change was made in the app or only in the API's copy.

Probe's root CA, so an agent can make a client trust it without reading these docs. Works without the app UI. No parameters.

Returns: {pem, sha256, spki_sha256, proxy, proxy_running, downloads, trusted, hints}. pem is the certificate the proxy signs with, read from memory (the same bytes the certificate server hands to devices). sha256 is the fingerprint as colon-separated hex; spki_sha256 is the base64 SHA-256 of its public key, the value Chromium's --ignore-certificate-errors-spki-list takes. downloads holds the certificate server's URLs (pem, der, android, ios, macos, on the proxy port minus one). trusted is the app's last trust check (true / false, with trusted_age_s: how many seconds ago it ran; the app re-checks when it comes to the front and after an install), or "unknown" without the app. hints gives one line per client: NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE and SSL_CERT_FILE for Python, CURL_CA_BUNDLE / --cacert, the Chromium flag, xcrun simctl keychain booted add-root-cert for the iOS simulator, http://10.0.2.2:<port>/download/android for the Android emulator, and probe_install_ca for this computer. While the proxy is stopped a note says the download URLs don't answer. Before the proxy has started once in this Probe process the call fails with caNotLoaded.

Install and trust the CA on this Mac or PC through the Probe app's own Install button, so every app on the computer trusts Probe. A person must approve the operating system's prompt on screen (the macOS administrator dialog, or the Windows security warning): the tool takes no password or other credential and never bypasses the prompt. Right before the prompt the app shows a notice that an AI agent asked for the install (approve only if you expected it). On macOS the install needs Probe's other windows (Scripting, Breakpoints and the like) closed, because the administrator prompt would freeze them: while one is open the tool closes nothing and shows no prompt, fails with 409 childWindowsOpen (listing the open windows), and the app asks the person to close them; call it again afterwards. Afterwards it shows its usual result dialog, with a line saying an AI agent asked. No parameters.

Returns: {outcome, trusted, reason?}. outcome is installed, already_trusted (the CA was trusted already: no prompt is shown), cancelled (the person dismissed the prompt) or failed with a reason (osError, trustNotVerified, unsupportedPlatform); the error text itself stays in the app's dialog. The call waits up to 120 s for the person; after that it fails with 504 timeout and the prompt may still be open, so read trusted with probe_get_ca instead of calling again. A second call while one is waiting fails with 409 installInProgress instead of showing a second prompt. Without the app UI it fails with 503 uiNotRunning, and with 503 windowNotOpen when the app runs without its main window: trust the CA per process with the hints of probe_get_ca.

Re-send a captured request, optionally edited, and return the response. By default the replay goes through Probe's proxy, so it is captured as a new traffic entry and Map Local, Map Remote, breakpoints and scripts apply to it (a matching breakpoint pauses it until you resume it: the call then fails after 60 s with an error naming the paused intercept id, and probe_resume_intercept releases it; through_proxy: false skips breakpoints). The captured accept-encoding header is dropped so the body comes back readable.

ParameterTypeRequiredDefaultDescription
idintegeryesTraffic entry to replay.
methodstringcapturedOverride the HTTP method.
urlstringcapturedOverride the full URL.
headersobjectHeader edits by name, case-insensitive: a string sets or replaces the header, null removes it. Other captured headers are kept.
bodystringcapturedReplace the request body.
through_proxybooleantruefalse sends straight to the origin, so nothing is captured.

Returns: {status_code, headers, body, body_truncated, duration_ms, new_id, through_proxy}. Probe reads at most 2 MiB of the response body: past that the body is cut, ends in a …[truncated: replay response cut at 2097152 bytes] marker and body_truncated is true (the whole response is in the captured entry new_id when the replay went through the proxy). new_id is the traffic id of the captured replay (compare it with probe_diff_traffic), or null when the replay went direct or wasn't captured. An unknown id or a failed send is an error with the reason.

If the captured request body is only a placeholder — binary ([binary N bytes]), too large to keep ([body too large: …]) or cut — the replay is refused (422) unless you pass body: re-sending it would send the placeholder text to the server instead of the real body. Pass the exact body to send (an empty string sends none).

Compare two captured entries — method, URL, status, headers, body. Useful for "did the replay match the original?" or "what changed between two similar calls?".

ParameterTypeRequiredDescription
left_idintegeryesThe "before" entry id.
right_idintegeryesThe "after" entry id.

Returns: a JSON diff — added/removed/changed fields per section.

Export an entry as a runnable cURL command.

ParameterTypeRequired
idintegeryes

Returns: the cURL string. The body is written with --data-raw, so a body starting with @ never makes cURL read a local file. An entry whose captured request body is only a placeholder (binary, too large, or cut) is refused (422): the command would send the placeholder instead of the body. Use probe_replay_request with an explicit body, or read it with probe_export_raw.

Export an entry as raw HTTP text — request line + headers + body, then the response if there is one.

ParameterTypeRequired
idintegeryes

Returns: the raw HTTP text.

Breakpoint rules pause matching traffic so the AI (or you) can edit it before it continues. Use probe_list_intercepts to see what's currently paused, then probe_resume_intercept to release it.

ParameterTypeRequiredDefaultDescription
patternstringyesAn exact host (api.example.com, no subdomains) or a host+path pattern (api.example.com/v1/*; scheme optional, * matches anything). A pattern that can never match (empty, a bare path like /v1/users, a host-only pattern with a port or wildcard, or no host) is refused with a hint.
intercept_requestbooleantruePause matching requests.
intercept_responsebooleantruePause matching responses.

Returns: "Breakpoint created for '<pattern>'.". The app saves it like a breakpoint added in its UI.

Remove one breakpoint by its exact pattern (breakpoints only, not Map Local/Remote, Mock or script rules). An unknown pattern is an error.

ParameterTypeRequired
patternstringyes

List every active breakpoint. No parameters. Returns: array of breakpoint definitions.

Remove all breakpoints in one call. No parameters.

List paused messages waiting for a decision. No parameters. Returns: an array of {id, phase, method, url, status_code}. phase is request (you can edit url, headers, body), response (you can edit status_code, headers, body) or unknown when the API's traffic capture is off; status_code is set for responses. A paused message resumes unchanged after 5 minutes.

Release a paused request or response, optionally edited.

ParameterTypeRequiredDescription
idintegeryesIntercepted message id.
abortbooleanIf true, answer the client with 502.
status_codeintegerOverride the response status (response phase).
headersarray of [key, value]Replace the whole header list. Omit it to keep the captured headers.
bodystringReplace the body of the paused request or response.
urlstringOverride the request URL (request phase).

Omitting headers keeps the captured ones, so a body-only or status-only edit doesn't strip Host, Authorization or cookies. If the API's traffic capture is off, the captured headers are unknown and such an edit is refused until you pass headers. An unknown id is an error. With the app open, its paused-request dialog for that message closes when you resume it here.

Returns: "Intercept <id> resumed." (or aborted).

These tools manage the SSL-proxying allowlist. They don't pause anything (use probe_create_breakpoint for that). While the allowlist is empty, every HTTPS host is decrypted. As soon as it has one entry, only the listed hosts and their subdomains are decrypted and logged with bodies; every other HTTPS host is tunnelled without decryption. With the app open, it saves the list (it survives a restart) and shows a banner above the traffic list naming the hosts while the list isn't empty; Decrypt All Hosts in that banner empties it. Without the app (headless), the list lives in the proxy for that run only.

ParameterTypeRequiredDescription
hoststringyesBare hostname or IP to decrypt, subdomains included (api.example.com). A scheme (https://…), port (:443), *. wildcard or path can never match, so it is refused with the bare host to send instead; nothing is stored.

Remove a host from the allowlist. When the list becomes empty, every HTTPS host is decrypted again. An unknown host is an error.

ParameterTypeRequired
hoststringyes

List the allowlist. Empty means every HTTPS host is decrypted. No parameters. Entries that can never match (saved before adds were checked) are named in a warning: remove them and add the bare hostname.

Traffic to a blocked host is tunnelled without decryption or logging. Use this to silence noise from analytics or telemetry hosts. Matching is case-insensitive, and the app saves the list in its Ignored hosts.

Ignore a host: its traffic is tunnelled without decryption or logging. Send a bare hostname or IP: it covers the host and every subdomain on every port (case-insensitive). A scheme, port, path or wildcard can never match and is refused with the host to send instead, like intercept hosts.

ParameterTypeRequired
hoststringyes

Remove a host from the list so it is decrypted and logged again. An unknown host is an error.

ParameterTypeRequired
hoststringyes

No parameters. Returns: array of host strings.

Block by client label — a process name (loopback) or IP address (remote phone). The client's traffic is forwarded without logging. Matching is case-insensitive, and the app saves the list in its ignored clients.

Ignore a client. A remote device (IP address) is matched at once: its HTTPS connections are tunnelled without decryption and its plain-HTTP requests are forwarded, neither logged. A local process is matched only once Probe has identified it (a background lookup, usually within seconds): until then its requests — plain HTTP and HTTPS alike — are decrypted and logged as usual; after that, its new HTTPS connections are tunnelled undecrypted and its further requests are forwarded without logging. Matching is case-insensitive, and an entry also covers the app's helper processes (Slack covers Slack Helper).

ParameterTypeRequiredDescription
labelstringyesProcess name or IP.

Remove a client from the list (case-insensitive) so its traffic is logged again. An unknown label is an error.

ParameterTypeRequired
labelstringyes

No parameters. Returns: array of label strings.

Probe has two throttle layers. The global throttle applies to every request; the scoped throttle rules apply per host/url-glob/app and run on top of it. The MCP can read both, and write the global one; the app's toolbar throttle icon then shows throttling is on (like a profile picked in the UI, it resets to Off on restart). Scoped rules are read-only here — Probe's UI owns them and pushes the list on every change.

Set the global download bandwidth cap for all proxied traffic. Upload, latency, DNS delay and packet loss keep their current values. Returns: "Throttle set to <n> bytes/sec.".

ParameterTypeRequiredDescription
bytes_per_secondintegeryesBandwidth in bytes/sec.

Turn off the whole global throttle: bandwidth both ways, latency, DNS delay and packet loss. Scoped throttle rules are not affected. No parameters.

Read the whole global throttle profile. No parameters.

Returns: {enabled, bytes_per_second, up_bytes_per_second, down_delay_ms, up_delay_ms, dns_delay_ms, down_loss_pct, up_loss_pct}. bytes_per_second is the download cap; enabled is true when any of the values is non-zero.

List the scoped throttle rules. Each rule has its own profile (down/up bps, DNS delay, loss percentage) and matches by host, url glob, or app. No parameters.

Returns: [{id, name, enabled, matcher: {type, pattern}, profile: {down_bps, up_bps, down_delay_ms, up_delay_ms, dns_delay_ms, down_loss_pct, up_loss_pct}}] in list order. With the app open this is every rule, disabled ones too; without it, only the enabled rules the proxy holds.

Add a scoped throttle rule. Matching requests get its profile instead of the global throttle; the first enabled rule that matches wins and the new rule goes last. The app saves the rule and shows it in its Throttle window, so the app must be open (503 uiNotRunning otherwise).

ParameterTypeRequired
namestringyes
matcherobject {type, pattern}yes — type is host (a bare host, api.example.com or *.example.com), urlGlob (the full URL with *, default ports ignored) or app (the client app name)
profileobjectno — down_bps, up_bps (bytes/s), down_delay_ms, up_delay_ms, dns_delay_ms, down_loss_pct, up_loss_pct (0–100); a missing key is 0, no limit
enabledbooleanno — default true

A pattern that can never match is refused with the form that would: a host pattern with a scheme, port or path (https://api.example.com:443/v1 → send api.example.com), a host wildcard anywhere but a leading *., whitespace, or a URL glob for a scheme other than http/https. Host patterns are stored lower-case.

Returns: the stored rule with its id.

Change a rule by rule_id. Only the fields you send change: inside matcher and profile each key is merged on its own, so {"profile": {"up_bps": 1000}} changes only the upload limit. The merged matcher passes the same checks as add. Needs the app open; an unknown id is a 404.

Returns: the stored rule.

Delete a rule by rule_id. Needs the app open; an unknown id is a 404.

Run JS through Probe's embedded QuickJS engine (probe_execute_script, the pro.* API), or manage the script rules that rewrite matching traffic. Script rules themselves run handleRequest / handleResponse in an external runtime — see Scripting.

Run a script once in Probe's embedded QuickJS engine (10 s timeout, 64 MB) and return what it did. Nothing is sent unless the script calls pro.sendRequest, which sends a real request (up to 5 per run); the script's variable and request changes are returned, not applied.

ParameterTypeRequiredDescription
scriptstringyesJS source.
phasestringpre-request or test (pro.response available). Default: test when response is given, else pre-request. Any other value is an error.
variablesobjectEnvironment variables (string values), readable with pro.environment.get and {{name}}.
globalsobjectGlobal variables, readable with pro.globals.get.
requestobjectOptional request context: method, url, headers (an object {name: value} or [[name, value], …]), body (a string, or any JSON value, sent as its JSON text).
responseobjectOptional response context: status (or its alias code), status_text, time_ms, headers (same shapes as above), body. In the script status is pro.response.code; pro.response.status is the reason text — status_text, or the standard one for status (200 → "OK").

Returns: {console_logs, test_results, variable_mutations, request_mutations, local_variables, script_failed, error, next_request, skip_request}, with the log, test and mutation fields decoded into JSON arrays and objects (console_logs is [{level, message}], test_results is [{name, passed, error?}]). A script that throws is a failed call (isError: true): the text starts Script failed: <message> and carries the full output (console logs and test results up to the throw) after Output:. A run that timed out shows empty lists / objects, not empty strings.

List every script rule and the global scripting on/off flag. No parameters.

Add a rule that auto-applies JS to matching traffic. A rule's scripts are not pro.* scripts like probe_execute_script: the app runs them in an external runtime (osascript -l JavaScript on macOS, otherwise node/bun/deno from your PATH), 5 seconds per run, with no pro global. request_script defines function handleRequest(request) { …; return request; } (request is {url, method, headers, body}); response_script defines function handleResponse(request, response) { …; return response; } (response is {statusCode, headers, body}). See Scripting.

ParameterTypeRequiredDefaultDescription
namestringyesHuman-readable name.
url_patternstringyesUnanchored match against the URL with and without its scheme: without * a plain substring, with * a wildcard that can match anywhere inside the URL (api.x.com also matches evil-api.x.com.io).
methodstring*HTTP method (case-insensitive), or * for all.
request_scriptstring""Runs on requests.
response_scriptstring""Runs on responses.
apply_to_requestbooleantrue
apply_to_responsebooleantrue

Rules are checked in list order and only the first matching rule runs; new rules are appended. The new rule's id is generated as mcp_<ms>_<n> and returned in the result. The app saves the rule and shows it in its Scripting list; it runs on matching traffic while the app is running and scripting is on.

Patch an existing rule — only fields you pass are changed.

ParameterTypeRequiredDescription
idstringyesRule id.
namestring
methodstring
url_patternstring
request_scriptstring
response_scriptstring
apply_to_requestboolean
apply_to_responseboolean
enabledboolean

An unknown id is an error.

ParameterTypeRequired
idstringyes

Master switch. When off, no script rules fire.

ParameterTypeRequired
enabledbooleanyes

Probe organises captured traffic into session tabs in the UI. The active recording tab fills with new traffic; other tabs are read-only snapshots. Saved sessions are the ones you saved, kept in the local database ~/.probe/probe.db. With the app open, these tools are answered by the app from its live tabs and saved sessions.

List every open tab. No parameters.

Returns: {tabs, active_tab_id, recording_tab_id}; each tab is {id, name, entry_count, is_recording, is_dirty, is_saved, created_at, hidden_by_domain_watch}. hidden_by_domain_watch counts requests the tab never captured because their root domain is not watched (Probe keeps only the first request per unwatched root).

Fetch one page of the entries inside a specific tab (oldest id first).

ParameterTypeRequired
idstringyes
offsetintegerno — entries to skip (default 0)
limitintegerno — entries to return (default 50, max 500)

Returns: {id, name, created_at, entry_count, offset, limit, entries} — entry_count is the total; each entry uses snake_case keys like the traffic tools (status_code, client_addr, req_headers, req_body, res_headers, res_body, content_type, duration_ms, req_size, res_size, request_end_ts, response_start_ts, http_version). The page also carries hidden_by_domain_watch; when it is above 0 it adds hidden_domains ([{domain, count}], top 20) and a hint: watch the domain in the app (right-click the domain, then Watch, or View, then Watched Domains). Requests already dropped are not recovered.

List your saved sessions, newest first. No parameters.

Returns: {sessions}; each is {id, name, entry_count, created_at}.

Fetch one page of the entries inside a saved session. Each entry's id is session-scoped (<session>#<n>), not a traffic id: the log-id counter restarts with the app, so the number it had at capture time (kept as original_log_id_not_live) now names a different request. probe_get_request, replay and diff refuse such an id with a hint.

ParameterTypeRequired
idstringyes
offsetintegerno — entries to skip (default 0)
limitintegerno — entries to return (default 50, max 500)

Returns: the same shape as probe_get_session_tab.

Save captured traffic as a saved session, the way Cmd+S does: it shows up under SAVED and survives a restart. Saves a live tab (the recording tab unless you pass tab_id); the tabs themselves are left as they are. Needs the app open (503 uiNotRunning otherwise).

ParameterTypeRequired
namestringyes
tab_idstringno — default the recording tab
hoststringno — keep only this host and its subdomains
idsinteger[]no — keep only these entry ids (at most 10,000)

Returns: {id, name, entry_count, created_at, source_tab_id}, plus missing_ids and missing_reason for ids the tab does not hold. The Domain Watch teaser keeps only the first request per unwatched root domain, so an id probe_list_traffic shows may never have reached the tab: when it dropped requests, the answer also carries hidden_by_domain_watch, hidden_domains and a hint. Nothing matching is an error, and nothing is saved. Read it back with probe_get_saved_session.

The Composer is Probe's request builder — Postman-like collections, environments, and inline auth. The compose tools are how the AI builds and fires off requests against your saved targets.

The write tools accept the shapes an AI tends to send and store them in the shape the app loads: headers, query_params/queryParams and cookies may be an object {name: value}, a list of {key, value, enabled} or a list of [name, value] pairs; request keys may be snake_case or camelCase; a non-string body is stored as its JSON text. Anything else is rejected with an error naming the field, instead of writing a collection the app would silently skip. A collection id may contain only letters, digits, ., _ and -, and may not start with . or contain ...

List every collection — name, request/folder/environment counts. No parameters.

Full collection content.

ParameterTypeRequired
idstringyes

Create a collection. Saves to disk and shows up immediately in Probe (its compose watcher reloads).

ParameterTypeRequiredDescription
namestringyesCollection name.
requestsarrayInline requests (each {name, method, url, headers, queryParams, body, contentType}; snake_case keys work too).
foldersarrayFolders with nested requests ({name, requests, folders}).
environmentsarrayEnvironments with {name, variables: [{key, defaultValue, currentValue?, enabled?, isSecret?, type?}]}. Any other member (e.g. a misspelled secret: true) is refused by path, so a secret is never stored as plain text by mistake.
default_headersobject or arrayHeaders applied to every request: {name: value} or [{key, value, enabled}].
default_authobjectDefault auth (e.g. {type: 'bearer', token: '...'}).

Returns: {id, name, requests, folders, environments} — the new collection's id and every created item as {id, name} (folders also carry their own requests / folders), so later calls can address them. The file path is not returned.

Import into Composer as a new collection, through the importers of the app's Import menu: the collection is saved and opened in the app, so the app must be open (503 uiNotRunning otherwise).

ParameterTypeRequired
formatpostman | curl | probeyes — a Postman Collection v2.1 JSON, one cURL command (a one-request collection), or a Probe-format export (probe_export_collection)
contentstringyes — the JSON text or the command, at most 1 MiB
namestringno — default the collection's own name, Imported for cURL

Postman variables of type secret are stored in the OS keychain, never in the collection file. An unreadable document, a collection with no requests or a cURL command without a URL is a 400.

Returns: {id, name, request_count}.

Add a single request to an existing collection.

ParameterTypeRequiredDefaultDescription
collection_idstringyes
namestringyes
methodstringGET
urlstringSupports {{variable}} syntax.
headersobject or array{name: value} or [{key, value, enabled}].
query_paramsobject or arraySame shapes as headers.
cookiesobject or arraySame shapes as headers.
bodyanyA string; any other JSON value is stored as its JSON text.
content_typestringapplication/json
graphql_variablesstringJSON object of GraphQL variables.
descriptionstring
timeout_secondsinteger30Send timeout, 1–110 (outside is refused, not clamped).
pre_request_scriptstringJS run before the request is sent.
test_scriptstringJS run after the response arrives.
folder_idstringSubfolder placement.
authobjectPer-request auth override; type is one of none (Postman's noauth is accepted and stored as none), bearer, basic, apikey (any other, such as apiKey, is refused with a hint).

Returns: the new request's {id, name}.

Add an environment with variables.

ParameterTypeRequiredDescription
collection_idstringyes
namestringyese.g. Development.
variablesarrayyes[{key, defaultValue, currentValue?, enabled?, isSecret?}].

Resolve variables and send a saved request through the real Composer send path — pre-request/test scripts run, auth and the cookie jar are applied, and a GraphQL content type is wrapped in the {query, variables} envelope. A pre-request script's pro.request edits change what is sent, and the scripts' variable writes are saved, as in the app. When the proxy is running, the request is routed through it and the resulting traffic also lands in the log. Requires the Probe app UI to be running — 503 uiNotRunning if it's not, 504 timeout if it never answers. The request's own timeout must be 1-110 s (outside is refused), and the whole send is stopped waiting for after 115 s with an error result.

ParameterTypeRequiredDescription
collection_idstringyes
request_idstringyes
environmentstringEnv name or id. Backs every variable variables doesn't set, and receives the scripts' environment writes (default: the active environment). An unknown one is an error listing the valid names; nothing is sent.
variablesobjectPer-call overrides: beat every environment and the globals, in the request and in the scripts' pro.variables reads (only a script's pro.variables.set locals rank higher).

Returns: {status_code, status_text, headers, body, duration_ms, resolved_url, error, test_results, console_logs}. resolved_url is the URL as sent. Secret variable values are masked (••••••••) in resolved_url, error, test error messages, console logs, and in the response headers and body when the server echoes one back — as typed, URL-encoded, or JSON-escaped (\", \\, \/, \uXXXX). Masking prevents accidental exposure only: the captured traffic of the send (probe_list_traffic, probe_get_request) is not masked — see What a connected AI can reach.

Send an ad-hoc HTTP request — no saved collection needed. Runs through the same Composer send path as probe_send_compose_request (auth, cookie jar, GraphQL envelope, proxy routing). Pass collection_id to borrow that collection's auth, cookie jar and variables; an unknown one is an error and nothing is sent. environment needs collection_id, and an unknown environment is an error listing the valid names (no fallback to the active one). Requires the Probe app UI to be running — 503 uiNotRunning if it's not, 504 timeout after 120 s.

ParameterTypeRequiredDefaultDescription
urlstringyesSupports {{variable}} syntax.
methodstringGET
headersarray or object[{key, value, enabled}] or a plain {key: value} object.
query_paramsarray or objectSame shapes as headers.
cookiesarray or objectSame shapes as headers.
bodystring
content_typestringe.g. application/json, application/graphql+json.
authobject{type: "bearer", token: "..."}; type is one of none (Postman's noauth is accepted and stored as none), bearer, basic, apikey, any other is refused.
timeout_secondsinteger301–110; a value outside that range is refused with an error naming the range, never clamped (the bridge gives the app 120 s).
graphql_variablesstringJSON object of GraphQL variables (only used with the GraphQL content type).
collection_idstringBorrow this collection's auth, cookie jar and variables.
environmentstringEnvironment name or id of collection_id to resolve variables against.
variablesobject{{variable}} overrides.

Returns: {status_code, status_text, headers, body, duration_ms, resolved_url, error, test_results, console_logs}.

Delete a collection by id (removes its JSON file). The app picks up the change automatically via its file watcher.

ParameterTypeRequired
idstringyes

404 if the id doesn't exist.

Partially update a saved request — only the fields you provide are changed. Searches the collection root and every nested folder.

ParameterTypeRequiredDescription
collection_idstringyes
request_idstringyes
namestring
methodstring
urlstring
headersobject or array{name: value} or [{key, value, enabled}]; replaces the whole list.
query_paramsobject or arraySame shapes; replaces the whole list.
cookiesobject or arraySame shapes; replaces the whole list.
bodyanyA string; any other JSON value is stored as its JSON text.
content_typestring
graphql_variablesstringJSON object of GraphQL variables.
descriptionstring
timeout_secondsinteger1–110 (outside is refused)
pre_request_scriptstring
test_scriptstring
authobject or nullnull clears the request's auth so it inherits from the collection; type is one of none (Postman's noauth is accepted and stored as none), bearer, basic, apikey.

404 if the collection or request doesn't exist. Error if no updatable field is provided.

Delete a saved request — searches the root and every nested folder.

ParameterTypeRequired
collection_idstringyes
request_idstringyes

404 if the collection or request doesn't exist.

Update an environment's name and/or variables. environment_id may be the environment's id or its name.

ParameterTypeRequiredDescription
collection_idstringyes
environment_idstringyesId or name.
namestring
variablesarrayFull replacement list: [{key, defaultValue, currentValue?, enabled?, isSecret?}].

Replacing variables replaces the whole array — but a variable whose stored value is the secret-keychain marker keeps that marker when the incoming value for it is blank, so you can't accidentally wipe out a secret by omitting its value. A new value for a secret variable is stored in the OS keychain by the running Probe app and the file keeps only the marker; if the app window isn't open the call fails with 409 secretNeedsApp and nothing changes. 404 if the collection or environment doesn't exist.

Delete an environment. environment_id may be the environment's id or its name. If it was the active environment, the collection falls back to the first remaining one.

ParameterTypeRequired
collection_idstringyes
environment_idstringyes

404 if the collection or environment doesn't exist.

Create a folder in a collection, optionally nested under an existing folder.

ParameterTypeRequiredDescription
collection_idstringyes
namestringyes
folder_idstringParent folder to nest under (omit for collection root).

Returns: the new folder's id. 404 if folder_id doesn't resolve to an existing folder.

List the app's Composer send history — every request/response sent from the Composer editor, newest first, including REST- and MCP-initiated sends recorded by the send bridge (probe_send_compose_request, probe_send_request); collection runs aren't recorded. Distinct from probe_list_collections/probe_get_collection, which are saved requests, not send history. Read-only.

ParameterTypeDescription
limitintegerMax entries, newest first (default 20, 1–200; a value outside that is refused).
offsetintegerEntries to skip from the newest (default 0).
include_bodiesbooleanInclude response bodies and request bodies, as composed (snapshot.body) and as sent (sentBody) (default false, which strips them along with responseBody and the editor snapshot's snapshot.resBody).

Returns: {entries, total, offset, next_offset} where total is the full count of entries in the history file (independent of limit) and next_offset is the offset of the next page, null on the last one. A response body over 256 KB is kept cut, with responseBodyTruncated: true and responseBodyOriginalBytes, so it is not the whole body.

Export a saved Composer collection (or a single request) to a shareable format. Pure data transform — works headless, no Probe UI required. Secret variable values are blanked in every format. Read-only.

ParameterTypeDescription
collection_idstringRequired. Collection ID (from probe_list_collections).
formatstringpostman (default), curl, probe, or postman-environment.
request_idstringExport only this request (postman and curl).
environmentstringEnvironment ID or name (postman-environment); defaults to the active environment.

Returns: the exported text. postman is Postman Collection v2.1 JSON including auth, cookies, and GraphQL bodies; curl is one command for a request, or a script with one command per request (with # folder/name comments) for the whole collection; probe is the Probe-native .pro envelope that Probe can import again; postman-environment is a Postman environment file with secret variables typed secret and left empty. 404 if the collection, request, or environment is not found.

Map Local rules serve a canned response from disk or an inline string instead of contacting the origin. See Map Local for the full feature doc.

List every rule and the master enable flag. No parameters.

Preview which rule (if any) would intercept a given request — no side effects.

ParameterTypeRequired
methodstringyes
urlstringyes

Returns: {matched: true, rule: {…}}, or {matched: false, reason, checks}; every answer also carries host_decrypted and, when it is false, a decryption_hint: the proxy never processes that request — an ignored host or one of Probe's built-in background hosts (certificate checks, OS telemetry) is forwarded untouched for every scheme, and an HTTPS host outside the intercept-host allowlist is tunnelled undecrypted — so a matching rule never fires. reason says why nothing matched (for example the master switch is off, or no rules exist) and checks gives each rule's reason — disabled, the method filter, or the pattern and the form of the URL it was compared with.

A url_pattern without a scheme (api.example.com/users/*) matches host + path over HTTP and HTTPS; with a scheme (https://api.example.com/users/*) it matches only that scheme. The default port (:443/:80) is ignored in both the request URL and your pattern, so you can paste a captured URL as is, and so is the query string unless the pattern contains ?.

Read one Map Local rule by id, in full (bodies are never cut to fit a list). Read-only. 404 if the id does not exist.

ParameterTypeRequired
idstringyes

Create a new rule. id is optional — generated as mcp_<ms>_<n> if you don't supply one.

ParameterTypeRequiredDescription
url_patternstringyesWildcard *, e.g. api.example.com/users/*.
sourcestringyeslocal_file or inline_body.
idstring
methodstringDefault GET.
match_all_methodsboolean
enabledboolean
file_pathstringAbsolute path. Required when source is local_file. Must be an existing regular file of at most 50 MiB inside ~/Downloads, ~/.probe/transfers or ~/.probe/map-local — any other path (or a symlink, .., a directory, a device file) is refused on create and on update, whatever the source, with an error naming the allowed folders. Put the body file in ~/.probe/map-local first. Rules you create in the app may point anywhere, but editing one over MCP needs its file in one of those folders. The file is served to every client whose request matches — including phones on your network.
inline_bodystringRequired when source is inline_body.
inline_formatstringjson, xml, html, or text.
status_codeintegerDefault 200.
response_headersobjectString → string map. probe-mcp reads tool arguments as JSON objects, which it keeps sorted by key, so the headers are saved in name order. To keep your own order, edit the rule in the app or use the REST API.
delay_msintegerArtificial latency.

Patch by id: only the fields you send change, every omitted field keeps its stored value (a disabled rule stays disabled, a body stays put). id is the only required field. The merged rule passes the same checks as create; an unknown id is 404. Read the rule first with probe_get_map_local_rule. Rules are checked in list order, the first match wins, and new rules are appended (there is no reorder tool).

ParameterTypeRequired
idstringyes

404 if id doesn't exist.

Flip a single rule's enabled flag.

ParameterTypeRequired
idstringyes

Master switch. When off, no rule matches.

ParameterTypeRequired
enabledbooleanyes

Map Remote rules redirect matching requests to a different upstream — same shape as Map Local rules but the destination is another origin instead of a canned body. See Map Remote.

As with Map Local, a rule written here is saved by the app and shown in its Map Remote window, and rules only fire while the master switch (off by default) is on.

List every rule and the master enable flag. No parameters.

Preview which rule would redirect a given request, plus the rewritten URL.

ParameterTypeRequired
methodstringyes
urlstringyes

Returns: the matching rule and the resolved destination URL, or {matched: false, reason, checks} (why nothing matched, and each rule's reason). Like the Map Local check, every answer carries host_decrypted and, when false, a decryption_hint.

Read one Map Remote rule by id, in full. Read-only. 404 if the id does not exist.

ParameterTypeRequired
idstringyes
ParameterTypeRequiredDescription
source_schemestringyeshttp or https.
source_hoststringyesHost only, matched exactly (case-insensitive; no wildcards). A wildcard host such as *.example.com is refused: add one rule per host.
source_pathstringyesStarts with /. Wildcards * are captured in order.
dest_schemestringyeshttp or https.
dest_hoststringyesHost only: the port goes in dest_port, the scheme in dest_scheme. localhost:3000, http://x or x.com/v2 is refused with a hint naming the right field.
dest_pathstringyesStarts with / (or *). Each * is filled with captures from source_path.
idstringGenerated as mcp_mr_<ms>_<n> if omitted.
source_portintegerDefaults to 443/80 by scheme.
dest_portinteger
methodstringDefault GET.
match_all_methodsboolean
enabledboolean
preserve_original_host_headerbooleanNot supported yet: true is refused on create and update (the forwarded request always carries Host: dest_host). Omit it or send false.

Patch by id: only the fields you send change, omitted fields keep their stored value; id is the only required field and an unknown id is 404. The merged rule is checked like a create: an empty source_host/dest_host, a scheme other than http/https, a host carrying a scheme, port or path, a wildcard source_host, or a path without a leading / is rejected.

ParameterTypeRequired
idstringyes
ParameterTypeRequired
idstringyes
ParameterTypeRequired
enabledbooleanyes

The Mock server is a standalone HTTP listener (default port 9100) that serves canned responses to clients that hit it directly — unlike Map Local, there's no proxying involved. See Mock Server for the full feature doc.

List every Mock rule and the master enable flag. No parameters.

Get the Mock server listener status: whether it is running and on which port. No parameters.

Check whether a given method+path would be served by any active Mock rule — no side effects.

ParameterTypeRequired
methodstringyes
pathstringyes

Returns: {matched: true, rule: {…}} or {matched: false, reason, checks} (why nothing matched — such as the master switch being off — and each rule's reason). The mock server is called directly, not through the proxy, so this check carries no host_decrypted.

Read one Mock rule by id, in full. Read-only. 404 if the id does not exist.

ParameterTypeRequired
idstringyes

Create a new rule. id is optional — generated as mcp_<ms>_<n> if you don't supply one.

ParameterTypeRequiredDescription
url_patternstringyesPath with * wildcards, e.g. /api/users/*.
idstring
namestring
methodstringEmpty matches any method.
enabledboolean
status_codeintegerDefault 200.
status_textstringOptional custom reason phrase.
headersarray of [name, value]
bodystring
body_is_base64booleanDecode body from base64 for binary responses.
latency_msintegerArtificial delay before responding.

Patch by id: only the fields you send change, omitted fields keep their stored value; id is the only required field and an unknown id is 404. The merged rule passes the same checks as create, including body_is_base64: true with a body that is not valid base64, which is refused at write time. New rules are appended and the first matching rule wins. While the Mock master switch is off, the server's 404 hint names probe_set_mock_enabled.

ParameterTypeRequired
idstringyes

404 if id doesn't exist.

Flip a single rule's enabled flag.

ParameterTypeRequired
idstringyes

Master switch. When off, no rule matches and the server answers 404 for everything.

ParameterTypeRequired
enabledbooleanyes

Start the standalone listener.

ParameterTypeRequiredDefaultDescription
portinteger9100Port to listen on (1–65535).

Errors if the port is 0, already in use, or the server is already running.

Stop the standalone listener. No parameters.

API Flows are Probe's node-based request chains — build once in the app (like Postman Flows), then drive them from the AI. Flows are Dart-owned: the two list/get tools read what the app has persisted; the create, update, graph and delete tools and probe_run_flow ask the running app to save or execute the chain. See Flows in CI for the headless-automation angle these tools plug into.

List every saved flow — id, name, node count, edge count, created/updated timestamps. No parameters.

Returns: each flow's id, name, nodeCount, edgeCount, createdAt, updatedAt. Use the id with probe_get_flow / probe_run_flow.

Full flow definition: nodes (start, request, condition, loop, delay, subflow and script, with extractions and assertions), edges, and canvas viewport.

ParameterTypeRequiredDescription
flow_idstringyesId from probe_list_flows.

Returns: the stored flow JSON, with revision (send it back to probe_set_flow_graph), plus a derived subflowIds array — the deduped target ids of every subflow node, so a client can discover a flow's sub-flow dependencies without walking node payloads itself.

Create a flow. Send only name to get an empty flow with one start node (like New Flow in the app), or a whole graph in probe_get_flow's shape.

ParameterTypeRequiredDescription
namestringyesUp to 200 characters.
nodesarrayEvery node: id, name, type (start, request, condition, loop, delay, subflow, script), position, and the type's config.
edgesarray{id, fromNodeId, toNodeId, fromPort?}.
viewportobjectCanvas pan and zoom.

The app checks the graph before saving anything and refuses it (400, with the reason) when there is no start node or more than one, an edge points at a node that doesn't exist, a node type is unknown, two nodes share an id or a name, the graph has a cycle, a condition port drives two edges, a sub-flow points at an unknown flow or would run itself, or the flow has more than 500 nodes or 2,000 edges. Nodes at negative positions move into view together (the layout is kept). Literal secrets in a request (an Authorization header, an API-key parameter) become {{variables}} and are listed in requiredInputs: pass their values in probe_run_flow's environment.

Returns: the stored flow, with its revision. Needs the Probe app (503 uiNotRunning otherwise).

Rename a flow.

ParameterTypeRequired
flow_idstringyes
namestringyes

Replace a flow's whole graph: read it with probe_get_flow, change the JSON, send nodes, edges (and viewport) back with the revision it carried.

ParameterTypeRequiredDescription
flow_idstringyes
revisionintegeryesrevision from probe_get_flow.
nodesarrayyesEvery node (nodes left out are removed).
edgesarrayEvery edge.
viewportobject

If the flow changed since your read (someone edited it on the canvas, or another agent wrote it), the answer is 409 and nothing changes: read it again and reapply your change. The checks are the same as probe_create_flow's; a request sent back unchanged keeps what is stored. An open canvas shows the new graph at once.

Delete a flow and its file. This cannot be undone.

ParameterTypeRequired
flow_idstringyes

Fetch the report of a flow run that outlived probe_run_flow's 120 s wait, by the run_id that the 504 answer carried. Read-only.

ParameterTypeRequired
run_idstringyes

Returns: {status: "running"} while the app is still running it, then {status: "finished", report}. 404 for an unknown id: only the last 32 timed-out runs are kept, and a run that finished in time returned its report directly.

Execute a flow end-to-end and return its run report.

ParameterTypeRequiredDescription
flow_idstringyesId from probe_list_flows.
environmentobjectVariable overrides for this run (name → value), forwarded to the executor. It is not an environment name: anything other than an object (such as a name string) is rejected.

Returns: per-node results (status, extracted values, assertion outcomes, and the request's resolved URL: the URL as sent, with its enabled Params rows), overall passed/failed/skipped counts, and total duration. Secret variable values (and environment overrides of secret variables) are masked (••••••••) everywhere in the report.

The Probe app must be running — this bridges to the UI to fire the flow's real HTTP requests. Fails with 503 if the app isn't running, 504 if it doesn't answer in time (runs can take up to two minutes); the 504 carries a run_id and status: "running": the run may still be going and still sending its requests, so fetch its report with probe_get_flow_run instead of running it again. A report with an error (unknown flow, already running, or the run threw) is returned as a tool error, and so is a run that ran but ended failed (a node errored or an assertion failed): the error names the failing nodes and the full report follows.

Thin wrappers over the Files feature's remote-fs REST routes — the same SFTP/FTP/FTPS/S3/WebDAV engine backing the app's Files browser. There is no connect tool: credentials stay entirely in the Probe UI path. Open a connection in the app first, then drive it from here by connection_id (see probe_list_remote_fs_connections). These calls don't move the folder you're looking at in the app's Files browser.

List every open Files connection. No parameters.

Returns: each connection's id, protocol, host, server label, home path, and whether the transport is encrypted.

List a directory's entries on an open connection, one page at a time.

ParameterTypeRequiredDescription
connection_idstringyesId from probe_list_remote_fs_connections.
pathstringyesAbsolute remote directory path, e.g. /var/www.
cursorstringnext_cursor from the previous page; omit it for the first page.
limitintegerEntries per page, 1–5000 (default 500).

Returns: {path, entries, total, next_cursor}: each entry's name, path, isDir, size, and (when the backend reports them) modifiedMs, permissions, owner, symlinkTarget; total entries in the directory; next_cursor to pass back for the next page, null on the last one. The cursor is a position in the listing, so a directory that changes between calls can skip or repeat an entry.

Create a directory.

ParameterTypeRequiredDescription
connection_idstringyes
pathstringyesAbsolute path of the directory to create.

Rename or move an entry.

ParameterTypeRequiredDescription
connection_idstringyes
fromstringyesCurrent absolute path.
tostringyesNew absolute path.

Delete one or more entries in a single call.

ParameterTypeRequiredDescription
connection_idstringyes
pathsarray of {path, is_dir}yesEntries to delete. is_dir (or isDir, the shape probe_remote_fs_list_dir returns) defaults to false — needed by backends like S3 that distinguish an object from a prefix. A probe_remote_fs_list_dir entry can be passed as-is (name, size, modifiedMs, permissions, owner, symlinkTarget are ignored); any other member is refused. On S3 a file delete of a key that doesn't exist fails notFound; when the path is a folder sent without is_dir, the message adds "is it a directory? pass isDir: true". Under a delete-only IAM policy (HeadObject forbidden) the file is still deleted and listed in deleted, but also in unconfirmed with a note: its existence couldn't be checked.

Directory deletes are recursive on S3 (every object under the prefix) and WebDAV (the server deletes the whole collection), but not on SFTP and FTP/FTPS (a non-empty directory fails). Every path is attempted even if an earlier one fails.

Returns: {ok, deleted}. When some paths could not be deleted (REST answers 207, ok is false) the tool reports an error that still lists deleted and failed: [{path, error: {code, message}}].

Enqueue a download from the connection to a local file.

ParameterTypeRequiredDescription
connection_idstringyes
remote_pathstringyesAbsolute remote source path.
local_pathstringyesAbsolute local destination path inside ~/Downloads or Probe's transfers folder ~/.probe/transfers (Windows: %USERPROFILE%\Downloads, %USERPROFILE%\.probe\transfers). A relative path, a .., a symbolic link, a directory, a missing folder or a path outside those folders is refused.
overwritebooleanReplace local_path if it already exists. Default false: an existing file is refused.

This writes a file on your own machine, so only use a path you asked for. Returns: {transferId} immediately — poll probe_remote_fs_transfers to watch progress and completion, or stop it with probe_remote_fs_cancel_transfer. The transfer also shows in the app's transfer queue, where the user can pause or cancel it.

Enqueue an upload of a local file to the connection.

ParameterTypeRequiredDescription
connection_idstringyes
local_pathstringyesAbsolute path of an existing regular file inside ~/Downloads or Probe's transfers folder ~/.probe/transfers (Windows: %USERPROFILE%\Downloads, %USERPROFILE%\.probe\transfers). Symbolic links are refused.
remote_pathstringyesAbsolute remote destination path.

This reads a file on your own machine and sends it to the server, so only upload a file you asked for. Returns: {transferId}, same as probe_remote_fs_download.

List the Files transfer queue. No parameters.

Returns: every download/upload the engine is tracking — the active ones plus the 100 most recently finished — with transferId, connectionId, kind, state (queued/running/paused/done/cancelled/error), remote/local paths, byte offset, and, in the error state, error: {code, message} saying why it failed. Cancelled transfers stay listed with state cancelled.

Cancel a queued, running or paused transfer, whether it was started from the app or through MCP. It stays in probe_remote_fs_transfers with state cancelled; a partly written destination file is left in place.

ParameterTypeRequiredDescription
transfer_idstringyestransferId from probe_remote_fs_download/upload or probe_remote_fs_transfers.

Returns: {ok: true, transferId}. An unknown id or a transfer that already finished is an error.

Group the requests one test produces, then read exactly those. Flow: begin, drive the app (when several shards share one Probe, have each send a header such as X-Probe-Test: shard-1 and pass header_value), list or search traffic with test_run_id, end. A run lives in Probe's memory only: it never touches your saved rules or any file, and it is gone when Probe restarts (a stale id answers runFromPreviousInstance). A run you forget ends itself after ttl_secs without use.

Start a run. Requests that start from now on belong to it.

ParameterTypeDescription
namestringA label.
clientstringOnly clients whose label (process name or IP) contains this.
hoststringOnly this host and its subdomains.
header_valuestringOnly requests whose header_name header has exactly this value.
header_namestringDefault X-Probe-Test; without header_value, the header only has to be present.
expect_hostsstring[]Hosts you expect traffic for; each gets a host_checks verdict.
ttl_secsintegerLease, default 900, at most 86400; every use renews it.

Returns: the run: id, state: "running", start_id, scope, ttl_secs, expires_at, and host_checks (host_decrypted: false plus a decryption_hint when Probe cannot see that host's requests).

Wait for traffic instead of polling. Returns as soon as min_count matching responses exist (or requests, with phase: "request").

ParameterTypeDescription
test_run_idstringOnly this run's requests; the run's existing traffic counts.
hoststringHost substring.
methodstringHTTP method.
pathstringURL substring.
phasestringresponse (default) or request.
cursorintegercursor of a previous answer: only later events count. Without a run, the default is "only traffic from now on".
min_countintegerRows needed, default 1, at most 100.
timeout_msintegerDefault 25000, at most 55000.

Returns: {matched: true, items, cursor} (at most 100 items, oldest event first), or after the timeout {matched: false, cursor, waited_ms} (not an error: call again with the same cursor), with capture_note / decryption_hint when they explain the silence. When capture is off or the proxy is stopped and nothing matching is there, it is an error (captureOff). With test_run_id the wait keeps the run's lease alive, never returns requests started after the run ended, and is an error (runExpired) if the lease ran out meanwhile.

Add a rule that applies to one test run only. It lives in Probe's memory with the run: it never appears in your saved Map Local / Map Remote / Mock rules, is never saved, and is removed when the run ends. It is tried before your own rules, for requests inside the run's scope. The run must have a scope (begin it with header_value, recommended, or client / host); a run without one refuses test rules with runScopeRequired, since its rules would rewrite every client's traffic.

ParameterTypeRequiredDescription
test_run_idstringyesA running run.
kindstringyesmap_local, map_remote or mock.
ruleobjectyesThe fields of probe_create_map_local_rule / probe_create_map_remote_rule / probe_create_mock_rule (same checks).

Returns: {kind, rule}; for mock also mock_base_url, the run's own listener (127.0.0.1, free port; separate from the Mock server): point the app under test at it. A response served or rewritten by the rule carries X-Probe-Test-Run and X-Probe-Rule headers.

Remove a run's test rules (the run stays), or with all: true and no test_run_id end and delete every run and test rule. It never touches your saved rules.

ParameterTypeDescription
test_run_idstringThe run whose rules go. Wins over all.
allbooleantrue: remove every run.
ParameterTypeRequiredDescription
test_run_idstringyesid from probe_begin_test_run.

Returns: the run with stats: count, pending_responses, evicted (a request of the run may have been dropped by the 10,000-row limit or deleted), cleared, first_available_id, capture_paused (capture is off now) and capture_interrupted (capture stopped at some point since the run began).

End a run: later requests no longer belong to it.

ParameterTypeRequiredDescription
test_run_idstringyesThe run to end.
settle_msintegerWait this long for requests already sent to land (default 300, at most 5000).

Returns: the ended run with stats. Its traffic stays listable for an hour.

Verify what a test sent, or hand the capture to other tools. Both accept the run from Test runs.

Check captured traffic against assertions. The call fails (an MCP error) when any assertion fails; the error lists only the failed results with their reason. See POST /api/v1/traffic/assert in the REST API for the targets, operators and result fields.

ParameterTypeRequiredDescription
assertionsobject[]yesEach {target, op, value?, quantifier?}; quantifier is all (default), any or none. For order, value is [{method?, path?, host?}].
test_run_idstringOnly this run's requests.
filterobjectThe traffic filters: host, method, path, status, search, client, header...

Returns: {passed: true, matched_count, results}. A bad target, operator or quantifier is an error naming the valid choices.

Export traffic as HAR 1.2.

ParameterTypeDescription
test_run_id, host, method, path, statusFilters, as in probe_list_traffic.
max_body_bytesintegerCut each body to this many bytes. Inline the default is 4096; with output_path it is the server default (262144).
output_pathstringWrite the HAR to this file instead of returning it. Absolute path only; the directory must exist; an existing file is refused unless overwrite is true; a path inside Probe's data directory (~/.probe or PROBE_DATA_DIR) is refused. The file is written by the probe-mcp process.
overwritebooleanReplace an existing output_path. Default false.

Returns: the HAR document, or {path, bytes, entries} when output_path is given. See GET /api/v1/traffic/export/har for _probe details (bodies Probe did not keep, cut bodies, pending requests).

Inspect WebSocket frames Probe captured through the proxy. These are read-only snapshots of a bounded in-memory store — the proxy observes frames MITM'd through it; it does not open sockets. See WebSocket for the UI side.

List captured WebSocket connections. No parameters.

Returns: each connection's conn_id, log_id (the traffic entry of the upgrade handshake, or null if uncorrelated), url, opened_ts, closed_ts, closed, and frame_count. Use conn_id with probe_ws_get_frames.

Get the captured frames for one connection.

ParameterTypeRequiredDescription
conn_idintegeryesconn_id from probe_ws_list_connections.
limitintegerReturn only the newest N frames (default 100; the store keeps up to 500 per connection).

Returns: {conn_id, url, items, total, returned}. items holds the newest limit frames, oldest first; total is how many frames are stored and returned how many are in items. Each frame has dir (in = server → client, out = client → server), opcode (text/binary/ping/pong/close), text (text payload, or base64 for binary — capped at 64 KB), size_bytes (true payload size), truncated (true when text holds only the first 64 KB of the payload, so it is not the whole message) and ts.

Every tool returns the same MCP envelope on failure:

{
"content": [{"type": "text", "text": "Error: <reason>"}],
"isError": true
}

Common reasons:

  • Missing required parameter: <name> — schema violation.
  • Probe is not running (no valid …/api-handshake.json). Start Probe, then retry. — there's no handshake file, or the process it names has exited. Start Probe and call the tool again; you don't need to restart your AI client.
  • <METHOD> <path>: cannot reach Probe (…). Is the Probe app running? — Probe was reachable earlier but isn't now, and the handshake hasn't changed (for example, the app quit or crashed). The API does not depend on the proxy: it answers whenever the app is open.
  • <METHOD> <path> timed out after 150s — the app didn't answer a call within the 150-second limit.
  • HTTP-status messages from the underlying REST API (400, 404, 500, 503, 504, …), with the API's error message. A bare 404 adds which list tool (such as probe_list_traffic) gives the valid ids. Error texts are cut after 4,000 characters, so an echoed request body can't flood the conversation.
  • A call the server answered but that didn't do its job is an error too: a Composer send with no response or an empty 502 from Probe's own proxy (the host could not be reached; probe_send_compose_request, probe_send_request), a flow-run report with an error or a run that ended failed (the error names the failing nodes), and a partial Files delete. The error text carries the result data. A 401 or a refused connection makes probe-mcp re-read the handshake and retry once before it reports anything. The send tools and probe_run_flow answer 503/504 when the app UI isn't there to run them; a rule or setting write answers 504 if the app didn't confirm it in time (the proxy applied it, but it may not be saved). After a send's 504 the request may still have been sent: check probe_list_compose_history before sending it again.
  • MCP Setup — wire probe-mcp into your client.
  • Internal REST API — what each tool calls under the hood.
  • Scripting — what pro.* exposes inside probe_execute_script.
  • Flows in CI — running probe_run_flow unattended, outside an interactive AI session.