Skip to content

Commit f97b56a

Browse files
authored
Merge pull request #62 from browserbase/kj/context
2 parents 72797da + 451f446 commit f97b56a

File tree

10 files changed

+368
-282
lines changed

10 files changed

+368
-282
lines changed

browserbase/README.md

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ node dist/index.js
2828
```json
2929
{
3030
"mcpServers": {
31-
"playwright": {
31+
"browserbase": {
3232
"url": "http://localhost:8931/sse",
33-
"args" : ["--proxies"],
3433
"env": {
3534
"BROWSERBASE_API_KEY": "",
3635
"BROWSERBASE_PROJECT_ID": ""
@@ -53,7 +52,80 @@ From here you should be able to put the url as "http://localhost:8931/sse" in th
5352
You can also pass in the optional flags for proxies and context id that are shown in [`config.d.ts`](./config.d.ts).
5453
____
5554

56-
The server communicates over stdio according to the Model Context Protocol.
55+
## Flags & Example Configs
56+
57+
### Proxies
58+
59+
Here are our docs on [Proxies](https://docs.browserbase.com/features/proxies).
60+
61+
To use proxies in STDIO, set the --proxies flag in your MCP Config
62+
63+
```json
64+
{
65+
"mcpServers": {
66+
"playwright": {
67+
"command" : "npx",
68+
"args" : ["@browserbasehq/mcp-server-browserbase", "--proxies"],
69+
"env": {
70+
"BROWSERBASE_API_KEY": "",
71+
"BROWSERBASE_PROJECT_ID": ""
72+
}
73+
}
74+
}
75+
}
76+
77+
```
78+
79+
### Contexts
80+
81+
Here are our docs on [Contexts](https://docs.browserbase.com/features/contexts)
82+
83+
To use proxies in STDIO, set the --proxies flag in your MCP Config
84+
85+
```json
86+
{
87+
"mcpServers": {
88+
"playwright": {
89+
"command" : "npx",
90+
"args" : ["@browserbasehq/mcp-server-browserbase", "--contextId", "<YOUR_CONTEXT_ID>"],
91+
"env": {
92+
"BROWSERBASE_API_KEY": "",
93+
"BROWSERBASE_PROJECT_ID": ""
94+
}
95+
}
96+
}
97+
}
98+
99+
```
100+
101+
### Cookie Injection
102+
103+
Why would you need to inject cookies? Our context API currently works on persistent cookies, but not session cookies. So sometimes our persistent auth might not work (we're working hard to add this functionality).
104+
105+
You can flag cookies into the MCP by adding the cookies.json to your MCP Config.
106+
107+
To use proxies in STDIO, set the --proxies flag in your MCP Config. Your cookies JSON must be in the type of [Playwright Cookies](https://playwright.dev/docs/api/class-browsercontext#browser-context-cookies)
108+
109+
```json
110+
{
111+
"mcpServers": {
112+
"playwright": {
113+
"command" : "npx",
114+
"args" : [
115+
"@browserbasehq/mcp-server-browserbase", "cookies",
116+
"{
117+
COOKIES JSON IN TYPE OF PLAYWRIGHT COOKIES
118+
}"
119+
],
120+
"env": {
121+
"BROWSERBASE_API_KEY": "",
122+
"BROWSERBASE_PROJECT_ID": ""
123+
}
124+
}
125+
}
126+
}
127+
128+
```
57129
58130
## Structure
59131

browserbase/config.d.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Cookie } from "playwright-core";
2+
13
export type Config = {
24
/**
35
* The Browserbase API Key to use
@@ -18,7 +20,36 @@ export type Config = {
1820
* Potential Browserbase Context to use
1921
* Would be a context ID
2022
*/
21-
context?: string;
23+
context?: {
24+
/**
25+
* The ID of the context to use
26+
*/
27+
contextId?: string;
28+
/**
29+
* Whether or not to persist the context
30+
*
31+
* @default true
32+
*/
33+
persist?: boolean;
34+
};
35+
/**
36+
*
37+
*/
38+
viewPort?: {
39+
/**
40+
* The width of the browser
41+
*/
42+
browserWidth?: number;
43+
/**
44+
* The height of the browser
45+
*/
46+
browserHeight?: number;
47+
};
48+
/**
49+
* Cookies to inject into the Browserbase context
50+
* Format: Array of cookie objects with name, value, domain, and optional path, expires, httpOnly, secure, sameSite
51+
*/
52+
cookies?: Cookie[];
2253
/**
2354
* Whether or not to port to a server
2455
*

browserbase/src/config.ts

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import os from 'os';
22
import fs from 'fs';
33
import path from 'path';
4-
import { sanitizeForFilePath } from './tools/utils.js'; // Assuming this path is correct
4+
import { sanitizeForFilePath } from './tools/utils.js';
5+
import type { Cookie } from "playwright-core";
56

67
export type ToolCapability = 'core' | string; // Example capabilities
78

@@ -13,23 +14,49 @@ export interface Config {
1314
host?: string;
1415
};
1516
proxies?: boolean;
16-
contextId?: string;
17+
context?: {
18+
contextId?: string;
19+
persist?: boolean;
20+
};
21+
viewPort?: {
22+
browserWidth?: number;
23+
browserHeight?: number;
24+
};
25+
cookies?: Cookie[];
1726
}
1827

1928
// Define Command Line Options Structure
2029
export type CLIOptions = {
30+
browserbaseApiKey?: string;
31+
browserbaseProjectId?: string;
2132
proxies?: boolean;
2233
contextId?: string;
34+
persist?: boolean;
2335
port?: number;
2436
host?: string;
37+
cookies?: Cookie[];
38+
browserWidth?: number;
39+
browserHeight?: number;
2540
};
2641

2742
// Default Configuration Values
2843
const defaultConfig: Config = {
2944
browserbaseApiKey: process.env.BROWSERBASE_API_KEY,
3045
browserbaseProjectId: process.env.BROWSERBASE_PROJECT_ID,
3146
proxies: false,
32-
contextId: undefined,
47+
context: {
48+
contextId: undefined,
49+
persist: true,
50+
},
51+
server: {
52+
port: undefined,
53+
host: undefined,
54+
},
55+
viewPort: {
56+
browserWidth: 1024,
57+
browserHeight: 768,
58+
},
59+
cookies: undefined,
3360
};
3461

3562
// Resolve final configuration by merging defaults, file config, and CLI options
@@ -40,8 +67,12 @@ export async function resolveConfig(cliOptions: CLIOptions): Promise<Config> {
4067

4168
// --- Add Browserbase Env Vars ---
4269
// Ensure env vars are read *after* dotenv potentially runs (in index.ts)
43-
mergedConfig.browserbaseApiKey = process.env.BROWSERBASE_API_KEY;
44-
mergedConfig.browserbaseProjectId = process.env.BROWSERBASE_PROJECT_ID;
70+
if (!mergedConfig.browserbaseApiKey) {
71+
mergedConfig.browserbaseApiKey = process.env.BROWSERBASE_API_KEY;
72+
}
73+
if (!mergedConfig.browserbaseProjectId) {
74+
mergedConfig.browserbaseProjectId = process.env.BROWSERBASE_PROJECT_ID;
75+
}
4576
// --------------------------------
4677

4778
// Basic validation for Browserbase keys
@@ -58,12 +89,22 @@ export async function resolveConfig(cliOptions: CLIOptions): Promise<Config> {
5889
// Create Config structure based on CLI options
5990
export async function configFromCLIOptions(cliOptions: CLIOptions): Promise<Config> {
6091
return {
92+
browserbaseApiKey: cliOptions.browserbaseApiKey,
93+
browserbaseProjectId: cliOptions.browserbaseProjectId,
6194
server: {
6295
port: cliOptions.port,
6396
host: cliOptions.host,
6497
},
65-
proxies: cliOptions.proxies || false,
66-
contextId: cliOptions.contextId || undefined,
98+
proxies: cliOptions.proxies,
99+
context: {
100+
contextId: cliOptions.contextId,
101+
persist: cliOptions.persist,
102+
},
103+
viewPort: {
104+
browserWidth: cliOptions.browserWidth,
105+
browserHeight: cliOptions.browserHeight,
106+
},
107+
cookies: cliOptions.cookies,
67108
};
68109
}
69110

@@ -85,8 +126,37 @@ function pickDefined<T extends object>(obj: T | undefined): Partial<T> {
85126

86127
// Merge two configuration objects (overrides takes precedence)
87128
function mergeConfig(base: Config, overrides: Config): Config {
88-
return {
89-
...pickDefined(base),
90-
...pickDefined(overrides),
91-
};
129+
const baseFiltered = pickDefined(base);
130+
const overridesFiltered = pickDefined(overrides);
131+
132+
// Create the result object
133+
const result = { ...baseFiltered } as Config;
134+
135+
// For each property in overrides
136+
for (const [key, value] of Object.entries(overridesFiltered)) {
137+
if (key === 'context' && value && result.context) {
138+
// Special handling for context object to ensure deep merge
139+
result.context = {
140+
...result.context,
141+
...(value as Config['context'])
142+
};
143+
} else if (
144+
value &&
145+
typeof value === 'object' &&
146+
!Array.isArray(value) &&
147+
result[key as keyof Config] &&
148+
typeof result[key as keyof Config] === 'object'
149+
) {
150+
// Deep merge for other nested objects
151+
result[key as keyof Config] = {
152+
...(result[key as keyof Config] as object),
153+
...value
154+
} as any;
155+
} else {
156+
// Simple override for primitives, arrays, etc.
157+
result[key as keyof Config] = value as any;
158+
}
159+
}
160+
161+
return result;
92162
}

browserbase/src/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import getText from "./tools/getText.js";
1515
import session from "./tools/session.js";
1616
import common from "./tools/common.js";
1717
import contextTools from "./tools/context.js";
18-
import cookies from "./tools/cookies.js";
1918
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2019
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
2120
import { z } from "zod";
@@ -60,7 +59,6 @@ export async function createServer(config: Config): Promise<Server> {
6059
...navigate,
6160
...session,
6261
...contextTools,
63-
...cookies,
6462
];
6563

6664
const toolsMap = new Map(tools.map(tool => [tool.schema.name, tool]));

browserbase/src/program.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@ import packageJSON from '../package.json' with { type: 'json' };
1212
program
1313
.version('Version ' + packageJSON.version)
1414
.name(packageJSON.name)
15+
.option('--browserbaseApiKey <key>', 'The Browserbase API Key to use')
16+
.option('--browserbaseProjectId <id>', 'The Browserbase Project ID to use')
1517
.option('--proxies', 'Use Browserbase proxies.')
16-
.option('--context <contextId>', 'Browserbase Context to use.')
18+
.option('--contextId <contextId>', 'Browserbase Context ID to use.')
19+
.option('--persist [boolean]', 'Whether to persist the Browserbase context', true)
1720
.option('--port <port>', 'Port to listen on for SSE transport.')
1821
.option('--host <host>', 'Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.')
22+
.option('--cookies [json]', 'JSON array of cookies to inject into the browser. Format: [{"name":"cookie1","value":"val1","domain":"example.com"}, ...]')
23+
.option('--browserWidth <width>', 'Browser width to use for the browser.')
24+
.option('--browserHeight <height>', 'Browser height to use for the browser.')
1925
.action(async options => {
2026
const config = await resolveConfig(options);
2127
const serverList = new ServerList(async() => createServer(config));

0 commit comments

Comments
 (0)