diff --git a/client/src/components/ToolsTab.tsx b/client/src/components/ToolsTab.tsx index ee9a4f97d..82ebdb0b9 100644 --- a/client/src/components/ToolsTab.tsx +++ b/client/src/components/ToolsTab.tsx @@ -8,15 +8,15 @@ import { Textarea } from "@/components/ui/textarea"; import DynamicJsonForm, { JsonSchemaType, JsonValue } from "./DynamicJsonForm"; import { generateDefaultValue } from "@/utils/schemaUtils"; import { + CallToolResultSchema, + CompatibilityCallToolResult, ListToolsResult, Tool, - CallToolResultSchema, } from "@modelcontextprotocol/sdk/types.js"; import { AlertCircle, Send } from "lucide-react"; import { useEffect, useState } from "react"; import ListPane from "./ListPane"; - -import { CompatibilityCallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { escapeUnicode } from "@/utils/escapeUnicode"; const ToolsTab = ({ tools, @@ -54,7 +54,7 @@ const ToolsTab = ({ <>

Invalid Tool Result:

-              {JSON.stringify(toolResult, null, 2)}
+              {escapeUnicode(toolResult)}
             

Errors:

{parsedResult.error.errors.map((error, idx) => ( @@ -62,7 +62,7 @@ const ToolsTab = ({ key={idx} className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64" > - {JSON.stringify(error, null, 2)} + {escapeUnicode(error)} ))} @@ -101,7 +101,7 @@ const ToolsTab = ({ ) : (
-                    {JSON.stringify(item.resource, null, 2)}
+                    {escapeUnicode(item.resource)}
                   
))} @@ -113,7 +113,7 @@ const ToolsTab = ({ <>

Tool Result (Legacy):

-            {JSON.stringify(toolResult.toolResult, null, 2)}
+            {escapeUnicode(toolResult.toolResult)}
           
); diff --git a/client/src/utils/__tests__/escapeUnicode.test.ts b/client/src/utils/__tests__/escapeUnicode.test.ts new file mode 100644 index 000000000..b2eb130c5 --- /dev/null +++ b/client/src/utils/__tests__/escapeUnicode.test.ts @@ -0,0 +1,27 @@ +import { escapeUnicode } from "../escapeUnicode"; + +describe("escapeUnicode", () => { + it("should escape Unicode characters in a string", () => { + const input = { text: "你好世界" }; + const expected = '{\n "text": "\\\\u4f60\\\\u597d\\\\u4e16\\\\u754c"\n}'; + expect(escapeUnicode(input)).toBe(expected); + }); + + it("should handle empty strings", () => { + const input = { text: "" }; + const expected = '{\n "text": ""\n}'; + expect(escapeUnicode(input)).toBe(expected); + }); + + it("should handle null and undefined values", () => { + const input = { text: null, value: undefined }; + const expected = '{\n "text": null\n}'; + expect(escapeUnicode(input)).toBe(expected); + }); + + it("should handle numbers and booleans", () => { + const input = { number: 123, boolean: true }; + const expected = '{\n "number": 123,\n "boolean": true\n}'; + expect(escapeUnicode(input)).toBe(expected); + }); +}); diff --git a/client/src/utils/escapeUnicode.ts b/client/src/utils/escapeUnicode.ts new file mode 100644 index 000000000..ed6fbd930 --- /dev/null +++ b/client/src/utils/escapeUnicode.ts @@ -0,0 +1,16 @@ +// Utility function to escape Unicode characters +export function escapeUnicode(obj: unknown): string { + return JSON.stringify( + obj, + (_key: string, value) => { + if (typeof value === "string") { + // Replace non-ASCII characters with their Unicode escape sequences + return value.replace(/[^\0-\x7F]/g, (char) => { + return "\\u" + ("0000" + char.charCodeAt(0).toString(16)).slice(-4); + }); + } + return value; + }, + 2, + ); +} diff --git a/package-lock.json b/package-lock.json index 21c9c3083..68929c522 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "mcp-inspector": "bin/cli.js" }, "devDependencies": { + "@types/jest": "^29.5.14", "@types/node": "^22.7.5", "@types/shell-quote": "^1.7.5", "prettier": "3.3.3" diff --git a/package.json b/package.json index 3f40d2e6e..e3bce9216 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "ts-node": "^10.9.2" }, "devDependencies": { + "@types/jest": "^29.5.14", "@types/node": "^22.7.5", "@types/shell-quote": "^1.7.5", "prettier": "3.3.3"