From 0fdfe3feb8965ac9e3c3e66342fb2c7f90fe3b0b Mon Sep 17 00:00:00 2001
From: Mahesh Murag <mahesh@anthropic.com>
Date: Tue, 19 Nov 2024 17:01:19 -0500
Subject: [PATCH 1/3] Update Puppeteer

---
 src/puppeteer/README.md |  54 +++++-
 src/puppeteer/index.ts  | 385 ++++++++++++++++++++++++++++++++--------
 2 files changed, 363 insertions(+), 76 deletions(-)

diff --git a/src/puppeteer/README.md b/src/puppeteer/README.md
index 4350f6fb..c6132b1b 100644
--- a/src/puppeteer/README.md
+++ b/src/puppeteer/README.md
@@ -1,3 +1,53 @@
-# Puppeteer server
+# Puppeteer
 
-This MCP server provides **resources** and **tools** for interacting with a browser and web pages, using [Puppeteer](https://pptr.dev/).
+A Model Context Protocol server that provides browser automation capabilities using Puppeteer. This server enables LLMs to interact with web pages, take screenshots, and execute JavaScript in a real browser environment.
+
+## Components
+
+### Tools
+
+- **puppeteer_navigate**
+  - Navigate to any URL in the browser
+  - Input: `url` (string)
+
+- **puppeteer_screenshot**
+  - Capture screenshots of the entire page or specific elements
+  - Inputs:
+    - `name` (string, required): Name for the screenshot
+    - `selector` (string, optional): CSS selector for element to screenshot
+    - `width` (number, optional, default: 800): Screenshot width
+    - `height` (number, optional, default: 600): Screenshot height
+
+- **puppeteer_click**
+  - Click elements on the page
+  - Input: `selector` (string): CSS selector for element to click
+
+- **puppeteer_fill**
+  - Fill out input fields
+  - Inputs:
+    - `selector` (string): CSS selector for input field
+    - `value` (string): Value to fill
+
+- **puppeteer_evaluate**
+  - Execute JavaScript in the browser console
+  - Input: `script` (string): JavaScript code to execute
+
+### Resources
+
+The server provides access to two types of resources:
+
+1. **Console Logs** (`console://logs`)
+   - Browser console output in text format
+   - Includes all console messages from the browser
+
+2. **Screenshots** (`screenshot://<name>`)
+   - PNG images of captured screenshots
+   - Accessible via the screenshot name specified during capture
+
+## Key Features
+
+- Browser automation
+- Console log monitoring
+- Screenshot capabilities
+- JavaScript execution
+- Basic web interaction (navigation, clicking, form filling)
\ No newline at end of file
diff --git a/src/puppeteer/index.ts b/src/puppeteer/index.ts
index cce6614e..c7d8959f 100644
--- a/src/puppeteer/index.ts
+++ b/src/puppeteer/index.ts
@@ -7,104 +7,341 @@ import {
   ListResourcesRequestSchema,
   ListToolsRequestSchema,
   ReadResourceRequestSchema,
+  CallToolResult,
+  TextContent,
+  ImageContent,
+  Tool,
 } from "@modelcontextprotocol/sdk/types.js";
-import puppeteer from "puppeteer";
+import puppeteer, { Browser, Page } from "puppeteer";
 
-const server = new Server(
+// Define the tools once to avoid repetition
+const TOOLS: Tool[] = [
   {
-    name: "example-servers/puppeteer",
-    version: "0.1.0",
+    name: "puppeteer_navigate",
+    description: "Navigate to a URL",
+    inputSchema: {
+      type: "object",
+      properties: {
+        url: { type: "string" },
+      },
+      required: ["url"],
+    },
   },
   {
-    capabilities: {
-      resources: {
-        listChanged: true,
+    name: "puppeteer_screenshot",
+    description: "Take a screenshot of the current page or a specific element",
+    inputSchema: {
+      type: "object",
+      properties: {
+        name: { type: "string", description: "Name for the screenshot" },
+        selector: { type: "string", description: "CSS selector for element to screenshot" },
+        width: { type: "number", description: "Width in pixels (default: 800)" },
+        height: { type: "number", description: "Height in pixels (default: 600)" },
       },
-      tools: {},
+      required: ["name"],
     },
   },
-);
-
-let browser: puppeteer.Browser | undefined;
-let consoleLogs: string[] = [];
-
-server.setRequestHandler(ListResourcesRequestSchema, async () => {
-  return {
-    resources: [
-      {
-        uri: "console://logs",
-        mimeType: "text/plain",
-        name: "Browser console logs",
+  {
+    name: "puppeteer_click",
+    description: "Click an element on the page",
+    inputSchema: {
+      type: "object",
+      properties: {
+        selector: { type: "string", description: "CSS selector for element to click" },
+      },
+      required: ["selector"],
+    },
+  },
+  {
+    name: "puppeteer_fill",
+    description: "Fill out an input field",
+    inputSchema: {
+      type: "object",
+      properties: {
+        selector: { type: "string", description: "CSS selector for input field" },
+        value: { type: "string", description: "Value to fill" },
       },
-    ],
-  };
-});
-
-server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
-  if (request.params.uri.toString() === "console://logs") {
-    return {
-      contents: [
+      required: ["selector", "value"],
+    },
+  },
+  {
+    name: "puppeteer_evaluate",
+    description: "Execute JavaScript in the browser console",
+    inputSchema: {
+      type: "object",
+      properties: {
+        script: { type: "string", description: "JavaScript code to execute" },
+      },
+      required: ["script"],
+    },
+  },
+];
+
+class PuppeteerServer {
+  private browser?: Browser;
+  private page?: Page;
+  private consoleLogs: string[] = [];
+  private screenshots: Map<string, string> = new Map();
+  private server: Server;
+
+  constructor() {
+    this.server = new Server({
+      name: "example-servers/puppeteer",
+      version: "0.4.0",
+    });
+    this.setupHandlers();
+  }
+
+  private async ensureBrowser() {
+    if (!this.browser) {
+      this.browser = await puppeteer.launch({ headless: false });
+      const pages = await this.browser.pages();
+      this.page = pages[0];
+      
+      this.page.on("console", (msg) => {
+        const logEntry = `[${msg.type()}] ${msg.text()}`;
+        this.consoleLogs.push(logEntry);
+        this.server.notification({
+          method: "notifications/resources/updated",
+          params: { uri: "console://logs" },
+        });
+      });
+    }
+    return this.page!;
+  }
+
+  private setupHandlers() {
+    this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
+      resources: [
         {
           uri: "console://logs",
           mimeType: "text/plain",
-          text: consoleLogs.join("\n"),
+          name: "Browser console logs",
         },
+        ...Array.from(this.screenshots.keys()).map(name => ({
+          uri: `screenshot://${name}`,
+          mimeType: "image/png",
+          name: `Screenshot: ${name}`,
+        })),
       ],
-    };
+    }));
+
+    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
+      const uri = request.params.uri.toString();
+      
+      if (uri === "console://logs") {
+        return {
+          contents: [{
+            uri,
+            mimeType: "text/plain",
+            text: this.consoleLogs.join("\n"),
+          }],
+        };
+      }
+
+      if (uri.startsWith("screenshot://")) {
+        const name = uri.split("://")[1];
+        const screenshot = this.screenshots.get(name);
+        if (screenshot) {
+          return {
+            contents: [{
+              uri,
+              mimeType: "image/png",
+              blob: screenshot,
+            }],
+          };
+        }
+      }
+
+      throw new Error(`Resource not found: ${uri}`);
+    });
+
+    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
+      tools: TOOLS,
+    }));
+
+    this.server.setRequestHandler(CallToolRequestSchema, async (request) => 
+      this.handleToolCall(request.params.name, request.params.arguments ?? {})
+    );
   }
-  console.error("Resource not found:", request.params.uri);
-  throw new Error("Resource not found");
-});
-
-server.setRequestHandler(ListToolsRequestSchema, async () => {
-  return {
-    tools: [
-      {
-        name: "navigate",
-        description: "Navigate to a URL",
-        inputSchema: {
-          type: "object",
-          properties: {
-            url: { type: "string" },
+
+  private async handleToolCall(name: string, args: any): Promise<{ toolResult: CallToolResult }> {
+    const page = await this.ensureBrowser();
+
+    switch (name) {
+      case "puppeteer_navigate":
+        await page.goto(args.url);
+        return {
+          toolResult: {
+            content: [{
+              type: "text",
+              text: `Navigated to ${args.url}`,
+            }],
+            isError: false,
           },
-        },
-      },
-    ],
-  };
-});
+        };
 
-server.setRequestHandler(CallToolRequestSchema, async (request) => {
-  if (request.params.name === "navigate") {
-    const url = request.params.arguments?.url as string;
+      case "puppeteer_screenshot": {
+        const width = args.width ?? 800;
+        const height = args.height ?? 600;
+        await page.setViewport({ width, height });
 
-    if (!browser) {
-      browser = await puppeteer.launch({ headless: false });
+        const screenshot = await (args.selector ? 
+          (await page.$(args.selector))?.screenshot({ encoding: "base64" }) :
+          page.screenshot({ encoding: "base64", fullPage: false }));
 
-      const pages = await browser.pages();
-      pages[0].on("console", (msg) => {
-        const logEntry = `[${msg.type()}] ${msg.text()}`;
-        consoleLogs.push(logEntry);
-        server.notification({
-          method: "notifications/resources/updated",
-          params: { uri: "console://logs" },
+        if (!screenshot) {
+          return {
+            toolResult: {
+              content: [{
+                type: "text",
+                text: args.selector ? `Element not found: ${args.selector}` : "Screenshot failed",
+              }],
+              isError: true,
+            },
+          };
+        }
+
+        this.screenshots.set(args.name, screenshot as string);
+        this.server.notification({
+          method: "notifications/resources/list_changed",
         });
-      });
-    }
 
-    const pages = await browser.pages();
-    await pages[0].goto(url);
+        return {
+          toolResult: {
+            content: [
+              {
+                type: "text",
+                text: `Screenshot '${args.name}' taken at ${width}x${height}`,
+              } as TextContent,
+              {
+                type: "image",
+                data: screenshot,
+                mimeType: "image/png",
+              } as ImageContent,
+            ],
+            isError: false,
+          },
+        };
+      }
+
+      case "puppeteer_click":
+        try {
+          await page.click(args.selector);
+          return {
+            toolResult: {
+              content: [{
+                type: "text",
+                text: `Clicked: ${args.selector}`,
+              }],
+              isError: false,
+            },
+          };
+        } catch (error) {
+          return {
+            toolResult: {
+              content: [{
+                type: "text",
+                text: `Failed to click ${args.selector}: ${(error as Error).message}`,
+              }],
+              isError: true,
+            },
+          };
+        }
+
+      case "puppeteer_fill":
+        try {
+          await page.waitForSelector(args.selector);
+          await page.type(args.selector, args.value);
+          return {
+            toolResult: {
+              content: [{
+                type: "text",
+                text: `Filled ${args.selector} with: ${args.value}`,
+              }],
+              isError: false,
+            },
+          };
+        } catch (error) {
+          return {
+            toolResult: {
+              content: [{
+                type: "text",
+                text: `Failed to fill ${args.selector}: ${(error as Error).message}`,
+              }],
+              isError: true,
+            },
+          };
+        }
+
+      case "puppeteer_evaluate":
+        try {
+          const result = await page.evaluate((script) => {
+            const logs: string[] = [];
+            const originalConsole = { ...console };
+            
+            ['log', 'info', 'warn', 'error'].forEach(method => {
+              (console as any)[method] = (...args: any[]) => {
+                logs.push(`[${method}] ${args.join(' ')}`);
+                (originalConsole as any)[method](...args);
+              };
+            });
+
+            try {
+              const result = eval(script);
+              Object.assign(console, originalConsole);
+              return { result, logs };
+            } catch (error) {
+              Object.assign(console, originalConsole);
+              throw error;
+            }
+          }, args.script);
 
-    return {
-      content: [{ type: "text", text: `Navigated to ${url}` }],
-      isError: false,
-    };
+          return {
+            toolResult: {
+              content: [
+                {
+                  type: "text",
+                  text: `Execution result:\n${JSON.stringify(result.result, null, 2)}\n\nConsole output:\n${result.logs.join('\n')}`,
+                },
+              ],
+              isError: false,
+            },
+          };
+        } catch (error) {
+          return {
+            toolResult: {
+              content: [{
+                type: "text",
+                text: `Script execution failed: ${(error as Error).message}`,
+              }],
+              isError: true,
+            },
+          };
+        }
+
+      default:
+        return {
+          toolResult: {
+            content: [{
+              type: "text",
+              text: `Unknown tool: ${name}`,
+            }],
+            isError: true,
+          },
+        };
+    }
   }
-  throw new Error(`Unknown tool: ${request.params.name}`);
-});
+
+  async start() {
+    const transport = new StdioServerTransport();
+    await this.server.connect(transport);
+  }
+}
 
 async function runServer() {
-  const transport = new StdioServerTransport();
-  await server.connect(transport);
+  const server = new PuppeteerServer();
+  await server.start();
 }
 
-runServer().catch(console.error);
+runServer().catch(console.error);
\ No newline at end of file

From f64c699c9511ad7fdccfee3b9e1e1b8b9250f045 Mon Sep 17 00:00:00 2001
From: Mahesh Murag <mahesh@anthropic.com>
Date: Tue, 19 Nov 2024 17:47:18 -0500
Subject: [PATCH 2/3] Remove classes

---
 src/puppeteer/index.ts | 423 ++++++++++++++++++++---------------------
 1 file changed, 206 insertions(+), 217 deletions(-)

diff --git a/src/puppeteer/index.ts b/src/puppeteer/index.ts
index c7d8959f..8f8f7d34 100644
--- a/src/puppeteer/index.ts
+++ b/src/puppeteer/index.ts
@@ -77,271 +77,260 @@ const TOOLS: Tool[] = [
   },
 ];
 
-class PuppeteerServer {
-  private browser?: Browser;
-  private page?: Page;
-  private consoleLogs: string[] = [];
-  private screenshots: Map<string, string> = new Map();
-  private server: Server;
+// Global state
+let browser: Browser | undefined;
+let page: Page | undefined;
+const consoleLogs: string[] = [];
+const screenshots = new Map<string, string>();
 
-  constructor() {
-    this.server = new Server({
-      name: "example-servers/puppeteer",
-      version: "0.4.0",
+async function ensureBrowser() {
+  if (!browser) {
+    browser = await puppeteer.launch({ headless: false });
+    const pages = await browser.pages();
+    page = pages[0];
+    
+    page.on("console", (msg) => {
+      const logEntry = `[${msg.type()}] ${msg.text()}`;
+      consoleLogs.push(logEntry);
+      server.notification({
+        method: "notifications/resources/updated",
+        params: { uri: "console://logs" },
+      });
     });
-    this.setupHandlers();
   }
+  return page!;
+}
 
-  private async ensureBrowser() {
-    if (!this.browser) {
-      this.browser = await puppeteer.launch({ headless: false });
-      const pages = await this.browser.pages();
-      this.page = pages[0];
-      
-      this.page.on("console", (msg) => {
-        const logEntry = `[${msg.type()}] ${msg.text()}`;
-        this.consoleLogs.push(logEntry);
-        this.server.notification({
-          method: "notifications/resources/updated",
-          params: { uri: "console://logs" },
-        });
-      });
-    }
-    return this.page!;
-  }
+async function handleToolCall(name: string, args: any): Promise<{ toolResult: CallToolResult }> {
+  const page = await ensureBrowser();
 
-  private setupHandlers() {
-    this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
-      resources: [
-        {
-          uri: "console://logs",
-          mimeType: "text/plain",
-          name: "Browser console logs",
+  switch (name) {
+    case "puppeteer_navigate":
+      await page.goto(args.url);
+      return {
+        toolResult: {
+          content: [{
+            type: "text",
+            text: `Navigated to ${args.url}`,
+          }],
+          isError: false,
         },
-        ...Array.from(this.screenshots.keys()).map(name => ({
-          uri: `screenshot://${name}`,
-          mimeType: "image/png",
-          name: `Screenshot: ${name}`,
-        })),
-      ],
-    }));
+      };
+
+    case "puppeteer_screenshot": {
+      const width = args.width ?? 800;
+      const height = args.height ?? 600;
+      await page.setViewport({ width, height });
+
+      const screenshot = await (args.selector ? 
+        (await page.$(args.selector))?.screenshot({ encoding: "base64" }) :
+        page.screenshot({ encoding: "base64", fullPage: false }));
 
-    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
-      const uri = request.params.uri.toString();
-      
-      if (uri === "console://logs") {
+      if (!screenshot) {
         return {
-          contents: [{
-            uri,
-            mimeType: "text/plain",
-            text: this.consoleLogs.join("\n"),
-          }],
+          toolResult: {
+            content: [{
+              type: "text",
+              text: args.selector ? `Element not found: ${args.selector}` : "Screenshot failed",
+            }],
+            isError: true,
+          },
         };
       }
 
-      if (uri.startsWith("screenshot://")) {
-        const name = uri.split("://")[1];
-        const screenshot = this.screenshots.get(name);
-        if (screenshot) {
-          return {
-            contents: [{
-              uri,
+      screenshots.set(args.name, screenshot as string);
+      server.notification({
+        method: "notifications/resources/list_changed",
+      });
+
+      return {
+        toolResult: {
+          content: [
+            {
+              type: "text",
+              text: `Screenshot '${args.name}' taken at ${width}x${height}`,
+            } as TextContent,
+            {
+              type: "image",
+              data: screenshot,
               mimeType: "image/png",
-              blob: screenshot,
+            } as ImageContent,
+          ],
+          isError: false,
+        },
+      };
+    }
+
+    case "puppeteer_click":
+      try {
+        await page.click(args.selector);
+        return {
+          toolResult: {
+            content: [{
+              type: "text",
+              text: `Clicked: ${args.selector}`,
             }],
-          };
-        }
+            isError: false,
+          },
+        };
+      } catch (error) {
+        return {
+          toolResult: {
+            content: [{
+              type: "text",
+              text: `Failed to click ${args.selector}: ${(error as Error).message}`,
+            }],
+            isError: true,
+          },
+        };
       }
 
-      throw new Error(`Resource not found: ${uri}`);
-    });
-
-    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
-      tools: TOOLS,
-    }));
-
-    this.server.setRequestHandler(CallToolRequestSchema, async (request) => 
-      this.handleToolCall(request.params.name, request.params.arguments ?? {})
-    );
-  }
-
-  private async handleToolCall(name: string, args: any): Promise<{ toolResult: CallToolResult }> {
-    const page = await this.ensureBrowser();
-
-    switch (name) {
-      case "puppeteer_navigate":
-        await page.goto(args.url);
+    case "puppeteer_fill":
+      try {
+        await page.waitForSelector(args.selector);
+        await page.type(args.selector, args.value);
         return {
           toolResult: {
             content: [{
               type: "text",
-              text: `Navigated to ${args.url}`,
+              text: `Filled ${args.selector} with: ${args.value}`,
             }],
             isError: false,
           },
         };
+      } catch (error) {
+        return {
+          toolResult: {
+            content: [{
+              type: "text",
+              text: `Failed to fill ${args.selector}: ${(error as Error).message}`,
+            }],
+            isError: true,
+          },
+        };
+      }
 
-      case "puppeteer_screenshot": {
-        const width = args.width ?? 800;
-        const height = args.height ?? 600;
-        await page.setViewport({ width, height });
-
-        const screenshot = await (args.selector ? 
-          (await page.$(args.selector))?.screenshot({ encoding: "base64" }) :
-          page.screenshot({ encoding: "base64", fullPage: false }));
+    case "puppeteer_evaluate":
+      try {
+        const result = await page.evaluate((script) => {
+          const logs: string[] = [];
+          const originalConsole = { ...console };
+          
+          ['log', 'info', 'warn', 'error'].forEach(method => {
+            (console as any)[method] = (...args: any[]) => {
+              logs.push(`[${method}] ${args.join(' ')}`);
+              (originalConsole as any)[method](...args);
+            };
+          });
 
-        if (!screenshot) {
-          return {
-            toolResult: {
-              content: [{
-                type: "text",
-                text: args.selector ? `Element not found: ${args.selector}` : "Screenshot failed",
-              }],
-              isError: true,
-            },
-          };
-        }
-
-        this.screenshots.set(args.name, screenshot as string);
-        this.server.notification({
-          method: "notifications/resources/list_changed",
-        });
+          try {
+            const result = eval(script);
+            Object.assign(console, originalConsole);
+            return { result, logs };
+          } catch (error) {
+            Object.assign(console, originalConsole);
+            throw error;
+          }
+        }, args.script);
 
         return {
           toolResult: {
             content: [
               {
                 type: "text",
-                text: `Screenshot '${args.name}' taken at ${width}x${height}`,
-              } as TextContent,
-              {
-                type: "image",
-                data: screenshot,
-                mimeType: "image/png",
-              } as ImageContent,
+                text: `Execution result:\n${JSON.stringify(result.result, null, 2)}\n\nConsole output:\n${result.logs.join('\n')}`,
+              },
             ],
             isError: false,
           },
         };
-      }
-
-      case "puppeteer_click":
-        try {
-          await page.click(args.selector);
-          return {
-            toolResult: {
-              content: [{
-                type: "text",
-                text: `Clicked: ${args.selector}`,
-              }],
-              isError: false,
-            },
-          };
-        } catch (error) {
-          return {
-            toolResult: {
-              content: [{
-                type: "text",
-                text: `Failed to click ${args.selector}: ${(error as Error).message}`,
-              }],
-              isError: true,
-            },
-          };
-        }
-
-      case "puppeteer_fill":
-        try {
-          await page.waitForSelector(args.selector);
-          await page.type(args.selector, args.value);
-          return {
-            toolResult: {
-              content: [{
-                type: "text",
-                text: `Filled ${args.selector} with: ${args.value}`,
-              }],
-              isError: false,
-            },
-          };
-        } catch (error) {
-          return {
-            toolResult: {
-              content: [{
-                type: "text",
-                text: `Failed to fill ${args.selector}: ${(error as Error).message}`,
-              }],
-              isError: true,
-            },
-          };
-        }
-
-      case "puppeteer_evaluate":
-        try {
-          const result = await page.evaluate((script) => {
-            const logs: string[] = [];
-            const originalConsole = { ...console };
-            
-            ['log', 'info', 'warn', 'error'].forEach(method => {
-              (console as any)[method] = (...args: any[]) => {
-                logs.push(`[${method}] ${args.join(' ')}`);
-                (originalConsole as any)[method](...args);
-              };
-            });
-
-            try {
-              const result = eval(script);
-              Object.assign(console, originalConsole);
-              return { result, logs };
-            } catch (error) {
-              Object.assign(console, originalConsole);
-              throw error;
-            }
-          }, args.script);
-
-          return {
-            toolResult: {
-              content: [
-                {
-                  type: "text",
-                  text: `Execution result:\n${JSON.stringify(result.result, null, 2)}\n\nConsole output:\n${result.logs.join('\n')}`,
-                },
-              ],
-              isError: false,
-            },
-          };
-        } catch (error) {
-          return {
-            toolResult: {
-              content: [{
-                type: "text",
-                text: `Script execution failed: ${(error as Error).message}`,
-              }],
-              isError: true,
-            },
-          };
-        }
-
-      default:
+      } catch (error) {
         return {
           toolResult: {
             content: [{
               type: "text",
-              text: `Unknown tool: ${name}`,
+              text: `Script execution failed: ${(error as Error).message}`,
             }],
             isError: true,
           },
         };
-    }
-  }
+      }
 
-  async start() {
-    const transport = new StdioServerTransport();
-    await this.server.connect(transport);
+    default:
+      return {
+        toolResult: {
+          content: [{
+            type: "text",
+            text: `Unknown tool: ${name}`,
+          }],
+          isError: true,
+        },
+      };
   }
 }
 
+const server = new Server({
+  name: "example-servers/puppeteer",
+  version: "0.4.0",
+});
+
+// Setup request handlers
+server.setRequestHandler(ListResourcesRequestSchema, async () => ({
+  resources: [
+    {
+      uri: "console://logs",
+      mimeType: "text/plain",
+      name: "Browser console logs",
+    },
+    ...Array.from(screenshots.keys()).map(name => ({
+      uri: `screenshot://${name}`,
+      mimeType: "image/png",
+      name: `Screenshot: ${name}`,
+    })),
+  ],
+}));
+
+server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
+  const uri = request.params.uri.toString();
+  
+  if (uri === "console://logs") {
+    return {
+      contents: [{
+        uri,
+        mimeType: "text/plain",
+        text: consoleLogs.join("\n"),
+      }],
+    };
+  }
+
+  if (uri.startsWith("screenshot://")) {
+    const name = uri.split("://")[1];
+    const screenshot = screenshots.get(name);
+    if (screenshot) {
+      return {
+        contents: [{
+          uri,
+          mimeType: "image/png",
+          blob: screenshot,
+        }],
+      };
+    }
+  }
+
+  throw new Error(`Resource not found: ${uri}`);
+});
+
+server.setRequestHandler(ListToolsRequestSchema, async () => ({
+  tools: TOOLS,
+}));
+
+server.setRequestHandler(CallToolRequestSchema, async (request) => 
+  handleToolCall(request.params.name, request.params.arguments ?? {})
+);
+
 async function runServer() {
-  const server = new PuppeteerServer();
-  await server.start();
+  const transport = new StdioServerTransport();
+  await server.connect(transport);
 }
 
 runServer().catch(console.error);
\ No newline at end of file

From b005dc9eef55dfe871d8fb3fa83cd82ec8d90fd8 Mon Sep 17 00:00:00 2001
From: Mahesh Murag <mahesh@anthropic.com>
Date: Tue, 19 Nov 2024 17:58:02 -0500
Subject: [PATCH 3/3] Remove classes

---
 src/puppeteer/index.ts | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/puppeteer/index.ts b/src/puppeteer/index.ts
index 8f8f7d34..9ad05cbe 100644
--- a/src/puppeteer/index.ts
+++ b/src/puppeteer/index.ts
@@ -269,10 +269,19 @@ async function handleToolCall(name: string, args: any): Promise<{ toolResult: Ca
   }
 }
 
-const server = new Server({
-  name: "example-servers/puppeteer",
-  version: "0.4.0",
-});
+const server = new Server(
+  {
+    name: "example-servers/puppeteer",
+    version: "0.1.0",
+  },
+  {
+    capabilities: {
+      resources: {},
+      tools: {},
+    },
+  },
+);
+
 
 // Setup request handlers
 server.setRequestHandler(ListResourcesRequestSchema, async () => ({