@opentabs-dev/mcp-server 0.0.66 → 0.0.68
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/browser-tools/analyze-site.d.ts.map +1 -1
- package/dist/browser-tools/analyze-site.js +3 -1
- package/dist/browser-tools/analyze-site.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/dist/extension-handlers.d.ts +2 -1
- package/dist/extension-handlers.d.ts.map +1 -1
- package/dist/extension-handlers.js +22 -1
- package/dist/extension-handlers.js.map +1 -1
- package/dist/extension-protocol.d.ts.map +1 -1
- package/dist/extension-protocol.js +6 -1
- package/dist/extension-protocol.js.map +1 -1
- package/dist/mcp-setup.d.ts +1 -13
- package/dist/mcp-setup.d.ts.map +1 -1
- package/dist/mcp-setup.js +4 -96
- package/dist/mcp-setup.js.map +1 -1
- package/dist/reload.js +5 -5
- package/dist/reload.js.map +1 -1
- package/package.json +3 -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,596 +0,0 @@
|
|
|
1
|
-
/** Plugin Development Guide resource content. */
|
|
2
|
-
export const PLUGIN_DEVELOPMENT_CONTENT = `# Plugin Development Guide
|
|
3
|
-
|
|
4
|
-
## Architecture
|
|
5
|
-
|
|
6
|
-
OpenTabs 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.
|
|
7
|
-
|
|
8
|
-
**Flow:** AI client → MCP server → Chrome extension (WebSocket) → adapter IIFE (page context) → tool handler → result back through the chain.
|
|
9
|
-
|
|
10
|
-
## Plugin Structure
|
|
11
|
-
|
|
12
|
-
A plugin is a standalone npm package with this structure:
|
|
13
|
-
|
|
14
|
-
\`\`\`
|
|
15
|
-
my-plugin/
|
|
16
|
-
├── package.json # Must include "opentabs" field
|
|
17
|
-
├── src/
|
|
18
|
-
│ ├── plugin.ts # OpenTabsPlugin subclass (entry point)
|
|
19
|
-
│ └── tools/
|
|
20
|
-
│ ├── get-data.ts # One file per tool (convention)
|
|
21
|
-
│ └── send-msg.ts
|
|
22
|
-
├── dist/ # Built by opentabs-plugin build
|
|
23
|
-
│ ├── adapter.iife.js # Injected into matching browser tabs
|
|
24
|
-
│ └── tools.json # Tool schemas for MCP registration
|
|
25
|
-
└── tsconfig.json
|
|
26
|
-
\`\`\`
|
|
27
|
-
|
|
28
|
-
### package.json
|
|
29
|
-
|
|
30
|
-
\`\`\`json
|
|
31
|
-
{
|
|
32
|
-
"name": "@scope/opentabs-plugin-myapp",
|
|
33
|
-
"version": "1.0.0",
|
|
34
|
-
"opentabs": {
|
|
35
|
-
"name": "myapp",
|
|
36
|
-
"displayName": "My App",
|
|
37
|
-
"description": "Tools for My App",
|
|
38
|
-
"urlPatterns": ["*://myapp.com/*"]
|
|
39
|
-
},
|
|
40
|
-
"main": "src/plugin.ts",
|
|
41
|
-
"scripts": {
|
|
42
|
-
"build": "opentabs-plugin build"
|
|
43
|
-
},
|
|
44
|
-
"dependencies": {
|
|
45
|
-
"@opentabs-dev/plugin-sdk": "latest"
|
|
46
|
-
},
|
|
47
|
-
"devDependencies": {
|
|
48
|
-
"@opentabs-dev/plugin-tools": "latest"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
\`\`\`
|
|
52
|
-
|
|
53
|
-
The \`opentabs.name\` field is the plugin identifier (lowercase, alphanumeric + hyphens). It becomes the tool name prefix (e.g., \`myapp_get_data\`).
|
|
54
|
-
|
|
55
|
-
## OpenTabsPlugin Base Class
|
|
56
|
-
|
|
57
|
-
Every plugin extends \`OpenTabsPlugin\` and exports an instance:
|
|
58
|
-
|
|
59
|
-
\`\`\`typescript
|
|
60
|
-
import { OpenTabsPlugin } from '@opentabs-dev/plugin-sdk';
|
|
61
|
-
import type { ToolDefinition } from '@opentabs-dev/plugin-sdk';
|
|
62
|
-
import { getDataTool } from './tools/get-data.js';
|
|
63
|
-
import { sendMsgTool } from './tools/send-msg.js';
|
|
64
|
-
|
|
65
|
-
class MyPlugin extends OpenTabsPlugin {
|
|
66
|
-
readonly name = 'myapp';
|
|
67
|
-
readonly displayName = 'My App';
|
|
68
|
-
readonly description = 'Tools for My App';
|
|
69
|
-
readonly urlPatterns = ['*://myapp.com/*'];
|
|
70
|
-
readonly tools: ToolDefinition[] = [getDataTool, sendMsgTool];
|
|
71
|
-
|
|
72
|
-
async isReady(): Promise<boolean> {
|
|
73
|
-
// Return true when the user is authenticated and the app is loaded
|
|
74
|
-
return document.querySelector('.logged-in-indicator') !== null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export default new MyPlugin();
|
|
79
|
-
\`\`\`
|
|
80
|
-
|
|
81
|
-
### Required Members
|
|
82
|
-
|
|
83
|
-
| Member | Type | Purpose |
|
|
84
|
-
|--------|------|---------|
|
|
85
|
-
| \`name\` | \`string\` | Unique identifier (lowercase alphanumeric + hyphens) |
|
|
86
|
-
| \`displayName\` | \`string\` | Human-readable name shown in side panel |
|
|
87
|
-
| \`description\` | \`string\` | Brief plugin description |
|
|
88
|
-
| \`urlPatterns\` | \`string[]\` | Chrome match patterns for tab injection |
|
|
89
|
-
| \`tools\` | \`ToolDefinition[]\` | Array of tool definitions |
|
|
90
|
-
| \`isReady()\` | \`() => Promise<boolean>\` | Readiness probe — returns true when tab is ready for tool calls |
|
|
91
|
-
|
|
92
|
-
### Tab State Machine
|
|
93
|
-
|
|
94
|
-
| State | Condition |
|
|
95
|
-
|-------|-----------|
|
|
96
|
-
| \`closed\` | No browser tab matches the plugin's URL patterns |
|
|
97
|
-
| \`unavailable\` | Tab matches URL patterns but \`isReady()\` returns false |
|
|
98
|
-
| \`ready\` | Tab matches URL patterns and \`isReady()\` returns true |
|
|
99
|
-
|
|
100
|
-
## defineTool Factory
|
|
101
|
-
|
|
102
|
-
Each tool is defined with \`defineTool\`, which provides type inference:
|
|
103
|
-
|
|
104
|
-
\`\`\`typescript
|
|
105
|
-
import { z } from 'zod';
|
|
106
|
-
import { defineTool, fetchJSON } from '@opentabs-dev/plugin-sdk';
|
|
107
|
-
import type { ToolHandlerContext } from '@opentabs-dev/plugin-sdk';
|
|
108
|
-
|
|
109
|
-
export const getDataTool = defineTool({
|
|
110
|
-
name: 'get_data',
|
|
111
|
-
displayName: 'Get Data',
|
|
112
|
-
description: 'Retrieves data from the app. Returns the matching records.',
|
|
113
|
-
summary: 'Retrieve app data',
|
|
114
|
-
icon: 'database',
|
|
115
|
-
group: 'Data',
|
|
116
|
-
input: z.object({
|
|
117
|
-
query: z.string().describe('Search query string'),
|
|
118
|
-
limit: z.number().int().min(1).max(100).default(25).describe('Max results to return'),
|
|
119
|
-
}),
|
|
120
|
-
output: z.object({
|
|
121
|
-
results: z.array(z.object({
|
|
122
|
-
id: z.string(),
|
|
123
|
-
title: z.string(),
|
|
124
|
-
})),
|
|
125
|
-
total: z.number(),
|
|
126
|
-
}),
|
|
127
|
-
async handle(params, context?: ToolHandlerContext) {
|
|
128
|
-
const data = await fetchJSON<{ items: Array<{ id: string; title: string }>; total: number }>(
|
|
129
|
-
\`/api/data?q=\${encodeURIComponent(params.query)}&limit=\${params.limit}\`
|
|
130
|
-
);
|
|
131
|
-
return { results: data?.items ?? [], total: data?.total ?? 0 };
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
\`\`\`
|
|
135
|
-
|
|
136
|
-
### ToolDefinition Fields
|
|
137
|
-
|
|
138
|
-
| Field | Required | Description |
|
|
139
|
-
|-------|----------|-------------|
|
|
140
|
-
| \`name\` | Yes | Tool name (auto-prefixed with plugin name) |
|
|
141
|
-
| \`displayName\` | No | Human-readable name for side panel (auto-derived from name if omitted) |
|
|
142
|
-
| \`description\` | Yes | Shown to AI agents — be specific and include return value info |
|
|
143
|
-
| \`summary\` | No | Short UI summary (falls back to description) |
|
|
144
|
-
| \`icon\` | No | Lucide icon name in kebab-case (defaults to \`wrench\`) |
|
|
145
|
-
| \`group\` | No | Visual grouping in the side panel |
|
|
146
|
-
| \`input\` | Yes | Zod object schema for parameters |
|
|
147
|
-
| \`output\` | Yes | Zod schema for return value |
|
|
148
|
-
| \`handle\` | Yes | Async function — runs in page context. Second arg is optional \`ToolHandlerContext\` |
|
|
149
|
-
|
|
150
|
-
### Progress Reporting
|
|
151
|
-
|
|
152
|
-
Long-running tools can report progress via the optional \`context\` parameter:
|
|
153
|
-
|
|
154
|
-
\`\`\`typescript
|
|
155
|
-
async handle(params, context?: ToolHandlerContext) {
|
|
156
|
-
const items = await getItemList();
|
|
157
|
-
for (let i = 0; i < items.length; i++) {
|
|
158
|
-
context?.reportProgress({ progress: i + 1, total: items.length, message: \`Processing \${items[i].name}\` });
|
|
159
|
-
await processItem(items[i]);
|
|
160
|
-
}
|
|
161
|
-
return { processed: items.length };
|
|
162
|
-
}
|
|
163
|
-
\`\`\`
|
|
164
|
-
|
|
165
|
-
## SDK Utilities Reference
|
|
166
|
-
|
|
167
|
-
All utilities are imported from \`@opentabs-dev/plugin-sdk\`. They run in the page context.
|
|
168
|
-
|
|
169
|
-
### DOM
|
|
170
|
-
|
|
171
|
-
| Function | Signature | Description |
|
|
172
|
-
|----------|-----------|-------------|
|
|
173
|
-
| \`waitForSelector\` | \`<T extends Element>(selector, opts?) → Promise<T>\` | Waits for element to appear (MutationObserver, default 10s timeout) |
|
|
174
|
-
| \`waitForSelectorRemoval\` | \`(selector, opts?) → Promise<void>\` | Waits for element to be removed (default 10s timeout) |
|
|
175
|
-
| \`querySelectorAll\` | \`<T extends Element>(selector) → T[]\` | Returns real array instead of NodeList |
|
|
176
|
-
| \`getTextContent\` | \`(selector) → string \\| null\` | Trimmed textContent of first match |
|
|
177
|
-
| \`observeDOM\` | \`(selector, callback, opts?) → () => void\` | MutationObserver on element, returns cleanup function |
|
|
178
|
-
|
|
179
|
-
### Fetch
|
|
180
|
-
|
|
181
|
-
All fetch utilities use \`credentials: 'include'\` to leverage the page's authenticated session.
|
|
182
|
-
|
|
183
|
-
| Function | Signature | Description |
|
|
184
|
-
|----------|-----------|-------------|
|
|
185
|
-
| \`fetchFromPage\` | \`(url, init?) → Promise<Response>\` | Fetch with session cookies, 30s timeout, ToolError on non-ok |
|
|
186
|
-
| \`fetchJSON\` | \`<T>(url, init?, schema?) → Promise<T>\` | Fetch + JSON parse. Optional Zod schema validation |
|
|
187
|
-
| \`postJSON\` | \`<T>(url, body, init?, schema?) → Promise<T>\` | POST with JSON body + parse response |
|
|
188
|
-
| \`putJSON\` | \`<T>(url, body, init?, schema?) → Promise<T>\` | PUT with JSON body + parse response |
|
|
189
|
-
| \`patchJSON\` | \`<T>(url, body, init?, schema?) → Promise<T>\` | PATCH with JSON body + parse response |
|
|
190
|
-
| \`deleteJSON\` | \`<T>(url, init?, schema?) → Promise<T>\` | DELETE + parse response |
|
|
191
|
-
| \`postForm\` | \`<T>(url, body, init?, schema?) → Promise<T>\` | POST URL-encoded form (Record<string,string>) |
|
|
192
|
-
| \`postFormData\` | \`<T>(url, body, init?, schema?) → Promise<T>\` | POST multipart/form-data (FormData) |
|
|
193
|
-
|
|
194
|
-
### Storage
|
|
195
|
-
|
|
196
|
-
| Function | Signature | Description |
|
|
197
|
-
|----------|-----------|-------------|
|
|
198
|
-
| \`getLocalStorage\` | \`(key) → string \\| null\` | Safe localStorage read (null on SecurityError) |
|
|
199
|
-
| \`setLocalStorage\` | \`(key, value) → void\` | Safe localStorage write |
|
|
200
|
-
| \`removeLocalStorage\` | \`(key) → void\` | Safe localStorage remove |
|
|
201
|
-
| \`getSessionStorage\` | \`(key) → string \\| null\` | Safe sessionStorage read |
|
|
202
|
-
| \`setSessionStorage\` | \`(key, value) → void\` | Safe sessionStorage write |
|
|
203
|
-
| \`removeSessionStorage\` | \`(key) → void\` | Safe sessionStorage remove |
|
|
204
|
-
| \`getCookie\` | \`(name) → string \\| null\` | Parse cookie by name from document.cookie |
|
|
205
|
-
|
|
206
|
-
### Page State
|
|
207
|
-
|
|
208
|
-
| Function | Signature | Description |
|
|
209
|
-
|----------|-----------|-------------|
|
|
210
|
-
| \`getPageGlobal\` | \`(path) → unknown\` | Safe deep property access on globalThis via dot-notation |
|
|
211
|
-
| \`getCurrentUrl\` | \`() → string\` | Returns window.location.href |
|
|
212
|
-
| \`getPageTitle\` | \`() → string\` | Returns document.title |
|
|
213
|
-
|
|
214
|
-
### Timing
|
|
215
|
-
|
|
216
|
-
| Function | Signature | Description |
|
|
217
|
-
|----------|-----------|-------------|
|
|
218
|
-
| \`retry\` | \`<T>(fn, opts?) → Promise<T>\` | Retry with configurable attempts (3), delay (1s), backoff, AbortSignal |
|
|
219
|
-
| \`sleep\` | \`(ms, opts?) → Promise<void>\` | Promisified setTimeout with optional AbortSignal |
|
|
220
|
-
| \`waitUntil\` | \`(predicate, opts?) → Promise<void>\` | Poll predicate at interval (200ms) until true, timeout (10s) |
|
|
221
|
-
|
|
222
|
-
### Logging
|
|
223
|
-
|
|
224
|
-
| Function | Description |
|
|
225
|
-
|----------|-------------|
|
|
226
|
-
| \`log.debug(message, ...args)\` | Debug level |
|
|
227
|
-
| \`log.info(message, ...args)\` | Info level |
|
|
228
|
-
| \`log.warn(message, ...args)\` | Warning level |
|
|
229
|
-
| \`log.error(message, ...args)\` | Error level |
|
|
230
|
-
|
|
231
|
-
Log 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.
|
|
232
|
-
|
|
233
|
-
## ToolError Factories
|
|
234
|
-
|
|
235
|
-
Use static factory methods for structured errors. The dispatch chain propagates metadata (category, retryable, retryAfterMs) to AI clients.
|
|
236
|
-
|
|
237
|
-
| Factory | Signature | Category | Retryable |
|
|
238
|
-
|---------|-----------|----------|-----------|
|
|
239
|
-
| \`ToolError.auth\` | \`(message, code?) → ToolError\` | \`auth\` | No |
|
|
240
|
-
| \`ToolError.notFound\` | \`(message, code?) → ToolError\` | \`not_found\` | No |
|
|
241
|
-
| \`ToolError.rateLimited\` | \`(message, retryAfterMs?, code?) → ToolError\` | \`rate_limit\` | Yes |
|
|
242
|
-
| \`ToolError.validation\` | \`(message, code?) → ToolError\` | \`validation\` | No |
|
|
243
|
-
| \`ToolError.timeout\` | \`(message, code?) → ToolError\` | \`timeout\` | Yes |
|
|
244
|
-
| \`ToolError.internal\` | \`(message, code?) → ToolError\` | \`internal\` | No |
|
|
245
|
-
|
|
246
|
-
\`\`\`typescript
|
|
247
|
-
import { ToolError, fetchJSON } from '@opentabs-dev/plugin-sdk';
|
|
248
|
-
|
|
249
|
-
// Auth errors are automatically thrown by fetchJSON on 401/403
|
|
250
|
-
// For manual auth checks:
|
|
251
|
-
const token = getPageGlobal('app.auth.token') as string | undefined;
|
|
252
|
-
if (!token) throw ToolError.auth('User is not logged in');
|
|
253
|
-
|
|
254
|
-
// For domain-specific errors with custom codes:
|
|
255
|
-
throw ToolError.notFound('Channel not found', 'CHANNEL_NOT_FOUND');
|
|
256
|
-
throw ToolError.rateLimited('Slow down', 5000, 'SLACK_RATE_LIMITED');
|
|
257
|
-
\`\`\`
|
|
258
|
-
|
|
259
|
-
## Zod Schema Rules
|
|
260
|
-
|
|
261
|
-
Schemas are serialized to JSON Schema via \`z.toJSONSchema()\` for MCP registration. Follow these rules:
|
|
262
|
-
|
|
263
|
-
1. **Never use \`.transform()\`** — transforms cannot be represented in JSON Schema. Normalize input in the handler.
|
|
264
|
-
2. **Avoid \`.pipe()\`, \`.preprocess()\`, and effects** — these are runtime-only and break serialization.
|
|
265
|
-
3. **\`.refine()\` callbacks must never throw** — Zod 4 runs refine even on invalid base values. Wrap throwing code in try-catch.
|
|
266
|
-
4. **Use \`.describe()\` on every field** — descriptions are shown to AI agents in the tool schema.
|
|
267
|
-
5. **Keep schemas declarative** — primitives, objects, arrays, unions, literals, enums, optional, default.
|
|
268
|
-
|
|
269
|
-
## Lifecycle Hooks
|
|
270
|
-
|
|
271
|
-
Optional methods on \`OpenTabsPlugin\` — implement only what you need:
|
|
272
|
-
|
|
273
|
-
| Hook | Signature | When Called |
|
|
274
|
-
|------|-----------|------------|
|
|
275
|
-
| \`onActivate\` | \`() → void\` | After adapter registered on \`globalThis.__openTabs.adapters\` |
|
|
276
|
-
| \`onDeactivate\` | \`() → void\` | Before adapter removal (fires before \`teardown\`) |
|
|
277
|
-
| \`onNavigate\` | \`(url: string) → void\` | On in-page URL changes (pushState, replaceState, popstate, hashchange) |
|
|
278
|
-
| \`onToolInvocationStart\` | \`(toolName: string) → void\` | Before each \`tool.handle()\` |
|
|
279
|
-
| \`onToolInvocationEnd\` | \`(toolName: string, success: boolean, durationMs: number) → void\` | After each \`tool.handle()\` |
|
|
280
|
-
| \`teardown\` | \`() → void\` | Before re-injection on plugin update |
|
|
281
|
-
|
|
282
|
-
Errors in hooks are caught and logged — they do not affect tool execution.
|
|
283
|
-
|
|
284
|
-
## isReady() Polling Pattern
|
|
285
|
-
|
|
286
|
-
The extension polls \`isReady()\` to determine tab state. Common patterns:
|
|
287
|
-
|
|
288
|
-
\`\`\`typescript
|
|
289
|
-
// DOM-based: check for a logged-in indicator
|
|
290
|
-
async isReady(): Promise<boolean> {
|
|
291
|
-
return document.querySelector('[data-testid="user-menu"]') !== null;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Global-based: check for auth token in window globals
|
|
295
|
-
async isReady(): Promise<boolean> {
|
|
296
|
-
return getPageGlobal('app.auth.token') !== undefined;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// API-based: verify session with a lightweight request
|
|
300
|
-
async isReady(): Promise<boolean> {
|
|
301
|
-
try {
|
|
302
|
-
await fetchJSON('/api/me');
|
|
303
|
-
return true;
|
|
304
|
-
} catch {
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
\`\`\`
|
|
309
|
-
|
|
310
|
-
## Auth Token Extraction
|
|
311
|
-
|
|
312
|
-
Plugins extract auth from the page — never ask users for credentials.
|
|
313
|
-
|
|
314
|
-
\`\`\`typescript
|
|
315
|
-
// From window globals (Slack pattern)
|
|
316
|
-
const token = getPageGlobal('TS.boot_data.api_token') as string | undefined;
|
|
317
|
-
if (!token) throw ToolError.auth('Not logged in');
|
|
318
|
-
|
|
319
|
-
// From localStorage
|
|
320
|
-
const token = getLocalStorage('auth_token');
|
|
321
|
-
if (!token) throw ToolError.auth('No auth token found');
|
|
322
|
-
|
|
323
|
-
// From cookies (session-based auth)
|
|
324
|
-
const session = getCookie('session_id');
|
|
325
|
-
if (!session) throw ToolError.auth('No session cookie');
|
|
326
|
-
|
|
327
|
-
// Cache on globalThis to avoid repeated extraction
|
|
328
|
-
const CACHE_KEY = '__opentabs_myapp_token';
|
|
329
|
-
function getToken(): string {
|
|
330
|
-
const cached = (globalThis as Record<string, unknown>)[CACHE_KEY] as string | undefined;
|
|
331
|
-
if (cached) return cached;
|
|
332
|
-
const token = getPageGlobal('app.token') as string | undefined;
|
|
333
|
-
if (!token) throw ToolError.auth('Not authenticated');
|
|
334
|
-
(globalThis as Record<string, unknown>)[CACHE_KEY] = token;
|
|
335
|
-
return token;
|
|
336
|
-
}
|
|
337
|
-
\`\`\`
|
|
338
|
-
|
|
339
|
-
## Build and Test Workflow
|
|
340
|
-
|
|
341
|
-
\`\`\`bash
|
|
342
|
-
# Build the plugin (generates dist/adapter.iife.js and dist/tools.json)
|
|
343
|
-
npx opentabs-plugin build
|
|
344
|
-
# Or if installed globally:
|
|
345
|
-
opentabs-plugin build
|
|
346
|
-
|
|
347
|
-
# The build command notifies the running MCP server via POST /reload
|
|
348
|
-
# No server restart needed — plugin changes are picked up automatically
|
|
349
|
-
\`\`\`
|
|
350
|
-
|
|
351
|
-
### Testing During Development
|
|
352
|
-
|
|
353
|
-
1. Build the plugin: \`opentabs-plugin build\`
|
|
354
|
-
2. Open the target web app in Chrome
|
|
355
|
-
3. Verify plugin loaded: call \`plugin_list_tabs\` from your AI client
|
|
356
|
-
4. Test a tool: call any plugin tool (e.g., \`myapp_get_data\`)
|
|
357
|
-
5. Check logs: call \`extension_get_logs\` to see adapter injection and tool execution logs
|
|
358
|
-
|
|
359
|
-
### Scaffolding a New Plugin
|
|
360
|
-
|
|
361
|
-
\`\`\`bash
|
|
362
|
-
npx @opentabs-dev/create-plugin
|
|
363
|
-
# Or with the CLI installed:
|
|
364
|
-
opentabs plugin create
|
|
365
|
-
\`\`\`
|
|
366
|
-
|
|
367
|
-
## Publishing to npm
|
|
368
|
-
|
|
369
|
-
\`\`\`json
|
|
370
|
-
{
|
|
371
|
-
"name": "@scope/opentabs-plugin-myapp",
|
|
372
|
-
"opentabs": {
|
|
373
|
-
"name": "myapp",
|
|
374
|
-
"displayName": "My App",
|
|
375
|
-
"description": "Tools for My App",
|
|
376
|
-
"urlPatterns": ["*://myapp.com/*"]
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
\`\`\`
|
|
380
|
-
|
|
381
|
-
Package naming convention: \`opentabs-plugin-<name>\` or \`@scope/opentabs-plugin-<name>\`. The MCP server auto-discovers packages matching these patterns in global node_modules.
|
|
382
|
-
|
|
383
|
-
\`\`\`bash
|
|
384
|
-
npm publish
|
|
385
|
-
# Users install with:
|
|
386
|
-
opentabs plugin install myapp
|
|
387
|
-
\`\`\`
|
|
388
|
-
|
|
389
|
-
## Common Patterns
|
|
390
|
-
|
|
391
|
-
### API Wrapper
|
|
392
|
-
|
|
393
|
-
\`\`\`typescript
|
|
394
|
-
const API_BASE = '/api/v1';
|
|
395
|
-
|
|
396
|
-
async function apiGet<T>(path: string): Promise<T> {
|
|
397
|
-
const result = await fetchJSON<T>(\`\${API_BASE}\${path}\`);
|
|
398
|
-
if (result === undefined) throw ToolError.internal(\`Unexpected empty response from \${path}\`);
|
|
399
|
-
return result;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
async function apiPost<T>(path: string, body: unknown): Promise<T> {
|
|
403
|
-
const result = await postJSON<T>(\`\${API_BASE}\${path}\`, body);
|
|
404
|
-
if (result === undefined) throw ToolError.internal(\`Unexpected empty response from \${path}\`);
|
|
405
|
-
return result;
|
|
406
|
-
}
|
|
407
|
-
\`\`\`
|
|
408
|
-
|
|
409
|
-
### Waiting for App State
|
|
410
|
-
|
|
411
|
-
\`\`\`typescript
|
|
412
|
-
import { waitForSelector, waitUntil, getPageGlobal } from '@opentabs-dev/plugin-sdk';
|
|
413
|
-
|
|
414
|
-
// Wait for the app to finish loading before executing
|
|
415
|
-
await waitForSelector('.app-loaded');
|
|
416
|
-
|
|
417
|
-
// Wait for a specific global to be set
|
|
418
|
-
await waitUntil(() => getPageGlobal('app.initialized') === true);
|
|
419
|
-
\`\`\`
|
|
420
|
-
|
|
421
|
-
### Retrying Flaky Operations
|
|
422
|
-
|
|
423
|
-
\`\`\`typescript
|
|
424
|
-
import { retry, ToolError } from '@opentabs-dev/plugin-sdk';
|
|
425
|
-
|
|
426
|
-
const result = await retry(
|
|
427
|
-
() => fetchJSON<Data>('/api/flaky-endpoint'),
|
|
428
|
-
{ maxAttempts: 3, delay: 1000, backoff: true }
|
|
429
|
-
);
|
|
430
|
-
\`\`\`
|
|
431
|
-
|
|
432
|
-
## Core Principle: APIs Not DOM
|
|
433
|
-
|
|
434
|
-
Every tool must use the web app's own APIs — 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).
|
|
435
|
-
|
|
436
|
-
When an API is hard to discover, invest time reverse-engineering network traffic rather than falling back to DOM. The only acceptable DOM uses are:
|
|
437
|
-
- **\`isReady()\`** — checking auth indicators (e.g., a logged-in avatar)
|
|
438
|
-
- **URL hash navigation** — changing views via \`window.location.hash\`
|
|
439
|
-
- **Last-resort compose flows** — when no API exists for creating content (extremely rare)
|
|
440
|
-
|
|
441
|
-
## Token Persistence
|
|
442
|
-
|
|
443
|
-
Module-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.
|
|
444
|
-
|
|
445
|
-
Persist auth tokens to \`globalThis.__openTabs.tokenCache.<pluginName>\`, which survives adapter re-injection (the page itself is not reloaded — only the IIFE is re-executed).
|
|
446
|
-
|
|
447
|
-
\`\`\`typescript
|
|
448
|
-
const getPersistedToken = (): string | null => {
|
|
449
|
-
try {
|
|
450
|
-
const ns = (globalThis as Record<string, unknown>).__openTabs as
|
|
451
|
-
| Record<string, unknown>
|
|
452
|
-
| undefined;
|
|
453
|
-
const cache = ns?.tokenCache as
|
|
454
|
-
| Record<string, string | undefined>
|
|
455
|
-
| undefined;
|
|
456
|
-
return cache?.myPlugin ?? null;
|
|
457
|
-
} catch {
|
|
458
|
-
return null;
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
const setPersistedToken = (token: string): void => {
|
|
463
|
-
try {
|
|
464
|
-
const g = globalThis as Record<string, unknown>;
|
|
465
|
-
if (!g.__openTabs) g.__openTabs = {};
|
|
466
|
-
const ns = g.__openTabs as Record<string, unknown>;
|
|
467
|
-
if (!ns.tokenCache) ns.tokenCache = {};
|
|
468
|
-
const cache = ns.tokenCache as Record<string, string | undefined>;
|
|
469
|
-
cache.myPlugin = token;
|
|
470
|
-
} catch {}
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
const clearPersistedToken = (): void => {
|
|
474
|
-
try {
|
|
475
|
-
const ns = (globalThis as Record<string, unknown>).__openTabs as
|
|
476
|
-
| Record<string, unknown>
|
|
477
|
-
| undefined;
|
|
478
|
-
const cache = ns?.tokenCache as
|
|
479
|
-
| Record<string, string | undefined>
|
|
480
|
-
| undefined;
|
|
481
|
-
if (cache) cache.myPlugin = undefined;
|
|
482
|
-
} catch {}
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
// In getAuth():
|
|
486
|
-
const getAuth = (): Auth | null => {
|
|
487
|
-
const persisted = getPersistedToken();
|
|
488
|
-
if (persisted) return { token: persisted };
|
|
489
|
-
|
|
490
|
-
const raw = readLocalStorage('token');
|
|
491
|
-
if (!raw) return null;
|
|
492
|
-
setPersistedToken(raw);
|
|
493
|
-
return { token: raw };
|
|
494
|
-
};
|
|
495
|
-
\`\`\`
|
|
496
|
-
|
|
497
|
-
Always clear the persisted token on 401 responses to handle token rotation.
|
|
498
|
-
|
|
499
|
-
## Adapter Injection Timing
|
|
500
|
-
|
|
501
|
-
Adapters are injected at **two points** during page load:
|
|
502
|
-
|
|
503
|
-
1. **\`loading\`** — before page JavaScript runs. The adapter IIFE registers on \`globalThis.__openTabs\` and can read localStorage/cookies before the host app modifies them.
|
|
504
|
-
2. **\`complete\`** — after the page is fully loaded. The adapter is re-injected (idempotent) and \`isReady()\` is probed to determine tab state.
|
|
505
|
-
|
|
506
|
-
This means:
|
|
507
|
-
- \`isReady()\` may be called at both injection points. At \`loading\` time, page globals do not exist yet — return \`false\` gracefully. At \`complete\` time, everything is ready.
|
|
508
|
-
- Auth tokens from localStorage should be cached at \`loading\` time before the host app can delete them.
|
|
509
|
-
|
|
510
|
-
## Advanced Auth Patterns
|
|
511
|
-
|
|
512
|
-
### XHR/Fetch Interception
|
|
513
|
-
|
|
514
|
-
Some 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.
|
|
515
|
-
|
|
516
|
-
\`\`\`typescript
|
|
517
|
-
const origOpen = XMLHttpRequest.prototype.open;
|
|
518
|
-
const origSetHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
519
|
-
const origSend = XMLHttpRequest.prototype.send;
|
|
520
|
-
|
|
521
|
-
XMLHttpRequest.prototype.open = function (method: string, url: string) {
|
|
522
|
-
(this as Record<string, unknown>)._url = url;
|
|
523
|
-
(this as Record<string, unknown>)._method = method;
|
|
524
|
-
return origOpen.apply(this, arguments as unknown as Parameters<typeof origOpen>);
|
|
525
|
-
};
|
|
526
|
-
XMLHttpRequest.prototype.setRequestHeader = function (name: string, value: string) {
|
|
527
|
-
if (name.toLowerCase() === 'authorization') {
|
|
528
|
-
setPersistedToken(value); // Capture auth header
|
|
529
|
-
}
|
|
530
|
-
return origSetHeader.apply(this, arguments as unknown as Parameters<typeof origSetHeader>);
|
|
531
|
-
};
|
|
532
|
-
\`\`\`
|
|
533
|
-
|
|
534
|
-
Install 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.
|
|
535
|
-
|
|
536
|
-
### Cookie-Based Auth with CSRF
|
|
537
|
-
|
|
538
|
-
Many 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\`).
|
|
539
|
-
|
|
540
|
-
\`\`\`typescript
|
|
541
|
-
const csrfToken = getCookie('csrftoken');
|
|
542
|
-
const response = await fetch('/api/endpoint', {
|
|
543
|
-
method: 'POST',
|
|
544
|
-
headers: {
|
|
545
|
-
'Content-Type': 'application/json',
|
|
546
|
-
'X-CSRFToken': csrfToken ?? '',
|
|
547
|
-
},
|
|
548
|
-
body: JSON.stringify(payload),
|
|
549
|
-
credentials: 'include', // HttpOnly cookies sent automatically
|
|
550
|
-
});
|
|
551
|
-
\`\`\`
|
|
552
|
-
|
|
553
|
-
Check \`window.__initialData.csrfCookieName\` or similar bootstrap globals to discover the cookie name. GET requests work without the CSRF token.
|
|
554
|
-
|
|
555
|
-
### Opaque Auth Headers
|
|
556
|
-
|
|
557
|
-
Some apps compute cryptographic auth tokens via obfuscated JavaScript. These tokens cannot be generated — only captured and replayed. Use the XHR interceptor pattern above to capture them, then implement a polling wait:
|
|
558
|
-
|
|
559
|
-
\`\`\`typescript
|
|
560
|
-
const waitForToken = async (): Promise<string> => {
|
|
561
|
-
for (let i = 0; i < 50; i++) {
|
|
562
|
-
const token = getPersistedToken();
|
|
563
|
-
if (token) return token;
|
|
564
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
565
|
-
}
|
|
566
|
-
throw ToolError.auth('Auth token not captured — try refreshing the page');
|
|
567
|
-
};
|
|
568
|
-
\`\`\`
|
|
569
|
-
|
|
570
|
-
If 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.
|
|
571
|
-
|
|
572
|
-
### Extension/Programmatic APIs
|
|
573
|
-
|
|
574
|
-
When standard API paths are blocked (undocumented crypto tokens, deprecated endpoints), complex web apps often expose higher-level programmatic interfaces:
|
|
575
|
-
|
|
576
|
-
- Internal extension APIs on \`window\` (compose, send, draft management)
|
|
577
|
-
- JavaScript-exposed infrastructure for accessibility or testing
|
|
578
|
-
- \`webpackChunk\`-based module access to internal stores
|
|
579
|
-
|
|
580
|
-
Discovery: 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.
|
|
581
|
-
|
|
582
|
-
### API Deprecation
|
|
583
|
-
|
|
584
|
-
Internal 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.
|
|
585
|
-
|
|
586
|
-
## CSP Considerations
|
|
587
|
-
|
|
588
|
-
The 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.
|
|
589
|
-
|
|
590
|
-
**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:
|
|
591
|
-
|
|
592
|
-
\`\`\`typescript
|
|
593
|
-
const text = html.replace(/<[^>]+>/g, '');
|
|
594
|
-
\`\`\`
|
|
595
|
-
`;
|
|
596
|
-
//# sourceMappingURL=plugin-development.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-development.js","sourceRoot":"","sources":["../../src/resources/plugin-development.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAEjD,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAilBzC,CAAC"}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/** Quick Start Guide resource content. */
|
|
2
|
-
export declare const QUICK_START_CONTENT = "# OpenTabs Quick Start Guide\n\n## What is OpenTabs?\n\nOpenTabs is a platform that gives AI agents access to web applications through the user's authenticated browser session. It consists of:\n\n- **MCP Server** \u2014 runs on localhost, serves tools to AI clients via Streamable HTTP\n- **Chrome Extension** \u2014 injects plugin adapters into matching browser tabs, relays tool calls\n- **Plugin SDK** \u2014 allows anyone to create plugins as standalone npm packages\n\nWhen connected, your AI client gets browser tools (tab management, screenshots, DOM interaction, network capture) and plugin tools (e.g., `slack_send_message`, `github_list_repos`) that operate in the user's authenticated context.\n\n## Installation\n\n```bash\nnpm install -g @opentabs-dev/cli\n```\n\n## Starting the Server\n\n```bash\nopentabs start\n```\n\nOn first run, this:\n1. Creates `~/.opentabs/` (config, logs, extension files)\n2. Generates a WebSocket auth secret at `~/.opentabs/extension/auth.json`\n3. Prints MCP client configuration blocks for Claude Code, Cursor, and Windsurf\n4. Starts the MCP server on `http://127.0.0.1:9515/mcp`\n\nTo re-display the configuration blocks later:\n\n```bash\nopentabs start --show-config\n```\n\n## Loading the Chrome Extension\n\n1. Open `chrome://extensions/` in Chrome\n2. Enable **Developer mode** (top-right toggle)\n3. Click **Load unpacked** and select `~/.opentabs/extension`\n\nThe extension icon appears in the toolbar. Click it to open the side panel showing plugin states and tool permissions.\n\n## Configuring Your MCP Client\n\nGet the auth secret:\n\n```bash\nopentabs config show --json --show-secret | jq -r .secret\n```\n\n### Claude Code\n\nCLI method (recommended):\n\n```bash\nclaude mcp add --transport http opentabs http://127.0.0.1:9515/mcp \\\n --header \"Authorization: Bearer YOUR_SECRET_HERE\"\n```\n\nOr merge into `~/.claude.json`:\n\n```json\n{\n \"mcpServers\": {\n \"opentabs\": {\n \"type\": \"streamable-http\",\n \"url\": \"http://127.0.0.1:9515/mcp\",\n \"headers\": {\n \"Authorization\": \"Bearer YOUR_SECRET_HERE\"\n }\n }\n }\n}\n```\n\n### Cursor\n\nAdd to `.cursor/mcp.json`:\n\n```json\n{\n \"mcpServers\": {\n \"opentabs\": {\n \"type\": \"http\",\n \"url\": \"http://127.0.0.1:9515/mcp\",\n \"headers\": {\n \"Authorization\": \"Bearer YOUR_SECRET_HERE\"\n }\n }\n }\n}\n```\n\n### Windsurf\n\nAdd to `~/.codeium/windsurf/mcp_config.json`:\n\n```json\n{\n \"mcpServers\": {\n \"opentabs\": {\n \"serverUrl\": \"http://127.0.0.1:9515/mcp\",\n \"headers\": {\n \"Authorization\": \"Bearer YOUR_SECRET_HERE\"\n }\n }\n }\n}\n```\n\n### OpenCode\n\nAdd to `opencode.json` in the project root:\n\n```json\n{\n \"mcp\": {\n \"opentabs\": {\n \"type\": \"remote\",\n \"url\": \"http://127.0.0.1:9515/mcp\",\n \"headers\": {\n \"Authorization\": \"Bearer YOUR_SECRET_HERE\"\n }\n }\n }\n}\n```\n\n## Installing a Plugin\n\n```bash\nopentabs plugin search # Browse available plugins\nopentabs plugin install <name> # Install (e.g., opentabs plugin install slack)\n```\n\nAfter installing, open the target web app in Chrome (e.g., `app.slack.com` for Slack). The extension detects the matching tab and loads the plugin adapter.\n\n## Plugin Review Flow\n\nPlugins start with permission `'off'` and must be reviewed before use. When you call a tool on an unreviewed plugin, the error response guides you through the review:\n\n1. Call `plugin_inspect` with the plugin name to retrieve the adapter source code and a review token\n2. Review the code for security (the response includes review guidance)\n3. If the code is safe, call `plugin_mark_reviewed` with the review token and desired permission (`'ask'` or `'auto'`)\n4. The plugin is now active \u2014 its tools are available\n\nWhen a plugin updates to a new version, its permission resets to `'off'` and requires re-review.\n\n## Permission Model\n\nEvery tool has a 3-state permission:\n\n| Permission | Behavior |\n|------------|----------|\n| `'off'` | Disabled \u2014 tool call returns an error |\n| `'ask'` | Requires human approval via the side panel dialog |\n| `'auto'` | Executes immediately without user confirmation |\n\nConfigure permissions via CLI:\n\n```bash\nopentabs config set plugin-permission.<plugin> ask\nopentabs config set tool-permission.<plugin>.<tool> auto\n```\n\nTo bypass all permission checks (development only):\n\n```bash\nOPENTABS_DANGEROUSLY_SKIP_PERMISSIONS=1 opentabs start\n```\n\n## Available Tool Categories\n\n### Plugin Tools (`<plugin>_<tool>`)\nExecute inside the web page context using the user's authenticated browser session. Each plugin exposes domain-specific tools (e.g., `slack_send_message`, `github_create_issue`).\n\n### Browser Tools (`browser_*`) \u2014 40 built-in tools\nGeneral-purpose tools organized by category:\n- **Tab Management** \u2014 open, close, list, switch tabs\n- **Content Retrieval** \u2014 read page content, HTML, take screenshots\n- **DOM Interaction** \u2014 click elements, type text, query selectors\n- **Scroll & Navigation** \u2014 scroll, navigate, go back/forward\n- **Storage & Cookies** \u2014 read/write localStorage, sessionStorage, cookies\n- **Network Capture** \u2014 capture and inspect network requests, WebSocket frames, HAR export\n- **Console** \u2014 read browser console logs\n- **Site Analysis** \u2014 comprehensive analysis of a web page for plugin development\n\n### Extension Tools (`extension_*`)\nDiagnostics: extension state, logs, adapter injection status, WebSocket connectivity.\n\n## Multi-Tab Targeting\n\nWhen multiple tabs match a plugin, use `plugin_list_tabs` to discover available tabs and their IDs. Pass the optional `tabId` parameter to any plugin tool to target a specific tab. Without `tabId`, the platform auto-selects the best-ranked tab.\n\n## Verifying the Setup\n\n```bash\nopentabs status # Check server, extension, and plugin status\nopentabs doctor # Run diagnostics and suggest fixes\n```\n\nFrom your AI client, you can also:\n1. Fetch `opentabs://status` to get a JSON snapshot of the server state\n2. Call `extension_get_state` to verify the Chrome extension is connected\n3. Call `plugin_list_tabs` to see which plugin tabs are ready\n";
|
|
3
|
-
//# sourceMappingURL=quick-start.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"quick-start.d.ts","sourceRoot":"","sources":["../../src/resources/quick-start.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAE1C,eAAO,MAAM,mBAAmB,qtMA+M/B,CAAC"}
|