opencode-browser 1.0.2 → 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 +63 -6
- package/index.ts +189 -6
- 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
|
|
@@ -121,6 +128,19 @@ For more control, you can disable Browser MCP tools globally and enable them per
|
|
|
121
128
|
}
|
|
122
129
|
```
|
|
123
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
|
+
|
|
124
144
|
### Environment Variables
|
|
125
145
|
|
|
126
146
|
If you need to pass environment variables to the Browser MCP server:
|
|
@@ -198,6 +218,33 @@ When performing browser automation tasks:
|
|
|
198
218
|
|
|
199
219
|
## Plugin Features
|
|
200
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
|
+
|
|
201
248
|
### Automatic Browser Tool Detection
|
|
202
249
|
|
|
203
250
|
The plugin automatically detects when Browser MCP tools are being used and logs the activity.
|
|
@@ -218,6 +265,21 @@ The plugin hooks into tool execution to:
|
|
|
218
265
|
|
|
219
266
|
## Troubleshooting
|
|
220
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
|
+
|
|
221
283
|
### Browser MCP Not Working
|
|
222
284
|
|
|
223
285
|
1. **Check extension is installed**: Open your browser and verify the Browser MCP extension is installed and enabled
|
|
@@ -298,10 +360,5 @@ For issues and questions:
|
|
|
298
360
|
|
|
299
361
|
## Changelog
|
|
300
362
|
|
|
301
|
-
|
|
363
|
+
See [CHANGELOG.md](CHANGELOG.md) for a detailed list of changes in each version.
|
|
302
364
|
|
|
303
|
-
- Initial release
|
|
304
|
-
- Browser MCP integration
|
|
305
|
-
- Session context preservation
|
|
306
|
-
- Tool execution logging
|
|
307
|
-
- 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
|
|
|
@@ -93,7 +267,16 @@ The Browser MCP integration has been used in this session. When resuming:
|
|
|
93
267
|
|
|
94
268
|
// Handle session errors - could help debug browser automation issues
|
|
95
269
|
if (event.type === "session.error") {
|
|
96
|
-
//
|
|
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()
|
|
97
280
|
}
|
|
98
281
|
}
|
|
99
282
|
}
|