Skip to content
Draft, pre-release documentation

Kasm Session Runtime serves 28 methods over the Model Context Protocol (MCP) when it runs as kasm-session-runtime mcp. An outside agent, such as Claude Code, Codex or any other MCP client, uses them to drive the desktop directly. In this mode there is no scenario and the runtime reaches no verdict; the loop is your agent’s. The same methods are available without MCP as plain HTTP calls at POST /v1/call/<method> (see HTTP API).

Most of these methods need no model in the runtime. Three of them, screen.judge, screen.ground and screen.ask, call the vision model configured in KASM_RUNTIME_VISION_ENDPOINT. With no endpoint set, they return a resolution error and every other method works as normal. The “Model” column in each table below says which is which.

Every call is recorded as one row in report.json’s steps[] with "backend": "rpc", the same record a scripted run leaves.

stdio Streamable HTTP
When the default KASM_RUNTIME_SESSION_TOKEN is set
Wire JSON-RPC 2.0, one message per line MCP Streamable HTTP at /mcp, port KASM_SESSION_RUNTIME_PORT (default 9434)
Protocol version initialize must send 2024-11-05 chosen per request by the Mcp-Protocol-Version header; 2026-07-28 and later are stateless, earlier versions use initialize and Mcp-Session-Id
Credential none; the pipe is the trust boundary the session token in the X-Kasm-Runtime-Session-Token header, 401 if missing or wrong
Typical caller a client that starts kasm-session-runtime mcp as a subprocess inside the session a client outside the container, reaching the session through Kasm’s reverse proxy

No method has a credential of its own; the transport’s credential covers every method.

The token goes in a custom header because Kasm’s port-map proxy overwrites Authorization before forwarding. Through Kasm, the address is https://<host>/desktop/<kasm_id>/mcp/mcp, where the first mcp is the port-map service name and the second is the runtime’s own path.

Calls are handled one at a time on both transports, and on HTTP a second call waits for the first to finish. The session has one display and one browser tab.

tools/list returns the 28 methods in sorted order, each with its JSON Schema. The server stays off unless the session is started with KASM_RUNTIME_SESSION_TOKEN; see Enable the runtime on a workspace.

A method that fails returns a normal tools/call result with isError: true. The message starts with the method name and says what went wrong. Failures fall into three classes.

Class Meaning What to do
Malformed bad or missing parameters; the call never touched the session fix the parameters and retry
Resolution a capability is unavailable, something was not found, or a wait timed out retry, try something else, or give up
Environment the runtime itself is broken: X11 unreachable, a command that cannot start, the model erroring not retryable

Over /v1/call/, the class decides the HTTP status: malformed is 400, resolution 422, environment 500.

JSON-RPC protocol errors are used for unparsable input, unknown methods and shutdown. A call that arrives after the server has begun shutting down, or is still waiting its turn then, is refused with a JSON-RPC error whose message names “server shutting down” (code -32001 on stdio); that call never ran and records no step. A call already running when shutdown begins finishes and is recorded.

Method Parameters Returns Model
screen.capture none {Width, Height, JPEGBase64}, at the size actually captured No
screen.judge expectation (string, required) {Holds, Description} Yes
screen.ground description (string, required) {Found, X, Y, Description} Yes
screen.ask question (string, required) a plain-text answer Yes
windows.list none the top-level X11 windows No
  • screen.capture, screen.ground, screen.ask and windows.list need the X11 display; without it they return a resolution error (“no X connection”).
  • The three model methods return a resolution error when no endpoint is configured, or when the runtime has stopped calling the endpoint after repeated failures. An error from the model during the call is an environment error, except in screen.ground, where a location the model cannot give is a resolution error.
  • screen.ground returns a location and does not click; pass its X and Y to input.click. They are screen pixels whatever answer convention the model uses. When the element is not visible it returns Found: false with no point.
  • screen.ask sends the screenshot and, when available, text from the attached browser tab (title, URL, visible text, interactive elements) and the accessibility tree’s actionable elements.
  • The grounding accuracy the project has measured is for the Qwen3-VL family. For any other model, screen.ground’s accuracy is unmeasured. See Vision models.
Method Parameters Returns Model
input.key chord (string, required), e.g. "ctrl+s" "pressed <chord>" No
input.click x, y (integers, required): root-window pixels "clicked (x, y)" No
input.type exactly one of ref or text "typed N character(s)" No
input.cursor none {cursor} No
input.wait_for exactly one of text, key or click; optional since, timeout_ms, origin {matched, captured, events, cursor, dropped} No
  • input.key, input.click and input.type need the X display (resolution error without it). An input the X server does not accept is a resolution error. A non-integer or out-of-range coordinate is malformed.
  • input.type records only the length of what it typed. See ref or literal.
  • input.cursor and input.wait_for need KASM_RUNTIME_OBSERVE_INPUT=true. Without it they return a resolution error saying so.
  • input.wait_for waits for input the session receives from any source, the Kasm viewer included. click is {button, x, y, radius}: button defaults to 1, radius to 0, and leaving out both x and y matches anywhere. timeout_ms defaults to 5000, at most 60000. origin is external (the default: not the runtime’s own input), runtime (clicks only) or any.
  • On timeout input.wait_for returns matched: false with what was captured, without an error. Take input.cursor before acting and pass it as since, so input that arrives before the wait begins still matches.
Method Parameters Returns Model
proc.list match: substring of /proc/*/cmdline, "" for all the matching processes No
proc.spawn cmd (string, required) "spawned" No
proc.kill match (required): a case-sensitive POSIX extended regular expression "sent" No
launch cmd (required), env (object of strings, optional) "launched" No
shell cmd (string, required) combined stdout and stderr No
wait_while kind (growing or process) and timeout_s (required); path or match; poll_s (default 0.5); quiet_polls (default 3) "quiesced" No
  • Processes start detached, as the session user. launch also records the process as the session’s launched app.
  • A command that cannot start is an environment error. A proc.kill pattern that does not compile is malformed.
  • shell runs to completion. A nonzero exit is a resolution error; sh failing to start is an environment error.
  • wait_while holds until a growing file or directory stops changing size, or no process matches, for quiet_polls samples in a row. If timeout_s passes first, it is a resolution error. growing needs path; process needs match.

These methods act on a browser tab over the Chrome DevTools Protocol (CDP). None needs a model.

Method Parameters Returns
dom.query selector (CSS, required) the element’s description and attributes
dom.click selector (CSS, required) the element’s description
dom.fill selector (required); exactly one of ref or value "filled <selector> (N character(s))"
dom.wait predicate (JavaScript boolean, required), timeout_s "holds"
dom.url none the tab’s location.href
dom.navigate url (required), timeout_s "navigated"
dom.list_interactive filter (optional, case-insensitive substring) [{handle, role, name, disabled}, ...]
dom.click_described description (required): a handle or the element’s text "clicked <name> (<role>)"
  • The runtime attaches to the browser on the first dom.* call if it is not attached yet, dialling KASM_RUNTIME_CDP_ENDPOINT (default http://127.0.0.1:9222) for up to 5 seconds. A failed attach is a resolution error naming the cause; call again once the browser is up. The browser must be running with --remote-debugging-port and a --user-data-dir (see Compatibility).
  • The endpoint must be a loopback address unless KASM_RUNTIME_CDP_ALLOW_REMOTE=true.
  • A missing required parameter is malformed. An element that is not found, a predicate that does not hold before timeout_s, and a navigation that fails are resolution errors.
  • dom.click and dom.click_described deliver the click to the element, with no screen coordinates.

dom.list_interactive reads the page’s live accessibility tree and returns each interactive element with the text the page gives it, grouped by name, so a product row’s link, button and checkbox come back as one entry named by the row. Each entry has a five-character handle. Pass the handle to dom.click_described. A paraphrased name matches nothing.

A handle does not point at a page node. Passing one back matches the element’s name against a fresh read of the page. If the page has changed, the call returns a resolution error listing what is there now, and nothing is clicked. Matching tries the handle (exact only), then the exact name, then a unique substring. No match and more than one match are both resolution errors, and the error lists the candidates with their handles so the call can be retried. filter uses the same matching as dom.click_described, so any text a filter shows can be clicked as written.

These methods use the application’s accessibility tree over AT-SPI. They need no model.

Method Parameters Returns
a11y.tree none {Status, Actionable: [...]}: the matched application’s actionable elements
a11y.action name (required); expect_state; expect_value (boolean, default true) a description
  • The application is matched by the loaded scenario’s window, process and app, or by KASM_RUNTIME_APP when no scenario is loaded.
  • a11y.action invokes the element’s default action. With expect_state, it waits for the screen to settle, reads the state back, and returns a resolution error if the state does not hold.
  • Both return a resolution error if the loaded scenario sets atspi: false.
Method Parameters Returns Model
file.get path {path, bytes, sha256, content_b64} No
directory.get path {path, files, bytes, skipped, zip_bytes, sha256, zip_b64} No
  • A path is ~, ~/<path>, /tmp or /tmp/<path>. An absolute path under the home directory is read as ~/<path>. A path outside those roots, including one that leaves them through a symlink, is malformed.
  • Content comes back inline as base64, up to 5 MiB. A larger file or zip is a resolution error that names the streaming route to use instead, GET /v1/files/ or GET /v1/dirs/ (HTTP API). Files are written over HTTP only (PUT /v1/files/).
  • file.get reads regular files only; a directory, FIFO or device is refused without blocking. directory.get zips regular files and directories, and skips and counts symlinks and special files.

input.type and dom.fill each take exactly one of:

  • ref: the name of a value in the loaded scenario’s provides: map. The runtime resolves it inside the session and never echoes it back.
  • a literal (text for input.type, value for dom.fill) that your agent composes.

Giving both, or neither, is a malformed call. A ref with no scenario loaded, or a ref the scenario does not provide, is a resolution error. A scenario is loaded with KASM_RUNTIME_SCENARIO_B64; a driven session usually has none.