diff --git a/client/package.json b/client/package.json index 0734ea9..3b50ab1 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/inspector-client", - "version": "0.8.2", + "version": "0.9.0", "description": "Client-side application for the Model Context Protocol inspector", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", diff --git a/client/src/components/DynamicJsonForm.tsx b/client/src/components/DynamicJsonForm.tsx index f5b0d63..bd77639 100644 --- a/client/src/components/DynamicJsonForm.tsx +++ b/client/src/components/DynamicJsonForm.tsx @@ -3,33 +3,9 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import JsonEditor from "./JsonEditor"; -import { updateValueAtPath, JsonObject } from "@/utils/jsonPathUtils"; +import { updateValueAtPath } from "@/utils/jsonUtils"; import { generateDefaultValue, formatFieldLabel } from "@/utils/schemaUtils"; - -export type JsonValue = - | string - | number - | boolean - | null - | undefined - | JsonValue[] - | { [key: string]: JsonValue }; - -export type JsonSchemaType = { - type: - | "string" - | "number" - | "integer" - | "boolean" - | "array" - | "object" - | "null"; - description?: string; - required?: boolean; - default?: JsonValue; - properties?: Record; - items?: JsonSchemaType; -}; +import type { JsonValue, JsonSchemaType, JsonObject } from "@/utils/jsonUtils"; interface DynamicJsonFormProps { schema: JsonSchemaType; diff --git a/client/src/components/JsonView.tsx b/client/src/components/JsonView.tsx index b59f3cc..3fcbf8e 100644 --- a/client/src/components/JsonView.tsx +++ b/client/src/components/JsonView.tsx @@ -1,9 +1,10 @@ import { useState, memo, useMemo, useCallback, useEffect } from "react"; -import { JsonValue } from "./DynamicJsonForm"; +import type { JsonValue } from "@/utils/jsonUtils"; import clsx from "clsx"; import { Copy, CheckCheck } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useToast } from "@/hooks/use-toast"; +import { getDataType, tryParseJson } from "@/utils/jsonUtils"; interface JsonViewProps { data: unknown; @@ -11,21 +12,7 @@ interface JsonViewProps { initialExpandDepth?: number; className?: string; withCopyButton?: boolean; -} - -function tryParseJson(str: string): { success: boolean; data: JsonValue } { - const trimmed = str.trim(); - if ( - !(trimmed.startsWith("{") && trimmed.endsWith("}")) && - !(trimmed.startsWith("[") && trimmed.endsWith("]")) - ) { - return { success: false, data: str }; - } - try { - return { success: true, data: JSON.parse(str) }; - } catch { - return { success: false, data: str }; - } + isError?: boolean; } const JsonView = memo( @@ -35,6 +22,7 @@ const JsonView = memo( initialExpandDepth = 3, className, withCopyButton = true, + isError = false, }: JsonViewProps) => { const { toast } = useToast(); const [copied, setCopied] = useState(false); @@ -100,6 +88,7 @@ const JsonView = memo( name={name} depth={0} initialExpandDepth={initialExpandDepth} + isError={isError} /> @@ -114,28 +103,28 @@ interface JsonNodeProps { name?: string; depth: number; initialExpandDepth: number; + isError?: boolean; } const JsonNode = memo( - ({ data, name, depth = 0, initialExpandDepth }: JsonNodeProps) => { + ({ + data, + name, + depth = 0, + initialExpandDepth, + isError = false, + }: JsonNodeProps) => { const [isExpanded, setIsExpanded] = useState(depth < initialExpandDepth); - - const getDataType = (value: JsonValue): string => { - if (Array.isArray(value)) return "array"; - if (value === null) return "null"; - return typeof value; - }; - - const dataType = getDataType(data); - - const typeStyleMap: Record = { + const [typeStyleMap] = useState>({ number: "text-blue-600", boolean: "text-amber-600", null: "text-purple-600", undefined: "text-gray-600", - string: "text-green-600 break-all whitespace-pre-wrap", + string: "text-green-600 group-hover:text-green-500", + error: "text-red-600 group-hover:text-red-500", default: "text-gray-700", - }; + }); + const dataType = getDataType(data); const renderCollapsible = (isArray: boolean) => { const items = isArray @@ -236,7 +225,14 @@ const JsonNode = memo( {name}: )} -
"{value}"
+
+              "{value}"
+            
); } @@ -250,8 +246,8 @@ const JsonNode = memo( )}
 setIsExpanded(!isExpanded)}
             title={isExpanded ? "Click to collapse" : "Click to expand"}
diff --git a/client/src/components/ListPane.tsx b/client/src/components/ListPane.tsx
index 90693dd..6f9a8cf 100644
--- a/client/src/components/ListPane.tsx
+++ b/client/src/components/ListPane.tsx
@@ -22,7 +22,7 @@ const ListPane = ({
   isButtonDisabled,
 }: ListPaneProps) => (
   
-
+

{title}

diff --git a/client/src/components/PromptsTab.tsx b/client/src/components/PromptsTab.tsx index 80e5fe6..c697d52 100644 --- a/client/src/components/PromptsTab.tsx +++ b/client/src/components/PromptsTab.tsx @@ -108,7 +108,7 @@ const PromptsTab = ({ />
-
+

{selectedPrompt ? selectedPrompt.name : "Select a prompt"}

diff --git a/client/src/components/ResourcesTab.tsx b/client/src/components/ResourcesTab.tsx index 23cfbe7..ac9a824 100644 --- a/client/src/components/ResourcesTab.tsx +++ b/client/src/components/ResourcesTab.tsx @@ -162,7 +162,7 @@ const ResourcesTab = ({ />
-
+

-
+

MCP Inspector v{version} @@ -105,14 +105,19 @@ const Sidebar = ({
- + setCommand(e.target.value)} @@ -134,8 +142,14 @@ const Sidebar = ({ />
- + setArgs(e.target.value)} @@ -146,8 +160,11 @@ const Sidebar = ({ ) : ( <>
- + setSseUrl(e.target.value)} @@ -159,6 +176,7 @@ const Sidebar = ({ variant="outline" onClick={() => setShowBearerToken(!showBearerToken)} className="flex items-center w-full" + aria-expanded={showBearerToken} > {showBearerToken ? ( @@ -169,8 +187,14 @@ const Sidebar = ({ {showBearerToken && (
- + setBearerToken(e.target.value)} @@ -189,6 +213,7 @@ const Sidebar = ({ onClick={() => setShowEnvVars(!showEnvVars)} className="flex items-center w-full" data-testid="env-vars-button" + aria-expanded={showEnvVars} > {showEnvVars ? ( @@ -203,6 +228,7 @@ const Sidebar = ({
{ @@ -245,6 +271,7 @@ const Sidebar = ({
setShowConfig(!showConfig)} className="flex items-center w-full" data-testid="config-button" + aria-expanded={showConfig} > {showConfig ? ( @@ -327,7 +355,10 @@ const Sidebar = ({ return (
-
{typeof configItem.value === "number" ? ( - + @@ -377,6 +409,7 @@ const Sidebar = ({ ) : ( { @@ -400,7 +433,13 @@ const Sidebar = ({
{connectionStatus === "connected" && (
- @@ -450,14 +489,19 @@ const Sidebar = ({ {loggingSupported && connectionStatus === "connected" && (
- +