Skip to content

Commit 9eb5e7b

Browse files
Merge branch 'main' into main
2 parents f5dba84 + 6a8c831 commit 9eb5e7b

30 files changed

+1321
-306
lines changed

.github/workflows/e2e_tests.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Playwright Tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
# Installing Playright dependencies can take quite awhile, and also depends on GitHub CI load.
12+
timeout-minutes: 15
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Install dependencies
17+
run: |
18+
sudo apt-get update
19+
sudo apt-get install -y libwoff1
20+
21+
- uses: actions/checkout@v4
22+
23+
- uses: actions/setup-node@v4
24+
with:
25+
node-version: 18
26+
27+
# Cache Playwright browsers
28+
- name: Cache Playwright browsers
29+
id: cache-playwright
30+
uses: actions/cache@v4
31+
with:
32+
path: ~/.cache/ms-playwright # The default Playwright cache path
33+
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }} # Cache key based on OS and package-lock.json
34+
restore-keys: |
35+
${{ runner.os }}-playwright-
36+
37+
- name: Install dependencies
38+
run: npm ci
39+
40+
- name: Install Playwright dependencies
41+
run: npx playwright install-deps
42+
43+
- name: Install Playwright and browsers unless cached
44+
run: npx playwright install --with-deps
45+
if: steps.cache-playwright.outputs.cache-hit != 'true'
46+
47+
- name: Run Playwright tests
48+
id: playwright-tests
49+
run: npm run test:e2e
50+
51+
- name: Upload Playwright Report and Screenshots
52+
uses: actions/upload-artifact@v4
53+
if: steps.playwright-tests.conclusion != 'skipped'
54+
with:
55+
name: playwright-report
56+
path: |
57+
client/playwright-report/
58+
client/test-results/
59+
client/results.json
60+
retention-days: 2
61+
62+
- name: Publish Playwright Test Summary
63+
uses: daun/playwright-report-summary@v3
64+
if: steps.playwright-tests.conclusion != 'skipped'
65+
with:
66+
create-comment: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
67+
report-file: client/results.json
68+
comment-title: "🎭 Playwright E2E Test Results"
69+
job-summary: true
70+
icon-style: "emojis"
71+
custom-info: |
72+
**Test Environment:** Ubuntu Latest, Node.js 18
73+
**Browsers:** Chromium, Firefox
74+
75+
📊 [View Detailed HTML Report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) (download artifacts)
76+
test-command: "npm run test:e2e"

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ client/tsconfig.app.tsbuildinfo
99
client/tsconfig.node.tsbuildinfo
1010
cli/build
1111
test-output
12+
client/playwright-report/
13+
client/results.json
14+
client/test-results/
15+

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ Thanks for your interest in contributing! This guide explains how to get involve
77
1. Fork the repository and clone it locally
88
2. Install dependencies with `npm install`
99
3. Run `npm run dev` to start both client and server in development mode
10-
4. Use the web UI at http://127.0.0.1:6274 to interact with the inspector
10+
4. Use the web UI at http://localhost:6274 to interact with the inspector
1111

1212
## Development Process & Pull Requests
1313

1414
1. Create a new branch for your changes
1515
2. Make your changes following existing code style and conventions. You can run `npm run prettier-check` and `npm run prettier-fix` as applicable.
16-
3. Test changes locally by running `npm test`
16+
3. Test changes locally by running `npm test` and `npm run test:e2e`
1717
4. Update documentation as needed
1818
5. Use clear commit messages explaining your changes
1919
6. Verify all changes work as expected

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,20 +168,20 @@ DANGEROUSLY_OMIT_AUTH=true npm start
168168

169169
#### Local-only Binding
170170

171-
By default, the MCP Inspector proxy server binds only to `127.0.0.1` (localhost) to prevent network access. This ensures the server is not accessible from other devices on the network. If you need to bind to all interfaces for development purposes, you can override this with the `HOST` environment variable:
171+
By default, both the MCP Inspector proxy server and client bind only to `localhost` to prevent network access. This ensures they are not accessible from other devices on the network. If you need to bind to all interfaces for development purposes, you can override this with the `HOST` environment variable:
172172

173173
```bash
174174
HOST=0.0.0.0 npm start
175175
```
176176

177-
**Warning:** Only bind to all interfaces in trusted network environments, as this exposes the proxy server's ability to execute local processes.
177+
**Warning:** Only bind to all interfaces in trusted network environments, as this exposes the proxy server's ability to execute local processes and both services to network access.
178178

179179
#### DNS Rebinding Protection
180180

181181
To prevent DNS rebinding attacks, the MCP Inspector validates the `Origin` header on incoming requests. By default, only requests from the client origin are allowed (respects `CLIENT_PORT` if set, defaulting to port 6274). You can configure additional allowed origins by setting the `ALLOWED_ORIGINS` environment variable (comma-separated list):
182182

183183
```bash
184-
ALLOWED_ORIGINS=http://localhost:6274,http://127.0.0.1:6274,http://localhost:8000 npm start
184+
ALLOWED_ORIGINS=http://localhost:6274,http://localhost:8000 npm start
185185
```
186186

187187
### Configuration
@@ -299,9 +299,12 @@ npx @modelcontextprotocol/inspector --cli node build/index.js --method resources
299299
# List available prompts
300300
npx @modelcontextprotocol/inspector --cli node build/index.js --method prompts/list
301301

302-
# Connect to a remote MCP server
302+
# Connect to a remote MCP server (default is SSE transport)
303303
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com
304304

305+
# Connect to a remote MCP server (with Streamable HTTP transport)
306+
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http
307+
305308
# Call a tool on a remote server
306309
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method tools/call --tool-name remotetool --tool-arg param=value
307310

cli/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/inspector-cli",
3-
"version": "0.14.3",
3+
"version": "0.15.0",
44
"description": "CLI for the Model Context Protocol inspector",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",
@@ -21,7 +21,7 @@
2121
},
2222
"devDependencies": {},
2323
"dependencies": {
24-
"@modelcontextprotocol/sdk": "^1.13.0",
24+
"@modelcontextprotocol/sdk": "^1.13.1",
2525
"commander": "^13.1.0",
2626
"spawn-rx": "^5.1.2"
2727
}

cli/scripts/cli-tests.js

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ console.log(`${colors.BLUE}- Resource-related options (--uri)${colors.NC}`);
4444
console.log(
4545
`${colors.BLUE}- Prompt-related options (--prompt-name, --prompt-args)${colors.NC}`,
4646
);
47-
console.log(`${colors.BLUE}- Logging options (--log-level)${colors.NC}\n`);
47+
console.log(`${colors.BLUE}- Logging options (--log-level)${colors.NC}`);
48+
console.log(
49+
`${colors.BLUE}- Transport types (--transport http/sse/stdio)${colors.NC}`,
50+
);
51+
console.log(
52+
`${colors.BLUE}- Transport inference from URL suffixes (/mcp, /sse)${colors.NC}`,
53+
);
54+
console.log(`\n`);
4855

4956
// Get directory paths
5057
const SCRIPTS_DIR = __dirname;
@@ -62,9 +69,11 @@ if (!fs.existsSync(OUTPUT_DIR)) {
6269
}
6370

6471
// Create a temporary directory for test files
65-
const TEMP_DIR = fs.mkdirSync(path.join(os.tmpdir(), "mcp-inspector-tests"), {
66-
recursive: true,
67-
});
72+
const TEMP_DIR = path.join(os.tmpdir(), "mcp-inspector-tests");
73+
fs.mkdirSync(TEMP_DIR, { recursive: true });
74+
75+
// Track servers for cleanup
76+
let runningServers = [];
6877

6978
process.on("exit", () => {
7079
try {
@@ -74,6 +83,21 @@ process.on("exit", () => {
7483
`${colors.RED}Failed to remove temp directory: ${err.message}${colors.NC}`,
7584
);
7685
}
86+
87+
runningServers.forEach((server) => {
88+
try {
89+
process.kill(-server.pid);
90+
} catch (e) {}
91+
});
92+
});
93+
94+
process.on("SIGINT", () => {
95+
runningServers.forEach((server) => {
96+
try {
97+
process.kill(-server.pid);
98+
} catch (e) {}
99+
});
100+
process.exit(1);
77101
});
78102

79103
// Use the existing sample config file
@@ -121,6 +145,11 @@ async function runBasicTest(testName, ...args) {
121145
stdio: ["ignore", "pipe", "pipe"],
122146
});
123147

148+
const timeout = setTimeout(() => {
149+
console.log(`${colors.YELLOW}Test timed out: ${testName}${colors.NC}`);
150+
child.kill();
151+
}, 10000);
152+
124153
// Pipe stdout and stderr to the output file
125154
child.stdout.pipe(outputStream);
126155
child.stderr.pipe(outputStream);
@@ -135,6 +164,7 @@ async function runBasicTest(testName, ...args) {
135164
});
136165

137166
child.on("close", (code) => {
167+
clearTimeout(timeout);
138168
outputStream.end();
139169

140170
if (code === 0) {
@@ -201,6 +231,13 @@ async function runErrorTest(testName, ...args) {
201231
stdio: ["ignore", "pipe", "pipe"],
202232
});
203233

234+
const timeout = setTimeout(() => {
235+
console.log(
236+
`${colors.YELLOW}Error test timed out: ${testName}${colors.NC}`,
237+
);
238+
child.kill();
239+
}, 10000);
240+
204241
// Pipe stdout and stderr to the output file
205242
child.stdout.pipe(outputStream);
206243
child.stderr.pipe(outputStream);
@@ -215,6 +252,7 @@ async function runErrorTest(testName, ...args) {
215252
});
216253

217254
child.on("close", (code) => {
255+
clearTimeout(timeout);
218256
outputStream.end();
219257

220258
// For error tests, we expect a non-zero exit code
@@ -611,6 +649,79 @@ async function runTests() {
611649
"debug",
612650
);
613651

652+
console.log(
653+
`\n${colors.YELLOW}=== Running HTTP Transport Tests ===${colors.NC}`,
654+
);
655+
656+
console.log(
657+
`${colors.BLUE}Starting server-everything in streamableHttp mode.${colors.NC}`,
658+
);
659+
const httpServer = spawn(
660+
"npx",
661+
["@modelcontextprotocol/server-everything", "streamableHttp"],
662+
{
663+
detached: true,
664+
stdio: "ignore",
665+
},
666+
);
667+
runningServers.push(httpServer);
668+
669+
await new Promise((resolve) => setTimeout(resolve, 3000));
670+
671+
// Test 25: HTTP transport inferred from URL ending with /mcp
672+
await runBasicTest(
673+
"http_transport_inferred",
674+
"http://127.0.0.1:3001/mcp",
675+
"--cli",
676+
"--method",
677+
"tools/list",
678+
);
679+
680+
// Test 26: HTTP transport with explicit --transport http flag
681+
await runBasicTest(
682+
"http_transport_with_explicit_flag",
683+
"http://127.0.0.1:3001",
684+
"--transport",
685+
"http",
686+
"--cli",
687+
"--method",
688+
"tools/list",
689+
);
690+
691+
// Test 27: HTTP transport with suffix and --transport http flag
692+
await runBasicTest(
693+
"http_transport_with_explicit_flag_and_suffix",
694+
"http://127.0.0.1:3001/mcp",
695+
"--transport",
696+
"http",
697+
"--cli",
698+
"--method",
699+
"tools/list",
700+
);
701+
702+
// Test 28: SSE transport given to HTTP server (should fail)
703+
await runErrorTest(
704+
"sse_transport_given_to_http_server",
705+
"http://127.0.0.1:3001",
706+
"--transport",
707+
"sse",
708+
"--cli",
709+
"--method",
710+
"tools/list",
711+
);
712+
713+
// Kill HTTP server
714+
try {
715+
process.kill(-httpServer.pid);
716+
console.log(
717+
`${colors.BLUE}HTTP server killed, waiting for port to be released...${colors.NC}`,
718+
);
719+
} catch (e) {
720+
console.log(
721+
`${colors.RED}Error killing HTTP server: ${e.message}${colors.NC}`,
722+
);
723+
}
724+
614725
// Print test summary
615726
console.log(`\n${colors.YELLOW}=== Test Summary ===${colors.NC}`);
616727
console.log(`${colors.GREEN}Passed: ${PASSED_TESTS}${colors.NC}`);

0 commit comments

Comments
 (0)