@attson/atwebpilot-mcp 0.0.70 → 0.0.71

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 +23 -7
  2. package/dist/index.js +309 -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` 分组);带 `enable: [...]` 把它们加进本进程的 `tools/list`(发送 `tools/list_changed`),并直接返回完整 schema。
53
+ - 执行面默认 **core 32 个** `browser_*`:浏览 / 采集 / 填表 / 导航 / 截图 闭环所需的工具。其余 19 个由 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`(默认) | 32 个核心工具 + `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,7 +94,7 @@ 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`) |
97
+ | `browser_navigate` / `browser_navigate_back` | `browser_navigate`(`action` 取 `goto` / `back` / `forward` / `reload`) |
82
98
  | `browser_tabs` | `browser_listTabs` / `openTab` / `closeTab` / `switchToTab` |
83
99
  | `browser_close` | `browser_closeTab` |
84
100
  | `browser_resize` | `browser_resize` |
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,7 @@ 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: { description: "Bring the session's tab to the foreground." },
1382
1429
  input_schema: {
1383
1430
  type: "object",
1384
1431
  properties: { tabId: { type: "integer" } },
@@ -1389,6 +1436,10 @@ var TOOL_DEFS = [
1389
1436
  {
1390
1437
  name: "screenshot",
1391
1438
  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",
1439
+ mcp: {
1440
+ description: "PNG/JPEG of the visible viewport, returned as an image block. selector or blockId scrolls to and highlights that region first; fullPage stitches the whole page.",
1441
+ params: { blockId: "page-index blockId to frame", indexId: "indexId that produced blockId", scale: "0.1\u20131; smaller saves tokens" }
1442
+ },
1392
1443
  input_schema: {
1393
1444
  type: "object",
1394
1445
  properties: {
@@ -1436,6 +1487,7 @@ var TOOL_DEFS = [
1436
1487
  {
1437
1488
  name: "searchBookmarks",
1438
1489
  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 }",
1490
+ mcp: { description: "Search browser bookmarks: [{id, title, url}]." },
1439
1491
  input_schema: {
1440
1492
  type: "object",
1441
1493
  properties: {
@@ -1448,6 +1500,7 @@ var TOOL_DEFS = [
1448
1500
  {
1449
1501
  name: "searchHistory",
1450
1502
  description: "[META] \u641C\u7D22\u6D4F\u89C8\u5668\u5386\u53F2\u3002daysBack \u9ED8\u8BA4 7\u3002\u8FD4\u56DE [{url, title, lastVisitTime, visitCount}]\u3002",
1503
+ mcp: { description: "Search browser history (daysBack default 7): [{url, title, lastVisitTime, visitCount}]." },
1451
1504
  input_schema: {
1452
1505
  type: "object",
1453
1506
  properties: {
@@ -1461,6 +1514,7 @@ var TOOL_DEFS = [
1461
1514
  {
1462
1515
  name: "downloadImage",
1463
1516
  description: "[ACT] \u628A\u4E00\u4E2A URL \u4E0B\u8F7D\u5230\u672C\u5730\uFF08Chrome Downloads\uFF09\u3002\u8FD4\u56DE {downloadId, filename}\u3002caution \u7EA7\u3002",
1517
+ mcp: { description: "Download a URL to Chrome Downloads. Returns {downloadId, filename}.", params: { filename: "suggested file name with extension" } },
1464
1518
  input_schema: {
1465
1519
  type: "object",
1466
1520
  properties: {
@@ -1473,6 +1527,10 @@ var TOOL_DEFS = [
1473
1527
  {
1474
1528
  name: "downloadSpreadsheet",
1475
1529
  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",
1530
+ mcp: {
1531
+ description: "Generate and download a real .xlsx. sheets[].rows may be 2-D arrays or objects; columns orders object keys and sets headers.",
1532
+ params: { filename: "suggested name; .xlsx optional" }
1533
+ },
1476
1534
  input_schema: {
1477
1535
  type: "object",
1478
1536
  properties: {
@@ -1518,6 +1576,7 @@ var TOOL_DEFS = [
1518
1576
  {
1519
1577
  name: "takeSnapshot",
1520
1578
  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",
1579
+ 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
1580
  input_schema: {
1522
1581
  type: "object",
1523
1582
  properties: {
@@ -1529,6 +1588,7 @@ var TOOL_DEFS = [
1529
1588
  {
1530
1589
  name: "clickByUid",
1531
1590
  description: "[ACT\xB7UID] \u7528 takeSnapshot \u8FD4\u56DE\u7684 uid \u70B9\u51FB\u5143\u7D20\u3002\u6BD4 selector \u7248\u7A33\u5B9A\u3002",
1591
+ mcp: { description: "Click an element by uid from takeSnapshot/findElements. More robust than a selector." },
1532
1592
  input_schema: {
1533
1593
  type: "object",
1534
1594
  properties: {
@@ -1541,6 +1601,7 @@ var TOOL_DEFS = [
1541
1601
  {
1542
1602
  name: "fillByUid",
1543
1603
  description: "[ACT\xB7UID] \u7528 takeSnapshot \u8FD4\u56DE\u7684 uid \u586B\u503C\uFF08input/textarea/contenteditable\uFF09\u3002",
1604
+ mcp: { description: "Set a value on an element by uid from takeSnapshot." },
1544
1605
  input_schema: {
1545
1606
  type: "object",
1546
1607
  properties: {
@@ -1555,6 +1616,7 @@ var TOOL_DEFS = [
1555
1616
  {
1556
1617
  name: "highlightElement",
1557
1618
  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",
1619
+ mcp: { description: "Draw a temporary red outline around an element (visual only)." },
1558
1620
  input_schema: {
1559
1621
  type: "object",
1560
1622
  properties: {
@@ -1568,6 +1630,7 @@ var TOOL_DEFS = [
1568
1630
  {
1569
1631
  name: "highlightText",
1570
1632
  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",
1633
+ mcp: { description: "Temporarily highlight the first occurrence of a text string (visual only)." },
1571
1634
  input_schema: {
1572
1635
  type: "object",
1573
1636
  properties: {
@@ -1581,6 +1644,7 @@ var TOOL_DEFS = [
1581
1644
  {
1582
1645
  name: "fillForm",
1583
1646
  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] }",
1647
+ mcp: { description: "Fill several fields in one call; each item is {selector|uid, value}. Returns {filled, failed}." },
1584
1648
  input_schema: {
1585
1649
  type: "object",
1586
1650
  properties: {
@@ -1605,6 +1669,7 @@ var TOOL_DEFS = [
1605
1669
  {
1606
1670
  name: "navigate",
1607
1671
  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' }",
1672
+ 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
1673
  input_schema: {
1609
1674
  type: "object",
1610
1675
  properties: {
@@ -1618,6 +1683,7 @@ var TOOL_DEFS = [
1618
1683
  {
1619
1684
  name: "getPageInfo",
1620
1685
  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",
1686
+ mcp: { description: "URL, title, hostname, language and OpenGraph meta of the current page. Cheapest 'where am I' check." },
1621
1687
  input_schema: {
1622
1688
  type: "object",
1623
1689
  properties: {
@@ -1628,6 +1694,7 @@ var TOOL_DEFS = [
1628
1694
  {
1629
1695
  name: "pressKey",
1630
1696
  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' }",
1697
+ 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
1698
  input_schema: {
1632
1699
  type: "object",
1633
1700
  properties: {
@@ -1644,6 +1711,7 @@ var TOOL_DEFS = [
1644
1711
  {
1645
1712
  name: "writeStorage",
1646
1713
  description: "[DANGER] \u5199 localStorage \u6216 sessionStorage\u3002\u6539\u7AD9\u70B9\u72B6\u6001\uFF0C\u9700\u8981\u5BA1\u9605\u3002",
1714
+ mcp: { description: "Write one key to localStorage or sessionStorage (dangerous, reviewed).", params: { value: "string; JSON.stringify non-strings yourself" } },
1647
1715
  input_schema: {
1648
1716
  type: "object",
1649
1717
  properties: {
@@ -1662,6 +1730,7 @@ var TOOL_DEFS = [
1662
1730
  {
1663
1731
  name: "consoleMessages",
1664
1732
  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 }",
1733
+ 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
1734
  input_schema: {
1666
1735
  type: "object",
1667
1736
  properties: {
@@ -1675,6 +1744,7 @@ var TOOL_DEFS = [
1675
1744
  {
1676
1745
  name: "networkRequests",
1677
1746
  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",
1747
+ 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
1748
  input_schema: {
1679
1749
  type: "object",
1680
1750
  properties: {
@@ -1691,6 +1761,10 @@ var TOOL_DEFS = [
1691
1761
  {
1692
1762
  name: "networkRequestDetail",
1693
1763
  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",
1764
+ mcp: {
1765
+ description: "Headers and bodies of one request from networkRequests (dangerous: may expose tokens). Bodies need recorderConfig({bodies:true}) unless CDP is on.",
1766
+ params: { id: "id from networkRequests", part: "return only this part" }
1767
+ },
1694
1768
  input_schema: {
1695
1769
  type: "object",
1696
1770
  properties: {
@@ -1708,6 +1782,7 @@ var TOOL_DEFS = [
1708
1782
  {
1709
1783
  name: "handleDialog",
1710
1784
  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",
1785
+ 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
1786
  input_schema: {
1712
1787
  type: "object",
1713
1788
  properties: {
@@ -1722,6 +1797,7 @@ var TOOL_DEFS = [
1722
1797
  {
1723
1798
  name: "recorderConfig",
1724
1799
  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",
1800
+ mcp: { description: "Toggle console/network/dialog recording, arm request/response body capture, clear buffers." },
1725
1801
  input_schema: {
1726
1802
  type: "object",
1727
1803
  properties: {
@@ -1740,16 +1816,19 @@ var TOOL_DEFS = [
1740
1816
  {
1741
1817
  name: "navigateBack",
1742
1818
  description: "[FLOW] \u540E\u9000\u4E00\u9875\u3002\u5DF2\u5728\u5386\u53F2\u8D77\u70B9\u65F6\u8FD4\u56DE { ok:false, reason }\uFF0C\u4E0D\u629B\u9519\u3002",
1819
+ mcp: { description: "Go back one page; returns {ok:false, reason} at history start." },
1743
1820
  input_schema: { type: "object", properties: { tabId: TAB_ID_FIELD } }
1744
1821
  },
1745
1822
  {
1746
1823
  name: "navigateForward",
1747
1824
  description: "[FLOW] \u524D\u8FDB\u4E00\u9875\u3002\u5DF2\u5728\u5386\u53F2\u672B\u5C3E\u65F6\u8FD4\u56DE { ok:false, reason }\uFF0C\u4E0D\u629B\u9519\u3002",
1825
+ mcp: { description: "Go forward one page; returns {ok:false, reason} at history end." },
1748
1826
  input_schema: { type: "object", properties: { tabId: TAB_ID_FIELD } }
1749
1827
  },
1750
1828
  {
1751
1829
  name: "resize",
1752
1830
  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",
1831
+ mcp: { description: "Resize the viewport to width\xD7height." },
1753
1832
  input_schema: {
1754
1833
  type: "object",
1755
1834
  properties: {
@@ -1763,6 +1842,7 @@ var TOOL_DEFS = [
1763
1842
  {
1764
1843
  name: "drag",
1765
1844
  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",
1845
+ mcp: { description: "Drag one element onto another by selector or uid. Emits pointer and HTML5 drag events; returns which the target consumed." },
1766
1846
  input_schema: {
1767
1847
  type: "object",
1768
1848
  properties: {
@@ -1777,6 +1857,7 @@ var TOOL_DEFS = [
1777
1857
  {
1778
1858
  name: "drop",
1779
1859
  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:'...' }] }",
1860
+ mcp: { description: "Simulate dropping external files/data onto an element. files present = upload (dangerous, reviewed).", params: { data: "MIME \u2192 string map" } },
1780
1861
  input_schema: {
1781
1862
  type: "object",
1782
1863
  properties: {
@@ -1802,6 +1883,7 @@ var TOOL_DEFS = [
1802
1883
  {
1803
1884
  name: "findElements",
1804
1885
  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",
1886
+ 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
1887
  input_schema: {
1806
1888
  type: "object",
1807
1889
  properties: {
@@ -1816,14 +1898,21 @@ var TOOL_DEFS = [
1816
1898
 
1817
1899
  // src/tool-gen.ts
1818
1900
  var BLOCKED_TOOLS = /* @__PURE__ */ new Set(["askUser", "attachTab", "detachTab"]);
1819
- var PARITY_TOOLS = [
1820
- // page state and search
1901
+ var CORE_TOOLS = [
1902
+ // page state
1821
1903
  "takeSnapshot",
1822
1904
  "findElements",
1823
1905
  "getPageInfo",
1906
+ "extractText",
1907
+ // page index
1908
+ "createPageIndex",
1909
+ "searchPageIndex",
1910
+ "readPageBlock",
1911
+ "extractPageFields",
1824
1912
  // interaction
1825
1913
  "clickByUid",
1826
1914
  "click",
1915
+ "fillByUid",
1827
1916
  "fillInput",
1828
1917
  "fillForm",
1829
1918
  "selectOption",
@@ -1835,10 +1924,12 @@ var PARITY_TOOLS = [
1835
1924
  "uploadFile",
1836
1925
  // navigation and tabs
1837
1926
  "navigate",
1838
- "navigateBack",
1839
1927
  "listTabs",
1840
1928
  "openTab",
1929
+ "closeTab",
1930
+ "switchToTab",
1841
1931
  "resize",
1932
+ "scroll",
1842
1933
  // observation
1843
1934
  "screenshot",
1844
1935
  "waitFor",
@@ -1848,40 +1939,198 @@ var PARITY_TOOLS = [
1848
1939
  ];
1849
1940
  var IMAGE_RESULT_TOOLS = /* @__PURE__ */ new Set(["screenshot"]);
1850
1941
  var JS_STEP_TOOLS = /* @__PURE__ */ new Set(["runJS"]);
1851
- function rebuildSchema(src) {
1942
+ var SESSION_ID_FIELD = {
1943
+ type: "string",
1944
+ description: "Session id from open_session"
1945
+ };
1946
+ function stripDescriptions(value) {
1947
+ if (Array.isArray(value)) {
1948
+ return value.map(stripDescriptions);
1949
+ }
1950
+ if (value !== null && typeof value === "object") {
1951
+ const out = {};
1952
+ for (const [key, v] of Object.entries(value)) {
1953
+ if (key === "description") continue;
1954
+ out[key] = stripDescriptions(v);
1955
+ }
1956
+ return out;
1957
+ }
1958
+ return value;
1959
+ }
1960
+ function rebuildSchema(src, params) {
1852
1961
  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
- };
1962
+ const properties = {};
1963
+ for (const [key, prop] of Object.entries(s.properties ?? {})) {
1964
+ if (key === "tabId") continue;
1965
+ const stripped = stripDescriptions(prop);
1966
+ properties[key] = params[key] ? { ...stripped, description: params[key] } : stripped;
1967
+ }
1968
+ properties.session_id = SESSION_ID_FIELD;
1859
1969
  const required = [.../* @__PURE__ */ new Set([...(s.required ?? []).filter((r) => r !== "tabId"), "session_id"])];
1860
1970
  return { type: "object", properties, required };
1861
1971
  }
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) => ({
1972
+ function fromDef(t) {
1973
+ return {
1868
1974
  name: `browser_${t.name}`,
1869
1975
  builtinTool: t.name,
1870
- description: t.description,
1976
+ builtinTools: [t.name],
1977
+ description: t.mcp?.description ?? t.description,
1871
1978
  resultKind: IMAGE_RESULT_TOOLS.has(t.name) ? "image" : "json",
1872
1979
  stepKind: JS_STEP_TOOLS.has(t.name) ? "js" : "tool",
1873
- inputSchema: rebuildSchema(t.input_schema)
1874
- }));
1980
+ inputSchema: rebuildSchema(t.input_schema, t.mcp?.params ?? {})
1981
+ };
1982
+ }
1983
+ var MERGED_AWAY_TOOLS = /* @__PURE__ */ new Set([
1984
+ "navigateBack",
1985
+ "navigateForward",
1986
+ "highlightElement",
1987
+ "highlightText",
1988
+ "readStorage",
1989
+ "writeStorage"
1990
+ ]);
1991
+ function schemaWithSession(properties, required) {
1992
+ return { type: "object", properties: { ...properties, session_id: SESSION_ID_FIELD }, required: [...required, "session_id"] };
1993
+ }
1994
+ var STORE_ENUM = { type: "string", enum: ["local", "session"] };
1995
+ var MERGED_TOOLS = [
1996
+ {
1997
+ name: "browser_highlight",
1998
+ builtinTool: "highlightElement",
1999
+ builtinTools: ["highlightElement", "highlightText"],
2000
+ description: "Temporarily outline an element (selector or uid) or highlight the first occurrence of text. Visual only; exactly one of text/selector/uid.",
2001
+ resultKind: "json",
2002
+ stepKind: "tool",
2003
+ inputSchema: schemaWithSession(
2004
+ {
2005
+ text: { type: "string" },
2006
+ selector: { type: "string" },
2007
+ uid: { type: "string" },
2008
+ ms: { type: "integer", default: 3e3 }
2009
+ },
2010
+ []
2011
+ ),
2012
+ resolve(args) {
2013
+ const given = ["text", "selector", "uid"].filter((k) => args[k] != null && args[k] !== "");
2014
+ if (given.length !== 1) {
2015
+ throw new Error("InvalidArgs: browser_highlight needs exactly one of text / selector / uid");
2016
+ }
2017
+ const { text, selector, uid, ms } = args;
2018
+ const withMs = (o) => ms == null ? o : { ...o, ms };
2019
+ if (given[0] === "text") return { builtinTool: "highlightText", args: withMs({ text }) };
2020
+ return { builtinTool: "highlightElement", args: withMs(selector != null ? { selector } : { uid }) };
2021
+ }
2022
+ },
2023
+ {
2024
+ name: "browser_storage",
2025
+ builtinTool: "readStorage",
2026
+ builtinTools: ["readStorage", "writeStorage"],
2027
+ description: "Read (op=get) or write (op=set, needs value) one key in localStorage or sessionStorage. Dangerous, reviewed.",
2028
+ resultKind: "json",
2029
+ stepKind: "tool",
2030
+ inputSchema: schemaWithSession(
2031
+ {
2032
+ op: { type: "string", enum: ["get", "set"] },
2033
+ store: STORE_ENUM,
2034
+ key: { type: "string" },
2035
+ value: { type: "string", description: "set only; JSON.stringify non-strings yourself" }
2036
+ },
2037
+ ["op", "store", "key"]
2038
+ ),
2039
+ resolve(args) {
2040
+ const { op, store, key, value } = args;
2041
+ if (op === "get") return { builtinTool: "readStorage", args: { store, key } };
2042
+ if (op === "set") {
2043
+ if (typeof value !== "string") throw new Error("InvalidArgs: browser_storage op=set requires a string value");
2044
+ return { builtinTool: "writeStorage", args: { store, key, value } };
2045
+ }
2046
+ throw new Error(`InvalidArgs: browser_storage op must be "get" or "set", got ${JSON.stringify(op)}`);
2047
+ }
2048
+ }
2049
+ ];
2050
+ function generateBrowserTools(mode = "core") {
2051
+ const core = new Set(CORE_TOOLS);
2052
+ const plain = TOOL_DEFS.filter((t) => {
2053
+ if (BLOCKED_TOOLS.has(t.name) || MERGED_AWAY_TOOLS.has(t.name)) return false;
2054
+ return mode === "full" || core.has(t.name);
2055
+ }).map(fromDef);
2056
+ return mode === "full" ? [...plain, ...MERGED_TOOLS] : plain;
1875
2057
  }
1876
2058
  function readToolMode(env) {
1877
2059
  const raw = env.ATWEBPILOT_MCP_TOOLS;
1878
- if (raw == null || raw === "") return "full";
1879
- if (raw === "full" || raw === "parity") return raw;
2060
+ if (raw == null || raw === "") return "core";
2061
+ if (raw === "core" || raw === "full") return raw;
1880
2062
  process.stderr.write(
1881
- `[atwebpilot-mcp] ATWEBPILOT_MCP_TOOLS="${raw}" is not recognised; using "full"
2063
+ `[atwebpilot-mcp] ATWEBPILOT_MCP_TOOLS="${raw}" is not recognised; using "core"
1882
2064
  `
1883
2065
  );
1884
- return "full";
2066
+ return "core";
2067
+ }
2068
+ var DISCOVERABLE_GROUPS = {
2069
+ export: ["browser_downloadImage", "browser_downloadSpreadsheet"],
2070
+ network: ["browser_httpRequest", "browser_networkRequestDetail", "browser_recorderConfig", "browser_handleDialog"],
2071
+ storage: ["browser_storage"],
2072
+ "browser-data": ["browser_searchBookmarks", "browser_searchHistory"],
2073
+ inspect: ["browser_inspectElement", "browser_highlight", "browser_getValue", "browser_extractFormState"],
2074
+ "legacy-dom": ["browser_snapshotDOM", "browser_querySelector", "browser_querySelectorAll", "browser_extractImages", "browser_focus"],
2075
+ form: ["browser_submitForm"]
2076
+ };
2077
+ var GROUP_BY_NAME = new Map(
2078
+ Object.entries(DISCOVERABLE_GROUPS).flatMap(([g, names]) => names.map((n) => [n, g]))
2079
+ );
2080
+ function groupOf(mcpName) {
2081
+ return GROUP_BY_NAME.get(mcpName);
2082
+ }
2083
+ function discoveryCatalog(all, advertised) {
2084
+ 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);
2085
+ }
2086
+
2087
+ // src/discover-tool.ts
2088
+ var DISCOVER_TOOL = {
2089
+ name: "browser_discoverTools",
2090
+ 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.",
2091
+ inputSchema: {
2092
+ type: "object",
2093
+ properties: {
2094
+ enable: { type: "array", items: { type: "string" }, description: "Tool names from the catalog to advertise" }
2095
+ },
2096
+ additionalProperties: false
2097
+ }
2098
+ };
2099
+ function isRunnable(t, supported) {
2100
+ return !supported || t.builtinTools.every((b) => supported.has(b));
2101
+ }
2102
+ function handleDiscover(input) {
2103
+ const { all, advertised, args, supported } = input;
2104
+ const byName = new Map(all.map((t) => [t.name, t]));
2105
+ const requested = Array.isArray(args.enable) ? args.enable.map(String) : null;
2106
+ if (!requested) {
2107
+ const runnable = all.filter((t) => isRunnable(t, supported));
2108
+ return { catalog: discoveryCatalog(runnable, advertised), changed: false };
2109
+ }
2110
+ const enabled = [];
2111
+ const unknown = [];
2112
+ const unsupported = [];
2113
+ for (const raw of requested) {
2114
+ const name = raw.startsWith("browser_") ? raw : `browser_${raw}`;
2115
+ const t = byName.get(name);
2116
+ if (!t) {
2117
+ unknown.push(raw);
2118
+ continue;
2119
+ }
2120
+ if (!isRunnable(t, supported)) {
2121
+ unsupported.push(raw);
2122
+ continue;
2123
+ }
2124
+ if (advertised.has(name)) continue;
2125
+ advertised.add(name);
2126
+ enabled.push({ name: t.name, description: t.description, inputSchema: t.inputSchema });
2127
+ }
2128
+ return {
2129
+ enabled,
2130
+ ...unknown.length ? { unknown } : {},
2131
+ ...unsupported.length ? { unsupported } : {},
2132
+ changed: enabled.length > 0
2133
+ };
1885
2134
  }
1886
2135
 
1887
2136
  // ../shared/src/static-scan.ts
@@ -2031,8 +2280,9 @@ async function handleBrowserTool(deps, gen, args) {
2031
2280
  const session = coordinator.sessions.get(session_id);
2032
2281
  if (!session) throw new Error(`session ${session_id} not found`);
2033
2282
  const { session_id: _omit, ...suppliedToolArgs } = args;
2034
- const toolArgs = { ...suppliedToolArgs };
2035
- const tool = gen.builtinTool;
2283
+ const resolved = gen.resolve ? gen.resolve(suppliedToolArgs) : { builtinTool: gen.builtinTool, args: suppliedToolArgs };
2284
+ const toolArgs = { ...resolved.args };
2285
+ const tool = resolved.builtinTool;
2036
2286
  if (gen.stepKind === "js") {
2037
2287
  const source = String(toolArgs.source ?? "");
2038
2288
  if (!source) throw new Error("browser_runJS: source required");
@@ -2057,14 +2307,14 @@ async function handleBrowserTool(deps, gen, args) {
2057
2307
  }
2058
2308
  return jsResult.return ?? null;
2059
2309
  }
2060
- if (gen.builtinTool === "switchToTab" || gen.builtinTool === "closeTab") {
2310
+ if (tool === "switchToTab" || tool === "closeTab") {
2061
2311
  const boundTabId = Number.parseInt(session.tab_id, 10);
2062
2312
  if (!Number.isFinite(boundTabId)) {
2063
2313
  throw new Error(`InvalidArgs: session tab_id "${session.tab_id}" is not numeric`);
2064
2314
  }
2065
2315
  toolArgs.tabId = boundTabId;
2066
2316
  }
2067
- const override = NON_BUILTIN_CAPABILITY[gen.builtinTool];
2317
+ const override = NON_BUILTIN_CAPABILITY[tool];
2068
2318
  if (override) {
2069
2319
  const v = coordinator.validateCall({ session_id, kind: "capability", capability: override });
2070
2320
  if (!v.ok) throw new Error(`${v.error.code}: ${v.error.message}`);
@@ -2129,9 +2379,12 @@ var SKILL_TOOL = {
2129
2379
  };
2130
2380
 
2131
2381
  // 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]));
2382
+ var ALL_BROWSER_TOOLS = generateBrowserTools("full");
2383
+ var BROWSER_BY_NAME = new Map(ALL_BROWSER_TOOLS.map((t) => [t.name, t]));
2384
+ function createToolState(mode) {
2385
+ return { advertised: new Set(generateBrowserTools(mode).map((t) => t.name)) };
2386
+ }
2387
+ var DEFAULT_STATE = createToolState(readToolMode(process.env));
2135
2388
  var LEGACY_TOOLS = [
2136
2389
  "snapshotDOM",
2137
2390
  "querySelector",
@@ -2161,12 +2414,13 @@ function workerToolSupport(deps) {
2161
2414
  const supported = workers[0].supported_tools;
2162
2415
  return supported ?? new Set(LEGACY_TOOLS);
2163
2416
  }
2164
- function buildToolList(deps) {
2417
+ function buildToolList(deps, state = DEFAULT_STATE) {
2165
2418
  const supported = workerToolSupport(deps);
2166
- const browser = supported ? BROWSER_TOOLS.filter((t) => supported.has(t.builtinTool)) : BROWSER_TOOLS;
2419
+ const browser = ALL_BROWSER_TOOLS.filter((t) => state.advertised.has(t.name)).filter((t) => !supported || t.builtinTools.every((b) => supported.has(b)));
2167
2420
  return [
2168
2421
  { name: SKILL_TOOL.name, description: SKILL_TOOL.description, inputSchema: SKILL_TOOL.inputSchema },
2169
2422
  ...CONTROL_TOOLS.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
2423
+ { name: DISCOVER_TOOL.name, description: DISCOVER_TOOL.description, inputSchema: DISCOVER_TOOL.inputSchema },
2170
2424
  ...browser.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema }))
2171
2425
  ];
2172
2426
  }
@@ -2181,12 +2435,23 @@ function toolResult(gen, data) {
2181
2435
  return { content: [{ type: "image", data: d.data, mimeType }] };
2182
2436
  }
2183
2437
  var fail2 = (message) => ({ content: [{ type: "text", text: message }], isError: true });
2184
- async function dispatchCall(deps, name, args, onPairingRequired) {
2438
+ async function dispatchCall(deps, name, args, onPairingRequired, state = DEFAULT_STATE, onListChanged) {
2185
2439
  try {
2186
2440
  if (name === SKILL_TOOL.name) {
2187
2441
  const bundle = readSkillBundle();
2188
2442
  return { content: [{ type: "text", text: bundle.content }] };
2189
2443
  }
2444
+ if (name === DISCOVER_TOOL.name) {
2445
+ const r = handleDiscover({
2446
+ all: ALL_BROWSER_TOOLS,
2447
+ advertised: state.advertised,
2448
+ args,
2449
+ supported: workerToolSupport(deps)
2450
+ });
2451
+ if (r.changed && onListChanged) await onListChanged();
2452
+ const { changed: _c, ...body } = r;
2453
+ return ok(body);
2454
+ }
2190
2455
  if (name === "list_tabs") return ok(await handleListTabs(deps, onPairingRequired));
2191
2456
  if (name === "open_session") return ok(await handleOpenSession(deps, args, onPairingRequired));
2192
2457
  if (name === "close_session") return ok(await handleCloseSession(deps, args));
@@ -2198,12 +2463,12 @@ async function dispatchCall(deps, name, args, onPairingRequired) {
2198
2463
  return fail2(e instanceof Error ? e.message : String(e));
2199
2464
  }
2200
2465
  }
2201
- function createMcpServer(deps) {
2466
+ function createMcpServer(deps, state = DEFAULT_STATE) {
2202
2467
  const server = new Server(
2203
2468
  { name: "atwebpilot-mcp", version: "0.1.0" },
2204
- { capabilities: { tools: {}, logging: {} } }
2469
+ { capabilities: { tools: { listChanged: true }, logging: {} } }
2205
2470
  );
2206
- server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: buildToolList(deps) }));
2471
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: buildToolList(deps, state) }));
2207
2472
  server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
2208
2473
  const args = req.params.arguments ?? {};
2209
2474
  const onPairingRequired = async (url) => {
@@ -2225,7 +2490,14 @@ function createMcpServer(deps) {
2225
2490
  );
2226
2491
  }
2227
2492
  };
2228
- return dispatchCall(deps, req.params.name, args, onPairingRequired);
2493
+ const onListChanged = async () => {
2494
+ try {
2495
+ await server.sendToolListChanged();
2496
+ } catch (error) {
2497
+ console.error("[atwebpilot-mcp] failed to send tools/list_changed:", error instanceof Error ? error.message : String(error));
2498
+ }
2499
+ };
2500
+ return dispatchCall(deps, req.params.name, args, onPairingRequired, state, onListChanged);
2229
2501
  });
2230
2502
  return server;
2231
2503
  }
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.71",
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",