mcpbrowser 0.3.52 → 0.3.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Claude Desktop](https://img.shields.io/badge/Claude-Desktop-5865F2?logo=anthropic)](https://modelcontextprotocol.io/quickstart/user)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- > ⚠️ **Security Notice:** MCPBrowser extracts webpage content and provides it to your AI agent (e.g., GitHub Copilot, Claude, Kiro, Antigravity), which then sends it to the LLM provider it uses (e.g., Anthropic, OpenAI, GitHub) for processing. Make sure you trust both your agent and the LLM provider — especially when accessing pages with sensitive or private data.
8
+ > ⚠️ **Security Notice:** MCPBrowser extracts webpage content and provides it to your AI agent (e.g., GitHub Copilot, Claude, Kiro, Antigravity), which then sends it to the LLM provider it uses (e.g., Anthropic, OpenAI, GitHub) for processing. Make sure you trust both your agent and the LLM provider — especially when accessing pages with sensitive or private data. MCPBrowser combines private data access, untrusted content exposure, and external communication (the MCP "[lethal trifecta](https://blog.modelcontextprotocol.io/posts/2026-03-16-tool-annotations/)") — all tool annotations accurately declare these risks so MCP clients can enforce appropriate safety controls. See [MCP_COMPLIANCE.md](docs/MCP_COMPLIANCE.md) for details.
9
9
 
10
10
  > 💡 **Why MCPBrowser over Puppeteer/Playwright MCP servers?** Puppeteer and Playwright are browser automation libraries — their MCP servers give agents raw, low-level browser commands. MCPBrowser uses Puppeteer under the hood and was built specifically for AI agents, adding an intelligence layer that handles the hard parts automatically.
11
11
  >
@@ -167,7 +167,7 @@ For VS Code, Kiro, Antigravity, or other editors — manual MCP setup. Add to yo
167
167
 
168
168
  ### `browser_fetch_webpage`
169
169
 
170
- Fetches web pages using your Chrome/Edge browser. Handles authentication, CAPTCHA, SSO, anti-bot protection, and JavaScript-heavy sites. Opens the URL in a browser tab (reuses existing tab for same domain) and waits for the page to fully load before returning content. **Automatically detects SPAs** (React, Vue, Angular) and waits for JavaScript to render content.
170
+ Fetches web pages using your Chrome/Edge/Brave browser. Handles authentication, CAPTCHA, SSO, anti-bot protection, and JavaScript-heavy sites. Opens the URL in a browser tab (reuses existing tab for same domain) and waits for the page to fully load before returning content. **Automatically detects SPAs** (React, Vue, Angular) and waits for JavaScript to render content.
171
171
 
172
172
  **Parameters:**
173
173
  - `url` (string, required) - The URL to fetch
@@ -458,7 +458,7 @@ Environment variables for advanced setup:
458
458
 
459
459
  | Variable | Description | Default |
460
460
  |----------|-------------|---------|
461
- | `CHROME_PATH` | Path to Chrome/Edge | Auto-detect |
461
+ | `CHROME_PATH` | Path to Chrome/Edge/Brave | Auto-detect |
462
462
  | `CHROME_USER_DATA_DIR` | Browser profile directory | `%LOCALAPPDATA%/ChromeAuthProfile` |
463
463
  | `CHROME_REMOTE_DEBUG_PORT` | DevTools port | `9222` |
464
464
 
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "mcpbrowser",
3
- "version": "0.3.52",
3
+ "version": "0.3.53",
4
4
  "mcpName": "io.github.cherchyk/mcpbrowser",
5
5
  "type": "module",
6
- "description": "MCP browser server — load and interact with any web page using a real Chrome/Edge/Brave browser with full JavaScript execution. Handles login flows, authentication, SSO, CAPTCHAs, and anti-bot protection. Use whenever a real browser is needed to load a page, especially when JavaScript rendering or user login is required.",
6
+ "description": "MCP browser server — loads and interacts with web pages using the user's Chromium-based browser with full JavaScript execution. Handles authentication, SSO, and anti-bot protection automatically via the user's existing browser session.",
7
7
  "main": "src/mcp-browser.js",
8
8
  "bin": {
9
9
  "mcpbrowser": "src/mcp-browser.js"
@@ -13,7 +13,8 @@
13
13
  "test": "node tests/run-all.js",
14
14
  "test:unit": "node tests/run-unit.js",
15
15
  "test:cli": "node tests/cli.test.js",
16
- "test:descriptions": "node tests/tool-selection/run-tool-selection-tests.js"
16
+ "test:descriptions": "node tests/tool-selection/run-tool-selection-tests.js",
17
+ "build:mcpb": "node scripts/build-mcpb.js"
17
18
  },
18
19
  "keywords": [
19
20
  "mcp",
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Build script for MCP Bundle (.mcpb) format.
4
+ * Creates a portable ZIP archive that MCP clients can install with one click.
5
+ *
6
+ * Usage: node scripts/build-mcpb.js
7
+ * Output: dist/mcpbrowser-<version>.mcpb
8
+ *
9
+ * See: https://github.com/modelcontextprotocol/mcpb/blob/main/MANIFEST.md
10
+ */
11
+
12
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync, rmSync } from 'fs';
13
+ import { join, dirname } from 'path';
14
+ import { fileURLToPath, pathToFileURL } from 'url';
15
+ import { execSync } from 'child_process';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+ const ROOT = join(__dirname, '..');
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // 1. Read package.json for version and metadata
23
+ // ---------------------------------------------------------------------------
24
+ const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // 2. Dynamically import action modules to extract tool name + description
28
+ // ---------------------------------------------------------------------------
29
+ async function getTools() {
30
+ const actionFiles = [
31
+ 'fetch-page.js', 'get-current-html.js', 'take-screenshot.js',
32
+ 'detect-forms.js', 'click-element.js', 'type-text.js',
33
+ 'scroll-page.js', 'navigate-history.js', 'execute-javascript.js',
34
+ 'close-tab.js', 'plugin-info.js', 'plugin-action.js'
35
+ ];
36
+
37
+ const tools = [];
38
+ for (const file of actionFiles) {
39
+ const mod = await import(pathToFileURL(join(ROOT, 'src', 'actions', file)).href);
40
+ // Each module exports a *_TOOL constant
41
+ const toolExport = Object.values(mod).find(v => v && typeof v === 'object' && v.name && v.description);
42
+ if (toolExport) {
43
+ tools.push({ name: toolExport.name, description: toolExport.description });
44
+ }
45
+ }
46
+ return tools;
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // 3. Import prompts
51
+ // ---------------------------------------------------------------------------
52
+ async function getPrompts() {
53
+ const { PROMPTS } = await import(pathToFileURL(join(ROOT, 'src', 'core', 'prompts.js')).href);
54
+ return PROMPTS.map(p => ({
55
+ name: p.name,
56
+ description: p.description,
57
+ arguments: p.arguments?.map(a => a.name) || []
58
+ }));
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // 4. Build manifest.json
63
+ // ---------------------------------------------------------------------------
64
+ async function buildManifest() {
65
+ const tools = await getTools();
66
+ const prompts = await getPrompts();
67
+
68
+ return {
69
+ manifest_version: "0.3",
70
+ name: "mcpbrowser",
71
+ display_name: "MCP Browser",
72
+ version: pkg.version,
73
+ description: pkg.description,
74
+ long_description: "Browser automation MCP server that connects to the user's existing Chromium-based browser (Chrome, Edge, Brave) with all cookies, logins, and SSO sessions intact. Provides 12 tools for fetching pages, clicking elements, typing text, taking screenshots, detecting forms, executing JavaScript, and more. Includes site-specific plugins for optimized interactions with known services.",
75
+ author: {
76
+ name: "cherchyk",
77
+ url: "https://github.com/cherchyk"
78
+ },
79
+ repository: {
80
+ type: "git",
81
+ url: "https://github.com/cherchyk/MCPBrowser.git"
82
+ },
83
+ homepage: "https://github.com/cherchyk/MCPBrowser#readme",
84
+ support: "https://github.com/cherchyk/MCPBrowser/issues",
85
+ icon: "icon.png",
86
+ license: "MIT",
87
+ server: {
88
+ type: "node",
89
+ entry_point: "server/src/mcp-browser.js",
90
+ mcp_config: {
91
+ command: "node",
92
+ args: ["${__dirname}/server/src/mcp-browser.js"],
93
+ env: {}
94
+ }
95
+ },
96
+ tools,
97
+ tools_generated: false,
98
+ prompts,
99
+ prompts_generated: false,
100
+ keywords: [
101
+ "browser", "web-automation", "puppeteer", "chrome", "edge",
102
+ "authentication", "sso", "mcp-server", "scraping"
103
+ ],
104
+ compatibility: {
105
+ platforms: ["darwin", "win32", "linux"],
106
+ runtimes: {
107
+ node: ">=18.0.0"
108
+ }
109
+ }
110
+ };
111
+ }
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // 5. Package into .mcpb (ZIP)
115
+ // ---------------------------------------------------------------------------
116
+ async function build() {
117
+ const distDir = join(ROOT, 'dist');
118
+ const stageDir = join(distDir, '_mcpb_stage');
119
+
120
+ // Clean previous build
121
+ if (existsSync(stageDir)) rmSync(stageDir, { recursive: true });
122
+ mkdirSync(join(stageDir, 'server'), { recursive: true });
123
+
124
+ // Generate manifest.json
125
+ const manifest = await buildManifest();
126
+ writeFileSync(join(stageDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
127
+ console.log(`✅ manifest.json (${manifest.tools.length} tools, ${manifest.prompts.length} prompts)`);
128
+
129
+ // Copy icon
130
+ const iconSrc = join(ROOT, '..', 'VSCodeExtension', 'icon.png');
131
+ if (existsSync(iconSrc)) {
132
+ cpSync(iconSrc, join(stageDir, 'icon.png'));
133
+ console.log('✅ icon.png');
134
+ } else {
135
+ console.log('⚠️ icon.png not found at VSCodeExtension/icon.png — skipping');
136
+ }
137
+
138
+ // Copy server files (src/ + package.json)
139
+ cpSync(join(ROOT, 'src'), join(stageDir, 'server', 'src'), { recursive: true });
140
+ cpSync(join(ROOT, 'package.json'), join(stageDir, 'server', 'package.json'));
141
+ console.log('✅ server/src + server/package.json');
142
+
143
+ // Install production dependencies in staging (clean, minimal node_modules)
144
+ console.log('📦 Installing production dependencies...');
145
+ execSync('npm install --omit=dev --ignore-scripts', {
146
+ cwd: join(stageDir, 'server'),
147
+ stdio: 'pipe'
148
+ });
149
+ // Move node_modules up to bundle root (MCPB convention)
150
+ const nmStaged = join(stageDir, 'server', 'node_modules');
151
+ if (existsSync(nmStaged)) {
152
+ cpSync(nmStaged, join(stageDir, 'node_modules'), { recursive: true });
153
+ rmSync(nmStaged, { recursive: true });
154
+ }
155
+ console.log('✅ node_modules (production only)');
156
+
157
+ // Create .mcpb ZIP using .NET ZipFile (fast, cross-platform on PowerShell/Node)
158
+ const outFile = join(distDir, `mcpbrowser-${pkg.version}.mcpb`);
159
+ if (existsSync(outFile)) rmSync(outFile);
160
+
161
+ if (process.platform === 'win32') {
162
+ // .NET ZipFile is orders of magnitude faster than Compress-Archive
163
+ execSync(
164
+ `powershell -NoProfile -Command "Add-Type -Assembly System.IO.Compression.FileSystem; [IO.Compression.ZipFile]::CreateFromDirectory('${stageDir}', '${outFile}')"`,
165
+ { stdio: 'inherit' }
166
+ );
167
+ } else {
168
+ execSync(`cd "${stageDir}" && zip -r "${outFile}" .`, { stdio: 'inherit' });
169
+ }
170
+
171
+ // Clean staging (use system rmdir on Windows — much faster for deep node_modules)
172
+ if (process.platform === 'win32') {
173
+ execSync(`rmdir /s /q "${stageDir}"`, { stdio: 'pipe' });
174
+ } else {
175
+ rmSync(stageDir, { recursive: true });
176
+ }
177
+
178
+ const stats = readFileSync(outFile);
179
+ const sizeMB = (stats.length / 1024 / 1024).toFixed(2);
180
+ console.log(`\n📦 ${outFile}`);
181
+ console.log(` Size: ${sizeMB} MB`);
182
+ console.log(` Version: ${pkg.version}`);
183
+ console.log(` Tools: ${manifest.tools.length}`);
184
+ console.log(` Prompts: ${manifest.prompts.length}`);
185
+ }
186
+
187
+ build().catch(err => {
188
+ console.error('❌ Build failed:', err.message);
189
+ process.exit(1);
190
+ });
@@ -159,6 +159,13 @@ export const CLICK_ELEMENT_TOOL = {
159
159
  },
160
160
  required: ["status", "fallbackUsed", "nativeAttempt", "currentUrl", "message", "html", "nextSteps"],
161
161
  additionalProperties: false
162
+ },
163
+ annotations: {
164
+ title: "Click Element",
165
+ readOnlyHint: false,
166
+ destructiveHint: false,
167
+ idempotentHint: false,
168
+ openWorldHint: true
162
169
  }
163
170
  };
164
171
 
@@ -81,6 +81,13 @@ export const CLOSE_TAB_TOOL = {
81
81
  },
82
82
  required: ["message", "hostname", "nextSteps"],
83
83
  additionalProperties: false
84
+ },
85
+ annotations: {
86
+ title: "Close Tab",
87
+ readOnlyHint: false,
88
+ destructiveHint: true,
89
+ idempotentHint: true,
90
+ openWorldHint: false
84
91
  }
85
92
  };
86
93
 
@@ -149,6 +149,13 @@ export const DETECT_FORMS_TOOL = {
149
149
  },
150
150
  required: ["forms", "orphanedFields", "totalFieldCount", "summary", "nextSteps"],
151
151
  additionalProperties: false
152
+ },
153
+ annotations: {
154
+ title: "Detect Forms",
155
+ readOnlyHint: true,
156
+ destructiveHint: false,
157
+ idempotentHint: true,
158
+ openWorldHint: false
152
159
  }
153
160
  };
154
161
 
@@ -90,6 +90,13 @@ export const EXECUTE_JAVASCRIPT_TOOL = {
90
90
  },
91
91
  required: ['type', 'executionTimeMs', 'truncated', 'urlChanged', 'currentUrl', 'nextSteps'],
92
92
  additionalProperties: false
93
+ },
94
+ annotations: {
95
+ title: 'Execute JavaScript',
96
+ readOnlyHint: false,
97
+ destructiveHint: true,
98
+ idempotentHint: false,
99
+ openWorldHint: true
93
100
  }
94
101
  };
95
102
 
@@ -74,14 +74,14 @@ export class FetchPageSuccessResponse extends MCPResponse {
74
74
  export const FETCH_WEBPAGE_TOOL = {
75
75
  name: "browser_fetch_webpage",
76
76
  title: "Fetch Web Page",
77
- description: "Load any URL and return its content using a real browser with full JavaScript execution. Use when: user shares a URL, you need to read a webpage, fetch content from a link, open a page, check a website, load a GitHub PR/issue, read documentation, or access any HTTP/HTTPS resource. Handles JavaScript rendering, login/SSO, CAPTCHA, and anti-bot protection automatically. Works on all sites including those behind authentication. Prefer this over generic HTTP fetch it always works, even on authenticated or JS-heavy pages that require proper JavaScript execution or user login.",
77
+ description: "Load any URL and return its content using a real browser with full JavaScript execution. Use whenever a webpage needs to be loaded — whether to read content, interact with a web app, or access any HTTP/HTTPS resource. Connects to the user's existing browser session with all cookies, logins, and SSO sessions available automatically. Handles JavaScript rendering, authentication, and anti-bot protection. Prefer this over generic HTTP fetch for authenticated or JS-heavy pages.",
78
78
  inputSchema: {
79
79
  type: "object",
80
80
  properties: {
81
81
  url: { type: "string", description: "The URL to fetch" },
82
82
  browser: {
83
83
  type: "string",
84
- description: "Browser to use: 'chrome' or 'edge'. Leave empty for auto-detection. Chrome/Edge use CDP to access existing sessions for authenticated sites.",
84
+ description: "Browser to use: 'chrome' or 'edge'. Leave empty for auto-detection. Uses CDP to connect to the user's existing browser session.",
85
85
  enum: ["", "chrome", "edge"]
86
86
  },
87
87
  removeUnnecessaryHTML: { type: "boolean", description: "Remove Unnecessary HTML for size reduction by 90%.", default: true },
@@ -113,6 +113,13 @@ export const FETCH_WEBPAGE_TOOL = {
113
113
  },
114
114
  required: ["currentUrl", "html", "nextSteps"],
115
115
  additionalProperties: false
116
+ },
117
+ annotations: {
118
+ title: "Fetch Web Page",
119
+ readOnlyHint: true,
120
+ destructiveHint: false,
121
+ idempotentHint: true,
122
+ openWorldHint: true
116
123
  }
117
124
  };
118
125
 
@@ -105,6 +105,13 @@ export const GET_CURRENT_HTML_TOOL = {
105
105
  },
106
106
  required: ["currentUrl", "html", "nextSteps"],
107
107
  additionalProperties: false
108
+ },
109
+ annotations: {
110
+ title: "Get Current HTML",
111
+ readOnlyHint: true,
112
+ destructiveHint: false,
113
+ idempotentHint: true,
114
+ openWorldHint: false
108
115
  }
109
116
  };
110
117
 
@@ -108,6 +108,7 @@ export const NAVIGATE_HISTORY_TOOL = {
108
108
  title: "Navigate Back/Forward",
109
109
  readOnlyHint: false,
110
110
  destructiveHint: false,
111
+ idempotentHint: false,
111
112
  openWorldHint: true
112
113
  }
113
114
  };
@@ -70,6 +70,13 @@ export const PLUGIN_ACTION_TOOL = {
70
70
  },
71
71
  required: ["nextSteps"],
72
72
  additionalProperties: true
73
+ },
74
+ annotations: {
75
+ title: "Plugin Action",
76
+ readOnlyHint: false,
77
+ destructiveHint: false,
78
+ idempotentHint: false,
79
+ openWorldHint: true
73
80
  }
74
81
  };
75
82
 
@@ -81,6 +81,13 @@ export const PLUGIN_INFO_TOOL = {
81
81
  },
82
82
  required: ["nextSteps"],
83
83
  additionalProperties: true
84
+ },
85
+ annotations: {
86
+ title: "Plugin Info",
87
+ readOnlyHint: true,
88
+ destructiveHint: false,
89
+ idempotentHint: true,
90
+ openWorldHint: false
84
91
  }
85
92
  };
86
93
 
@@ -146,6 +146,13 @@ export const SCROLL_PAGE_TOOL = {
146
146
  },
147
147
  required: ["currentUrl", "scrollX", "scrollY", "pageWidth", "pageHeight", "viewportWidth", "viewportHeight", "nextSteps"],
148
148
  additionalProperties: false
149
+ },
150
+ annotations: {
151
+ title: "Scroll Page",
152
+ readOnlyHint: false,
153
+ destructiveHint: false,
154
+ idempotentHint: false,
155
+ openWorldHint: false
149
156
  }
150
157
  };
151
158
 
@@ -113,6 +113,13 @@ export const TAKE_SCREENSHOT_TOOL = {
113
113
  },
114
114
  required: ["currentUrl", "screenshotBase64", "mimeType", "nextSteps"],
115
115
  additionalProperties: false
116
+ },
117
+ annotations: {
118
+ title: "Take Screenshot",
119
+ readOnlyHint: true,
120
+ destructiveHint: false,
121
+ idempotentHint: true,
122
+ openWorldHint: false
116
123
  }
117
124
  };
118
125
 
@@ -111,6 +111,13 @@ export const TYPE_TEXT_TOOL = {
111
111
  },
112
112
  required: ["currentUrl", "message", "html", "nextSteps"],
113
113
  additionalProperties: false
114
+ },
115
+ annotations: {
116
+ title: "Type Text",
117
+ readOnlyHint: false,
118
+ destructiveHint: false,
119
+ idempotentHint: false,
120
+ openWorldHint: false
114
121
  }
115
122
  };
116
123
 
@@ -0,0 +1,148 @@
1
+ /**
2
+ * MCP Prompts — reusable workflow templates exposed as slash-commands.
3
+ * Each prompt returns a message array that guides the LLM through
4
+ * a multi-tool workflow using MCPBrowser's tools.
5
+ */
6
+
7
+ // ============================================================================
8
+ // PROMPT DEFINITIONS
9
+ // ============================================================================
10
+
11
+ export const PROMPTS = [
12
+ {
13
+ name: "scrape-page",
14
+ description: "Fetch a web page and extract its content as clean HTML",
15
+ arguments: [
16
+ { name: "url", description: "URL to scrape", required: true },
17
+ { name: "selector", description: "CSS selector to scope extraction (optional)", required: false }
18
+ ]
19
+ },
20
+ {
21
+ name: "fill-form",
22
+ description: "Detect form fields on a page and fill them with provided values",
23
+ arguments: [
24
+ { name: "url", description: "URL of the page with the form", required: true },
25
+ { name: "values", description: "JSON object mapping field selectors to values, e.g. {\"#email\": \"user@example.com\", \"#password\": \"secret\"}", required: true }
26
+ ]
27
+ },
28
+ {
29
+ name: "visual-audit",
30
+ description: "Take a screenshot and get HTML of a page for visual comparison or accessibility review",
31
+ arguments: [
32
+ { name: "url", description: "URL to audit", required: true }
33
+ ]
34
+ },
35
+ {
36
+ name: "authenticated-workflow",
37
+ description: "Navigate an authenticated site — fetch the page, handle login if needed, then continue",
38
+ arguments: [
39
+ { name: "url", description: "URL that may require authentication", required: true }
40
+ ]
41
+ }
42
+ ];
43
+
44
+ // ============================================================================
45
+ // PROMPT MESSAGE GENERATORS
46
+ // ============================================================================
47
+
48
+ /**
49
+ * Returns the messages array for a given prompt name and arguments.
50
+ * @param {string} name - Prompt name
51
+ * @param {Record<string, string>} args - Prompt arguments
52
+ * @returns {{ description: string, messages: Array<{ role: string, content: { type: string, text: string } }> }}
53
+ */
54
+ export function getPromptMessages(name, args = {}) {
55
+ const prompt = PROMPTS.find(p => p.name === name);
56
+ if (!prompt) {
57
+ throw new Error(`Unknown prompt: ${name}`);
58
+ }
59
+
60
+ // Validate required arguments
61
+ const missing = (prompt.arguments || [])
62
+ .filter(a => a.required && !args[a.name])
63
+ .map(a => a.name);
64
+ if (missing.length > 0) {
65
+ throw new Error(`Missing required argument(s) for prompt "${name}": ${missing.join(', ')}`);
66
+ }
67
+
68
+ switch (name) {
69
+ case "scrape-page":
70
+ return buildScrapePage(args);
71
+ case "fill-form":
72
+ return buildFillForm(args);
73
+ case "visual-audit":
74
+ return buildVisualAudit(args);
75
+ case "authenticated-workflow":
76
+ return buildAuthenticatedWorkflow(args);
77
+ default:
78
+ throw new Error(`Unknown prompt: ${name}`);
79
+ }
80
+ }
81
+
82
+ // ============================================================================
83
+ // BUILDERS
84
+ // ============================================================================
85
+
86
+ function buildScrapePage({ url, selector }) {
87
+ const selectorInstruction = selector
88
+ ? ` Focus on the content matching the CSS selector \`${selector}\`.`
89
+ : '';
90
+
91
+ return {
92
+ description: `Scrape content from ${url}`,
93
+ messages: [
94
+ {
95
+ role: "user",
96
+ content: {
97
+ type: "text",
98
+ text: `Fetch the web page at ${url} using browser_fetch_webpage.${selectorInstruction} Return the extracted HTML content. If the page requires scrolling to load more content, use browser_scroll_page to load it, then browser_get_current_html to capture the full page.`
99
+ }
100
+ }
101
+ ]
102
+ };
103
+ }
104
+
105
+ function buildFillForm({ url, values }) {
106
+ return {
107
+ description: `Fill form on ${url}`,
108
+ messages: [
109
+ {
110
+ role: "user",
111
+ content: {
112
+ type: "text",
113
+ text: `Fill out the form on ${url}:\n\n1. Fetch the page with browser_fetch_webpage.\n2. Detect available forms with browser_detect_forms.\n3. For each field in the values below, use browser_type_text to enter the value:\n\n${values}\n\n4. After filling all fields, use browser_click_element to submit the form (look for a submit button).\n5. Return the resulting page HTML with browser_get_current_html.`
114
+ }
115
+ }
116
+ ]
117
+ };
118
+ }
119
+
120
+ function buildVisualAudit({ url }) {
121
+ return {
122
+ description: `Visual audit of ${url}`,
123
+ messages: [
124
+ {
125
+ role: "user",
126
+ content: {
127
+ type: "text",
128
+ text: `Perform a visual audit of ${url}:\n\n1. Fetch the page with browser_fetch_webpage.\n2. Take a full-page screenshot with browser_take_screenshot.\n3. Get the current HTML with browser_get_current_html.\n4. Analyze the screenshot and HTML for:\n - Layout issues or broken elements\n - Missing alt text on images\n - Color contrast problems\n - Mobile responsiveness concerns\n5. Provide a summary of findings.`
129
+ }
130
+ }
131
+ ]
132
+ };
133
+ }
134
+
135
+ function buildAuthenticatedWorkflow({ url }) {
136
+ return {
137
+ description: `Authenticated workflow for ${url}`,
138
+ messages: [
139
+ {
140
+ role: "user",
141
+ content: {
142
+ type: "text",
143
+ text: `Navigate to ${url} using browser_fetch_webpage. This page may require authentication.\n\nIf the page shows a login form or redirects to a login page:\n1. Notify me that authentication is needed.\n2. Wait for me to complete the login in the browser window.\n3. After I confirm login is complete, use browser_fetch_webpage again with the original URL.\n\nOnce authenticated, return the page content using browser_get_current_html.`
144
+ }
145
+ }
146
+ ]
147
+ };
148
+ }
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
9
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
- import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
10
+ import { ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js";
11
11
  import { fileURLToPath } from 'url';
12
12
  import { readFileSync } from 'fs';
13
13
  import { dirname, join } from 'path';
@@ -38,6 +38,9 @@ import { detectForms, DETECT_FORMS_TOOL } from './actions/detect-forms.js';
38
38
  import { pluginAction, PLUGIN_ACTION_TOOL } from './actions/plugin-action.js';
39
39
  import { pluginInfo, PLUGIN_INFO_TOOL } from './actions/plugin-info.js';
40
40
 
41
+ // Import prompt definitions
42
+ import { PROMPTS, getPromptMessages } from './core/prompts.js';
43
+
41
44
  // Import functions for testing exports
42
45
  import { getBrowser, closeBrowser } from './core/browser.js';
43
46
  import { getOrCreatePage, queueRequest, navigateToUrl, waitForPageReady, extractAndProcessHtml } from './core/page.js';
@@ -62,7 +65,10 @@ async function main() {
62
65
 
63
66
  const server = new Server(
64
67
  { name: "MCPBrowser", version: packageJson.version },
65
- { capabilities: { tools: {}, logging: {} } }
68
+ {
69
+ capabilities: { tools: {}, logging: {}, prompts: {} },
70
+ instructions: "Browser automation server using the user's existing browser session (cookies and auth intact). Workflow: browser_fetch_webpage → browser_take_screenshot (visual content) or browser_get_current_html (re-read after interaction) → browser_click_element / browser_type_text (interact) → browser_close_tab (cleanup). All tools except browser_fetch_webpage and browser_plugin_info require a page loaded first. One tab per domain — same-domain navigations reuse the existing tab. Requests are queued and processed sequentially. Maximum one browser connection at a time. Pages loaded via browser_fetch_webpage persist until browser_close_tab is called or the server shuts down. Screenshots return base64 PNG — prefer browser_get_current_html for text extraction. browser_execute_javascript runs in the page context with access to the full DOM and JavaScript APIs. If plugins are loaded, browser_plugin_info provides site-specific optimized actions for known sites. If authentication is required, the user must complete login in the browser window, then retry the same URL."
71
+ }
66
72
  );
67
73
 
68
74
  // Wire server to logger so logs flow to agent via notifications/message.
@@ -102,6 +108,14 @@ async function main() {
102
108
 
103
109
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
104
110
 
111
+ // --- Prompts handlers ---
112
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: PROMPTS }));
113
+
114
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
115
+ const { name, arguments: args } = request.params;
116
+ return getPromptMessages(name, args);
117
+ });
118
+
105
119
  server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
106
120
  const { name, arguments: args } = request.params;
107
121
  const safeArgs = args || {};