Skip to content

Commit d81dcc8

Browse files
committed
moving types around, removing unnecessary code, reworking stagehand session create
1 parent d6fb91d commit d81dcc8

15 files changed

+89
-110
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Apache License
2-
Version 2.0, June 2025
2+
Version 2.0, January 2004
33
http://www.apache.org/licenses/
44

55
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

eslint.config.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@ export default defineConfig([
1515
languageOptions: { globals: { ...globals.browser, ...globals.node } },
1616
ignores: ["dist/**/*"],
1717
},
18-
tseslint.configs.recommended,
18+
...tseslint.configs.recommended,
1919
{
20-
files: ["**/*.{ts,mts,cts}"],
20+
files: ["src/types/**/*.ts", "src/mcp/**/*.ts"],
2121
rules: {
2222
"@typescript-eslint/no-explicit-any": "off",
2323
"@typescript-eslint/no-unused-vars": "off",
2424
"@typescript-eslint/ban-ts-comment": "off",
2525
},
26-
ignores: ["dist/**/*"],
2726
},
2827
{
2928
ignores: ["dist/**/*", "node_modules/**/*"],

src/context.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,10 @@
11
import type { Stagehand } from "@browserbasehq/stagehand";
22
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
33
import type { Config } from "../config.js";
4-
import {
5-
CallToolResult,
6-
TextContent,
7-
ImageContent,
8-
} from "@modelcontextprotocol/sdk/types.js";
4+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
95
import { listResources, readResource } from "./mcp/resources.js";
10-
import {
11-
getSession,
12-
defaultSessionId,
13-
type BrowserSession,
14-
} from "./sessionManager.js";
15-
16-
export type ToolActionResult =
17-
| { content?: (ImageContent | TextContent)[] }
18-
| undefined
19-
| void;
6+
import { getSession, defaultSessionId } from "./sessionManager.js";
7+
import type { MCPTool, BrowserSession } from "./types/types.js";
208

219
export class Context {
2210
public readonly config: Config;
@@ -69,15 +57,15 @@ export class Context {
6957
return session.browser;
7058
}
7159

72-
async run(tool: any, args: any): Promise<CallToolResult> {
60+
async run(tool: MCPTool, args: unknown): Promise<CallToolResult> {
7361
try {
7462
console.error(
7563
`Executing tool: ${tool.schema.name} with args: ${JSON.stringify(args)}`,
7664
);
7765

7866
// Check if this tool has a handle method (new tool system)
7967
if ("handle" in tool && typeof tool.handle === "function") {
80-
const toolResult = await tool.handle(this as any, args);
68+
const toolResult = await tool.handle(this, args);
8169

8270
if (toolResult?.action) {
8371
const actionResult = await toolResult.action();

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ dotenv.config();
33

44
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
55
import { z } from "zod";
6-
import type { Tool } from "./tools/tool.js";
6+
import type { MCPToolsArray } from "./types/types.js";
77

88
import { Context } from "./context.js";
99
import type { Config } from "../config.js";
@@ -185,7 +185,7 @@ export default function ({ config }: { config: z.infer<typeof configSchema> }) {
185185
return prompt;
186186
});
187187

188-
const tools: Tool<any>[] = [...TOOLS];
188+
const tools: MCPToolsArray = [...TOOLS];
189189

190190
// Register each tool with the Smithery server
191191
tools.forEach((tool) => {

src/program.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ try {
2020
} catch {
2121
// Fallback for CommonJS or when import.meta is not available
2222
__filename =
23-
(globalThis as any).__filename || process.cwd() + "/dist/program.js";
23+
(globalThis as { __filename: string }).__filename ||
24+
process.cwd() + "/dist/program.js";
2425
__dirname = path.dirname(__filename);
2526
}
2627

src/sessionManager.ts

Lines changed: 18 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
1-
import { Browser, Page } from "playwright-core";
2-
import { AvailableModel, Stagehand } from "@browserbasehq/stagehand";
1+
import { Page } from "playwright-core";
2+
import { BrowserContext } from "@browserbasehq/stagehand";
33
import type { Config } from "../config.js";
44
import type { Cookie } from "playwright-core";
5-
6-
export type BrowserSession = {
7-
browser: Browser;
8-
page: Page;
9-
sessionId: string;
10-
stagehand: Stagehand;
11-
};
5+
import { createStagehandInstance } from "./stagehandStore.js";
6+
import type { BrowserSession } from "./types/types.js";
127

138
// Global state for managing browser sessions
149
const browsers = new Map<string, BrowserSession>();
@@ -50,7 +45,7 @@ export function getActiveSessionId(): string {
5045
* @param cookies Array of cookies to add
5146
*/
5247
export async function addCookiesToContext(
53-
context: any,
48+
context: BrowserContext,
5449
cookies: Cookie[],
5550
): Promise<void> {
5651
if (!cookies || cookies.length === 0) {
@@ -92,40 +87,14 @@ export async function createNewBrowserSession(
9287
`[SessionManager] ${resumeSessionId ? "Resuming" : "Creating"} Stagehand session ${newSessionId}...\n`,
9388
);
9489

95-
// Create and initialize Stagehand instance
96-
const stagehand = new Stagehand({
97-
env: "BROWSERBASE",
98-
apiKey: config.browserbaseApiKey,
99-
projectId: config.browserbaseProjectId,
100-
modelName: (config.modelName ||
101-
"google/gemini-2.0-flash") as AvailableModel,
102-
modelClientOptions: {
103-
apiKey: process.env.GEMINI_API_KEY, // TODO: change this in prod to just use our key
104-
},
105-
...(resumeSessionId && { browserbaseSessionID: resumeSessionId }),
106-
browserbaseSessionCreateParams: {
107-
projectId: config.browserbaseProjectId!,
108-
proxies: config.proxies,
109-
browserSettings: {
110-
viewport: {
111-
width: config.viewPort?.browserWidth ?? 1024,
112-
height: config.viewPort?.browserHeight ?? 768,
113-
},
114-
context: config.context?.contextId
115-
? {
116-
id: config.context?.contextId,
117-
persist: config.context?.persist ?? true,
118-
}
119-
: undefined,
120-
advancedStealth: config.advancedStealth ?? undefined,
121-
},
90+
// Create and initialize Stagehand instance using shared function
91+
const stagehand = await createStagehandInstance(
92+
config,
93+
{
94+
...(resumeSessionId && { browserbaseSessionID: resumeSessionId }),
12295
},
123-
logger: (logLine) => {
124-
console.error(`Stagehand[${newSessionId}]: ${logLine.message}`);
125-
},
126-
});
127-
128-
await stagehand.init();
96+
newSessionId,
97+
);
12998

13099
// Get the page and browser from Stagehand
131100
const page = stagehand.page as unknown as Page;
@@ -171,7 +140,10 @@ export async function createNewBrowserSession(
171140
Array.isArray(config.cookies) &&
172141
config.cookies.length > 0
173142
) {
174-
await addCookiesToContext(page.context(), config.cookies);
143+
await addCookiesToContext(
144+
page.context() as BrowserContext,
145+
config.cookies,
146+
);
175147
}
176148

177149
const sessionObj: BrowserSession = {
@@ -306,7 +278,7 @@ export async function getSession(
306278
if (sessionId === defaultSessionId && createIfMissing) {
307279
try {
308280
return await ensureDefaultSessionInternal(config);
309-
} catch (error) {
281+
} catch {
310282
process.stderr.write(
311283
`[SessionManager] Failed to get default session due to error in ensureDefaultSessionInternal for ${sessionId}. See previous messages for details.\n`,
312284
);
@@ -385,7 +357,7 @@ export async function closeAllSessions(): Promise<void> {
385357
}
386358
try {
387359
await Promise.all(closePromises);
388-
} catch (_e) {
360+
} catch {
389361
// Individual errors are caught and logged by closeBrowserGracefully
390362
process.stderr.write(
391363
`[SessionManager] WARN - Some errors occurred during batch session closing. See individual messages.\n`,

src/stagehandStore.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,20 @@ import type { Config } from "../config.js";
88
const store = new Map<string, StagehandSession>();
99

1010
/**
11-
* Create a new Stagehand session
11+
* Create a configured Stagehand instance
1212
*/
13-
export const create = async (
13+
export const createStagehandInstance = async (
1414
config: Config,
1515
params: CreateSessionParams = {},
16-
): Promise<StagehandSession> => {
17-
const id = randomUUID() + "_" + Date.now();
18-
19-
// Merge config with params
16+
sessionId: string,
17+
): Promise<Stagehand> => {
2018
const apiKey = params.apiKey || config.browserbaseApiKey;
2119
const projectId = params.projectId || config.browserbaseProjectId;
2220

2321
if (!apiKey || !projectId) {
2422
throw new Error("Browserbase API Key and Project ID are required");
2523
}
2624

27-
process.stderr.write(`[StagehandStore] Creating new session ${id}...\n`);
28-
2925
const stagehand = new Stagehand({
3026
env: "BROWSERBASE",
3127
apiKey,
@@ -34,12 +30,12 @@ export const create = async (
3430
config.modelName ||
3531
"google/gemini-2.0-flash") as AvailableModel,
3632
modelClientOptions: {
37-
apiKey: process.env.GEMINI_API_KEY, //TODO:
33+
apiKey: config.modelApiKey || process.env.GEMINI_API_KEY,
3834
},
3935
...(params.browserbaseSessionID && {
4036
browserbaseSessionID: params.browserbaseSessionID,
4137
}),
42-
browserbaseSessionCreateParams: params.browserbaseSessionCreateParams || {
38+
browserbaseSessionCreateParams: {
4339
projectId,
4440
proxies: config.proxies,
4541
browserSettings: {
@@ -57,11 +53,27 @@ export const create = async (
5753
},
5854
},
5955
logger: (logLine) => {
60-
console.error(`Stagehand[${id}]: ${logLine.message}`);
56+
console.error(`Stagehand[${sessionId}]: ${logLine.message}`);
6157
},
6258
});
6359

6460
await stagehand.init();
61+
return stagehand;
62+
};
63+
64+
/**
65+
* Create a new Stagehand session
66+
*/
67+
export const create = async (
68+
config: Config,
69+
params: CreateSessionParams = {},
70+
): Promise<StagehandSession> => {
71+
// Global ID, must be 100% Unique
72+
const id = randomUUID() + "_" + config.browserbaseProjectId;
73+
74+
process.stderr.write(`[StagehandStore] Creating new session ${id}...\n`);
75+
76+
const stagehand = await createStagehandInstance(config, params, id);
6577

6678
const page = stagehand.page as unknown as Page;
6779
const browser = page.context().browser();

src/tools/act.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from "zod";
22
import type { Tool, ToolSchema, ToolResult } from "./tool.js";
33
import type { Context } from "../context.js";
4-
import type { ToolActionResult } from "../context.js";
4+
import type { ToolActionResult } from "../types/types.js";
55

66
const ActInputSchema = z.object({
77
action: z

src/tools/extract.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from "zod";
22
import type { Tool, ToolSchema, ToolResult } from "./tool.js";
33
import type { Context } from "../context.js";
4-
import type { ToolActionResult } from "../context.js";
4+
import type { ToolActionResult } from "../types/types.js";
55

66
const ExtractInputSchema = z.object({
77
instruction: z

src/tools/multiSession.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ function createMultiSessionAwareTool<TInput extends InputType>(
5151
description: `${originalTool.schema.description} (for a specific session)`,
5252
inputSchema: newInputSchema,
5353
},
54-
handle: async (context: Context, params: any): Promise<ToolResult> => {
54+
handle: async (
55+
context: Context,
56+
params: z.infer<typeof newInputSchema>,
57+
): Promise<ToolResult> => {
5558
const { sessionId, ...originalParams } = params;
5659

5760
// Get the session
@@ -94,22 +97,15 @@ export const createSessionTool = defineTool({
9497
.describe(
9598
"Resume an existing Browserbase session by providing its session ID. Use this to continue work in a previously created browser session that may have been paused or disconnected.",
9699
),
97-
browserbaseSessionCreateParams: z
98-
.any()
99-
.optional()
100-
.describe(
101-
"Advanced Browserbase session configuration parameters for customizing browser settings, viewport, user agent, etc. Leave empty for default settings.",
102-
),
103100
}),
104101
},
105102
handle: async (
106103
context: Context,
107-
{ name, browserbaseSessionID, browserbaseSessionCreateParams },
104+
{ name, browserbaseSessionID },
108105
): Promise<ToolResult> => {
109106
try {
110107
const params: CreateSessionParams = {
111108
browserbaseSessionID,
112-
browserbaseSessionCreateParams,
113109
meta: name ? { name } : undefined,
114110
};
115111

0 commit comments

Comments
 (0)