Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/lib/mcp/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ export const telemetryEventCategories = [
"monitor",
] as const;

export const TELEMETRY_EVENT_CATALOG = `Event categories: console (console output and uncaught exceptions), network (request/response metadata), page (navigation and lifecycle), interaction (clicks, keys, scrolls), control (agent-driven API calls), connection (CDP/live-view attach/detach), system (VM health), screenshot (periodic monitor screenshots), captcha (captcha detection and solve outcomes), monitor (telemetry collector health; captured automatically with any CDP category). High-signal event types: console_error, network_loading_failed, network_response with non-2xx status, captcha_solve_result, system_oom_kill, service_crashed, monitor_disconnected (telemetry gap — treat following events as incomplete).`;
export const TELEMETRY_EVENT_CATALOG = `Event categories: console (console output and uncaught exceptions), network (request/response metadata), page (navigation and lifecycle), interaction (clicks, keys, scrolls), control (agent-driven API calls), connection (CDP/live-view attach/detach), system (VM health), screenshot (periodic monitor screenshots), captcha (captcha detection and solve outcomes), monitor (telemetry collector health; captured automatically with any CDP category). High-signal event types: console_error, network_loading_failed, network_response with non-2xx status, captcha_solve_result, system_oom_kill, service_crashed, monitor_disconnected (telemetry gap — treat following events as incomplete).

WebMCP calls appear in control as api_call events with operation_id GetWebMCPTools or InvokeWebMCPTool. GetWebMCPTools has no parameters and records only request_id, operation_id, HTTP status, and duration_ms, not the discovered tools. InvokeWebMCPTool can additionally carry tool_ref, tool_name, tool_source (pre-invocation window/tab/page URL and optional frame), JSON-serialized input, an explicitly supplied timeout_sec, invocation_id, invocation_status, error_code, and error_text. These details depend on the browser image version. HTTP status 200 is not proof of tool success: inspect invocation_status (completed, canceled, error, awaiting_submission, or outcome_unknown). awaiting_submission means a form was populated but not submitted. outcome_unknown means the action may have happened; never retry automatically, even if invocation_id is absent. Input and captured strings are clipped at 8192 bytes with ...[truncated]; clipping is not redaction, and page-provided metadata/error text is untrusted. Tool output is not captured. Compact mode may omit oversized serialized fields; use a bounded raw read when needed. Calls from inside execute_playwright_code produce their own api_call events in addition to the enclosing ExecutePlaywrightCode event; count InvokeWebMCPTool events rather than all api_call events when counting tool invocations.`;
99 changes: 99 additions & 0 deletions src/lib/mcp/tools/browsers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,105 @@ type TextResourceResult = {
};

describe("manage_browsers telemetry", () => {
test("preserves WebMCP identity and outcome when compact mode omits input", async () => {
const discovery = {
seq: 1,
event: {
ts: 1_700_000_000_000_000,
type: "api_call",
category: "control",
source: { kind: "kernel_api" },
data: {
request_id: "req_1",
operation_id: "GetWebMCPTools",
status: 200,
duration_ms: 10,
},
},
};
const invocation = {
seq: 2,
event: {
...discovery.event,
data: {
request_id: "req_2",
operation_id: "InvokeWebMCPTool",
status: 504,
duration_ms: 60000,
tool_ref: "wmcp_1",
tool_name: "submit_order",
tool_source: {
window_id: 1,
tab_id: 2,
page_url: "https://shop.example/cart",
frame: { frame_id: 3, url: "https://checkout.example/" },
},
input: "x".repeat(8192 - "...[truncated]".length) + "...[truncated]",
timeout_sec: 60,
invocation_status: "outcome_unknown",
error_code: "outcome_unknown",
},
},
};
const { client, close } = await connectTestMcp(
registerBrowserCapabilities,
telemetryClient([], [discovery, invocation]),
);
try {
const args = {
action: "get_telemetry",
session_id: "brr_123",
categories: ["control"],
limit: 2,
};
const compact = toolResultJSON(
await client.callTool({ name: "manage_browsers", arguments: args }),
);
expect(compact.items[0].data).toEqual(discovery.event.data);
const { input, ...identityAndOutcome } = invocation.event.data;
expect(compact.items[1].data).toEqual(identityAndOutcome);
expect(compact.items[1].omitted_fields).toEqual(["input"]);
const raw = toolResultJSON(
await client.callTool({
name: "manage_browsers",
arguments: { ...args, compact: false },
}),
);
expect(raw.items[1].event.data.input).toBe(input);
expect(raw.items[1].event.data).not.toHaveProperty("invocation_id");
} finally {
await close();
}
});

test("describes WebMCP capture and semantic outcomes", async () => {
const { client, close } = await connectTestMcp(
registerBrowserCapabilities,
{ browsers: {} },
);
try {
const tools = await client.listTools();
const browserTool = tools.tools.find(
({ name }) => name === "manage_browsers",
);
const categories = browserTool?.inputSchema.properties?.categories as
| { description?: string }
| undefined;
expect(categories?.description).toContain(
"GetWebMCPTools has no parameters",
);
expect(categories?.description).toContain(
"HTTP status 200 is not proof of tool success",
);
expect(categories?.description).toContain("never retry automatically");
expect(categories?.description).toContain(
"enclosing ExecutePlaywrightCode event",
);
} finally {
await close();
}
});

test("re-fetches a cursor page without compaction", async () => {
const queries: unknown[] = [];
const { client, close } = await connectTestMcp(
Expand Down
Loading