-
Notifications
You must be signed in to change notification settings - Fork 0
feat: implement launchBrowser tool #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| import { z } from "zod"; | ||
| import { ToolDefinition, Context } from "../types.js"; | ||
| import { ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js"; | ||
| import { contextProvider } from "../context-provider.js"; | ||
| import { createSimpleResponse, createErrorResponse } from "../responses/index.js"; | ||
| import { BrowserContext, type BrowserOptions } from "../browser-context.js"; | ||
|
|
||
| const desiredCapabilitiesSchema = z | ||
| .object({}) | ||
| .catchall(z.unknown()) | ||
| .superRefine((value, ctx) => { | ||
| const browserName = value?.["browserName"]; | ||
|
|
||
| if (browserName !== undefined && typeof browserName !== "string") { | ||
| ctx.addIssue({ code: z.ZodIssueCode.custom, message: '"browserName" must be a string' }); | ||
| } | ||
| }) | ||
| .describe( | ||
| 'WebDriver desiredCapabilities that should be used when launching the browser. Example to launch Chrome with mobile emulation: {"browserName":"chrome","goog:chromeOptions":{"mobileEmulation":{"deviceMetrics":{"width":360,"height":800,"pixelRatio":1.0}}}}', | ||
| ); | ||
|
|
||
| const windowSizeSchema = z | ||
| .union([ | ||
| z | ||
| .object({ | ||
| width: z.number().int().positive(), | ||
| height: z.number().int().positive(), | ||
| }) | ||
| .strict(), | ||
| z | ||
| .string() | ||
| .trim() | ||
| .regex(/^[0-9]+x[0-9]+$/, { | ||
| message: '"windowSize" should use the format "<width>x<height>" (e.g. "1600x900")', | ||
| }), | ||
| z.null(), | ||
| ]) | ||
| .optional() | ||
| .describe( | ||
| 'Viewport to use for the session. Provide {"width": number, "height": number} or a string like "1280x720"; use null to reset to the default size.', | ||
| ); | ||
|
|
||
| export const launchBrowserSchema = { | ||
| desiredCapabilities: desiredCapabilitiesSchema.optional(), | ||
| gridUrl: z | ||
| .string() | ||
| .default("local") | ||
| .describe( | ||
| 'WebDriver endpoint to connect to. "local" (default) lets Testplane MCP manage Chrome and Firefox automatically; set a Selenium grid URL only when you need other browsers.', | ||
| ), | ||
| windowSize: windowSizeSchema, | ||
| }; | ||
|
|
||
| const launchBrowserCb: ToolCallback<typeof launchBrowserSchema> = async args => { | ||
| try { | ||
| const context = contextProvider.getContext(); | ||
| const desiredCapabilities = args.desiredCapabilities as BrowserOptions["desiredCapabilities"]; | ||
| const gridUrl = args.gridUrl ?? "local"; | ||
| const windowSizeInput = args.windowSize; | ||
|
|
||
| if (await context.browser.isActive()) { | ||
| console.error("Closing existing browser before launching a new one"); | ||
| await context.browser.close(); | ||
| } | ||
|
|
||
| const updatedOptions: BrowserOptions = { | ||
| ...context.browser.getOptions(), | ||
| }; | ||
|
|
||
| if (Object.prototype.hasOwnProperty.call(args, "desiredCapabilities")) { | ||
| updatedOptions.desiredCapabilities = desiredCapabilities; | ||
| } | ||
|
|
||
| if (!gridUrl || gridUrl === "local") { | ||
| delete updatedOptions.gridUrl; | ||
| } else { | ||
| updatedOptions.gridUrl = gridUrl; | ||
| } | ||
|
|
||
| if (Object.prototype.hasOwnProperty.call(args, "windowSize")) { | ||
| if (windowSizeInput === null) { | ||
| updatedOptions.windowSize = null; | ||
| } else if (typeof windowSizeInput === "string") { | ||
| const [width, height] = windowSizeInput.split("x").map(value => Number.parseInt(value, 10)); | ||
| updatedOptions.windowSize = { width, height }; | ||
| } else if (windowSizeInput === undefined) { | ||
| delete updatedOptions.windowSize; | ||
| } else { | ||
| updatedOptions.windowSize = windowSizeInput as BrowserOptions["windowSize"]; | ||
| } | ||
| } | ||
|
|
||
| const browserContext = new BrowserContext(updatedOptions); | ||
| const newContext: Context = { | ||
| browser: browserContext, | ||
| }; | ||
| contextProvider.setContext(newContext); | ||
|
|
||
| await browserContext.get(); | ||
|
|
||
| return createSimpleResponse("Successfully launched browser session"); | ||
| } catch (error) { | ||
| console.error("Error launching browser:", error); | ||
| return createErrorResponse("Error launching browser", error instanceof Error ? error : undefined); | ||
| } | ||
| }; | ||
|
|
||
| export const launchBrowser: ToolDefinition<typeof launchBrowserSchema> = { | ||
| name: "launchBrowser", | ||
| description: | ||
| "Launch a new browser session with custom desired capabilities. Avoid using this tool unless the user explicitly requests a custom browser configuration; browsers are launched automatically for commands like navigate to URL. Testplane MCP can ONLY download Chrome and Firefox automatically, for other browsers you MUST ensure that driver is launched and provide it as custom gridUrl.", | ||
| schema: launchBrowserSchema, | ||
| cb: launchBrowserCb, | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <title>Mobile Emulation Diagnostics</title> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
| <style> | ||
| body { | ||
| font-family: Arial, sans-serif; | ||
| margin: 20px; | ||
| line-height: 1.6; | ||
| } | ||
| h1 { | ||
| color: #333; | ||
| } | ||
| p { | ||
| margin: 0.5rem 0; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <h1>Mobile Emulation Diagnostics</h1> | ||
| <p id="viewport-width">Viewport width:</p> | ||
| <p id="viewport-height">Viewport height:</p> | ||
| <p id="device-pixel-ratio">Device pixel ratio:</p> | ||
| <p id="user-agent">User agent:</p> | ||
| <script> | ||
| const $ = id => document.getElementById(id); | ||
|
|
||
| const updateDiagnostics = () => { | ||
| $("viewport-width").textContent = `Viewport width: ${Math.round(window.innerWidth)}`; | ||
| $("viewport-height").textContent = `Viewport height: ${Math.round(window.innerHeight)}`; | ||
| $("device-pixel-ratio").textContent = `Device pixel ratio: ${window.devicePixelRatio}`; | ||
| $("user-agent").textContent = `User agent: ${navigator.userAgent}`; | ||
| }; | ||
|
|
||
| updateDiagnostics(); | ||
| window.addEventListener("resize", updateDiagnostics); | ||
| </script> | ||
| </body> | ||
| </html> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is "Object.prototype.hasOwnProperty" different from "Object.hasOwn"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the difference is very subtle