Skip to content
Draft
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
6 changes: 3 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@clerk/themes": "^2.4.19",
"@mcp-ui/server": "^5.10.0",
"@modelcontextprotocol/sdk": "1.26.0",
"@onkernel/sdk": "^0.60.0",
"@onkernel/sdk": "^0.74.0",
"@types/jsonwebtoken": "^9.0.10",
"@types/redis": "^4.0.11",
"builtin-modules": "^5.0.0",
Expand Down
31 changes: 25 additions & 6 deletions src/lib/mcp/prompts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { TELEMETRY_EVENT_CATALOG } from "@/lib/mcp/telemetry";

export function registerKernelPrompts(server: McpServer) {
// MCP Prompt explaining Kernel concepts
Expand Down Expand Up @@ -128,7 +129,23 @@ kernel browsers process --help
kernel browsers playwright --help
\`\`\`

**MCP Exception:** The \`computer_action\` MCP tool with action "screenshot" is useful since it returns images directly to the agent.
**MCP Exceptions:** The \`computer_action\` MCP tool with action "screenshot" is useful since it returns images directly to the agent, and \`get_browser_telemetry\` reads structured telemetry events (see below).

---

## Telemetry Events (structured signal — works even after the session is deleted)

**Check telemetry first when it's available** — it's the fastest way to pinpoint failures.

**Gotcha: telemetry is opt-in and must have been enabled while the session ran.** Check with \`manage_browsers\` action "get" — if the \`telemetry\` field is null, no events were captured. The default bundle (control/connection/system/captcha) also omits the debug-critical categories. If the failing session had telemetry off, recreate the browser with \`telemetry_enabled\`, \`telemetry_console\`, \`telemetry_network\`, and \`telemetry_page\` set to true, reproduce the issue, then read events.

**Flow:**
1. \`get_browser_telemetry\` with session_id "${session_id}" — filter with categories ["console", "network", "page"] to cut noise, or order "desc" to inspect the end of the session
2. Scan for \`console_error\`, \`network_loading_failed\`, \`network_response\` with non-2xx status, and \`captcha_*\` outcomes
3. Correlate event timestamps with the failing automation step
4. Page with \`next_offset\` while \`has_more\` is true

${TELEMETRY_EVENT_CATALOG}

---

Expand Down Expand Up @@ -224,6 +241,7 @@ These are **normal** and don't indicate problems:
## Debugging Checklist

- [ ] Session exists and is active
- [ ] Telemetry events reviewed (if telemetry was enabled on the session)
- [ ] Screenshot shows expected content (or reveals error)
- [ ] Current URL is as expected
- [ ] Supervisor logs show all services running
Expand All @@ -237,11 +255,12 @@ These are **normal** and don't indicate problems:

Based on your issue "${issue_description}", start with:

1. **Get browser info** to confirm session is active
2. **Take screenshot** to see current state
3. **Check page URL** to see if on error page
4. **Test network** if seeing connection errors
5. **Review logs** for specific error patterns`;
1. **Get browser info** to confirm session is active and check whether telemetry was enabled
2. **Read telemetry events** if enabled (or recreate with telemetry on and reproduce)
3. **Take screenshot** to see current state
4. **Check page URL** to see if on error page
5. **Test network** if seeing connection errors
6. **Review logs** for specific error patterns`;

return {
messages: [
Expand Down
14 changes: 14 additions & 0 deletions src/lib/mcp/telemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const telemetryEventCategories = [
"console",
"network",
"page",
"interaction",
"control",
"connection",
"system",
"screenshot",
"captcha",
"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).`;
26 changes: 17 additions & 9 deletions src/lib/mcp/tools/browser-pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import { registerJsonResourceTemplate } from "@/lib/mcp/resource-templates";
import {
jsonResponse,
errorResponse,
paginatedJsonResponse,
textResponse,
toolErrorResponse,
} from "@/lib/mcp/responses";
import { paginationParams } from "@/lib/mcp/schemas";

type BrowserPoolCreateParams = Parameters<
KernelClient["browserPools"]["create"]
Expand Down Expand Up @@ -159,14 +161,18 @@ export function registerBrowserPoolCapabilities(server: McpServer) {
}

const client = createKernelClient(extra.authInfo.token);
const pools = await client.browserPools.list();
// Resources take no pagination params, so collect every page.
const pools = [];
for await (const pool of client.browserPools.list()) {
pools.push(pool);
}
return {
contents: [
{
uri: uri.toString(),
mimeType: "application/json",
text:
pools && pools.length > 0
pools.length > 0
? JSON.stringify(pools.map(summarizeBrowserPool), null, 2)
: "No browser pools found",
},
Expand Down Expand Up @@ -335,6 +341,7 @@ export function registerBrowserPoolCapabilities(server: McpServer) {
.boolean()
.describe("(release) Reuse browser instance or recreate. Default true.")
.optional(),
...paginationParams,
},
{
title: "Manage Kernel browser pools",
Expand Down Expand Up @@ -391,13 +398,14 @@ export function registerBrowserPoolCapabilities(server: McpServer) {
});
}
case "list": {
const pools = (await client.browserPools.list()) ?? [];
return pools.length > 0
? jsonResponse({
items: pools.map(summarizeBrowserPool),
note: 'Use action "get" with id_or_name for full pool details.',
})
: textResponse("No browser pools found");
const page = await client.browserPools.list({
...(params.limit !== undefined && { limit: params.limit }),
...(params.offset !== undefined && { offset: params.offset }),
});
return paginatedJsonResponse(page, {
mapItem: summarizeBrowserPool,
note: 'Use action "get" with id_or_name for full pool details.',
});
}
case "get": {
if (!params.id_or_name)
Expand Down
Loading
Loading