@opentabs-dev/mcp-server 0.0.67 → 0.0.69
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/dist/adapter-files.d.ts.map +1 -1
- package/dist/adapter-files.js +8 -0
- package/dist/adapter-files.js.map +1 -1
- package/dist/browser-tools/extension-get-logs.d.ts +2 -2
- package/dist/browser-tools/get-console-logs.d.ts +2 -2
- package/package.json +1 -1
- package/dist/browser-tool-names.d.ts +0 -14
- package/dist/browser-tool-names.d.ts.map +0 -1
- package/dist/browser-tool-names.js +0 -55
- package/dist/browser-tool-names.js.map +0 -1
- package/dist/discovery-legacy.d.ts +0 -32
- package/dist/discovery-legacy.d.ts.map +0 -1
- package/dist/discovery-legacy.js +0 -415
- package/dist/discovery-legacy.js.map +0 -1
- package/dist/manifest-schema.d.ts +0 -14
- package/dist/manifest-schema.d.ts.map +0 -1
- package/dist/manifest-schema.js +0 -51
- package/dist/manifest-schema.js.map +0 -1
- package/dist/mcp-prompts.d.ts +0 -71
- package/dist/mcp-prompts.d.ts.map +0 -1
- package/dist/mcp-prompts.js +0 -248
- package/dist/mcp-prompts.js.map +0 -1
- package/dist/mcp-resources.d.ts +0 -53
- package/dist/mcp-resources.d.ts.map +0 -1
- package/dist/mcp-resources.js +0 -139
- package/dist/mcp-resources.js.map +0 -1
- package/dist/permissions.d.ts +0 -59
- package/dist/permissions.d.ts.map +0 -1
- package/dist/permissions.js +0 -144
- package/dist/permissions.js.map +0 -1
- package/dist/prompts/audit-ai-docs.d.ts +0 -6
- package/dist/prompts/audit-ai-docs.d.ts.map +0 -1
- package/dist/prompts/audit-ai-docs.js +0 -155
- package/dist/prompts/audit-ai-docs.js.map +0 -1
- package/dist/prompts/build-plugin.d.ts +0 -3
- package/dist/prompts/build-plugin.d.ts.map +0 -1
- package/dist/prompts/build-plugin.js +0 -455
- package/dist/prompts/build-plugin.js.map +0 -1
- package/dist/prompts/contribute-learnings.d.ts +0 -12
- package/dist/prompts/contribute-learnings.d.ts.map +0 -1
- package/dist/prompts/contribute-learnings.js +0 -107
- package/dist/prompts/contribute-learnings.js.map +0 -1
- package/dist/prompts/plugin-icon.d.ts +0 -5
- package/dist/prompts/plugin-icon.d.ts.map +0 -1
- package/dist/prompts/plugin-icon.js +0 -147
- package/dist/prompts/plugin-icon.js.map +0 -1
- package/dist/prompts/setup-plugin.d.ts +0 -3
- package/dist/prompts/setup-plugin.d.ts.map +0 -1
- package/dist/prompts/setup-plugin.js +0 -197
- package/dist/prompts/setup-plugin.js.map +0 -1
- package/dist/prompts/troubleshoot.d.ts +0 -3
- package/dist/prompts/troubleshoot.d.ts.map +0 -1
- package/dist/prompts/troubleshoot.js +0 -191
- package/dist/prompts/troubleshoot.js.map +0 -1
- package/dist/resources/browser-tools.d.ts +0 -3
- package/dist/resources/browser-tools.d.ts.map +0 -1
- package/dist/resources/browser-tools.js +0 -100
- package/dist/resources/browser-tools.js.map +0 -1
- package/dist/resources/cli.d.ts +0 -3
- package/dist/resources/cli.d.ts.map +0 -1
- package/dist/resources/cli.js +0 -217
- package/dist/resources/cli.js.map +0 -1
- package/dist/resources/plugin-development.d.ts +0 -3
- package/dist/resources/plugin-development.d.ts.map +0 -1
- package/dist/resources/plugin-development.js +0 -596
- package/dist/resources/plugin-development.js.map +0 -1
- package/dist/resources/quick-start.d.ts +0 -3
- package/dist/resources/quick-start.d.ts.map +0 -1
- package/dist/resources/quick-start.js +0 -210
- package/dist/resources/quick-start.js.map +0 -1
- package/dist/resources/sdk-api.d.ts +0 -3
- package/dist/resources/sdk-api.d.ts.map +0 -1
- package/dist/resources/sdk-api.js +0 -199
- package/dist/resources/sdk-api.js.map +0 -1
- package/dist/resources/self-improvement.d.ts +0 -10
- package/dist/resources/self-improvement.d.ts.map +0 -1
- package/dist/resources/self-improvement.js +0 -91
- package/dist/resources/self-improvement.js.map +0 -1
- package/dist/resources/status.d.ts +0 -5
- package/dist/resources/status.d.ts.map +0 -1
- package/dist/resources/status.js +0 -27
- package/dist/resources/status.js.map +0 -1
- package/dist/resources/troubleshooting.d.ts +0 -3
- package/dist/resources/troubleshooting.d.ts.map +0 -1
- package/dist/resources/troubleshooting.js +0 -167
- package/dist/resources/troubleshooting.js.map +0 -1
- package/dist/sanitize-tool-output.d.ts +0 -20
- package/dist/sanitize-tool-output.d.ts.map +0 -1
- package/dist/sanitize-tool-output.js +0 -52
- package/dist/sanitize-tool-output.js.map +0 -1
- package/dist/skip-confirmation.d.ts +0 -15
- package/dist/skip-confirmation.d.ts.map +0 -1
- package/dist/skip-confirmation.js +0 -16
- package/dist/skip-confirmation.js.map +0 -1
- package/dist/skip-permissions.d.ts +0 -11
- package/dist/skip-permissions.d.ts.map +0 -1
- package/dist/skip-permissions.js +0 -12
- package/dist/skip-permissions.js.map +0 -1
- package/dist/skip-sanitization.d.ts +0 -17
- package/dist/skip-sanitization.d.ts.map +0 -1
- package/dist/skip-sanitization.js +0 -18
- package/dist/skip-sanitization.js.map +0 -1
- package/dist/skip-verification.d.ts +0 -11
- package/dist/skip-verification.d.ts.map +0 -1
- package/dist/skip-verification.js +0 -12
- package/dist/skip-verification.js.map +0 -1
- package/dist/verify-plugin.d.ts +0 -53
- package/dist/verify-plugin.d.ts.map +0 -1
- package/dist/verify-plugin.js +0 -123
- package/dist/verify-plugin.js.map +0 -1
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/** Browser Tools Reference resource content. */
|
|
2
|
-
export const BROWSER_TOOLS_CONTENT = `# Browser Tools Reference
|
|
3
|
-
|
|
4
|
-
41 built-in tools organized by category. All browser tools are always available regardless of installed plugins.
|
|
5
|
-
|
|
6
|
-
## Tabs (6 tools)
|
|
7
|
-
|
|
8
|
-
| Tool | Description |
|
|
9
|
-
|------|-------------|
|
|
10
|
-
| \`browser_open_tab\` | Open a new browser tab with a URL. Returns the new tab ID |
|
|
11
|
-
| \`browser_list_tabs\` | List all open tabs with IDs, titles, URLs, and active status |
|
|
12
|
-
| \`browser_close_tab\` | Close a tab by ID |
|
|
13
|
-
| \`browser_navigate_tab\` | Navigate a tab to a new URL |
|
|
14
|
-
| \`browser_focus_tab\` | Focus a tab and bring its window to the foreground |
|
|
15
|
-
| \`browser_get_tab_info\` | Get tab details: loading status, URL, title, favicon, incognito |
|
|
16
|
-
|
|
17
|
-
## Page Interaction (7 tools)
|
|
18
|
-
|
|
19
|
-
| Tool | Description |
|
|
20
|
-
|------|-------------|
|
|
21
|
-
| \`browser_click_element\` | Click an element by CSS selector. Dispatches trusted mouse events via CDP |
|
|
22
|
-
| \`browser_type_text\` | Type text into an input/textarea. Focuses, optionally clears, sets value, dispatches events |
|
|
23
|
-
| \`browser_select_option\` | Select a \`<select>\` dropdown option by value or label |
|
|
24
|
-
| \`browser_press_key\` | Press a keyboard key (Enter, Escape, Tab, arrows, Ctrl+K, etc.) via CDP |
|
|
25
|
-
| \`browser_scroll\` | Scroll by selector (into view), direction (up/down/left/right), or absolute position |
|
|
26
|
-
| \`browser_hover_element\` | Hover over an element to trigger dropdowns, tooltips, and hover states |
|
|
27
|
-
| \`browser_handle_dialog\` | Handle JS dialogs (alert, confirm, prompt) that block page execution |
|
|
28
|
-
|
|
29
|
-
## Page Inspection (10 tools)
|
|
30
|
-
|
|
31
|
-
| Tool | Description |
|
|
32
|
-
|------|-------------|
|
|
33
|
-
| \`browser_get_tab_content\` | Extract visible text content from a page or element |
|
|
34
|
-
| \`browser_get_page_html\` | Get raw HTML (outerHTML) of a page or element |
|
|
35
|
-
| \`browser_screenshot_tab\` | Capture a screenshot as base64 PNG |
|
|
36
|
-
| \`browser_query_elements\` | Query elements by CSS selector, return tags, text, and attributes |
|
|
37
|
-
| \`browser_execute_script\` | Execute JavaScript in a tab's MAIN world with full DOM/window access |
|
|
38
|
-
| \`browser_get_console_logs\` | Get console messages (requires network capture active) |
|
|
39
|
-
| \`browser_clear_console_logs\` | Clear console log buffer without disabling capture |
|
|
40
|
-
| \`browser_list_resources\` | List all resources loaded by a page (scripts, CSS, images, fonts) |
|
|
41
|
-
| \`browser_get_resource_content\` | Read a resource's content from browser cache |
|
|
42
|
-
| \`browser_wait_for_element\` | Wait for an element to appear in the DOM (polls until found or timeout) |
|
|
43
|
-
|
|
44
|
-
## Storage & Cookies (5 tools)
|
|
45
|
-
|
|
46
|
-
| Tool | Description |
|
|
47
|
-
|------|-------------|
|
|
48
|
-
| \`browser_get_storage\` | Read localStorage or sessionStorage entries |
|
|
49
|
-
| \`browser_get_cookies\` | Get cookies for a URL (including HttpOnly) |
|
|
50
|
-
| \`browser_set_cookie\` | Set or overwrite a cookie |
|
|
51
|
-
| \`browser_delete_cookies\` | Delete a cookie by URL and name |
|
|
52
|
-
|
|
53
|
-
**Security note:** Storage and cookie tools expose sensitive auth data. Only use when the user directly requests it.
|
|
54
|
-
|
|
55
|
-
## Network (5 tools)
|
|
56
|
-
|
|
57
|
-
| Tool | Description |
|
|
58
|
-
|------|-------------|
|
|
59
|
-
| \`browser_enable_network_capture\` | Start capturing HTTP requests, responses, and WebSocket frames via CDP |
|
|
60
|
-
| \`browser_get_network_requests\` | Get captured requests with URLs, methods, headers, bodies, timing |
|
|
61
|
-
| \`browser_get_websocket_frames\` | Get captured WebSocket frames with direction, data, and timestamps |
|
|
62
|
-
| \`browser_export_har\` | Export captured traffic as a HAR 1.2 JSON file |
|
|
63
|
-
| \`browser_disable_network_capture\` | Stop capturing and release the CDP debugger |
|
|
64
|
-
|
|
65
|
-
Use \`urlFilter\` on \`browser_enable_network_capture\` to focus on API calls (e.g., "/api") and reduce noise.
|
|
66
|
-
|
|
67
|
-
**Security note:** Network capture records authorization headers and sensitive API traffic. Only use when the user directly requests it.
|
|
68
|
-
|
|
69
|
-
## Extension (6 tools)
|
|
70
|
-
|
|
71
|
-
| Tool | Description |
|
|
72
|
-
|------|-------------|
|
|
73
|
-
| \`extension_reload\` | Reload the Chrome extension (briefly disconnects) |
|
|
74
|
-
| \`extension_get_state\` | Get WebSocket status, registered plugins, active captures |
|
|
75
|
-
| \`extension_get_logs\` | Get extension background script and offscreen document logs |
|
|
76
|
-
| \`extension_get_side_panel\` | Get side panel React state and rendered HTML |
|
|
77
|
-
| \`extension_check_adapter\` | Diagnose adapter injection for a plugin across matching tabs |
|
|
78
|
-
| \`extension_force_reconnect\` | Force WebSocket disconnect and immediate reconnection |
|
|
79
|
-
|
|
80
|
-
## Plugins (2 tools)
|
|
81
|
-
|
|
82
|
-
| Tool | Description |
|
|
83
|
-
|------|-------------|
|
|
84
|
-
| \`plugin_list_tabs\` | List tabs matching a plugin's URL patterns with readiness status |
|
|
85
|
-
| \`plugin_analyze_site\` | Comprehensive site analysis for plugin development: auth, APIs, frameworks, storage, tool suggestions |
|
|
86
|
-
|
|
87
|
-
\`plugin_list_tabs\` reads from server-side state (no extension round-trip). Use it to discover tab IDs before targeting with \`tabId\`.
|
|
88
|
-
|
|
89
|
-
\`plugin_analyze_site\` opens the URL, captures network traffic, probes for frameworks/auth/APIs/storage, and returns concrete tool suggestions with implementation approaches.
|
|
90
|
-
|
|
91
|
-
## Platform Tools (always available, hidden from side panel)
|
|
92
|
-
|
|
93
|
-
| Tool | Description |
|
|
94
|
-
|------|-------------|
|
|
95
|
-
| \`plugin_inspect\` | Retrieve a plugin's adapter source code for security review + review token |
|
|
96
|
-
| \`plugin_mark_reviewed\` | Mark a plugin as reviewed and set its permission |
|
|
97
|
-
|
|
98
|
-
These bypass permission checks and are used in the plugin review flow.
|
|
99
|
-
`;
|
|
100
|
-
//# sourceMappingURL=browser-tools.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"browser-tools.js","sourceRoot":"","sources":["../../src/resources/browser-tools.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiGpC,CAAC"}
|
package/dist/resources/cli.d.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/** CLI Reference resource content. */
|
|
2
|
-
export declare const CLI_CONTENT = "# CLI Reference\n\n## opentabs CLI\n\nUser-facing CLI for managing the OpenTabs platform.\n\n### Core Commands\n\n| Command | Description |\n|---------|-------------|\n| `opentabs start [options]` | Start the MCP server |\n| `opentabs stop [options]` | Stop the background MCP server |\n| `opentabs status [options]` | Show server status, extension connection, and plugin states |\n| `opentabs logs [options]` | Show recent MCP server log output |\n| `opentabs audit [options]` | Show recent tool invocation history |\n| `opentabs doctor [options]` | Diagnose your OpenTabs setup |\n| `opentabs update` | Update CLI to the latest version |\n\n### start\n\n```bash\nopentabs start [--port <number>] [--background] [--show-config]\n```\n\n- `--port <number>` \u2014 Server port (default: 9515)\n- `--background` \u2014 Run as a background process (PID written to `~/.opentabs/server.pid`)\n- `--show-config` \u2014 Print MCP client configuration blocks\n\nOn first run, creates `~/.opentabs/`, generates auth secret, and prints configuration.\n\n### stop\n\n```bash\nopentabs stop [--port <number>]\n```\n\nStops a background server started with `opentabs start --background`.\n\n### status\n\n```bash\nopentabs status [--port <number>] [--json]\n```\n\n- `--json` \u2014 Output raw JSON from the health endpoint\n\nShows: server version, uptime, extension connection, plugin count, per-plugin details.\n\n### logs\n\n```bash\nopentabs logs [--lines <n>] [-f|--follow] [--plugin <name>]\n```\n\n- `--lines <n>` \u2014 Number of lines (default: 50)\n- `-f, --follow` \u2014 Tail the log (like `tail -f`)\n- `--plugin <name>` \u2014 Filter logs by plugin name\n\n### audit\n\n```bash\nopentabs audit [--limit <n>] [--plugin <name>] [--tool <name>] [--since <duration>] [--json] [--file]\n```\n\n- `--limit <n>` \u2014 Number of entries (default: 20)\n- `--plugin <name>` \u2014 Filter by plugin name\n- `--tool <name>` \u2014 Filter by tool name\n- `--since <duration>` \u2014 Time range (e.g., `30m`, `1h`, `2d`)\n- `--file` \u2014 Read from disk log (`~/.opentabs/audit.log`) instead of running server\n\n### doctor\n\n```bash\nopentabs doctor [--port <number>]\n```\n\nChecks: runtime, browser, config file, auth secret, server health, extension status, extension version, MCP client config, local plugins, npm plugins.\n\n### update\n\n```bash\nopentabs update\n```\n\nChecks npm for updates, warns if server is running, auto-restarts background servers after update.\n\n## Configuration Commands\n\n### config show (alias: config get)\n\n```bash\nopentabs config show [--json] [--show-secret]\n```\n\n- `--json` \u2014 Output as JSON\n- `--show-secret` \u2014 Display auth secret and MCP client configurations\n\n### config set\n\n```bash\nopentabs config set <key> [value] [-f|--force]\n```\n\n**Supported keys:**\n\n| Key Format | Value | Example |\n|------------|-------|---------|\n| `tool-permission.<plugin>.<tool>` | `off\\|ask\\|auto` | `opentabs config set tool-permission.slack.send_message auto` |\n| `plugin-permission.<plugin>` | `off\\|ask\\|auto` | `opentabs config set plugin-permission.slack ask` |\n| `port` | `1-65535` | `opentabs config set port 9515` |\n| `localPlugins.add` | path | `opentabs config set localPlugins.add /path/to/plugin` |\n| `localPlugins.remove` | path | `opentabs config set localPlugins.remove /path/to/plugin` |\n\n`--force` allows `localPlugins.add` even if the path doesn't exist yet.\n\n### config path\n\n```bash\nopentabs config path\n```\n\nPrints the absolute path to `~/.opentabs/config.json`.\n\n### config reset\n\n```bash\nopentabs config reset [--confirm]\n```\n\nDeletes the config file. Server regenerates defaults on next start.\n\n### config rotate-secret\n\n```bash\nopentabs config rotate-secret [--confirm]\n```\n\nGenerates new 256-bit auth secret, notifies running server, requires MCP clients to update.\n\n## Plugin Management Commands\n\n### plugin search\n\n```bash\nopentabs plugin search [query]\n```\n\nSearch npm registry for OpenTabs plugins. Omit query to list all available plugins.\n\n### plugin list (alias: plugin ls)\n\n```bash\nopentabs plugin list [--port <number>] [--json] [-v|--verbose]\n```\n\n- `--json` \u2014 Machine-readable JSON output\n- `-v, --verbose` \u2014 Show tool names for each plugin\n\n### plugin install (alias: plugin add)\n\n```bash\nopentabs plugin install <name>\n```\n\nResolves shorthand names (e.g., `slack` \u2192 `opentabs-plugin-slack` or `@opentabs-dev/opentabs-plugin-slack`).\n\n### plugin remove (alias: plugin rm)\n\n```bash\nopentabs plugin remove <name> [-y|--confirm]\n```\n\n### plugin create\n\n```bash\nopentabs plugin create [name] [--domain <domain>] [--display <name>] [--description <desc>]\n```\n\nScaffolds a new plugin project. Interactive mode if arguments not provided.\n\n## opentabs-plugin CLI\n\nPlugin developer CLI for building and inspecting plugins.\n\n### opentabs-plugin build\n\n```bash\nopentabs-plugin build [--watch]\n```\n\n- Generates `dist/tools.json` (tool schemas + SDK version)\n- Bundles adapter as IIFE in `dist/adapter.iife.js`\n- Auto-registers in `~/.opentabs/config.json` on first build\n- Notifies running MCP server via `POST /reload`\n- `--watch` \u2014 Rebuild on file changes\n\n### opentabs-plugin inspect\n\n```bash\nopentabs-plugin inspect [--json]\n```\n\nPretty-prints the built plugin manifest: name, version, SDK version, tool count, and detailed tool schemas.\n\n## File Paths\n\n| Path | Purpose |\n|------|---------|\n| `~/.opentabs/config.json` | Server and plugin configuration |\n| `~/.opentabs/extension/auth.json` | WebSocket auth secret |\n| `~/.opentabs/server.log` | Server log output |\n| `~/.opentabs/audit.log` | Persistent audit log (NDJSON) |\n| `~/.opentabs/server.pid` | Background server PID |\n| `~/.opentabs/extension/` | Chrome extension files |\n";
|
|
3
|
-
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/resources/cli.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,eAAO,MAAM,WAAW,qtLAsNvB,CAAC"}
|
package/dist/resources/cli.js
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/** CLI Reference resource content. */
|
|
2
|
-
export const CLI_CONTENT = `# CLI Reference
|
|
3
|
-
|
|
4
|
-
## opentabs CLI
|
|
5
|
-
|
|
6
|
-
User-facing CLI for managing the OpenTabs platform.
|
|
7
|
-
|
|
8
|
-
### Core Commands
|
|
9
|
-
|
|
10
|
-
| Command | Description |
|
|
11
|
-
|---------|-------------|
|
|
12
|
-
| \`opentabs start [options]\` | Start the MCP server |
|
|
13
|
-
| \`opentabs stop [options]\` | Stop the background MCP server |
|
|
14
|
-
| \`opentabs status [options]\` | Show server status, extension connection, and plugin states |
|
|
15
|
-
| \`opentabs logs [options]\` | Show recent MCP server log output |
|
|
16
|
-
| \`opentabs audit [options]\` | Show recent tool invocation history |
|
|
17
|
-
| \`opentabs doctor [options]\` | Diagnose your OpenTabs setup |
|
|
18
|
-
| \`opentabs update\` | Update CLI to the latest version |
|
|
19
|
-
|
|
20
|
-
### start
|
|
21
|
-
|
|
22
|
-
\`\`\`bash
|
|
23
|
-
opentabs start [--port <number>] [--background] [--show-config]
|
|
24
|
-
\`\`\`
|
|
25
|
-
|
|
26
|
-
- \`--port <number>\` — Server port (default: 9515)
|
|
27
|
-
- \`--background\` — Run as a background process (PID written to \`~/.opentabs/server.pid\`)
|
|
28
|
-
- \`--show-config\` — Print MCP client configuration blocks
|
|
29
|
-
|
|
30
|
-
On first run, creates \`~/.opentabs/\`, generates auth secret, and prints configuration.
|
|
31
|
-
|
|
32
|
-
### stop
|
|
33
|
-
|
|
34
|
-
\`\`\`bash
|
|
35
|
-
opentabs stop [--port <number>]
|
|
36
|
-
\`\`\`
|
|
37
|
-
|
|
38
|
-
Stops a background server started with \`opentabs start --background\`.
|
|
39
|
-
|
|
40
|
-
### status
|
|
41
|
-
|
|
42
|
-
\`\`\`bash
|
|
43
|
-
opentabs status [--port <number>] [--json]
|
|
44
|
-
\`\`\`
|
|
45
|
-
|
|
46
|
-
- \`--json\` — Output raw JSON from the health endpoint
|
|
47
|
-
|
|
48
|
-
Shows: server version, uptime, extension connection, plugin count, per-plugin details.
|
|
49
|
-
|
|
50
|
-
### logs
|
|
51
|
-
|
|
52
|
-
\`\`\`bash
|
|
53
|
-
opentabs logs [--lines <n>] [-f|--follow] [--plugin <name>]
|
|
54
|
-
\`\`\`
|
|
55
|
-
|
|
56
|
-
- \`--lines <n>\` — Number of lines (default: 50)
|
|
57
|
-
- \`-f, --follow\` — Tail the log (like \`tail -f\`)
|
|
58
|
-
- \`--plugin <name>\` — Filter logs by plugin name
|
|
59
|
-
|
|
60
|
-
### audit
|
|
61
|
-
|
|
62
|
-
\`\`\`bash
|
|
63
|
-
opentabs audit [--limit <n>] [--plugin <name>] [--tool <name>] [--since <duration>] [--json] [--file]
|
|
64
|
-
\`\`\`
|
|
65
|
-
|
|
66
|
-
- \`--limit <n>\` — Number of entries (default: 20)
|
|
67
|
-
- \`--plugin <name>\` — Filter by plugin name
|
|
68
|
-
- \`--tool <name>\` — Filter by tool name
|
|
69
|
-
- \`--since <duration>\` — Time range (e.g., \`30m\`, \`1h\`, \`2d\`)
|
|
70
|
-
- \`--file\` — Read from disk log (\`~/.opentabs/audit.log\`) instead of running server
|
|
71
|
-
|
|
72
|
-
### doctor
|
|
73
|
-
|
|
74
|
-
\`\`\`bash
|
|
75
|
-
opentabs doctor [--port <number>]
|
|
76
|
-
\`\`\`
|
|
77
|
-
|
|
78
|
-
Checks: runtime, browser, config file, auth secret, server health, extension status, extension version, MCP client config, local plugins, npm plugins.
|
|
79
|
-
|
|
80
|
-
### update
|
|
81
|
-
|
|
82
|
-
\`\`\`bash
|
|
83
|
-
opentabs update
|
|
84
|
-
\`\`\`
|
|
85
|
-
|
|
86
|
-
Checks npm for updates, warns if server is running, auto-restarts background servers after update.
|
|
87
|
-
|
|
88
|
-
## Configuration Commands
|
|
89
|
-
|
|
90
|
-
### config show (alias: config get)
|
|
91
|
-
|
|
92
|
-
\`\`\`bash
|
|
93
|
-
opentabs config show [--json] [--show-secret]
|
|
94
|
-
\`\`\`
|
|
95
|
-
|
|
96
|
-
- \`--json\` — Output as JSON
|
|
97
|
-
- \`--show-secret\` — Display auth secret and MCP client configurations
|
|
98
|
-
|
|
99
|
-
### config set
|
|
100
|
-
|
|
101
|
-
\`\`\`bash
|
|
102
|
-
opentabs config set <key> [value] [-f|--force]
|
|
103
|
-
\`\`\`
|
|
104
|
-
|
|
105
|
-
**Supported keys:**
|
|
106
|
-
|
|
107
|
-
| Key Format | Value | Example |
|
|
108
|
-
|------------|-------|---------|
|
|
109
|
-
| \`tool-permission.<plugin>.<tool>\` | \`off\\|ask\\|auto\` | \`opentabs config set tool-permission.slack.send_message auto\` |
|
|
110
|
-
| \`plugin-permission.<plugin>\` | \`off\\|ask\\|auto\` | \`opentabs config set plugin-permission.slack ask\` |
|
|
111
|
-
| \`port\` | \`1-65535\` | \`opentabs config set port 9515\` |
|
|
112
|
-
| \`localPlugins.add\` | path | \`opentabs config set localPlugins.add /path/to/plugin\` |
|
|
113
|
-
| \`localPlugins.remove\` | path | \`opentabs config set localPlugins.remove /path/to/plugin\` |
|
|
114
|
-
|
|
115
|
-
\`--force\` allows \`localPlugins.add\` even if the path doesn't exist yet.
|
|
116
|
-
|
|
117
|
-
### config path
|
|
118
|
-
|
|
119
|
-
\`\`\`bash
|
|
120
|
-
opentabs config path
|
|
121
|
-
\`\`\`
|
|
122
|
-
|
|
123
|
-
Prints the absolute path to \`~/.opentabs/config.json\`.
|
|
124
|
-
|
|
125
|
-
### config reset
|
|
126
|
-
|
|
127
|
-
\`\`\`bash
|
|
128
|
-
opentabs config reset [--confirm]
|
|
129
|
-
\`\`\`
|
|
130
|
-
|
|
131
|
-
Deletes the config file. Server regenerates defaults on next start.
|
|
132
|
-
|
|
133
|
-
### config rotate-secret
|
|
134
|
-
|
|
135
|
-
\`\`\`bash
|
|
136
|
-
opentabs config rotate-secret [--confirm]
|
|
137
|
-
\`\`\`
|
|
138
|
-
|
|
139
|
-
Generates new 256-bit auth secret, notifies running server, requires MCP clients to update.
|
|
140
|
-
|
|
141
|
-
## Plugin Management Commands
|
|
142
|
-
|
|
143
|
-
### plugin search
|
|
144
|
-
|
|
145
|
-
\`\`\`bash
|
|
146
|
-
opentabs plugin search [query]
|
|
147
|
-
\`\`\`
|
|
148
|
-
|
|
149
|
-
Search npm registry for OpenTabs plugins. Omit query to list all available plugins.
|
|
150
|
-
|
|
151
|
-
### plugin list (alias: plugin ls)
|
|
152
|
-
|
|
153
|
-
\`\`\`bash
|
|
154
|
-
opentabs plugin list [--port <number>] [--json] [-v|--verbose]
|
|
155
|
-
\`\`\`
|
|
156
|
-
|
|
157
|
-
- \`--json\` — Machine-readable JSON output
|
|
158
|
-
- \`-v, --verbose\` — Show tool names for each plugin
|
|
159
|
-
|
|
160
|
-
### plugin install (alias: plugin add)
|
|
161
|
-
|
|
162
|
-
\`\`\`bash
|
|
163
|
-
opentabs plugin install <name>
|
|
164
|
-
\`\`\`
|
|
165
|
-
|
|
166
|
-
Resolves shorthand names (e.g., \`slack\` → \`opentabs-plugin-slack\` or \`@opentabs-dev/opentabs-plugin-slack\`).
|
|
167
|
-
|
|
168
|
-
### plugin remove (alias: plugin rm)
|
|
169
|
-
|
|
170
|
-
\`\`\`bash
|
|
171
|
-
opentabs plugin remove <name> [-y|--confirm]
|
|
172
|
-
\`\`\`
|
|
173
|
-
|
|
174
|
-
### plugin create
|
|
175
|
-
|
|
176
|
-
\`\`\`bash
|
|
177
|
-
opentabs plugin create [name] [--domain <domain>] [--display <name>] [--description <desc>]
|
|
178
|
-
\`\`\`
|
|
179
|
-
|
|
180
|
-
Scaffolds a new plugin project. Interactive mode if arguments not provided.
|
|
181
|
-
|
|
182
|
-
## opentabs-plugin CLI
|
|
183
|
-
|
|
184
|
-
Plugin developer CLI for building and inspecting plugins.
|
|
185
|
-
|
|
186
|
-
### opentabs-plugin build
|
|
187
|
-
|
|
188
|
-
\`\`\`bash
|
|
189
|
-
opentabs-plugin build [--watch]
|
|
190
|
-
\`\`\`
|
|
191
|
-
|
|
192
|
-
- Generates \`dist/tools.json\` (tool schemas + SDK version)
|
|
193
|
-
- Bundles adapter as IIFE in \`dist/adapter.iife.js\`
|
|
194
|
-
- Auto-registers in \`~/.opentabs/config.json\` on first build
|
|
195
|
-
- Notifies running MCP server via \`POST /reload\`
|
|
196
|
-
- \`--watch\` — Rebuild on file changes
|
|
197
|
-
|
|
198
|
-
### opentabs-plugin inspect
|
|
199
|
-
|
|
200
|
-
\`\`\`bash
|
|
201
|
-
opentabs-plugin inspect [--json]
|
|
202
|
-
\`\`\`
|
|
203
|
-
|
|
204
|
-
Pretty-prints the built plugin manifest: name, version, SDK version, tool count, and detailed tool schemas.
|
|
205
|
-
|
|
206
|
-
## File Paths
|
|
207
|
-
|
|
208
|
-
| Path | Purpose |
|
|
209
|
-
|------|---------|
|
|
210
|
-
| \`~/.opentabs/config.json\` | Server and plugin configuration |
|
|
211
|
-
| \`~/.opentabs/extension/auth.json\` | WebSocket auth secret |
|
|
212
|
-
| \`~/.opentabs/server.log\` | Server log output |
|
|
213
|
-
| \`~/.opentabs/audit.log\` | Persistent audit log (NDJSON) |
|
|
214
|
-
| \`~/.opentabs/server.pid\` | Background server PID |
|
|
215
|
-
| \`~/.opentabs/extension/\` | Chrome extension files |
|
|
216
|
-
`;
|
|
217
|
-
//# sourceMappingURL=cli.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/resources/cli.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsN1B,CAAC"}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/** Plugin Development Guide resource content. */
|
|
2
|
-
export declare const PLUGIN_DEVELOPMENT_CONTENT = "# Plugin Development Guide\n\n## Architecture\n\nOpenTabs plugins run **in the browser page context**, not on the server. The MCP server discovers plugins, but tool execution happens inside the web page via an adapter IIFE injected by the Chrome extension. This means plugin code has full access to the page's DOM, JavaScript globals, cookies, localStorage, and authenticated fetch requests.\n\n**Flow:** AI client \u2192 MCP server \u2192 Chrome extension (WebSocket) \u2192 adapter IIFE (page context) \u2192 tool handler \u2192 result back through the chain.\n\n## Plugin Structure\n\nA plugin is a standalone npm package with this structure:\n\n```\nmy-plugin/\n\u251C\u2500\u2500 package.json # Must include \"opentabs\" field\n\u251C\u2500\u2500 src/\n\u2502 \u251C\u2500\u2500 plugin.ts # OpenTabsPlugin subclass (entry point)\n\u2502 \u2514\u2500\u2500 tools/\n\u2502 \u251C\u2500\u2500 get-data.ts # One file per tool (convention)\n\u2502 \u2514\u2500\u2500 send-msg.ts\n\u251C\u2500\u2500 dist/ # Built by opentabs-plugin build\n\u2502 \u251C\u2500\u2500 adapter.iife.js # Injected into matching browser tabs\n\u2502 \u2514\u2500\u2500 tools.json # Tool schemas for MCP registration\n\u2514\u2500\u2500 tsconfig.json\n```\n\n### package.json\n\n```json\n{\n \"name\": \"@scope/opentabs-plugin-myapp\",\n \"version\": \"1.0.0\",\n \"opentabs\": {\n \"name\": \"myapp\",\n \"displayName\": \"My App\",\n \"description\": \"Tools for My App\",\n \"urlPatterns\": [\"*://myapp.com/*\"]\n },\n \"main\": \"src/plugin.ts\",\n \"scripts\": {\n \"build\": \"opentabs-plugin build\"\n },\n \"dependencies\": {\n \"@opentabs-dev/plugin-sdk\": \"latest\"\n },\n \"devDependencies\": {\n \"@opentabs-dev/plugin-tools\": \"latest\"\n }\n}\n```\n\nThe `opentabs.name` field is the plugin identifier (lowercase, alphanumeric + hyphens). It becomes the tool name prefix (e.g., `myapp_get_data`).\n\n## OpenTabsPlugin Base Class\n\nEvery plugin extends `OpenTabsPlugin` and exports an instance:\n\n```typescript\nimport { OpenTabsPlugin } from '@opentabs-dev/plugin-sdk';\nimport type { ToolDefinition } from '@opentabs-dev/plugin-sdk';\nimport { getDataTool } from './tools/get-data.js';\nimport { sendMsgTool } from './tools/send-msg.js';\n\nclass MyPlugin extends OpenTabsPlugin {\n readonly name = 'myapp';\n readonly displayName = 'My App';\n readonly description = 'Tools for My App';\n readonly urlPatterns = ['*://myapp.com/*'];\n readonly tools: ToolDefinition[] = [getDataTool, sendMsgTool];\n\n async isReady(): Promise<boolean> {\n // Return true when the user is authenticated and the app is loaded\n return document.querySelector('.logged-in-indicator') !== null;\n }\n}\n\nexport default new MyPlugin();\n```\n\n### Required Members\n\n| Member | Type | Purpose |\n|--------|------|---------|\n| `name` | `string` | Unique identifier (lowercase alphanumeric + hyphens) |\n| `displayName` | `string` | Human-readable name shown in side panel |\n| `description` | `string` | Brief plugin description |\n| `urlPatterns` | `string[]` | Chrome match patterns for tab injection |\n| `tools` | `ToolDefinition[]` | Array of tool definitions |\n| `isReady()` | `() => Promise<boolean>` | Readiness probe \u2014 returns true when tab is ready for tool calls |\n\n### Tab State Machine\n\n| State | Condition |\n|-------|-----------|\n| `closed` | No browser tab matches the plugin's URL patterns |\n| `unavailable` | Tab matches URL patterns but `isReady()` returns false |\n| `ready` | Tab matches URL patterns and `isReady()` returns true |\n\n## defineTool Factory\n\nEach tool is defined with `defineTool`, which provides type inference:\n\n```typescript\nimport { z } from 'zod';\nimport { defineTool, fetchJSON } from '@opentabs-dev/plugin-sdk';\nimport type { ToolHandlerContext } from '@opentabs-dev/plugin-sdk';\n\nexport const getDataTool = defineTool({\n name: 'get_data',\n displayName: 'Get Data',\n description: 'Retrieves data from the app. Returns the matching records.',\n summary: 'Retrieve app data',\n icon: 'database',\n group: 'Data',\n input: z.object({\n query: z.string().describe('Search query string'),\n limit: z.number().int().min(1).max(100).default(25).describe('Max results to return'),\n }),\n output: z.object({\n results: z.array(z.object({\n id: z.string(),\n title: z.string(),\n })),\n total: z.number(),\n }),\n async handle(params, context?: ToolHandlerContext) {\n const data = await fetchJSON<{ items: Array<{ id: string; title: string }>; total: number }>(\n `/api/data?q=${encodeURIComponent(params.query)}&limit=${params.limit}`\n );\n return { results: data?.items ?? [], total: data?.total ?? 0 };\n },\n});\n```\n\n### ToolDefinition Fields\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `name` | Yes | Tool name (auto-prefixed with plugin name) |\n| `displayName` | No | Human-readable name for side panel (auto-derived from name if omitted) |\n| `description` | Yes | Shown to AI agents \u2014 be specific and include return value info |\n| `summary` | No | Short UI summary (falls back to description) |\n| `icon` | No | Lucide icon name in kebab-case (defaults to `wrench`) |\n| `group` | No | Visual grouping in the side panel |\n| `input` | Yes | Zod object schema for parameters |\n| `output` | Yes | Zod schema for return value |\n| `handle` | Yes | Async function \u2014 runs in page context. Second arg is optional `ToolHandlerContext` |\n\n### Progress Reporting\n\nLong-running tools can report progress via the optional `context` parameter:\n\n```typescript\nasync handle(params, context?: ToolHandlerContext) {\n const items = await getItemList();\n for (let i = 0; i < items.length; i++) {\n context?.reportProgress({ progress: i + 1, total: items.length, message: `Processing ${items[i].name}` });\n await processItem(items[i]);\n }\n return { processed: items.length };\n}\n```\n\n## SDK Utilities Reference\n\nAll utilities are imported from `@opentabs-dev/plugin-sdk`. They run in the page context.\n\n### DOM\n\n| Function | Signature | Description |\n|----------|-----------|-------------|\n| `waitForSelector` | `<T extends Element>(selector, opts?) \u2192 Promise<T>` | Waits for element to appear (MutationObserver, default 10s timeout) |\n| `waitForSelectorRemoval` | `(selector, opts?) \u2192 Promise<void>` | Waits for element to be removed (default 10s timeout) |\n| `querySelectorAll` | `<T extends Element>(selector) \u2192 T[]` | Returns real array instead of NodeList |\n| `getTextContent` | `(selector) \u2192 string \\| null` | Trimmed textContent of first match |\n| `observeDOM` | `(selector, callback, opts?) \u2192 () => void` | MutationObserver on element, returns cleanup function |\n\n### Fetch\n\nAll fetch utilities use `credentials: 'include'` to leverage the page's authenticated session.\n\n| Function | Signature | Description |\n|----------|-----------|-------------|\n| `fetchFromPage` | `(url, init?) \u2192 Promise<Response>` | Fetch with session cookies, 30s timeout, ToolError on non-ok |\n| `fetchJSON` | `<T>(url, init?, schema?) \u2192 Promise<T>` | Fetch + JSON parse. Optional Zod schema validation |\n| `postJSON` | `<T>(url, body, init?, schema?) \u2192 Promise<T>` | POST with JSON body + parse response |\n| `putJSON` | `<T>(url, body, init?, schema?) \u2192 Promise<T>` | PUT with JSON body + parse response |\n| `patchJSON` | `<T>(url, body, init?, schema?) \u2192 Promise<T>` | PATCH with JSON body + parse response |\n| `deleteJSON` | `<T>(url, init?, schema?) \u2192 Promise<T>` | DELETE + parse response |\n| `postForm` | `<T>(url, body, init?, schema?) \u2192 Promise<T>` | POST URL-encoded form (Record<string,string>) |\n| `postFormData` | `<T>(url, body, init?, schema?) \u2192 Promise<T>` | POST multipart/form-data (FormData) |\n\n### Storage\n\n| Function | Signature | Description |\n|----------|-----------|-------------|\n| `getLocalStorage` | `(key) \u2192 string \\| null` | Safe localStorage read (null on SecurityError) |\n| `setLocalStorage` | `(key, value) \u2192 void` | Safe localStorage write |\n| `removeLocalStorage` | `(key) \u2192 void` | Safe localStorage remove |\n| `getSessionStorage` | `(key) \u2192 string \\| null` | Safe sessionStorage read |\n| `setSessionStorage` | `(key, value) \u2192 void` | Safe sessionStorage write |\n| `removeSessionStorage` | `(key) \u2192 void` | Safe sessionStorage remove |\n| `getCookie` | `(name) \u2192 string \\| null` | Parse cookie by name from document.cookie |\n\n### Page State\n\n| Function | Signature | Description |\n|----------|-----------|-------------|\n| `getPageGlobal` | `(path) \u2192 unknown` | Safe deep property access on globalThis via dot-notation |\n| `getCurrentUrl` | `() \u2192 string` | Returns window.location.href |\n| `getPageTitle` | `() \u2192 string` | Returns document.title |\n\n### Timing\n\n| Function | Signature | Description |\n|----------|-----------|-------------|\n| `retry` | `<T>(fn, opts?) \u2192 Promise<T>` | Retry with configurable attempts (3), delay (1s), backoff, AbortSignal |\n| `sleep` | `(ms, opts?) \u2192 Promise<void>` | Promisified setTimeout with optional AbortSignal |\n| `waitUntil` | `(predicate, opts?) \u2192 Promise<void>` | Poll predicate at interval (200ms) until true, timeout (10s) |\n\n### Logging\n\n| Function | Description |\n|----------|-------------|\n| `log.debug(message, ...args)` | Debug level |\n| `log.info(message, ...args)` | Info level |\n| `log.warn(message, ...args)` | Warning level |\n| `log.error(message, ...args)` | Error level |\n\nLog entries flow from the page context through the extension to the MCP server and connected clients. Falls back to `console` methods outside the adapter runtime.\n\n## ToolError Factories\n\nUse static factory methods for structured errors. The dispatch chain propagates metadata (category, retryable, retryAfterMs) to AI clients.\n\n| Factory | Signature | Category | Retryable |\n|---------|-----------|----------|-----------|\n| `ToolError.auth` | `(message, code?) \u2192 ToolError` | `auth` | No |\n| `ToolError.notFound` | `(message, code?) \u2192 ToolError` | `not_found` | No |\n| `ToolError.rateLimited` | `(message, retryAfterMs?, code?) \u2192 ToolError` | `rate_limit` | Yes |\n| `ToolError.validation` | `(message, code?) \u2192 ToolError` | `validation` | No |\n| `ToolError.timeout` | `(message, code?) \u2192 ToolError` | `timeout` | Yes |\n| `ToolError.internal` | `(message, code?) \u2192 ToolError` | `internal` | No |\n\n```typescript\nimport { ToolError, fetchJSON } from '@opentabs-dev/plugin-sdk';\n\n// Auth errors are automatically thrown by fetchJSON on 401/403\n// For manual auth checks:\nconst token = getPageGlobal('app.auth.token') as string | undefined;\nif (!token) throw ToolError.auth('User is not logged in');\n\n// For domain-specific errors with custom codes:\nthrow ToolError.notFound('Channel not found', 'CHANNEL_NOT_FOUND');\nthrow ToolError.rateLimited('Slow down', 5000, 'SLACK_RATE_LIMITED');\n```\n\n## Zod Schema Rules\n\nSchemas are serialized to JSON Schema via `z.toJSONSchema()` for MCP registration. Follow these rules:\n\n1. **Never use `.transform()`** \u2014 transforms cannot be represented in JSON Schema. Normalize input in the handler.\n2. **Avoid `.pipe()`, `.preprocess()`, and effects** \u2014 these are runtime-only and break serialization.\n3. **`.refine()` callbacks must never throw** \u2014 Zod 4 runs refine even on invalid base values. Wrap throwing code in try-catch.\n4. **Use `.describe()` on every field** \u2014 descriptions are shown to AI agents in the tool schema.\n5. **Keep schemas declarative** \u2014 primitives, objects, arrays, unions, literals, enums, optional, default.\n\n## Lifecycle Hooks\n\nOptional methods on `OpenTabsPlugin` \u2014 implement only what you need:\n\n| Hook | Signature | When Called |\n|------|-----------|------------|\n| `onActivate` | `() \u2192 void` | After adapter registered on `globalThis.__openTabs.adapters` |\n| `onDeactivate` | `() \u2192 void` | Before adapter removal (fires before `teardown`) |\n| `onNavigate` | `(url: string) \u2192 void` | On in-page URL changes (pushState, replaceState, popstate, hashchange) |\n| `onToolInvocationStart` | `(toolName: string) \u2192 void` | Before each `tool.handle()` |\n| `onToolInvocationEnd` | `(toolName: string, success: boolean, durationMs: number) \u2192 void` | After each `tool.handle()` |\n| `teardown` | `() \u2192 void` | Before re-injection on plugin update |\n\nErrors in hooks are caught and logged \u2014 they do not affect tool execution.\n\n## isReady() Polling Pattern\n\nThe extension polls `isReady()` to determine tab state. Common patterns:\n\n```typescript\n// DOM-based: check for a logged-in indicator\nasync isReady(): Promise<boolean> {\n return document.querySelector('[data-testid=\"user-menu\"]') !== null;\n}\n\n// Global-based: check for auth token in window globals\nasync isReady(): Promise<boolean> {\n return getPageGlobal('app.auth.token') !== undefined;\n}\n\n// API-based: verify session with a lightweight request\nasync isReady(): Promise<boolean> {\n try {\n await fetchJSON('/api/me');\n return true;\n } catch {\n return false;\n }\n}\n```\n\n## Auth Token Extraction\n\nPlugins extract auth from the page \u2014 never ask users for credentials.\n\n```typescript\n// From window globals (Slack pattern)\nconst token = getPageGlobal('TS.boot_data.api_token') as string | undefined;\nif (!token) throw ToolError.auth('Not logged in');\n\n// From localStorage\nconst token = getLocalStorage('auth_token');\nif (!token) throw ToolError.auth('No auth token found');\n\n// From cookies (session-based auth)\nconst session = getCookie('session_id');\nif (!session) throw ToolError.auth('No session cookie');\n\n// Cache on globalThis to avoid repeated extraction\nconst CACHE_KEY = '__opentabs_myapp_token';\nfunction getToken(): string {\n const cached = (globalThis as Record<string, unknown>)[CACHE_KEY] as string | undefined;\n if (cached) return cached;\n const token = getPageGlobal('app.token') as string | undefined;\n if (!token) throw ToolError.auth('Not authenticated');\n (globalThis as Record<string, unknown>)[CACHE_KEY] = token;\n return token;\n}\n```\n\n## Build and Test Workflow\n\n```bash\n# Build the plugin (generates dist/adapter.iife.js and dist/tools.json)\nnpx opentabs-plugin build\n# Or if installed globally:\nopentabs-plugin build\n\n# The build command notifies the running MCP server via POST /reload\n# No server restart needed \u2014 plugin changes are picked up automatically\n```\n\n### Testing During Development\n\n1. Build the plugin: `opentabs-plugin build`\n2. Open the target web app in Chrome\n3. Verify plugin loaded: call `plugin_list_tabs` from your AI client\n4. Test a tool: call any plugin tool (e.g., `myapp_get_data`)\n5. Check logs: call `extension_get_logs` to see adapter injection and tool execution logs\n\n### Scaffolding a New Plugin\n\n```bash\nnpx @opentabs-dev/create-plugin\n# Or with the CLI installed:\nopentabs plugin create\n```\n\n## Publishing to npm\n\n```json\n{\n \"name\": \"@scope/opentabs-plugin-myapp\",\n \"opentabs\": {\n \"name\": \"myapp\",\n \"displayName\": \"My App\",\n \"description\": \"Tools for My App\",\n \"urlPatterns\": [\"*://myapp.com/*\"]\n }\n}\n```\n\nPackage naming convention: `opentabs-plugin-<name>` or `@scope/opentabs-plugin-<name>`. The MCP server auto-discovers packages matching these patterns in global node_modules.\n\n```bash\nnpm publish\n# Users install with:\nopentabs plugin install myapp\n```\n\n## Common Patterns\n\n### API Wrapper\n\n```typescript\nconst API_BASE = '/api/v1';\n\nasync function apiGet<T>(path: string): Promise<T> {\n const result = await fetchJSON<T>(`${API_BASE}${path}`);\n if (result === undefined) throw ToolError.internal(`Unexpected empty response from ${path}`);\n return result;\n}\n\nasync function apiPost<T>(path: string, body: unknown): Promise<T> {\n const result = await postJSON<T>(`${API_BASE}${path}`, body);\n if (result === undefined) throw ToolError.internal(`Unexpected empty response from ${path}`);\n return result;\n}\n```\n\n### Waiting for App State\n\n```typescript\nimport { waitForSelector, waitUntil, getPageGlobal } from '@opentabs-dev/plugin-sdk';\n\n// Wait for the app to finish loading before executing\nawait waitForSelector('.app-loaded');\n\n// Wait for a specific global to be set\nawait waitUntil(() => getPageGlobal('app.initialized') === true);\n```\n\n### Retrying Flaky Operations\n\n```typescript\nimport { retry, ToolError } from '@opentabs-dev/plugin-sdk';\n\nconst result = await retry(\n () => fetchJSON<Data>('/api/flaky-endpoint'),\n { maxAttempts: 3, delay: 1000, backoff: true }\n);\n```\n\n## Core Principle: APIs Not DOM\n\nEvery tool must use the web app's own APIs \u2014 the same endpoints the web app calls internally. DOM scraping is never acceptable as a tool implementation strategy: it is fragile (breaks on UI changes), limited (only sees what's rendered), and slow (requires waiting for DOM mutations).\n\nWhen an API is hard to discover, invest time reverse-engineering network traffic rather than falling back to DOM. The only acceptable DOM uses are:\n- **`isReady()`** \u2014 checking auth indicators (e.g., a logged-in avatar)\n- **URL hash navigation** \u2014 changing views via `window.location.hash`\n- **Last-resort compose flows** \u2014 when no API exists for creating content (extremely rare)\n\n## Token Persistence\n\nModule-level variables (`let cachedAuth = null`) are reset when the Chrome extension reloads and re-injects the adapter IIFE. If the host app has already deleted the token from localStorage by this point, the plugin becomes unavailable.\n\nPersist auth tokens to `globalThis.__openTabs.tokenCache.<pluginName>`, which survives adapter re-injection (the page itself is not reloaded \u2014 only the IIFE is re-executed).\n\n```typescript\nconst getPersistedToken = (): string | null => {\n try {\n const ns = (globalThis as Record<string, unknown>).__openTabs as\n | Record<string, unknown>\n | undefined;\n const cache = ns?.tokenCache as\n | Record<string, string | undefined>\n | undefined;\n return cache?.myPlugin ?? null;\n } catch {\n return null;\n }\n};\n\nconst setPersistedToken = (token: string): void => {\n try {\n const g = globalThis as Record<string, unknown>;\n if (!g.__openTabs) g.__openTabs = {};\n const ns = g.__openTabs as Record<string, unknown>;\n if (!ns.tokenCache) ns.tokenCache = {};\n const cache = ns.tokenCache as Record<string, string | undefined>;\n cache.myPlugin = token;\n } catch {}\n};\n\nconst clearPersistedToken = (): void => {\n try {\n const ns = (globalThis as Record<string, unknown>).__openTabs as\n | Record<string, unknown>\n | undefined;\n const cache = ns?.tokenCache as\n | Record<string, string | undefined>\n | undefined;\n if (cache) cache.myPlugin = undefined;\n } catch {}\n};\n\n// In getAuth():\nconst getAuth = (): Auth | null => {\n const persisted = getPersistedToken();\n if (persisted) return { token: persisted };\n\n const raw = readLocalStorage('token');\n if (!raw) return null;\n setPersistedToken(raw);\n return { token: raw };\n};\n```\n\nAlways clear the persisted token on 401 responses to handle token rotation.\n\n## Adapter Injection Timing\n\nAdapters are injected at **two points** during page load:\n\n1. **`loading`** \u2014 before page JavaScript runs. The adapter IIFE registers on `globalThis.__openTabs` and can read localStorage/cookies before the host app modifies them.\n2. **`complete`** \u2014 after the page is fully loaded. The adapter is re-injected (idempotent) and `isReady()` is probed to determine tab state.\n\nThis means:\n- `isReady()` may be called at both injection points. At `loading` time, page globals do not exist yet \u2014 return `false` gracefully. At `complete` time, everything is ready.\n- Auth tokens from localStorage should be cached at `loading` time before the host app can delete them.\n\n## Advanced Auth Patterns\n\n### XHR/Fetch Interception\n\nSome web apps use internal RPC endpoints or obfuscated API paths that are hard to discover via network capture. Monkey-patch `XMLHttpRequest` to intercept all API traffic and capture auth headers at runtime.\n\n```typescript\nconst origOpen = XMLHttpRequest.prototype.open;\nconst origSetHeader = XMLHttpRequest.prototype.setRequestHeader;\nconst origSend = XMLHttpRequest.prototype.send;\n\nXMLHttpRequest.prototype.open = function (method: string, url: string) {\n (this as Record<string, unknown>)._url = url;\n (this as Record<string, unknown>)._method = method;\n return origOpen.apply(this, arguments as unknown as Parameters<typeof origOpen>);\n};\nXMLHttpRequest.prototype.setRequestHeader = function (name: string, value: string) {\n if (name.toLowerCase() === 'authorization') {\n setPersistedToken(value); // Capture auth header\n }\n return origSetHeader.apply(this, arguments as unknown as Parameters<typeof origSetHeader>);\n};\n```\n\nInstall the interceptor at adapter load time to capture auth tokens from early boot requests. Store captured tokens on `globalThis` so they survive adapter re-injection.\n\n### Cookie-Based Auth with CSRF\n\nMany web apps use HttpOnly session cookies for auth but require a CSRF token for write operations. The CSRF token is typically in a non-HttpOnly cookie (e.g., `csrftoken`, `sentry-sc`).\n\n```typescript\nconst csrfToken = getCookie('csrftoken');\nconst response = await fetch('/api/endpoint', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRFToken': csrfToken ?? '',\n },\n body: JSON.stringify(payload),\n credentials: 'include', // HttpOnly cookies sent automatically\n});\n```\n\nCheck `window.__initialData.csrfCookieName` or similar bootstrap globals to discover the cookie name. GET requests work without the CSRF token.\n\n### Opaque Auth Headers\n\nSome apps compute cryptographic auth tokens via obfuscated JavaScript. These tokens cannot be generated \u2014 only captured and replayed. Use the XHR interceptor pattern above to capture them, then implement a polling wait:\n\n```typescript\nconst waitForToken = async (): Promise<string> => {\n for (let i = 0; i < 50; i++) {\n const token = getPersistedToken();\n if (token) return token;\n await new Promise((r) => setTimeout(r, 200));\n }\n throw ToolError.auth('Auth token not captured \u2014 try refreshing the page');\n};\n```\n\nIf a write operation returns 200 but the action does not take effect, the cryptographic token may be missing or stale. Capture and replay the token using the XHR interceptor pattern above.\n\n### Extension/Programmatic APIs\n\nWhen standard API paths are blocked (undocumented crypto tokens, deprecated endpoints), complex web apps often expose higher-level programmatic interfaces:\n\n- Internal extension APIs on `window` (compose, send, draft management)\n- JavaScript-exposed infrastructure for accessibility or testing\n- `webpackChunk`-based module access to internal stores\n\nDiscovery: use `browser_execute_script` with `Object.keys(window).filter(k => !['location', 'chrome', 'document', 'navigator'].includes(k))` to find non-standard globals, then explore their methods.\n\n### API Deprecation\n\nInternal API endpoints can be deprecated without warning. When multiple API generations exist, test each endpoint independently. If an endpoint returns 404 or 403 unexpectedly, it may be deprecated for that account or region. Remove tools that depend on deprecated endpoints rather than shipping broken tools.\n\n## CSP Considerations\n\nThe adapter IIFE bypasses the page's Content Security Policy via file-based injection (`chrome.scripting.executeScript({ files: [...] })`). Plugin code runs as extension-origin code and is not subject to inline script restrictions.\n\n**Trusted Types**: Some pages enforce Trusted Types CSP, which blocks `innerHTML`, `outerHTML`, and `insertAdjacentHTML`. If you need to extract text from HTML strings, use regex instead:\n\n```typescript\nconst text = html.replace(/<[^>]+>/g, '');\n```\n";
|
|
3
|
-
//# sourceMappingURL=plugin-development.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-development.d.ts","sourceRoot":"","sources":["../../src/resources/plugin-development.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAEjD,eAAO,MAAM,0BAA0B,q0vBAilBtC,CAAC"}
|