mcpbrowser 0.2.19 → 0.2.22

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.
@@ -0,0 +1,33 @@
1
+ name: MCP Server Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ matrix:
15
+ node-version: [18.x, 20.x, 22.x]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Setup Node.js ${{ matrix.node-version }}
21
+ uses: actions/setup-node@v4
22
+ with:
23
+ node-version: ${{ matrix.node-version }}
24
+ cache: 'npm'
25
+
26
+ - name: Install dependencies
27
+ run: npm ci
28
+
29
+ - name: Run MCP server tests
30
+ run: npm run test:mcp
31
+
32
+ - name: Run all tests
33
+ run: npm test
package/README.md CHANGED
@@ -38,7 +38,7 @@ Add to your `mcp.json`:
38
38
  "type": "stdio",
39
39
  "command": "npx",
40
40
  "args": ["-y", "mcpbrowser@latest"],
41
- "description": "Fetches authenticated web pages using your Chrome session"
41
+ "description": "Use AUTOMATICALLY on 401/403 errors, login pages, SSO prompts, anti-bot blocks, OR when you think loading via real browser would be beneficial (JavaScript-heavy sites, dynamic content, SPAs). First domain request: ask user confirmation (browser opens for auth). Subsequent same-domain: use automatically (session preserved). Returns HTML from authenticated Chrome session. Handles Microsoft, GitHub, AWS, Google, corporate sites."
42
42
  }
43
43
  ```
44
44
 
@@ -102,7 +102,7 @@ Add this entry to your `mcp.json` file under the `"servers"` section:
102
102
  "type": "stdio",
103
103
  "command": "node",
104
104
  "args": ["<PATH_TO_MCPBROWSER>/src/mcp-browser.js"],
105
- "description": "**USE THIS AUTOMATICALLY** when you encounter ANY authentication/authorization barrier: 401/403 errors, login redirects, 'fetch denied', SSO prompts, or web pages requiring sign-in. Fetches web content using the user's authenticated Chrome session (works for ANY site: Microsoft, GitHub, AWS, Google, corporate intranets, etc.). IMPORTANT: Tool waits automatically for web pages to fetch (up to 60s default, handles auth redirects, waits for network idle). Do NOT retry or give up if you think web page is loading - trust the tool's timeout. Do NOT ask permission - use immediately when auth is required."
105
+ "description": "Use AUTOMATICALLY on 401/403 errors, login pages, SSO prompts, anti-bot blocks, OR when you think loading via real browser would be beneficial (JavaScript-heavy sites, dynamic content, SPAs). First domain request: ask user confirmation (browser opens for auth). Subsequent same-domain: use automatically (session preserved). Returns HTML from authenticated Chrome session. Handles Microsoft, GitHub, AWS, Google, corporate sites."
106
106
  }
107
107
  ```
108
108
 
@@ -2,7 +2,7 @@
2
2
  "name": "mcpbrowser",
3
3
  "displayName": "MCP Browser",
4
4
  "description": "Lightweight MCP server-extension that allows Copilot to fetch protected web pages you can authenticate to via browser - handles login, SSO, and anti-crawler restrictions. Should be used when standard fetch_webpage fails",
5
- "version": "0.2.19",
5
+ "version": "0.2.22",
6
6
  "publisher": "cherchyk",
7
7
  "icon": "icon.png",
8
8
  "engines": {
@@ -92,7 +92,7 @@ async function configureMcpBrowser() {
92
92
  type: "stdio",
93
93
  command: "npx",
94
94
  args: ["-y", "mcpbrowser@latest"],
95
- description: "Use AUTOMATICALLY on 401/403 errors, login pages, SSO prompts, or anti-bot blocks. First domain request: ask user confirmation (browser opens for auth). Subsequent same-domain: use automatically (session preserved). Returns HTML from authenticated Chrome session. Handles Microsoft, GitHub, AWS, Google, corporate sites."
95
+ description: "Use AUTOMATICALLY on 401/403 errors, login pages, SSO prompts, anti-bot blocks, OR when you think loading via real browser would be beneficial (JavaScript-heavy sites, dynamic content, SPAs). First domain request: ask user confirmation (browser opens for auth). Subsequent same-domain: use automatically (session preserved). Returns HTML from authenticated Chrome session. Handles Microsoft, GitHub, AWS, Google, corporate sites."
96
96
  };
97
97
 
98
98
  // Write back to file with pretty formatting
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  { "name": "mcpbrowser",
2
- "version": "0.2.19",
2
+ "version": "0.2.22",
3
3
  "mcpName": "io.github.cherchyk/browser",
4
4
  "type": "module",
5
5
  "description": "MCP server that fetches protected web pages using Chrome DevTools Protocol",
@@ -8,7 +8,9 @@
8
8
  "mcpbrowser": "src/mcp-browser.js"
9
9
  },
10
10
  "scripts": {
11
- "mcp": "node src/mcp-browser.js"
11
+ "mcp": "node src/mcp-browser.js",
12
+ "test": "node --test tests/*.test.js",
13
+ "test:mcp": "node --test tests/mcp-server.test.js"
12
14
  },
13
15
  "keywords": [
14
16
  "mcp",
package/server.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/cherchyk/MCPBrowser",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.2.19",
9
+ "version": "0.2.22",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
@@ -393,7 +393,7 @@ function prepareHtml(html, baseUrl) {
393
393
  }
394
394
 
395
395
  async function main() {
396
- const server = new Server({ name: "MCPBrowser", version: "0.2.19" }, { capabilities: { tools: {} } });
396
+ const server = new Server({ name: "MCPBrowser", version: "0.2.22" }, { capabilities: { tools: {} } });
397
397
 
398
398
  const tools = [
399
399
  {
@@ -455,10 +455,8 @@ async function main() {
455
455
  // Export for testing
456
456
  export { fetchPage, getBrowser, prepareHtml };
457
457
 
458
- // Only run main if this is the entry point
459
- if (import.meta.url === `file://${process.argv[1]}`) {
460
- main().catch((err) => {
461
- console.error(err);
462
- process.exit(1);
463
- });
464
- }
458
+ // Run the MCP server
459
+ main().catch((err) => {
460
+ console.error(err);
461
+ process.exit(1);
462
+ });
package/test-mcp.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process';
3
+
4
+ const mcpProcess = spawn('node', ['src/mcp-browser.js'], {
5
+ cwd: process.cwd(),
6
+ stdio: ['pipe', 'pipe', 'inherit']
7
+ });
8
+
9
+ // Send initialize request
10
+ const initRequest = {
11
+ jsonrpc: '2.0',
12
+ id: 1,
13
+ method: 'initialize',
14
+ params: {
15
+ protocolVersion: '2024-11-05',
16
+ capabilities: {},
17
+ clientInfo: { name: 'test', version: '1.0' }
18
+ }
19
+ };
20
+
21
+ console.log('Sending initialize request...');
22
+ mcpProcess.stdin.write(JSON.stringify(initRequest) + '\n');
23
+
24
+ // Send list tools request
25
+ const listToolsRequest = {
26
+ jsonrpc: '2.0',
27
+ id: 2,
28
+ method: 'tools/list',
29
+ params: {}
30
+ };
31
+
32
+ setTimeout(() => {
33
+ console.log('Sending tools/list request...');
34
+ mcpProcess.stdin.write(JSON.stringify(listToolsRequest) + '\n');
35
+ }, 1000);
36
+
37
+ let responseBuffer = '';
38
+ mcpProcess.stdout.on('data', (data) => {
39
+ responseBuffer += data.toString();
40
+ const lines = responseBuffer.split('\n');
41
+ responseBuffer = lines.pop() || '';
42
+
43
+ lines.forEach(line => {
44
+ if (line.trim()) {
45
+ try {
46
+ const response = JSON.parse(line);
47
+ console.log('Response:', JSON.stringify(response, null, 2));
48
+ } catch (e) {
49
+ console.log('Raw output:', line);
50
+ }
51
+ }
52
+ });
53
+ });
54
+
55
+ mcpProcess.on('error', (err) => {
56
+ console.error('Error:', err);
57
+ });
58
+
59
+ setTimeout(() => {
60
+ console.log('Closing...');
61
+ mcpProcess.kill();
62
+ process.exit(0);
63
+ }, 3000);
@@ -0,0 +1,154 @@
1
+ import { describe, it } from 'node:test';
2
+ import { strict as assert } from 'node:assert';
3
+ import { spawn } from 'child_process';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ describe('MCP Server', () => {
11
+ it('should start and respond to initialize request', async () => {
12
+ const mcpProcess = spawn('node', [join(__dirname, '..', 'src', 'mcp-browser.js')], {
13
+ stdio: ['pipe', 'pipe', 'inherit']
14
+ });
15
+
16
+ try {
17
+ const initRequest = {
18
+ jsonrpc: '2.0',
19
+ id: 1,
20
+ method: 'initialize',
21
+ params: {
22
+ protocolVersion: '2024-11-05',
23
+ capabilities: {},
24
+ clientInfo: { name: 'test', version: '1.0' }
25
+ }
26
+ };
27
+
28
+ const response = await new Promise((resolve, reject) => {
29
+ const timeout = setTimeout(() => {
30
+ reject(new Error('Server did not respond to initialize request within 5 seconds'));
31
+ }, 5000);
32
+
33
+ let buffer = '';
34
+ mcpProcess.stdout.on('data', (data) => {
35
+ buffer += data.toString();
36
+ const lines = buffer.split('\n');
37
+
38
+ for (const line of lines) {
39
+ if (line.trim()) {
40
+ try {
41
+ const parsed = JSON.parse(line);
42
+ if (parsed.id === 1) {
43
+ clearTimeout(timeout);
44
+ resolve(parsed);
45
+ return;
46
+ }
47
+ } catch (e) {
48
+ // Not JSON, continue
49
+ }
50
+ }
51
+ }
52
+ });
53
+
54
+ mcpProcess.on('error', (err) => {
55
+ clearTimeout(timeout);
56
+ reject(err);
57
+ });
58
+
59
+ mcpProcess.on('exit', (code) => {
60
+ clearTimeout(timeout);
61
+ reject(new Error(`Server exited with code ${code} before responding`));
62
+ });
63
+
64
+ mcpProcess.stdin.write(JSON.stringify(initRequest) + '\n');
65
+ });
66
+
67
+ assert.ok(response.result, 'Initialize response should have result');
68
+ assert.equal(response.result.protocolVersion, '2024-11-05', 'Protocol version should match');
69
+ assert.ok(response.result.serverInfo, 'Server info should be present');
70
+ assert.equal(response.result.serverInfo.name, 'MCPBrowser', 'Server name should be MCPBrowser');
71
+ } finally {
72
+ mcpProcess.kill();
73
+ }
74
+ });
75
+
76
+ it('should respond to tools/list request', async () => {
77
+ const mcpProcess = spawn('node', [join(__dirname, '..', 'src', 'mcp-browser.js')], {
78
+ stdio: ['pipe', 'pipe', 'inherit']
79
+ });
80
+
81
+ try {
82
+ // First initialize
83
+ const initRequest = {
84
+ jsonrpc: '2.0',
85
+ id: 1,
86
+ method: 'initialize',
87
+ params: {
88
+ protocolVersion: '2024-11-05',
89
+ capabilities: {},
90
+ clientInfo: { name: 'test', version: '1.0' }
91
+ }
92
+ };
93
+
94
+ mcpProcess.stdin.write(JSON.stringify(initRequest) + '\n');
95
+
96
+ // Wait a bit for initialization
97
+ await new Promise(resolve => setTimeout(resolve, 500));
98
+
99
+ // Then request tools list
100
+ const toolsRequest = {
101
+ jsonrpc: '2.0',
102
+ id: 2,
103
+ method: 'tools/list',
104
+ params: {}
105
+ };
106
+
107
+ const response = await new Promise((resolve, reject) => {
108
+ const timeout = setTimeout(() => {
109
+ reject(new Error('Server did not respond to tools/list request within 5 seconds'));
110
+ }, 5000);
111
+
112
+ let buffer = '';
113
+ mcpProcess.stdout.on('data', (data) => {
114
+ buffer += data.toString();
115
+ const lines = buffer.split('\n');
116
+
117
+ for (const line of lines) {
118
+ if (line.trim()) {
119
+ try {
120
+ const parsed = JSON.parse(line);
121
+ if (parsed.id === 2) {
122
+ clearTimeout(timeout);
123
+ resolve(parsed);
124
+ return;
125
+ }
126
+ } catch (e) {
127
+ // Not JSON, continue
128
+ }
129
+ }
130
+ }
131
+ });
132
+
133
+ mcpProcess.on('error', (err) => {
134
+ clearTimeout(timeout);
135
+ reject(err);
136
+ });
137
+
138
+ mcpProcess.stdin.write(JSON.stringify(toolsRequest) + '\n');
139
+ });
140
+
141
+ assert.ok(response.result, 'Tools/list response should have result');
142
+ assert.ok(response.result.tools, 'Result should have tools array');
143
+ assert.ok(Array.isArray(response.result.tools), 'Tools should be an array');
144
+ assert.ok(response.result.tools.length > 0, 'Should have at least one tool');
145
+
146
+ const fetchTool = response.result.tools.find(t => t.name === 'fetch_webpage_protected');
147
+ assert.ok(fetchTool, 'Should have fetch_webpage_protected tool');
148
+ assert.ok(fetchTool.description, 'Tool should have description');
149
+ assert.ok(fetchTool.inputSchema, 'Tool should have input schema');
150
+ } finally {
151
+ mcpProcess.kill();
152
+ }
153
+ });
154
+ });