@weavetab/mcp 2.5.0-beta.4 → 2.5.0-beta.5

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 (43) hide show
  1. package/LICENSE +216 -631
  2. package/README.md +153 -271
  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.js +36 -33
  17. package/dist/tools/browser_detect.js +2 -2
  18. package/dist/tools/captcha.js +1 -1
  19. package/dist/tools/navigate.d.ts +1 -1
  20. package/dist/tools/navigate.js +5 -5
  21. package/dist/tools/tabs.js +1 -1
  22. package/dist/version.d.ts +1 -1
  23. package/dist/version.js +1 -1
  24. package/docs/DOCUMENTATION.md +18 -11
  25. package/docs/PLUGINS.md +5 -5
  26. package/docs/TOOLS.md +54 -56
  27. package/docs/architecture/overview.md +1 -1
  28. package/docs/architecture/security.md +8 -7
  29. package/docs/architecture/transport.md +2 -2
  30. package/docs/cli/cli-commands.md +7 -1
  31. package/docs/cli/cli-flags.md +15 -7
  32. package/docs/cli/cli-scripting.md +6 -3
  33. package/docs/configuration/config-locations.md +3 -4
  34. package/docs/configuration/env-variables.md +4 -4
  35. package/docs/desktop-automation/electron-apps.md +3 -6
  36. package/docs/get-started/introduction.md +4 -4
  37. package/docs/get-started/setup-wizard.md +7 -6
  38. package/docs/tools/tools.md +1 -1
  39. package/docs/troubleshooting/trouble-browser.md +1 -1
  40. package/icon.png +0 -0
  41. package/mcp.json +2 -20
  42. package/package.json +118 -118
  43. package/server.json +4 -4
package/README.md CHANGED
@@ -1,271 +1,153 @@
1
- <div align="center">
2
-
3
- <img src="icon.png" width="96" alt="Weavetab" /><br /><br />
4
-
5
- <h1>Weavetab MCP</h1>
6
-
7
- <p>A local <a href="https://modelcontextprotocol.io">Model Context Protocol</a> server that gives AI agents<br />direct, physical control over browsers and desktop applications via the Chrome DevTools Protocol.</p>
8
-
9
- <p>
10
- <a href="https://github.com/weavetab/mcp/releases"><img src="https://img.shields.io/github/v/release/weavetab/mcp?label=version&style=flat-square&color=3b82f6" alt="version" /></a>
11
- <a href="LICENSE"><img src="https://img.shields.io/badge/license-AGPL--3.0-8b5cf6?style=flat-square" alt="license" /></a>
12
- <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-compatible-10b981?style=flat-square" alt="MCP" /></a>
13
- <img src="https://img.shields.io/badge/Node.js-%3E%3D18-green?style=flat-square" alt="Node" />
14
- <a href="docs/tools/tools.md"><img src="https://img.shields.io/badge/tools-44-ec4899?style=flat-square" alt="tools" /></a>
15
- <a href="https://github.com/sponsors/fy2ne"><img src="https://img.shields.io/badge/sponsor-fy2ne-ea4aaa?style=flat-square&logo=github-sponsors" alt="sponsor" /></a>
16
- </p>
17
-
18
- <p>
19
- <a href="docs/tools/tools.md">Tool Reference</a> &nbsp;&middot;&nbsp;
20
- <a href="docs/DOCUMENTATION.md">Documentation</a> &nbsp;&middot;&nbsp;
21
- <a href="ARCHITECTURE.md">Architecture</a> &nbsp;&middot;&nbsp;
22
- <a href="SUPPORT.md">Support</a> &nbsp;&middot;&nbsp;
23
- <a href="SECURITY.md">Security</a>
24
- </p>
25
-
26
- </div>
27
-
28
- ---
29
-
30
- Weavetab is a local, production-grade MCP server that connects AI agents directly to Chromium browsers and Electron desktop applications through the **Chrome DevTools Protocol (CDP) WebSocket at Layer 0** — no WebDriver binaries, no Playwright processes, no cloud relay.
31
-
32
- Tool invocations resolve in **3–8 ms of mean latency**. DOM state is compressed into agent-readable semantic maps and diffed across turns, cutting prompt token usage by **up to 95%**. Security is enforced locally via per-domain policies, 4-tier RBAC, and cryptographic blind credential injection.
33
-
34
- Built by [fy2ne](https://github.com/fy2ne).
35
-
36
- ---
37
-
38
- ## Why not Playwright or Puppeteer?
39
-
40
- > The standard approach — Playwright over MCP — works. But it carries fundamental architectural costs that compound at scale.
41
-
42
- | | Playwright / Puppeteer | **Weavetab** |
43
- |---|---|---|
44
- | **Protocol layer** | WebDriver BiDi or CDP via browser binary | Raw CDP WebSocket, Layer 0 |
45
- | **Tool execution latency** | 50–300 ms (binary + IPC overhead) | **3–8 ms** |
46
- | **Desktop app control** | Browser only | Chrome + any Electron app (Discord, VS Code, Figma…) |
47
- | **DOM representation** | Full HTML dump to LLM | Semantic Map 3.0 with delta diffing |
48
- | **Token cost per map** | ~2,000–8,000 tokens | **~80–400 tokens** (95% reduction via delta) |
49
- | **CAPTCHA** | API service required | **Zero-API** via CDP mouse physics |
50
- | **Credential safety** | Secrets passed in plaintext | **Blind Injection** — never in LLM context |
51
- | **Extension access** | Limited | Full Chrome Extension API surface |
52
-
53
- ---
54
-
55
- ## Architecture
56
-
57
- ```
58
- MCP Client (Claude / Cursor / Antigravity / Copilot / Windsurf)
59
- |
60
- | JSON-RPC 2.0 (stdio or WebSocket :3000)
61
- v
62
- +-------------------------------------------------------------+
63
- | Weavetab MCP Server |
64
- | |
65
- | +-----------------+ +---------------------------------+ |
66
- | | Security Layer |-->| Intelligence Engine | |
67
- | | Rate · RBAC | | Map 3.0 · Delta · Memory | |
68
- | | Policy · Audit | | Pruner · Framework Detector | |
69
- | +-----------------+ +----------------+----------------+ |
70
- | | |
71
- | +----------v----------------+ |
72
- | | CDP Bridge | |
73
- | | Ghost Cursor · Typing | |
74
- | | App Router · Loop Guard | |
75
- | +----------+----------------+ |
76
- +-------------------------------------------------------------+
77
- | CDP WebSocket (127.0.0.1)
78
- +---------------------------+-----------------------------+
79
- v v v
80
- Chrome / Edge VS Code / Discord Your Electron App
81
- (any tab) (Electron apps) (local dev)
82
- ```
83
-
84
- No cloud. No external process. The CDP WebSocket connection is strictly bound to `127.0.0.1`.
85
-
86
- ---
87
-
88
- ## Quick Start
89
-
90
- ```bash
91
- npx -y @weavetab/mcp
92
- ```
93
-
94
- Add to your MCP client config:
95
-
96
- ```json
97
- {
98
- "mcpServers": {
99
- "weavetab": {
100
- "command": "npx",
101
- "args": ["-y", "@weavetab/mcp"]
102
- }
103
- }
104
- }
105
- ```
106
-
107
- | Client | Config path |
108
- |---|---|
109
- | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` |
110
- | Claude Code | `claude mcp add weavetab -- npx -y @weavetab/mcp` |
111
- | Cursor | `.cursor/mcp.json` |
112
- | Antigravity | `~/.gemini/antigravity/mcp/weavetab/` |
113
- | VS Code Copilot | `.vscode/mcp.json` |
114
- | Windsurf | `~/.codeium/windsurf/mcp_config.json` |
115
-
116
- ---
117
-
118
- ## Core Technology
119
-
120
- ### Semantic Map 3.0
121
-
122
- The primary bottleneck in browser-based agents is DOM representation. Full HTML dumps are noisy, large, and structurally meaningless to language models. Weavetab's `browser_map` runs an in-browser CDP walker that produces a compact semantic map:
123
-
124
- ```
125
- ╔═ ACTIVE DIALOG: "Confirm Payment" ═╗
126
- [w:3] button "Confirm" [w:4] button "Cancel"
127
-
128
- ┌ "Pro Plan — $49/mo"
129
- [w:12] button "Upgrade" [w:13] button "View Details"
130
- [w:14] input val:"john@example.com" ph:"Email address" FOCUSED
131
-
132
- [w:20...w:38] 18 nav links: "Dashboard" ... "Sign out"
133
- ```
134
-
135
- Key properties:
136
-
137
- - **Ref IDs** (`w:N`) are stable within a session and used by all interaction tools
138
- - **Container binding** — buttons and inputs are grouped under their parent card, row, or section header
139
- - **Modal hoisting** — open dialogs are isolated at the top so agents cannot miss them
140
- - **Link collapsing** — repeated nav/footer runs of 4+ links compress into a single token line
141
- - **Semantic delta** — on re-reads, only changed nodes are transmitted (`~` modified, `+` added, `−` removed), cutting re-read cost by up to 95%
142
-
143
- ### Ghost Cursor
144
-
145
- Clicks are not dispatched as synthetic DOM events. They are delivered as a physical sequence of `Input.dispatchMouseEvent` CDP calls along a Bezier curve trajectory with randomized acceleration, natural jitter, and hold durations. Anti-bot fingerprinting that flags event-based automation does not trigger.
146
-
147
- `browser_pointer` supports `hover_chain` for multi-level menu traversal in a single tool call:
148
-
149
- ```json
150
- {
151
- "hover_chain": ["File", "Export As", "PNG"],
152
- "click_final": true
153
- }
154
- ```
155
-
156
- ### Blind Credential Injection
157
-
158
- `browser_type_secret` resolves secrets server-side from `~/.weavetab/secrets.json`. The resolved value is never included in the MCP response, never appears in DOM snapshots, and is permanently masked as `[REDACTED]` in all audit logs. The LLM context never sees the credential.
159
-
160
- ```bash
161
- weavetab secrets set GITHUB_TOKEN
162
- # Stored. Never echoed, never logged, never surfaced to the agent.
163
- ```
164
-
165
- ### Electron & Desktop App Automation
166
-
167
- Any tool accepts an optional `app` parameter that routes the command to a registered Electron process instead of a browser tab:
168
-
169
- ```json
170
- { "app": "discord", "ref": "w:24", "text": "Hey, deploying now" }
171
- ```
172
-
173
- Register apps in `~/.weavetab/config.json`:
174
-
175
- ```json
176
- {
177
- "electron": {
178
- "discord": "C:\\Users\\<user>\\AppData\\Local\\Discord\\Discord.exe",
179
- "vscode": "C:\\Users\\<user>\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe",
180
- "figma": "C:\\Users\\<user>\\AppData\\Local\\Figma\\Figma.exe"
181
- }
182
- }
183
- ```
184
-
185
- Weavetab auto-attaches via CDP to the registered process. No code changes required in the target application.
186
-
187
- ### Zero-API CAPTCHA Solving
188
-
189
- `browser_captcha` solves Cloudflare Turnstile, reCAPTCHA v2/v3, GeeTest, and hCaptcha by orchestrating CDP mouse events that replicate human behavioral signatures — velocity curves, micro-pauses, scroll inertia — with no third-party API, no external service, and no API key required.
190
-
191
- ---
192
-
193
- ## Tool Suite
194
-
195
- 44 tools across 9 categories. Full parameter reference: [`docs/tools/tools.md`](docs/tools/tools.md)
196
-
197
- | Category | Tools |
198
- |---|---|
199
- | **Vision & DOM** | `browser_map` `browser_find` `browser_scrape` `browser_inspect` `browser_snapshot` `browser_screenshot` `browser_console` `browser_extract_design` |
200
- | **Input & Interaction** | `browser_click` `browser_type` `browser_type_secret` `browser_key` `browser_pointer` `browser_select` `browser_fill` `browser_upload` `browser_scroll` |
201
- | **Navigation & Environment** | `browser_navigate` `browser_tabs` `browser_detect` `browser_viewport` `browser_emulate` |
202
- | **Mission & Automation** | `browser_burst` `browser_automation` `browser_recording` `browser_pattern_learn` |
203
- | **Safety & Human Oversight** | `browser_dialog` `browser_thoughts` `browser_clipboard` `browser_wait` |
204
- | **Autonomous CAPTCHA** | `browser_captcha` |
205
- | **Network & Storage** | `browser_network_intercept` `browser_storage` `browser_cookies` `browser_checkpoint` `browser_performance` |
206
- | **GitHub & Documents** | `browser_github` `browser_office` |
207
- | **Canvas, Eval & Plugins** | `browser_eval` `browser_pdf` `browser_canvas` `browser_highlight` `list_plugins` `load_plugin` |
208
-
209
- ---
210
-
211
- ## Security
212
-
213
- - **Transport**: CDP WebSocket strictly bound to `127.0.0.1`. No listener on external interfaces.
214
- - **Policy Engine**: Per-domain `allow` / `read-only` / `deny` / `audit` rules in `~/.weavetab/policy.json`
215
- - **RBAC**: Four roles — `admin`, `operator`, `viewer`, `automation` — enforce tool-level permission gates
216
- - **Blind Injection**: Credentials resolved server-side, masked in all outputs and audit logs
217
- - **Loop Breaker**: Strike-based stuck-loop detector halts runaway autonomous sequences
218
- - **Audit Log**: All tool invocations, policy decisions, and errors written to `~/.weavetab/logs/audit.log`
219
-
220
- Security vulnerabilities: `security@fy2ne.me` (do not use public issues).
221
-
222
- ---
223
-
224
- ## CLI Reference
225
-
226
- ```bash
227
- weavetab # Start in stdio mode (default)
228
- weavetab --ws --port 3000 # WebSocket daemon mode
229
- weavetab config # Interactive configuration wizard
230
- weavetab secrets set KEY_NAME # Store a secret (never echoed)
231
- weavetab secrets list # List stored secret keys (not values)
232
- weavetab plugin add <name> # Install a plugin from npm
233
- weavetab plugin list # List installed plugins
234
- ```
235
-
236
- Configuration file: `~/.weavetab/config.json`
237
-
238
- ```json
239
- {
240
- "browserType": "chromium",
241
- "headless": false,
242
- "extension": true,
243
- "maxActionsPerMinute": 60,
244
- "role": "admin",
245
- "electron": {
246
- "discord": "...",
247
- "vscode": "...",
248
- "antigravity": "..."
249
- }
250
- }
251
- ```
252
-
253
- ---
254
-
255
- ## Extensibility
256
-
257
- Weavetab exposes a typed Plugin SDK for authoring custom MCP tools, HUD overlays, and tool lifecycle hooks:
258
-
259
- ```bash
260
- npx @weavetab/sdk init my-plugin
261
- ```
262
-
263
- See [`docs/PLUGINS.md`](docs/PLUGINS.md) for architecture, manifest spec, and SDK API reference.
264
-
265
- ---
266
-
267
- ## License
268
-
269
- AGPL-3.0-only. Client connections over stdio or WebSocket do not extend copyleft obligations to your client application or models.
270
-
271
- [Sponsor development](https://github.com/sponsors/fy2ne) &nbsp;&middot;&nbsp; [Discussions](https://github.com/weavetab/mcp/discussions) &nbsp;&middot;&nbsp; [SUPPORT.md](SUPPORT.md)
1
+ # MCP Registry
2
+
3
+ The MCP registry provides MCP clients with a list of MCP servers, like an app store for MCP servers.
4
+
5
+ [**📤 Publish my MCP server**](docs/modelcontextprotocol-io/quickstart.mdx) | [**⚡️ Live API docs**](https://registry.modelcontextprotocol.io/docs) | [**👀 Ecosystem vision**](docs/design/ecosystem-vision.md) | 📖 **[Full documentation](./docs)**
6
+
7
+ ## Development Status
8
+
9
+ **2025-10-24 update**: The Registry API has entered an **API freeze (v0.1)** 🎉. For the next month or more, the API will remain stable with no breaking changes, allowing integrators to confidently implement support. This freeze applies to v0.1 while development continues on v0. We'll use this period to validate the API in real-world integrations and gather feedback to shape v1 for general availability. Thank you to everyone for your contributions and patience—your involvement has been key to getting us here!
10
+
11
+ **2025-09-08 update**: The registry has launched in preview 🎉 ([announcement blog post](https://blog.modelcontextprotocol.io/posts/2025-09-08-mcp-registry-preview/)). While the system is now more stable, this is still a preview release and breaking changes or data resets may occur. A general availability (GA) release will follow later. We'd love your feedback in [GitHub discussions](https://github.com/modelcontextprotocol/registry/discussions/new?category=ideas) or in the [#registry-dev Discord](https://discord.com/channels/1358869848138059966/1369487942862504016) ([joining details here](https://modelcontextprotocol.io/community/communication)).
12
+
13
+ Registry Working Group:
14
+ - **Radoslav (Rado) Dimitrov** (Stacklok) [@rdimitrov](https://github.com/rdimitrov) - WG Lead
15
+ - **Tadas Antanavicius** (PulseMCP) [@tadasant](https://github.com/tadasant)
16
+ - **Bob Dickinson** (TeamSpark) [@BobDickinson](https://github.com/BobDickinson)
17
+ - **Preeti (Pree) Dewani** (Ravenmail) [@pree-dew](https://github.com/pree-dew)
18
+
19
+ ## Contributing
20
+
21
+ We use multiple channels for collaboration - see [modelcontextprotocol.io/community/communication](https://modelcontextprotocol.io/community/communication).
22
+
23
+ Often (but not always) ideas flow through this pipeline:
24
+
25
+ - **[Discord](https://modelcontextprotocol.io/community/communication)** - Real-time community discussions
26
+ - **[Discussions](https://github.com/modelcontextprotocol/registry/discussions)** - Propose and discuss product/technical requirements
27
+ - **[Issues](https://github.com/modelcontextprotocol/registry/issues)** - Track well-scoped technical work
28
+ - **[Pull Requests](https://github.com/modelcontextprotocol/registry/pulls)** - Contribute work towards issues
29
+
30
+ ### Quick start:
31
+
32
+ #### Pre-requisites
33
+
34
+ - **Docker**
35
+ - **Go 1.24.x**
36
+ - **ko** - Container image builder for Go ([installation instructions](https://ko.build/install/))
37
+ - **golangci-lint v2.4.0**
38
+
39
+ #### Running the server
40
+
41
+ ```bash
42
+ # Start full development environment
43
+ make dev-compose
44
+ ```
45
+
46
+ This starts the registry at [`localhost:8080`](http://localhost:8080) with PostgreSQL. The database uses ephemeral storage and is reset each time you restart the containers, ensuring a clean state for development and testing.
47
+
48
+ **Note:** The registry uses [ko](https://ko.build) to build container images. The `make dev-compose` command automatically builds the registry image with ko and loads it into your local Docker daemon before starting the services.
49
+
50
+ By default, the registry seeds from the production API with a filtered subset of servers (to keep startup fast). This ensures your local environment mirrors production behavior and all seed data passes validation. For offline development you can seed from a file without validation with `MCP_REGISTRY_SEED_FROM=data/seed.json MCP_REGISTRY_ENABLE_REGISTRY_VALIDATION=false make dev-compose`.
51
+
52
+ The setup can be configured with environment variables in [docker-compose.yml](./docker-compose.yml) - see [.env.example](./.env.example) for a reference.
53
+
54
+ <details>
55
+ <summary>Alternative: Running a pre-built Docker image</summary>
56
+
57
+ Pre-built Docker images are automatically published to GitHub Container Registry. Note that the image does not bundle PostgreSQL, so you need to run your own and point the registry at it via `MCP_REGISTRY_DATABASE_URL` (see [docker-compose.yml](./docker-compose.yml) for a working example):
58
+
59
+ ```bash
60
+ # Run latest stable release
61
+ docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:latest
62
+
63
+ # Run latest from main branch (continuous deployment)
64
+ docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:main
65
+
66
+ # Run specific release version
67
+ docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:v1.0.0
68
+
69
+ # Run development build from main branch
70
+ docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:main-20250906-abc123d
71
+ ```
72
+
73
+ **Available tags:**
74
+ - **Releases**: `latest`, `v1.0.0`, `v1.1.0`, etc.
75
+ - **Continuous**: `main` (latest main branch build)
76
+ - **Development**: `main-<date>-<sha>` (specific commit builds)
77
+
78
+ </details>
79
+
80
+ #### Publishing a server
81
+
82
+ To publish a server, we've built a simple CLI. You can use it with:
83
+
84
+ ```bash
85
+ # Build the latest CLI
86
+ make publisher
87
+
88
+ # Use it!
89
+ ./bin/mcp-publisher --help
90
+ ```
91
+
92
+ See [the publisher guide](./docs/modelcontextprotocol-io/quickstart.mdx) for more details.
93
+
94
+ #### Other commands
95
+
96
+ ```bash
97
+ # Run lint, unit tests and integration tests
98
+ make check
99
+ ```
100
+
101
+ There are also a few more helpful commands for development. Run `make help` to learn more, or look in [Makefile](./Makefile).
102
+
103
+ <!--
104
+ For Claude and other AI tools: Always prefer make targets over custom commands where possible.
105
+ -->
106
+
107
+ ## Architecture
108
+
109
+ ### Project Structure
110
+
111
+ ```
112
+ ├── cmd/ # Application entry points
113
+ │ └── publisher/ # Server publishing tool
114
+ ├── data/ # Seed data
115
+ ├── deploy/ # Deployment configuration (Pulumi)
116
+ ├── docs/ # Documentation
117
+ ├── internal/ # Private application code
118
+ │ ├── api/ # HTTP handlers and routing
119
+ │ ├── auth/ # Authentication (GitHub OAuth, JWT, namespace blocking)
120
+ │ ├── config/ # Configuration management
121
+ │ ├── database/ # Data persistence (PostgreSQL)
122
+ │ ├── service/ # Business logic
123
+ │ ├── telemetry/ # Metrics and monitoring
124
+ │ └── validators/ # Input validation
125
+ ├── pkg/ # Public packages
126
+ │ ├── api/ # API types and structures
127
+ │ │ └── v0/ # Version 0 API types
128
+ │ └── model/ # Data models for server.json
129
+ ├── scripts/ # Development and testing scripts
130
+ ├── tests/ # Integration tests
131
+ └── tools/ # CLI tools and utilities
132
+ └── validate-*.sh # Schema validation tools
133
+ ```
134
+
135
+ ### Authentication
136
+
137
+ Publishing supports multiple authentication methods:
138
+ - **GitHub OAuth** - For publishing by logging into GitHub
139
+ - **GitHub OIDC** - For publishing from GitHub Actions
140
+ - **DNS verification** - For proving ownership of a domain and its subdomains
141
+ - **HTTP verification** - For proving ownership of a domain
142
+
143
+ The registry validates namespace ownership when publishing. E.g. to publish...:
144
+ - `io.github.domdomegg/my-cool-mcp` you must login to GitHub as `domdomegg`, or be in a GitHub Action on domdomegg's repos
145
+ - `me.adamjones/my-cool-mcp` you must prove ownership of `adamjones.me` via DNS or HTTP challenge
146
+
147
+ ## Community Projects
148
+
149
+ Check out [community projects](docs/community-projects.md) to explore notable registry-related work created by the community.
150
+
151
+ ## More documentation
152
+
153
+ See the [documentation](./docs) for more details if your question has not been answered here!
@@ -614,7 +614,7 @@ export async function getTabList(agentId, targetApp) {
614
614
  }
615
615
  }
616
616
  if (!port)
617
- throw new Error(`[!] No active ${targetApp ? targetApp : "browser"} session. Run browser_go or launch app first.`);
617
+ throw new Error(`[!] No active ${targetApp ? targetApp : "browser"} session. Run browser_navigate or launch app first.`);
618
618
  try {
619
619
  const metaSession = await withCDPTimeout(CDP({ host: "127.0.0.1", port }), 5000, "getTabList CDP");
620
620
  const { targetInfos } = await withCDPTimeout(metaSession.Target.getTargets(), 5000, "getTabList getTargets");
@@ -671,7 +671,7 @@ export async function connectToTab(targetId, port, agentId, browserContextId, ap
671
671
  const lock = readLock(appName);
672
672
  const p = port || (appName ? lock?.port : (actualPort || lock?.port));
673
673
  if (!p)
674
- throw new Error(`[!] No active ${appName || "browser"} session. Run browser_go first.`);
674
+ throw new Error(`[!] No active ${appName || "browser"} session. Run browser_navigate first.`);
675
675
  let t = null;
676
676
  try {
677
677
  const targets = await withCDPTimeout(CDP.List({ host: "127.0.0.1", port: p }), 3000, "connectToTab CDP.List");
@@ -1,4 +1,5 @@
1
1
  export declare function runPluginAdd(inputName: string, args?: string[]): Promise<void>;
2
+ export declare function runPluginAddLocal(inputPath: string): Promise<void>;
2
3
  export declare function runPluginList(): Promise<void>;
3
4
  export declare function runPluginSearch(query?: string): Promise<void>;
4
5
  export declare function runPluginBrowse(): Promise<void>;
@@ -2,6 +2,7 @@ import * as p from "@clack/prompts";
2
2
  import pc from "picocolors";
3
3
  import boxen from "boxen";
4
4
  import ora from "ora";
5
+ import fs from "node:fs";
5
6
  import path from "node:path";
6
7
  import { spawn } from "node:child_process";
7
8
  import { createRequire } from "node:module";
@@ -24,7 +25,7 @@ function runCommand(cmd, cmdArgs, cwd = process.cwd()) {
24
25
  const executable = isWin && !cmd.endsWith(".cmd") && !cmd.endsWith(".exe") ? `${cmd}.cmd` : cmd;
25
26
  const child = spawn(executable, cmdArgs, {
26
27
  cwd,
27
- shell: false,
28
+ shell: isWin,
28
29
  stdio: ["ignore", "pipe", "pipe"],
29
30
  });
30
31
  let stdout = "";
@@ -47,8 +48,20 @@ function runNpmCommand(cmdArgs, cwd = process.cwd()) {
47
48
  // ---------------------------------------------------------------------------
48
49
  export async function runPluginAdd(inputName, args = []) {
49
50
  if (!inputName || inputName.startsWith("-")) {
50
- console.log(pc.red("[ERROR] Please provide a package name to install."));
51
- console.log(pc.dim("Usage: weavetab plugin add <packageName> [--official | --community] [--yes]"));
51
+ console.log(pc.red("[ERROR] Please provide a package name or local path to install."));
52
+ console.log(pc.dim("Usage: weavetab plugin add <packageName|./local-path> [--official | --community] [--yes]"));
53
+ return;
54
+ }
55
+ // Local path install: `weavetab plugin add ./my-plugin` (npm installs the directory).
56
+ if (inputName === "." ||
57
+ inputName.startsWith("./") ||
58
+ inputName.startsWith("../") ||
59
+ inputName.startsWith("file:") ||
60
+ inputName.startsWith("/") ||
61
+ inputName.startsWith("\\") ||
62
+ /^[a-zA-Z]:[\\/]/.test(inputName) ||
63
+ inputName.startsWith("~/")) {
64
+ await runPluginAddLocal(inputName);
52
65
  return;
53
66
  }
54
67
  const isForce = args.includes("--yes") || args.includes("-y") || args.includes("--force");
@@ -223,6 +236,49 @@ export async function runPluginAdd(inputName, args = []) {
223
236
  }
224
237
  }
225
238
  // ---------------------------------------------------------------------------
239
+ // 1b. ADD FROM LOCAL PATH (`weavetab plugin add ./my-plugin`)
240
+ // ---------------------------------------------------------------------------
241
+ export async function runPluginAddLocal(inputPath) {
242
+ let dir = inputPath;
243
+ if (dir.startsWith("file:"))
244
+ dir = dir.slice("file:".length);
245
+ if (dir.startsWith("~/"))
246
+ dir = path.join(process.env.HOME || process.env.USERPROFILE || "", dir.slice(2));
247
+ const absDir = path.resolve(process.cwd(), dir);
248
+ if (!fs.existsSync(absDir) || !fs.statSync(absDir).isDirectory()) {
249
+ console.log(pc.red(`[ERROR] Local path not found: ${inputPath}`));
250
+ return;
251
+ }
252
+ const local = inspectInstalledPackage(absDir);
253
+ if (!local) {
254
+ console.log(pc.red(`[ERROR] No valid weavetab.json manifest in ${absDir}`));
255
+ console.log(pc.dim("Scaffold one with: npx @weavetab/sdk init <name>"));
256
+ return;
257
+ }
258
+ const spinner = ora(`Installing local plugin ${pc.cyan(local.packageName)} from ${pc.dim(absDir)} via npm...`).start();
259
+ const installResult = await runNpmCommand(["install", absDir]);
260
+ if (installResult.code !== 0) {
261
+ spinner.fail(pc.red(`Failed to install ${local.packageName}`));
262
+ if (installResult.stderr)
263
+ console.error(pc.dim(installResult.stderr));
264
+ return;
265
+ }
266
+ spinner.succeed(pc.green(`installed ${pc.bold(local.packageName)} (local path)`));
267
+ const targetDir = path.join(process.cwd(), "node_modules", local.packageName);
268
+ const installedInfo = fs.existsSync(targetDir) ? inspectInstalledPackage(targetDir) : local;
269
+ const manifest = (installedInfo?.manifest || local.manifest);
270
+ const tools = manifest.tools || [];
271
+ console.log();
272
+ console.log(boxen(`${pc.bold("Plugin Details:")}\n` +
273
+ ` ${pc.dim("Name:")} ${manifest.name || local.packageName}\n` +
274
+ ` ${pc.dim("Version:")} ${manifest.version || "1.0.0"}\n` +
275
+ ` ${pc.dim("Source:")} ${absDir} (local path)\n` +
276
+ ` ${pc.dim("Description:")} ${manifest.description || "No description provided"}` +
277
+ (tools.length > 0 ? `\n\n${pc.bold(`Exposed Tools (${tools.length}):`)}` + tools.map((t) => `\n • ${pc.cyan(t.name)}: ${pc.dim(t.description || "")}`).join("") : ""), { padding: 1, borderStyle: "round", borderColor: "green" }));
278
+ console.log();
279
+ console.log(pc.green("✔ Plugin is ready to use! Restart Weavetab MCP server to activate changes.\n"));
280
+ }
281
+ // ---------------------------------------------------------------------------
226
282
  // 2. LIST INSTALLED PLUGINS
227
283
  // ---------------------------------------------------------------------------
228
284
  export async function runPluginList() {
@@ -434,7 +490,7 @@ export async function runPluginCli(args) {
434
490
  console.log(` weavetab plugin <command> [options]`);
435
491
  console.log();
436
492
  console.log(pc.bold("COMMANDS"));
437
- console.log(` ${pc.cyan("add <pkg>")} Install any plugin by npm package name (e.g. @fy2ne/wt-youtube, my-plugin)`);
493
+ console.log(` ${pc.cyan("add <pkg|path>")} Install a plugin by npm name or local path (e.g. @fy2ne/wt-youtube, ./my-plugin)`);
438
494
  console.log(` ${pc.cyan("browse")} Interactive terminal explorer to browse & 1-click install`);
439
495
  console.log(` ${pc.cyan("search [query]")} Search plugins across official registry & npm`);
440
496
  console.log(` ${pc.cyan("list")} List all installed plugins and their registered tools`);
@@ -1,6 +1,6 @@
1
1
  export type DevModeConfig = boolean;
2
2
  export type PolicyMode = "allow" | "deny" | "audit-only" | "read-only";
3
- export type Role = "admin" | "viewer" | "automation" | "system";
3
+ export type Role = "admin" | "operator" | "viewer" | "automation" | "system";
4
4
  export type BrowserEngine = "chromium" | "firefox" | "webkit";
5
5
  export interface PolicyEntry {
6
6
  pattern: string;
@@ -153,7 +153,7 @@ export function validateConfigInternal(raw) {
153
153
  const d = DEFAULT_CONFIG;
154
154
  const VALID_MODES = ["session", "attach"];
155
155
  const VALID_ENGINES = ["chromium", "firefox", "webkit"];
156
- const VALID_ROLES = ["admin", "viewer", "automation", "system"];
156
+ const VALID_ROLES = ["admin", "operator", "viewer", "automation", "system"];
157
157
  const mode = validateField("mode", raw.mode, isStr, d.mode, v => VALID_MODES.includes(v));
158
158
  // Path validation helpers
159
159
  function safePathField(field, value) {