@weavetab/mcp 2.5.0-beta.5 → 2.5.1
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/AGENTS.md +49 -49
- package/ARCHITECTURE.md +590 -590
- package/README.md +216 -100
- package/SECURITY.md +207 -207
- package/TROUBLESHOOTING.md +373 -365
- package/dist/anticaptcha/detector.js +395 -395
- package/dist/anticaptcha/solver.js +130 -130
- package/dist/cdp/bridge.js +73 -73
- package/dist/cdp/connector.js +46 -22
- package/dist/cdp/extension.js +34 -34
- package/dist/cdp/helpers.js +174 -174
- package/dist/cdp/pruner.js +195 -195
- package/dist/cdp/ref_cache.js +66 -66
- package/dist/cdp/security.js +10 -10
- package/dist/cdp/verify.js +57 -57
- package/dist/cdp/walker.js +563 -525
- package/dist/cli/plugin.js +26 -3
- package/dist/config/loader.d.ts +0 -3
- package/dist/config/loader.js +0 -9
- package/dist/index.js +0 -4
- package/dist/intelligence/intent.js +81 -81
- package/dist/intelligence/sessionMemory.js +2 -2
- package/dist/overlay/ghost.js +22 -22
- package/dist/overlay/injector.js +89 -89
- package/dist/plugin/extension_bridge.js +54 -54
- package/dist/plugin/interface.d.ts +7 -0
- package/dist/plugin/loader.js +3 -4
- package/dist/plugin/schema.d.ts +11 -0
- package/dist/plugin/schema.js +59 -0
- package/dist/security/guard.js +18 -18
- package/dist/sensors/actionability.js +167 -167
- package/dist/sensors/guard.js +29 -29
- package/dist/sensors/network.js +15 -17
- package/dist/server.d.ts +1 -1
- package/dist/server.js +173 -144
- package/dist/state/delta.js +6 -7
- package/dist/tools/automation.js +85 -85
- package/dist/tools/canvas.js +48 -25
- package/dist/tools/checkpoint.js +94 -35
- package/dist/tools/click.js +52 -41
- package/dist/tools/clipboard.js +80 -74
- package/dist/tools/design_extract.js +441 -441
- package/dist/tools/dialog.js +13 -13
- package/dist/tools/emulation.d.ts +2 -0
- package/dist/tools/emulation.js +7 -0
- package/dist/tools/fill.js +344 -347
- package/dist/tools/find.js +78 -52
- package/dist/tools/highlight.js +67 -67
- package/dist/tools/inspect.js +54 -25
- package/dist/tools/key.js +42 -35
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +35 -35
- package/dist/tools/office/parse.js +62 -18
- package/dist/tools/pdf.d.ts +9 -0
- package/dist/tools/pdf.js +7 -4
- package/dist/tools/performance.d.ts +11 -4
- package/dist/tools/performance.js +94 -42
- package/dist/tools/performance_test.d.ts +2 -2
- package/dist/tools/performance_test.js +69 -69
- package/dist/tools/pointer.js +116 -116
- package/dist/tools/read.js +99 -67
- package/dist/tools/recording.js +9 -0
- package/dist/tools/scrape.js +146 -146
- package/dist/tools/screenshot.d.ts +2 -0
- package/dist/tools/screenshot.js +46 -4
- package/dist/tools/scroll.d.ts +3 -1
- package/dist/tools/scroll.js +13 -15
- package/dist/tools/select.d.ts +3 -0
- package/dist/tools/select.js +131 -124
- package/dist/tools/snapshot.js +8 -6
- package/dist/tools/storage.d.ts +5 -1
- package/dist/tools/storage.js +16 -0
- package/dist/tools/tabs.d.ts +7 -5
- package/dist/tools/tabs.js +79 -26
- package/dist/tools/thought.js +8 -8
- package/dist/tools/type.js +274 -274
- package/dist/tools/type_secret.js +23 -23
- package/dist/tools/upload.js +3 -3
- package/dist/tools/video_compiler.js +9 -0
- package/dist/tools/wait.js +35 -35
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/DOCUMENTATION.md +583 -585
- package/docs/PLUGINS.md +139 -121
- package/docs/TOOLS.md +3 -4
- package/docs/architecture/blind-injection.md +83 -83
- package/docs/architecture/overview.md +49 -49
- package/docs/configuration/configuration.md +39 -27
- package/docs/configuration/folders.md +62 -55
- package/docs/desktop-automation/electron-apps.md +225 -214
- package/docs/get-started/installation.md +1 -1
- package/docs/get-started/introduction.md +60 -60
- package/docs/get-started/quickstart.md +2 -2
- package/docs/get-started/setup-wizard.md +1 -1
- package/docs/intelligence/site-profiles.md +151 -151
- package/docs/tools/tools.md +582 -631
- package/docs/troubleshooting/trouble-loop.md +31 -31
- package/ext-dist/background.js +415 -415
- package/ext-dist/content.js +2527 -2527
- package/ext-dist/dashboard.html +583 -583
- package/ext-dist/hud/hud.js +3 -3
- package/ext-dist/hud/index.html +20 -20
- package/ext-dist/manifest.json +73 -73
- package/ext-dist/styles.css +223 -223
- package/mcp.json +796 -805
- package/package.json +8 -8
- package/server.json +23 -23
- package/smithery.yaml +7 -11
- package/dist/cdp/walker.test.d.ts +0 -2
- package/dist/cdp/walker.test.js +0 -172
- package/dist/intelligence/sessionMemory.test.d.ts +0 -2
- package/dist/intelligence/sessionMemory.test.js +0 -62
- package/dist/intelligence/trail.test.d.ts +0 -2
- package/dist/intelligence/trail.test.js +0 -110
- package/dist/state/cache.test.d.ts +0 -2
- package/dist/state/cache.test.js +0 -76
- package/dist/state/delta.test.d.ts +0 -2
- package/dist/state/delta.test.js +0 -279
- package/dist/state/groups.d.ts +0 -15
- package/dist/state/groups.js +0 -80
- package/dist/tools/accessibility.d.ts +0 -14
- package/dist/tools/accessibility.js +0 -32
- package/dist/tools/ask.d.ts +0 -14
- package/dist/tools/ask.js +0 -49
- package/dist/tools/click_and_wait.d.ts +0 -33
- package/dist/tools/click_and_wait.js +0 -149
- package/dist/tools/drag.d.ts +0 -15
- package/dist/tools/drag.js +0 -29
- package/dist/tools/geo.d.ts +0 -19
- package/dist/tools/geo.js +0 -29
- package/dist/tools/github/analyze.d.ts +0 -4
- package/dist/tools/github/analyze.js +0 -113
- package/dist/tools/github/cache.d.ts +0 -21
- package/dist/tools/github/cache.js +0 -28
- package/dist/tools/github/index.d.ts +0 -19
- package/dist/tools/github/index.js +0 -40
- package/dist/tools/github/issues.d.ts +0 -9
- package/dist/tools/github/issues.js +0 -88
- package/dist/tools/github/pr.d.ts +0 -5
- package/dist/tools/github/pr.js +0 -88
- package/dist/tools/github/rateLimiter.d.ts +0 -4
- package/dist/tools/github/rateLimiter.js +0 -46
- package/dist/tools/github/read.d.ts +0 -34
- package/dist/tools/github/read.js +0 -74
- package/dist/tools/github/repoContext.d.ts +0 -20
- package/dist/tools/github/repoContext.js +0 -129
- package/dist/tools/hover.d.ts +0 -19
- package/dist/tools/hover.js +0 -57
- package/dist/tools/integration.test.d.ts +0 -2
- package/dist/tools/integration.test.js +0 -96
- package/dist/tools/macro_compiler.d.ts +0 -7
- package/dist/tools/macro_compiler.js +0 -82
- package/dist/tools/mouse.d.ts +0 -21
- package/dist/tools/mouse.js +0 -62
- package/dist/tools/response.d.ts +0 -129
- package/dist/tools/response.js +0 -5
- package/dist/tools/wait_for.d.ts +0 -23
- package/dist/tools/wait_for.js +0 -30
- package/dist/tools/windows.d.ts +0 -22
- package/dist/tools/windows.js +0 -57
package/dist/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Weavetab — server.ts
|
|
3
|
-
* MCP server:
|
|
3
|
+
* MCP server: 41 browser automation tools with security, rate limiting, and visual overlay.
|
|
4
4
|
* Weavetab by fy2ne
|
|
5
5
|
*/
|
|
6
6
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -55,20 +55,19 @@ import { resetLoopCounter } from "./intelligence/trail.js";
|
|
|
55
55
|
import { getSiteProfile, learnPattern, recordPatternExecution, recordQuirk, getPatternReliability, formatSiteProfileHint } from "./intelligence/patternCompiler.js";
|
|
56
56
|
import { discoverPlugins } from "./plugin/loader.js";
|
|
57
57
|
import { hookPipeline } from "./plugin/pipeline.js";
|
|
58
|
+
import { toPluginInputShape } from "./plugin/schema.js";
|
|
58
59
|
import { agentAction } from "./sensors/pacing.js";
|
|
59
60
|
import { getCachedConfig, getElectronApps } from "./config/loader.js";
|
|
60
61
|
import { agentState } from "./state/agent.js";
|
|
61
62
|
import { SessionSecretTracker } from "./security/secrets.js";
|
|
62
63
|
import { sessionRegistry } from "./state/session.js";
|
|
63
|
-
// GitHub tools
|
|
64
|
-
import { browserGithub } from "./tools/github/index.js";
|
|
65
64
|
// Office document tools
|
|
66
65
|
import { browserOfficeParse, browserOfficeList } from "./tools/office/index.js";
|
|
67
66
|
const activeLoadedPlugins = new Map();
|
|
68
67
|
const ROLE_TOOL_ALLOW = {
|
|
69
68
|
admin: new Set(["*"]),
|
|
70
69
|
operator: new Set(["*"]),
|
|
71
|
-
viewer: new Set(["browser_map", "browser_find", "browser_scrape", "browser_console", "browser_eval", "browser_inspect", "browser_extract_design", "browser_snapshot", "browser_screenshot", "browser_pdf", "browser_cookies", "browser_tabs", "browser_wait", "browser_pattern_learn"
|
|
70
|
+
viewer: new Set(["browser_map", "browser_find", "browser_scrape", "browser_console", "browser_eval", "browser_inspect", "browser_extract_design", "browser_snapshot", "browser_screenshot", "browser_pdf", "browser_cookies", "browser_tabs", "browser_wait", "browser_pattern_learn"]),
|
|
72
71
|
automation: new Set(["*"]), // Allowed all except interactive tools (enforced in isToolAllowedForRole)
|
|
73
72
|
system: new Set(["browser_thoughts", "browser_detect", "browser_performance", "browser_checkpoint", "browser_pattern_learn"]),
|
|
74
73
|
};
|
|
@@ -83,7 +82,7 @@ function isToolAllowedForRole(role, toolName) {
|
|
|
83
82
|
return { allowed: true };
|
|
84
83
|
if (allowed.has(toolName))
|
|
85
84
|
return { allowed: true };
|
|
86
|
-
return { allowed: false, reason: `Role '${role}' does not have permission to use '${toolName}'. ${role === "viewer" ? "Viewer role only permits read-only tools (map/find/scrape/console/eval/inspect/snapshot/pdf/cookies
|
|
85
|
+
return { allowed: false, reason: `Role '${role}' does not have permission to use '${toolName}'. ${role === "viewer" ? "Viewer role only permits read-only tools (map/find/scrape/console/eval/inspect/snapshot/pdf/cookies)." : ""}` };
|
|
87
86
|
}
|
|
88
87
|
/** Push a rich log card to every active browser session's overlay. Only shown when devMode is on. */
|
|
89
88
|
async function pushRichLog(agentId, toolName, title, details, status, devMode = false) {
|
|
@@ -168,7 +167,7 @@ function inferTargetApp(requestedApp, url, tabId) {
|
|
|
168
167
|
}
|
|
169
168
|
return undefined;
|
|
170
169
|
}
|
|
171
|
-
async function withSecurity(config, toolName, handler, isOfficial = false, targetApp) {
|
|
170
|
+
async function withSecurity(config, toolName, handler, isOfficial = false, targetApp, requiresSession = true) {
|
|
172
171
|
const liveConfig = getCachedConfig();
|
|
173
172
|
const rl = checkRateLimit(liveConfig);
|
|
174
173
|
if (!rl.allowed) {
|
|
@@ -181,79 +180,83 @@ async function withSecurity(config, toolName, handler, isOfficial = false, targe
|
|
|
181
180
|
throw new Error(`[!] ${roleToolCheck.reason}`);
|
|
182
181
|
}
|
|
183
182
|
}
|
|
184
|
-
const resolvedTargetApp = inferTargetApp(targetApp);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const stopCheck = await session.Runtime.evaluate({
|
|
190
|
-
expression: 'window.__wt_stop_requested===true',
|
|
191
|
-
returnByValue: true,
|
|
192
|
-
});
|
|
193
|
-
if (stopCheck?.result?.value === true) {
|
|
194
|
-
await session.Runtime.evaluate({ expression: 'window.__wt_stop_requested=false;' }).catch(() => { });
|
|
195
|
-
const { sendToExtension } = await import("./cdp/helpers.js");
|
|
196
|
-
await sendToExtension(session, { type: 'Weavetab_GROUP_STATE', state: 'idle' }).catch(() => { });
|
|
197
|
-
await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: '' } }).catch(() => { });
|
|
198
|
-
await sendToExtension(session, { type: 'Weavetab_CLEAR_THOUGHT' }).catch(() => { });
|
|
199
|
-
agentState.reportTransportClosed();
|
|
200
|
-
throw new Error('[!] mission stopped by user via HUD.');
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
catch (e) {
|
|
204
|
-
if (e instanceof Error && e.message.startsWith('[!]'))
|
|
205
|
-
throw e;
|
|
183
|
+
const resolvedTargetApp = requiresSession ? inferTargetApp(targetApp) : undefined;
|
|
184
|
+
let session;
|
|
185
|
+
if (requiresSession) {
|
|
186
|
+
({ session } = await withTimeoutGuard(() => getOrConnect(liveConfig, resolvedTargetApp), 15000, `CDP Connection (${toolName}${resolvedTargetApp ? `:${resolvedTargetApp}` : ''})`));
|
|
187
|
+
agentState.setSession(session);
|
|
206
188
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
189
|
+
global.__wt_live_config = liveConfig;
|
|
190
|
+
const { sendToExtension } = await import("./cdp/helpers.js");
|
|
191
|
+
const chatMessages = [];
|
|
192
|
+
if (requiresSession) {
|
|
193
|
+
try {
|
|
194
|
+
const stopCheck = await session.Runtime.evaluate({
|
|
195
|
+
expression: 'window.__wt_stop_requested===true',
|
|
196
|
+
returnByValue: true,
|
|
197
|
+
});
|
|
198
|
+
if (stopCheck?.result?.value === true) {
|
|
199
|
+
await session.Runtime.evaluate({ expression: 'window.__wt_stop_requested=false;' }).catch(() => { });
|
|
200
|
+
await sendToExtension(session, { type: 'Weavetab_GROUP_STATE', state: 'idle' }).catch(() => { });
|
|
201
|
+
await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: '' } }).catch(() => { });
|
|
202
|
+
await sendToExtension(session, { type: 'Weavetab_CLEAR_THOUGHT' }).catch(() => { });
|
|
203
|
+
agentState.reportTransportClosed();
|
|
204
|
+
throw new Error('[!] mission stopped by user via HUD.');
|
|
217
205
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
206
|
+
}
|
|
207
|
+
catch (e) {
|
|
208
|
+
if (e instanceof Error && e.message.startsWith('[!]'))
|
|
209
|
+
throw e;
|
|
210
|
+
}
|
|
211
|
+
if (!isOfficial) {
|
|
212
|
+
// Phase 7c: Domain policy check for read-only / deny / audit-only modes
|
|
213
|
+
const activeClient = sessionRegistry.getActive(liveConfig.agentId, targetApp);
|
|
214
|
+
if (activeClient?.url && !activeClient.isElectron) {
|
|
215
|
+
const domainCheck = checkDomain(activeClient.url, liveConfig);
|
|
216
|
+
if (!domainCheck.allowed) {
|
|
217
|
+
throw new Error(`[!] ${domainCheck.reason}`);
|
|
218
|
+
}
|
|
219
|
+
if (domainCheck.mode === "deny") {
|
|
220
|
+
throw new Error(`[!] Domain denied by policy`);
|
|
221
|
+
}
|
|
222
|
+
if (domainCheck.mode === "read-only") {
|
|
223
|
+
const toolCheck = isToolAllowedInMode(toolName, "read-only");
|
|
224
|
+
if (!toolCheck.allowed) {
|
|
225
|
+
throw new Error(`[!] ${toolCheck.reason}`);
|
|
226
|
+
}
|
|
222
227
|
}
|
|
223
228
|
}
|
|
224
229
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
try {
|
|
238
|
-
const { result: hasMsg } = await session.Runtime.evaluate({
|
|
239
|
-
expression: 'window.__wt_chat_queue&&window.__wt_chat_queue.length>0',
|
|
240
|
-
returnByValue: true,
|
|
241
|
-
});
|
|
242
|
-
if (hasMsg?.value === true) {
|
|
243
|
-
const { result: qResult } = await session.Runtime.evaluate({
|
|
244
|
-
expression: `(function(){ const q=window.__wt_chat_queue||[];window.__wt_chat_queue=[];return JSON.stringify(q); })()`,
|
|
230
|
+
// Push config to extension once per session (idempotent, ignored if already set)
|
|
231
|
+
sendToExtension(session, {
|
|
232
|
+
type: 'Weavetab_CONFIG',
|
|
233
|
+
groupTabs: liveConfig.groupTabs,
|
|
234
|
+
devMode: liveConfig.devMode,
|
|
235
|
+
}).catch(() => { });
|
|
236
|
+
if (liveConfig.devMode) {
|
|
237
|
+
sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: 'Mission Running', detail: toolName } }).catch(() => { });
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
const { result: hasMsg } = await session.Runtime.evaluate({
|
|
241
|
+
expression: 'window.__wt_chat_queue&&window.__wt_chat_queue.length>0',
|
|
245
242
|
returnByValue: true,
|
|
246
243
|
});
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
244
|
+
if (hasMsg?.value === true) {
|
|
245
|
+
const { result: qResult } = await session.Runtime.evaluate({
|
|
246
|
+
expression: `(function(){ const q=window.__wt_chat_queue||[];window.__wt_chat_queue=[];return JSON.stringify(q); })()`,
|
|
247
|
+
returnByValue: true,
|
|
248
|
+
});
|
|
249
|
+
const raw = qResult?.value;
|
|
250
|
+
if (raw && raw !== '[]') {
|
|
251
|
+
chatMessages.push(...JSON.parse(raw));
|
|
252
|
+
for (const msg of chatMessages) {
|
|
253
|
+
logAction('chat_queue', toolName, msg);
|
|
254
|
+
}
|
|
252
255
|
}
|
|
253
256
|
}
|
|
254
257
|
}
|
|
258
|
+
catch { /* non-fatal */ }
|
|
255
259
|
}
|
|
256
|
-
catch { /* non-fatal */ }
|
|
257
260
|
// Emit a "started" info log so the user sees the tool immediately
|
|
258
261
|
const argsPreview = '(executing…)';
|
|
259
262
|
pushRichLog(liveConfig.agentId, toolName, ` ${toolName} ${argsPreview}`, '', 'info', !!liveConfig.devMode);
|
|
@@ -305,7 +308,9 @@ async function withSecurity(config, toolName, handler, isOfficial = false, targe
|
|
|
305
308
|
details = String(result);
|
|
306
309
|
}
|
|
307
310
|
await pushRichLog(liveConfig.agentId, toolName, `${toolName} · ${ms}ms${tokenStr}`, details, 'success', true);
|
|
308
|
-
|
|
311
|
+
if (session) {
|
|
312
|
+
await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: `[OK] ${toolName} (${ms}ms${tokenStr})` } });
|
|
313
|
+
}
|
|
309
314
|
}
|
|
310
315
|
// ── Image block output: return proper MCP image block for screenshot results ──
|
|
311
316
|
// If the result contains a dataUrl (e.g. from browser_screenshot), extract the
|
|
@@ -321,14 +326,15 @@ async function withSecurity(config, toolName, handler, isOfficial = false, targe
|
|
|
321
326
|
if (resultAny && typeof resultAny === 'object' && typeof resultAny.dataUrl === 'string' && resultAny.dataUrl.startsWith('data:')) {
|
|
322
327
|
const dataUrl = resultAny.dataUrl;
|
|
323
328
|
const mimeMatch = dataUrl.match(/^data:([^;]+);base64,/);
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
// Return metadata (without the giant dataUrl) + native image block
|
|
329
|
+
const base64Data = mimeMatch ? dataUrl.slice(mimeMatch[0].length) : '';
|
|
330
|
+
const isB64 = base64Data.length > 0 && base64Data.length % 4 === 0 && /^[A-Za-z0-9+/]+={0,2}$/.test(base64Data);
|
|
327
331
|
const { dataUrl: _omit, ...metaOnly } = resultAny;
|
|
328
332
|
content = [
|
|
329
333
|
{ type: "text", text: JSON.stringify(metaOnly) },
|
|
330
|
-
{ type: "image", data: base64Data, mimeType },
|
|
331
334
|
];
|
|
335
|
+
if (mimeMatch && isB64) {
|
|
336
|
+
content.push({ type: "image", data: base64Data, mimeType: mimeMatch[1] });
|
|
337
|
+
}
|
|
332
338
|
}
|
|
333
339
|
else if (resultAny && typeof resultAny === 'object' && Array.isArray(resultAny.content)) {
|
|
334
340
|
// Passthrough if already conforming McpToolResult (prevents double JSON wrapping)
|
|
@@ -406,7 +412,9 @@ async function withSecurity(config, toolName, handler, isOfficial = false, targe
|
|
|
406
412
|
});
|
|
407
413
|
if (liveConfig.devMode) {
|
|
408
414
|
await pushRichLog(liveConfig.agentId, toolName, `${toolName} · FAILED · ${ms}ms`, errMsg, 'error', true);
|
|
409
|
-
|
|
415
|
+
if (session) {
|
|
416
|
+
await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: `[ERR] ${toolName} Error` } });
|
|
417
|
+
}
|
|
410
418
|
}
|
|
411
419
|
throw err;
|
|
412
420
|
}
|
|
@@ -464,7 +472,9 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
464
472
|
// ── 1. browser_map ─────────────────────────────────────────────────────────
|
|
465
473
|
server.tool("browser_map", "Snapshot interactive DOM with volatile ref IDs that invalidate after DOM mutations. For complex SPAs (e.g. GitHub dashboards), use `scope` or `query` to reduce tokens. For forms/pagination/sequential loops, ALWAYS use `delta: true` to save 97% tokens. For canvas/WebGL/Figma pages where the DOM has < 5 interactive elements, use `visual: \"auto\"` to get a numbered screenshot — then use browser_click({ mark: N }) to interact.", {
|
|
466
474
|
full: z.boolean().optional().describe("Set to true to return a full markdown table (heavier context) instead of the lite list."),
|
|
467
|
-
|
|
475
|
+
lite: z.boolean().optional().describe("Return a compact, token-optimized interactive element list."),
|
|
476
|
+
limit: z.number().optional().describe("Maximum number of elements to return."),
|
|
477
|
+
scope: z.enum(["navigation", "main_content", "main", "sidebar", "form", "modal", "utility"]).optional().describe("Restrict parsing to a semantic page area to save tokens. Useful on dense dashboards. 'main' is an alias for 'main_content'."),
|
|
468
478
|
query: z.string().optional().describe("Filter returned elements by label or role substring. Useful on dense dashboards."),
|
|
469
479
|
delta: z.boolean().optional().describe("Diff mode: return ONLY elements that changed since the last browser_map call (added, removed, or modified). Diffs across time — distinct from prune. Cuts prompt tokens by up to 97% on consecutive calls."),
|
|
470
480
|
ax_tree: z.boolean().optional().describe("Bypass the extension entirely and read raw Chrome Accessibility Tree. Use this if Shadow DOMs hide elements."),
|
|
@@ -551,7 +561,7 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
551
561
|
targetId = args.tab_id;
|
|
552
562
|
}
|
|
553
563
|
if (args.url) {
|
|
554
|
-
const navResult = await agentAction(targetSession, 'navigating', () => weaveNavigate(targetSession, args.url, liveConfig, targetId, args.force), 'browser_navigate');
|
|
564
|
+
const navResult = await agentAction(targetSession, 'navigating', () => weaveNavigate(targetSession, args.url, liveConfig, targetId, args.force, false, resolvedApp), 'browser_navigate');
|
|
555
565
|
await hookPipeline.dispatchPageNavigate(args.url, targetSession);
|
|
556
566
|
return navResult;
|
|
557
567
|
}
|
|
@@ -569,7 +579,7 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
569
579
|
selector: z.string().optional().describe("CSS selector of the element to click."),
|
|
570
580
|
target: z.string().optional().describe("CSS selector or query string. Alias for selector."),
|
|
571
581
|
label: z.string().optional().describe("Visible text or aria-label to click."),
|
|
572
|
-
intent: z.string().optional().describe("Semantic description of the action (e.g. 'submit login form').
|
|
582
|
+
intent: z.string().optional().describe("Semantic description of the action (e.g. 'submit login form'). Weavetab checks learned memory first — if a high-confidence selector is found it clicks immediately without DOM resolution. Falls through to normal resolution on a miss; does not error."),
|
|
573
583
|
mark: z.number().int().positive().optional().describe("Mark number from browser_map({ visual: true/\"auto\" }). Clicks the center of the numbered badge seen in the annotated screenshot. Zero DOM walk — instant coordinate dispatch. Use when the page is canvas/WebGL and DOM refs are unavailable."),
|
|
574
584
|
backendNodeId: z.number().optional().describe("Raw CDP backendNodeId for direct hardware targeting."),
|
|
575
585
|
x: z.number().optional().describe("Raw X coordinate. Use for precise positional clicks when element ID is not available."),
|
|
@@ -691,6 +701,9 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
691
701
|
label: z.string().optional().describe("Element label to scroll into view."),
|
|
692
702
|
direction: z.enum(["up", "down", "left", "right"]).describe("Scroll direction."),
|
|
693
703
|
distance: z.number().optional().describe("Pixels to scroll (default 400)."),
|
|
704
|
+
pixels: z.number().optional().describe("Alias for distance."),
|
|
705
|
+
amount: z.number().optional().describe("Alias for distance."),
|
|
706
|
+
speed: z.enum(["human", "read", "fast"]).optional().describe("Scroll animation speed profile."),
|
|
694
707
|
tab_id: z.string().optional().describe("Execute in a specific tab instead of active. DO NOT SPAM. If action fails or loops, STOP calling this tool and use browser_map to re-evaluate the page state."),
|
|
695
708
|
app: z.string().optional().describe("Electron app name configured in config.json under 'electron'."),
|
|
696
709
|
}, async (args) => {
|
|
@@ -709,6 +722,7 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
709
722
|
normalizedArgs.id = args.selector;
|
|
710
723
|
if (args.target && !normalizedArgs.id)
|
|
711
724
|
normalizedArgs.id = args.target;
|
|
725
|
+
normalizedArgs.distance = args.distance ?? args.pixels ?? args.amount ?? 400;
|
|
712
726
|
return await browserScroll(targetSession, normalizedArgs, liveConfig);
|
|
713
727
|
}, false, args.app);
|
|
714
728
|
}
|
|
@@ -777,19 +791,19 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
777
791
|
return await WeavetabList(liveConfig, resolvedApp);
|
|
778
792
|
if (action === "open") {
|
|
779
793
|
if (!url)
|
|
780
|
-
|
|
794
|
+
return { success: false, error_code: "MISSING_PARAM", message: "url is required for action: open" };
|
|
781
795
|
return await WeavetabOpen(url, liveConfig, reuse !== false, resolvedApp);
|
|
782
796
|
}
|
|
783
797
|
if (action === "switch") {
|
|
784
798
|
if (!tabId)
|
|
785
|
-
|
|
799
|
+
return { success: false, error_code: "MISSING_PARAM", message: "tabId is required for action: switch" };
|
|
786
800
|
return await WeavetabSwitch(tabId, liveConfig, resolvedApp);
|
|
787
801
|
}
|
|
788
802
|
if (action === "close") {
|
|
789
803
|
if (tabIds && tabIds.length > 0)
|
|
790
804
|
return await WeavetabCloseBatch(tabIds, liveConfig, force, resolvedApp);
|
|
791
805
|
if (!tabId)
|
|
792
|
-
|
|
806
|
+
return { success: false, error_code: "MISSING_PARAM", message: "tabId or tabIds is required for action: close" };
|
|
793
807
|
return await WeavetabClose(tabId, liveConfig, force, resolvedApp);
|
|
794
808
|
}
|
|
795
809
|
if (action === "list_windows") {
|
|
@@ -797,7 +811,7 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
797
811
|
}
|
|
798
812
|
if (action === "focus_window") {
|
|
799
813
|
if (windowId === undefined)
|
|
800
|
-
|
|
814
|
+
return { success: false, error_code: "MISSING_PARAM", message: "windowId is required for action: focus_window" };
|
|
801
815
|
return await weaveFocusWindow(windowId, liveConfig, resolvedApp);
|
|
802
816
|
}
|
|
803
817
|
if (action === "consolidate") {
|
|
@@ -806,7 +820,7 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
806
820
|
if (action === "heal") {
|
|
807
821
|
return await WeavetabHeal(liveConfig, resolvedApp);
|
|
808
822
|
}
|
|
809
|
-
|
|
823
|
+
return { success: false, error_code: "INVALID_ACTION", message: `Invalid action: ${action}` };
|
|
810
824
|
}, false, resolvedApp);
|
|
811
825
|
}
|
|
812
826
|
catch (e) {
|
|
@@ -949,7 +963,10 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
949
963
|
}
|
|
950
964
|
});
|
|
951
965
|
// ── 14. browser_select ───────────────────────────────────────────────────────
|
|
952
|
-
server.tool("browser_select", "Select an option from a native dropdown.", {
|
|
966
|
+
server.tool("browser_select", "Select an option from a native dropdown or custom UI dropdown.", {
|
|
967
|
+
id: z.string().optional().describe("Element ref ID (e.g. 'w:42') or selector."),
|
|
968
|
+
ref: z.string().optional().describe("Alias for id."),
|
|
969
|
+
target: z.string().optional().describe("Alternative selector or ref ID."),
|
|
953
970
|
nodeId: z.number().optional().describe("CDP backendNodeId."),
|
|
954
971
|
selector: z.string().optional().describe("CSS Selector."),
|
|
955
972
|
value: z.string().describe("The exact text value to select."),
|
|
@@ -957,7 +974,12 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
957
974
|
}, async (args) => {
|
|
958
975
|
try {
|
|
959
976
|
return await withSecurity(config, "browser_select", async (session, liveConfig) => {
|
|
960
|
-
|
|
977
|
+
const normalized = { ...args };
|
|
978
|
+
if (args.ref && !normalized.id)
|
|
979
|
+
normalized.id = args.ref;
|
|
980
|
+
if (args.target && !normalized.id)
|
|
981
|
+
normalized.id = args.target;
|
|
982
|
+
return await browserSelect(session, normalized, liveConfig);
|
|
961
983
|
}, false, args.app);
|
|
962
984
|
}
|
|
963
985
|
catch (e) {
|
|
@@ -1060,8 +1082,8 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1060
1082
|
// ── 23. browser_storage ─────────────────────────────────────────────────────
|
|
1061
1083
|
server.tool("browser_storage", "Manage localStorage and sessionStorage.", {
|
|
1062
1084
|
scope: z.enum(["local", "session"]).describe("Storage scope to manipulate."),
|
|
1063
|
-
action: z.enum(["get", "set", "clear"]).describe("
|
|
1064
|
-
key: z.string().optional().describe("Key name (required for 'get' and '
|
|
1085
|
+
action: z.enum(["get", "set", "remove", "list", "clear"]).describe("'get'/'set' a key, 'remove' a single key, 'list' all keys with values, or 'clear' the scope."),
|
|
1086
|
+
key: z.string().optional().describe("Key name (required for 'get', 'set', and 'remove' actions)."),
|
|
1065
1087
|
value: z.string().optional().describe("String value (required for 'set' action)."),
|
|
1066
1088
|
app: z.string().optional().describe("Electron app name configured in config.json under 'electron'."),
|
|
1067
1089
|
}, async (args) => {
|
|
@@ -1075,7 +1097,7 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1075
1097
|
}
|
|
1076
1098
|
});
|
|
1077
1099
|
// ── 24. browser_network_intercept ───────────────────────────────────────────
|
|
1078
|
-
server.tool("browser_network_intercept", `Intercept/stub network responses (e.g. mock APIs
|
|
1100
|
+
server.tool("browser_network_intercept", `Intercept/stub network responses (e.g. mock APIs) OR retrieve captured network telemetry: per-request TLS/SSL certificate details (when the connection exposes them), millisecond timing phases, and failure reasons.`, {
|
|
1079
1101
|
action: z.enum(["enable", "disable", "get_telemetry", "clear_telemetry"]).describe("Action to perform: 'enable'/'disable' network response interception, 'get_telemetry' to read captured requests with TLS/SSL certs & timing breakdown, or 'clear_telemetry' to reset buffer."),
|
|
1080
1102
|
pattern: z.string().optional().describe("URL glob pattern to intercept or filter telemetry (e.g. '*api/login*')."),
|
|
1081
1103
|
mockStatus: z.number().optional().describe("HTTP status code to return when enabling mock (default 200)."),
|
|
@@ -1116,6 +1138,15 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1116
1138
|
landscape: z.boolean().optional().describe("Print in landscape orientation (default false)."),
|
|
1117
1139
|
scale: z.number().optional().describe("Scale factor (default 1.0)."),
|
|
1118
1140
|
pageRanges: z.string().optional().describe("Page ranges to print (e.g. '1-5'). Leave empty for all pages."),
|
|
1141
|
+
margin: z.union([
|
|
1142
|
+
z.number(),
|
|
1143
|
+
z.object({
|
|
1144
|
+
top: z.number().optional(),
|
|
1145
|
+
bottom: z.number().optional(),
|
|
1146
|
+
left: z.number().optional(),
|
|
1147
|
+
right: z.number().optional()
|
|
1148
|
+
})
|
|
1149
|
+
]).optional().describe("Page margin in inches (default 0.4). Pass a single number (all sides) or an object {top,bottom,left,right}."),
|
|
1119
1150
|
app: z.string().optional().describe("Electron app name configured in config.json under 'electron'."),
|
|
1120
1151
|
}, async (args) => {
|
|
1121
1152
|
try {
|
|
@@ -1149,6 +1180,10 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1149
1180
|
value: z.string().optional().describe("Cookie value (required for 'set')."),
|
|
1150
1181
|
domain: z.string().optional().describe("Domain for the cookie."),
|
|
1151
1182
|
path: z.string().optional().describe("Path for the cookie."),
|
|
1183
|
+
expires: z.number().optional().describe("Expiry as a Unix timestamp in seconds (omit for a session cookie)."),
|
|
1184
|
+
httpOnly: z.boolean().optional().describe("Set the HttpOnly flag (default false)."),
|
|
1185
|
+
secure: z.boolean().optional().describe("Set the Secure flag (auto-true for https pages)."),
|
|
1186
|
+
sameSite: z.enum(["Strict", "Lax", "None"]).optional().describe("SameSite attribute."),
|
|
1152
1187
|
url: z.string().optional().describe("URL scope for 'get' action (defaults to current page)."),
|
|
1153
1188
|
app: z.string().optional().describe("Electron app name configured in config.json under 'electron'."),
|
|
1154
1189
|
}, async (args) => {
|
|
@@ -1202,6 +1237,7 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1202
1237
|
server.tool("browser_snapshot", "Capture or restore an MHTML snapshot. Best for offline archival, replay testing, and compliance audits.", {
|
|
1203
1238
|
action: z.enum(["capture", "restore"]).describe("Action to perform."),
|
|
1204
1239
|
filePath: z.string().describe("Absolute file path to save/load the .mhtml snapshot."),
|
|
1240
|
+
hideOverlay: z.boolean().optional().describe("If false, keeps the Weavetab HUD and overlay visible in snapshot. Default true."),
|
|
1205
1241
|
app: z.string().optional().describe("Electron app name configured in config.json under 'electron'."),
|
|
1206
1242
|
}, async (args) => {
|
|
1207
1243
|
try {
|
|
@@ -1224,6 +1260,7 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1224
1260
|
width: z.number().describe("Width of clip region."),
|
|
1225
1261
|
height: z.number().describe("Height of clip region.")
|
|
1226
1262
|
}).optional().describe("Clip region. Use to capture only part of the page (e.g. just a chart area) to reduce token cost."),
|
|
1263
|
+
selector: z.string().optional().describe("CSS selector of an element to crop the capture to (its bounding rect)."),
|
|
1227
1264
|
app: z.string().optional().describe("Electron app name configured in config.json under 'electron'."),
|
|
1228
1265
|
}, async (args) => {
|
|
1229
1266
|
try {
|
|
@@ -1236,12 +1273,20 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1236
1273
|
}
|
|
1237
1274
|
});
|
|
1238
1275
|
// ── 33. browser_viewport ────────────────────────────────────────────────────
|
|
1239
|
-
server.tool("browser_viewport", "Set viewport dimensions and device emulation.", {
|
|
1240
|
-
action: z.enum(["set", "clear"]).describe("
|
|
1276
|
+
server.tool("browser_viewport", "Set viewport dimensions and device emulation (device presets, geolocation, locale, timezone, color scheme, touch, orientation).", {
|
|
1277
|
+
action: z.enum(["set", "clear", "list", "preset"]).describe("'set' viewport/emulation, 'clear' all overrides, 'list' available device presets, 'preset' apply a named device."),
|
|
1241
1278
|
width: z.number().optional().describe("Viewport width in pixels."),
|
|
1242
1279
|
height: z.number().optional().describe("Viewport height in pixels."),
|
|
1243
1280
|
deviceScaleFactor: z.number().optional().describe("Device Pixel Ratio (default 1)."),
|
|
1244
1281
|
mobile: z.boolean().optional().describe("Whether to emulate a mobile device (default false)."),
|
|
1282
|
+
devicePreset: z.string().optional().describe("Device preset name for action='preset' (see action='list')."),
|
|
1283
|
+
userAgent: z.string().optional().describe("Override the User Agent string."),
|
|
1284
|
+
geolocation: z.object({ latitude: z.number(), longitude: z.number(), accuracy: z.number().optional() }).optional().describe("Override geolocation coordinates."),
|
|
1285
|
+
locale: z.string().optional().describe("Override locale (e.g. 'en-US')."),
|
|
1286
|
+
timezoneId: z.string().optional().describe("Override timezone (e.g. 'America/New_York')."),
|
|
1287
|
+
colorScheme: z.enum(["light", "dark", "no-preference"]).optional().describe("Emulate prefers-color-scheme."),
|
|
1288
|
+
hasTouch: z.boolean().optional().describe("Whether to emulate touch support (defaults to the 'mobile' flag)."),
|
|
1289
|
+
screenOrientation: z.enum(["portrait", "landscape", "none"]).optional().describe("Emulated screen orientation."),
|
|
1245
1290
|
app: z.string().optional().describe("Electron app name configured in config.json under 'electron'."),
|
|
1246
1291
|
}, async (args) => {
|
|
1247
1292
|
try {
|
|
@@ -1355,12 +1400,13 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1355
1400
|
}
|
|
1356
1401
|
});
|
|
1357
1402
|
// ── browser_performance ───────────────────────────────────────────────────
|
|
1358
|
-
server.tool("browser_performance", "Retrieve page performance metrics.", {
|
|
1403
|
+
server.tool("browser_performance", "Retrieve page performance metrics: Core Web Vitals (FCP, LCP, CLS, INP, TTFB), JS heap memory, navigation timing, slowest resources, and raw CDP counters.", {
|
|
1404
|
+
metrics: z.array(z.enum(["vitals", "memory", "navigation", "resources", "raw"])).optional().describe("Sections to return. Defaults to all sections."),
|
|
1359
1405
|
app: z.string().optional().describe("Electron app name configured in config.json under 'electron'."),
|
|
1360
1406
|
}, async (args) => {
|
|
1361
1407
|
try {
|
|
1362
1408
|
return await withSecurity(config, "browser_performance", async (session, liveConfig) => {
|
|
1363
|
-
const result = await browserPerformance(session, liveConfig);
|
|
1409
|
+
const result = await browserPerformance(session, liveConfig, args);
|
|
1364
1410
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
1365
1411
|
}, false, args.app);
|
|
1366
1412
|
}
|
|
@@ -1444,6 +1490,9 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1444
1490
|
if (args.success === undefined)
|
|
1445
1491
|
return { content: [{ type: "text", text: JSON.stringify({ error: "success (boolean) is required for action='record_execution'" }) }] };
|
|
1446
1492
|
const updated = await recordPatternExecution(args.origin, args.name, args.success, args.duration_ms);
|
|
1493
|
+
if (!updated) {
|
|
1494
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: false, error_code: "PATTERN_NOT_FOUND", message: `No pattern named '${args.name}' exists for origin '${args.origin}'. Learn it first with action='learn'.` }) }] };
|
|
1495
|
+
}
|
|
1447
1496
|
return { content: [{ type: "text", text: JSON.stringify({ ok: true, pattern: updated }) }] };
|
|
1448
1497
|
}
|
|
1449
1498
|
if (args.action === "record_quirk") {
|
|
@@ -1460,36 +1509,15 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
1460
1509
|
return wrapError(e);
|
|
1461
1510
|
}
|
|
1462
1511
|
});
|
|
1463
|
-
// ── GitHub Tool (v2.5.0 Consolidated) ──────────────────────────────────
|
|
1464
|
-
server.tool("browser_github", "Interact with GitHub repositories without burning browser turns. Analyze repo trees, read raw source files, search/inspect issues, and examine PR diffs and review comments.", {
|
|
1465
|
-
action: z.enum(["analyze", "read", "issues", "pr"]).describe("GitHub operation: 'analyze' (repo structure & README), 'read' (raw file contents), 'issues' (list/search/inspect issues), 'pr' (PR details, diff files & comments)."),
|
|
1466
|
-
repo: z.string().optional().describe("Optional 'owner/repo' slug or GitHub URL. If omitted, uses active tab's repository."),
|
|
1467
|
-
paths: z.array(z.string()).optional().describe("For action: 'read'. File paths relative to repo root."),
|
|
1468
|
-
line_range: z.array(z.number()).length(2).optional().describe("For action: 'read'. Optional [start, end] line range (1-indexed)."),
|
|
1469
|
-
number: z.number().optional().describe("For action: 'issues' or 'pr'. Issue or PR number to fetch details and comments."),
|
|
1470
|
-
search: z.string().optional().describe("For action: 'issues'. Search query to find issues."),
|
|
1471
|
-
labels: z.array(z.string()).optional().describe("For action: 'issues'. Filter issues by labels."),
|
|
1472
|
-
state: z.enum(["open", "closed", "all"]).optional().describe("For action: 'issues'. Filter issues by state."),
|
|
1473
|
-
limit: z.number().optional().describe("For action: 'issues'. Max number of issues to list (default 5).")
|
|
1474
|
-
}, async (args) => {
|
|
1475
|
-
try {
|
|
1476
|
-
return await withSecurity(config, "browser_github", async (session, liveConfig) => {
|
|
1477
|
-
return await browserGithub(session, args, liveConfig);
|
|
1478
|
-
});
|
|
1479
|
-
}
|
|
1480
|
-
catch (e) {
|
|
1481
|
-
return wrapError(e);
|
|
1482
|
-
}
|
|
1483
|
-
});
|
|
1484
1512
|
// ── 43. browser_office ──────────────────────────────────────────────────────
|
|
1485
|
-
server.tool("browser_office", `Read and extract structured content from office documents (Excel, Word, PDF, CSV). Note: PDF/Word extraction requires local binary dependencies (e.g., poppler) and output is truncated by max_chars/max_rows limits. Two modes:
|
|
1486
|
-
- **url** — fetch a document URL using the browser's live session cookies (works for authenticated downloads, SharePoint, Google Drive exports, etc). Automatically detects format from URL or MIME type.
|
|
1487
|
-
- **file** — parse a file that was already downloaded to the local downloads directory (bare filename only, e.g. 'report.xlsx').
|
|
1488
|
-
|
|
1489
|
-
Supported formats: .xlsx/.xls (returns per-sheet JSON tables), .docx (returns extracted text), .pdf (returns page text + metadata), .csv (returns row objects).
|
|
1490
|
-
|
|
1491
|
-
Use 'list' action first if you don't know what files are in the downloads directory.
|
|
1492
|
-
|
|
1513
|
+
server.tool("browser_office", `Read and extract structured content from office documents (Excel, Word, PDF, CSV). Note: PDF/Word extraction requires local binary dependencies (e.g., poppler) and output is truncated by max_chars/max_rows limits. Two modes:
|
|
1514
|
+
- **url** — fetch a document URL using the browser's live session cookies (works for authenticated downloads, SharePoint, Google Drive exports, etc). Automatically detects format from URL or MIME type.
|
|
1515
|
+
- **file** — parse a file that was already downloaded to the local downloads directory (bare filename only, e.g. 'report.xlsx').
|
|
1516
|
+
|
|
1517
|
+
Supported formats: .xlsx/.xls (returns per-sheet JSON tables), .docx (returns extracted text), .pdf (returns page text + metadata), .csv (returns row objects).
|
|
1518
|
+
|
|
1519
|
+
Use 'list' action first if you don't know what files are in the downloads directory.
|
|
1520
|
+
|
|
1493
1521
|
DO NOT use browser_scrape or browser_map for binary documents — they cannot read inside Excel, Word, or PDF files.`, {
|
|
1494
1522
|
action: z.enum(["read", "list"]).describe("'read' — parse a document and return its content. 'list' — show all files in the downloads directory."),
|
|
1495
1523
|
url: z.string().optional().describe("URL of the document to fetch and parse in-browser (using active session cookies). Supports .pdf, .xlsx, .xls, .docx, .csv. Use for authenticated downloads, SharePoint links, Google Drive exports."),
|
|
@@ -1542,6 +1570,7 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
|
|
|
1542
1570
|
accuracy: z.number().optional(),
|
|
1543
1571
|
}).optional().describe("Override geolocation coordinates"),
|
|
1544
1572
|
timezoneId: z.string().optional().describe("Override browser timezone (e.g. 'America/New_York')"),
|
|
1573
|
+
colorScheme: z.enum(["light", "dark", "no-preference"]).optional().describe("Emulate prefers-color-scheme (dark/light mode)."),
|
|
1545
1574
|
userAgent: z.string().optional().describe("Override User Agent string"),
|
|
1546
1575
|
app: z.string().optional().describe("Electron app name configured in config.json under 'electron'."),
|
|
1547
1576
|
}, async (args) => {
|
|
@@ -1579,7 +1608,7 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
|
|
|
1579
1608
|
return {
|
|
1580
1609
|
messages: [{
|
|
1581
1610
|
role: "user",
|
|
1582
|
-
content: { type: "text", text: "No active
|
|
1611
|
+
content: { type: "text", text: "No active Weavetab session found. Open a browser tab and navigate to a page first." }
|
|
1583
1612
|
}]
|
|
1584
1613
|
};
|
|
1585
1614
|
}
|
|
@@ -1588,22 +1617,22 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
|
|
|
1588
1617
|
let pageContextStr = "";
|
|
1589
1618
|
try {
|
|
1590
1619
|
const { result: metrics } = await session.Runtime.evaluate({
|
|
1591
|
-
expression: `
|
|
1592
|
-
new Promise(resolve => {
|
|
1593
|
-
const getMetrics = () => resolve(JSON.stringify({
|
|
1594
|
-
readyState: document.readyState,
|
|
1595
|
-
w: window.innerWidth,
|
|
1596
|
-
h: window.innerHeight,
|
|
1597
|
-
scrollX: Math.round(window.scrollX),
|
|
1598
|
-
scrollY: Math.round(window.scrollY)
|
|
1599
|
-
}));
|
|
1600
|
-
if (document.readyState === 'complete') {
|
|
1601
|
-
getMetrics();
|
|
1602
|
-
} else {
|
|
1603
|
-
setTimeout(getMetrics, 1500); // Max wait if slow
|
|
1604
|
-
window.addEventListener('load', getMetrics);
|
|
1605
|
-
}
|
|
1606
|
-
})
|
|
1620
|
+
expression: `
|
|
1621
|
+
new Promise(resolve => {
|
|
1622
|
+
const getMetrics = () => resolve(JSON.stringify({
|
|
1623
|
+
readyState: document.readyState,
|
|
1624
|
+
w: window.innerWidth,
|
|
1625
|
+
h: window.innerHeight,
|
|
1626
|
+
scrollX: Math.round(window.scrollX),
|
|
1627
|
+
scrollY: Math.round(window.scrollY)
|
|
1628
|
+
}));
|
|
1629
|
+
if (document.readyState === 'complete') {
|
|
1630
|
+
getMetrics();
|
|
1631
|
+
} else {
|
|
1632
|
+
setTimeout(getMetrics, 1500); // Max wait if slow
|
|
1633
|
+
window.addEventListener('load', getMetrics);
|
|
1634
|
+
}
|
|
1635
|
+
})
|
|
1607
1636
|
`,
|
|
1608
1637
|
awaitPromise: true,
|
|
1609
1638
|
returnByValue: true
|
|
@@ -1650,7 +1679,7 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
|
|
|
1650
1679
|
catch { /* non-fatal */ }
|
|
1651
1680
|
// 4. Assemble the prompt text
|
|
1652
1681
|
const promptText = [
|
|
1653
|
-
`# 🌐
|
|
1682
|
+
`# 🌐 Weavetab Context: ${title ?? "(unknown)"}`,
|
|
1654
1683
|
`**URL:** \`${url ?? "(unknown)"}\``,
|
|
1655
1684
|
pageContextStr,
|
|
1656
1685
|
frameworkHint,
|
|
@@ -1729,11 +1758,11 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
|
|
|
1729
1758
|
}
|
|
1730
1759
|
const existingTool = server._registeredTools?.[ov.name];
|
|
1731
1760
|
const existingDesc = existingTool?.description || "";
|
|
1732
|
-
const existingSchema = existingTool?.parameters?.shape || {};
|
|
1761
|
+
const existingSchema = existingTool?.inputSchema?.shape || existingTool?.parameters?.shape || {};
|
|
1733
1762
|
if (existingTool) {
|
|
1734
1763
|
delete server._registeredTools[ov.name];
|
|
1735
1764
|
}
|
|
1736
|
-
server.tool(ov.name, ov.description || existingDesc || `Tool overridden by ${plugin.name}`, ov.schema
|
|
1765
|
+
server.tool(ov.name, ov.description || existingDesc || `Tool overridden by ${plugin.name}`, ov.schema ? toPluginInputShape(ov.schema) : existingSchema, async (args) => {
|
|
1737
1766
|
try {
|
|
1738
1767
|
return await withSecurity(config, ov.name, async (session, liveConfig) => {
|
|
1739
1768
|
const wrappedOrig = existingTool
|
|
@@ -1766,12 +1795,12 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
|
|
|
1766
1795
|
process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} registered tool "${td.name}" which replaced an existing tool.\x1b[0m\n`);
|
|
1767
1796
|
delete server._registeredTools[td.name];
|
|
1768
1797
|
}
|
|
1769
|
-
server.tool(td.name, td.description, td.schema || td.parameters
|
|
1798
|
+
server.tool(td.name, td.description, toPluginInputShape(td.schema || td.parameters), async (args) => {
|
|
1770
1799
|
try {
|
|
1771
1800
|
return await withSecurity(config, td.name, async (session, liveConfig) => {
|
|
1772
1801
|
const result = await td.handler(args, session, liveConfig);
|
|
1773
1802
|
return result;
|
|
1774
|
-
}, isOfficial);
|
|
1803
|
+
}, isOfficial, undefined, td.requiresSession !== false);
|
|
1775
1804
|
}
|
|
1776
1805
|
catch (e) {
|
|
1777
1806
|
return wrapError(e);
|
|
@@ -1791,11 +1820,11 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
|
|
|
1791
1820
|
process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} dynamic tool "${tool.name}" replaced an existing tool.\x1b[0m\n`);
|
|
1792
1821
|
delete server._registeredTools[tool.name];
|
|
1793
1822
|
}
|
|
1794
|
-
server.tool(tool.name, tool.description || "", tool.schema || tool.parameters
|
|
1823
|
+
server.tool(tool.name, tool.description || "", toPluginInputShape(tool.schema || tool.parameters), async (args) => {
|
|
1795
1824
|
return await withSecurity(config, tool.name, async (session, liveConfig) => {
|
|
1796
1825
|
const result = await tool.handler(args, session, liveConfig);
|
|
1797
1826
|
return result;
|
|
1798
|
-
}, isOfficial);
|
|
1827
|
+
}, isOfficial, undefined, tool.requiresSession !== false);
|
|
1799
1828
|
});
|
|
1800
1829
|
};
|
|
1801
1830
|
fullPluginCtx.mcp.overrideTool = (toolName, overrideHandler) => {
|