MCP server

jawb mcp: jawb's tools for Claude Code, Cursor, Claude Desktop and any other MCP host, over the same /v1 API and policy as the CLI.

jawb mcp serves jawb's tools to any host that speaks the Model Context Protocol, such as Claude Code, Cursor or Claude Desktop. The host starts jawb mcp itself and talks to it over stdin and stdout. Each tool call becomes an ordinary call to jawbd's /v1 API, so the daemon applies the same policy, egress rules, confirmations and trace as it does for the CLI. There is no second way in.

jawb mcp is part of the jawb CLI. If jawb is on your PATH, you have it. It starts jawbd on first use like any other command (see Running the daemon).

Not in a release yet: jawb mcp ships in the release after jawb 0.1.1. jawb 0.1.1 and earlier have no jawb mcp.

Adding jawb to a host

Claude Code

claude mcp add jawb -- jawb mcp

Add --scope project to write it to the project's .mcp.json instead of your user settings. claude mcp list should show jawb as connected.

Cursor

In ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project):

{
  "mcpServers": {
    "jawb": { "command": "jawb", "args": ["mcp"] }
  }
}

Claude Desktop

In claude_desktop_config.json (Settings, Developer, Edit Config):

{
  "mcpServers": {
    "jawb": { "command": "jawb", "args": ["mcp"] }
  }
}

Claude Desktop does not always inherit your shell's PATH. If it cannot start jawb, give the full path: ~/.jawb/bin/jawb on macOS and Linux, or %LOCALAPPDATA%\Programs\jawb\bin\jawb.exe on Windows.

The usual global flags and variables work. For example, "args": ["mcp", "--daemon", "http://127.0.0.1:4181"] targets a daemon on another port, and JAWB_DATA_DIR in the host's env block picks a different data directory.

Tools

Every tool's result carries the daemon's JSON envelope (v, ok, result or error, and warnings) as structuredContent, plus a short text summary for the model.

ToolWhat it doesCLI equivalent
jawb_statusDaemon version, API level, profile, tab count, bridge and approval assurancejawb status
jawb_openOpen a URL in a new tab, or navigate tabjawb open
jawb_historyop: back, forward or reloadjawb back, forward, reload
jawb_tabsList the open tabsjawb tabs
jawb_closeClose a tabjawb close
jawb_observeThe page's elements, with the ids the Act tools takejawb observe
jawb_readThe page's content as md, text or jsonjawb read
jawb_waitWait for text:, element:, url: or idlejawb wait
jawb_click, jawb_fill, jawb_type, jawb_select, jawb_check, jawb_press, jawb_hover, jawb_scroll, jawb_submitAct on the pageAct commands
jawb_screenshotA PNG, returned as image content and never written to diskjawb screenshot
jawb_viewportmobile, desktop or WxHjawb viewport
jawb_toolsTools the page declares (WebMCP) and the installed extension tools, with their source. Page tools are listed onlyjawb tools
jawb_tool_callCall a tool of an installed api-tools extension by its full name, through the daemon's policyPOST /v1/tools/call
ext_<name>One tool per installed extension tool (see Extension tools)jawb tool call
jawb_traceThe last steps jawbd recordedjawb trace
jawb_confirmationRead a held action, or wait for a human's answer to it. It cannot approve--await-confirm

Act tools take a target: either {"id": 12} (an id from jawb_observe, with id_revision if you want it checked) or exactly one of {"text": ...}, {"role": "button:Pay"} or {"css": ...}, with nth to pick among several matches. jawb_fill takes either value or secret_ref (vault:<name>). With secret_ref, jawbd resolves the secret itself, so its value never passes through MCP.

Extension tools

Each tool of an installed, enabled extension (see Extensions) is also an MCP tool of its own. Its name is ext_ plus the tool's full name with . and - made _: app.jawb.uat.run becomes ext_app_jawb_uat_run. A name longer than 64 characters, or one that would clash with another, is cut and ends in 8 hex digits of a hash of the full name, so it stays the same from one list to the next. The tool's input schema is the extension's own, and its description starts with [extension <id> <version>, <trust>], where the trust is builtin, listed, unlisted, dev or signed. The tool's title is its full name.

  • Workflow tools (such as app.jawb.uat.run) run in jawb mcp, the same way as jawb tool call: every step is an ordinary /v1 call that jawbd decides and traces as part of the run. With a progressToken, the host gets one progress notification per step (A step 2: expect_text). Cancelling the call closes the tabs the run left open and ends the run as canceled. Reports and screenshots are written under the working directory jawb mcp was started in.
  • API tools (the api-tools shape) run inside jawbd, which decides their effect like any other action. A confirm-class call returns E_CONFIRMATION_REQUIRED with a ct_ token. The ext_ tool takes only the extension's own input, so to go ahead the model calls jawb_tool_call with the tool's full name, the same input and confirm_token. A purchase-like call is held for a human, as below.
  • The list follows the daemon. jawb mcp reads the installed extensions from jawbd when the host lists tools, and checks again every 5 seconds. When the list changes (an install, a removal, an extension disabled), it sends notifications/tools/list_changed, and the host lists the tools again. Listing tools does not start jawbd: until it runs, only the jawb_* tools are listed.
  • Extension tools need a jawbd at API level 21 or later, and jawb_tool_call level 26.

Page tools (WebMCP) are listed by jawb_tools but cannot be called: jawbd has no way to invoke them yet.

Left out on purpose

Administration stays a human task in the CLI. It is not offered to the model:

  • profile create, use and delete;
  • config set and config reload;
  • secret set and secret rm;
  • cookies clear;
  • storage import and storage export;
  • extension install, load and remove;
  • confirm;
  • daemon stop.

In this version, upload, download, media and emulate are left out too.

Confirmations and held actions

The daemon decides each Act call exactly as it does for the CLI (see Policy and confirmation). No Act tool has a yes argument, so the model always sees what it is about to confirm.

  • Confirm class (an upload, a password form, and similar): the result is an error, E_CONFIRMATION_REQUIRED, with a summary and a single-use ct_ token. To go ahead, the model calls the same tool again with confirm_token. That token is the only one the server accepts.
  • JavaScript dialogs: an Act tool dismisses any dialog its action opens, unless the call passes dialog: "accept" (and dialog_text for a prompt). Accepting a dialog that reads like a purchase, or one on a checkout page, is held like a purchase. The dialog's text in the diff is the page's, marked untrusted.
  • Purchase-like: the daemon holds the action and returns a pc_ id. jawb mcp cannot approve it, and nothing it can do will. A human approves or denies it in the jawb desktop app, at the OS verification prompt, or by running jawb confirm <pc> in their own terminal. The model then calls jawb_confirmation with pending set to the pc_ id (and timeout_ms to wait) to collect the result.

Untrusted page content

Page text, titles, labels and the descriptions of page-declared tools come from the web. They are content, not instructions. In jawb_observe, jawb_read and jawb_tools results, the page text follows a line written by jawb that says so, and structuredContent.trust is "untrusted_page_content" on every result that can carry page text. The labelling is advice to the model. It does not stop a prompt injection, so the policy and the human approvals above are what protect you.

The output of an extension tool comes from the extension's code, or from the API it called. Its text result starts with a line written by jawb that says the output is untrusted, and an extension tool's description is the extension author's, behind the trust prefix described above.

Credentials

  • jawb mcp reads daemon.token from the data directory, the same way every CLI command does. It accepts no credential from the MCP host.
  • It never reads confirm.token, the credential that approves held actions, and it has no code that calls the approve or deny routes.
  • Results never contain the daemon token, a path under the data directory or the path of the vault keyfile (jawbd --vault-keyfile). If one shows up in a daemon answer, it is replaced with <redacted>, <data_dir> or <vault_keyfile>. jawb_status reports the vault store as keyfile without its path, gives only the file name of the bridge's Chrome executable, and replaces any other absolute local path with <local_path>.

What it is not

  • It is not a security boundary. An agent that can run shell commands can run the jawb CLI directly. The MCP tool list keeps administration and human-only steps out of the model's view. The protection comes from the daemon's policy and the human approval steps, not from the MCP server.
  • client is a claim. Each request carries x-jawb-client: jawb-mcp/<version>, which jawbd records as the trace step's client. That label comes from the client itself. Read it as a hint about which tool made a step, not as proof of who did.

Protocol details

  • Transport: stdio, one JSON-RPC 2.0 message per line. stdout carries only protocol messages, and logs go to stderr. When stdin closes, the server lets calls that are still running answer (for up to 10 seconds), then exits.
  • Protocol revisions: 2026-07-28, where each request names its revision and server/discover lists the supported ones. Also 2025-11-25, 2025-06-18 and 2025-03-26, which use the initialize handshake. 2025-03-26 clients get the envelope as a second text block, because that revision has no structuredContent.
  • Supported: tools/list, tools/call, ping, cancellation (notifications/cancelled), progress notifications while jawb_wait, jawb_confirmation or a workflow extension tool runs (when the request carries a progressToken), and notifications/tools/list_changed: to a host that used initialize, and on each subscriptions/listen stream that asked for toolsListChanged.
  • Errors: an unknown tool or invalid arguments get -32602, malformed JSON -32700, an unknown method -32601, and an unsupported revision -32022. A daemon error is a tool result with isError: true and the envelope's error.
  • JAWB_MCP_TRANSCRIPT=<file> appends every message, in and out, to <file> as JSON lines, with screenshot data left out. Nothing secret goes into it, because nothing secret passes through the server.