Skip to content

Commit 73873e2

Browse files
authored
Merge branch 'main' into docs/utils-jsdoc
2 parents e5d1fe2 + 7fc4e87 commit 73873e2

23 files changed

+1520
-665
lines changed

.github/workflows/claude.yml

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,14 @@ jobs:
1818
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
1919
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
2020
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
21-
) &&
22-
(
23-
github.actor == 'ihrpr' ||
24-
github.actor == 'olaservo'
2521
)
2622
runs-on: ubuntu-latest
2723
permissions:
2824
contents: read
2925
pull-requests: read
3026
issues: read
3127
id-token: write
28+
actions: read
3229
steps:
3330
- name: Checkout repository
3431
uses: actions/checkout@v4
@@ -41,24 +38,16 @@ jobs:
4138
with:
4239
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
4340

44-
# Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)
45-
# model: "claude-opus-4-20250514"
46-
47-
# Optional: Customize the trigger phrase (default: @claude)
48-
# trigger_phrase: "/claude"
49-
50-
# Optional: Trigger when specific user is assigned to an issue
51-
# assignee_trigger: "claude-bot"
41+
# Allow Claude to read CI results on PRs
42+
additional_permissions: |
43+
actions: read
5244
53-
# Optional: Allow Claude to run specific commands
54-
# allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)"
45+
# Trigger when assigned to an issue
46+
assignee_trigger: "claude"
5547

56-
# Optional: Add custom instructions for Claude to customize its behavior for your project
57-
# custom_instructions: |
58-
# Follow our coding standards
59-
# Ensure all new code has tests
60-
# Use TypeScript for new files
48+
# Allow Claude to run bash
49+
# This should be safe given the repo is already public
50+
allowed_tools: "Bash"
6151

62-
# Optional: Custom environment variables for Claude
63-
# claude_env: |
64-
# NODE_ENV: test
52+
custom_instructions: |
53+
If posting a comment to GitHub, give a concise summary of the comment at the top and put all the details in a <details> block.

.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) {

0 commit comments

Comments
 (0)