Skip to content

Commit 42e6f0a

Browse files
committed
Merge branch 'main' into auto_open
2 parents d82e06f + f19b382 commit 42e6f0a

26 files changed

+619
-101
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ jobs:
2626
# - run: npm ci
2727
- run: npm install --no-package-lock
2828

29+
- name: Check linting
30+
working-directory: ./client
31+
run: npm run lint
32+
2933
- name: Run client tests
3034
working-directory: ./client
3135
run: npm test
@@ -54,8 +58,6 @@ jobs:
5458
# - run: npm ci
5559
- run: npm install --no-package-lock
5660

57-
- run: npm run build
58-
5961
# TODO: Add --provenance once the repo is public
6062
- run: npm run publish-all
6163
env:

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.10.2",
3+
"version": "0.11.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.10.0",
24+
"@modelcontextprotocol/sdk": "^1.10.2",
2525
"commander": "^13.1.0",
2626
"spawn-rx": "^5.1.2"
2727
}

client/bin/client.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,34 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
99
const distPath = join(__dirname, "../dist");
1010

1111
const server = http.createServer((request, response) => {
12-
return handler(request, response, {
12+
const handlerOptions = {
1313
public: distPath,
1414
rewrites: [{ source: "/**", destination: "/index.html" }],
15-
});
15+
headers: [
16+
{
17+
// Ensure index.html is never cached
18+
source: "index.html",
19+
headers: [
20+
{
21+
key: "Cache-Control",
22+
value: "no-cache, no-store, max-age=0",
23+
},
24+
],
25+
},
26+
{
27+
// Allow long-term caching for hashed assets
28+
source: "assets/**",
29+
headers: [
30+
{
31+
key: "Cache-Control",
32+
value: "public, max-age=31536000, immutable",
33+
},
34+
],
35+
},
36+
],
37+
};
38+
39+
return handler(request, response, handlerOptions);
1640
});
1741

1842
const port = process.env.PORT || 6274;

client/jest.config.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module.exports = {
22
preset: "ts-jest",
3-
testEnvironment: "jsdom",
3+
testEnvironment: "jest-fixed-jsdom",
44
moduleNameMapper: {
55
"^@/(.*)$": "<rootDir>/src/$1",
66
"\\.css$": "<rootDir>/src/__mocks__/styleMock.js",

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.10.2",
3+
"version": "0.11.0",
44
"description": "Client-side application for the Model Context Protocol inspector",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",
@@ -23,7 +23,7 @@
2323
"test:watch": "jest --config jest.config.cjs --watch"
2424
},
2525
"dependencies": {
26-
"@modelcontextprotocol/sdk": "^1.10.0",
26+
"@modelcontextprotocol/sdk": "^1.10.2",
2727
"@radix-ui/react-checkbox": "^1.1.4",
2828
"@radix-ui/react-dialog": "^1.1.3",
2929
"@radix-ui/react-icons": "^1.3.0",

client/src/App.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,14 @@ const App = () => {
8181
const [sseUrl, setSseUrl] = useState<string>(() => {
8282
return localStorage.getItem("lastSseUrl") || "http://localhost:3001/sse";
8383
});
84-
const [transportType, setTransportType] = useState<"stdio" | "sse">(() => {
84+
const [transportType, setTransportType] = useState<
85+
"stdio" | "sse" | "streamable-http"
86+
>(() => {
8587
return (
86-
(localStorage.getItem("lastTransportType") as "stdio" | "sse") || "stdio"
88+
(localStorage.getItem("lastTransportType") as
89+
| "stdio"
90+
| "sse"
91+
| "streamable-http") || "stdio"
8792
);
8893
});
8994
const [logLevel, setLogLevel] = useState<LoggingLevel>("debug");
@@ -642,6 +647,7 @@ const App = () => {
642647
setSelectedPrompt={(prompt) => {
643648
clearError("prompts");
644649
setSelectedPrompt(prompt);
650+
setPromptContent("");
645651
}}
646652
handleCompletion={handleCompletion}
647653
completionsSupported={completionsSupported}

client/src/components/DynamicJsonForm.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,12 @@ const DynamicJsonForm = ({
250250
<div className="space-y-4">
251251
<div className="flex justify-end space-x-2">
252252
{isJsonMode && (
253-
<Button variant="outline" size="sm" onClick={formatJson}>
253+
<Button
254+
type="button"
255+
variant="outline"
256+
size="sm"
257+
onClick={formatJson}
258+
>
254259
Format JSON
255260
</Button>
256261
)}

client/src/components/JsonView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { JsonValue } from "@/utils/jsonUtils";
33
import clsx from "clsx";
44
import { Copy, CheckCheck } from "lucide-react";
55
import { Button } from "@/components/ui/button";
6-
import { useToast } from "@/hooks/use-toast";
6+
import { useToast } from "@/lib/hooks/useToast";
77
import { getDataType, tryParseJson } from "@/utils/jsonUtils";
88

99
interface JsonViewProps {

client/src/components/OAuthCallback.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useEffect, useRef } from "react";
22
import { InspectorOAuthClientProvider } from "../lib/auth";
33
import { SESSION_KEYS } from "../lib/constants";
44
import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
5-
import { useToast } from "@/hooks/use-toast.ts";
5+
import { useToast } from "@/lib/hooks/useToast";
66
import {
77
generateOAuthErrorDescription,
88
parseOAuthCallbackParams,
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { Button } from "@/components/ui/button";
2+
import JsonView from "./JsonView";
3+
import { useMemo, useState } from "react";
4+
import {
5+
CreateMessageResult,
6+
CreateMessageResultSchema,
7+
} from "@modelcontextprotocol/sdk/types.js";
8+
import { PendingRequest } from "./SamplingTab";
9+
import DynamicJsonForm from "./DynamicJsonForm";
10+
import { useToast } from "@/lib/hooks/useToast";
11+
import { JsonSchemaType, JsonValue } from "@/utils/jsonUtils";
12+
13+
export type SamplingRequestProps = {
14+
request: PendingRequest;
15+
onApprove: (id: number, result: CreateMessageResult) => void;
16+
onReject: (id: number) => void;
17+
};
18+
19+
const SamplingRequest = ({
20+
onApprove,
21+
request,
22+
onReject,
23+
}: SamplingRequestProps) => {
24+
const { toast } = useToast();
25+
26+
const [messageResult, setMessageResult] = useState<JsonValue>({
27+
model: "stub-model",
28+
stopReason: "endTurn",
29+
role: "assistant",
30+
content: {
31+
type: "text",
32+
text: "",
33+
},
34+
});
35+
36+
const contentType = (
37+
(messageResult as { [key: string]: JsonValue })?.content as {
38+
[key: string]: JsonValue;
39+
}
40+
)?.type;
41+
42+
const schema = useMemo(() => {
43+
const s: JsonSchemaType = {
44+
type: "object",
45+
description: "Message result",
46+
properties: {
47+
model: {
48+
type: "string",
49+
default: "stub-model",
50+
description: "model name",
51+
},
52+
stopReason: {
53+
type: "string",
54+
default: "endTurn",
55+
description: "Stop reason",
56+
},
57+
role: {
58+
type: "string",
59+
default: "endTurn",
60+
description: "Role of the model",
61+
},
62+
content: {
63+
type: "object",
64+
properties: {
65+
type: {
66+
type: "string",
67+
default: "text",
68+
description: "Type of content",
69+
},
70+
},
71+
},
72+
},
73+
};
74+
75+
if (contentType === "text" && s.properties) {
76+
s.properties.content.properties = {
77+
...s.properties.content.properties,
78+
text: {
79+
type: "string",
80+
default: "",
81+
description: "text content",
82+
},
83+
};
84+
setMessageResult((prev) => ({
85+
...(prev as { [key: string]: JsonValue }),
86+
content: {
87+
type: contentType,
88+
text: "",
89+
},
90+
}));
91+
} else if (contentType === "image" && s.properties) {
92+
s.properties.content.properties = {
93+
...s.properties.content.properties,
94+
data: {
95+
type: "string",
96+
default: "",
97+
description: "Base64 encoded image data",
98+
},
99+
mimeType: {
100+
type: "string",
101+
default: "",
102+
description: "Mime type of the image",
103+
},
104+
};
105+
setMessageResult((prev) => ({
106+
...(prev as { [key: string]: JsonValue }),
107+
content: {
108+
type: contentType,
109+
data: "",
110+
mimeType: "",
111+
},
112+
}));
113+
}
114+
115+
return s;
116+
}, [contentType]);
117+
118+
const handleApprove = (id: number) => {
119+
const validationResult = CreateMessageResultSchema.safeParse(messageResult);
120+
if (!validationResult.success) {
121+
toast({
122+
title: "Error",
123+
description: `There was an error validating the message result: ${validationResult.error.message}`,
124+
variant: "destructive",
125+
});
126+
return;
127+
}
128+
129+
onApprove(id, validationResult.data);
130+
};
131+
132+
return (
133+
<div
134+
data-testid="sampling-request"
135+
className="flex gap-4 p-4 border rounded-lg space-y-4"
136+
>
137+
<div className="flex-1 bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-2 rounded">
138+
<JsonView data={JSON.stringify(request.request)} />
139+
</div>
140+
<form className="flex-1 space-y-4">
141+
<div className="space-y-2">
142+
<DynamicJsonForm
143+
schema={schema}
144+
value={messageResult}
145+
onChange={(newValue: JsonValue) => {
146+
setMessageResult(newValue);
147+
}}
148+
/>
149+
</div>
150+
<div className="flex space-x-2 mt-1">
151+
<Button type="button" onClick={() => handleApprove(request.id)}>
152+
Approve
153+
</Button>
154+
<Button
155+
type="button"
156+
variant="outline"
157+
onClick={() => onReject(request.id)}
158+
>
159+
Reject
160+
</Button>
161+
</div>
162+
</form>
163+
</div>
164+
);
165+
};
166+
167+
export default SamplingRequest;

0 commit comments

Comments
 (0)