vibium internals

What happens when a vibium MCP tool call is sent

Companion to the CLI version — same underlying daemon and browser layer, one extra process and a full model reasoning turn added on top. Traced from this project's own journey_mcp.py harness (2026-08-04): direct transcript parsing of real agent-driven runs, not documentation.

The path, one tool call

sequenceDiagram
    participant A as Claude agent (LLM)
    participant M as vibium MCP server
(long-lived for the session) participant D as vibium daemon participant X as Chrome for Testing participant P as Page (live site) Note over A: reasons about what to do next —
a real model inference call A->>M: mcp__vibium__browser_click
(JSON-RPC over stdio) activate M Note over M: NOT spawned per call —
one process for the whole agent session M->>D: connect via Unix socket
(same socket the CLI uses) activate D alt no browser session yet D->>X: spawn Chrome, fresh temp
--user-data-dir profile activate X end D->>X: forward command over BiDi
(WebSocket) X->>P: dispatch the actual action
(click / eval / navigate) P-->>X: DOM state / JS return value X-->>D: BiDi response D-->>M: Unix socket response deactivate D M-->>A: JSON-RPC tool_result deactivate M Note over A: reads the result, decides the
next tool call — repeats per step

Stage by stage

1
The agent's own reasoning is a real, timed step — not free.

Before every tool call the model decides what to do next: a genuine inference call to the Anthropic API. This is the step with no equivalent at all in the CLI path — a scripted vibium click has no "decide what to click" step, it's already decided.

2
The MCP server is one long-lived process for the whole session.

Unlike the CLI, where every command is a fresh OS process, vibium mcp is launched once (as a child process of claude -p, per this project's harness config) and stays alive for every tool call the agent makes during that session — confirmed by the same server handling all 8+ tool calls in a single run's transcript.

3
Tool names carry the server's prefix.

Confirmed live parsing real transcripts: calls show up as mcp__vibium__browser_navigate, mcp__vibium__browser_click, etc. — not the bare browser_navigate the MCP spec examples show. A transcript parser that matches on the bare name silently finds nothing — this is exactly the bug this project's own journey_mcp.py hit and fixed first.

4
Beneath the MCP server, it's the identical daemon/browser path.

Same Unix socket, same daemon PID, same BiDi transport to the same kind of Chrome-for-Testing process with its own fresh temp profile — see the CLI diagram. The MCP layer sits entirely above this, translating agent tool calls into the same commands the CLI issues directly.

5
Tool results are content-block lists, not bare strings.

A tool_result's content is [{"type":"text","text":"..."}], not a plain string — JSON-encoding the whole block instead of extracting the text was the second bug this session's harness hit before results parsed correctly.

6
The loop repeats per step, each one paying the reasoning cost again.

Navigate, dismiss, fill, click, fill, click — each is its own reasoning-then-tool-call round trip. That accumulation, not any single slow step, is why an MCP-driven run reaches the same real submit click roughly 17.5–30.1 seconds after load (hardened, n=70), against the CLI arm's 4.4–5.0 seconds for the identical sequence.

What this costs, measured

17.5–30.1s
arrival time at the real submit click, hardened across 70 real runs (2026-08-04) — vs. the CLI arm's 4.4–5.0s for the identical 8-step task.
70/70
runs where the negative-path assertion (step 6, the validation-error check) actually confirmed — vs. 0/70 for the CLI arm, whose own precondition check is fast enough to be swallowed by the same race it's testing for.

None of this is CLI/BiDi overhead — the daemon-and-browser layer underneath costs the MCP arm the same 170–250ms per actual browser action as the CLI. The gap is entirely the reasoning layer sitting on top of it. See references/timing-methodology.md's explicit scope note: the two arms measure different kinds of time and shouldn't be netted against each other.