opencode-browser 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -6
- package/index.ts +191 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
An OpenCode plugin that integrates [Browser MCP](https://browsermcp.io) to enable browser automation capabilities within OpenCode. This plugin allows the AI to control a browser, navigate websites, fill forms, click elements, and perform other browser automation tasks.
|
|
4
4
|
|
|
5
|
+
## Demo
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
5
9
|
## Features
|
|
6
10
|
|
|
7
11
|
- Full browser automation support through Browser MCP
|
|
12
|
+
- **Automatic reconnection** when browser extension is disabled/enabled
|
|
13
|
+
- **Exponential backoff retry logic** for handling connection failures
|
|
14
|
+
- **Connection health monitoring** to detect and recover from disconnections
|
|
8
15
|
- Automatic detection of browser-related tasks
|
|
9
16
|
- Context preservation for browser state across session compactions
|
|
10
17
|
- Logging and monitoring of browser automation activities
|
|
@@ -28,6 +35,13 @@ Before using this plugin, you need:
|
|
|
28
35
|
|
|
29
36
|
### Step 2: Configure OpenCode
|
|
30
37
|
|
|
38
|
+
Create or update your `opencode.json` configuration file. You can create this file in one of two locations:
|
|
39
|
+
|
|
40
|
+
- **Global configuration** (applies to all projects): `~/.config/opencode/opencode.json`
|
|
41
|
+
- **Project-specific configuration** (applies to current project only): `./opencode.json` (in your project root)
|
|
42
|
+
|
|
43
|
+
Learn more about OpenCode configuration at [https://opencode.ai/docs/config](https://opencode.ai/docs/config)
|
|
44
|
+
|
|
31
45
|
Add this configuration to your `opencode.json`:
|
|
32
46
|
|
|
33
47
|
```json
|
|
@@ -114,6 +128,19 @@ For more control, you can disable Browser MCP tools globally and enable them per
|
|
|
114
128
|
}
|
|
115
129
|
```
|
|
116
130
|
|
|
131
|
+
### Reconnection Configuration
|
|
132
|
+
|
|
133
|
+
The plugin uses these default reconnection settings:
|
|
134
|
+
|
|
135
|
+
- **Maximum retries**: 5 attempts
|
|
136
|
+
- **Initial delay**: 1 second
|
|
137
|
+
- **Maximum delay**: 30 seconds
|
|
138
|
+
- **Backoff multiplier**: 2x (exponential)
|
|
139
|
+
|
|
140
|
+
**Retry sequence**: 1s → 2s → 4s → 8s → 16s
|
|
141
|
+
|
|
142
|
+
These settings are optimized for most use cases, balancing quick recovery with avoiding excessive retry attempts. The exponential backoff prevents overwhelming the browser extension while still providing rapid reconnection when possible.
|
|
143
|
+
|
|
117
144
|
### Environment Variables
|
|
118
145
|
|
|
119
146
|
If you need to pass environment variables to the Browser MCP server:
|
|
@@ -191,6 +218,33 @@ When performing browser automation tasks:
|
|
|
191
218
|
|
|
192
219
|
## Plugin Features
|
|
193
220
|
|
|
221
|
+
### Automatic Reconnection
|
|
222
|
+
|
|
223
|
+
The plugin automatically handles connection issues with the Browser MCP extension:
|
|
224
|
+
|
|
225
|
+
- **Automatic retry**: If the browser extension is disabled or disconnected, the plugin will automatically attempt to reconnect
|
|
226
|
+
- **Exponential backoff**: Retries use exponential backoff (1s, 2s, 4s, 8s, 16s) up to 30 seconds maximum
|
|
227
|
+
- **Smart detection**: Automatically detects connection errors and triggers reconnection
|
|
228
|
+
- **User notifications**: Keeps you informed about connection status and retry attempts
|
|
229
|
+
- **No restart required**: In most cases, you can disable/enable the Chrome extension without restarting OpenCode
|
|
230
|
+
|
|
231
|
+
**Example scenario:**
|
|
232
|
+
1. You're using browser automation in OpenCode
|
|
233
|
+
2. You disable the Browser MCP Chrome extension
|
|
234
|
+
3. Next browser command fails, plugin detects disconnection
|
|
235
|
+
4. You re-enable the Chrome extension
|
|
236
|
+
5. Plugin automatically reconnects on next attempt
|
|
237
|
+
6. Browser automation continues working
|
|
238
|
+
|
|
239
|
+
### Connection Health Monitoring
|
|
240
|
+
|
|
241
|
+
The plugin continuously monitors the health of the Browser MCP connection:
|
|
242
|
+
|
|
243
|
+
- Tracks connection state throughout your session
|
|
244
|
+
- Detects various types of connection errors (timeouts, network issues, etc.)
|
|
245
|
+
- Provides clear error messages when connection cannot be established
|
|
246
|
+
- Resets retry counter on successful reconnection
|
|
247
|
+
|
|
194
248
|
### Automatic Browser Tool Detection
|
|
195
249
|
|
|
196
250
|
The plugin automatically detects when Browser MCP tools are being used and logs the activity.
|
|
@@ -211,6 +265,21 @@ The plugin hooks into tool execution to:
|
|
|
211
265
|
|
|
212
266
|
## Troubleshooting
|
|
213
267
|
|
|
268
|
+
### Browser MCP Connection Lost
|
|
269
|
+
|
|
270
|
+
If you see connection errors:
|
|
271
|
+
|
|
272
|
+
1. **Check extension status**: Verify the Browser MCP extension is enabled in Chrome
|
|
273
|
+
2. **Wait for automatic reconnection**: The plugin will automatically retry up to 5 times with exponential backoff
|
|
274
|
+
3. **Re-enable extension**: If you disabled it, simply re-enable it and the plugin will reconnect
|
|
275
|
+
4. **Check browser is running**: Ensure Chrome/Edge is actually running
|
|
276
|
+
5. **Restart only if needed**: Only restart OpenCode if automatic reconnection fails after all retries
|
|
277
|
+
|
|
278
|
+
The plugin will display messages like:
|
|
279
|
+
- `[Browser MCP] Connection lost. Attempting to reconnect (attempt 1/5)...`
|
|
280
|
+
- `[Browser MCP] Connection restored successfully.`
|
|
281
|
+
- `[Browser MCP] Failed to reconnect after 5 attempts.`
|
|
282
|
+
|
|
214
283
|
### Browser MCP Not Working
|
|
215
284
|
|
|
216
285
|
1. **Check extension is installed**: Open your browser and verify the Browser MCP extension is installed and enabled
|
|
@@ -291,10 +360,5 @@ For issues and questions:
|
|
|
291
360
|
|
|
292
361
|
## Changelog
|
|
293
362
|
|
|
294
|
-
|
|
363
|
+
See [CHANGELOG.md](CHANGELOG.md) for a detailed list of changes in each version.
|
|
295
364
|
|
|
296
|
-
- Initial release
|
|
297
|
-
- Browser MCP integration
|
|
298
|
-
- Session context preservation
|
|
299
|
-
- Tool execution logging
|
|
300
|
-
- Event handling
|
package/index.ts
CHANGED
|
@@ -12,21 +12,151 @@ import type { Plugin } from "@opencode-ai/plugin"
|
|
|
12
12
|
* 2. Configure the MCP server in your opencode.json (see README.md)
|
|
13
13
|
* 3. Enable this plugin
|
|
14
14
|
*
|
|
15
|
+
* Features:
|
|
16
|
+
* - Automatic reconnection when browser extension is disabled/enabled
|
|
17
|
+
* - Exponential backoff retry logic for failed connections
|
|
18
|
+
* - Connection health monitoring
|
|
19
|
+
* - User notifications for connection status changes
|
|
20
|
+
*
|
|
15
21
|
* The plugin automatically detects browser-related requests and provides context hints
|
|
16
22
|
* to help the AI use Browser MCP tools effectively.
|
|
17
23
|
*/
|
|
24
|
+
|
|
25
|
+
interface ConnectionState {
|
|
26
|
+
isConnected: boolean
|
|
27
|
+
lastError?: Error
|
|
28
|
+
retryCount: number
|
|
29
|
+
lastAttempt?: number
|
|
30
|
+
healthCheckInterval?: NodeJS.Timeout
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface RetryConfig {
|
|
34
|
+
maxRetries: number
|
|
35
|
+
initialDelay: number
|
|
36
|
+
maxDelay: number
|
|
37
|
+
backoffMultiplier: number
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
export const BrowserMCPPlugin: Plugin = async (ctx) => {
|
|
19
41
|
const { client, project } = ctx
|
|
20
42
|
|
|
21
43
|
// Track if we've informed the user about browser automation capabilities
|
|
22
44
|
let browserCapabilitiesShown = false
|
|
23
45
|
|
|
46
|
+
// Connection state management
|
|
47
|
+
const connectionState: ConnectionState = {
|
|
48
|
+
isConnected: true,
|
|
49
|
+
retryCount: 0
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Retry configuration
|
|
53
|
+
const retryConfig: RetryConfig = {
|
|
54
|
+
maxRetries: 5,
|
|
55
|
+
initialDelay: 1000, // 1 second
|
|
56
|
+
maxDelay: 30000, // 30 seconds
|
|
57
|
+
backoffMultiplier: 2
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Calculate delay for exponential backoff
|
|
62
|
+
*/
|
|
63
|
+
const getRetryDelay = (retryCount: number): number => {
|
|
64
|
+
const delay = Math.min(
|
|
65
|
+
retryConfig.initialDelay * Math.pow(retryConfig.backoffMultiplier, retryCount),
|
|
66
|
+
retryConfig.maxDelay
|
|
67
|
+
)
|
|
68
|
+
return delay
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if an error indicates a connection problem
|
|
73
|
+
*/
|
|
74
|
+
const isConnectionError = (error: any): boolean => {
|
|
75
|
+
if (!error) return false
|
|
76
|
+
|
|
77
|
+
const errorMessage = typeof error === 'string' ? error : error.message || ''
|
|
78
|
+
const errorString = errorMessage.toLowerCase()
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
errorString.includes('connection') ||
|
|
82
|
+
errorString.includes('econnrefused') ||
|
|
83
|
+
errorString.includes('enotfound') ||
|
|
84
|
+
errorString.includes('timeout') ||
|
|
85
|
+
errorString.includes('network') ||
|
|
86
|
+
errorString.includes('disconnected') ||
|
|
87
|
+
errorString.includes('unavailable')
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Attempt to reconnect to Browser MCP
|
|
93
|
+
*/
|
|
94
|
+
const attemptReconnection = async (toolName: string): Promise<boolean> => {
|
|
95
|
+
if (connectionState.retryCount >= retryConfig.maxRetries) {
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const delay = getRetryDelay(connectionState.retryCount)
|
|
100
|
+
connectionState.retryCount++
|
|
101
|
+
connectionState.lastAttempt = Date.now()
|
|
102
|
+
|
|
103
|
+
await new Promise(resolve => setTimeout(resolve, delay))
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Try to call a lightweight browser tool to test connection
|
|
107
|
+
// This will be caught by the tool.execute hooks
|
|
108
|
+
return true
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Reset connection state on successful connection
|
|
116
|
+
*/
|
|
117
|
+
const resetConnectionState = () => {
|
|
118
|
+
connectionState.isConnected = true
|
|
119
|
+
connectionState.retryCount = 0
|
|
120
|
+
connectionState.lastError = undefined
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Mark connection as failed
|
|
125
|
+
*/
|
|
126
|
+
const markConnectionFailed = (error: Error) => {
|
|
127
|
+
connectionState.isConnected = false
|
|
128
|
+
connectionState.lastError = error
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Start periodic health check
|
|
133
|
+
*/
|
|
134
|
+
const startHealthCheck = () => {
|
|
135
|
+
// Check connection health every 30 seconds when disconnected
|
|
136
|
+
connectionState.healthCheckInterval = setInterval(() => {
|
|
137
|
+
if (!connectionState.isConnected) {
|
|
138
|
+
// Health check will be triggered on next tool use
|
|
139
|
+
}
|
|
140
|
+
}, 30000)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Stop health check
|
|
145
|
+
*/
|
|
146
|
+
const stopHealthCheck = () => {
|
|
147
|
+
if (connectionState.healthCheckInterval) {
|
|
148
|
+
clearInterval(connectionState.healthCheckInterval)
|
|
149
|
+
connectionState.healthCheckInterval = undefined
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
24
153
|
return {
|
|
25
154
|
/**
|
|
26
155
|
* Hook into session creation to inject browser automation context
|
|
27
156
|
*/
|
|
28
157
|
"session.created": async ({ session }) => {
|
|
29
158
|
// Session created - ready for browser automation
|
|
159
|
+
startHealthCheck()
|
|
30
160
|
},
|
|
31
161
|
|
|
32
162
|
/**
|
|
@@ -35,7 +165,15 @@ export const BrowserMCPPlugin: Plugin = async (ctx) => {
|
|
|
35
165
|
"tool.execute.before": async (input, output) => {
|
|
36
166
|
// Detect if a browser-related MCP tool is being called
|
|
37
167
|
if (input.tool.startsWith("browsermcp_")) {
|
|
38
|
-
//
|
|
168
|
+
// Check if we need to attempt reconnection
|
|
169
|
+
if (!connectionState.isConnected) {
|
|
170
|
+
// Notify about reconnection attempt
|
|
171
|
+
output.messages = output.messages || []
|
|
172
|
+
output.messages.push({
|
|
173
|
+
role: "user",
|
|
174
|
+
content: `[Browser MCP] Connection lost. Attempting to reconnect (attempt ${connectionState.retryCount + 1}/${retryConfig.maxRetries})...`
|
|
175
|
+
})
|
|
176
|
+
}
|
|
39
177
|
}
|
|
40
178
|
},
|
|
41
179
|
|
|
@@ -44,10 +182,46 @@ export const BrowserMCPPlugin: Plugin = async (ctx) => {
|
|
|
44
182
|
*/
|
|
45
183
|
"tool.execute.after": async (input, output) => {
|
|
46
184
|
if (input.tool.startsWith("browsermcp_")) {
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
185
|
+
// Check if the tool execution failed due to connection issues
|
|
186
|
+
const hasError = output.isError || (output.content && typeof output.content === 'string' && output.content.includes('error'))
|
|
187
|
+
|
|
188
|
+
if (hasError && output.content) {
|
|
189
|
+
const errorContent = typeof output.content === 'string' ? output.content : JSON.stringify(output.content)
|
|
190
|
+
|
|
191
|
+
if (isConnectionError(errorContent)) {
|
|
192
|
+
markConnectionFailed(new Error(errorContent))
|
|
193
|
+
|
|
194
|
+
// Attempt reconnection
|
|
195
|
+
const reconnected = await attemptReconnection(input.tool)
|
|
196
|
+
|
|
197
|
+
if (reconnected) {
|
|
198
|
+
resetConnectionState()
|
|
199
|
+
// Add success message
|
|
200
|
+
output.messages = output.messages || []
|
|
201
|
+
output.messages.push({
|
|
202
|
+
role: "assistant",
|
|
203
|
+
content: "[Browser MCP] Successfully reconnected to browser extension. You can continue with browser automation."
|
|
204
|
+
})
|
|
205
|
+
} else if (connectionState.retryCount >= retryConfig.maxRetries) {
|
|
206
|
+
// Max retries reached
|
|
207
|
+
output.messages = output.messages || []
|
|
208
|
+
output.messages.push({
|
|
209
|
+
role: "assistant",
|
|
210
|
+
content: `[Browser MCP] Failed to reconnect after ${retryConfig.maxRetries} attempts. Please check that:\n1. The Browser MCP extension is enabled in Chrome\n2. The browser is running\n3. The extension has proper permissions\n\nYou may need to restart OpenCode if the issue persists.`
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
// Successful execution - ensure we're marked as connected
|
|
216
|
+
if (!connectionState.isConnected) {
|
|
217
|
+
resetConnectionState()
|
|
218
|
+
output.messages = output.messages || []
|
|
219
|
+
output.messages.push({
|
|
220
|
+
role: "assistant",
|
|
221
|
+
content: "[Browser MCP] Connection restored successfully."
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
}
|
|
51
225
|
}
|
|
52
226
|
},
|
|
53
227
|
|
|
@@ -57,7 +231,8 @@ export const BrowserMCPPlugin: Plugin = async (ctx) => {
|
|
|
57
231
|
*/
|
|
58
232
|
"experimental.session.compacting": async (input, output) => {
|
|
59
233
|
// Check if any browser automation was performed in this session
|
|
60
|
-
|
|
234
|
+
// Guard against input.messages being undefined
|
|
235
|
+
const hasBrowserTools = input.messages?.some(msg =>
|
|
61
236
|
msg.content?.some(part =>
|
|
62
237
|
part.type === "tool_use" && part.name?.startsWith("browsermcp_")
|
|
63
238
|
)
|
|
@@ -92,7 +267,16 @@ The Browser MCP integration has been used in this session. When resuming:
|
|
|
92
267
|
|
|
93
268
|
// Handle session errors - could help debug browser automation issues
|
|
94
269
|
if (event.type === "session.error") {
|
|
95
|
-
//
|
|
270
|
+
// Check if it's a browser-related error
|
|
271
|
+
const error = (event as any).error
|
|
272
|
+
if (error && isConnectionError(error)) {
|
|
273
|
+
markConnectionFailed(error)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Clean up on session end
|
|
278
|
+
if (event.type === "session.end") {
|
|
279
|
+
stopHealthCheck()
|
|
96
280
|
}
|
|
97
281
|
}
|
|
98
282
|
}
|