@attson/atwebpilot-mcp 0.0.80 → 0.0.81
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 -1
- package/dist/index.js +347 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,6 +51,7 @@ listen 端口的能力,所以方向只能如此。这也是配对页存在的
|
|
|
51
51
|
- 控制面 5 个:`pairing_status / list_tabs / open_session / close_session / get_quota`
|
|
52
52
|
- 发现 1 个:`browser_discoverTools` —— 已知工具时直接传 `enable: [...]`,已知分组时直接传 `enableGroups: ["export", ...]`,也可用 `profile: "core" | "full"` 切换整套工具面;只有这些都未知时才不带参数读取目录。选中的工具会加入本进程的 `tools/list`(发送 `tools/list_changed`),并始终返回完整 schema,包括已经启用的工具。
|
|
53
53
|
- 同轮执行桥 1 个:`browser_call({session_id, tool, args})` —— 某些宿主虽已刷新 `tools/list`,却不会在当前模型轮次注入新工具。AI 可在 `discoverTools` 后立刻通过该桥执行,不需要让用户回复“继续”;下一轮看到原生工具后仍优先使用原生工具。
|
|
54
|
+
- 批量编排 1 个:`browser_runSteps({session_id, steps, return_bindings?})` —— 在步骤和参数已经明确时,把 1–20 个 safe/caution 工具合并为一次 MCP 调用;`${binding}` 可引用前序结果。整批先做工具支持、权限和配额预检,dangerous、脚本、截图、下载与 tab 生命周期操作保持单步。
|
|
54
55
|
- 执行面在浏览器连接前不进入 `tools/list`;连接确认后加载默认 **core 31 个** `browser_*`。其余 20 个由 AI 按需 `discoverTools` 拉取,用户不用配置。
|
|
55
56
|
- 不暴露 `askUser`(MCP 会话没有人在侧边栏应答)和 `attachTab` / `detachTab`(目标 tab 已由 `open_session` 绑定)。
|
|
56
57
|
|
|
@@ -66,7 +67,7 @@ hover / uploadFile / scroll / waitFor`);`browser_storage` 也不会列出,
|
|
|
66
67
|
|
|
67
68
|
| 值 | 效果 |
|
|
68
69
|
|---|---|
|
|
69
|
-
| `core`(默认) |
|
|
70
|
+
| `core`(默认) | 连接前仅控制面、发现工具、同轮执行桥与批量编排;连接后再列出 31 个核心工具 |
|
|
70
71
|
| `full` | 连接前同上;连接后列出全部 51 个原生工具 |
|
|
71
72
|
|
|
72
73
|
无法识别的值(包括已移除的 `parity`)按 `core` 处理,并往 stderr 打一条提示。
|
package/dist/index.js
CHANGED
|
@@ -2414,7 +2414,7 @@ var NON_BUILTIN_CAPABILITY = {
|
|
|
2414
2414
|
listTabs: "tab:open",
|
|
2415
2415
|
openTab: "tab:open"
|
|
2416
2416
|
};
|
|
2417
|
-
async function
|
|
2417
|
+
async function prepareBrowserTool(deps, gen, args) {
|
|
2418
2418
|
const { coordinator, hub } = await deps.ensure();
|
|
2419
2419
|
const session_id = String(args.session_id);
|
|
2420
2420
|
const session = coordinator.sessions.get(session_id);
|
|
@@ -2429,23 +2429,20 @@ async function handleBrowserTool(deps, gen, args) {
|
|
|
2429
2429
|
const unsafe = highestSeverity(runStaticScan(source)) === "dangerous";
|
|
2430
2430
|
const v = coordinator.validateCall({ session_id, kind: "runJS", unsafe });
|
|
2431
2431
|
if (!v.ok) throw new Error(`${v.error.code}: ${v.error.message}`);
|
|
2432
|
-
coordinator.recordCall(session_id, v.dangerous);
|
|
2433
2432
|
const requestedTimeout = toolArgs.timeoutMs;
|
|
2434
|
-
|
|
2433
|
+
return {
|
|
2434
|
+
coordinator,
|
|
2435
|
+
hub,
|
|
2435
2436
|
session_id,
|
|
2437
|
+
worker_id: session.worker_id,
|
|
2436
2438
|
tab_id: session.tab_id,
|
|
2437
2439
|
step: {
|
|
2438
2440
|
kind: "js",
|
|
2439
2441
|
source,
|
|
2440
2442
|
...typeof requestedTimeout === "number" && Number.isInteger(requestedTimeout) && requestedTimeout > 0 ? { timeoutMs: requestedTimeout } : {}
|
|
2441
|
-
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
throw new Error(
|
|
2445
|
-
jsResult.error ? `${jsResult.error.code}: ${jsResult.error.message}` : "EXEC failed"
|
|
2446
|
-
);
|
|
2447
|
-
}
|
|
2448
|
-
return jsResult.return ?? null;
|
|
2443
|
+
},
|
|
2444
|
+
dangerous: v.dangerous
|
|
2445
|
+
};
|
|
2449
2446
|
}
|
|
2450
2447
|
if (tool === "switchToTab" || tool === "closeTab") {
|
|
2451
2448
|
const boundTabId = Number.parseInt(session.tab_id, 10);
|
|
@@ -2455,10 +2452,11 @@ async function handleBrowserTool(deps, gen, args) {
|
|
|
2455
2452
|
toolArgs.tabId = boundTabId;
|
|
2456
2453
|
}
|
|
2457
2454
|
const override = NON_BUILTIN_CAPABILITY[tool];
|
|
2455
|
+
let dangerous;
|
|
2458
2456
|
if (override) {
|
|
2459
2457
|
const v = coordinator.validateCall({ session_id, kind: "capability", capability: override });
|
|
2460
2458
|
if (!v.ok) throw new Error(`${v.error.code}: ${v.error.message}`);
|
|
2461
|
-
|
|
2459
|
+
dangerous = v.dangerous;
|
|
2462
2460
|
} else {
|
|
2463
2461
|
const raw = toolArgs;
|
|
2464
2462
|
const v = coordinator.validateCall({
|
|
@@ -2470,13 +2468,321 @@ async function handleBrowserTool(deps, gen, args) {
|
|
|
2470
2468
|
recorderArmsBodies: tool === "recorderConfig" ? raw.bodies === true : void 0
|
|
2471
2469
|
});
|
|
2472
2470
|
if (!v.ok) throw new Error(`${v.error.code}: ${v.error.message}`);
|
|
2473
|
-
|
|
2471
|
+
dangerous = v.dangerous;
|
|
2474
2472
|
}
|
|
2475
|
-
|
|
2473
|
+
return {
|
|
2474
|
+
coordinator,
|
|
2475
|
+
hub,
|
|
2476
|
+
session_id,
|
|
2477
|
+
worker_id: session.worker_id,
|
|
2478
|
+
tab_id: session.tab_id,
|
|
2479
|
+
step: { kind: "tool", tool, args: toolArgs },
|
|
2480
|
+
dangerous
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
async function handleBrowserTool(deps, gen, args) {
|
|
2484
|
+
const prepared = await prepareBrowserTool(deps, gen, args);
|
|
2485
|
+
prepared.coordinator.recordCall(prepared.session_id, prepared.dangerous);
|
|
2486
|
+
const result = await prepared.hub.exec(prepared.worker_id, {
|
|
2487
|
+
session_id: prepared.session_id,
|
|
2488
|
+
tab_id: prepared.tab_id,
|
|
2489
|
+
step: prepared.step
|
|
2490
|
+
});
|
|
2476
2491
|
if (!result.ok) throw new Error(result.error ? `${result.error.code}: ${result.error.message}` : "EXEC failed");
|
|
2477
2492
|
return result.return ?? null;
|
|
2478
2493
|
}
|
|
2479
2494
|
|
|
2495
|
+
// src/browser-run-steps.ts
|
|
2496
|
+
var BROWSER_RUN_STEPS_TOOL = {
|
|
2497
|
+
name: "browser_runSteps",
|
|
2498
|
+
description: "Run 1-20 enabled safe/caution browser tools sequentially in one MCP call. Supports ${binding} references and returns only the final result plus requested bindings.",
|
|
2499
|
+
inputSchema: {
|
|
2500
|
+
type: "object",
|
|
2501
|
+
required: ["session_id", "steps"],
|
|
2502
|
+
properties: {
|
|
2503
|
+
session_id: { type: "string", description: "Session id from open_session" },
|
|
2504
|
+
steps: {
|
|
2505
|
+
type: "array",
|
|
2506
|
+
minItems: 1,
|
|
2507
|
+
maxItems: 20,
|
|
2508
|
+
items: {
|
|
2509
|
+
type: "object",
|
|
2510
|
+
required: ["tool", "args"],
|
|
2511
|
+
properties: {
|
|
2512
|
+
tool: {
|
|
2513
|
+
type: "string",
|
|
2514
|
+
description: "Enabled browser tool name; browser_ prefix is optional"
|
|
2515
|
+
},
|
|
2516
|
+
args: {
|
|
2517
|
+
type: "object",
|
|
2518
|
+
description: "Tool arguments excluding session_id; may reference earlier ${bindings}",
|
|
2519
|
+
additionalProperties: true
|
|
2520
|
+
},
|
|
2521
|
+
bind_result_to: {
|
|
2522
|
+
type: "string",
|
|
2523
|
+
pattern: "^[A-Za-z_][A-Za-z0-9_]{0,63}$"
|
|
2524
|
+
}
|
|
2525
|
+
},
|
|
2526
|
+
additionalProperties: false
|
|
2527
|
+
}
|
|
2528
|
+
},
|
|
2529
|
+
return_bindings: {
|
|
2530
|
+
type: "array",
|
|
2531
|
+
items: {
|
|
2532
|
+
type: "string",
|
|
2533
|
+
pattern: "^[A-Za-z_][A-Za-z0-9_]{0,63}$"
|
|
2534
|
+
},
|
|
2535
|
+
uniqueItems: true
|
|
2536
|
+
}
|
|
2537
|
+
},
|
|
2538
|
+
additionalProperties: false
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2541
|
+
var BINDING_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
2542
|
+
var BLOCKED_BATCH_TOOLS = /* @__PURE__ */ new Set([
|
|
2543
|
+
"browser_downloadImage",
|
|
2544
|
+
"browser_downloadSpreadsheet",
|
|
2545
|
+
"browser_openTab",
|
|
2546
|
+
"browser_closeTab",
|
|
2547
|
+
"browser_switchToTab"
|
|
2548
|
+
]);
|
|
2549
|
+
var CAPABILITY_SENSITIVE_FIELDS = /* @__PURE__ */ new Map([
|
|
2550
|
+
["browser_httpRequest", ["withCredentials"]],
|
|
2551
|
+
["browser_drop", ["files"]],
|
|
2552
|
+
["browser_recorderConfig", ["bodies"]]
|
|
2553
|
+
]);
|
|
2554
|
+
function runStepsPreflightError(message) {
|
|
2555
|
+
return {
|
|
2556
|
+
ok: false,
|
|
2557
|
+
phase: "preflight",
|
|
2558
|
+
completed: 0,
|
|
2559
|
+
error: { message },
|
|
2560
|
+
bindings: {}
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
async function handleBrowserRunSteps(deps, args, context) {
|
|
2564
|
+
const sessionId = typeof args.session_id === "string" ? args.session_id : "";
|
|
2565
|
+
if (!sessionId) throw new Error("InvalidArgs: browser_runSteps requires session_id");
|
|
2566
|
+
if (!Array.isArray(args.steps) || args.steps.length < 1 || args.steps.length > 20) {
|
|
2567
|
+
throw new Error("InvalidArgs: browser_runSteps requires 1-20 steps");
|
|
2568
|
+
}
|
|
2569
|
+
const steps = args.steps.map(parseStep);
|
|
2570
|
+
const requested = parseReturnBindings(args.return_bindings);
|
|
2571
|
+
const bindings = /* @__PURE__ */ Object.create(null);
|
|
2572
|
+
let result = null;
|
|
2573
|
+
const bindingIssue = validateBindingPlan(steps, requested);
|
|
2574
|
+
if (bindingIssue) {
|
|
2575
|
+
return preflightFailure(steps, bindingIssue.index, bindingIssue.message);
|
|
2576
|
+
}
|
|
2577
|
+
const { coordinator } = await deps.ensure();
|
|
2578
|
+
const quota = coordinator.quotaFor(sessionId);
|
|
2579
|
+
if (!quota) throw new Error(`session ${sessionId} not found`);
|
|
2580
|
+
const remainingSteps = quota.max_steps - quota.steps_used;
|
|
2581
|
+
if (steps.length > remainingSteps) {
|
|
2582
|
+
const failedIndex = Math.max(0, remainingSteps);
|
|
2583
|
+
return preflightFailure(
|
|
2584
|
+
steps,
|
|
2585
|
+
failedIndex,
|
|
2586
|
+
`Session has only ${remainingSteps} steps remaining; batch requires ${steps.length}`
|
|
2587
|
+
);
|
|
2588
|
+
}
|
|
2589
|
+
for (let index = 0; index < steps.length; index++) {
|
|
2590
|
+
const step = steps[index];
|
|
2591
|
+
const name = browserToolName(step.tool);
|
|
2592
|
+
const generated = context.tools.get(name);
|
|
2593
|
+
try {
|
|
2594
|
+
assertAvailable(name, step.tool, generated, context);
|
|
2595
|
+
if (generated.stepKind === "js" || generated.resultKind === "image" || BLOCKED_BATCH_TOOLS.has(name)) {
|
|
2596
|
+
throw new Error(`Tool ${name} is not allowed in browser_runSteps`);
|
|
2597
|
+
}
|
|
2598
|
+
for (const field of CAPABILITY_SENSITIVE_FIELDS.get(name) ?? []) {
|
|
2599
|
+
if (bindingReferences(step.args[field]).length > 0) {
|
|
2600
|
+
throw new Error(
|
|
2601
|
+
`Capability-sensitive field ${field} cannot reference bindings in browser_runSteps`
|
|
2602
|
+
);
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
const prepared = await prepareBrowserTool(deps, generated, {
|
|
2606
|
+
...step.args,
|
|
2607
|
+
session_id: sessionId
|
|
2608
|
+
});
|
|
2609
|
+
if (prepared.dangerous) {
|
|
2610
|
+
throw new Error("Dangerous tool is not allowed in browser_runSteps");
|
|
2611
|
+
}
|
|
2612
|
+
} catch (error) {
|
|
2613
|
+
return {
|
|
2614
|
+
ok: false,
|
|
2615
|
+
phase: "preflight",
|
|
2616
|
+
completed: 0,
|
|
2617
|
+
failed_step: { index, tool: name },
|
|
2618
|
+
error: { message: error instanceof Error ? error.message : String(error) },
|
|
2619
|
+
bindings: {}
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
for (let index = 0; index < steps.length; index++) {
|
|
2624
|
+
const step = steps[index];
|
|
2625
|
+
const name = browserToolName(step.tool);
|
|
2626
|
+
const generated = context.tools.get(name);
|
|
2627
|
+
assertAvailable(name, step.tool, generated, context);
|
|
2628
|
+
const resolvedArgs = resolveBindings(step.args, bindings);
|
|
2629
|
+
try {
|
|
2630
|
+
result = await handleBrowserTool(deps, generated, { ...resolvedArgs, session_id: sessionId });
|
|
2631
|
+
} catch (error) {
|
|
2632
|
+
return {
|
|
2633
|
+
ok: false,
|
|
2634
|
+
phase: "execution",
|
|
2635
|
+
completed: index,
|
|
2636
|
+
failed_step: { index, tool: name },
|
|
2637
|
+
error: { message: error instanceof Error ? error.message : String(error) },
|
|
2638
|
+
bindings: selectBindings(requested, bindings, false)
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2641
|
+
if (step.bind_result_to) bindings[step.bind_result_to] = result;
|
|
2642
|
+
}
|
|
2643
|
+
return {
|
|
2644
|
+
ok: true,
|
|
2645
|
+
completed: steps.length,
|
|
2646
|
+
result,
|
|
2647
|
+
bindings: selectBindings(requested, bindings, true)
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
function preflightFailure(steps, index, message) {
|
|
2651
|
+
const boundedIndex = Math.min(Math.max(index, 0), steps.length - 1);
|
|
2652
|
+
return {
|
|
2653
|
+
ok: false,
|
|
2654
|
+
phase: "preflight",
|
|
2655
|
+
completed: 0,
|
|
2656
|
+
failed_step: {
|
|
2657
|
+
index: boundedIndex,
|
|
2658
|
+
tool: browserToolName(steps[boundedIndex].tool)
|
|
2659
|
+
},
|
|
2660
|
+
error: { message },
|
|
2661
|
+
bindings: {}
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
function parseReturnBindings(value) {
|
|
2665
|
+
if (value === void 0) return [];
|
|
2666
|
+
if (!Array.isArray(value) || value.some((name) => typeof name !== "string")) {
|
|
2667
|
+
throw new Error("InvalidArgs: return_bindings must be an array of binding names");
|
|
2668
|
+
}
|
|
2669
|
+
return value;
|
|
2670
|
+
}
|
|
2671
|
+
function validateBindingPlan(steps, requested) {
|
|
2672
|
+
const declared = /* @__PURE__ */ new Set();
|
|
2673
|
+
for (let index = 0; index < steps.length; index++) {
|
|
2674
|
+
const step = steps[index];
|
|
2675
|
+
for (const name of bindingReferences(step.args)) {
|
|
2676
|
+
if (!declared.has(name)) {
|
|
2677
|
+
return {
|
|
2678
|
+
index,
|
|
2679
|
+
message: `InvalidArgs: binding ${name} must be declared by an earlier step`
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
if (step.bind_result_to) {
|
|
2684
|
+
if (!BINDING_NAME.test(step.bind_result_to)) {
|
|
2685
|
+
return { index, message: `InvalidArgs: invalid binding name ${step.bind_result_to}` };
|
|
2686
|
+
}
|
|
2687
|
+
if (declared.has(step.bind_result_to)) {
|
|
2688
|
+
return { index, message: `InvalidArgs: duplicate binding ${step.bind_result_to}` };
|
|
2689
|
+
}
|
|
2690
|
+
declared.add(step.bind_result_to);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
const seenRequested = /* @__PURE__ */ new Set();
|
|
2694
|
+
for (const name of requested) {
|
|
2695
|
+
if (!BINDING_NAME.test(name)) {
|
|
2696
|
+
return { index: 0, message: `InvalidArgs: invalid return binding ${name}` };
|
|
2697
|
+
}
|
|
2698
|
+
if (seenRequested.has(name)) {
|
|
2699
|
+
return { index: 0, message: `InvalidArgs: duplicate return binding ${name}` };
|
|
2700
|
+
}
|
|
2701
|
+
if (!declared.has(name)) {
|
|
2702
|
+
return { index: 0, message: `InvalidArgs: unknown return binding ${name}` };
|
|
2703
|
+
}
|
|
2704
|
+
seenRequested.add(name);
|
|
2705
|
+
}
|
|
2706
|
+
return null;
|
|
2707
|
+
}
|
|
2708
|
+
function bindingReferences(value) {
|
|
2709
|
+
if (typeof value === "string") {
|
|
2710
|
+
return [...value.matchAll(/\$\{([^}]+)\}/g)].map((match) => match[1]);
|
|
2711
|
+
}
|
|
2712
|
+
if (Array.isArray(value)) return value.flatMap(bindingReferences);
|
|
2713
|
+
if (value && typeof value === "object") {
|
|
2714
|
+
return Object.values(value).flatMap(bindingReferences);
|
|
2715
|
+
}
|
|
2716
|
+
return [];
|
|
2717
|
+
}
|
|
2718
|
+
function assertAvailable(name, rawName, generated, context) {
|
|
2719
|
+
if (!generated) throw new Error(`Unknown browser tool: ${rawName}`);
|
|
2720
|
+
if (!context.advertised.has(name)) {
|
|
2721
|
+
throw new Error(`${name} is not enabled; call browser_discoverTools first`);
|
|
2722
|
+
}
|
|
2723
|
+
if (context.supported && !generated.builtinTools.every((tool) => context.supported.has(tool))) {
|
|
2724
|
+
throw new Error(`${name} is not supported by the connected browser extension`);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
function parseStep(value) {
|
|
2728
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2729
|
+
throw new Error("InvalidArgs: each browser_runSteps step must be an object");
|
|
2730
|
+
}
|
|
2731
|
+
const raw = value;
|
|
2732
|
+
if (typeof raw.tool !== "string" || raw.tool.trim() === "") {
|
|
2733
|
+
throw new Error("InvalidArgs: each browser_runSteps step requires tool");
|
|
2734
|
+
}
|
|
2735
|
+
if (!raw.args || typeof raw.args !== "object" || Array.isArray(raw.args)) {
|
|
2736
|
+
throw new Error("InvalidArgs: each browser_runSteps step requires object args");
|
|
2737
|
+
}
|
|
2738
|
+
if (raw.bind_result_to !== void 0 && typeof raw.bind_result_to !== "string") {
|
|
2739
|
+
throw new Error("InvalidArgs: bind_result_to must be a binding name");
|
|
2740
|
+
}
|
|
2741
|
+
return {
|
|
2742
|
+
tool: raw.tool,
|
|
2743
|
+
args: raw.args,
|
|
2744
|
+
...typeof raw.bind_result_to === "string" ? { bind_result_to: raw.bind_result_to } : {}
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
function browserToolName(raw) {
|
|
2748
|
+
const name = raw.trim();
|
|
2749
|
+
return name.startsWith("browser_") ? name : `browser_${name}`;
|
|
2750
|
+
}
|
|
2751
|
+
function resolveBindings(value, bindings) {
|
|
2752
|
+
if (typeof value === "string") {
|
|
2753
|
+
const exact = value.match(/^\$\{([^}]+)\}$/);
|
|
2754
|
+
if (exact) return requireBinding(exact[1], bindings);
|
|
2755
|
+
return value.replace(/\$\{([^}]+)\}/g, (_match, name) => {
|
|
2756
|
+
const bound = requireBinding(name, bindings);
|
|
2757
|
+
return typeof bound === "string" ? bound : JSON.stringify(bound);
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
if (Array.isArray(value)) return value.map((item) => resolveBindings(item, bindings));
|
|
2761
|
+
if (value && typeof value === "object") {
|
|
2762
|
+
const resolved = {};
|
|
2763
|
+
for (const [key, item] of Object.entries(value)) {
|
|
2764
|
+
resolved[key] = resolveBindings(item, bindings);
|
|
2765
|
+
}
|
|
2766
|
+
return resolved;
|
|
2767
|
+
}
|
|
2768
|
+
return value;
|
|
2769
|
+
}
|
|
2770
|
+
function requireBinding(name, bindings) {
|
|
2771
|
+
if (!(name in bindings)) throw new Error(`InvalidArgs: binding ${name} is not available`);
|
|
2772
|
+
return bindings[name];
|
|
2773
|
+
}
|
|
2774
|
+
function selectBindings(requested, bindings, requireAll) {
|
|
2775
|
+
const selected = {};
|
|
2776
|
+
for (const name of requested) {
|
|
2777
|
+
if (!(name in bindings)) {
|
|
2778
|
+
if (requireAll) throw new Error(`InvalidArgs: unknown return binding ${name}`);
|
|
2779
|
+
continue;
|
|
2780
|
+
}
|
|
2781
|
+
selected[name] = bindings[name];
|
|
2782
|
+
}
|
|
2783
|
+
return selected;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2480
2786
|
// src/skill-bundle.ts
|
|
2481
2787
|
import { readFileSync } from "fs";
|
|
2482
2788
|
import { dirname, join } from "path";
|
|
@@ -2490,8 +2796,9 @@ var TOPICS = {
|
|
|
2490
2796
|
2. If list_tabs times out and returns pairing_required, show pair_url and wait for browser approval. Use pairing_status for a later non-blocking status check; do not ask the user to reply just to continue.
|
|
2491
2797
|
3. Choose a returned tab, then call open_session with its tab_id.
|
|
2492
2798
|
4. For interaction use browser_getPageInfo then browser_takeSnapshot; for broad reading use browser_createPageIndex.
|
|
2493
|
-
5.
|
|
2494
|
-
6.
|
|
2799
|
+
5. When 2-20 safe/caution steps and their arguments are already known, use browser_runSteps to execute them in one MCP call. Keep dangerous actions and steps needing intermediate inspection as separate calls.
|
|
2800
|
+
6. Prefer purpose-built tools and verified results. Use browser_discoverTools only for capabilities outside the connected core surface. Its response always includes the selected schemas. If a newly enabled native tool is not visible in the current turn, call it immediately with browser_call; do not ask the user to send "continue".
|
|
2801
|
+
7. Close the session when finished.
|
|
2495
2802
|
`,
|
|
2496
2803
|
pairing: `# AtWebPilot pairing
|
|
2497
2804
|
|
|
@@ -2537,9 +2844,11 @@ function readSkillBundle(rawTopic = "quickstart") {
|
|
|
2537
2844
|
}
|
|
2538
2845
|
if (cached == null) {
|
|
2539
2846
|
try {
|
|
2540
|
-
cached =
|
|
2847
|
+
cached = `${TOPICS.quickstart}
|
|
2848
|
+
${readFileSync(locateSkillFile(), "utf-8")}`;
|
|
2541
2849
|
} catch {
|
|
2542
|
-
cached =
|
|
2850
|
+
cached = `${TOPICS.quickstart}
|
|
2851
|
+
# ${SKILL_NAME}
|
|
2543
2852
|
|
|
2544
2853
|
${SKILL_DESCRIPTION}
|
|
2545
2854
|
|
|
@@ -2569,7 +2878,7 @@ var SKILL_TOOL = {
|
|
|
2569
2878
|
// package.json
|
|
2570
2879
|
var package_default = {
|
|
2571
2880
|
name: "@attson/atwebpilot-mcp",
|
|
2572
|
-
version: "0.0.
|
|
2881
|
+
version: "0.0.81",
|
|
2573
2882
|
license: "Apache-2.0",
|
|
2574
2883
|
description: "MCP server for the atwebpilot browser extension - let Codex or Claude Code drive your browser.",
|
|
2575
2884
|
homepage: "https://github.com/attson/atwebpilot#readme",
|
|
@@ -2724,6 +3033,7 @@ function buildToolList(deps, state = DEFAULT_STATE) {
|
|
|
2724
3033
|
...CONTROL_TOOLS.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
|
|
2725
3034
|
{ name: DISCOVER_TOOL.name, description: DISCOVER_TOOL.description, inputSchema: DISCOVER_TOOL.inputSchema },
|
|
2726
3035
|
{ name: BROWSER_CALL_TOOL.name, description: BROWSER_CALL_TOOL.description, inputSchema: BROWSER_CALL_TOOL.inputSchema },
|
|
3036
|
+
{ name: BROWSER_RUN_STEPS_TOOL.name, description: BROWSER_RUN_STEPS_TOOL.description, inputSchema: BROWSER_RUN_STEPS_TOOL.inputSchema },
|
|
2727
3037
|
...browser.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema }))
|
|
2728
3038
|
];
|
|
2729
3039
|
}
|
|
@@ -2763,6 +3073,24 @@ async function dispatchCall(deps, name, args, state = DEFAULT_STATE, onListChang
|
|
|
2763
3073
|
if (name === BROWSER_CALL_TOOL.name) {
|
|
2764
3074
|
return handleBrowserCall(deps, args, state);
|
|
2765
3075
|
}
|
|
3076
|
+
if (name === BROWSER_RUN_STEPS_TOOL.name) {
|
|
3077
|
+
let result;
|
|
3078
|
+
try {
|
|
3079
|
+
result = await handleBrowserRunSteps(deps, args, {
|
|
3080
|
+
tools: BROWSER_BY_NAME,
|
|
3081
|
+
advertised: state.advertised,
|
|
3082
|
+
supported: workerToolSupport(deps)
|
|
3083
|
+
});
|
|
3084
|
+
} catch (error) {
|
|
3085
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3086
|
+
if (message.includes("PAIRING_REQUIRED")) {
|
|
3087
|
+
const pairUrl = deps.pairUrl();
|
|
3088
|
+
if (pairUrl) onPairingRequired?.(pairUrl);
|
|
3089
|
+
}
|
|
3090
|
+
result = runStepsPreflightError(message);
|
|
3091
|
+
}
|
|
3092
|
+
return result.ok ? ok(result) : { ...ok(result), isError: true };
|
|
3093
|
+
}
|
|
2766
3094
|
if (name === "pairing_status") {
|
|
2767
3095
|
const result = await handlePairingStatus(deps);
|
|
2768
3096
|
if (result.status === "connected") await revealBrowserSurface(state, onListChanged);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attson/atwebpilot-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.81",
|
|
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",
|