Skip to content

Commit 4f43379

Browse files
authored
Merge branch 'main' into patch-1
2 parents 0f3d34b + 4ab7794 commit 4f43379

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2150
-364
lines changed

.github/workflows/e2e_tests.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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+
id: setup_node
25+
with:
26+
node-version-file: package.json
27+
cache: npm
28+
29+
# Cache Playwright browsers
30+
- name: Cache Playwright browsers
31+
id: cache-playwright
32+
uses: actions/cache@v4
33+
with:
34+
path: ~/.cache/ms-playwright # The default Playwright cache path
35+
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }} # Cache key based on OS and package-lock.json
36+
restore-keys: |
37+
${{ runner.os }}-playwright-
38+
39+
- name: Install dependencies
40+
run: npm ci
41+
42+
- name: Install Playwright dependencies
43+
run: npx playwright install-deps
44+
45+
- name: Install Playwright and browsers unless cached
46+
run: npx playwright install --with-deps
47+
if: steps.cache-playwright.outputs.cache-hit != 'true'
48+
49+
- name: Run Playwright tests
50+
id: playwright-tests
51+
run: npm run test:e2e
52+
53+
- name: Upload Playwright Report and Screenshots
54+
uses: actions/upload-artifact@v4
55+
if: steps.playwright-tests.conclusion != 'skipped'
56+
with:
57+
name: playwright-report
58+
path: |
59+
client/playwright-report/
60+
client/test-results/
61+
client/results.json
62+
retention-days: 2
63+
64+
- name: Publish Playwright Test Summary
65+
uses: daun/playwright-report-summary@v3
66+
if: steps.playwright-tests.conclusion != 'skipped'
67+
with:
68+
create-comment: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
69+
report-file: client/results.json
70+
comment-title: "🎭 Playwright E2E Test Results"
71+
job-summary: true
72+
icon-style: "emojis"
73+
custom-info: |
74+
**Test Environment:** Ubuntu Latest, Node.js ${{ steps.setup_node.outputs.node-version }}
75+
**Browsers:** Chromium, Firefox
76+
77+
📊 [View Detailed HTML Report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) (download artifacts)
78+
test-command: "npm run test:e2e"

.github/workflows/main.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ jobs:
1919

2020
- uses: actions/setup-node@v4
2121
with:
22-
node-version: 18
22+
node-version-file: package.json
2323
cache: npm
2424

2525
# Working around https://github.com/npm/cli/issues/4828
2626
# - run: npm ci
2727
- run: npm install --no-package-lock
2828

29+
- name: Check version consistency
30+
run: npm run check-version
31+
2932
- name: Check linting
3033
working-directory: ./client
3134
run: npm run lint
@@ -50,7 +53,7 @@ jobs:
5053
- uses: actions/checkout@v4
5154
- uses: actions/setup-node@v4
5255
with:
53-
node-version: 18
56+
node-version-file: package.json
5457
cache: npm
5558
registry-url: "https://registry.npmjs.org"
5659

.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+

.node-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22.x.x

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@ The project is organized as a monorepo with workspaces:
3030

3131
- `client/`: React frontend with Vite, TypeScript and Tailwind
3232
- `server/`: Express backend with TypeScript
33-
- `bin/`: CLI scripts
33+
- `cli/`: Command-line interface for testing and invoking MCP server methods directly

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: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,9 @@ The MCP Inspector proxy server requires authentication by default. When starting
148148
http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=3a1c267fad21f7150b7d624c160b7f09b0b8c4f623c7107bbf13378f051538d4
149149
```
150150

151-
This token must be included as a Bearer token in the Authorization header for all requests to the server. When authentication is enabled, auto-open is disabled by default to ensure you use the secure URL.
151+
This token must be included as a Bearer token in the Authorization header for all requests to the server. The inspector will automatically open your browser with the token pre-filled in the URL.
152152

153-
**Recommended: Use the pre-filled URL** - Click or copy the link shown in the console to open the inspector with the token already configured.
153+
**Automatic browser opening** - The inspector now automatically opens your browser with the token pre-filled in the URL when authentication is enabled.
154154

155155
**Alternative: Manual configuration** - If you already have the inspector open:
156156

@@ -174,33 +174,33 @@ MCP_PROXY_AUTH_TOKEN=$(openssl rand -hex 32) npm start
174174

175175
#### Local-only Binding
176176

177-
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:
177+
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:
178178

179179
```bash
180180
HOST=0.0.0.0 npm start
181181
```
182182

183-
**Warning:** Only bind to all interfaces in trusted network environments, as this exposes the proxy server's ability to execute local processes.
183+
**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.
184184

185185
#### DNS Rebinding Protection
186186

187187
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):
188188

189189
```bash
190-
ALLOWED_ORIGINS=http://localhost:6274,http://127.0.0.1:6274,http://localhost:8000 npm start
190+
ALLOWED_ORIGINS=http://localhost:6274,http://localhost:8000 npm start
191191
```
192192

193193
### Configuration
194194

195195
The MCP Inspector supports the following configuration settings. To change them, click on the `Configuration` button in the MCP Inspector UI:
196196

197-
| Setting | Description | Default |
198-
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------- |
199-
| `MCP_SERVER_REQUEST_TIMEOUT` | Timeout for requests to the MCP server (ms) | 10000 |
200-
| `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | Reset timeout on progress notifications | true |
201-
| `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications) | 60000 |
202-
| `MCP_PROXY_FULL_ADDRESS` | Set this if you are running the MCP Inspector Proxy on a non-default address. Example: http://10.1.1.22:5577 | "" |
203-
| `MCP_AUTO_OPEN_ENABLED` | Enable automatic browser opening when inspector starts. Only as environment var, not configurable in browser. | true |
197+
| Setting | Description | Default |
198+
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
199+
| `MCP_SERVER_REQUEST_TIMEOUT` | Timeout for requests to the MCP server (ms) | 10000 |
200+
| `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | Reset timeout on progress notifications | true |
201+
| `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications) | 60000 |
202+
| `MCP_PROXY_FULL_ADDRESS` | Set this if you are running the MCP Inspector Proxy on a non-default address. Example: http://10.1.1.22:5577 | "" |
203+
| `MCP_AUTO_OPEN_ENABLED` | Enable automatic browser opening when inspector starts (works with authentication enabled). Only as environment var, not configurable in browser. | true |
204204

205205
These settings can be adjusted in real-time through the UI and will persist across sessions.
206206

@@ -305,9 +305,12 @@ npx @modelcontextprotocol/inspector --cli node build/index.js --method resources
305305
# List available prompts
306306
npx @modelcontextprotocol/inspector --cli node build/index.js --method prompts/list
307307

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

311+
# Connect to a remote MCP server (with Streamable HTTP transport)
312+
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http
313+
311314
# Call a tool on a remote server
312315
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method tools/call --tool-name remotetool --tool-arg param=value
313316

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.2",
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.12.1",
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)