@@ -7,9 +7,12 @@ import {
7
7
ListToolsRequestSchema ,
8
8
CallToolResult ,
9
9
Tool ,
10
+ ListResourcesRequestSchema ,
11
+ ListResourceTemplatesRequestSchema
10
12
} from "@modelcontextprotocol/sdk/types.js" ;
11
13
12
14
import { Stagehand } from "@browserbasehq/stagehand" ;
15
+ import type { ConstructorParams , LogLine } from "@browserbasehq/stagehand" ;
13
16
14
17
import { AnyZodObject } from "zod" ;
15
18
import { jsonSchemaToZod } from "./utils.js" ;
@@ -29,6 +32,41 @@ if (!fs.existsSync(LOG_DIR)) {
29
32
fs . mkdirSync ( LOG_DIR , { recursive : true } ) ;
30
33
}
31
34
35
+ // Helper function to convert LogLine to string
36
+ function logLineToString ( logLine : LogLine ) : string {
37
+ const timestamp = logLine . timestamp ? new Date ( logLine . timestamp ) . toISOString ( ) : new Date ( ) . toISOString ( ) ;
38
+ const level = logLine . level !== undefined ?
39
+ ( logLine . level === 0 ? 'DEBUG' :
40
+ logLine . level === 1 ? 'INFO' :
41
+ logLine . level === 2 ? 'ERROR' : 'UNKNOWN' ) : 'UNKNOWN' ;
42
+ return `[${ timestamp } ] [${ level } ] ${ logLine . message || '' } ` ;
43
+ }
44
+
45
+ // Define Stagehand configuration
46
+ const stagehandConfig : ConstructorParams = {
47
+ env :
48
+ process . env . BROWSERBASE_API_KEY && process . env . BROWSERBASE_PROJECT_ID
49
+ ? "BROWSERBASE"
50
+ : "LOCAL" ,
51
+ apiKey : process . env . BROWSERBASE_API_KEY /* API key for authentication */ ,
52
+ projectId : process . env . BROWSERBASE_PROJECT_ID /* Project identifier */ ,
53
+ debugDom : true /* Enable DOM debugging features */ ,
54
+ headless : false /* Run browser in headless mode */ ,
55
+ logger : ( message : LogLine ) =>
56
+ console . log ( logLineToString ( message ) ) /* Custom logging function */ ,
57
+ domSettleTimeoutMs : 30_000 /* Timeout for DOM to settle in milliseconds */ ,
58
+ browserbaseSessionCreateParams : {
59
+ projectId : process . env . BROWSERBASE_PROJECT_ID ! ,
60
+ } ,
61
+ enableCaching : true /* Enable caching functionality */ ,
62
+ browserbaseSessionID :
63
+ undefined /* Session ID for resuming Browserbase sessions */ ,
64
+ modelName : "gpt-4o" /* Name of the model to use */ ,
65
+ modelClientOptions : {
66
+ apiKey : process . env . OPENAI_API_KEY ,
67
+ } /* Configuration options for the model client */ ,
68
+ } ;
69
+
32
70
// Define the Stagehand tools
33
71
const TOOLS : Tool [ ] = [
34
72
{
@@ -44,7 +82,7 @@ const TOOLS: Tool[] = [
44
82
} ,
45
83
{
46
84
name : "stagehand_act" ,
47
- description : "Performs an action on the web page" ,
85
+ description : "Performs an action on a web page element " ,
48
86
inputSchema : {
49
87
type : "object" ,
50
88
properties : {
@@ -58,6 +96,7 @@ const TOOLS: Tool[] = [
58
96
required : [ "action" ] ,
59
97
} ,
60
98
} ,
99
+ /*
61
100
{
62
101
name: "stagehand_extract",
63
102
description: `Extracts structured data from the web page based on an instruction and a JSON schema.`,
@@ -153,6 +192,7 @@ const TOOLS: Tool[] = [
153
192
required: ["instruction", "schema"],
154
193
},
155
194
},
195
+ */
156
196
{
157
197
name : "stagehand_observe" ,
158
198
description : "Observes actions that can be performed on the web page" ,
@@ -161,9 +201,10 @@ const TOOLS: Tool[] = [
161
201
properties : {
162
202
instruction : {
163
203
type : "string" ,
164
- description : "Instruction for observation" ,
204
+ description : "Instruction for observation (e.g., 'find the login button') " ,
165
205
} ,
166
206
} ,
207
+ required : [ "instruction" ] ,
167
208
} ,
168
209
} ,
169
210
] ;
@@ -210,13 +251,7 @@ async function ensureStagehand() {
210
251
log ( "Ensuring Stagehand is initialized..." ) ;
211
252
if ( ! stagehand ) {
212
253
log ( "Initializing Stagehand..." ) ;
213
- stagehand = new Stagehand ( {
214
- env : "BROWSERBASE" ,
215
- headless : true ,
216
- verbose : 2 ,
217
- debugDom : true ,
218
- modelName : "claude-3-5-sonnet-20241022" ,
219
- } ) ;
254
+ stagehand = new Stagehand ( stagehandConfig ) ;
220
255
log ( "Running init()" ) ;
221
256
await stagehand . init ( ) ;
222
257
log ( "Stagehand initialized successfully" ) ;
@@ -319,7 +354,7 @@ async function handleToolCall(
319
354
case "stagehand_act" :
320
355
try {
321
356
log ( `Performing action: ${ args . action } ` ) ;
322
- await stagehand . act ( {
357
+ await stagehand . page . act ( {
323
358
action : args . action ,
324
359
variables : args . variables ,
325
360
} ) ;
@@ -351,6 +386,7 @@ async function handleToolCall(
351
386
} ;
352
387
}
353
388
389
+ /*
354
390
case "stagehand_extract":
355
391
try {
356
392
log(`Extracting data with instruction: ${args.instruction}`);
@@ -396,10 +432,11 @@ async function handleToolCall(
396
432
isError: true,
397
433
};
398
434
}
435
+ */
399
436
case "stagehand_observe" :
400
437
try {
401
438
log ( `Starting observation with instruction: ${ args . instruction } ` ) ;
402
- const observations = await stagehand . observe ( {
439
+ const observations = await stagehand . page . observe ( {
403
440
instruction : args . instruction ,
404
441
} ) ;
405
442
log (
@@ -513,6 +550,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
513
550
}
514
551
} ) ;
515
552
553
+
554
+ server . setRequestHandler ( ListResourcesRequestSchema , async ( request ) => {
555
+ try {
556
+ logRequest ( 'ListResources' , request . params ) ;
557
+ // Return an empty list since we don't have any resources defined
558
+ const response = { resources : [ ] } ;
559
+ const sanitizedResponse = sanitizeMessage ( response ) ;
560
+ logResponse ( 'ListResources' , JSON . parse ( sanitizedResponse ) ) ;
561
+ return JSON . parse ( sanitizedResponse ) ;
562
+ } catch ( error ) {
563
+ const errorMsg = error instanceof Error ? error . message : String ( error ) ;
564
+ log ( `ListResources handler error: ${ errorMsg } ` , 'error' ) ;
565
+ return {
566
+ error : {
567
+ code : - 32603 ,
568
+ message : `Internal error: ${ errorMsg } ` ,
569
+ } ,
570
+ } ;
571
+ }
572
+ } ) ;
573
+
574
+ server . setRequestHandler ( ListResourceTemplatesRequestSchema , async ( request ) => {
575
+ try {
576
+ logRequest ( 'ListResourceTemplates' , request . params ) ;
577
+ // Return an empty list since we don't have any resource templates defined
578
+ const response = { resourceTemplates : [ ] } ;
579
+ const sanitizedResponse = sanitizeMessage ( response ) ;
580
+ logResponse ( 'ListResourceTemplates' , JSON . parse ( sanitizedResponse ) ) ;
581
+ return JSON . parse ( sanitizedResponse ) ;
582
+ } catch ( error ) {
583
+ const errorMsg = error instanceof Error ? error . message : String ( error ) ;
584
+ log ( `ListResourceTemplates handler error: ${ errorMsg } ` , 'error' ) ;
585
+ return {
586
+ error : {
587
+ code : - 32603 ,
588
+ message : `Internal error: ${ errorMsg } ` ,
589
+ } ,
590
+ } ;
591
+ }
592
+ } ) ;
593
+
516
594
// Run the server
517
595
async function runServer ( ) {
518
596
log ( "Starting Stagehand MCP server..." , 'info' ) ;
0 commit comments