Skip to content

Commit f32fd05

Browse files
committed
v0 multi-session tools
1 parent 91e313f commit f32fd05

File tree

7 files changed

+487
-3
lines changed

7 files changed

+487
-3
lines changed

README.md

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This server provides cloud browser automation capabilities using [Browserbase](h
1919
| Model Flexibility | Supports multiple models (OpenAI, Claude, Gemini, and more) |
2020
| Vision Support | Use annotated screenshots for complex DOMs |
2121
| Session Management | Create, manage, and close browser sessions |
22+
| Multi-Session | Run multiple browser sessions in parallel |
2223

2324
### Alternative Installation Methods
2425

@@ -332,6 +333,82 @@ The Browserbase MCP server provides the following tools for browser automation:
332333
- Output:
333334
- Confirmation message and session replay URL
334335

336+
### Multi-Session Management Tools
337+
338+
The server now supports managing multiple browser sessions in parallel, allowing you to control multiple browsers simultaneously:
339+
340+
- **stagehand_session_create_multi**
341+
- Create a new independent Stagehand browser session
342+
- Inputs:
343+
- `name` (string, optional): Optional name for the session
344+
- `browserbaseSessionID` (string, optional): Resume an existing Browserbase session
345+
- `browserbaseSessionCreateParams` (object, optional): Custom Browserbase session parameters
346+
- Output:
347+
- Session ID and Browserbase session ID
348+
349+
- **stagehand_session_list**
350+
- List all active Stagehand browser sessions
351+
- No inputs required
352+
- Output:
353+
- List of active sessions with IDs, names, and metadata
354+
355+
- **stagehand_session_close_multi**
356+
- Close a specific Stagehand browser session
357+
- Input:
358+
- `sessionId` (string): The session ID to close
359+
- Output:
360+
- Confirmation message
361+
362+
- **stagehand_navigate_session**
363+
- Navigate to a URL in a specific browser session
364+
- Inputs:
365+
- `sessionId` (string): The session ID to use
366+
- `url` (string): The URL to navigate to
367+
368+
- **stagehand_act_session**
369+
- Perform an action in a specific browser session
370+
- Inputs:
371+
- `sessionId` (string): The session ID to use
372+
- `action` (string): The action to perform
373+
- `variables` (object, optional): Variables for the action
374+
375+
- **stagehand_extract_session**
376+
- Extract information from a specific browser session
377+
- Inputs:
378+
- `sessionId` (string): The session ID to use
379+
- `instruction` (string): What to extract
380+
381+
- **stagehand_observe_session**
382+
- Observe elements in a specific browser session
383+
- Inputs:
384+
- `sessionId` (string): The session ID to use
385+
- `instruction` (string): What to observe
386+
- `returnAction` (boolean, optional): Whether to return the action
387+
388+
### Multi-Session Example
389+
390+
Here's an example of using multiple browser sessions in parallel:
391+
392+
```javascript
393+
// Create two sessions
394+
const searchSession = await createSession({ name: "Search Engine" });
395+
const newsSession = await createSession({ name: "News Reader" });
396+
397+
// Navigate both sessions in parallel
398+
await Promise.all([
399+
navigateSession(searchSession.id, "https://google.com"),
400+
navigateSession(newsSession.id, "https://news.ycombinator.com")
401+
]);
402+
403+
// Perform actions on both sessions
404+
await actSession(searchSession.id, "Search for 'AI tools'");
405+
const news = await extractSession(newsSession.id, "Extract top 5 headlines");
406+
407+
// Clean up
408+
await closeSession(searchSession.id);
409+
await closeSession(newsSession.id);
410+
```
411+
335412
### Resources
336413

337414
The server provides access to screenshot resources:
@@ -414,9 +491,10 @@ mcp-server-browserbase/
414491

415492
This server implements the following MCP capabilities:
416493

417-
- **Tools**: 7 tools for comprehensive browser automation
494+
- **Tools**: 14 tools for comprehensive browser automation
418495
- 5 Stagehand tools: navigate, act, extract, observe, screenshot
419496
- 2 Session management tools: create and close Browserbase sessions
497+
- 7 Multi-session tools: create, list, close, navigate, act, extract, observe with specific sessions
420498
- **Prompts**: Prompt templates for common automation tasks
421499
- **Resources**: Screenshot resource management with URI-based access
422500

src/program.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { fileURLToPath } from "url";
66
import createServerFunction from "./index.js";
77
import { ServerList } from "./server.js";
88
import { startHttpTransport, startStdioTransport } from "./transport.js";
9+
import * as stagehandStore from "./stagehandStore.js";
910

1011
import { resolveConfig } from "./config.js";
1112

@@ -79,6 +80,7 @@ program
7980
function setupExitWatchdog(serverList: ServerList) {
8081
const handleExit = async () => {
8182
setTimeout(() => process.exit(0), 15000);
83+
await stagehandStore.removeAll();
8284
await serverList.closeAll();
8385
process.exit(0);
8486
};

src/stagehandStore.ts

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { randomUUID } from "crypto";
2+
import { Stagehand, AvailableModel } from "@browserbasehq/stagehand";
3+
import { Page } from "playwright-core";
4+
import { StagehandSession, CreateSessionParams } from "./types/types.js";
5+
import type { Config } from "../config.js";
6+
7+
// Store for all active sessions
8+
const store = new Map<string, StagehandSession>();
9+
10+
/**
11+
* Create a new Stagehand session
12+
*/
13+
export const create = async (
14+
config: Config,
15+
params: CreateSessionParams = {}
16+
): Promise<StagehandSession> => {
17+
const id = randomUUID();
18+
19+
// Merge config with params
20+
const apiKey = params.apiKey || config.browserbaseApiKey;
21+
const projectId = params.projectId || config.browserbaseProjectId;
22+
23+
if (!apiKey || !projectId) {
24+
throw new Error("Browserbase API Key and Project ID are required");
25+
}
26+
27+
process.stderr.write(`[StagehandStore] Creating new session ${id}...\n`);
28+
29+
const stagehand = new Stagehand({
30+
env: "BROWSERBASE",
31+
apiKey,
32+
projectId,
33+
modelName: (params.modelName || config.modelName || "google/gemini-2.0-flash") as AvailableModel,
34+
modelClientOptions: {
35+
apiKey: process.env.GEMINI_API_KEY, //TODO:
36+
},
37+
...(params.browserbaseSessionID && { browserbaseSessionID: params.browserbaseSessionID }),
38+
browserbaseSessionCreateParams: params.browserbaseSessionCreateParams || {
39+
projectId,
40+
proxies: config.proxies,
41+
browserSettings: {
42+
viewport: {
43+
width: config.viewPort?.browserWidth ?? 1024,
44+
height: config.viewPort?.browserHeight ?? 768,
45+
},
46+
context: config.context?.contextId
47+
? {
48+
id: config.context?.contextId,
49+
persist: config.context?.persist ?? true,
50+
}
51+
: undefined,
52+
advancedStealth: config.advancedStealth ?? undefined,
53+
},
54+
},
55+
logger: (logLine) => {
56+
console.error(`Stagehand[${id}]: ${logLine.message}`);
57+
},
58+
});
59+
60+
await stagehand.init();
61+
62+
const page = stagehand.page as unknown as Page;
63+
const browser = page.context().browser();
64+
65+
if (!browser) {
66+
throw new Error("Failed to get browser from Stagehand page context");
67+
}
68+
69+
const session: StagehandSession = {
70+
id,
71+
stagehand,
72+
page,
73+
browser,
74+
created: Date.now(),
75+
metadata: {
76+
...params.meta,
77+
bbSessionId: stagehand.browserbaseSessionID,
78+
},
79+
};
80+
81+
store.set(id, session);
82+
83+
process.stderr.write(
84+
`[StagehandStore] Session created: ${id} (BB: ${stagehand.browserbaseSessionID})\n`
85+
);
86+
process.stderr.write(
87+
`[StagehandStore] Live debugger: https://www.browserbase.com/sessions/${stagehand.browserbaseSessionID}\n`
88+
);
89+
90+
// Set up disconnect handler
91+
browser.on("disconnected", () => {
92+
process.stderr.write(`[StagehandStore] Session disconnected: ${id}\n`);
93+
store.delete(id);
94+
});
95+
96+
return session;
97+
};
98+
99+
/**
100+
* Get a session by ID
101+
*/
102+
export const get = (id: string): StagehandSession | null => {
103+
return store.get(id) ?? null;
104+
};
105+
106+
/**
107+
* List all active sessions
108+
*/
109+
export const list = (): StagehandSession[] => {
110+
return Array.from(store.values());
111+
};
112+
113+
/**
114+
* Remove and close a session
115+
*/
116+
export const remove = async (id: string): Promise<void> => {
117+
const session = store.get(id);
118+
if (!session) {
119+
process.stderr.write(`[StagehandStore] Session not found for removal: ${id}\n`);
120+
return;
121+
}
122+
123+
process.stderr.write(`[StagehandStore] Removing session: ${id}\n`);
124+
125+
try {
126+
await session.stagehand.close();
127+
process.stderr.write(`[StagehandStore] Session closed: ${id}\n`);
128+
} catch (error) {
129+
process.stderr.write(
130+
`[StagehandStore] Error closing session ${id}: ${
131+
error instanceof Error ? error.message : String(error)
132+
}\n`
133+
);
134+
}
135+
136+
store.delete(id);
137+
};
138+
139+
/**
140+
* Remove all sessions
141+
*/
142+
export const removeAll = async (): Promise<void> => {
143+
process.stderr.write(`[StagehandStore] Removing all ${store.size} sessions...\n`);
144+
await Promise.all(list().map(s => remove(s.id)));
145+
process.stderr.write(`[StagehandStore] All sessions removed\n`);
146+
};
147+
148+
/**
149+
* Get store size
150+
*/
151+
export const size = (): number => {
152+
return store.size;
153+
};

src/tools/index.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1-
// Import all individual tools
21
import navigateTool from "./navigate.js";
32
import actTool from "./act.js";
43
import extractTool from "./extract.js";
54
import observeTool from "./observe.js";
65
import screenshotTool from "./screenshot.js";
76
import sessionTools from "./session.js";
7+
import {
8+
createSessionTool,
9+
listSessionsTool,
10+
closeSessionTool,
11+
navigateWithSessionTool,
12+
actWithSessionTool,
13+
extractWithSessionTool,
14+
observeWithSessionTool,
15+
} from "./multiSession.js";
816

917
// Export individual tools
1018
export { default as navigateTool } from "./navigate.js";
@@ -14,6 +22,17 @@ export { default as observeTool } from "./observe.js";
1422
export { default as screenshotTool } from "./screenshot.js";
1523
export { default as sessionTools } from "./session.js";
1624

25+
// Multi-session tools array
26+
export const multiSessionTools = [
27+
createSessionTool,
28+
listSessionsTool,
29+
closeSessionTool,
30+
navigateWithSessionTool,
31+
actWithSessionTool,
32+
extractWithSessionTool,
33+
observeWithSessionTool,
34+
];
35+
1736
// Export all tools as array
1837
export const TOOLS = [
1938
...sessionTools,
@@ -22,6 +41,7 @@ export const TOOLS = [
2241
extractTool,
2342
observeTool,
2443
screenshotTool,
44+
...multiSessionTools,
2545
];
2646

2747
export const sessionManagementTools = sessionTools;

0 commit comments

Comments
 (0)