@attson/atwebpilot-mcp 0.0.68 → 0.0.70
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/README.md +2 -2
- package/dist/index.js +107 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,11 +17,11 @@ Claude Code:
|
|
|
17
17
|
**不需要手填端口。** 从 Plan 33 起是配对流程:
|
|
18
18
|
|
|
19
19
|
1. 会话启动时**不绑任何端口** —— 不碰网页的会话零副作用。
|
|
20
|
-
2. AI 第一次调 `list_tabs` / `browser_*`
|
|
20
|
+
2. AI 第一次调 `list_tabs` / `browser_*` 时才绑一个空闲端口,尝试用系统默认浏览器打开配对页,并立即在 MCP 进度消息中显示配对 URL。
|
|
21
21
|
3. 你在浏览器里点「允许」,扩展即接入;正在等待的调用会自动继续,无需告诉 AI 授权成功或手动重试。
|
|
22
22
|
4. 之后本机的会话都免确认(信任是安装级的);端口能复用时连配对页都不会弹。
|
|
23
23
|
|
|
24
|
-
首次调用默认等待授权 90
|
|
24
|
+
首次调用默认等待授权 90 秒。若系统未弹出浏览器,可直接打开进度消息中的 URL;拒绝或超时也会返回明确错误和配对页地址。授权成功只以扩展实际建立连接并发送 `HELLO` 为准。
|
|
25
25
|
|
|
26
26
|
多个 Codex / Claude Code 会话可以同时接入同一个浏览器,各自一条连接。可在扩展设置 →
|
|
27
27
|
Coordinator 查看已接入的会话、单独断开,或撤销信任。
|
package/dist/index.js
CHANGED
|
@@ -932,7 +932,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprot
|
|
|
932
932
|
var CONTROL_TOOLS = [
|
|
933
933
|
{
|
|
934
934
|
name: "list_tabs",
|
|
935
|
-
description: "\u5217\u51FA\
|
|
935
|
+
description: "\u5217\u51FA\u6D4F\u89C8\u5668\u53EF\u7528\u6807\u7B7E\u9875\uFF1A[{tab_id,url,title}]\u3002\u82E5\u6269\u5C55\u5C1A\u672A\u8FDE\u63A5\uFF0C\u9996\u6B21\u8C03\u7528\u4F1A\u5C1D\u8BD5\u81EA\u52A8\u6253\u5F00\u914D\u5BF9\u9875\u5E76\u7B49\u5F85\u6388\u6743\uFF08\u6700\u591A 90 \u79D2\uFF09\uFF0C\u7B49\u5F85\u671F\u95F4\u4F1A\u901A\u77E5\u5B9E\u9645\u914D\u5BF9 URL\u3002\u5148\u8C03\u5B83\u62FF tab_id\u3002",
|
|
936
936
|
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
937
937
|
},
|
|
938
938
|
{
|
|
@@ -1312,7 +1312,7 @@ var TOOL_DEFS = [
|
|
|
1312
1312
|
},
|
|
1313
1313
|
{
|
|
1314
1314
|
name: "runJS",
|
|
1315
|
-
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",
|
|
1315
|
+
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",
|
|
1316
1316
|
input_schema: {
|
|
1317
1317
|
type: "object",
|
|
1318
1318
|
properties: {
|
|
@@ -1993,16 +1993,16 @@ function locate(source, offset) {
|
|
|
1993
1993
|
}
|
|
1994
1994
|
|
|
1995
1995
|
// src/handlers.ts
|
|
1996
|
-
async function handleListTabs(deps) {
|
|
1996
|
+
async function handleListTabs(deps, onPairingRequired) {
|
|
1997
1997
|
const { coordinator } = await deps.ensure();
|
|
1998
|
-
const workerId = await deps.waitForWorker();
|
|
1998
|
+
const workerId = await deps.waitForWorker(void 0, onPairingRequired);
|
|
1999
1999
|
const w = coordinator.workers.get(workerId);
|
|
2000
2000
|
if (!w) throw new Error(`worker ${workerId} disconnected while opening browser context`);
|
|
2001
2001
|
return { tabs: w.available_tabs };
|
|
2002
2002
|
}
|
|
2003
|
-
async function handleOpenSession(deps, args) {
|
|
2003
|
+
async function handleOpenSession(deps, args, onPairingRequired) {
|
|
2004
2004
|
const { coordinator } = await deps.ensure();
|
|
2005
|
-
const worker_id = await deps.waitForWorker();
|
|
2005
|
+
const worker_id = await deps.waitForWorker(void 0, onPairingRequired);
|
|
2006
2006
|
const tab_id = String(args.tab_id);
|
|
2007
2007
|
const requested = Array.isArray(args.capabilities) ? args.capabilities.map(String).filter(isCapability) : [];
|
|
2008
2008
|
const scope = new Set(requested.length ? requested : CAPABILITIES);
|
|
@@ -2181,14 +2181,14 @@ function toolResult(gen, data) {
|
|
|
2181
2181
|
return { content: [{ type: "image", data: d.data, mimeType }] };
|
|
2182
2182
|
}
|
|
2183
2183
|
var fail2 = (message) => ({ content: [{ type: "text", text: message }], isError: true });
|
|
2184
|
-
async function dispatchCall(deps, name, args) {
|
|
2184
|
+
async function dispatchCall(deps, name, args, onPairingRequired) {
|
|
2185
2185
|
try {
|
|
2186
2186
|
if (name === SKILL_TOOL.name) {
|
|
2187
2187
|
const bundle = readSkillBundle();
|
|
2188
2188
|
return { content: [{ type: "text", text: bundle.content }] };
|
|
2189
2189
|
}
|
|
2190
|
-
if (name === "list_tabs") return ok(await handleListTabs(deps));
|
|
2191
|
-
if (name === "open_session") return ok(await handleOpenSession(deps, args));
|
|
2190
|
+
if (name === "list_tabs") return ok(await handleListTabs(deps, onPairingRequired));
|
|
2191
|
+
if (name === "open_session") return ok(await handleOpenSession(deps, args, onPairingRequired));
|
|
2192
2192
|
if (name === "close_session") return ok(await handleCloseSession(deps, args));
|
|
2193
2193
|
if (name === "get_quota") return ok(await handleGetQuota(deps, args));
|
|
2194
2194
|
const gen = BROWSER_BY_NAME.get(name);
|
|
@@ -2199,11 +2199,33 @@ async function dispatchCall(deps, name, args) {
|
|
|
2199
2199
|
}
|
|
2200
2200
|
}
|
|
2201
2201
|
function createMcpServer(deps) {
|
|
2202
|
-
const server = new Server(
|
|
2202
|
+
const server = new Server(
|
|
2203
|
+
{ name: "atwebpilot-mcp", version: "0.1.0" },
|
|
2204
|
+
{ capabilities: { tools: {}, logging: {} } }
|
|
2205
|
+
);
|
|
2203
2206
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: buildToolList(deps) }));
|
|
2204
|
-
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
2207
|
+
server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
|
|
2205
2208
|
const args = req.params.arguments ?? {};
|
|
2206
|
-
|
|
2209
|
+
const onPairingRequired = async (url) => {
|
|
2210
|
+
const message = `\u7B49\u5F85\u6D4F\u89C8\u5668\u6388\u6743\uFF08\u6700\u591A 90 \u79D2\uFF09\u3002\u914D\u5BF9\u9875\uFF1A${url}\u3002\u5DF2\u5C1D\u8BD5\u81EA\u52A8\u6253\u5F00\uFF1B\u82E5\u672A\u5F39\u51FA\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00\u3002`;
|
|
2211
|
+
try {
|
|
2212
|
+
const progressToken = extra._meta?.progressToken;
|
|
2213
|
+
if (progressToken != null) {
|
|
2214
|
+
await extra.sendNotification({
|
|
2215
|
+
method: "notifications/progress",
|
|
2216
|
+
params: { progressToken, progress: 0, total: 1, message }
|
|
2217
|
+
});
|
|
2218
|
+
} else {
|
|
2219
|
+
await server.sendLoggingMessage({ level: "info", logger: "atwebpilot", data: message });
|
|
2220
|
+
}
|
|
2221
|
+
} catch (error) {
|
|
2222
|
+
console.error(
|
|
2223
|
+
"[atwebpilot-mcp] failed to report pairing URL:",
|
|
2224
|
+
error instanceof Error ? error.message : String(error)
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
return dispatchCall(deps, req.params.name, args, onPairingRequired);
|
|
2207
2229
|
});
|
|
2208
2230
|
return server;
|
|
2209
2231
|
}
|
|
@@ -2695,6 +2717,7 @@ function createHubEnsurer(d) {
|
|
|
2695
2717
|
let inflight = null;
|
|
2696
2718
|
let pairUrlValue = null;
|
|
2697
2719
|
let opened = false;
|
|
2720
|
+
let opening = null;
|
|
2698
2721
|
let hubIsClosed = false;
|
|
2699
2722
|
let pendingDenial = false;
|
|
2700
2723
|
let workerWait = null;
|
|
@@ -2773,20 +2796,33 @@ function createHubEnsurer(d) {
|
|
|
2773
2796
|
};
|
|
2774
2797
|
}
|
|
2775
2798
|
}
|
|
2776
|
-
async function
|
|
2777
|
-
if (
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
return b2;
|
|
2781
|
-
});
|
|
2782
|
-
const b = await inflight;
|
|
2783
|
-
if (!opened) {
|
|
2799
|
+
async function openPairPage() {
|
|
2800
|
+
if (opened || !pairUrlValue || !d.openUrl) return;
|
|
2801
|
+
const url = pairUrlValue;
|
|
2802
|
+
opening ??= Promise.resolve().then(() => d.openUrl(url)).then(() => {
|
|
2784
2803
|
opened = true;
|
|
2785
|
-
|
|
2804
|
+
}).catch((error) => {
|
|
2805
|
+
console.error(
|
|
2806
|
+
`[atwebpilot-mcp] failed to open pairing page ${url}:`,
|
|
2807
|
+
error instanceof Error ? error.message : String(error)
|
|
2808
|
+
);
|
|
2809
|
+
}).finally(() => {
|
|
2810
|
+
opening = null;
|
|
2811
|
+
});
|
|
2812
|
+
await opening;
|
|
2813
|
+
}
|
|
2814
|
+
async function ensure() {
|
|
2815
|
+
if (!bundle) {
|
|
2816
|
+
inflight ??= bind().then((b) => {
|
|
2817
|
+
bundle = b;
|
|
2818
|
+
return b;
|
|
2819
|
+
});
|
|
2820
|
+
await inflight;
|
|
2786
2821
|
}
|
|
2787
|
-
|
|
2822
|
+
await openPairPage();
|
|
2823
|
+
return bundle;
|
|
2788
2824
|
}
|
|
2789
|
-
async function waitForWorker(timeoutMs = DEFAULT_WORKER_WAIT_TIMEOUT_MS) {
|
|
2825
|
+
async function waitForWorker(timeoutMs = DEFAULT_WORKER_WAIT_TIMEOUT_MS, onWaiting) {
|
|
2790
2826
|
const { coordinator } = await ensure();
|
|
2791
2827
|
if (hubIsClosed) throw new Error("\u6D4F\u89C8\u5668\u914D\u5BF9\u670D\u52A1\u5DF2\u5173\u95ED\u3002");
|
|
2792
2828
|
const connected = connectedWorkerId(coordinator);
|
|
@@ -2797,6 +2833,7 @@ function createHubEnsurer(d) {
|
|
|
2797
2833
|
"\u7528\u6237\u62D2\u7EDD\u4E86\u6D4F\u89C8\u5668\u914D\u5BF9\u6388\u6743\u3002" + (pairUrlValue ? `\u914D\u5BF9\u9875\uFF1A${pairUrlValue}` : "")
|
|
2798
2834
|
);
|
|
2799
2835
|
}
|
|
2836
|
+
if (pairUrlValue) await onWaiting?.(pairUrlValue);
|
|
2800
2837
|
if (workerWait) return workerWait.promise;
|
|
2801
2838
|
let resolve;
|
|
2802
2839
|
let reject;
|
|
@@ -2807,7 +2844,7 @@ function createHubEnsurer(d) {
|
|
|
2807
2844
|
const timer = setTimeout(() => {
|
|
2808
2845
|
finishWorkerWait({
|
|
2809
2846
|
error: new Error(
|
|
2810
|
-
`\u7B49\u5F85\u6D4F\u89C8\u5668\u6388\u6743\u8D85\u65F6\uFF08${Math.ceil(timeoutMs / 1e3)} \u79D2\uFF09\u3002` + (pairUrlValue ? `\u8BF7\
|
|
2847
|
+
`\u7B49\u5F85\u6D4F\u89C8\u5668\u6388\u6743\u8D85\u65F6\uFF08${Math.ceil(timeoutMs / 1e3)} \u79D2\uFF09\u3002` + (pairUrlValue ? `\u8BF7\u6253\u5F00\u914D\u5BF9\u9875 ${pairUrlValue} \u5B8C\u6210\u6388\u6743\u540E\u91CD\u8BD5\u3002` : "")
|
|
2811
2848
|
)
|
|
2812
2849
|
});
|
|
2813
2850
|
}, timeoutMs);
|
|
@@ -2823,14 +2860,54 @@ function createHubEnsurer(d) {
|
|
|
2823
2860
|
bound: () => bundle != null
|
|
2824
2861
|
};
|
|
2825
2862
|
}
|
|
2826
|
-
function
|
|
2827
|
-
|
|
2828
|
-
|
|
2863
|
+
function openCommands(url) {
|
|
2864
|
+
if (process.platform === "darwin") return [{ command: "open", args: [url] }];
|
|
2865
|
+
if (process.platform === "win32") {
|
|
2866
|
+
return [{ command: "cmd.exe", args: ["/d", "/s", "/c", "start", "", url] }];
|
|
2867
|
+
}
|
|
2868
|
+
if (process.env.WSL_DISTRO_NAME) {
|
|
2869
|
+
return [{ command: "cmd.exe", args: ["/c", "start", "", url] }];
|
|
2870
|
+
}
|
|
2871
|
+
return [
|
|
2872
|
+
{ command: "xdg-open", args: [url] },
|
|
2873
|
+
{ command: "gio", args: ["open", url] }
|
|
2874
|
+
];
|
|
2875
|
+
}
|
|
2876
|
+
async function runOpenCommand({ command, args }) {
|
|
2877
|
+
const { spawn } = await import("child_process");
|
|
2878
|
+
await new Promise((resolve, reject) => {
|
|
2879
|
+
const child = spawn(command, args, { stdio: "ignore", detached: true });
|
|
2880
|
+
let settled = false;
|
|
2881
|
+
const finish = (error) => {
|
|
2882
|
+
if (settled) return;
|
|
2883
|
+
settled = true;
|
|
2884
|
+
clearTimeout(timer);
|
|
2885
|
+
if (error) reject(error);
|
|
2886
|
+
else resolve();
|
|
2887
|
+
};
|
|
2888
|
+
const timer = setTimeout(() => {
|
|
2889
|
+
child.unref();
|
|
2890
|
+
finish();
|
|
2891
|
+
}, 3e3);
|
|
2892
|
+
timer.unref?.();
|
|
2893
|
+
child.once("error", (error) => finish(error));
|
|
2894
|
+
child.once("exit", (code, signal) => {
|
|
2895
|
+
if (code === 0) finish();
|
|
2896
|
+
else finish(new Error(`${command} exited with ${code ?? signal ?? "unknown status"}`));
|
|
2897
|
+
});
|
|
2898
|
+
});
|
|
2899
|
+
}
|
|
2900
|
+
async function defaultOpenUrl(url) {
|
|
2901
|
+
const errors = [];
|
|
2902
|
+
for (const candidate of openCommands(url)) {
|
|
2829
2903
|
try {
|
|
2830
|
-
|
|
2831
|
-
|
|
2904
|
+
await runOpenCommand(candidate);
|
|
2905
|
+
return;
|
|
2906
|
+
} catch (error) {
|
|
2907
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
2832
2908
|
}
|
|
2833
|
-
}
|
|
2909
|
+
}
|
|
2910
|
+
throw new Error(errors.join("; ") || "no browser opener available");
|
|
2834
2911
|
}
|
|
2835
2912
|
|
|
2836
2913
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attson/atwebpilot-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.70",
|
|
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",
|