@attson/atwebpilot-mcp 0.0.70 → 0.0.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +24 -8
  2. package/dist/index.js +311 -37
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -49,20 +49,36 @@ listen 端口的能力,所以方向只能如此。这也是配对页存在的
49
49
  ## 工具面
50
50
 
51
51
  - 控制面 4 个:`list_tabs / open_session / close_session / get_quota`
52
- - 执行面 54 个 `browser_*`:扩展的全部内置工具,除了 `askUser`(MCP 会话没有人在侧边栏应答)和
53
- `attachTab` / `detachTab`(侧边栏多 tab 记账,MCP 的目标 tab 已由 `open_session` 绑定)。
52
+ - 发现 1 个:`browser_discoverTools` —— 不带参数返回未发布工具目录(按 `export / network / storage / browser-data / inspect / legacy-dom / form / tabs` 分组);带 `enable: [...]` 把它们加进本进程的 `tools/list`(发送 `tools/list_changed`),并直接返回完整 schema。
53
+ - 执行面默认 **core 31 个** `browser_*`:浏览 / 采集 / 填表 / 导航 / 截图 闭环所需的工具。其余 20 个由 AI 按需 `discoverTools` 拉取,用户不用配置。
54
+ - 不暴露 `askUser`(MCP 会话没有人在侧边栏应答)和 `attachTab` / `detachTab`(目标 tab 已由 `open_session` 绑定)。
55
+
56
+ MCP 层的描述是精简英文(≈2.5k tokens for core),侧边栏内置 LLM 仍用中文长描述;两者共享同一份 `TOOL_DEFS`,靠 `mcp` 字段区分。
54
57
 
55
58
  工具列表会和扩展 `HELLO` 里上报的 `supported_tools` 求交集,所以旧版扩展配新版 server 时不会
56
- 出现「列出来但一调就 unknown tool」。字段缺失(Plan 32 之前的扩展)时回落到旧的 19 个。
59
+ 出现「列出来但一调就 unknown tool」。字段缺失(Plan 32 之前的扩展)时回落到旧版扩展支持的内置
60
+ 工具集,与 core 求交集后只剩 9 个(`extractText / click / fillInput / selectOption / setCheckbox /
61
+ hover / uploadFile / scroll / waitFor`);`browser_storage` 也不会列出,因为旧版没有
62
+ `writeStorage`。请升级扩展。
57
63
 
58
64
  ### 环境变量 `ATWEBPILOT_MCP_TOOLS`
59
65
 
60
66
  | 值 | 效果 |
61
67
  |---|---|
62
- | `full`(默认) | 全部 54 个 |
63
- | `parity` | 只出对标 playwright-ext 那 24 个能力的子集,省上下文 |
68
+ | `core`(默认) | 31 个核心工具 + `browser_discoverTools`,其余按需发现 |
69
+ | `full` | 一开始就全部列出(51 个),适合不想让 AI 多一步发现的用户 |
70
+
71
+ 无法识别的值(包括已移除的 `parity`)按 `core` 处理,并往 stderr 打一条提示。
64
72
 
65
- 无法识别的值按 `full` 处理,并往 stderr 打一条提示。
73
+ ### 从 0.0.70 及更早版本迁移
74
+
75
+ | 旧 | 新 |
76
+ |---|---|
77
+ | `browser_navigateBack` / `browser_navigateForward` | `browser_navigate({ action: "back" \| "forward" })` |
78
+ | `browser_highlightElement` / `browser_highlightText` | `browser_highlight({ selector \| uid \| text })` |
79
+ | `browser_readStorage` / `browser_writeStorage` | `browser_storage({ op: "get" \| "set", store, key, value? })` |
80
+ | `ATWEBPILOT_MCP_TOOLS=parity` | 已移除;默认即 `core` |
81
+ | 默认列出全部工具 | 默认只列 core;其余通过 `browser_discoverTools` 获取,或设 `ATWEBPILOT_MCP_TOOLS=full` |
66
82
 
67
83
  ## 替代 playwright-ext
68
84
 
@@ -78,8 +94,8 @@ listen 端口的能力,所以方向只能如此。这也是配对页存在的
78
94
  | `browser_select_option` / `browser_hover` / `browser_press_key` | 同名去掉下划线:`browser_selectOption` / `browser_hover` / `browser_pressKey` |
79
95
  | `browser_drag` / `browser_drop` | `browser_drag` / `browser_drop` |
80
96
  | `browser_file_upload` | `browser_uploadFile` |
81
- | `browser_navigate` / `browser_navigate_back` | `browser_navigate` / `browser_navigateBack`(另有 `navigateForward`) |
82
- | `browser_tabs` | `browser_listTabs` / `openTab` / `closeTab` / `switchToTab` |
97
+ | `browser_navigate` / `browser_navigate_back` | `browser_navigate`(`action` 取 `goto` / `back` / `forward` / `reload`) |
98
+ | `browser_tabs` | `browser_listTabs` / `openTab` / `closeTab`; `switchToTab` 需按需发现,仅用于用户明确要求前台切换 |
83
99
  | `browser_close` | `browser_closeTab` |
84
100
  | `browser_resize` | `browser_resize` |
85
101
  | `browser_take_screenshot` | `browser_screenshot`(含 `fullPage` / `format` / `scale`) |
package/dist/index.js CHANGED
@@ -970,6 +970,10 @@ var TOOL_DEFS = [
970
970
  {
971
971
  name: "snapshotDOM",
972
972
  description: "[FIRST\xB7LEGACY] \u8FD4\u56DE\u9875\u9762 DOM \u7B80\u5316\u6811\uFF08tag/id/classes/\u76F4\u63A5\u6587\u672C/children\uFF09\u3002\n\u5982\u679C\u4F60\u53EA\u662F\u8981\u627E\u4EA4\u4E92\u5143\u7D20\u5E76\u968F\u540E\u64CD\u4F5C\uFF0C**\u4F18\u5148\u7528 takeSnapshot**\uFF08UID \u7A33\u5B9A\uFF0CclickByUid \u5065\u58EE\uFF09\uFF1B\nsnapshotDOM \u66F4\u9002\u5408\u300C\u6211\u8981\u5206\u6790\u6574\u4E2A\u9875\u9762\u7ED3\u6784\u300D\u8FD9\u79CD\u63A2\u67E5\u7C7B\u9700\u6C42\u3002\n\n\u793A\u4F8B\uFF1A\n- \u770B\u6574\u9875\uFF1A{ }\uFF08\u9ED8\u8BA4 maxDepth=3\uFF09\n- \u770B\u67D0\u533A\u57DF\uFF1A{ root: '.main-content', maxDepth: 5 }\n- \u770B\u5230\u5E95\uFF1A{ maxDepth: 8 }",
973
+ mcp: {
974
+ description: "Simplified DOM tree (tag/id/classes/text/children) for analysing page structure. Prefer takeSnapshot when you intend to click or fill.",
975
+ params: { root: "CSS selector to start from; defaults to <html>" }
976
+ },
973
977
  input_schema: {
974
978
  type: "object",
975
979
  properties: {
@@ -982,6 +986,7 @@ var TOOL_DEFS = [
982
986
  {
983
987
  name: "querySelector",
984
988
  description: "[FAST] \u8FD4\u56DE\u9996\u4E2A\u5339\u914D\u5143\u7D20\u7684\u6D45\u5C42\u6458\u8981 (tag/id/classes/text/attrs)\u3002\u4EC5\u63A2\u67E5\u7528\u3002\n\u8981\u540E\u7EED\u70B9\u51FB\u6216\u586B\u503C\uFF0C\u914D\u5408 selector \u76F4\u63A5\u4F20\u7ED9 click / fillInput\uFF0C\u6216\u7528 takeSnapshot \u62FF UID\u3002\n\n\u793A\u4F8B\uFF1A\n- \u627E\u6309\u94AE\uFF1A{ selector: 'button[type=submit]' }\n- \u627E\u8F93\u5165\uFF1A{ selector: 'input[name=email]' }",
989
+ mcp: { description: "Shallow summary (tag/id/classes/text/attrs) of the first element matching a CSS selector." },
985
990
  input_schema: {
986
991
  type: "object",
987
992
  properties: {
@@ -995,6 +1000,7 @@ var TOOL_DEFS = [
995
1000
  {
996
1001
  name: "querySelectorAll",
997
1002
  description: "[FAST] \u8FD4\u56DE\u6240\u6709\u5339\u914D\u5143\u7D20\u7684\u6D45\u5C42\u6458\u8981\u6570\u7EC4\u3002\n\n\u793A\u4F8B\uFF1A\n- \u6240\u6709\u8BC4\u8BBA\uFF1A{ selector: '.comment-item', limit: 50 }\n- \u6240\u6709\u94FE\u63A5\uFF1A{ selector: 'a[href]', limit: 20 }",
1003
+ mcp: { description: "Shallow summaries of all elements matching a CSS selector." },
998
1004
  input_schema: {
999
1005
  type: "object",
1000
1006
  properties: {
@@ -1009,6 +1015,10 @@ var TOOL_DEFS = [
1009
1015
  {
1010
1016
  name: "inspectElement",
1011
1017
  description: "[FAST\xB7READ\xB7LAYOUT] \u8BFB\u53D6\u5143\u7D20\u7684\u8FD0\u884C\u65F6\u5E03\u5C40\u771F\u76F8\uFF1Acomputed style\u3001viewport/document rect\u3001\u53EF\u89C1\u6027\u3001\u7956\u5148\u94FE\u4E0E shadow root \u4FE1\u606F\u3002\n\u8C03\u8BD5 display/box-sizing/position/overflow/z-index\u3001\u5F39\u7A97\u6302\u8F7D\u70B9\u6216\u7236\u7EA7\u5E03\u5C40\u65F6\u4F18\u5148\u7528\u5B83\uFF0C\u4E0D\u8981\u4E3A\u8FD9\u4E9B\u53EA\u8BFB\u4FE1\u606F\u8C03\u7528 runJS\u3002",
1018
+ mcp: {
1019
+ description: "Runtime layout truth for one element: computed style, rects, visibility, ancestor chain, shadow root. Use for display/overflow/z-index/positioning questions instead of runJS.",
1020
+ params: { selector: "CSS selector; one of selector/uid", uid: "uid from takeSnapshot/findElements; one of selector/uid", styleProperties: "CSS properties to read; defaults to common layout props" }
1021
+ },
1012
1022
  input_schema: {
1013
1023
  type: "object",
1014
1024
  properties: {
@@ -1028,6 +1038,7 @@ var TOOL_DEFS = [
1028
1038
  {
1029
1039
  name: "extractText",
1030
1040
  description: "[FAST\xB7TARGETED] \u63D0\u53D6\u9009\u62E9\u5668\u547D\u4E2D\u5143\u7D20\u7684\u6587\u672C\u3002single=true \u8FD4\u56DE\u5B57\u7B26\u4E32\uFF0C\u5426\u5219\u8FD4\u56DE\u6570\u7EC4\u3002\n\u53EA\u7528\u4E8E\u660E\u786E\u7684\u5C0F\u8303\u56F4 selector\uFF1B\u666E\u901A\u7F51\u9875\u7406\u89E3/\u5B57\u6BB5\u63D0\u53D6\u4E0D\u8981\u7528 extractText({selector:'body'})\uFF0C\u5148\u7528 createPageIndex + extractPageFields/searchPageIndex\u3002\n\n\u793A\u4F8B\uFF1A\n- \u63D0\u53D6\u6807\u9898\uFF1A{ selector: 'h1', single: true }\n- \u63D0\u53D6\u6240\u6709\u6BB5\u843D\uFF1A{ selector: 'article p' }",
1041
+ mcp: { description: "Text of elements matching a small, targeted CSS selector. single=true returns a string, else an array. Do not use on body; use createPageIndex for whole pages." },
1031
1042
  input_schema: {
1032
1043
  type: "object",
1033
1044
  properties: {
@@ -1043,6 +1054,10 @@ var TOOL_DEFS = [
1043
1054
  {
1044
1055
  name: "createPageIndex",
1045
1056
  description: "[PAGE-INDEX][FIRST\xB7READ] \u5728\u5185\u5BB9\u811A\u672C\u672C\u5730\u6784\u5EFA/\u5237\u65B0\u9875\u9762\u7D22\u5F15\uFF0C\u8FD4\u56DE\u5C0F\u578B\u9875\u9762\u5730\u56FE\u3001blockId\u3001kinds\u3001truncation \u5143\u6570\u636E\u3002\n\u7528\u4E8E\u666E\u901A\u7F51\u9875\u7406\u89E3\u3001\u5546\u54C1/\u6587\u7AE0/\u8868\u683C\u5B57\u6BB5\u63D0\u53D6\u3001\u91C7\u96C6\u524D\u5B9A\u4F4D\u3002\u4E0D\u8981\u5148\u8BFB\u53D6 body\uFF1B\u5148\u5EFA\u7D22\u5F15\uFF0C\u518D\u7528 extractPageFields/searchPageIndex\u3002",
1057
+ mcp: {
1058
+ description: "Build or refresh a local page index; returns a compact page map with blockIds. Call first on any content page before extractPageFields/searchPageIndex.",
1059
+ params: { refresh: "true = rescan, ignore cache" }
1060
+ },
1046
1061
  input_schema: {
1047
1062
  type: "object",
1048
1063
  properties: {
@@ -1056,6 +1071,7 @@ var TOOL_DEFS = [
1056
1071
  {
1057
1072
  name: "searchPageIndex",
1058
1073
  description: "[PAGE-INDEX] \u5728\u672C\u5730\u9875\u9762\u7D22\u5F15\u4E2D\u641C\u7D22\u5173\u952E\u8BCD/\u5B57\u6BB5\uFF0C\u8FD4\u56DE\u5C0F\u8BC1\u636E\u7247\u6BB5\u3001blockId\u3001complete/availableChars\u3001truncation \u5143\u6570\u636E\u3002\n\u9002\u5408\u5B9A\u4F4D\u6392\u540D\u3001\u4EF7\u683C\u3001\u8BC4\u8BBA\u6570\u3001\u65E5\u671F\u7B49\u8BC1\u636E\uFF1B\u4E0D\u8981\u7528 extractText({selector:'body'}) \u6765\u641C\u7D22\u5927\u9875\u9762\u3002",
1074
+ mcp: { description: "Search the page index for a keyword/field; returns evidence snippets with blockIds.", params: { query: "keyword or phrase" } },
1059
1075
  input_schema: {
1060
1076
  type: "object",
1061
1077
  properties: {
@@ -1071,6 +1087,10 @@ var TOOL_DEFS = [
1071
1087
  {
1072
1088
  name: "readPageBlock",
1073
1089
  description: "[PAGE-INDEX] \u6309 blockId \u8BFB\u53D6\u5C40\u90E8\u5185\u5BB9\uFF1B\u957F\u5185\u5BB9\u6309 offset/maxChars \u5206\u9875\uFF0C\u8FD4\u56DE hasMore\u3001nextOffset\u3001recommendedNext \u548C truncation \u65E5\u5FD7\u3002\n\u53EA\u5728 searchPageIndex/extractPageFields \u8BC1\u636E\u4E0D\u8DB3\u6216\u9700\u8981\u6838\u5BF9\u90BB\u8FD1\u4E0A\u4E0B\u6587\u65F6\u4F7F\u7528\u3002",
1090
+ mcp: {
1091
+ description: "Read one index block by blockId, paged via offset/maxChars. Use when search/extract evidence is insufficient.",
1092
+ params: { blockId: "blockId from createPageIndex/searchPageIndex/extractPageFields", indexId: "bind to the index that produced blockId" }
1093
+ },
1074
1094
  input_schema: {
1075
1095
  type: "object",
1076
1096
  properties: {
@@ -1087,6 +1107,7 @@ var TOOL_DEFS = [
1087
1107
  {
1088
1108
  name: "extractPageFields",
1089
1109
  description: "[PAGE-INDEX][FIELD-FIRST] \u901A\u7528\u5B57\u6BB5\u5019\u9009\u63D0\u53D6\uFF1A\u8F93\u5165\u5B57\u6BB5\u540D\u6570\u7EC4\uFF0C\u8FD4\u56DE value candidates\u3001confidence\u3001evidence\u3001blockId\u3001truncation\u3002\n\u9002\u5408\u5546\u54C1\u4FE1\u606F\u3001\u6587\u7AE0\u5143\u4FE1\u606F\u3001\u8868\u683C\u8BE6\u60C5\u3001\u8868\u5355\u5B57\u6BB5\u7B49\u7ED3\u6784\u5316\u63D0\u53D6\uFF1B\u8BC1\u636E\u4E0D\u8DB3\u518D\u7528 readPageBlock \u5B9A\u5411\u8BFB\u53D6\u3002",
1110
+ mcp: { description: "Extract named fields (price, author, date, \u2026) from the page index with candidates, confidence and evidence.", params: { fields: "field names to extract" } },
1090
1111
  input_schema: {
1091
1112
  type: "object",
1092
1113
  properties: {
@@ -1102,6 +1123,7 @@ var TOOL_DEFS = [
1102
1123
  {
1103
1124
  name: "extractImages",
1104
1125
  description: "[FAST] \u5728 root \u8303\u56F4\u5185\u63D0\u53D6\u6240\u6709 <img> \u7684 src/data-src/srcset\uFF1BincludeBg=true \u65F6\u4E5F\u63D0\u53D6\u80CC\u666F\u56FE\u3002\u8FD4\u56DE [{url, via}].\n\n\u793A\u4F8B\uFF1A\n- \u5168\u9875\u56FE\uFF1A{ }\uFF08\u9ED8\u8BA4 root=document\uFF09\n- \u5546\u54C1\u4E3B\u56FE\uFF1A{ root: '.product-gallery' }",
1126
+ mcp: { description: "All <img> src/data-src/srcset under root; includeBg=true adds CSS background images." },
1105
1127
  input_schema: {
1106
1128
  type: "object",
1107
1129
  properties: {
@@ -1114,6 +1136,7 @@ var TOOL_DEFS = [
1114
1136
  {
1115
1137
  name: "scroll",
1116
1138
  description: "[FLOW] \u6EDA\u52A8\u9875\u9762\u3002to \u53EF\u4E3A 'bottom' / 'top' / number\u3002max \u662F\u6EDA\u52A8\u6B21\u6570\uFF1BuntilSelector \u51FA\u73B0\u65F6\u63D0\u524D\u505C\u3002\n\n\u793A\u4F8B\uFF1A\n- \u89E6\u53D1\u61D2\u52A0\u8F7D\uFF1A{ to: 'bottom', max: 5 }\n- \u6EDA\u5230\u951A\u70B9\uFF1A{ to: 'top' } \u540E\u7528 element.scrollIntoView \u4E5F\u53EF\n- \u7B49\u5F85\u65B0\u5143\u7D20\uFF1A{ to: 'bottom', max: 10, untilSelector: '.item:nth-child(20)' }",
1139
+ mcp: { description: "Scroll the page. to = 'top' | 'bottom' | pixel number; max = repeat count; untilSelector stops early when it appears (lazy-load).", params: { to: "'bottom' | 'top' | number" } },
1117
1140
  input_schema: {
1118
1141
  type: "object",
1119
1142
  properties: {
@@ -1129,6 +1152,7 @@ var TOOL_DEFS = [
1129
1152
  {
1130
1153
  name: "waitFor",
1131
1154
  description: "[FLOW] \u7B49\u5F85\u56FA\u5B9A ms\uFF0C\u6216\u7B49\u5F85\u9009\u62E9\u5668\u51FA\u73B0\uFF08\u5E26 timeoutMs \u515C\u5E95\uFF09\u3002\n\n\u793A\u4F8B\uFF1A\n- \u7B49 500ms\uFF1A{ ms: 500 }\n- \u7B49\u5143\u7D20\u51FA\u73B0\uFF1A{ selector: '.lazy-loaded', timeoutMs: 8000 }",
1155
+ mcp: { description: "Wait a fixed ms, or until a selector/text appears or text disappears, with timeoutMs.", params: { text: "wait until this text appears", textGone: "wait until this text disappears" } },
1132
1156
  input_schema: {
1133
1157
  type: "object",
1134
1158
  properties: {
@@ -1144,6 +1168,7 @@ var TOOL_DEFS = [
1144
1168
  {
1145
1169
  name: "click",
1146
1170
  description: "[ACT] \u70B9\u51FB\u9009\u62E9\u5668\u547D\u4E2D\u7684\u5143\u7D20\u3002required=false \u65F6\u627E\u4E0D\u5230\u4E0D\u62A5\u9519\u3002\u4F1A\u7ECF\u8FC7\u5BA1\u9605\uFF08caution\uFF09\u3002\n\u7528 takeSnapshot \u62FF\u5230 UID \u540E\u5EFA\u8BAE\u6539\u7528 clickByUid\uFF0C\u66F4\u7A33\u3002",
1171
+ mcp: { description: "Click the element matching a CSS selector. Supports button/doubleClick/modifiers. required=false makes a missing element a no-op. Prefer clickByUid after takeSnapshot." },
1147
1172
  input_schema: {
1148
1173
  type: "object",
1149
1174
  properties: {
@@ -1163,6 +1188,7 @@ var TOOL_DEFS = [
1163
1188
  {
1164
1189
  name: "httpRequest",
1165
1190
  description: "[ACT] \u901A\u8FC7\u540E\u53F0\u4EE3\u7406\u53D1\u8BF7\u6C42\u3002withCredentials=true \u65F6\u5E26 cookie\uFF08dangerous\uFF0C\u8981\u5BA1\u9605\uFF09\uFF1B\u9ED8\u8BA4 omit\u3002\n\n\u793A\u4F8B\uFF1A\n- \u7FFB\u8BC4\u8BBA\u9875\uFF1A{ url: 'https://x.com/api/comments?page=2', withCredentials: false }\n- \u5E26\u767B\u5F55\u6001\u8C03\u5185\u90E8\u63A5\u53E3\uFF1A{ url: '...', withCredentials: true }",
1191
+ mcp: { description: "HTTP request via the background proxy. withCredentials=true sends cookies (dangerous, reviewed); default omits them.", params: { body: "any JSON value" } },
1166
1192
  input_schema: {
1167
1193
  type: "object",
1168
1194
  properties: {
@@ -1179,6 +1205,7 @@ var TOOL_DEFS = [
1179
1205
  {
1180
1206
  name: "readStorage",
1181
1207
  description: "[DANGER] \u8BFB localStorage \u6216 sessionStorage \u6307\u5B9A key\u3002\u9700\u8981\u5BA1\u9605\u3002",
1208
+ mcp: { description: "Read one key from localStorage or sessionStorage (dangerous, reviewed)." },
1182
1209
  input_schema: {
1183
1210
  type: "object",
1184
1211
  properties: {
@@ -1192,6 +1219,10 @@ var TOOL_DEFS = [
1192
1219
  {
1193
1220
  name: "fillInput",
1194
1221
  description: "[ACT] \u5F80 input/textarea/contenteditable \u586B\u503C\uFF1B\u89E6\u53D1 input/change \u4E8B\u4EF6\u517C\u5BB9 React/Vue\u3002\n**\u6279\u91CF\u586B\u8868\u8BF7\u7528 fillForm**\uFF08\u66F4\u9AD8\u6548\uFF09\u3002\n\n\u793A\u4F8B\uFF1A\n- \u586B\u90AE\u7BB1\uFF1A{ selector: 'input[name=email]', value: 'a@b.c' }\n- \u4E0D\u6E05\u7A7A\u76F4\u63A5\u8FFD\u52A0\uFF1A{ selector: '.editor', value: 'tail', clear: false }",
1222
+ mcp: {
1223
+ description: "Set the value of an input/textarea/contenteditable and fire input/change. Use fillForm for several fields.",
1224
+ params: { slowly: "type char by char for controlled components", submit: "press Enter afterwards" }
1225
+ },
1195
1226
  input_schema: {
1196
1227
  type: "object",
1197
1228
  properties: {
@@ -1212,6 +1243,7 @@ var TOOL_DEFS = [
1212
1243
  {
1213
1244
  name: "setCheckbox",
1214
1245
  description: "[ACT] \u8BBE\u7F6E checkbox \u52FE\u9009\u72B6\u6001\uFF1B\u6D3E\u53D1 change \u4E8B\u4EF6\u3002",
1246
+ mcp: { description: "Set a checkbox's checked state and dispatch change." },
1215
1247
  input_schema: {
1216
1248
  type: "object",
1217
1249
  properties: {
@@ -1225,6 +1257,7 @@ var TOOL_DEFS = [
1225
1257
  {
1226
1258
  name: "selectOption",
1227
1259
  description: "[ACT] <select> \u5143\u7D20\u6309 value \u6216 label \u9009\u9879\u3002\u540C\u65F6\u7ED9\u4E24\u8005\u65F6\u4F18\u5148 value\u3002",
1260
+ mcp: { description: "Choose a <select> option by value or label (value wins if both given)." },
1228
1261
  input_schema: {
1229
1262
  type: "object",
1230
1263
  properties: {
@@ -1239,6 +1272,7 @@ var TOOL_DEFS = [
1239
1272
  {
1240
1273
  name: "submitForm",
1241
1274
  description: "[CONFIRM\xB7DANGER] \u63D0\u4EA4 <form>\u3002\u4F1A\u89E6\u53D1\u670D\u52A1\u7AEF\u52A8\u4F5C\uFF08\u4E0B\u5355\u3001\u7559\u8A00\u7B49\uFF09\uFF0C\u7528\u6237\u5FC5\u987B\u5BA1\u9605\u3002\n\u8C03\u7528\u524D\u5EFA\u8BAE\u5148\u7528 askUser \u8BA9\u7528\u6237\u6700\u7EC8\u786E\u8BA4\u3002",
1275
+ mcp: { description: "Submit a <form> (dangerous: triggers server-side actions, reviewed)." },
1242
1276
  input_schema: {
1243
1277
  type: "object",
1244
1278
  properties: {
@@ -1251,6 +1285,7 @@ var TOOL_DEFS = [
1251
1285
  {
1252
1286
  name: "hover",
1253
1287
  description: "[ACT] \u628A\u9F20\u6807\u60AC\u505C\u5728\u5143\u7D20\u4E0A\uFF08\u89E6\u53D1 mouseenter / mouseover / mousemove\uFF09\u3002",
1288
+ mcp: { description: "Hover an element (mouseenter/mouseover/mousemove)." },
1254
1289
  input_schema: {
1255
1290
  type: "object",
1256
1291
  properties: {
@@ -1263,6 +1298,7 @@ var TOOL_DEFS = [
1263
1298
  {
1264
1299
  name: "focus",
1265
1300
  description: "[ACT] \u628A\u7126\u70B9\u7ED9\u67D0\u5143\u7D20\uFF08\u89E6\u53D1 focus / focusin\uFF09\u3002",
1301
+ mcp: { description: "Focus an element (focus/focusin)." },
1266
1302
  input_schema: {
1267
1303
  type: "object",
1268
1304
  properties: {
@@ -1275,6 +1311,7 @@ var TOOL_DEFS = [
1275
1311
  {
1276
1312
  name: "uploadFile",
1277
1313
  description: "[CONFIRM\xB7DANGER] \u628A\u540E\u7AEF\u4EE3\u7406\u62C9\u5230\u7684\u6587\u4EF6\u586B\u5230 <input type=file>\u3002\u67D0\u4E9B\u7AD9\u70B9\u4F1A\u62D2\u7EDD\u5408\u6210 File\u3002",
1314
+ mcp: { description: "Fetch a URL through the proxy and put it into an <input type=file> (dangerous, reviewed). Some sites reject synthetic files." },
1278
1315
  input_schema: {
1279
1316
  type: "object",
1280
1317
  properties: {
@@ -1290,6 +1327,7 @@ var TOOL_DEFS = [
1290
1327
  {
1291
1328
  name: "getValue",
1292
1329
  description: "[FAST] \u8BFB input/select/textarea/contenteditable \u7684\u5F53\u524D\u503C\u3002",
1330
+ mcp: { description: "Current value of an input/select/textarea/contenteditable." },
1293
1331
  input_schema: {
1294
1332
  type: "object",
1295
1333
  properties: {
@@ -1302,6 +1340,7 @@ var TOOL_DEFS = [
1302
1340
  {
1303
1341
  name: "extractFormState",
1304
1342
  description: "[FAST\xB7USE BEFORE FILL] \u628A <form> \u5185\u6240\u6709\u53EF\u586B\u5B57\u6BB5\u8BFB\u6210 {name: value} \u5BF9\u8C61\uFF08radio \u53D6\u9009\u4E2D\u503C\uFF1Bcheckbox \u591A\u9009\u53D6\u6570\u7EC4\uFF09\u3002\n\u586B\u8868\u524D\u5148\u8C03\u4E00\u6B21\uFF0C\u80FD\u7701\u4E0B\u5927\u91CF\u76F2\u586B\u3002",
1343
+ mcp: { description: "All fillable fields of a <form> as {name: value}. Call before filling to avoid blind fills.", params: { form: "CSS selector of the form; defaults to the first form" } },
1305
1344
  input_schema: {
1306
1345
  type: "object",
1307
1346
  properties: {
@@ -1313,6 +1352,10 @@ var TOOL_DEFS = [
1313
1352
  {
1314
1353
  name: "runJS",
1315
1354
  description: "[LAST RESORT\xB7DANGER] \u5728 MAIN world \u6CE8\u5165\u5E76\u6267\u884C async \u51FD\u6570\u4F53\uFF08receives `ctx` = bindings\uFF09\u3002\u5FC5\u987B return \u503C\u3002\n**\u4EC5\u5728\u7ED3\u6784\u5316\u5DE5\u5177\u4E0D\u591F\u7528\u65F6\u4F7F\u7528**\u2014\u2014\u4F1A\u7ECF\u8FC7\u9759\u6001\u626B\u63CF\u4E0E\u4EBA\u5DE5\u5BA1\u9605\u3002\u4E25\u683C CSP \u9875\u9762\u9700\u8981\u7528\u6237\u9884\u5148\u5728\u6269\u5C55\u8BBE\u7F6E\u4E2D\u5F00\u542F CDP/debugger\uFF1B\u8BFB\u53D6\u6837\u5F0F\u3001\u4F4D\u7F6E\u6216\u7956\u5148\u94FE\u8BF7\u4F18\u5148\u4F7F\u7528 inspectElement\u3002",
1355
+ mcp: {
1356
+ description: "Last resort: run an async function body in the page MAIN world; must return a value. Statically scanned and reviewed. Strict-CSP pages need CDP enabled in extension settings.",
1357
+ params: { source: "async function body" }
1358
+ },
1316
1359
  input_schema: {
1317
1360
  type: "object",
1318
1361
  properties: {
@@ -1327,6 +1370,7 @@ var TOOL_DEFS = [
1327
1370
  {
1328
1371
  name: "listTabs",
1329
1372
  description: "[META] \u5217\u51FA\u6240\u6709\u7A97\u53E3\u7684\u53EF\u8BBF\u95EE tab\uFF1B\u8FD4\u56DE [{tabId, windowId, url, title, attached, isCurrent}]\u3002\n\u5728\u4F60\u9700\u8981\u8BC6\u522B / \u627E\u65B0 tab \u65F6\u8C03\u7528\u3002",
1373
+ mcp: { description: "List accessible tabs across windows: [{tabId, windowId, url, title, attached, isCurrent}].", params: { windowId: "restrict to one window" } },
1330
1374
  input_schema: {
1331
1375
  type: "object",
1332
1376
  properties: {
@@ -1337,6 +1381,7 @@ var TOOL_DEFS = [
1337
1381
  {
1338
1382
  name: "openTab",
1339
1383
  description: "[META] \u6253\u5F00\u65B0 tab\uFF0C\u6210\u529F\u540E\u81EA\u52A8\u52A0\u5165\u4F1A\u8BDD attachedTabs\uFF08source=ai-open\uFF09\u3002\u8FD4\u56DE {tabId, url, title}\u3002",
1384
+ mcp: { description: "Open a new tab and attach it to the session. Returns {tabId, url, title}.", params: { active: "true = switch to it" } },
1340
1385
  input_schema: {
1341
1386
  type: "object",
1342
1387
  properties: {
@@ -1370,6 +1415,7 @@ var TOOL_DEFS = [
1370
1415
  {
1371
1416
  name: "closeTab",
1372
1417
  description: "[META] \u771F\u6B63\u5173\u95ED\u4E00\u4E2A tab\u3002**\u53EA\u80FD\u5173 attachedTabs \u91CC\u7684 tab**\uFF08\u9632\u6B62\u8BEF\u5173\u7528\u6237\u5176\u5B83\u7A97\u53E3\uFF09\u3002",
1418
+ mcp: { description: "Close the session's tab." },
1373
1419
  input_schema: {
1374
1420
  type: "object",
1375
1421
  properties: { tabId: { type: "integer" } },
@@ -1379,6 +1425,9 @@ var TOOL_DEFS = [
1379
1425
  {
1380
1426
  name: "switchToTab",
1381
1427
  description: "[META] \u628A Chrome \u524D\u53F0\u5207\u5230\u76EE\u6807 tab\u3002tabId \u5FC5\u987B\u5DF2\u5728 attachedTabs \u6216\u5F53\u524D tab\u3002",
1428
+ mcp: {
1429
+ description: "Bring the session's tab and Chrome window to the foreground. Use only when the user explicitly asks to see that tab; all other browser tools already operate on the session-bound tab without this setup step."
1430
+ },
1382
1431
  input_schema: {
1383
1432
  type: "object",
1384
1433
  properties: { tabId: { type: "integer" } },
@@ -1389,6 +1438,10 @@ var TOOL_DEFS = [
1389
1438
  {
1390
1439
  name: "screenshot",
1391
1440
  description: "[VISION] \u622A\u5F53\u524D tab \u53EF\u89C1\u533A\u57DF\u4E3A PNG\uFF08\u81EA\u52A8\u4F5C\u4E3A image block \u6CE8\u5165\u4E0B\u8F6E\uFF09\u3002\u7528\u4E8E\u89C6\u89C9\u8C03\u8BD5\u3001\u770B\u56FE\u56DE\u7B54\u3001\u6838\u5BF9 page-index \u8BC1\u636E\u3002\n\u5982\u679C\u5DF2\u6709 searchPageIndex/extractPageFields \u8FD4\u56DE\u7684 blockId/indexId\uFF0C\u4F18\u5148\u4F20 {blockId,indexId}\uFF1B\u5DE5\u5177\u4F1A\u6EDA\u52A8\u5E76\u9AD8\u4EAE\u8BE5\u5C40\u90E8\u533A\u57DF\u540E\u622A\u56FE\u3002\u4E5F\u53EF\u4F20 selector\u3002",
1441
+ mcp: {
1442
+ description: "PNG/JPEG returned as an image block. With opt-in CDP enabled, captures viewport/fullPage/selector in the background without activating or scrolling the tab; compatibility mode may temporarily activate the tab.",
1443
+ params: { blockId: "page-index blockId to frame", indexId: "indexId that produced blockId", scale: "0.1\u20131; smaller saves tokens" }
1444
+ },
1392
1445
  input_schema: {
1393
1446
  type: "object",
1394
1447
  properties: {
@@ -1436,6 +1489,7 @@ var TOOL_DEFS = [
1436
1489
  {
1437
1490
  name: "searchBookmarks",
1438
1491
  description: "[META] \u641C\u7D22\u6D4F\u89C8\u5668\u4E66\u7B7E\uFF08chrome.bookmarks.search\uFF09\u3002\u8FD4\u56DE [{id, title, url}]\u3002\n\n\u793A\u4F8B\uFF1A{ query: 'react', limit: 20 }",
1492
+ mcp: { description: "Search browser bookmarks: [{id, title, url}]." },
1439
1493
  input_schema: {
1440
1494
  type: "object",
1441
1495
  properties: {
@@ -1448,6 +1502,7 @@ var TOOL_DEFS = [
1448
1502
  {
1449
1503
  name: "searchHistory",
1450
1504
  description: "[META] \u641C\u7D22\u6D4F\u89C8\u5668\u5386\u53F2\u3002daysBack \u9ED8\u8BA4 7\u3002\u8FD4\u56DE [{url, title, lastVisitTime, visitCount}]\u3002",
1505
+ mcp: { description: "Search browser history (daysBack default 7): [{url, title, lastVisitTime, visitCount}]." },
1451
1506
  input_schema: {
1452
1507
  type: "object",
1453
1508
  properties: {
@@ -1461,6 +1516,7 @@ var TOOL_DEFS = [
1461
1516
  {
1462
1517
  name: "downloadImage",
1463
1518
  description: "[ACT] \u628A\u4E00\u4E2A URL \u4E0B\u8F7D\u5230\u672C\u5730\uFF08Chrome Downloads\uFF09\u3002\u8FD4\u56DE {downloadId, filename}\u3002caution \u7EA7\u3002",
1519
+ mcp: { description: "Download a URL to Chrome Downloads. Returns {downloadId, filename}.", params: { filename: "suggested file name with extension" } },
1464
1520
  input_schema: {
1465
1521
  type: "object",
1466
1522
  properties: {
@@ -1473,6 +1529,10 @@ var TOOL_DEFS = [
1473
1529
  {
1474
1530
  name: "downloadSpreadsheet",
1475
1531
  description: "[ACT] \u751F\u6210\u5E76\u4E0B\u8F7D\u771F\u6B63\u7684 .xlsx Excel \u6587\u4EF6\uFF08Chrome Downloads\uFF09\u3002\u9002\u5408\u628A\u91C7\u96C6/\u62BD\u53D6\u7ED3\u679C\u5BFC\u51FA\u4E3A\u8868\u683C\u3002\u652F\u6301\u591A\u4E2A sheet\uFF1Brows \u53EF\u4EE5\u662F\u4E8C\u7EF4\u6570\u7EC4\uFF0C\u4E5F\u53EF\u4EE5\u662F\u5BF9\u8C61\u6570\u7EC4\u3002\u5BF9\u8C61\u6570\u7EC4\u53EF\u914D columns \u63A7\u5236\u5217\u987A\u5E8F\u548C\u8868\u5934\u3002\u8FD4\u56DE {downloadId, filename, sheets, rows, bytes}\u3002caution \u7EA7\u3002",
1532
+ mcp: {
1533
+ description: "Generate and download a real .xlsx. sheets[].rows may be 2-D arrays or objects; columns orders object keys and sets headers.",
1534
+ params: { filename: "suggested name; .xlsx optional" }
1535
+ },
1476
1536
  input_schema: {
1477
1537
  type: "object",
1478
1538
  properties: {
@@ -1518,6 +1578,7 @@ var TOOL_DEFS = [
1518
1578
  {
1519
1579
  name: "takeSnapshot",
1520
1580
  description: "[FIRST\xB7UID] \u6293\u53D6\u9875\u9762 accessibility snapshot\uFF1A\u8FD4\u56DE [{uid, role, name, tag, text, bounds}]\u3002\nUID \u5728\u672C\u6B21 snapshot \u5185\u7A33\u5B9A\uFF0C\u540E\u7EED clickByUid / fillByUid \u5F15\u7528\uFF1B\u6BD4 selector \u5065\u58EE\uFF0C\u4E0D\u6015 class \u6539\u540D\u3002\n\u6BCF\u6B21\u5927\u52A8\u4F5C\u524D\u5237\u65B0\u4E00\u6B21\u3002snapshot \u9ED8\u8BA4\u53EA\u8FD4\u56DE\u4EA4\u4E92\u5143\u7D20\uFF08button / link / input / textarea / select / [role] / [data-testid]\uFF09\u3002",
1581
+ mcp: { description: "Accessibility snapshot: [{uid, role, name, tag, text, bounds}]. uids feed clickByUid/fillByUid. Refresh before each major action.", params: { includeAll: "true = every element, not just interactive" } },
1521
1582
  input_schema: {
1522
1583
  type: "object",
1523
1584
  properties: {
@@ -1529,6 +1590,7 @@ var TOOL_DEFS = [
1529
1590
  {
1530
1591
  name: "clickByUid",
1531
1592
  description: "[ACT\xB7UID] \u7528 takeSnapshot \u8FD4\u56DE\u7684 uid \u70B9\u51FB\u5143\u7D20\u3002\u6BD4 selector \u7248\u7A33\u5B9A\u3002",
1593
+ mcp: { description: "Click an element by uid from takeSnapshot/findElements. More robust than a selector." },
1532
1594
  input_schema: {
1533
1595
  type: "object",
1534
1596
  properties: {
@@ -1541,6 +1603,7 @@ var TOOL_DEFS = [
1541
1603
  {
1542
1604
  name: "fillByUid",
1543
1605
  description: "[ACT\xB7UID] \u7528 takeSnapshot \u8FD4\u56DE\u7684 uid \u586B\u503C\uFF08input/textarea/contenteditable\uFF09\u3002",
1606
+ mcp: { description: "Set a value on an element by uid from takeSnapshot." },
1544
1607
  input_schema: {
1545
1608
  type: "object",
1546
1609
  properties: {
@@ -1555,6 +1618,7 @@ var TOOL_DEFS = [
1555
1618
  {
1556
1619
  name: "highlightElement",
1557
1620
  description: "[VISUAL] \u7ED9\u9875\u9762\u67D0\u5143\u7D20\u52A0\u7EA2\u8272\u865A\u7EBF\u6846\uFF08\u9ED8\u8BA4 3s \u81EA\u52A8\u6D88\u5931\uFF09\uFF0C\u8BA9\u7528\u6237\u770B\u6E05\u4F60\u8BF4\u7684\u662F\u54EA\u4E2A\u3002\u4EC5\u89C6\u89C9\uFF0C\u4E0D\u6539 DOM\u3002\n\u53EF\u7528 selector \u6216 uid \u4EFB\u4E00\u79CD\u3002",
1621
+ mcp: { description: "Draw a temporary red outline around an element (visual only)." },
1558
1622
  input_schema: {
1559
1623
  type: "object",
1560
1624
  properties: {
@@ -1568,6 +1632,7 @@ var TOOL_DEFS = [
1568
1632
  {
1569
1633
  name: "highlightText",
1570
1634
  description: "[VISUAL] \u5728\u9875\u9762\u6587\u672C\u91CC\u9AD8\u4EAE\u67D0\u6BB5\u6587\u5B57\uFF08\u9EC4\u8272\u80CC\u666F\uFF0C3s \u540E\u8FD8\u539F\uFF09\u3002\u4EC5\u627E\u5230\u7B2C\u4E00\u6B21\u51FA\u73B0\u7684\u4F4D\u7F6E\u3002",
1635
+ mcp: { description: "Temporarily highlight the first occurrence of a text string (visual only)." },
1571
1636
  input_schema: {
1572
1637
  type: "object",
1573
1638
  properties: {
@@ -1581,6 +1646,7 @@ var TOOL_DEFS = [
1581
1646
  {
1582
1647
  name: "fillForm",
1583
1648
  description: "[BATCH\xB7ACT] \u4E00\u6B21\u6027\u586B\u591A\u4E2A\u5B57\u6BB5\u3002\u6BCF\u9879\u5199 selector + value \u6216 uid + value\u3002\u8FD4\u56DE {filled: N, failed: [{at, error}]}\u3002\n\u6BD4\u5FAA\u73AF\u8C03 fillInput \u5FEB\u5F97\u591A\uFF0C\u4E5F\u7701 round-trip\u3002\n\n\u793A\u4F8B\uFF1A\n{ fields: [\n { selector: 'input[name=name]', value: '\u5F20\u4E09' },\n { selector: 'input[name=phone]', value: '13800000000' },\n { uid: 'el_5', value: 'mushroom' }\n] }",
1649
+ mcp: { description: "Fill several fields in one call; each item is {selector|uid, value}. Returns {filled, failed}." },
1584
1650
  input_schema: {
1585
1651
  type: "object",
1586
1652
  properties: {
@@ -1605,6 +1671,7 @@ var TOOL_DEFS = [
1605
1671
  {
1606
1672
  name: "navigate",
1607
1673
  description: "[ACT] \u9875\u9762\u5BFC\u822A\uFF1A\u540E\u9000 / \u524D\u8FDB / \u91CD\u8F7D / \u8DF3\u8F6C\u3002**\u4F18\u5148**\u7528\u672C\u5DE5\u5177\u800C\u4E0D\u662F runJS('location.href = ...')\u3002\n\u793A\u4F8B\uFF1A\n- \u540E\u9000\u4E00\u9875\uFF1A{ action: 'back' }\n- \u8DF3\u5230\u65B0 URL\uFF1A{ action: 'goto', url: 'https://example.com/page' }",
1674
+ mcp: { description: "Navigate: action = back | forward | reload | goto (with url). Prefer this over runJS location changes.", params: { url: "goto only; http/https/file/ftp" } },
1608
1675
  input_schema: {
1609
1676
  type: "object",
1610
1677
  properties: {
@@ -1618,6 +1685,7 @@ var TOOL_DEFS = [
1618
1685
  {
1619
1686
  name: "getPageInfo",
1620
1687
  description: "[FAST\xB7READ] \u8BFB\u5F53\u524D\u9875\u57FA\u672C\u4FE1\u606F\uFF1AURL / title / hostname / \u8BED\u8A00 / OpenGraph meta\u3002\n\u591A\u9875\u5BF9\u8BDD\u4E2D\u300C\u6211\u5728\u54EA\u4E2A\u9875\u9762\u300D\u7684\u9996\u9009\uFF1B\u6BD4 snapshotDOM \u4FBF\u5B9C\u5F97\u591A\u3002",
1688
+ mcp: { description: "URL, title, hostname, language and OpenGraph meta of the current page. Cheapest 'where am I' check." },
1621
1689
  input_schema: {
1622
1690
  type: "object",
1623
1691
  properties: {
@@ -1628,6 +1696,7 @@ var TOOL_DEFS = [
1628
1696
  {
1629
1697
  name: "pressKey",
1630
1698
  description: "[ACT] \u6A21\u62DF\u952E\u76D8\u4E8B\u4EF6\uFF08keydown + \u53EF\u6253\u5370\u5B57\u7B26 keypress + keyup\uFF09\u3002\n\u5E38\u7528\uFF1AEnter \u63D0\u4EA4\u65E0 form \u7684\u641C\u7D22\u6846 / Escape \u5173 modal / Tab \u5207\u7126\u70B9\u3002key \u7528 KeyboardEvent.key \u503C\u3002\n\u672C\u5DE5\u5177**\u4E0D**\u6539 input \u503C\u2014\u2014\u586B\u503C\u4ECD\u8D70 fillInput / fillByUid\u3002\n\u793A\u4F8B\uFF1A\n- \u63D0\u4EA4\u641C\u7D22\uFF1A{ selector: 'input[name=q]', key: 'Enter' }\n- \u5173 modal\uFF1A{ key: 'Escape' }",
1699
+ mcp: { description: "Dispatch a key (keydown/keypress/keyup) to a selector or the active element; e.g. Enter, Escape, Tab. Does not change input values.", params: { key: "KeyboardEvent.key value" } },
1631
1700
  input_schema: {
1632
1701
  type: "object",
1633
1702
  properties: {
@@ -1644,6 +1713,7 @@ var TOOL_DEFS = [
1644
1713
  {
1645
1714
  name: "writeStorage",
1646
1715
  description: "[DANGER] \u5199 localStorage \u6216 sessionStorage\u3002\u6539\u7AD9\u70B9\u72B6\u6001\uFF0C\u9700\u8981\u5BA1\u9605\u3002",
1716
+ mcp: { description: "Write one key to localStorage or sessionStorage (dangerous, reviewed).", params: { value: "string; JSON.stringify non-strings yourself" } },
1647
1717
  input_schema: {
1648
1718
  type: "object",
1649
1719
  properties: {
@@ -1662,6 +1732,7 @@ var TOOL_DEFS = [
1662
1732
  {
1663
1733
  name: "consoleMessages",
1664
1734
  description: "[OBSERVE] \u8BFB\u53D6\u672C\u9875 console \u65E5\u5FD7\u4E0E\u672A\u6355\u83B7\u9519\u8BEF\u3002\u8FD4\u56DE\u91CC\u5E26 backend \u5B57\u6BB5\uFF1Amain-world \u6863\u770B\u4E0D\u5230\u811A\u672C\u6CE8\u5165\u4E4B\u524D\u7684\u6D88\u606F\u548C\u6D4F\u89C8\u5668\u7EA7 CORS/CSP \u62A5\u9519\uFF0Ccdp \u6863\u80FD\u770B\u5230\u3002\n\u793A\u4F8B\uFF1A\u53EA\u770B\u62A5\u9519 { level: 'error', limit: 50 }\uFF1B\u589E\u91CF\u8F6E\u8BE2 { sinceId: \u4E0A\u6B21\u8FD4\u56DE\u7684\u6700\u5927 id }",
1735
+ mcp: { description: "Console output and uncaught errors of the page. Filter by level; sinceId for incremental reads.", params: { sinceId: "only messages with id greater than this" } },
1665
1736
  input_schema: {
1666
1737
  type: "object",
1667
1738
  properties: {
@@ -1675,6 +1746,7 @@ var TOOL_DEFS = [
1675
1746
  {
1676
1747
  name: "networkRequests",
1677
1748
  description: "[OBSERVE] \u5217\u51FA\u672C\u9875\u53D1\u51FA\u7684\u7F51\u7EDC\u8BF7\u6C42\u6458\u8981\uFF08method / url / status / \u8017\u65F6\uFF09\u3002\u9ED8\u8BA4\u9690\u85CF PerformanceObserver \u89C2\u6D4B\u5230\u7684\u9759\u6001\u8D44\u6E90\uFF0CincludeStatic:true \u624D\u5E26\u4E0A\u3002\n\u8981\u770B headers \u6216 body \u7528 networkRequestDetail\u3002",
1749
+ mcp: { description: "Summaries of the page's network requests (method/url/status/duration). Static assets hidden unless includeStatic.", params: { urlPattern: "substring, or /regex/ with optional i flag" } },
1678
1750
  input_schema: {
1679
1751
  type: "object",
1680
1752
  properties: {
@@ -1691,6 +1763,10 @@ var TOOL_DEFS = [
1691
1763
  {
1692
1764
  name: "networkRequestDetail",
1693
1765
  description: "[OBSERVE] \u8BFB\u53D6\u5355\u6761\u8BF7\u6C42\u7684 headers \u4E0E body\u3002**dangerous**\uFF1A\u54CD\u5E94\u5934\u91CC\u53EF\u80FD\u6709 Authorization / Set-Cookie / token\u3002\nmain-world \u6863\u9700\u5148 recorderConfig({bodies:true}) \u624D\u4F1A\u8BB0\u5F55 body\uFF0C\u4E14\u53EA\u8BB0 256KB \u4EE5\u5185\u7684\u6587\u672C\u7C7B\u54CD\u5E94\uFF1Bcdp \u6863\u76F4\u63A5\u53EF\u53D6\u3002",
1766
+ mcp: {
1767
+ description: "Headers and bodies of one request from networkRequests (dangerous: may expose tokens). Bodies need recorderConfig({bodies:true}) unless CDP is on.",
1768
+ params: { id: "id from networkRequests", part: "return only this part" }
1769
+ },
1694
1770
  input_schema: {
1695
1771
  type: "object",
1696
1772
  properties: {
@@ -1708,6 +1784,7 @@ var TOOL_DEFS = [
1708
1784
  {
1709
1785
  name: "handleDialog",
1710
1786
  description: "[OBSERVE] \u8BBE\u5B9A alert / confirm / prompt \u7684\u5E94\u7B54\u7B56\u7565\uFF0C\u5E76\u8FD4\u56DE\u5DF2\u8BB0\u5F55\u7684\u5F39\u7A97\u3002\nmain-world \u6863\u4E0B\u5F39\u7A97\u662F\u540C\u6B65\u7684\uFF0C\u65E0\u6CD5\u6302\u8D77\u7B49\u4F60\u51B3\u5B9A\uFF0C\u6240\u4EE5\u8FD9\u91CC\u8BBE\u7684\u662F**\u9884\u5148\u7B56\u7565**\uFF1A\u8C03\u7528\u4E4B\u540E\u53D1\u751F\u7684\u5F39\u7A97\u6309\u6B64\u5904\u7406\u3002cdp \u6863\u4E0B\u5F39\u7A97\u771F\u6302\u8D77\uFF0C\u672C\u8C03\u7528\u4F1A\u7ACB\u5373\u5E94\u7B54\u5F53\u524D\u6302\u8D77\u7684\u5F39\u7A97\u3002\n\u672A\u8C03\u7528\u8FC7\u672C\u5DE5\u5177\u65F6\u5F39\u7A97\u4FDD\u6301\u539F\u751F\u884C\u4E3A\uFF08passthrough\uFF09\uFF0C\u9875\u9762\u8868\u73B0\u4E0E\u6CA1\u88C5\u6269\u5C55\u4E00\u81F4\u3002",
1787
+ mcp: { description: "Set the accept/dismiss policy for alert/confirm/prompt and return recorded dialogs. Pre-set policy in main-world mode; answers a pending dialog under CDP.", params: { promptText: "text for prompt dialogs" } },
1711
1788
  input_schema: {
1712
1789
  type: "object",
1713
1790
  properties: {
@@ -1722,6 +1799,7 @@ var TOOL_DEFS = [
1722
1799
  {
1723
1800
  name: "recorderConfig",
1724
1801
  description: "[OBSERVE] \u5F00\u5173\u9875\u9762\u4E8B\u4EF6\u5F55\u5236\u3002bodies:true \u6253\u5F00\u8BF7\u6C42/\u54CD\u5E94 body \u6355\u83B7\uFF08\u9ED8\u8BA4\u5173\uFF0C\u6709\u5185\u5B58\u4EE3\u4EF7\uFF09\uFF1Bdialog:true \u8BA9\u5F39\u7A97\u8D70\u7B56\u7565\u800C\u4E0D\u662F\u539F\u751F\u884C\u4E3A\uFF1Bclear \u6E05\u7A7A\u7F13\u51B2\u3002\u7701\u7565\u7684\u5B57\u6BB5\u4FDD\u6301\u539F\u6837\u3002",
1802
+ mcp: { description: "Toggle console/network/dialog recording, arm request/response body capture, clear buffers." },
1725
1803
  input_schema: {
1726
1804
  type: "object",
1727
1805
  properties: {
@@ -1740,16 +1818,19 @@ var TOOL_DEFS = [
1740
1818
  {
1741
1819
  name: "navigateBack",
1742
1820
  description: "[FLOW] \u540E\u9000\u4E00\u9875\u3002\u5DF2\u5728\u5386\u53F2\u8D77\u70B9\u65F6\u8FD4\u56DE { ok:false, reason }\uFF0C\u4E0D\u629B\u9519\u3002",
1821
+ mcp: { description: "Go back one page; returns {ok:false, reason} at history start." },
1743
1822
  input_schema: { type: "object", properties: { tabId: TAB_ID_FIELD } }
1744
1823
  },
1745
1824
  {
1746
1825
  name: "navigateForward",
1747
1826
  description: "[FLOW] \u524D\u8FDB\u4E00\u9875\u3002\u5DF2\u5728\u5386\u53F2\u672B\u5C3E\u65F6\u8FD4\u56DE { ok:false, reason }\uFF0C\u4E0D\u629B\u9519\u3002",
1827
+ mcp: { description: "Go forward one page; returns {ok:false, reason} at history end." },
1748
1828
  input_schema: { type: "object", properties: { tabId: TAB_ID_FIELD } }
1749
1829
  },
1750
1830
  {
1751
1831
  name: "resize",
1752
1832
  description: "[FLOW] \u628A\u89C6\u53E3\u8C03\u6574\u5230\u6307\u5B9A\u5C3A\u5BF8\u3002main-world \u6863\u91CF\u53D6 outerWidth-innerWidth \u53CD\u63A8\u6D4F\u89C8\u5668\u8FB9\u6846\u540E\u6539\u7A97\u53E3\u5916\u6846\uFF0C\u89C6\u53E3\u7CBE\u786E\u4F46**\u7528\u6237\u7684\u7A97\u53E3\u4F1A\u771F\u7684\u53D8\u5927\u5C0F**\uFF1Bcdp \u6863\u7528 Emulation \u8986\u76D6\u8BBE\u5907\u5C3A\u5BF8\uFF0C\u4E0D\u52A8\u771F\u5B9E\u7A97\u53E3\u3002",
1833
+ mcp: { description: "Resize the viewport to width\xD7height." },
1753
1834
  input_schema: {
1754
1835
  type: "object",
1755
1836
  properties: {
@@ -1763,6 +1844,7 @@ var TOOL_DEFS = [
1763
1844
  {
1764
1845
  name: "drag",
1765
1846
  description: "[ACT] \u628A\u4E00\u4E2A\u5143\u7D20\u62D6\u5230\u53E6\u4E00\u4E2A\u5143\u7D20\u4E0A\u3002\u540C\u65F6\u53D1 pointer \u5E8F\u5217\u548C HTML5 DragEvent\uFF08\u5171\u7528\u4E00\u4E2A DataTransfer\uFF09\uFF0C\u517C\u5BB9\u539F\u751F\u62D6\u653E\u548C\u81EA\u5B9A\u4E49 pointer \u62D6\u653E\u3002\n\u8FD4\u56DE { consumed: { pointer, html5 } } \u8BF4\u660E\u76EE\u6807\u5B9E\u9645\u6D88\u8D39\u4E86\u54EA\u4E00\u7C7B\u4E8B\u4EF6\u2014\u2014\u90FD\u4E3A false \u8BF4\u660E\u8FD9\u4E2A\u62D6\u653E\u5B9E\u73B0\u4E24\u6761\u8DEF\u90FD\u4E0D\u5403\u3002",
1847
+ mcp: { description: "Drag one element onto another by selector or uid. Emits pointer and HTML5 drag events; returns which the target consumed." },
1766
1848
  input_schema: {
1767
1849
  type: "object",
1768
1850
  properties: {
@@ -1777,6 +1859,7 @@ var TOOL_DEFS = [
1777
1859
  {
1778
1860
  name: "drop",
1779
1861
  description: "[ACT] \u6A21\u62DF\u4ECE\u6D4F\u89C8\u5668\u5916\u90E8\u628A\u6587\u4EF6\u6216\u6570\u636E\u62D6\u653E\u5230\u9875\u9762\u5143\u7D20\u4E0A\u3002\u5E26 files \u65F6\u7B49\u540C\u4E0A\u4F20\uFF08dangerous\uFF09\u3002\n\u793A\u4F8B\uFF1A{ selector:'#dropzone', files:[{ name:'a.csv', mimeType:'text/csv', base64:'...' }] }",
1862
+ mcp: { description: "Simulate dropping external files/data onto an element. files present = upload (dangerous, reviewed).", params: { data: "MIME \u2192 string map" } },
1780
1863
  input_schema: {
1781
1864
  type: "object",
1782
1865
  properties: {
@@ -1802,6 +1885,7 @@ var TOOL_DEFS = [
1802
1885
  {
1803
1886
  name: "findElements",
1804
1887
  description: "[READ] \u6309\u6587\u672C\u6216\u6B63\u5219\u5728\u53EF\u4EA4\u4E92\u5143\u7D20\u91CC\u627E\u76EE\u6807\uFF0C\u8FD4\u56DE uid / role / name / bounds\u3002\u4E0D\u9700\u8981\u5148 createPageIndex\u3002\n\u62FF\u5230 uid \u540E\u7528 clickByUid / fillByUid \u64CD\u4F5C\u6700\u7A33\u3002",
1888
+ mcp: { description: "Find interactive elements by text or regex; returns uid/role/name/bounds without needing a page index.", params: { text: "case-insensitive substring", regex: "regex; alternative to text" } },
1805
1889
  input_schema: {
1806
1890
  type: "object",
1807
1891
  properties: {
@@ -1816,14 +1900,21 @@ var TOOL_DEFS = [
1816
1900
 
1817
1901
  // src/tool-gen.ts
1818
1902
  var BLOCKED_TOOLS = /* @__PURE__ */ new Set(["askUser", "attachTab", "detachTab"]);
1819
- var PARITY_TOOLS = [
1820
- // page state and search
1903
+ var CORE_TOOLS = [
1904
+ // page state
1821
1905
  "takeSnapshot",
1822
1906
  "findElements",
1823
1907
  "getPageInfo",
1908
+ "extractText",
1909
+ // page index
1910
+ "createPageIndex",
1911
+ "searchPageIndex",
1912
+ "readPageBlock",
1913
+ "extractPageFields",
1824
1914
  // interaction
1825
1915
  "clickByUid",
1826
1916
  "click",
1917
+ "fillByUid",
1827
1918
  "fillInput",
1828
1919
  "fillForm",
1829
1920
  "selectOption",
@@ -1835,10 +1926,11 @@ var PARITY_TOOLS = [
1835
1926
  "uploadFile",
1836
1927
  // navigation and tabs
1837
1928
  "navigate",
1838
- "navigateBack",
1839
1929
  "listTabs",
1840
1930
  "openTab",
1931
+ "closeTab",
1841
1932
  "resize",
1933
+ "scroll",
1842
1934
  // observation
1843
1935
  "screenshot",
1844
1936
  "waitFor",
@@ -1848,40 +1940,199 @@ var PARITY_TOOLS = [
1848
1940
  ];
1849
1941
  var IMAGE_RESULT_TOOLS = /* @__PURE__ */ new Set(["screenshot"]);
1850
1942
  var JS_STEP_TOOLS = /* @__PURE__ */ new Set(["runJS"]);
1851
- function rebuildSchema(src) {
1943
+ var SESSION_ID_FIELD = {
1944
+ type: "string",
1945
+ description: "Session id from open_session"
1946
+ };
1947
+ function stripDescriptions(value) {
1948
+ if (Array.isArray(value)) {
1949
+ return value.map(stripDescriptions);
1950
+ }
1951
+ if (value !== null && typeof value === "object") {
1952
+ const out = {};
1953
+ for (const [key, v] of Object.entries(value)) {
1954
+ if (key === "description") continue;
1955
+ out[key] = stripDescriptions(v);
1956
+ }
1957
+ return out;
1958
+ }
1959
+ return value;
1960
+ }
1961
+ function rebuildSchema(src, params) {
1852
1962
  const s = src;
1853
- const properties = { ...s.properties ?? {} };
1854
- delete properties.tabId;
1855
- properties.session_id = {
1856
- type: "string",
1857
- description: "open_session \u8FD4\u56DE\u7684\u4F1A\u8BDD id\uFF08\u51B3\u5B9A\u76EE\u6807 worker \u4E0E tab\uFF09"
1858
- };
1963
+ const properties = {};
1964
+ for (const [key, prop] of Object.entries(s.properties ?? {})) {
1965
+ if (key === "tabId") continue;
1966
+ const stripped = stripDescriptions(prop);
1967
+ properties[key] = params[key] ? { ...stripped, description: params[key] } : stripped;
1968
+ }
1969
+ properties.session_id = SESSION_ID_FIELD;
1859
1970
  const required = [.../* @__PURE__ */ new Set([...(s.required ?? []).filter((r) => r !== "tabId"), "session_id"])];
1860
1971
  return { type: "object", properties, required };
1861
1972
  }
1862
- function generateBrowserTools(mode = "full") {
1863
- const parity = new Set(PARITY_TOOLS);
1864
- return TOOL_DEFS.filter((t) => {
1865
- if (BLOCKED_TOOLS.has(t.name)) return false;
1866
- return mode === "full" || parity.has(t.name);
1867
- }).map((t) => ({
1973
+ function fromDef(t) {
1974
+ return {
1868
1975
  name: `browser_${t.name}`,
1869
1976
  builtinTool: t.name,
1870
- description: t.description,
1977
+ builtinTools: [t.name],
1978
+ description: t.mcp?.description ?? t.description,
1871
1979
  resultKind: IMAGE_RESULT_TOOLS.has(t.name) ? "image" : "json",
1872
1980
  stepKind: JS_STEP_TOOLS.has(t.name) ? "js" : "tool",
1873
- inputSchema: rebuildSchema(t.input_schema)
1874
- }));
1981
+ inputSchema: rebuildSchema(t.input_schema, t.mcp?.params ?? {})
1982
+ };
1983
+ }
1984
+ var MERGED_AWAY_TOOLS = /* @__PURE__ */ new Set([
1985
+ "navigateBack",
1986
+ "navigateForward",
1987
+ "highlightElement",
1988
+ "highlightText",
1989
+ "readStorage",
1990
+ "writeStorage"
1991
+ ]);
1992
+ function schemaWithSession(properties, required) {
1993
+ return { type: "object", properties: { ...properties, session_id: SESSION_ID_FIELD }, required: [...required, "session_id"] };
1994
+ }
1995
+ var STORE_ENUM = { type: "string", enum: ["local", "session"] };
1996
+ var MERGED_TOOLS = [
1997
+ {
1998
+ name: "browser_highlight",
1999
+ builtinTool: "highlightElement",
2000
+ builtinTools: ["highlightElement", "highlightText"],
2001
+ description: "Temporarily outline an element (selector or uid) or highlight the first occurrence of text. Visual only; exactly one of text/selector/uid.",
2002
+ resultKind: "json",
2003
+ stepKind: "tool",
2004
+ inputSchema: schemaWithSession(
2005
+ {
2006
+ text: { type: "string" },
2007
+ selector: { type: "string" },
2008
+ uid: { type: "string" },
2009
+ ms: { type: "integer", default: 3e3 }
2010
+ },
2011
+ []
2012
+ ),
2013
+ resolve(args) {
2014
+ const given = ["text", "selector", "uid"].filter((k) => args[k] != null && args[k] !== "");
2015
+ if (given.length !== 1) {
2016
+ throw new Error("InvalidArgs: browser_highlight needs exactly one of text / selector / uid");
2017
+ }
2018
+ const { text, selector, uid, ms } = args;
2019
+ const withMs = (o) => ms == null ? o : { ...o, ms };
2020
+ if (given[0] === "text") return { builtinTool: "highlightText", args: withMs({ text }) };
2021
+ return { builtinTool: "highlightElement", args: withMs(selector != null ? { selector } : { uid }) };
2022
+ }
2023
+ },
2024
+ {
2025
+ name: "browser_storage",
2026
+ builtinTool: "readStorage",
2027
+ builtinTools: ["readStorage", "writeStorage"],
2028
+ description: "Read (op=get) or write (op=set, needs value) one key in localStorage or sessionStorage. Dangerous, reviewed.",
2029
+ resultKind: "json",
2030
+ stepKind: "tool",
2031
+ inputSchema: schemaWithSession(
2032
+ {
2033
+ op: { type: "string", enum: ["get", "set"] },
2034
+ store: STORE_ENUM,
2035
+ key: { type: "string" },
2036
+ value: { type: "string", description: "set only; JSON.stringify non-strings yourself" }
2037
+ },
2038
+ ["op", "store", "key"]
2039
+ ),
2040
+ resolve(args) {
2041
+ const { op, store, key, value } = args;
2042
+ if (op === "get") return { builtinTool: "readStorage", args: { store, key } };
2043
+ if (op === "set") {
2044
+ if (typeof value !== "string") throw new Error("InvalidArgs: browser_storage op=set requires a string value");
2045
+ return { builtinTool: "writeStorage", args: { store, key, value } };
2046
+ }
2047
+ throw new Error(`InvalidArgs: browser_storage op must be "get" or "set", got ${JSON.stringify(op)}`);
2048
+ }
2049
+ }
2050
+ ];
2051
+ function generateBrowserTools(mode = "core") {
2052
+ const core = new Set(CORE_TOOLS);
2053
+ const plain = TOOL_DEFS.filter((t) => {
2054
+ if (BLOCKED_TOOLS.has(t.name) || MERGED_AWAY_TOOLS.has(t.name)) return false;
2055
+ return mode === "full" || core.has(t.name);
2056
+ }).map(fromDef);
2057
+ return mode === "full" ? [...plain, ...MERGED_TOOLS] : plain;
1875
2058
  }
1876
2059
  function readToolMode(env) {
1877
2060
  const raw = env.ATWEBPILOT_MCP_TOOLS;
1878
- if (raw == null || raw === "") return "full";
1879
- if (raw === "full" || raw === "parity") return raw;
2061
+ if (raw == null || raw === "") return "core";
2062
+ if (raw === "core" || raw === "full") return raw;
1880
2063
  process.stderr.write(
1881
- `[atwebpilot-mcp] ATWEBPILOT_MCP_TOOLS="${raw}" is not recognised; using "full"
2064
+ `[atwebpilot-mcp] ATWEBPILOT_MCP_TOOLS="${raw}" is not recognised; using "core"
1882
2065
  `
1883
2066
  );
1884
- return "full";
2067
+ return "core";
2068
+ }
2069
+ var DISCOVERABLE_GROUPS = {
2070
+ export: ["browser_downloadImage", "browser_downloadSpreadsheet"],
2071
+ network: ["browser_httpRequest", "browser_networkRequestDetail", "browser_recorderConfig", "browser_handleDialog"],
2072
+ storage: ["browser_storage"],
2073
+ "browser-data": ["browser_searchBookmarks", "browser_searchHistory"],
2074
+ inspect: ["browser_inspectElement", "browser_highlight", "browser_getValue", "browser_extractFormState"],
2075
+ "legacy-dom": ["browser_snapshotDOM", "browser_querySelector", "browser_querySelectorAll", "browser_extractImages", "browser_focus"],
2076
+ form: ["browser_submitForm"],
2077
+ tabs: ["browser_switchToTab"]
2078
+ };
2079
+ var GROUP_BY_NAME = new Map(
2080
+ Object.entries(DISCOVERABLE_GROUPS).flatMap(([g, names]) => names.map((n) => [n, g]))
2081
+ );
2082
+ function groupOf(mcpName) {
2083
+ return GROUP_BY_NAME.get(mcpName);
2084
+ }
2085
+ function discoveryCatalog(all, advertised) {
2086
+ return all.filter((t) => !advertised.has(t.name)).map((t) => ({ name: t.name, group: groupOf(t.name) ?? "legacy-dom", description: t.description })).sort((a, b) => a.group < b.group ? -1 : a.group > b.group ? 1 : a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
2087
+ }
2088
+
2089
+ // src/discover-tool.ts
2090
+ var DISCOVER_TOOL = {
2091
+ name: "browser_discoverTools",
2092
+ description: "The default tool list is the core set. Call with no arguments to see the catalog of additional browser tools (export, network, storage, browser-data, inspect, legacy-dom, form). Call with enable=[names] to add them to tools/list for this server; the response includes their full schemas so you can call them immediately. Use this instead of rebuilding missing capabilities with runJS.",
2093
+ inputSchema: {
2094
+ type: "object",
2095
+ properties: {
2096
+ enable: { type: "array", items: { type: "string" }, description: "Tool names from the catalog to advertise" }
2097
+ },
2098
+ additionalProperties: false
2099
+ }
2100
+ };
2101
+ function isRunnable(t, supported) {
2102
+ return !supported || t.builtinTools.every((b) => supported.has(b));
2103
+ }
2104
+ function handleDiscover(input) {
2105
+ const { all, advertised, args, supported } = input;
2106
+ const byName = new Map(all.map((t) => [t.name, t]));
2107
+ const requested = Array.isArray(args.enable) ? args.enable.map(String) : null;
2108
+ if (!requested) {
2109
+ const runnable = all.filter((t) => isRunnable(t, supported));
2110
+ return { catalog: discoveryCatalog(runnable, advertised), changed: false };
2111
+ }
2112
+ const enabled = [];
2113
+ const unknown = [];
2114
+ const unsupported = [];
2115
+ for (const raw of requested) {
2116
+ const name = raw.startsWith("browser_") ? raw : `browser_${raw}`;
2117
+ const t = byName.get(name);
2118
+ if (!t) {
2119
+ unknown.push(raw);
2120
+ continue;
2121
+ }
2122
+ if (!isRunnable(t, supported)) {
2123
+ unsupported.push(raw);
2124
+ continue;
2125
+ }
2126
+ if (advertised.has(name)) continue;
2127
+ advertised.add(name);
2128
+ enabled.push({ name: t.name, description: t.description, inputSchema: t.inputSchema });
2129
+ }
2130
+ return {
2131
+ enabled,
2132
+ ...unknown.length ? { unknown } : {},
2133
+ ...unsupported.length ? { unsupported } : {},
2134
+ changed: enabled.length > 0
2135
+ };
1885
2136
  }
1886
2137
 
1887
2138
  // ../shared/src/static-scan.ts
@@ -2031,8 +2282,9 @@ async function handleBrowserTool(deps, gen, args) {
2031
2282
  const session = coordinator.sessions.get(session_id);
2032
2283
  if (!session) throw new Error(`session ${session_id} not found`);
2033
2284
  const { session_id: _omit, ...suppliedToolArgs } = args;
2034
- const toolArgs = { ...suppliedToolArgs };
2035
- const tool = gen.builtinTool;
2285
+ const resolved = gen.resolve ? gen.resolve(suppliedToolArgs) : { builtinTool: gen.builtinTool, args: suppliedToolArgs };
2286
+ const toolArgs = { ...resolved.args };
2287
+ const tool = resolved.builtinTool;
2036
2288
  if (gen.stepKind === "js") {
2037
2289
  const source = String(toolArgs.source ?? "");
2038
2290
  if (!source) throw new Error("browser_runJS: source required");
@@ -2057,14 +2309,14 @@ async function handleBrowserTool(deps, gen, args) {
2057
2309
  }
2058
2310
  return jsResult.return ?? null;
2059
2311
  }
2060
- if (gen.builtinTool === "switchToTab" || gen.builtinTool === "closeTab") {
2312
+ if (tool === "switchToTab" || tool === "closeTab") {
2061
2313
  const boundTabId = Number.parseInt(session.tab_id, 10);
2062
2314
  if (!Number.isFinite(boundTabId)) {
2063
2315
  throw new Error(`InvalidArgs: session tab_id "${session.tab_id}" is not numeric`);
2064
2316
  }
2065
2317
  toolArgs.tabId = boundTabId;
2066
2318
  }
2067
- const override = NON_BUILTIN_CAPABILITY[gen.builtinTool];
2319
+ const override = NON_BUILTIN_CAPABILITY[tool];
2068
2320
  if (override) {
2069
2321
  const v = coordinator.validateCall({ session_id, kind: "capability", capability: override });
2070
2322
  if (!v.ok) throw new Error(`${v.error.code}: ${v.error.message}`);
@@ -2129,9 +2381,12 @@ var SKILL_TOOL = {
2129
2381
  };
2130
2382
 
2131
2383
  // src/mcp-server.ts
2132
- var TOOL_MODE = readToolMode(process.env);
2133
- var BROWSER_TOOLS = generateBrowserTools(TOOL_MODE);
2134
- var BROWSER_BY_NAME = new Map(BROWSER_TOOLS.map((t) => [t.name, t]));
2384
+ var ALL_BROWSER_TOOLS = generateBrowserTools("full");
2385
+ var BROWSER_BY_NAME = new Map(ALL_BROWSER_TOOLS.map((t) => [t.name, t]));
2386
+ function createToolState(mode) {
2387
+ return { advertised: new Set(generateBrowserTools(mode).map((t) => t.name)) };
2388
+ }
2389
+ var DEFAULT_STATE = createToolState(readToolMode(process.env));
2135
2390
  var LEGACY_TOOLS = [
2136
2391
  "snapshotDOM",
2137
2392
  "querySelector",
@@ -2161,12 +2416,13 @@ function workerToolSupport(deps) {
2161
2416
  const supported = workers[0].supported_tools;
2162
2417
  return supported ?? new Set(LEGACY_TOOLS);
2163
2418
  }
2164
- function buildToolList(deps) {
2419
+ function buildToolList(deps, state = DEFAULT_STATE) {
2165
2420
  const supported = workerToolSupport(deps);
2166
- const browser = supported ? BROWSER_TOOLS.filter((t) => supported.has(t.builtinTool)) : BROWSER_TOOLS;
2421
+ const browser = ALL_BROWSER_TOOLS.filter((t) => state.advertised.has(t.name)).filter((t) => !supported || t.builtinTools.every((b) => supported.has(b)));
2167
2422
  return [
2168
2423
  { name: SKILL_TOOL.name, description: SKILL_TOOL.description, inputSchema: SKILL_TOOL.inputSchema },
2169
2424
  ...CONTROL_TOOLS.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
2425
+ { name: DISCOVER_TOOL.name, description: DISCOVER_TOOL.description, inputSchema: DISCOVER_TOOL.inputSchema },
2170
2426
  ...browser.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema }))
2171
2427
  ];
2172
2428
  }
@@ -2181,12 +2437,23 @@ function toolResult(gen, data) {
2181
2437
  return { content: [{ type: "image", data: d.data, mimeType }] };
2182
2438
  }
2183
2439
  var fail2 = (message) => ({ content: [{ type: "text", text: message }], isError: true });
2184
- async function dispatchCall(deps, name, args, onPairingRequired) {
2440
+ async function dispatchCall(deps, name, args, onPairingRequired, state = DEFAULT_STATE, onListChanged) {
2185
2441
  try {
2186
2442
  if (name === SKILL_TOOL.name) {
2187
2443
  const bundle = readSkillBundle();
2188
2444
  return { content: [{ type: "text", text: bundle.content }] };
2189
2445
  }
2446
+ if (name === DISCOVER_TOOL.name) {
2447
+ const r = handleDiscover({
2448
+ all: ALL_BROWSER_TOOLS,
2449
+ advertised: state.advertised,
2450
+ args,
2451
+ supported: workerToolSupport(deps)
2452
+ });
2453
+ if (r.changed && onListChanged) await onListChanged();
2454
+ const { changed: _c, ...body } = r;
2455
+ return ok(body);
2456
+ }
2190
2457
  if (name === "list_tabs") return ok(await handleListTabs(deps, onPairingRequired));
2191
2458
  if (name === "open_session") return ok(await handleOpenSession(deps, args, onPairingRequired));
2192
2459
  if (name === "close_session") return ok(await handleCloseSession(deps, args));
@@ -2198,12 +2465,12 @@ async function dispatchCall(deps, name, args, onPairingRequired) {
2198
2465
  return fail2(e instanceof Error ? e.message : String(e));
2199
2466
  }
2200
2467
  }
2201
- function createMcpServer(deps) {
2468
+ function createMcpServer(deps, state = DEFAULT_STATE) {
2202
2469
  const server = new Server(
2203
2470
  { name: "atwebpilot-mcp", version: "0.1.0" },
2204
- { capabilities: { tools: {}, logging: {} } }
2471
+ { capabilities: { tools: { listChanged: true }, logging: {} } }
2205
2472
  );
2206
- server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: buildToolList(deps) }));
2473
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: buildToolList(deps, state) }));
2207
2474
  server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
2208
2475
  const args = req.params.arguments ?? {};
2209
2476
  const onPairingRequired = async (url) => {
@@ -2225,7 +2492,14 @@ function createMcpServer(deps) {
2225
2492
  );
2226
2493
  }
2227
2494
  };
2228
- return dispatchCall(deps, req.params.name, args, onPairingRequired);
2495
+ const onListChanged = async () => {
2496
+ try {
2497
+ await server.sendToolListChanged();
2498
+ } catch (error) {
2499
+ console.error("[atwebpilot-mcp] failed to send tools/list_changed:", error instanceof Error ? error.message : String(error));
2500
+ }
2501
+ };
2502
+ return dispatchCall(deps, req.params.name, args, onPairingRequired, state, onListChanged);
2229
2503
  });
2230
2504
  return server;
2231
2505
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@attson/atwebpilot-mcp",
3
- "version": "0.0.70",
3
+ "version": "0.0.72",
4
4
  "license": "Apache-2.0",
5
5
  "description": "MCP server for the atwebpilot browser extension - let Codex or Claude Code drive your browser.",
6
6
  "homepage": "https://github.com/attson/atwebpilot#readme",