mcpbrowser 0.2.19 → 0.2.21

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
@@ -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.21",
6
6
  "publisher": "cherchyk",
7
7
  "icon": "icon.png",
8
8
  "engines": {
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  { "name": "mcpbrowser",
2
- "version": "0.2.19",
2
+ "version": "0.2.21",
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.21",
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.21" }, { 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
+ });