Skip to content

Commit 430d6d4

Browse files
authored
Merge branch 'main' into completion-support-default
2 parents e106194 + 5028496 commit 430d6d4

File tree

18 files changed

+612
-127
lines changed

18 files changed

+612
-127
lines changed

.github/workflows/claude.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Claude Code
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
pull_request_review_comment:
7+
types: [created]
8+
issues:
9+
types: [opened, assigned]
10+
pull_request_review:
11+
types: [submitted]
12+
13+
jobs:
14+
claude:
15+
if: |
16+
(
17+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
18+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
19+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
20+
(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'
25+
)
26+
runs-on: ubuntu-latest
27+
permissions:
28+
contents: read
29+
pull-requests: read
30+
issues: read
31+
id-token: write
32+
steps:
33+
- name: Checkout repository
34+
uses: actions/checkout@v4
35+
with:
36+
fetch-depth: 1
37+
38+
- name: Run Claude Code
39+
id: claude
40+
uses: anthropics/claude-code-action@beta
41+
with:
42+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
43+
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"
52+
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:*)"
55+
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
61+
62+
# Optional: Custom environment variables for Claude
63+
# claude_env: |
64+
# NODE_ENV: test

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ client/tsconfig.app.tsbuildinfo
99
client/tsconfig.node.tsbuildinfo
1010
cli/build
1111
test-output
12+
# symlinked by `npm run link:sdk`:
13+
sdk
1214
client/playwright-report/
1315
client/results.json
1416
client/test-results/
15-

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ If you need to disable authentication (NOT RECOMMENDED), you can set the `DANGER
166166
DANGEROUSLY_OMIT_AUTH=true npm start
167167
```
168168

169+
You can also set the token via the `MCP_PROXY_AUTH_TOKEN` environment variable when starting the server:
170+
171+
```bash
172+
MCP_PROXY_AUTH_TOKEN=$(openssl rand -hex 32) npm start
173+
```
174+
169175
#### Local-only Binding
170176

171177
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:
@@ -254,6 +260,12 @@ Development mode:
254260

255261
```bash
256262
npm run dev
263+
264+
# To co-develop with the typescript-sdk package (assuming it's cloned in ../typescript-sdk; set MCP_SDK otherwise):
265+
npm run dev:sdk "cd sdk && npm run examples:simple-server:w"
266+
# then open http://localhost:3000/mcp as SHTTP in the inspector.
267+
# To go back to the deployed SDK version:
268+
# npm run unlink:sdk && npm i
257269
```
258270

259271
> **Note for Windows users:**

cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/inspector-cli",
3-
"version": "0.15.0",
3+
"version": "0.16.1",
44
"description": "CLI for the Model Context Protocol inspector",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",

cli/src/cli.ts

Lines changed: 7 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -50,71 +50,29 @@ function delay(ms: number): Promise<void> {
5050
}
5151

5252
async function runWebClient(args: Args): Promise<void> {
53-
const inspectorServerPath = resolve(
54-
__dirname,
55-
"../../",
56-
"server",
57-
"build",
58-
"index.js",
59-
);
60-
6153
// Path to the client entry point
6254
const inspectorClientPath = resolve(
6355
__dirname,
6456
"../../",
6557
"client",
6658
"bin",
67-
"client.js",
59+
"start.js",
6860
);
6961

70-
const CLIENT_PORT: string = process.env.CLIENT_PORT ?? "6274";
71-
const SERVER_PORT: string = process.env.SERVER_PORT ?? "6277";
72-
73-
console.log("Starting MCP inspector...");
74-
7562
const abort = new AbortController();
7663
let cancelled: boolean = false;
7764
process.on("SIGINT", () => {
7865
cancelled = true;
7966
abort.abort();
8067
});
8168

82-
let server: ReturnType<typeof spawnPromise>;
83-
let serverOk: unknown;
84-
8569
try {
86-
server = spawnPromise(
87-
"node",
88-
[
89-
inspectorServerPath,
90-
...(args.command ? [`--env`, args.command] : []),
91-
...(args.args ? [`--args=${args.args.join(" ")}`] : []),
92-
],
93-
{
94-
env: {
95-
...process.env,
96-
PORT: SERVER_PORT,
97-
MCP_ENV_VARS: JSON.stringify(args.envArgs),
98-
},
99-
signal: abort.signal,
100-
echoOutput: true,
101-
},
102-
);
103-
104-
// Make sure server started before starting client
105-
serverOk = await Promise.race([server, delay(2 * 1000)]);
106-
} catch (error) {}
107-
108-
if (serverOk) {
109-
try {
110-
await spawnPromise("node", [inspectorClientPath], {
111-
env: { ...process.env, PORT: CLIENT_PORT },
112-
signal: abort.signal,
113-
echoOutput: true,
114-
});
115-
} catch (e) {
116-
if (!cancelled || process.env.DEBUG) throw e;
117-
}
70+
await spawnPromise("node", [inspectorClientPath], {
71+
signal: abort.signal,
72+
echoOutput: true,
73+
});
74+
} catch (e) {
75+
if (!cancelled || process.env.DEBUG) throw e;
11876
}
11977
}
12078

client/bin/start.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ async function startDevServer(serverOptions) {
4040
...process.env,
4141
SERVER_PORT,
4242
CLIENT_PORT,
43-
MCP_PROXY_TOKEN: sessionToken,
43+
MCP_PROXY_AUTH_TOKEN: sessionToken,
4444
MCP_ENV_VARS: JSON.stringify(envVars),
4545
},
4646
signal: abort.signal,
@@ -99,7 +99,7 @@ async function startProdServer(serverOptions) {
9999
...process.env,
100100
SERVER_PORT,
101101
CLIENT_PORT,
102-
MCP_PROXY_TOKEN: sessionToken,
102+
MCP_PROXY_AUTH_TOKEN: sessionToken,
103103
MCP_ENV_VARS: JSON.stringify(envVars),
104104
},
105105
signal: abort.signal,
@@ -247,8 +247,9 @@ async function main() {
247247
: "Starting MCP inspector...",
248248
);
249249

250-
// Generate session token for authentication
251-
const sessionToken = randomBytes(32).toString("hex");
250+
// Use provided token from environment or generate a new one
251+
const sessionToken =
252+
process.env.MCP_PROXY_AUTH_TOKEN || randomBytes(32).toString("hex");
252253
const authDisabled = !!process.env.DANGEROUSLY_OMIT_AUTH;
253254

254255
const abort = new AbortController();

client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/inspector-client",
3-
"version": "0.15.0",
3+
"version": "0.16.1",
44
"description": "Client-side application for the Model Context Protocol inspector",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",

client/src/App.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ const App = () => {
8080
ResourceTemplate[]
8181
>([]);
8282
const [resourceContent, setResourceContent] = useState<string>("");
83+
const [resourceContentMap, setResourceContentMap] = useState<
84+
Record<string, string>
85+
>({});
8386
const [prompts, setPrompts] = useState<Prompt[]>([]);
8487
const [promptContent, setPromptContent] = useState<string>("");
8588
const [tools, setTools] = useState<Tool[]>([]);
@@ -461,7 +464,12 @@ const App = () => {
461464
ReadResourceResultSchema,
462465
"resources",
463466
);
464-
setResourceContent(JSON.stringify(response, null, 2));
467+
const content = JSON.stringify(response, null, 2);
468+
setResourceContent(content);
469+
setResourceContentMap((prev) => ({
470+
...prev,
471+
[uri]: content,
472+
}));
465473
};
466474

467475
const subscribeToResource = async (uri: string) => {
@@ -863,6 +871,11 @@ const App = () => {
863871
toolResult={toolResult}
864872
nextCursor={nextToolCursor}
865873
error={errors.tools}
874+
resourceContent={resourceContentMap}
875+
onReadResource={(uri: string) => {
876+
clearError("resources");
877+
readResource(uri);
878+
}}
866879
/>
867880
<ConsoleTab />
868881
<PingTab

client/src/components/DynamicJsonForm.tsx

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import JsonEditor from "./JsonEditor";
55
import { updateValueAtPath } from "@/utils/jsonUtils";
66
import { generateDefaultValue } from "@/utils/schemaUtils";
77
import type { JsonValue, JsonSchemaType } from "@/utils/jsonUtils";
8+
import { useToast } from "@/lib/hooks/useToast";
9+
import { CheckCheck, Copy } from "lucide-react";
810

911
interface DynamicJsonFormProps {
1012
schema: JsonSchemaType;
@@ -60,6 +62,9 @@ const DynamicJsonForm = ({
6062
const isOnlyJSON = !isSimpleObject(schema);
6163
const [isJsonMode, setIsJsonMode] = useState(isOnlyJSON);
6264
const [jsonError, setJsonError] = useState<string>();
65+
const [copiedJson, setCopiedJson] = useState<boolean>(false);
66+
const { toast } = useToast();
67+
6368
// Store the raw JSON string to allow immediate feedback during typing
6469
// while deferring parsing until the user stops typing
6570
const [rawJsonValue, setRawJsonValue] = useState<string>(
@@ -399,19 +404,64 @@ const DynamicJsonForm = ({
399404
}
400405
}, [shouldUseJsonMode, isJsonMode]);
401406

407+
const handleCopyJson = useCallback(() => {
408+
const copyToClipboard = async () => {
409+
try {
410+
await navigator.clipboard.writeText(
411+
JSON.stringify(value, null, 2) ?? "[]",
412+
);
413+
setCopiedJson(true);
414+
415+
toast({
416+
title: "JSON copied",
417+
description:
418+
"The JSON data has been successfully copied to your clipboard.",
419+
});
420+
421+
setTimeout(() => {
422+
setCopiedJson(false);
423+
}, 2000);
424+
} catch (error) {
425+
toast({
426+
title: "Error",
427+
description: `Failed to copy JSON: ${error instanceof Error ? error.message : String(error)}`,
428+
variant: "destructive",
429+
});
430+
}
431+
};
432+
433+
copyToClipboard();
434+
}, [toast, value]);
435+
402436
return (
403437
<div className="space-y-4">
404438
<div className="flex justify-end space-x-2">
405439
{isJsonMode && (
406-
<Button
407-
type="button"
408-
variant="outline"
409-
size="sm"
410-
onClick={formatJson}
411-
>
412-
Format JSON
413-
</Button>
440+
<>
441+
<Button
442+
type="button"
443+
variant="outline"
444+
size="sm"
445+
onClick={handleCopyJson}
446+
>
447+
{copiedJson ? (
448+
<CheckCheck className="h-4 w-4 mr-2" />
449+
) : (
450+
<Copy className="h-4 w-4 mr-2" />
451+
)}
452+
Copy JSON
453+
</Button>
454+
<Button
455+
type="button"
456+
variant="outline"
457+
size="sm"
458+
onClick={formatJson}
459+
>
460+
Format JSON
461+
</Button>
462+
</>
414463
)}
464+
415465
{!isOnlyJSON && (
416466
<Button variant="outline" size="sm" onClick={handleSwitchToFormMode}>
417467
{isJsonMode ? "Switch to Form" : "Switch to JSON"}

0 commit comments

Comments
 (0)