Skip to content

Commit 3dbcce5

Browse files
authored
Merge branch 'main' into fix/oauth-cors-proxy-support
2 parents 1dba680 + 7c8b031 commit 3dbcce5

File tree

8 files changed

+112
-38
lines changed

8 files changed

+112
-38
lines changed

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.20.0",
3+
"version": "0.21.1",
44
"description": "CLI for the Model Context Protocol inspector",
55
"license": "SEE LICENSE IN LICENSE",
66
"author": "Model Context Protocol a Series of LF Projects, LLC.",

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.20.0",
3+
"version": "0.21.1",
44
"description": "Client-side application for the Model Context Protocol inspector",
55
"license": "SEE LICENSE IN LICENSE",
66
"author": "Model Context Protocol a Series of LF Projects, LLC.",

client/src/components/DynamicJsonForm.tsx

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
125125
const [rawJsonValue, setRawJsonValue] = useState<string>(
126126
JSON.stringify(value ?? generateDefaultValue(schema), null, 2),
127127
);
128+
const [numericInputDrafts, setNumericInputDrafts] = useState<
129+
Record<string, string>
130+
>({});
128131

129132
// Use a ref to manage debouncing timeouts to avoid parsing JSON
130133
// on every keystroke which would be inefficient and error-prone
@@ -134,6 +137,37 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
134137
return !!jsonError;
135138
};
136139

140+
const getPathKey = (path: string[]) =>
141+
path.length === 0 ? "$root" : path.join(".");
142+
143+
const getNumericDisplayValue = (
144+
path: string[],
145+
currentValue: JsonValue,
146+
): string => {
147+
const pathKey = getPathKey(path);
148+
if (Object.prototype.hasOwnProperty.call(numericInputDrafts, pathKey)) {
149+
return numericInputDrafts[pathKey];
150+
}
151+
return typeof currentValue === "number" ? currentValue.toString() : "";
152+
};
153+
154+
const updateNumericDraft = (path: string[], draftValue: string) => {
155+
const pathKey = getPathKey(path);
156+
setNumericInputDrafts((prev) => ({ ...prev, [pathKey]: draftValue }));
157+
};
158+
159+
const clearNumericDraft = (path: string[]) => {
160+
const pathKey = getPathKey(path);
161+
setNumericInputDrafts((prev) => {
162+
if (!Object.prototype.hasOwnProperty.call(prev, pathKey)) {
163+
return prev;
164+
}
165+
const next = { ...prev };
166+
delete next[pathKey];
167+
return next;
168+
});
169+
};
170+
137171
// Debounce JSON parsing and parent updates to handle typing gracefully
138172
const debouncedUpdateParent = useCallback(
139173
(jsonString: string) => {
@@ -429,9 +463,10 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
429463
return (
430464
<Input
431465
type="number"
432-
value={(currentValue as number)?.toString() ?? ""}
466+
value={getNumericDisplayValue(path, currentValue)}
433467
onChange={(e) => {
434468
const val = e.target.value;
469+
updateNumericDraft(path, val);
435470
if (!val && !isRequired) {
436471
handleFieldChange(path, undefined);
437472
} else {
@@ -441,6 +476,19 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
441476
}
442477
}
443478
}}
479+
onBlur={(e) => {
480+
const val = e.target.value;
481+
if (!val) {
482+
clearNumericDraft(path);
483+
return;
484+
}
485+
486+
const num = Number(val);
487+
if (!isNaN(num)) {
488+
handleFieldChange(path, num);
489+
}
490+
clearNumericDraft(path);
491+
}}
444492
placeholder={propSchema.description}
445493
required={isRequired}
446494
min={propSchema.minimum}
@@ -453,9 +501,10 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
453501
<Input
454502
type="number"
455503
step="1"
456-
value={(currentValue as number)?.toString() ?? ""}
504+
value={getNumericDisplayValue(path, currentValue)}
457505
onChange={(e) => {
458506
const val = e.target.value;
507+
updateNumericDraft(path, val);
459508
if (!val && !isRequired) {
460509
handleFieldChange(path, undefined);
461510
} else {
@@ -465,6 +514,19 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
465514
}
466515
}
467516
}}
517+
onBlur={(e) => {
518+
const val = e.target.value;
519+
if (!val) {
520+
clearNumericDraft(path);
521+
return;
522+
}
523+
524+
const num = Number(val);
525+
if (!isNaN(num) && Number.isInteger(num)) {
526+
handleFieldChange(path, num);
527+
}
528+
clearNumericDraft(path);
529+
}}
468530
placeholder={propSchema.description}
469531
required={isRequired}
470532
min={propSchema.minimum}

client/src/components/ToolResults.tsx

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,14 @@ const checkContentCompatibility = (
2222
text?: string;
2323
[key: string]: unknown;
2424
}>,
25-
): { isCompatible: boolean; message: string } => {
25+
): { hasMatch: boolean; message: string } | null => {
2626
// Look for at least one text content block that matches the structured content
2727
const textBlocks = unstructuredContent.filter(
2828
(block) => block.type === "text",
2929
);
3030

3131
if (textBlocks.length === 0) {
32-
return {
33-
isCompatible: false,
34-
message: "No text blocks to match structured content",
35-
};
32+
return null;
3633
}
3734

3835
// Check if any text block contains JSON that matches the structured content
@@ -49,7 +46,7 @@ const checkContentCompatibility = (
4946

5047
if (isEqual) {
5148
return {
52-
isCompatible: true,
49+
hasMatch: true,
5350
message: `Structured content matches text block${textBlocks.length > 1 ? " (multiple blocks)" : ""}${unstructuredContent.length > textBlocks.length ? " + other content" : ""}`,
5451
};
5552
}
@@ -59,10 +56,7 @@ const checkContentCompatibility = (
5956
}
6057
}
6158

62-
return {
63-
isCompatible: false,
64-
message: "No text block matches structured content",
65-
};
59+
return null;
6660
};
6761

6862
const ToolResults = ({
@@ -195,16 +189,9 @@ const ToolResults = ({
195189
<h5 className="font-semibold mb-2 text-sm">
196190
Unstructured Content:
197191
</h5>
198-
{compatibilityResult && (
199-
<div
200-
className={`mb-2 p-2 rounded text-sm ${
201-
compatibilityResult.isCompatible
202-
? "bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200"
203-
: "bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200"
204-
}`}
205-
>
206-
{compatibilityResult.isCompatible ? "✓" : "⚠"}{" "}
207-
{compatibilityResult.message}
192+
{compatibilityResult?.hasMatch && (
193+
<div className="mb-2 p-2 rounded text-sm bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200">
194+
{compatibilityResult.message}
208195
</div>
209196
)}
210197
</>

client/src/components/__tests__/DynamicJsonForm.test.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
22
import "@testing-library/jest-dom";
33
import { describe, it, expect, jest } from "@jest/globals";
4-
import { useRef } from "react";
4+
import { useRef, useState } from "react";
55
import DynamicJsonForm, { DynamicJsonFormRef } from "../DynamicJsonForm";
66
import type { JsonSchemaType } from "@/utils/jsonUtils";
77

@@ -402,6 +402,29 @@ describe("DynamicJsonForm Number Fields", () => {
402402

403403
expect(onChange).toHaveBeenCalledWith(98.6);
404404
});
405+
406+
it("should preserve decimal zero while typing", () => {
407+
const schema: JsonSchemaType = {
408+
type: "number",
409+
description: "Coordinate",
410+
};
411+
412+
const WrappedForm = () => {
413+
const [value, setValue] = useState<number>(0);
414+
return (
415+
<DynamicJsonForm schema={schema} value={value} onChange={setValue} />
416+
);
417+
};
418+
419+
render(<WrappedForm />);
420+
const input = screen.getByRole("spinbutton") as HTMLInputElement;
421+
422+
fireEvent.change(input, { target: { value: "-74.0" } });
423+
expect(input.value).toBe("-74.0");
424+
425+
fireEvent.change(input, { target: { value: "-74.01" } });
426+
expect(input.value).toBe("-74.01");
427+
});
405428
});
406429
});
407430

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/inspector",
3-
"version": "0.20.0",
3+
"version": "0.21.1",
44
"description": "Model Context Protocol inspector",
55
"license": "SEE LICENSE IN LICENSE",
66
"author": "Model Context Protocol a Series of LF Projects, LLC.",
@@ -14,6 +14,7 @@
1414
"client/bin",
1515
"client/dist",
1616
"server/build",
17+
"server/static",
1718
"cli/build"
1819
],
1920
"workspaces": [
@@ -47,9 +48,9 @@
4748
"check-version": "node scripts/check-version-consistency.js"
4849
},
4950
"dependencies": {
50-
"@modelcontextprotocol/inspector-cli": "^0.20.0",
51-
"@modelcontextprotocol/inspector-client": "^0.20.0",
52-
"@modelcontextprotocol/inspector-server": "^0.20.0",
51+
"@modelcontextprotocol/inspector-cli": "^0.21.1",
52+
"@modelcontextprotocol/inspector-client": "^0.21.1",
53+
"@modelcontextprotocol/inspector-server": "^0.21.1",
5354
"@modelcontextprotocol/sdk": "^1.25.2",
5455
"concurrently": "^9.2.0",
5556
"node-fetch": "^3.3.2",

server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/inspector-server",
3-
"version": "0.20.0",
3+
"version": "0.21.1",
44
"description": "Server-side application for the Model Context Protocol inspector",
55
"license": "SEE LICENSE IN LICENSE",
66
"author": "Model Context Protocol a Series of LF Projects, LLC.",
@@ -12,6 +12,7 @@
1212
},
1313
"files": [
1414
"build",
15+
"static",
1516
"LICENSE"
1617
],
1718
"scripts": {

0 commit comments

Comments
 (0)