Skip to content

Commit 426a33c

Browse files
authored
Merge branch 'main' into update-cc-action-yaml
2 parents 5bbbd64 + 616d9fa commit 426a33c

22 files changed

+1509
-643
lines changed

.husky/pre-commit

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
npx lint-staged
2+
git update-index --again

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Build stage
2-
FROM node:24-slim AS builder
2+
FROM node:current-alpine3.22 AS builder
33

44
# Set working directory
55
WORKDIR /app
@@ -49,4 +49,4 @@ ENV SERVER_PORT=6277
4949
EXPOSE ${CLIENT_PORT} ${SERVER_PORT}
5050

5151
# Use ENTRYPOINT with CMD for arguments
52-
ENTRYPOINT ["npm", "start"]
52+
ENTRYPOINT ["npm", "start"]

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ npx @modelcontextprotocol/inspector
2929

3030
The server will start up and the UI will be accessible at `http://localhost:6274`.
3131

32+
### Docker Container
33+
34+
You can also start it in a Docker container with the following command:
35+
36+
```bash
37+
docker run --rm --network host -p 6274:6274 -p 6277:6277 ghcr.io/modelcontextprotocol/inspector:latest
38+
```
39+
3240
### From an MCP server repository
3341

3442
To inspect an MCP server implementation, there's no need to clone this repo. Instead, use `npx`. For example, if your server is built at `build/index.js`:
@@ -166,6 +174,16 @@ If you need to disable authentication (NOT RECOMMENDED), you can set the `DANGER
166174
DANGEROUSLY_OMIT_AUTH=true npm start
167175
```
168176

177+
---
178+
179+
**🚨 WARNING 🚨**
180+
181+
Disabling authentication with `DANGEROUSLY_OMIT_AUTH` is incredibly dangerous! Disabling auth leaves your machine open to attack not just when exposed to the public internet, but also **via your web browser**. Meaning, visiting a malicious website OR viewing a malicious advertizement could allow an attacker to remotely compromise your computer. Do not disable this feature unless you truly understand the risks.
182+
183+
Read more about the risks of this vulnerability on Oligo's blog: [Critical RCE Vulnerability in Anthropic MCP Inspector - CVE-2025-49596](https://www.oligo.security/blog/critical-rce-vulnerability-in-anthropic-mcp-inspector-cve-2025-49596)
184+
185+
---
186+
169187
You can also set the token via the `MCP_PROXY_AUTH_TOKEN` environment variable when starting the server:
170188

171189
```bash

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.16.4",
3+
"version": "0.16.5",
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.17.2",
24+
"@modelcontextprotocol/sdk": "^1.17.3",
2525
"commander": "^13.1.0",
2626
"spawn-rx": "^5.1.2"
2727
}

cli/src/transport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ function createStdioTransport(options: TransportOptions): Transport {
3232
const defaultEnv = getDefaultEnvironment();
3333

3434
const env: Record<string, string> = {
35-
...processEnv,
3635
...defaultEnv,
36+
...processEnv,
3737
};
3838

3939
const { cmd: actualCommand, args: actualArgs } = findActualExecutable(

client/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-client",
3-
"version": "0.16.4",
3+
"version": "0.16.5",
44
"description": "Client-side application for the Model Context Protocol inspector",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",
@@ -25,7 +25,7 @@
2525
"cleanup:e2e": "node e2e/global-teardown.js"
2626
},
2727
"dependencies": {
28-
"@modelcontextprotocol/sdk": "^1.17.2",
28+
"@modelcontextprotocol/sdk": "^1.17.3",
2929
"@radix-ui/react-checkbox": "^1.1.4",
3030
"@radix-ui/react-dialog": "^1.1.3",
3131
"@radix-ui/react-icons": "^1.3.0",

client/src/App.tsx

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import {
3434
useDraggablePane,
3535
useDraggableSidebar,
3636
} from "./lib/hooks/useDraggablePane";
37-
import { StdErrNotification } from "./lib/notificationTypes";
3837

3938
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
4039
import { Button } from "@/components/ui/button";
@@ -106,9 +105,6 @@ const App = () => {
106105
>(getInitialTransportType);
107106
const [logLevel, setLogLevel] = useState<LoggingLevel>("debug");
108107
const [notifications, setNotifications] = useState<ServerNotification[]>([]);
109-
const [stdErrNotifications, setStdErrNotifications] = useState<
110-
StdErrNotification[]
111-
>([]);
112108
const [roots, setRoots] = useState<Root[]>([]);
113109
const [env, setEnv] = useState<Record<string, string>>({});
114110

@@ -224,12 +220,6 @@ const App = () => {
224220
onNotification: (notification) => {
225221
setNotifications((prev) => [...prev, notification as ServerNotification]);
226222
},
227-
onStdErrNotification: (notification) => {
228-
setStdErrNotifications((prev) => [
229-
...prev,
230-
notification as StdErrNotification,
231-
]);
232-
},
233223
onPendingRequest: (request, resolve, reject) => {
234224
setPendingSampleRequests((prev) => [
235225
...prev,
@@ -757,10 +747,6 @@ const App = () => {
757747
setLogLevel(level);
758748
};
759749

760-
const clearStdErrNotifications = () => {
761-
setStdErrNotifications([]);
762-
};
763-
764750
const AuthDebuggerWrapper = () => (
765751
<TabsContent value="auth">
766752
<AuthDebugger
@@ -829,11 +815,9 @@ const App = () => {
829815
setOauthScope={setOauthScope}
830816
onConnect={connectMcpServer}
831817
onDisconnect={disconnectMcpServer}
832-
stdErrNotifications={stdErrNotifications}
833818
logLevel={logLevel}
834819
sendLogLevelRequest={sendLogLevelRequest}
835820
loggingSupported={!!serverCapabilities?.logging || false}
836-
clearStdErrNotifications={clearStdErrNotifications}
837821
/>
838822
<div
839823
onMouseDown={handleSidebarDragStart}

client/src/components/AuthDebugger.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { AuthDebuggerState, EMPTY_DEBUGGER_STATE } from "../lib/auth-types";
66
import { OAuthFlowProgress } from "./OAuthFlowProgress";
77
import { OAuthStateMachine } from "../lib/oauth-state-machine";
88
import { SESSION_KEYS } from "../lib/constants";
9+
import { validateRedirectUrl } from "@/utils/urlValidation";
910

1011
export interface AuthDebuggerProps {
1112
serverUrl: string;
@@ -163,6 +164,23 @@ const AuthDebugger = ({
163164
currentState.oauthStep === "authorization_code" &&
164165
currentState.authorizationUrl
165166
) {
167+
// Validate the URL before redirecting
168+
try {
169+
validateRedirectUrl(currentState.authorizationUrl);
170+
} catch (error) {
171+
updateAuthState({
172+
...currentState,
173+
isInitiatingAuth: false,
174+
latestError:
175+
error instanceof Error ? error : new Error(String(error)),
176+
statusMessage: {
177+
type: "error",
178+
message: `Invalid authorization URL: ${error instanceof Error ? error.message : String(error)}`,
179+
},
180+
});
181+
return;
182+
}
183+
166184
// Store the current auth state before redirecting
167185
sessionStorage.setItem(
168186
SESSION_KEYS.AUTH_DEBUGGER_STATE,

client/src/components/OAuthDebugCallback.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ const OAuthDebugCallback = ({ onConnect }: OAuthCallbackProps) => {
4646
if (storedState) {
4747
try {
4848
restoredState = JSON.parse(storedState);
49+
if (restoredState && typeof restoredState.resource === "string") {
50+
restoredState.resource = new URL(restoredState.resource);
51+
}
52+
if (
53+
restoredState &&
54+
typeof restoredState.authorizationUrl === "string"
55+
) {
56+
restoredState.authorizationUrl = new URL(
57+
restoredState.authorizationUrl,
58+
);
59+
}
4960
// Clean up the stored state
5061
sessionStorage.removeItem(SESSION_KEYS.AUTH_DEBUGGER_STATE);
5162
} catch (e) {

client/src/components/OAuthFlowProgress.tsx

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Button } from "./ui/button";
44
import { DebugInspectorOAuthClientProvider } from "@/lib/auth";
55
import { useEffect, useMemo, useState } from "react";
66
import { OAuthClientInformation } from "@modelcontextprotocol/sdk/shared/auth.js";
7+
import { validateRedirectUrl } from "@/utils/urlValidation";
8+
import { useToast } from "@/lib/hooks/useToast";
79

810
interface OAuthStepProps {
911
label: string;
@@ -71,6 +73,7 @@ export const OAuthFlowProgress = ({
7173
updateAuthState,
7274
proceedToNextStep,
7375
}: OAuthFlowProgressProps) => {
76+
const { toast } = useToast();
7477
const provider = useMemo(
7578
() => new DebugInspectorOAuthClientProvider(serverUrl),
7679
[serverUrl],
@@ -239,16 +242,32 @@ export const OAuthFlowProgress = ({
239242
<p className="text-xs break-all">
240243
{authState.authorizationUrl}
241244
</p>
242-
<a
243-
href={authState.authorizationUrl}
244-
target="_blank"
245-
rel="noopener noreferrer"
245+
<button
246+
onClick={() => {
247+
try {
248+
validateRedirectUrl(authState.authorizationUrl!);
249+
window.open(
250+
authState.authorizationUrl!,
251+
"_blank",
252+
"noopener noreferrer",
253+
);
254+
} catch (error) {
255+
toast({
256+
title: "Invalid URL",
257+
description:
258+
error instanceof Error
259+
? error.message
260+
: "The authorization URL is not valid",
261+
variant: "destructive",
262+
});
263+
}
264+
}}
246265
className="flex items-center text-blue-500 hover:text-blue-700"
247266
aria-label="Open authorization URL in new tab"
248267
title="Open authorization URL"
249268
>
250269
<ExternalLink className="h-4 w-4" />
251-
</a>
270+
</button>
252271
</div>
253272
<p className="text-xs text-muted-foreground mt-2">
254273
Click the link to authorize in your browser. After
@@ -354,7 +373,21 @@ export const OAuthFlowProgress = ({
354373
authState.authorizationUrl && (
355374
<Button
356375
variant="outline"
357-
onClick={() => window.open(authState.authorizationUrl!, "_blank")}
376+
onClick={() => {
377+
try {
378+
validateRedirectUrl(authState.authorizationUrl!);
379+
window.open(authState.authorizationUrl!, "_blank");
380+
} catch (error) {
381+
toast({
382+
title: "Invalid URL",
383+
description:
384+
error instanceof Error
385+
? error.message
386+
: "The authorization URL is not valid",
387+
variant: "destructive",
388+
});
389+
}
390+
}}
358391
>
359392
Open in New Tab
360393
</Button>

0 commit comments

Comments
 (0)