ghost-bridge 0.3.0 → 0.5.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 <Author>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,85 +1,159 @@
1
- # Ghost Bridge(Claude MCP 无重启调试桥)
1
+ # 👻 Ghost Bridge
2
2
 
3
- > 目标:在日常 Chrome 内零启动参数附加 DevTools,面向线上压缩代码(无 sourcemap)快速定位。
3
+ [![npm version](https://img.shields.io/npm/v/ghost-bridge.svg?style=flat-square)](https://www.npmjs.com/package/ghost-bridge)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
4
5
 
5
- ## 目录结构
6
- - `server.js`:MCP server,通过 stdio 被 Claude CLI 拉起,与扩展用 WebSocket 通信
7
- - `extension/`:Chrome MV3 扩展,使用 `chrome.debugger` 附加当前激活标签
6
+ > **Zero-restart Chrome AI Copilot** — Subvert your workflow. Allow AI to seamlessly take over the browser you're already using, enabling real-time debugging, visual observation, and interactive manipulation without launching a new Chrome instance.
8
7
 
9
- # Ghost Bridge(Claude MCP 无重启调试桥)
8
+ ---
9
+
10
+ ## ✨ Why Ghost Bridge?
10
11
 
11
- > 目标:在日常 Chrome 内零启动参数附加 DevTools,面向线上压缩代码(无 sourcemap)快速定位。
12
+ - 🔌 **Zero-Config Attach** — Bypasses the need for `--remote-debugging-port`. Captures Chrome's native DevTools Protocol directly via an extension.
13
+ - 🔍 **No-Sourcemap Debugging** — Slice code fragments, perform string searches, and analyze coverage to pinpoint bugs straight in minified production code.
14
+ - 🌐 **Deep Network Analysis** — Comprehensive capture of requests/responses with multi-dimensional filtering and response body inspection.
15
+ - 📸 **Visual & Structural Perception** — Full-page or clipped high-fidelity screenshots paired with structural data extraction (titles, links, forms, buttons).
16
+ - 🎯 **DOM Physical Manipulation** — Empowers AI to click, type, and form-submit with CDP-level physical simulation. Fully compatible with complex SPAs (React/Vue/Angular/Svelte).
17
+ - 📊 **Performance Diagnostics** — Get granular engine metrics: JS Heap, Layout recalculations, Web Vitals (TTFB/FCP/LCP), and resource loading speeds.
18
+ - 🔄 **Multi-Client Mastery** — Built-in singleton manager automatically coordinates multiple MCP clients sharing a single Chrome transport.
12
19
 
13
- ## 快速使用
20
+ ---
14
21
 
15
- ### 1. 安装與初始化
22
+ ## 🚀 Quick Start
23
+
24
+ ### 1. Install & Initialize
16
25
 
17
26
  ```bash
18
- # 全局安装
27
+ # Install globally
19
28
  npm install -g ghost-bridge
20
29
 
21
- # 自动配置 Claude MCP 并准备扩展
30
+ # Auto-configure MCP (Claude Code, Antigravity, Codex) and prepare the extension directory
22
31
  ghost-bridge init
23
32
  ```
24
33
 
25
- ### 2. 加载 Chrome 扩展
34
+ > **Note on other MCP clients (Cursor, Windsurf, Roo):**
35
+ > `ghost-bridge init` attempts to auto-configure supported clients. If your client isn't auto-detected, simply add the following to your client's MCP configuration file (e.g., `mcp.json`):
36
+ > ```json
37
+ > {
38
+ > "mcpServers": {
39
+ > "ghost-bridge": {
40
+ > "command": "node",
41
+ > "args": ["/absolute/path/to/global/node_modules/ghost-bridge/dist/server.js"]
42
+ > }
43
+ > }
44
+ > }
45
+ > ```
46
+
47
+ ### 2. Load the Chrome Extension
48
+
49
+ 1. Open Chrome and navigate to `chrome://extensions`
50
+ 2. Toggle **Developer mode** in the top right corner.
51
+ 3. Click **Load unpacked**
52
+ 4. Select the directory: `~/.ghost-bridge/extension`
53
+
54
+ > 💡 *Tip: Run `ghost-bridge extension --open` to reveal the directory directly.*
55
+
56
+ ### 3. Connect & Command
57
+
58
+ 1. Click the **Ghost Bridge** ghost icon in your browser toolbar.
59
+ 2. Click **Connect** and wait for the status to turn to ✅ **ON**.
60
+ 3. Open Claude Desktop or your Claude CLI. All tools are now primed and ready!
61
+
62
+ ---
63
+
64
+ ## 🛠️ Tool Arsenal
65
+
66
+ ### 🔍 Core Debugging
67
+
68
+ | Tool | Capability |
69
+ |------|------------|
70
+ | `get_server_info` | Retrieves server instance status, WebSocket ports, and client roles. |
71
+ | `get_last_error` | Aggregates recent exceptions, console errors, and failed network requests with mapped locators. |
72
+ | `get_script_source` | Pulls raw script definitions. Supports URL-fragment filtering, specific line targeting, and beautification. |
73
+ | `coverage_snapshot` | Triggers a quick coverage trace (1.5s default) to identify the most active scripts on the page. |
74
+ | `find_by_string` | Scans page script sources for keywords, returning a 200-character context window. |
75
+ | `symbolic_hints` | Gathers context clues: Resource lists, Global Variable keys, LocalStorage schema, and UA strings. |
76
+ | `eval_script` | Executes raw JavaScript expressions in the page context. *(Use with caution)* |
77
+
78
+ ### 🌍 Network Intelligence
79
+
80
+ | Tool | Capability |
81
+ |------|------------|
82
+ | `list_network_requests` | Lists captured network traffic. Supports filtering by URL, Method, Status Code, or Resource Type. |
83
+ | `get_network_detail` | Dives deep into a specific request's Headers, Timing, and optional Response Body extraction. |
84
+ | `clear_network_requests` | Wipes the current network capture buffer. |
85
+
86
+ ### 📸 Page Perception
87
+
88
+ | Tool | Capability |
89
+ |------|------------|
90
+ | `capture_screenshot` | Captures the viewport or emulates full-page scrolling screenshots. |
91
+ | `get_page_content` | Extracts raw text, sanitized HTML, or structured actionable data representations. |
92
+
93
+ ### 🎯 Interactive Automation (DOM)
94
+
95
+ | Tool | Capability |
96
+ |------|------------|
97
+ | `get_interactive_snapshot` | Scans for visible interactive elements, returning a concise map `[e1, e2...]`. Pierces open Shadow DOMs. |
98
+ | `dispatch_action` | Dispatches physical UI actions (click, fill, press, hover) against targeted element references (e.g., `e1`). |
99
+
100
+ **Example Agent Workflow:**
101
+ 1. AI: `get_interactive_snapshot` ➝ `[{ref:"e1", tag:"input", placeholder:"Search..."}, {ref:"e2", tag:"button", text:"Login"}]`
102
+ 2. AI: `dispatch_action({ref: "e1", action: "fill", value: "hello"})`
103
+ 3. AI: `dispatch_action({ref: "e2", action: "click"})`
104
+ 4. AI: `capture_screenshot` to verify state changes.
105
+
106
+ ### 📊 Performance Profiling
107
+
108
+ | Tool | Capability |
109
+ |------|------------|
110
+ | `perf_metrics` | Collects layered performance data (Engine Metrics, Web Vitals, and Resource Load Summaries). |
111
+
112
+ ---
113
+
114
+ ## ⚙️ Configuration
115
+
116
+ | Setting | Default | Description |
117
+ |---------|---------|-------------|
118
+ | **Base Port** | `33333` | WS port. Auto-increments if occupied. |
119
+ | **Token** | *Monthly UUID* | Local WS auth token, auto-rotates on the 1st of every month. |
120
+ | **Auto Detach** | `false` | Keeps debugger attached to actively buffer invisible exceptions and network calls. |
121
+
122
+ **Environment Variables:**
123
+ - `GHOST_BRIDGE_PORT` — Override base port.
124
+ - `GHOST_BRIDGE_TOKEN` — Override connection token.
125
+
126
+ ---
127
+
128
+ ## 🏗️ Architecture
129
+
130
+ ```mermaid
131
+ graph LR
132
+ A[Claude CLI/Desktop] <-->|stdio| B(MCP Server\nserver.js)
133
+ B <-->|WebSocket| C(Chrome Extension\nbackground.js)
134
+ C <-->|CDP| D[Browser Tab\nTarget Context]
135
+ ```
136
+
137
+ - **MCP Server**: Spawned by Claude via standard I/O streams. Orchestrates WS connections.
138
+ - **Chrome Extension (MV3)**: Taps into `chrome.debugger` API. Utilizes an Offscreen Document to prevent WS hibernation.
139
+ - **Singleton Design**: If multiple agents spawn servers, the first becomes the master bridge while subsequent instances chain transparently as clients.
26
140
 
27
- 1. 打开 Chrome,访问 `chrome://extensions`
28
- 2. 开启右上角的“开发者模式”
29
- 3. 点击“加载已解压的扩展程序”
30
- 4. 选择目录:`~/.ghost-bridge/extension`
31
- > 提示:运行 `ghost-bridge extension --open` 可直接打开该目录
141
+ ---
32
142
 
33
- ### 3. 开始使用
143
+ ## ⚠️ Known Limitations
34
144
 
35
- 启动 Claude Desktop Claude CLI,Ghost Bridge 工具即可直接通过 MCP 调用。无需额外启动任何服务(MCP 会自动管理)。
145
+ - **Service Workers Suspending**: MV3 background workers may suspend. We've built robust auto-reconnection logic, but prolonged inactivity might require re-toggling.
146
+ - **DevTools Conflict**: If you manually open Chrome DevTools (F12) on the target tab, `chrome.debugger.attach` may be rejected.
147
+ - **Beautify Overhead**: Beautifying massive single-line bundles is expensive; the server will auto-truncate overly large scripts.
148
+ - **Cross-Origin OOPIF**: Elements and errors deeply embedded in strict Cross-Origin Iframes might evade the primary debugger hook without further multi-target attach logic.
36
149
 
37
150
  ---
38
151
 
39
- ## 常用命令
40
-
41
- - `ghost-bridge init`: 配置 MCP 并复制扩展文件
42
- - `ghost-bridge status`: 检查配置状态
43
- - `ghost-bridge extension`: 显示扩展安装路径
44
- - `ghost-bridge start`: 手动启动 MCP 服务(通常不需要)
45
-
46
- ## 默认配置
47
- - 端口:`3301`(`server.js` / `extension/background.js`)
48
- - token:`1`(仅用于本机 WS 校验,如需修改请保持两端一致)
49
-
50
- ## 工具说明
51
-
52
- ### 基础调试工具
53
- - **get_last_error**:汇总最近异常/console/网络报错,附带行列与脚本标识
54
- - **get_script_source**:支持 `scriptUrlContains`、`line`、`column`,返回源码片段(无 sourcemap 仍可用)
55
- - **coverage_snapshot**:默认 1.5s,输出调用次数最高的脚本
56
- - **find_by_string**:在脚本源码里按关键词搜索,返回上下文 200 字符窗口
57
- - **symbolic_hints**:采集资源列表、全局变量 key、localStorage key、UA 与 URL
58
- - **eval_script**:只读表达式执行;谨慎使用,避免改写页面状态
59
-
60
- ### 🆕 网络请求分析工具
61
- - **list_network_requests**:列出捕获的网络请求
62
- - 支持按 URL 关键词过滤(`filter`)
63
- - 支持按请求方法过滤(`method`: GET/POST/PUT/DELETE)
64
- - 支持按状态过滤(`status`: success/error/failed/pending)
65
- - 支持按资源类型过滤(`resourceType`: XHR/Fetch/Script/Image)
66
- - 返回:URL、方法、状态码、耗时、大小等摘要信息
67
-
68
- - **get_network_detail**:获取单个请求的详细信息
69
- - 请求头和响应头
70
- - 请求方法、状态码、MIME 类型
71
- - 耗时分析(timing)
72
- - 可选获取响应体(`includeBody: true`)
73
-
74
- - **clear_network_requests**:清空已捕获的网络请求记录
75
-
76
- ## 设计取舍
77
- - 不依赖 `--remote-debugging-port`,完全通过扩展获取 CDP,满足"零重启"
78
- - 默认 `autoDetach=false` 保持附加,便于持续捕获异常和网络请求;可通过图标 OFF 立即解除调试
79
- - 无 sourcemap 时通过片段截取、字符串搜索与覆盖率提供线索
80
- - 网络请求完整记录,支持查看请求/响应详情和响应体
81
-
82
- ## 已知限制
83
- - 扩展 service worker 可能被挂起,已内置 1s 重连策略;若长时间无流量需重新唤醒
84
- - 若目标页面自带 DevTools 打开,`chrome.debugger.attach` 可能失败,请关闭后重试
85
- - 大体积单行 bundle beautify 可能耗时,server 端对超长源码会只截取片段
152
+ ## 🤝 Contributing
153
+
154
+ Contributions, issues, and feature requests are welcome!
155
+ Check out our [Contributing Guide](CONTRIBUTING.md) to get started building tools or improving the bridge.
156
+
157
+ ## 📄 License
158
+
159
+ This project is [MIT](LICENSE) licensed.
package/dist/cli.js CHANGED
@@ -5454,9 +5454,15 @@ var require_lib = __commonJS({
5454
5454
  import path from "path";
5455
5455
  import os2 from "os";
5456
5456
  import { fileURLToPath } from "url";
5457
- function getClaudeConfigPath() {
5457
+ function getClientConfigPaths() {
5458
5458
  const homeDir = os2.homedir();
5459
- return path.join(homeDir, ".claude.json");
5459
+ return [
5460
+ { name: "Claude Code", path: path.join(homeDir, ".claude.json") },
5461
+ { name: "Antigravity", path: path.join(homeDir, ".gemini", "antigravity", "mcp.json") },
5462
+ // Codex config placeholder - exact path depends on how Codex stores user config.
5463
+ // Assuming a common pattern here like ~/.codex/mcp.json or similar.
5464
+ { name: "Codex", path: path.join(homeDir, ".codex", "mcp.json") }
5465
+ ];
5460
5466
  }
5461
5467
  function getExtensionPath() {
5462
5468
  const __filename2 = fileURLToPath(import.meta.url);
@@ -5502,20 +5508,28 @@ function openFolder(folderPath) {
5502
5508
  }
5503
5509
  async function init(options) {
5504
5510
  console.log(source_default.bold("\u{1F47B} Ghost Bridge Initialization"));
5505
- const configPath = getClaudeConfigPath();
5511
+ const clientConfigs = getClientConfigPaths();
5506
5512
  const serverPath = getServerPath();
5507
5513
  const isDryRun = options.dryRun;
5508
- console.log(source_default.dim("Checking Claude configuration..."));
5509
- if (isDryRun) {
5510
- console.log(source_default.yellow(`[Dry Run] Would check config at: ${configPath}`));
5511
- console.log(source_default.yellow(`[Dry Run] Would add MCP server pointing to: ${serverPath}`));
5512
- } else {
5513
- if (!import_fs_extra.default.existsSync(configPath)) {
5514
- console.log(source_default.yellow(`Configuration file not found at ${configPath}, creating/skipping...`));
5514
+ console.log(source_default.dim("Checking MCP Client configurations..."));
5515
+ let configuredCount = 0;
5516
+ for (const client of clientConfigs) {
5517
+ const configPath = client.path;
5518
+ if (isDryRun) {
5519
+ console.log(source_default.yellow(`[Dry Run] Would check ${client.name} config at: ${configPath}`));
5520
+ if (import_fs_extra.default.existsSync(configPath) || client.name === "Claude Code") {
5521
+ console.log(source_default.yellow(`[Dry Run] Would add MCP server logic for ${client.name}`));
5522
+ }
5523
+ continue;
5524
+ }
5525
+ const exists = import_fs_extra.default.existsSync(configPath);
5526
+ if (!exists && client.name !== "Claude Code") {
5527
+ continue;
5528
+ }
5529
+ if (!exists) {
5530
+ console.log(source_default.yellow(`Configuration file not found for ${client.name} at ${configPath}, creating...`));
5515
5531
  await import_fs_extra.default.ensureDir(path2.dirname(configPath));
5516
- if (!import_fs_extra.default.existsSync(configPath)) {
5517
- await import_fs_extra.default.writeJson(configPath, { mcpServers: {} }, { spaces: 2 });
5518
- }
5532
+ await import_fs_extra.default.writeJson(configPath, { mcpServers: {} }, { spaces: 2 });
5519
5533
  }
5520
5534
  try {
5521
5535
  const config = await import_fs_extra.default.readJson(configPath);
@@ -5525,11 +5539,15 @@ async function init(options) {
5525
5539
  args: [serverPath]
5526
5540
  };
5527
5541
  await import_fs_extra.default.writeJson(configPath, config, { spaces: 2 });
5528
- console.log(source_default.green(`\u2705 MCP Server 'ghost-bridge' configured in ${configPath}`));
5542
+ console.log(source_default.green(`\u2705 MCP Server configured for ${source_default.bold(client.name)} in ${configPath}`));
5543
+ configuredCount++;
5529
5544
  } catch (err) {
5530
- console.error(source_default.red(`Failed to update config: ${err.message}`));
5545
+ console.error(source_default.red(`Failed to update config for ${client.name}: ${err.message}`));
5531
5546
  }
5532
5547
  }
5548
+ if (configuredCount === 0 && !isDryRun) {
5549
+ console.log(source_default.yellow("\u26A0\uFE0F No supported MCP clients found to configure automatically."));
5550
+ }
5533
5551
  const sourceExt = getExtensionPath();
5534
5552
  const targetExt = getUserExtensionDir();
5535
5553
  console.log(source_default.dim(`Setting up extension in ${targetExt}...`));
@@ -5617,34 +5635,47 @@ __export(status_exports, {
5617
5635
  });
5618
5636
  async function status() {
5619
5637
  console.log(source_default.bold("\u{1F47B} Ghost Bridge Status"));
5620
- const configPath = getClaudeConfigPath();
5638
+ const clientConfigs = getClientConfigPaths();
5621
5639
  const extDir = getUserExtensionDir();
5622
5640
  const serverPath = getServerPath();
5623
- let mcpStatus = source_default.red("Not Configured");
5624
- let mcpDetails = "";
5625
- if (import_fs_extra3.default.existsSync(configPath)) {
5626
- try {
5627
- const config = await import_fs_extra3.default.readJson(configPath);
5628
- if (config.mcpServers && config.mcpServers["ghost-bridge"]) {
5629
- mcpStatus = source_default.green("Configured");
5630
- const cfg = config.mcpServers["ghost-bridge"];
5631
- const configuredPath = cfg.args[0];
5632
- if (configuredPath === serverPath) {
5633
- mcpDetails = source_default.dim("(Paths match)");
5634
- } else {
5635
- mcpDetails = source_default.yellow(`(Path mismatch)
5636
- Configured: ${configuredPath}
5637
- Current: ${serverPath}`);
5641
+ console.log(source_default.bold.blue("\nMCP Client Configurations:"));
5642
+ let configuredCount = 0;
5643
+ for (const client of clientConfigs) {
5644
+ let mcpStatus = source_default.gray("Not Configured");
5645
+ let mcpDetails = "";
5646
+ const configPath = client.path;
5647
+ if (import_fs_extra3.default.existsSync(configPath)) {
5648
+ try {
5649
+ const config = await import_fs_extra3.default.readJson(configPath);
5650
+ if (config.mcpServers && config.mcpServers["ghost-bridge"]) {
5651
+ mcpStatus = source_default.green("Configured");
5652
+ const cfg = config.mcpServers["ghost-bridge"];
5653
+ const configuredPath = cfg.args[0];
5654
+ if (configuredPath === serverPath) {
5655
+ mcpDetails = source_default.dim("(Paths match)");
5656
+ } else {
5657
+ mcpDetails = source_default.yellow(`(Path mismatch)
5658
+ Configured: ${configuredPath}
5659
+ Current: ${serverPath}`);
5660
+ }
5661
+ configuredCount++;
5638
5662
  }
5663
+ } catch (e) {
5664
+ mcpStatus = source_default.red("Error reading config");
5665
+ }
5666
+ } else {
5667
+ if (client.name === "Claude Code") {
5668
+ mcpStatus = source_default.yellow("Config file not found");
5669
+ } else {
5670
+ mcpStatus = source_default.gray("Not Installed");
5639
5671
  }
5640
- } catch (e) {
5641
- mcpStatus = source_default.red("Error reading config");
5642
5672
  }
5643
- } else {
5644
- mcpStatus = source_default.yellow("Config file not found");
5673
+ console.log(` ${source_default.bold(client.name)}: ${mcpStatus} ${mcpDetails}`);
5674
+ console.log(` Config File: ${source_default.dim(configPath)}`);
5675
+ }
5676
+ if (configuredCount === 0) {
5677
+ console.log(source_default.yellow("\n No MCP clients currently have ghost-bridge configured. Run `ghost-bridge init`."));
5645
5678
  }
5646
- console.log(`MCP Configuration: ${mcpStatus} ${mcpDetails}`);
5647
- console.log(` Config File: ${configPath}`);
5648
5679
  let extStatus = source_default.red("Not Installed (Run init)");
5649
5680
  if (import_fs_extra3.default.existsSync(extDir)) {
5650
5681
  extStatus = source_default.green("Installed");
package/dist/server.js CHANGED
@@ -28896,7 +28896,7 @@ function buildSnippet(source, line, column, { beautifyEnabled = true, contextLin
28896
28896
  return result;
28897
28897
  }
28898
28898
  var server = new Server(
28899
- { name: "ghost-bridge", version: "0.3.0" },
28899
+ { name: "ghost-bridge", version: "0.4.0" },
28900
28900
  { capabilities: { tools: {} } }
28901
28901
  );
28902
28902
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
@@ -29067,6 +29067,66 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29067
29067
  }
29068
29068
  }
29069
29069
  }
29070
+ },
29071
+ {
29072
+ name: "get_interactive_snapshot",
29073
+ description: "\u3010\u64CD\u4F5C\u9875\u9762\u524D\u5FC5\u987B\u5148\u8C03\u7528\u3011\u626B\u63CF\u5F53\u524D\u9875\u9762\u6240\u6709\u53EF\u89C1\u7684\u53EF\u4EA4\u4E92\u5143\u7D20\uFF08\u6309\u94AE/\u94FE\u63A5/\u8F93\u5165\u6846/\u4E0B\u62C9\u6846\u7B49\uFF09\uFF0C\u8FD4\u56DE\u5E26\u6709 ref \u77ED\u6807\u8BC6\uFF08\u5982 e1, e2, e3\uFF09\u7684\u7CBE\u7B80\u5217\u8868\uFF0C\u5305\u542B\u5143\u7D20\u7C7B\u578B\u3001\u6587\u672C\u548C\u4F4D\u7F6E\u3002Token \u6781\u7701\uFF08\u901A\u5E38 < 1000 tokens\uFF09\uFF0C\u4E13\u4E3A AI \u64CD\u4F5C\u9875\u9762\u800C\u8BBE\u8BA1\u3002\u83B7\u53D6\u540E\u53EF\u901A\u8FC7 dispatch_action \u5DE5\u5177\u4F7F\u7528 ref \u6807\u8BC6\u6765\u70B9\u51FB\u3001\u586B\u5199\u3001\u6309\u952E\u7B49\u3002\u652F\u6301 Shadow DOM \u7A7F\u900F\u3002\n\u26A0\uFE0F \u4EC5\u7528\u4E8E\u4EA4\u4E92\u64CD\u4F5C\u524D\u7684\u5143\u7D20\u5B9A\u4F4D\u3002\u5982\u9700\u6392\u67E5 UI/CSS \u5E03\u5C40\u95EE\u9898\uFF0C\u8BF7\u4F7F\u7528 capture_screenshot \u6216 get_page_content\u3002",
29074
+ inputSchema: {
29075
+ type: "object",
29076
+ properties: {
29077
+ selector: {
29078
+ type: "string",
29079
+ description: "CSS \u9009\u62E9\u5668\uFF0C\u9650\u5B9A\u626B\u63CF\u8303\u56F4\u3002\u4E0D\u6307\u5B9A\u5219\u626B\u63CF\u6574\u4E2A\u9875\u9762"
29080
+ },
29081
+ includeText: {
29082
+ type: "boolean",
29083
+ description: "\u662F\u5426\u5305\u542B\u5143\u7D20\u7684\u6587\u672C/\u5360\u4F4D\u7B26\u7B49\u4FE1\u606F\uFF0C\u9ED8\u8BA4 true"
29084
+ },
29085
+ maxElements: {
29086
+ type: "number",
29087
+ description: "\u6700\u5927\u8FD4\u56DE\u5143\u7D20\u6570\u91CF\uFF0C\u9ED8\u8BA4 100"
29088
+ }
29089
+ }
29090
+ }
29091
+ },
29092
+ {
29093
+ name: "dispatch_action",
29094
+ description: "\u3010\u64CD\u4F5C\u9875\u9762\u5143\u7D20\u3011\u5BF9 get_interactive_snapshot \u8FD4\u56DE\u7684\u5143\u7D20\u6267\u884C\u52A8\u4F5C\u3002\u901A\u8FC7 ref \u6807\u8BC6\uFF08\u5982 e1, e5\uFF09\u7CBE\u51C6\u5B9A\u4F4D\u5143\u7D20\uFF0C\u4F7F\u7528 CDP \u7269\u7406\u7EA7\u6A21\u62DF\u6267\u884C\u64CD\u4F5C\uFF0C\u517C\u5BB9\u6240\u6709\u524D\u7AEF\u6846\u67B6\uFF08React/Vue/Angular\uFF09\uFF0C\u6210\u529F\u7387\u6781\u9AD8\u3002\n\u652F\u6301\u7684\u52A8\u4F5C\uFF1Aclick\uFF08\u70B9\u51FB\uFF09\u3001fill\uFF08\u586B\u5199\u8F93\u5165\u6846\uFF09\u3001press\uFF08\u6309\u952E\u5982 Enter\uFF09\u3001scroll\uFF08\u6EDA\u52A8\uFF09\u3001select\uFF08\u4E0B\u62C9\u9009\u62E9\uFF09\u3001hover\uFF08\u60AC\u505C\uFF09\u3001focus\uFF08\u805A\u7126\uFF09\u3002\n\u26A0\uFE0F \u4F7F\u7528\u524D\u5FC5\u987B\u5148\u8C03\u7528 get_interactive_snapshot \u83B7\u53D6\u5143\u7D20\u5217\u8868\u3002\u64CD\u4F5C\u540E\u5EFA\u8BAE\u7528 capture_screenshot \u6216\u518D\u6B21 get_interactive_snapshot \u9A8C\u8BC1\u7ED3\u679C\u3002",
29095
+ inputSchema: {
29096
+ type: "object",
29097
+ properties: {
29098
+ ref: {
29099
+ type: "string",
29100
+ description: "\u76EE\u6807\u5143\u7D20\u7684 ref \u6807\u8BC6\uFF0C\u5982 'e1'\u3001'e5'\uFF08\u4ECE get_interactive_snapshot \u83B7\u53D6\uFF09"
29101
+ },
29102
+ action: {
29103
+ type: "string",
29104
+ enum: ["click", "fill", "press", "scroll", "select", "hover", "focus"],
29105
+ description: "\u8981\u6267\u884C\u7684\u52A8\u4F5C\u7C7B\u578B"
29106
+ },
29107
+ value: {
29108
+ type: "string",
29109
+ description: "fill \u65F6\u4E3A\u8981\u8F93\u5165\u7684\u6587\u672C\uFF1Bselect \u65F6\u4E3A\u8981\u9009\u62E9\u7684 option value\uFF1Bpress \u65F6\u4E3A\u6309\u952E\u540D\uFF08\u53EF\u9009\uFF09"
29110
+ },
29111
+ key: {
29112
+ type: "string",
29113
+ description: "press \u52A8\u4F5C\u7684\u6309\u952E\u540D\uFF0C\u5982 'Enter'\u3001'Escape'\u3001'Tab'\u3001'Backspace'\u3002\u9ED8\u8BA4 'Enter'"
29114
+ },
29115
+ deltaX: {
29116
+ type: "number",
29117
+ description: "scroll \u52A8\u4F5C\u7684\u6C34\u5E73\u6EDA\u52A8\u91CF\uFF08\u50CF\u7D20\uFF09\uFF0C\u9ED8\u8BA4 0"
29118
+ },
29119
+ deltaY: {
29120
+ type: "number",
29121
+ description: "scroll \u52A8\u4F5C\u7684\u5782\u76F4\u6EDA\u52A8\u91CF\uFF08\u50CF\u7D20\uFF09\uFF0C\u9ED8\u8BA4 300\uFF08\u6B63\u6570\u5411\u4E0B\uFF0C\u8D1F\u6570\u5411\u4E0A\uFF09"
29122
+ },
29123
+ waitMs: {
29124
+ type: "number",
29125
+ description: "\u64CD\u4F5C\u540E\u7B49\u5F85\u9875\u9762\u54CD\u5E94\u7684\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 500\uFF0C\u6700\u5927 3000"
29126
+ }
29127
+ },
29128
+ required: ["ref", "action"]
29129
+ }
29070
29130
  }
29071
29131
  ]
29072
29132
  }));
@@ -29223,6 +29283,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
29223
29283
  const res = await askChrome("getPageContent", { mode, selector, maxLength, includeMetadata });
29224
29284
  return { content: [{ type: "text", text: jsonText(res) }] };
29225
29285
  }
29286
+ if (name === "get_interactive_snapshot") {
29287
+ const { selector, includeText, maxElements } = args;
29288
+ const res = await askChrome("getInteractiveSnapshot", { selector, includeText, maxElements });
29289
+ return { content: [{ type: "text", text: jsonText(res) }] };
29290
+ }
29291
+ if (name === "dispatch_action") {
29292
+ const { ref, action, value, key, deltaX, deltaY, waitMs } = args;
29293
+ const res = await askChrome("dispatchAction", { ref, action, value, key, deltaX, deltaY, waitMs }, { timeoutMs: 1e4 });
29294
+ return { content: [{ type: "text", text: jsonText(res) }] };
29295
+ }
29226
29296
  return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177\uFF1A${name}` }] };
29227
29297
  } catch (e) {
29228
29298
  return { content: [{ type: "text", text: `Error: ${e.message}` }] };