@attson/atwebpilot-mcp 0.0.77 → 0.0.78
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 +6 -5
- package/dist/index.js +133 -17
- package/dist/skill/SKILL.md +12 -2
- 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` / `pairing_status`
|
|
20
|
+
2. AI 第一次调 `list_tabs` / `pairing_status` 时才绑一个空闲端口,通过 MCP URL elicitation 请宿主打开配对页,并立即返回 `pairing_required + pair_url`,不阻塞等待;不支持该能力的宿主回退到系统默认浏览器。
|
|
21
21
|
3. 你在浏览器里点「允许」,扩展即接入;AI 用轻量 `pairing_status` 确认连接后再读取 tab,不重复阻塞式调用。
|
|
22
22
|
4. 之后本机的会话都免确认(信任是安装级的);端口能复用时连配对页都不会弹。
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
若宿主和系统都未弹出浏览器,可直接打开返回结果中的 `pair_url`。授权成功只以扩展实际建立连接并发送 `HELLO` 为准;打开配对页本身不代表已连接。
|
|
25
25
|
|
|
26
26
|
多个 Codex / Claude Code 会话可以同时接入同一个浏览器,各自一条连接。可在扩展设置 →
|
|
27
27
|
Coordinator 查看已接入的会话、单独断开,或撤销信任。
|
|
@@ -49,7 +49,8 @@ listen 端口的能力,所以方向只能如此。这也是配对页存在的
|
|
|
49
49
|
## 工具面
|
|
50
50
|
|
|
51
51
|
- 控制面 5 个:`pairing_status / list_tabs / open_session / close_session / get_quota`
|
|
52
|
-
- 发现 1 个:`browser_discoverTools` —— 已知工具时直接传 `enable: [...]`,已知分组时直接传 `enableGroups: ["export", ...]
|
|
52
|
+
- 发现 1 个:`browser_discoverTools` —— 已知工具时直接传 `enable: [...]`,已知分组时直接传 `enableGroups: ["export", ...]`,也可用 `profile: "core" | "full"` 切换整套工具面;只有这些都未知时才不带参数读取目录。选中的工具会加入本进程的 `tools/list`(发送 `tools/list_changed`),并始终返回完整 schema,包括已经启用的工具。
|
|
53
|
+
- 同轮执行桥 1 个:`browser_call({session_id, tool, args})` —— 某些宿主虽已刷新 `tools/list`,却不会在当前模型轮次注入新工具。AI 可在 `discoverTools` 后立刻通过该桥执行,不需要让用户回复“继续”;下一轮看到原生工具后仍优先使用原生工具。
|
|
53
54
|
- 执行面在浏览器连接前不进入 `tools/list`;连接确认后加载默认 **core 31 个** `browser_*`。其余 20 个由 AI 按需 `discoverTools` 拉取,用户不用配置。
|
|
54
55
|
- 不暴露 `askUser`(MCP 会话没有人在侧边栏应答)和 `attachTab` / `detachTab`(目标 tab 已由 `open_session` 绑定)。
|
|
55
56
|
|
|
@@ -65,8 +66,8 @@ hover / uploadFile / scroll / waitFor`);`browser_storage` 也不会列出,
|
|
|
65
66
|
|
|
66
67
|
| 值 | 效果 |
|
|
67
68
|
|---|---|
|
|
68
|
-
| `core`(默认) |
|
|
69
|
-
| `full` |
|
|
69
|
+
| `core`(默认) | 连接前仅控制面、发现工具与同轮执行桥;连接后再列出 31 个核心工具 |
|
|
70
|
+
| `full` | 连接前同上;连接后列出全部 51 个原生工具 |
|
|
70
71
|
|
|
71
72
|
无法识别的值(包括已移除的 `parity`)按 `core` 处理,并往 stderr 打一条提示。
|
|
72
73
|
|
package/dist/index.js
CHANGED
|
@@ -2109,13 +2109,23 @@ function discoveryCatalog(all, advertised) {
|
|
|
2109
2109
|
|
|
2110
2110
|
// src/discover-tool.ts
|
|
2111
2111
|
var DISCOVER_GROUPS = Object.keys(DISCOVERABLE_GROUPS);
|
|
2112
|
+
var DISCOVER_PROFILES = ["core", "full"];
|
|
2113
|
+
var PROFILE_TOOLS = {
|
|
2114
|
+
core: generateBrowserTools("core").map((tool) => tool.name),
|
|
2115
|
+
full: generateBrowserTools("full").map((tool) => tool.name)
|
|
2116
|
+
};
|
|
2112
2117
|
var DISCOVER_TOOL = {
|
|
2113
2118
|
name: "browser_discoverTools",
|
|
2114
|
-
description: "The default tool list is the core set. Enable known extras directly with enable=[names] or enableGroups=[groups]. Omit both only when you need the catalog. Groups: export, network, storage, browser-data, inspect, legacy-dom, form, tabs.
|
|
2119
|
+
description: "The default tool list is the core set. Enable known extras directly with enable=[names] or enableGroups=[groups]. Use profile=core|full to return and enable a complete profile. Omit both only when you need the catalog. Groups: export, network, storage, browser-data, inspect, legacy-dom, form, tabs. The response includes full schemas even for tools already enabled. If the host has not refreshed them in this turn, call them through browser_call. Use this instead of rebuilding missing capabilities with runJS.",
|
|
2115
2120
|
inputSchema: {
|
|
2116
2121
|
type: "object",
|
|
2117
2122
|
properties: {
|
|
2118
2123
|
enable: { type: "array", items: { type: "string" }, description: "Tool names from the catalog to advertise" },
|
|
2124
|
+
profile: {
|
|
2125
|
+
type: "string",
|
|
2126
|
+
enum: [...DISCOVER_PROFILES],
|
|
2127
|
+
description: "Return and enable every runnable tool in the core or full profile"
|
|
2128
|
+
},
|
|
2119
2129
|
enableGroups: {
|
|
2120
2130
|
type: "array",
|
|
2121
2131
|
items: { type: "string", enum: DISCOVER_GROUPS },
|
|
@@ -2131,14 +2141,16 @@ function isRunnable(t, supported) {
|
|
|
2131
2141
|
function handleDiscover(input) {
|
|
2132
2142
|
const { all, advertised, args, supported } = input;
|
|
2133
2143
|
const byName = new Map(all.map((t) => [t.name, t]));
|
|
2134
|
-
const hasSelection = Array.isArray(args.enable) || Array.isArray(args.enableGroups);
|
|
2144
|
+
const hasSelection = Array.isArray(args.enable) || Array.isArray(args.enableGroups) || args.profile != null;
|
|
2135
2145
|
if (!hasSelection) {
|
|
2136
2146
|
const runnable = all.filter((t) => isRunnable(t, supported));
|
|
2137
2147
|
return { catalog: discoveryCatalog(runnable, advertised), changed: false };
|
|
2138
2148
|
}
|
|
2139
2149
|
const explicit = Array.isArray(args.enable) ? args.enable.map(String) : [];
|
|
2140
2150
|
const groups = Array.isArray(args.enableGroups) ? args.enableGroups.map(String) : [];
|
|
2151
|
+
const profile = args.profile == null ? null : String(args.profile);
|
|
2141
2152
|
const unknownGroups = groups.filter((group) => !DISCOVER_GROUPS.includes(group));
|
|
2153
|
+
const unknownProfiles = profile != null && !DISCOVER_PROFILES.includes(profile) ? [profile] : [];
|
|
2142
2154
|
const grouped = groups.flatMap(
|
|
2143
2155
|
(group) => DISCOVERABLE_GROUPS[group] ?? []
|
|
2144
2156
|
);
|
|
@@ -2150,7 +2162,13 @@ function handleDiscover(input) {
|
|
|
2150
2162
|
for (const name of grouped) {
|
|
2151
2163
|
if (!requestedByName.has(name)) requestedByName.set(name, name);
|
|
2152
2164
|
}
|
|
2165
|
+
if (profile && unknownProfiles.length === 0) {
|
|
2166
|
+
for (const name of PROFILE_TOOLS[profile]) {
|
|
2167
|
+
if (!requestedByName.has(name)) requestedByName.set(name, name);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2153
2170
|
const enabled = [];
|
|
2171
|
+
let changed = false;
|
|
2154
2172
|
const unknown = [];
|
|
2155
2173
|
const unsupported = [];
|
|
2156
2174
|
for (const [name, reportAs] of requestedByName) {
|
|
@@ -2163,19 +2181,42 @@ function handleDiscover(input) {
|
|
|
2163
2181
|
unsupported.push(reportAs);
|
|
2164
2182
|
continue;
|
|
2165
2183
|
}
|
|
2166
|
-
if (advertised.has(name))
|
|
2167
|
-
|
|
2184
|
+
if (!advertised.has(name)) {
|
|
2185
|
+
advertised.add(name);
|
|
2186
|
+
changed = true;
|
|
2187
|
+
}
|
|
2168
2188
|
enabled.push({ name: t.name, description: t.description, inputSchema: t.inputSchema });
|
|
2169
2189
|
}
|
|
2170
2190
|
return {
|
|
2171
2191
|
enabled,
|
|
2172
2192
|
...unknown.length ? { unknown } : {},
|
|
2173
2193
|
...unknownGroups.length ? { unknownGroups } : {},
|
|
2194
|
+
...unknownProfiles.length ? { unknownProfiles } : {},
|
|
2174
2195
|
...unsupported.length ? { unsupported } : {},
|
|
2175
|
-
changed
|
|
2196
|
+
changed
|
|
2176
2197
|
};
|
|
2177
2198
|
}
|
|
2178
2199
|
|
|
2200
|
+
// src/browser-call.ts
|
|
2201
|
+
var BROWSER_CALL_TOOL = {
|
|
2202
|
+
name: "browser_call",
|
|
2203
|
+
description: "Call an enabled browser tool in the current turn after browser_discoverTools returns its schema. Prefer the native tool when available.",
|
|
2204
|
+
inputSchema: {
|
|
2205
|
+
type: "object",
|
|
2206
|
+
required: ["session_id", "tool", "args"],
|
|
2207
|
+
properties: {
|
|
2208
|
+
session_id: { type: "string", description: "Session id from open_session" },
|
|
2209
|
+
tool: { type: "string", description: "Enabled browser tool name; browser_ prefix is optional" },
|
|
2210
|
+
args: {
|
|
2211
|
+
type: "object",
|
|
2212
|
+
description: "Arguments matching the schema returned by browser_discoverTools, excluding session_id",
|
|
2213
|
+
additionalProperties: true
|
|
2214
|
+
}
|
|
2215
|
+
},
|
|
2216
|
+
additionalProperties: false
|
|
2217
|
+
}
|
|
2218
|
+
};
|
|
2219
|
+
|
|
2179
2220
|
// ../shared/src/static-scan.ts
|
|
2180
2221
|
var RULES = [
|
|
2181
2222
|
{
|
|
@@ -2423,7 +2464,7 @@ var TOPICS = {
|
|
|
2423
2464
|
2. After approval, call pairing_status. Do not retry list_tabs while status is unchanged.
|
|
2424
2465
|
3. Call list_tabs when connected, choose a tab, then open_session with its tab_id.
|
|
2425
2466
|
4. For interaction use browser_getPageInfo then browser_takeSnapshot; for broad reading use browser_createPageIndex.
|
|
2426
|
-
5. Prefer purpose-built tools and verified results. Use browser_discoverTools only for capabilities outside the connected core surface.
|
|
2467
|
+
5. 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".
|
|
2427
2468
|
6. Close the session when finished.
|
|
2428
2469
|
`,
|
|
2429
2470
|
pairing: `# AtWebPilot pairing
|
|
@@ -2502,7 +2543,7 @@ var SKILL_TOOL = {
|
|
|
2502
2543
|
// package.json
|
|
2503
2544
|
var package_default = {
|
|
2504
2545
|
name: "@attson/atwebpilot-mcp",
|
|
2505
|
-
version: "0.0.
|
|
2546
|
+
version: "0.0.78",
|
|
2506
2547
|
license: "Apache-2.0",
|
|
2507
2548
|
description: "MCP server for the atwebpilot browser extension - let Codex or Claude Code drive your browser.",
|
|
2508
2549
|
homepage: "https://github.com/attson/atwebpilot#readme",
|
|
@@ -2552,6 +2593,45 @@ var package_default = {
|
|
|
2552
2593
|
// src/version.ts
|
|
2553
2594
|
var MCP_VERSION = package_default.version;
|
|
2554
2595
|
|
|
2596
|
+
// src/pairing-presenter.ts
|
|
2597
|
+
import { randomUUID } from "crypto";
|
|
2598
|
+
function createPairingPresenter(server, fallbackOpenUrl) {
|
|
2599
|
+
let presentedUrl = null;
|
|
2600
|
+
const runFallback = async (url) => {
|
|
2601
|
+
if (!fallbackOpenUrl) return;
|
|
2602
|
+
try {
|
|
2603
|
+
await fallbackOpenUrl(url);
|
|
2604
|
+
} catch (error) {
|
|
2605
|
+
presentedUrl = null;
|
|
2606
|
+
console.error(
|
|
2607
|
+
`[atwebpilot-mcp] failed to open pairing page ${url}:`,
|
|
2608
|
+
error instanceof Error ? error.message : String(error)
|
|
2609
|
+
);
|
|
2610
|
+
}
|
|
2611
|
+
};
|
|
2612
|
+
return (url) => {
|
|
2613
|
+
if (presentedUrl === url) return;
|
|
2614
|
+
presentedUrl = url;
|
|
2615
|
+
if (!server.getClientCapabilities()?.elicitation?.url) {
|
|
2616
|
+
void runFallback(url);
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
void server.elicitInput({
|
|
2620
|
+
mode: "url",
|
|
2621
|
+
message: "Open AtWebPilot in the browser to approve this pairing request.",
|
|
2622
|
+
url,
|
|
2623
|
+
elicitationId: `atwebpilot-pairing-${randomUUID()}`
|
|
2624
|
+
}).catch((error) => {
|
|
2625
|
+
if (!server.transport) return;
|
|
2626
|
+
console.error(
|
|
2627
|
+
"[atwebpilot-mcp] URL elicitation failed; falling back to the system browser:",
|
|
2628
|
+
error instanceof Error ? error.message : String(error)
|
|
2629
|
+
);
|
|
2630
|
+
return runFallback(url);
|
|
2631
|
+
});
|
|
2632
|
+
};
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2555
2635
|
// src/mcp-server.ts
|
|
2556
2636
|
var ALL_BROWSER_TOOLS = generateBrowserTools("full");
|
|
2557
2637
|
var BROWSER_BY_NAME = new Map(ALL_BROWSER_TOOLS.map((t) => [t.name, t]));
|
|
@@ -2598,6 +2678,7 @@ function buildToolList(deps, state = DEFAULT_STATE) {
|
|
|
2598
2678
|
{ name: SKILL_TOOL.name, description: SKILL_TOOL.description, inputSchema: SKILL_TOOL.inputSchema },
|
|
2599
2679
|
...CONTROL_TOOLS.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
|
|
2600
2680
|
{ name: DISCOVER_TOOL.name, description: DISCOVER_TOOL.description, inputSchema: DISCOVER_TOOL.inputSchema },
|
|
2681
|
+
{ name: BROWSER_CALL_TOOL.name, description: BROWSER_CALL_TOOL.description, inputSchema: BROWSER_CALL_TOOL.inputSchema },
|
|
2601
2682
|
...browser.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema }))
|
|
2602
2683
|
];
|
|
2603
2684
|
}
|
|
@@ -2617,7 +2698,7 @@ function toolResult(gen, data) {
|
|
|
2617
2698
|
return { content };
|
|
2618
2699
|
}
|
|
2619
2700
|
var fail2 = (message) => ({ content: [{ type: "text", text: message }], isError: true });
|
|
2620
|
-
async function dispatchCall(deps, name, args, state = DEFAULT_STATE, onListChanged) {
|
|
2701
|
+
async function dispatchCall(deps, name, args, state = DEFAULT_STATE, onListChanged, onPairingRequired) {
|
|
2621
2702
|
try {
|
|
2622
2703
|
if (name === SKILL_TOOL.name) {
|
|
2623
2704
|
const bundle = readSkillBundle(args.topic);
|
|
@@ -2634,14 +2715,19 @@ async function dispatchCall(deps, name, args, state = DEFAULT_STATE, onListChang
|
|
|
2634
2715
|
const { changed: _c, ...body } = r;
|
|
2635
2716
|
return ok(body);
|
|
2636
2717
|
}
|
|
2718
|
+
if (name === BROWSER_CALL_TOOL.name) {
|
|
2719
|
+
return handleBrowserCall(deps, args, state);
|
|
2720
|
+
}
|
|
2637
2721
|
if (name === "pairing_status") {
|
|
2638
2722
|
const result = await handlePairingStatus(deps);
|
|
2639
2723
|
if (result.status === "connected") await revealBrowserSurface(state, onListChanged);
|
|
2724
|
+
else onPairingRequired?.(result.pair_url);
|
|
2640
2725
|
return ok(result);
|
|
2641
2726
|
}
|
|
2642
2727
|
if (name === "list_tabs") {
|
|
2643
2728
|
const result = await handleListTabs(deps);
|
|
2644
2729
|
if (result.status === "connected") await revealBrowserSurface(state, onListChanged);
|
|
2730
|
+
else onPairingRequired?.(result.pair_url);
|
|
2645
2731
|
return ok(result);
|
|
2646
2732
|
}
|
|
2647
2733
|
if (name === "open_session") {
|
|
@@ -2655,19 +2741,50 @@ async function dispatchCall(deps, name, args, state = DEFAULT_STATE, onListChang
|
|
|
2655
2741
|
if (gen) return toolResult(gen, await handleBrowserTool(deps, gen, args));
|
|
2656
2742
|
return fail2(`unknown tool: ${name}`);
|
|
2657
2743
|
} catch (e) {
|
|
2658
|
-
|
|
2744
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
2745
|
+
if (message.includes("PAIRING_REQUIRED")) {
|
|
2746
|
+
const pairUrl = deps.pairUrl();
|
|
2747
|
+
if (pairUrl) onPairingRequired?.(pairUrl);
|
|
2748
|
+
}
|
|
2749
|
+
return fail2(message);
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
async function handleBrowserCall(deps, args, state) {
|
|
2753
|
+
const rawName = typeof args.tool === "string" ? args.tool.trim() : "";
|
|
2754
|
+
if (!rawName) return fail2("InvalidArgs: browser_call requires tool");
|
|
2755
|
+
const name = rawName.startsWith("browser_") ? rawName : `browser_${rawName}`;
|
|
2756
|
+
const gen = BROWSER_BY_NAME.get(name);
|
|
2757
|
+
if (!gen) return fail2(`Unknown browser tool: ${rawName}`);
|
|
2758
|
+
if (!state.advertised.has(name)) {
|
|
2759
|
+
return fail2(`${name} is not enabled; call browser_discoverTools first`);
|
|
2659
2760
|
}
|
|
2761
|
+
const supported = workerToolSupport(deps);
|
|
2762
|
+
if (supported && !gen.builtinTools.every((tool) => supported.has(tool))) {
|
|
2763
|
+
return fail2(`${name} is not supported by the connected browser extension`);
|
|
2764
|
+
}
|
|
2765
|
+
if (typeof args.session_id !== "string" || args.session_id.length === 0) {
|
|
2766
|
+
return fail2("InvalidArgs: browser_call requires session_id");
|
|
2767
|
+
}
|
|
2768
|
+
if (args.args === null || typeof args.args !== "object" || Array.isArray(args.args)) {
|
|
2769
|
+
return fail2("InvalidArgs: browser_call args must be an object");
|
|
2770
|
+
}
|
|
2771
|
+
const toolArgs = {
|
|
2772
|
+
...args.args,
|
|
2773
|
+
session_id: args.session_id
|
|
2774
|
+
};
|
|
2775
|
+
return toolResult(gen, await handleBrowserTool(deps, gen, toolArgs));
|
|
2660
2776
|
}
|
|
2661
2777
|
async function revealBrowserSurface(state, onListChanged) {
|
|
2662
2778
|
if (state.browserSurfaceVisible) return;
|
|
2663
2779
|
state.browserSurfaceVisible = true;
|
|
2664
2780
|
await onListChanged?.();
|
|
2665
2781
|
}
|
|
2666
|
-
function createMcpServer(deps, state = DEFAULT_STATE) {
|
|
2782
|
+
function createMcpServer(deps, state = DEFAULT_STATE, options = {}) {
|
|
2667
2783
|
const server = new Server(
|
|
2668
2784
|
{ name: "atwebpilot-mcp", version: MCP_VERSION },
|
|
2669
2785
|
{ capabilities: { tools: { listChanged: true }, logging: {} } }
|
|
2670
2786
|
);
|
|
2787
|
+
const presentPairing = createPairingPresenter(server, options.fallbackOpenUrl);
|
|
2671
2788
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: buildToolList(deps, state) }));
|
|
2672
2789
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
2673
2790
|
const args = req.params.arguments ?? {};
|
|
@@ -2678,7 +2795,7 @@ function createMcpServer(deps, state = DEFAULT_STATE) {
|
|
|
2678
2795
|
console.error("[atwebpilot-mcp] failed to send tools/list_changed:", error instanceof Error ? error.message : String(error));
|
|
2679
2796
|
}
|
|
2680
2797
|
};
|
|
2681
|
-
return dispatchCall(deps, req.params.name, args, state, onListChanged);
|
|
2798
|
+
return dispatchCall(deps, req.params.name, args, state, onListChanged, presentPairing);
|
|
2682
2799
|
});
|
|
2683
2800
|
return server;
|
|
2684
2801
|
}
|
|
@@ -3119,7 +3236,7 @@ function installWire(hub, coordinator, clock, onWorkerReady) {
|
|
|
3119
3236
|
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
3120
3237
|
import { homedir } from "os";
|
|
3121
3238
|
import { join as join2 } from "path";
|
|
3122
|
-
import { randomBytes, randomUUID } from "crypto";
|
|
3239
|
+
import { randomBytes, randomUUID as randomUUID2 } from "crypto";
|
|
3123
3240
|
var FILE = "identity.json";
|
|
3124
3241
|
function defaultIdentityDir() {
|
|
3125
3242
|
return join2(homedir(), ".atwebpilot");
|
|
@@ -3143,7 +3260,7 @@ function loadOrCreateIdentity(dir = defaultIdentityDir()) {
|
|
|
3143
3260
|
const existing = read(dir);
|
|
3144
3261
|
if (existing) return { installId: existing.installId, secret: existing.secret };
|
|
3145
3262
|
const created = {
|
|
3146
|
-
installId: `inst_${
|
|
3263
|
+
installId: `inst_${randomUUID2().replace(/-/g, "").slice(0, 16)}`,
|
|
3147
3264
|
secret: randomBytes(24).toString("base64url")
|
|
3148
3265
|
};
|
|
3149
3266
|
write(dir, created);
|
|
@@ -3162,7 +3279,7 @@ function processInfo(cwd = process.cwd()) {
|
|
|
3162
3279
|
const home = homedir();
|
|
3163
3280
|
const label = home && cwd.startsWith(home) ? `~${cwd.slice(home.length)}` : cwd;
|
|
3164
3281
|
return {
|
|
3165
|
-
sessionId: `sess_${
|
|
3282
|
+
sessionId: `sess_${randomUUID2().replace(/-/g, "").slice(0, 12)}`,
|
|
3166
3283
|
label,
|
|
3167
3284
|
pid: process.pid
|
|
3168
3285
|
};
|
|
@@ -3387,10 +3504,9 @@ async function main() {
|
|
|
3387
3504
|
clock,
|
|
3388
3505
|
idGen,
|
|
3389
3506
|
explicitPort,
|
|
3390
|
-
token
|
|
3391
|
-
openUrl: defaultOpenUrl
|
|
3507
|
+
token
|
|
3392
3508
|
});
|
|
3393
|
-
const server = createMcpServer(deps);
|
|
3509
|
+
const server = createMcpServer(deps, void 0, { fallbackOpenUrl: defaultOpenUrl });
|
|
3394
3510
|
await server.connect(new StdioServerTransport());
|
|
3395
3511
|
console.error("[atwebpilot-mcp] stdio MCP connected; ws port binds on first browser use");
|
|
3396
3512
|
const shutdown = () => {
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -64,16 +64,26 @@ do this, in order:
|
|
|
64
64
|
`browser_discoverTools({ enable: ["browser_downloadSpreadsheet", ...] })`.
|
|
65
65
|
If you know only the group, use
|
|
66
66
|
`browser_discoverTools({ enableGroups: ["export"] })`.
|
|
67
|
+
To switch the whole process surface explicitly, use
|
|
68
|
+
`browser_discoverTools({ profile: "core" })` or `{ profile: "full" }`.
|
|
67
69
|
2. Call `browser_discoverTools({})` only when neither the tool nor group is
|
|
68
70
|
known. Enabled tools join `tools/list` (a `tools/list_changed` notification
|
|
69
|
-
is sent), and the response carries
|
|
71
|
+
is sent), and the response carries the selected full schemas even if those
|
|
72
|
+
tools were already enabled.
|
|
73
|
+
3. Prefer a newly visible native tool. If the MCP host has refreshed
|
|
74
|
+
`tools/list` but the current model turn still has the old snapshot, call the
|
|
75
|
+
selected tool immediately through
|
|
76
|
+
`browser_call({ session_id, tool: "browser_downloadSpreadsheet", args: {...} })`.
|
|
77
|
+
Do not ask the user to reply "continue" just to start another model turn.
|
|
78
|
+
Nested `args` omit `session_id`; the bridge injects the top-level value.
|
|
70
79
|
|
|
71
80
|
**Do not** rebuild these capabilities with `runJS` (`fetch`, CSV blobs,
|
|
72
81
|
`localStorage[...]`). It is slower, loses schema validation, and usually trips
|
|
73
82
|
the dangerous-tier review that the purpose-built tool would have avoided.
|
|
74
83
|
|
|
75
84
|
`ATWEBPILOT_MCP_TOOLS=full` advertises everything from the start for users who
|
|
76
|
-
prefer to pay the context cost once.
|
|
85
|
+
prefer to pay the context cost once. `browser_call` is a current-turn
|
|
86
|
+
compatibility path; use the native tool once the host exposes it.
|
|
77
87
|
|
|
78
88
|
## Recommended flow
|
|
79
89
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attson/atwebpilot-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.78",
|
|
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",
|