@weavetab/mcp 2.5.0-beta.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +212 -627
  2. package/README.md +25 -27
  3. package/dist/cdp/connector.js +2 -2
  4. package/dist/cli/plugin.d.ts +1 -0
  5. package/dist/cli/plugin.js +60 -4
  6. package/dist/config/loader.d.ts +1 -1
  7. package/dist/config/loader.js +1 -1
  8. package/dist/index.js +42 -6
  9. package/dist/plugin/interface.d.ts +14 -5
  10. package/dist/plugin/loader.d.ts +0 -3
  11. package/dist/plugin/loader.js +78 -15
  12. package/dist/plugin/registry.d.ts +2 -1
  13. package/dist/plugin/registry.js +21 -12
  14. package/dist/security/secrets.d.ts +6 -0
  15. package/dist/security/secrets.js +24 -0
  16. package/dist/server.d.ts +1 -1
  17. package/dist/server.js +37 -34
  18. package/dist/tools/browser_detect.js +2 -2
  19. package/dist/tools/captcha.js +1 -1
  20. package/dist/tools/navigate.d.ts +1 -1
  21. package/dist/tools/navigate.js +5 -5
  22. package/dist/tools/tabs.js +1 -1
  23. package/dist/version.d.ts +1 -1
  24. package/dist/version.js +1 -1
  25. package/docs/DOCUMENTATION.md +18 -11
  26. package/docs/PLUGINS.md +5 -5
  27. package/docs/TOOLS.md +54 -56
  28. package/docs/architecture/overview.md +1 -1
  29. package/docs/architecture/security.md +36 -35
  30. package/docs/architecture/transport.md +37 -37
  31. package/docs/cli/cli-commands.md +50 -44
  32. package/docs/cli/cli-flags.md +28 -20
  33. package/docs/cli/cli-scripting.md +35 -32
  34. package/docs/configuration/config-locations.md +23 -24
  35. package/docs/configuration/env-variables.md +19 -19
  36. package/docs/desktop-automation/electron-apps.md +3 -6
  37. package/docs/get-started/introduction.md +4 -4
  38. package/docs/get-started/setup-wizard.md +27 -26
  39. package/docs/tools/tools.md +1 -1
  40. package/docs/troubleshooting/trouble-browser.md +27 -27
  41. package/icon.png +0 -0
  42. package/mcp.json +3 -21
  43. package/package.json +3 -3
  44. package/server.json +6 -6
  45. package/smithery.yaml +7 -11
@@ -19,7 +19,8 @@ export interface PluginCandidate {
19
19
  description?: string;
20
20
  }
21
21
  export declare const OFFICIAL_PLUGIN_PREFIX = "@weavetab/plugin-";
22
- export declare const REGISTRY_URL = "https://raw.githubusercontent.com/weavetab/registry/main/verified.json";
22
+ export declare const REGISTRY_URL = "https://weavetab.dev/api/registry/verified.json";
23
+ export declare const REGISTRY_FALLBACK_URL = "https://raw.githubusercontent.com/weavetab/registry/main/verified.json";
23
24
  /**
24
25
  * Returns the set of verified package names.
25
26
  * - Tries local registry/verified.json first (dev/test mode).
@@ -18,8 +18,9 @@ const __dirname = dirname(__filename);
18
18
  // Verification Registry
19
19
  // ---------------------------------------------------------------------------
20
20
  export const OFFICIAL_PLUGIN_PREFIX = "@weavetab/plugin-";
21
- // The production remote URL — points to weavetab/registry on GitHub.
22
- export const REGISTRY_URL = "https://raw.githubusercontent.com/weavetab/registry/main/verified.json";
21
+ // The production remote URLs — weavetab.dev (D1-backed) primary, GitHub raw fallback.
22
+ export const REGISTRY_URL = "https://weavetab.dev/api/registry/verified.json";
23
+ export const REGISTRY_FALLBACK_URL = "https://raw.githubusercontent.com/weavetab/registry/main/verified.json";
23
24
  // Local override: when this file exists at the root of the repo, we use it instead
24
25
  // of the remote URL. This allows full offline/local testing.
25
26
  const LOCAL_REGISTRY_PATH = join(__dirname, "..", "..", "..", "registry", "verified.json");
@@ -59,16 +60,24 @@ function loadLocalRegistry() {
59
60
  return null;
60
61
  }
61
62
  async function fetchRemoteRegistry() {
62
- const res = await fetch(REGISTRY_URL, {
63
- signal: AbortSignal.timeout(5000),
64
- headers: { "User-Agent": "Weavetab-Registry-Check/1.0" },
65
- });
66
- if (!res.ok)
67
- throw new Error(`registry fetch failed: ${res.status}`);
68
- const data = await res.json();
69
- if (!Array.isArray(data.verified))
70
- throw new Error("invalid registry format");
71
- return extractPackageNames(data.verified);
63
+ for (const url of [REGISTRY_URL, REGISTRY_FALLBACK_URL]) {
64
+ try {
65
+ const res = await fetch(url, {
66
+ signal: AbortSignal.timeout(5000),
67
+ headers: { "User-Agent": "Weavetab-Registry-Check/1.0" },
68
+ });
69
+ if (!res.ok)
70
+ continue;
71
+ const data = await res.json();
72
+ if (!Array.isArray(data.verified))
73
+ continue;
74
+ return extractPackageNames(data.verified);
75
+ }
76
+ catch {
77
+ continue;
78
+ }
79
+ }
80
+ throw new Error("registry fetch failed: all sources unreachable");
72
81
  }
73
82
  /**
74
83
  * Returns the set of verified package names.
@@ -18,6 +18,12 @@ export interface SecretListEntry {
18
18
  updatedAt: string;
19
19
  }
20
20
  export declare const GLOBAL_SECRETS_PATH: string;
21
+ export declare const PROJECT_SECRETS_DIRNAME = ".weavetab";
22
+ export declare const PROJECT_SECRETS_FILENAME = "secrets.json";
23
+ /** Absolute path of the project-scoped secrets file for a directory (default: cwd). */
24
+ export declare function getProjectSecretsPath(cwd?: string): string;
25
+ /** Creates an empty project-scoped secrets file (chmod 600). Returns its path. */
26
+ export declare function initProjectSecrets(cwd?: string): string;
21
27
  declare class JsonSecretStoreImpl implements SecretProvider {
22
28
  /**
23
29
  * Resolve a secret value by key.
@@ -86,7 +86,31 @@ function decrypt(ciphertext) {
86
86
  import { GLOBAL_CONFIG_DIR } from "../config/loader.js";
87
87
  // ─── Path resolution ──────────────────────────────────────────────────────────
88
88
  export const GLOBAL_SECRETS_PATH = path.join(GLOBAL_CONFIG_DIR, "secrets.json");
89
+ export const PROJECT_SECRETS_DIRNAME = ".weavetab";
90
+ export const PROJECT_SECRETS_FILENAME = "secrets.json";
91
+ /** Absolute path of the project-scoped secrets file for a directory (default: cwd). */
92
+ export function getProjectSecretsPath(cwd = process.cwd()) {
93
+ return path.join(cwd, PROJECT_SECRETS_DIRNAME, PROJECT_SECRETS_FILENAME);
94
+ }
95
+ /** Creates an empty project-scoped secrets file (chmod 600). Returns its path. */
96
+ export function initProjectSecrets(cwd = process.cwd()) {
97
+ const filePath = getProjectSecretsPath(cwd);
98
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
99
+ if (!fs.existsSync(filePath)) {
100
+ fs.writeFileSync(filePath, JSON.stringify({ version: 1, secrets: {} }, null, 2), { mode: 0o600 });
101
+ }
102
+ return filePath;
103
+ }
89
104
  function resolveSecretsPath() {
105
+ // Project-scoped file wins when present — lets a checkout carry its own keys.
106
+ try {
107
+ const projectPath = getProjectSecretsPath();
108
+ if (fs.existsSync(projectPath))
109
+ return projectPath;
110
+ }
111
+ catch {
112
+ // fall through to global
113
+ }
90
114
  return GLOBAL_SECRETS_PATH;
91
115
  }
92
116
  // ─── Permission guard ─────────────────────────────────────────────────────────
package/dist/server.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Weavetab — server.ts
3
- * MCP server: 44 browser automation tools with security, rate limiting, and visual overlay.
3
+ * MCP server: 42 browser automation tools with security, rate limiting, and visual overlay.
4
4
  * Weavetab by fy2ne
5
5
  */
6
6
  import type { Config } from "./config/loader.js";
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Weavetab — server.ts
3
- * MCP server: 44 browser automation tools with security, rate limiting, and visual overlay.
3
+ * MCP server: 42 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";
@@ -53,7 +53,7 @@ import { browserCheckpoint } from "./tools/checkpoint.js";
53
53
  import { getActiveEngine } from "./cdp/connector.js";
54
54
  import { resetLoopCounter } from "./intelligence/trail.js";
55
55
  import { getSiteProfile, learnPattern, recordPatternExecution, recordQuirk, getPatternReliability, formatSiteProfileHint } from "./intelligence/patternCompiler.js";
56
- import { discoverPlugins, listInstalledPlugins, loadPluginByName } from "./plugin/loader.js";
56
+ import { discoverPlugins } from "./plugin/loader.js";
57
57
  import { hookPipeline } from "./plugin/pipeline.js";
58
58
  import { agentAction } from "./sensors/pacing.js";
59
59
  import { getCachedConfig, getElectronApps } from "./config/loader.js";
@@ -1701,22 +1701,32 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
1701
1701
  }
1702
1702
  // 1. Tool Removals (disable built-in tools)
1703
1703
  const manifestPerms = plugin.manifest?.permissions;
1704
- const manifestToolsRemove = Array.isArray(manifestPerms)
1704
+ const declaredRemove = Array.isArray(manifestPerms)
1705
1705
  ? []
1706
1706
  : manifestPerms?.tools?.remove || [];
1707
1707
  const toRemove = [
1708
1708
  ...(plugin.removeTools || []),
1709
- ...manifestToolsRemove,
1710
1709
  ];
1711
1710
  for (const toolName of toRemove) {
1711
+ if (!declaredRemove.includes(toolName)) {
1712
+ process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} tried to remove "${toolName}" but it's not listed in permissions.tools.remove — skipped.\x1b[0m\n`);
1713
+ continue;
1714
+ }
1712
1715
  if (server._registeredTools?.[toolName]) {
1713
1716
  delete server._registeredTools[toolName];
1714
1717
  removedNames.push(toolName);
1715
1718
  }
1716
1719
  }
1717
1720
  // 2. Tool Overrides (replace or wrap built-in tools)
1721
+ const declaredOverride = Array.isArray(manifestPerms)
1722
+ ? []
1723
+ : manifestPerms?.tools?.override || [];
1718
1724
  const overrides = plugin.overrides || [];
1719
1725
  for (const ov of overrides) {
1726
+ if (!declaredOverride.includes(ov.name)) {
1727
+ process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} tried to override "${ov.name}" but it's not listed in permissions.tools.override — skipped.\x1b[0m\n`);
1728
+ continue;
1729
+ }
1720
1730
  const existingTool = server._registeredTools?.[ov.name];
1721
1731
  const existingDesc = existingTool?.description || "";
1722
1732
  const existingSchema = existingTool?.parameters?.shape || {};
@@ -1745,8 +1755,13 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
1745
1755
  overriddenNames.push(ov.name);
1746
1756
  }
1747
1757
  // 3. Tool Additions (new tools)
1758
+ const canAdd = !Array.isArray(manifestPerms) && manifestPerms?.tools?.add === true;
1748
1759
  const newTools = plugin.tools || [];
1749
1760
  for (const td of newTools) {
1761
+ if (!canAdd) {
1762
+ process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} tried to add tool "${td.name}" but permissions.tools.add is not declared — skipped.\x1b[0m\n`);
1763
+ continue;
1764
+ }
1750
1765
  if (server._registeredTools?.[td.name]) {
1751
1766
  process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} registered tool "${td.name}" which replaced an existing tool.\x1b[0m\n`);
1752
1767
  delete server._registeredTools[td.name];
@@ -1768,6 +1783,10 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
1768
1783
  const fullPluginCtx = plugin._context;
1769
1784
  if (fullPluginCtx && fullPluginCtx.mcp) {
1770
1785
  fullPluginCtx.mcp.registerTool = (tool) => {
1786
+ if (!canAdd) {
1787
+ process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} dynamic registerTool("${tool?.name}"): permissions.tools.add not declared — skipped.\x1b[0m\n`);
1788
+ return;
1789
+ }
1771
1790
  if (server._registeredTools?.[tool.name]) {
1772
1791
  process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} dynamic tool "${tool.name}" replaced an existing tool.\x1b[0m\n`);
1773
1792
  delete server._registeredTools[tool.name];
@@ -1780,6 +1799,10 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
1780
1799
  });
1781
1800
  };
1782
1801
  fullPluginCtx.mcp.overrideTool = (toolName, overrideHandler) => {
1802
+ if (!declaredOverride.includes(toolName)) {
1803
+ process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} dynamic overrideTool("${toolName}"): not in permissions.tools.override — skipped.\x1b[0m\n`);
1804
+ return;
1805
+ }
1783
1806
  const existingTool = server._registeredTools?.[toolName];
1784
1807
  if (existingTool) {
1785
1808
  delete server._registeredTools[toolName];
@@ -1799,11 +1822,19 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
1799
1822
  });
1800
1823
  };
1801
1824
  fullPluginCtx.mcp.removeTool = (toolName) => {
1825
+ if (!declaredRemove.includes(toolName)) {
1826
+ process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} dynamic removeTool("${toolName}"): not in permissions.tools.remove — skipped.\x1b[0m\n`);
1827
+ return;
1828
+ }
1802
1829
  if (server._registeredTools?.[toolName]) {
1803
1830
  delete server._registeredTools[toolName];
1804
1831
  }
1805
1832
  };
1806
1833
  fullPluginCtx.mcp.unregisterTool = (toolName) => {
1834
+ if (!declaredRemove.includes(toolName)) {
1835
+ process.stderr.write(` \x1b[33m[!] Plugin ${plugin.name} dynamic unregisterTool("${toolName}"): not in permissions.tools.remove — skipped.\x1b[0m\n`);
1836
+ return;
1837
+ }
1807
1838
  if (server._registeredTools?.[toolName]) {
1808
1839
  delete server._registeredTools[toolName];
1809
1840
  }
@@ -1830,38 +1861,10 @@ DO NOT use browser_scrape or browser_map for binary documents — they cannot re
1830
1861
  // Ignore if client doesn't support it or not connected yet
1831
1862
  }
1832
1863
  };
1833
- // Add the built-in ecosystem tools
1834
- server.tool("list_plugins", "List all available Weavetab plugins that can be loaded.", {}, async () => {
1835
- const installed = listInstalledPlugins();
1836
- const available = installed
1837
- .filter(info => !loadedPluginNames.has(info.packageName))
1838
- .map(info => `- ${info.packageName}: ${info.manifest?.description || "No description"}`);
1839
- if (available.length === 0) {
1840
- return { content: [{ type: "text", text: "No un-loaded plugins available." }] };
1841
- }
1842
- return { content: [{ type: "text", text: `Available plugins:\n${available.join("\n")}\n\nUse load_plugin with the plugin package name to load one.` }] };
1843
- });
1844
- server.tool("load_plugin", "Load any Weavetab plugin dynamically by npm package name (e.g. '@fy2ne/wt-youtube', 'my-plugin').", {
1845
- name: z.string().describe("The exact npm package name of the plugin to load (e.g. '@fy2ne/wt-youtube', 'my-plugin')")
1846
- }, async ({ name }) => {
1847
- if (loadedPluginNames.has(name)) {
1848
- return { content: [{ type: "text", text: `Plugin ${name} is already loaded.` }] };
1849
- }
1850
- const plugin = loadPluginByName(name);
1851
- if (!plugin) {
1852
- return { content: [{ type: "text", text: `Plugin "${name}" not found. Make sure it is installed via "weavetab plugin add ${name}".` }] };
1853
- }
1854
- await registerPluginTools(plugin);
1855
- const toolsList = (plugin.tools || []).map((t) => t.name).join(", ");
1856
- return { content: [{ type: "text", text: `loaded plugin ${name}. Tools registered: ${toolsList || "(dynamic tools registered)"}` }] };
1857
- });
1858
- // Auto-load plugins by default unless explicitly marked with lazy: true
1864
+ // Auto-load all installed plugins at startup
1859
1865
  const allPlugins = discoverPlugins();
1860
1866
  for (const plugin of allPlugins) {
1861
- const isExplicitlyLazy = plugin.manifest?.lazy === true;
1862
- if (!isExplicitlyLazy) {
1863
- await registerPluginTools(plugin);
1864
- }
1867
+ await registerPluginTools(plugin);
1865
1868
  }
1866
1869
  let transport;
1867
1870
  if (transportMode === "ws") {
@@ -10,7 +10,7 @@ const ENGINE_COVERAGE = {
10
10
  "browser_network_intercept",
11
11
  "browser_viewport",
12
12
  "browser_recording",
13
- "browser_thought",
13
+ "browser_thoughts",
14
14
  "browser_highlight",
15
15
  "browser_inspect",
16
16
  "browser_snapshot",
@@ -24,7 +24,7 @@ const ENGINE_COVERAGE = {
24
24
  "browser_network_intercept",
25
25
  "browser_viewport",
26
26
  "browser_recording",
27
- "browser_thought",
27
+ "browser_thoughts",
28
28
  "browser_highlight",
29
29
  "browser_inspect",
30
30
  "browser_snapshot",
@@ -111,7 +111,7 @@ export async function browserCaptcha(session, args, _config) {
111
111
  user_instructions: solution.userInstructions,
112
112
  next_action: isVisionAssisted
113
113
  ? "Take a screenshot to view the CAPTCHA, then use browser_click or browser_type to solve it yourself. Do not ask the user for help."
114
- : `Call browser_thought({ action: "question", message: "${solution.userInstructions}" }) to ask the user to complete it, then call browser_captcha({ action: "wait_for_user" }).`,
114
+ : `Call browser_thoughts({ action: "question", message: "${solution.userInstructions}" }) to ask the user to complete it, then call browser_captcha({ action: "wait_for_user" }).`,
115
115
  execution_ms: Date.now() - t0,
116
116
  };
117
117
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Weavetab — tools/navigate.ts
2
+ * Weavetab — tools/navigate.ts
3
3
  * URL navigation with domain blocklist enforcement and post-navigation DOM stability wait.
4
4
  * Weavetab by fy2ne
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Weavetab — tools/navigate.ts
2
+ * Weavetab — tools/navigate.ts
3
3
  * URL navigation with domain blocklist enforcement and post-navigation DOM stability wait.
4
4
  * Weavetab by fy2ne
5
5
  */
@@ -71,10 +71,10 @@ export async function weaveNavigate(session, url, config, targetId = "default",
71
71
  }
72
72
  }
73
73
  // Extension pages: open in new tab instead of navigating current tab
74
- // (chrome-extension:// pages use a different process context — Page.navigate
74
+ // (chrome-extension:// pages use a different process context — Page.navigate
75
75
  // can break the CDP session or not fire loadEventFired properly)
76
76
  if (parsed.protocol === 'chrome-extension:') {
77
- logDebug(`[weaveNavigate] Extension URL detected — opening in new tab: ${parsed.href}`);
77
+ logDebug(`[weaveNavigate] Extension URL detected — opening in new tab: ${parsed.href}`);
78
78
  const { sendToExtension } = await import("../cdp/helpers.js");
79
79
  await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: `opening extension page...` } });
80
80
  const { WeavetabOpen } = await import("./tabs.js");
@@ -98,7 +98,7 @@ export async function weaveNavigate(session, url, config, targetId = "default",
98
98
  }
99
99
  }
100
100
  catch { /* overlay install is best-effort */ }
101
- logAction("browser_go", url, `opened extension tab ${openRes.tabId}`);
101
+ logAction("browser_navigate", url, `opened extension tab ${openRes.tabId}`);
102
102
  return {
103
103
  success: true,
104
104
  url: openRes.url,
@@ -194,7 +194,7 @@ export async function weaveNavigate(session, url, config, targetId = "default",
194
194
  if (active)
195
195
  active.url = finalUrl;
196
196
  sendToExtension(session, { type: 'Weavetab_STATE', cursor: { x: 10, y: 10 }, state: 'idle', hud: { action: '' } }).catch(() => { });
197
- logAction("browser_go", url, `arrived at ${title}`);
197
+ logAction("browser_navigate", url, `arrived at ${title}`);
198
198
  // Bump the visit counter for this domain exactly once per navigation
199
199
  bumpVisitCount(parsed.hostname).catch(() => { });
200
200
  return { success: true, url: finalUrl, title, tab_id: resolvedTargetId };
@@ -17,7 +17,7 @@ function resolvePort(_config, appName) {
17
17
  }
18
18
  const port = getPort() || readLock()?.port;
19
19
  if (!port)
20
- throw new Error("[!] No active browser session. Run browser_go first.");
20
+ throw new Error("[!] No active browser session. Run browser_navigate first.");
21
21
  return port;
22
22
  }
23
23
  const IDLE_THRESHOLD_MS = 30_000;
package/dist/version.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * Weavetab Version Constant
3
3
  * Single source of truth for runtime versioning.
4
4
  */
5
- export declare const WEAVETAB_VERSION = "2.5.0-beta.4";
5
+ export declare const WEAVETAB_VERSION = "2.5.0";
package/dist/version.js CHANGED
@@ -2,4 +2,4 @@
2
2
  * Weavetab Version Constant
3
3
  * Single source of truth for runtime versioning.
4
4
  */
5
- export const WEAVETAB_VERSION = "2.5.0-beta.4";
5
+ export const WEAVETAB_VERSION = "2.5.0";
@@ -65,7 +65,7 @@ MCP Client → stdio/WebSocket → WithSecurity Pipeline → CDP WebSocket → C
65
65
 
66
66
  All tools share: Zod schema validation, `withSecurity()` pipeline (rate limiting + domain policy + RBAC), structured audit logging.
67
67
 
68
- ### Vision & Reading (7 tools)
68
+ ### Vision & Reading (8 tools)
69
69
 
70
70
  | Tool | Description |
71
71
  |------|-------------|
@@ -76,6 +76,7 @@ All tools share: Zod schema validation, `withSecurity()` pipeline (rate limiting
76
76
  | `browser_snapshot` | MHTML capture/restore of DOM state + resources |
77
77
  | `browser_screenshot` | PNG/JPEG capture via CDP, full-page + clip region, returns base64 data URL (no file pollution) |
78
78
  | `browser_console` | Read/clear buffered console logs and exceptions |
79
+ | `browser_extract_design` | Extract design tokens, typography, colors, and clean CSS with pixel fidelity |
79
80
 
80
81
  ### Navigation (1 tool)
81
82
 
@@ -116,14 +117,15 @@ All tools share: Zod schema validation, `withSecurity()` pipeline (rate limiting
116
117
  | Tool | Description |
117
118
  |------|-------------|
118
119
  | `browser_dialog` | Accept/dismiss JavaScript alert/confirm/prompt dialogs |
119
- | `browser_thought` | Green thought bubbles: feedback (one-way) or question with input (sentence/paragraph modes) |
120
+ | `browser_thoughts` | Green thought bubbles: feedback (one-way) or question with input (sentence/paragraph modes) |
120
121
  | `browser_clipboard` | System clipboard R/W (text + images) |
121
122
 
122
- ### Wait & Timing (1 tool)
123
+ ### Wait & Timing (2 tools)
123
124
 
124
125
  | Tool | Description |
125
126
  |------|-------------|
126
127
  | `browser_wait` | Wait for element/text/URL/stable/network/auto — timeout configurable, optional loop counter reset (`reset_loop: true`) |
128
+ | `browser_captcha` | Autonomous zero-API detection and solving for Cloudflare Turnstile, reCAPTCHA v2/v3, GeeTest, and hCaptcha via CDP mouse physics |
127
129
 
128
130
  ### Storage & Cookies (2 tools)
129
131
 
@@ -132,11 +134,12 @@ All tools share: Zod schema validation, `withSecurity()` pipeline (rate limiting
132
134
  | `browser_storage` | localStorage & sessionStorage get/set/clear |
133
135
  | `browser_cookies` | Cookies get/set/delete |
134
136
 
135
- ### Device & Environment (1 tool)
137
+ ### Device & Environment (2 tools)
136
138
 
137
139
  | Tool | Description |
138
140
  |------|-------------|
139
141
  | `browser_viewport` | Viewport, device emulation, geolocation, timezone override |
142
+ | `browser_emulate` | Network throttling (3G/4G/offline), CPU slowdown, geolocation, timezone, and User-Agent override |
140
143
 
141
144
  ### Network (1 tool)
142
145
 
@@ -176,12 +179,15 @@ All tools share: Zod schema validation, `withSecurity()` pipeline (rate limiting
176
179
  |------|-------------|
177
180
  | `browser_github` | Action-based repo metadata analysis, raw source file reading, issue triage, and PR inspection |
178
181
 
179
- ### Utility (2 tools)
182
+ ### Utility (5 tools)
180
183
 
181
184
  | Tool | Description |
182
185
  |------|-------------|
183
- | `browser_thoughts` | Ask user a question or show a green sticky feedback |
184
- | Plugins | Dynamic loading from any npm package with `weavetab.json` (or `@weavetab/plugin-*`) |
186
+ | `list_plugins` | Inspect installed SDK plugins, loaded tool overrides, and capability manifests |
187
+ | `load_plugin` | Dynamically load a Weavetab plugin by npm package name with isolated sandboxing |
188
+ | `browser_checkpoint` | Snapshot and restore browser state (cookies, storage, scroll/URL) into named checkpoints for time-travel and rollback |
189
+ | `browser_pattern_learn` | Read, register, and update named workflow patterns and site quirks per origin |
190
+ | `browser_office` | Parse Word (`.docx`), Excel (`.xlsx`), PDF, and CSV files into structured data |
185
191
 
186
192
  ---
187
193
 
@@ -341,10 +347,11 @@ Configure policies directly in your config file (or via `~/.Weavetab/policy.json
341
347
 
342
348
  | Role | Permissions |
343
349
  |------|-------------|
344
- | `admin` | Full access to all tools and system operations |
345
- | `operator` | Full tool access except system operations (logs, detect, performance) |
346
- | `viewer` | Read-only access to observation tools only |
347
- | `automation` | Restricted to burst/plan/wait/map for automated workflows |
350
+ | `admin` | Full access to all tools |
351
+ | `operator` | Full access to all tools (standard agent role) |
352
+ | `viewer` | Read-only observation tools only (`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`, `browser_github`) |
353
+ | `automation` | Full access except `browser_thoughts` (non-interactive CI/CD mode) |
354
+ | `system` | Internal tools only (`browser_thoughts`, `browser_detect`, `browser_performance`, `browser_checkpoint`, `browser_pattern_learn`) |
348
355
 
349
356
  ### Setting Role
350
357
 
package/docs/PLUGINS.md CHANGED
@@ -1,6 +1,6 @@
1
- # Weavetab Plugin System & SDK Specification
1
+ # Weavetab Plugin System & SDK Specification
2
2
 
3
- > **Version:** 2.5.0-beta.2 | **SDK:** @weavetab/sdk | **License:** AGPL-3.0-only
3
+ > **Version:** 2.5.0 | **SDK:** @weavetab/sdk (MIT) | **Runtime:** AGPL-3.0-only
4
4
 
5
5
  The **Weavetab Plugin System** allows developers to extend the MCP runtime with custom tools, visual HUD overlays, thought bubbles, lifecycle hooks, and tool overrides.
6
6
 
@@ -9,14 +9,14 @@ The **Weavetab Plugin System** allows developers to extend the MCP runtime with
9
9
  ## 1. Quick Start
10
10
 
11
11
  ### Scaffold a New Plugin
12
- `ash
12
+ ````bash
13
13
  npx @weavetab/sdk init my-custom-plugin
14
14
  cd my-custom-plugin
15
15
  npm install
16
16
  `
17
17
 
18
18
  ### Build & Compile
19
- `ash
19
+ ````bash
20
20
  npm run build
21
21
  `
22
22
 
@@ -104,7 +104,7 @@ Plugins require a weavetab.json manifest in their package root:
104
104
 
105
105
  ## 5. Installing Plugins
106
106
 
107
- `ash
107
+ ```bash
108
108
  # Install an official plugin
109
109
  weavetab plugin add @weavetab/plugin-youtube
110
110