# Debugger MCP 协议

Web SDK Debugger MCP 是本地调试服务暴露给调试 Agent 的接口，用来发起模拟器对话，以及发现、渲染和读取当前
Page / Widget 运行现场。它不替代用户业务 MCP server，只描述 debugger 自身的调试能力。

## 目录

- [Endpoint](#endpoint)
- [Transport](#transport)
- [Snapshot](#snapshot)
- [Tools](#tools)
- [`list_console_messages`](#list_console_messages)
- [`get_console_message`](#get_console_message)
- [`get_log_snapshot`](#get_log_snapshot)
- [`list_render_targets`](#list_render_targets)
- [`render_page`](#render_page)
- [`render_widget`](#render_widget)
- [`submit_chat_query`](#submit_chat_query)
- [`set_viewport`](#set_viewport)
- [`take_snapshot`](#take_snapshot)
- [`get_computed_style`](#get_computed_style)
- [`get_element_rect`](#get_element_rect)
- [`scroll_to`](#scroll_to)
- [`take_screenshot`](#take_screenshot)
- [`list_agent_trace_events`](#list_agent_trace_events)
- [`list_agent_tool_calls`](#list_agent_tool_calls)
- [安全边界](#安全边界)

## Endpoint

本协议只约定 MCP endpoint path：

```text
/__wsd_mcp
```

完整 URL 的发现方式由启动器或宿主集成文档说明，不属于 Debugger MCP 协议本身。

## Transport

- 协议：MCP Streamable HTTP。
- 协议版本：支持 `2026-07-28` modern era，也接受 2025-era legacy 请求；modern client 通过
  `server/discover` 协商版本。
- 方法：`POST /__wsd_mcp`。
- 会话：当前实现不生成 session id，每次请求创建一次 MCP server / transport。
- CORS 预检：`OPTIONS /__wsd_mcp` 返回 204。
- 数据来源：调试台 UI 会把运行期快照写回 `/__agent_trace`，查询 tools 从 debugger server 内存中的最新快照读取数据。
- 浏览器控制：调试台通过内部 SSE 通道接收控制命令；渲染命令会在目标完成渲染且生成匹配命令的新 DOM 快照后回执。

Web SDK Debugger 连接用户业务 MCP endpoint 时使用自动版本协商：优先选择 `2026-07-28`，对只支持 2025-era
的 server 回退到 legacy handshake。每次 `tools/list` 或 `tools/call` 都建立独立 client，因此会重新执行协商，不缓存
endpoint 的 era 判定。

当 debugger server 尚未收到快照，或当前没有 Page / Widget 运行现场时，tools 返回：

```json
{
  "stored": false
}
```

如果 debugger server 已经记录过更新时间，返回体可能同时包含 `updatedAtMs`。

## Snapshot

MCP tools 读取 debugger server 内存中的最新 snapshot。

所有 tools 都返回 MCP tool result：

- `structuredContent`：任意 JSON value；当前 debugger 内置 tools 返回对象。
- `content[0].text`：默认是同一 JSON 的格式化字符串；`take_snapshot` 直接返回 uid 文本树，方便 Agent 阅读。

分页类 tools 的通用字段：

| 字段 | 说明 |
|------|------|
| `stored` | 是否已有可读快照 |
| `storedAt` | 快照写入时间 |
| `updatedAtMs` | 快照更新时间戳 |
| `total` | 当前过滤条件下的总数 |
| `pageIdx` | 当前页序号，从 0 开始 |
| `pageSize` | 每页条数，最大 200 |
| `hasMore` | 是否还有下一页 |

## Tools

| Tool | 用途 | 关键参数 |
|------|------|----------|
| `list_console_messages` | 分页列出 runtime console | `target`、`levels`、`query`、`pageIdx`、`pageSize` |
| `get_console_message` | 按 `msgid` 获取完整 console entry | `msgid` |
| `get_log_snapshot` | 返回最近一次调试现场快照 | 无 |
| `list_render_targets` | 列出 manifest 中可调试的 Page / Widget 及当前目标 | `type` |
| `render_page` | 切换到全页视图并强制渲染指定路由 | `url` |
| `render_widget` | 切换到卡片视图并渲染指定 Widget | `widgetId`、`structuredContent` |
| `submit_chat_query` | 通过调试台对话框提交 query，并等待本轮生成结束 | `query` |
| `set_viewport` | 设置当前 Page / Widget 的模拟视口尺寸 | `width`、`height` |
| `take_snapshot` | 返回当前 Page / Widget 的 uid 文本树和可访问性信息 | `target`、`verbose` |
| `get_computed_style` | 按快照 uid 查询元素的 computed style | `uid`、`properties` |
| `get_element_rect` | 按快照 uid 查询元素的 x、y、width、height | `uid` |
| `scroll_to` | 将当前 Page / Widget 的滚动容器移动到绝对坐标 | `uid`、`x`、`y` |
| `take_screenshot` | 截取当前 Page / Widget 或指定 uid 元素 | `uid`、`format`、`quality` |
| `list_agent_trace_events` | 分页列出 simulator / MCP / runtime trace events | `category`、`event`、`requestId`、`pageIdx`、`pageSize` |
| `list_agent_tool_calls` | 分页列出 simulator tool calls | `toolName`、`status`、`requestId`、`query`、`pageIdx`、`pageSize` |

除 `render_page`、`render_widget`、`submit_chat_query`、`set_viewport` 和 `scroll_to` 外，以上 tools 都是只读
操作。渲染 tools 会改变本地调试台的当前视图和运行目标；`submit_chat_query` 会新增一轮对话并可能调用网络与 MCP
tools；`set_viewport` 会改变当前模拟设备尺寸；`scroll_to` 会改变运行目标内滚动容器的当前位置。

## `list_console_messages`

`target` 可选值：

- `current`：根据当前运行模式选择 Page / Widget / App。
- `page`：只读 Page runtime console。
- `widget`：只读 Widget runtime console。
- `app`：只读 App runtime console。
- `all`：读取全部 runtime console。

`levels` 支持 `log`、`info`、`warn`、`error`、`debug`、`eval`。

返回 `structuredContent`：

```ts
{
  stored: true;
  storedAt?: string;
  updatedAtMs?: number;
  total: number;
  pageIdx: number;
  pageSize: number;
  hasMore: boolean;
  messages: Array<{
    msgid: string;
    target: 'page' | 'widget' | 'app';
    level: string;
    source: string;
    timestamp: number;
    text: string;
    url?: string;
    lineNumber?: number;
    columnNumber?: number;
    updatedAtMs?: number;
  }>;
}
```

列表项里的 `msgid` 可用于继续调用 `get_console_message` 读取完整参数和 stack。

## `get_console_message`

输入 `msgid`，返回：

- `found`：是否命中。
- `message`：完整 console entry，包括原始 `args`、位置和 stack 信息。

返回 `structuredContent`：

```ts
{
  stored: true;
  storedAt?: string;
  updatedAtMs?: number;
  found: true;
  message: {
    msgid: string;
    target: 'page' | 'widget' | 'app';
    level: string;
    source: string;
    timestamp: number;
    text: string;
    url?: string;
    lineNumber?: number;
    columnNumber?: number;
    updatedAtMs?: number;
    entry: Record<string, unknown>;
  };
}
```

未命中时返回：

```ts
{
  stored: true;
  storedAt?: string;
  updatedAtMs?: number;
  found: false;
  msgid: string;
}
```

## `get_log_snapshot`

返回当前调试现场快照，包含 runtime console、trace events、tool calls 和调试上下文，适合 Agent 一次性读取当前现场。

返回 `structuredContent`：

```ts
{
  stored: true;
  storedAt?: string;
  updatedAtMs?: number;
  snapshot: Record<string, unknown>;
}
```

## `list_render_targets`

从最近一次调试快照列出 manifest 中可渲染的目标。`type` 可选值为 `all`、`page`、`widget`，默认 `all`。

返回 `structuredContent`：

```ts
{
  stored: true;
  storedAt?: string;
  updatedAtMs?: number;
  pages: Array<{
    pageId: string;
    path: string;
    name: string;
    isHome: boolean;
  }>;
  widgets: Array<{
    widgetId: string;
    name: string;
  }>;
  currentTarget?: RuntimeTargetInfo;
}
```

首期只包含 manifest Page 和 Widget 模板，不包含对话区已经渲染的卡片实例。

## `render_page`

输入现有页面路由格式的 `url`，可携带 query。例如：

```json
{
  "url": "/pages/weather/index?city=beijing"
}
```

调用会切换到全页视图。即使当前已经是同一路由，也会强制重渲染。只有 Page controller 完成渲染，并生成携带同一
command id 的新 DOM 快照后，tool 才返回成功：

```ts
{
  rendered: true;
  target: RuntimeTargetInfo;
}
```

如需读取新页面结构，渲染成功后继续调用 `take_snapshot`。

## `render_widget`

输入 Widget id 和可选的 `structuredContent`：

```json
{
  "widgetId": "weather-card",
  "structuredContent": {
    "city": "北京"
  }
}
```

`structuredContent` 默认 `{}`，不会复用调试台输入框的历史数据。调用会切换到卡片视图、同步输入框并强制渲染
指定 Widget。成功返回与 `render_page` 相同的 `{ rendered, target }` 结构；随后调用 `take_snapshot` 读取新卡片
结构。

## `submit_chat_query`

输入非空的 `query`：

```json
{
  "query": "生成一张北京天气卡片"
}
```

调用会切换到对话视图，并复用调试台输入框的正常提交流程；debugger server 不会绕过调试台直接请求 Simulator
Gateway。tool 会等待本轮流式响应结束，成功时返回：

```ts
{
  submitted: true;
  completed: true;
  query: string;
  requestId: string;
}
```

可用返回的 `requestId` 继续查询 `list_agent_trace_events` 和 `list_agent_tool_calls`，并通过 `get_log_snapshot`
读取本轮 simulator message 和卡片数据。该 tool 只接收 `query`，不接收 token、header 或 session 参数；认证和
Simulator 上下文均沿用当前调试台。

## `set_viewport`

输入模拟视口的 `width` 和 `height`，两者都必须是 1～9999 的整数：

```json
{
  "width": 450,
  "height": 844
}
```

调用等价于在调试台选择自定义尺寸，只修改当前 Page / Widget 的模拟设备尺寸，不切换视图或运行目标。尺寸应用后返回：

```ts
{
  updated: true;
  viewport: {
    width: number;
    height: number;
  };
}
```

当前目标可能因设备尺寸变化而重新渲染；如需读取更新后的结构或图片，继续调用 `take_snapshot` 或 `take_screenshot`。

## `take_snapshot`

返回当前运行目标的 uid 文本树、可访问性信息、目标标识和视口信息。`target` 可选值为 `current`、`page`、
`widget`，默认 `current`；指定类型与当前目标不一致时返回 `TARGET_MISMATCH`。`verbose: true` 会包含全部已采集
属性和 `focusable=false` 等详细字段。

返回 `structuredContent`：

```ts
{
  stored: true;
  available: true;
  target: RuntimeTargetInfo;
  nodeCount: number;
  selectedUid?: string;
  truncated: boolean;
  snapshotUpdatedAtMs?: number;
  snapshot: string;
}
```

文本树示例：

```text
uid=root page
  uid=title-1 text role="heading" name="天气" class="title" [selected]
    uid=text-1 text "北京 26°C"
```

结构快照最多保留 5000 个节点，单个属性值最多保留 500 个字符；达到限制时 `truncated` 为 `true`。

## `get_computed_style`

输入 `take_snapshot` 返回的元素 `uid`，按需读取浏览器 `getComputedStyle` 结果。`properties` 可选；不传时返回全部
computed style，传入时只返回指定 CSS 属性：

```json
{
  "uid": "node-12",
  "properties": ["display", "color", "font-size"]
}
```

返回：

```ts
{
  uid: string;
  computedStyle: Array<{
    name: string;
    value: string;
  }>;
}
```

uid 不存在或对应节点不是元素时返回 `ELEMENT_NOT_FOUND`。

## `get_element_rect`

输入 `take_snapshot` 返回的元素 `uid`，返回相对于模拟设备视口的元素矩形；坐标和尺寸已经消除调试台显示缩放：

```ts
{
  uid: string;
  x: number;
  y: number;
  width: number;
  height: number;
}
```

## `scroll_to`

按绝对坐标滚动当前 Page / Widget 中的容器。`uid` 来自 `take_snapshot`；传入时精确操作对应元素，不传时从当前
inspector tree 中选择在运行目标视口内可见面积最大、且在请求坐标轴上有滚动范围的容器：

```json
{
  "uid": "node-12",
  "y": 480
}
```

`x`、`y` 必须是大于等于 0 的有限数字。未传的坐标轴保持当前位置；两者都不传时只查询当前滚动信息。坐标超过
范围时自动 clamp。滚动设置后等待两个 animation frame，再读取最终位置并返回：

```ts
{
  scrolled: true;
  uid: string;
  x: number;
  y: number;
  maxX: number;
  maxY: number;
  viewportWidth: number;
  viewportHeight: number;
}
```

指定 uid 不存在时返回 `ELEMENT_NOT_FOUND`；当前没有运行目标时返回 `TARGET_NOT_FOUND`；找不到可滚动容器或请求
坐标轴没有滚动范围时返回 `SCROLL_FAILED`。

连续逐屏截图可由 Agent 编排 `scroll_to` 和 `take_screenshot`：

1. 调用 `scroll_to({ y: 0 })`，保存返回的 `uid` 并截取当前目标。
2. 若 `y !== maxY`，调用
   `scroll_to({ uid, y: Math.min(y + viewportHeight, maxY) })`，再调用 `take_screenshot`。
3. 重复上一步，直到返回 `y === maxY`。

本期只负责逐屏滚动和截图，不生成或拼接完整长图。

## `take_screenshot`

不传 `uid` 时截取当前 Page / Widget 运行目标；传入 `uid` 时只截取对应元素。支持 `png`、`jpeg`、`webp`，默认
`png`；`quality` 范围为 0～100，仅对 JPEG 和 WebP 生效：

```json
{
  "uid": "node-12",
  "format": "webp",
  "quality": 80
}
```

图片通过 MCP image content 返回，`structuredContent` 只包含元数据，不重复携带 base64：

```ts
{
  captured: true;
  format: 'png' | 'jpeg' | 'webp';
  mimeType: string;
  width: number;
  height: number;
  uid?: string;
}
```

截图支持运行目标中的 open Shadow DOM。单张图片的 base64 负载限制为 8 MiB；跨域图片或字体无法被浏览器读取时可能返回
`SCREENSHOT_FAILED`。

## `list_agent_trace_events`

按 `category`、`event`、`requestId` 过滤 trace events。常见 category 包括 `simulator`、`mcp`、`runtime`。

返回 `structuredContent`：

```ts
{
  stored: true;
  storedAt?: string;
  updatedAtMs?: number;
  total: number;
  pageIdx: number;
  pageSize: number;
  hasMore: boolean;
  events: Array<{
    source: 'simulator' | 'widget_runtime' | 'page_runtime';
    id: string;
    timestamp: number;
    category: string;
    event: string;
    requestId: string;
    payload: unknown;
  }>;
}
```

## `list_agent_tool_calls`

按 `toolName`、`status`、`requestId` 或 `query` 过滤 simulator tool calls。

返回 `structuredContent`：

```ts
{
  stored: true;
  storedAt?: string;
  updatedAtMs?: number;
  total: number;
  pageIdx: number;
  pageSize: number;
  hasMore: boolean;
  toolCalls: Array<{
    id: string;
    requestId: string;
    toolCallId: string;
    toolName: string;
    serverId: string;
    appKey: string;
    status: string;
    createdAtMs: number;
    updatedAtMs: number;
    requiresUserApproval: boolean;
    isMiniApp: boolean;
    message: string;
  }>;
}
```

## 安全边界

- 查询 tools 标记为 read-only，不修改调试现场。
- `render_page`、`render_widget`、`submit_chat_query`、`set_viewport`、`scroll_to` 标记为非只读、非破坏性；
  `set_viewport` 和 `scroll_to` 还标记为幂等、无外部网络访问。渲染、对话或调整尺寸可能触发生命周期、网络请求
  和其他本地运行时副作用。
- 同一调试台的控制命令串行执行；普通控制命令超时时间为 15 秒，`submit_chat_query` 为 120 秒；SSE 断线会
  立即终止正在执行和排队的命令。
- 控制命令失败返回 MCP error，并在 `structuredContent.error.code` 中使用
  `DEBUGGER_NOT_CONNECTED`、`DEBUGGER_DISCONNECTED`、`TARGET_NOT_FOUND`、`ELEMENT_NOT_FOUND`、
  `RENDER_FAILED`、`RENDER_TIMEOUT`、`VIEWPORT_FAILED`、`INSPECTION_FAILED`、`SCROLL_FAILED`、
  `SCREENSHOT_FAILED`、`CHAT_NOT_READY`、`CHAT_BUSY`、`CHAT_SUBMIT_FAILED`、`CHAT_TIMEOUT` 或
  `COMMAND_TIMEOUT`。
  失败时不会把旧目标快照当作成功结果返回。
- 不提供通用点击、输入或样式修改；`submit_chat_query` 只开放对话 query 提交。
