Skip to content

Commit 1930325

Browse files
committed
Support structured tool results
1 parent f3406ca commit 1930325

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

client/src/App.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
22
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
33
import {
4-
CallToolResultSchema,
4+
CompatibilityCallToolResultSchema,
55
ClientRequest,
66
CreateMessageRequestSchema,
77
CreateMessageResult,
@@ -19,6 +19,7 @@ import {
1919
Root,
2020
ServerNotification,
2121
Tool,
22+
CompatibilityCallToolResult,
2223
} from "@modelcontextprotocol/sdk/types.js";
2324
import { useEffect, useRef, useState } from "react";
2425

@@ -44,7 +45,7 @@ import {
4445
FolderTree,
4546
} from "lucide-react";
4647

47-
import { AnyZodObject } from "zod";
48+
import { ZodType } from "zod";
4849
import "./App.css";
4950
import ConsoleTab from "./components/ConsoleTab";
5051
import HistoryAndNotifications from "./components/History";
@@ -69,7 +70,8 @@ const App = () => {
6970
const [prompts, setPrompts] = useState<Prompt[]>([]);
7071
const [promptContent, setPromptContent] = useState<string>("");
7172
const [tools, setTools] = useState<Tool[]>([]);
72-
const [toolResult, setToolResult] = useState<string>("");
73+
const [toolResult, setToolResult] =
74+
useState<CompatibilityCallToolResult | null>(null);
7375
const [error, setError] = useState<string | null>(null);
7476
const [command, setCommand] = useState<string>(() => {
7577
return (
@@ -150,7 +152,7 @@ const App = () => {
150152
]);
151153
};
152154

153-
const makeRequest = async <T extends AnyZodObject>(
155+
const makeRequest = async <T extends ZodType<object>>(
154156
request: ClientRequest,
155157
schema: T,
156158
) => {
@@ -254,9 +256,9 @@ const App = () => {
254256
},
255257
},
256258
},
257-
CallToolResultSchema,
259+
CompatibilityCallToolResultSchema,
258260
);
259-
setToolResult(JSON.stringify(response.toolResult, null, 2));
261+
setToolResult(response);
260262
};
261263

262264
const handleRootsChange = async () => {
@@ -444,7 +446,7 @@ const App = () => {
444446
selectedTool={selectedTool}
445447
setSelectedTool={(tool) => {
446448
setSelectedTool(tool);
447-
setToolResult("");
449+
setToolResult(null);
448450
}}
449451
toolResult={toolResult}
450452
nextCursor={nextToolCursor}

client/src/components/ToolsTab.tsx

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { AlertCircle, Send } from "lucide-react";
88
import { useState } from "react";
99
import ListPane from "./ListPane";
1010

11+
import { CompatibilityCallToolResult } from "@modelcontextprotocol/sdk/types.js";
12+
1113
const ToolsTab = ({
1214
tools,
1315
listTools,
@@ -23,12 +25,56 @@ const ToolsTab = ({
2325
callTool: (name: string, params: Record<string, unknown>) => void;
2426
selectedTool: Tool | null;
2527
setSelectedTool: (tool: Tool) => void;
26-
toolResult: string;
28+
toolResult: CompatibilityCallToolResult | null;
2729
nextCursor: ListToolsResult["nextCursor"];
2830
error: string | null;
2931
}) => {
3032
const [params, setParams] = useState<Record<string, unknown>>({});
3133

34+
const renderToolResult = () => {
35+
if (!toolResult) return null;
36+
37+
if ("content" in toolResult) {
38+
return (
39+
<>
40+
<h4 className="font-semibold mb-2">
41+
Tool Result: {toolResult.isError ? "Error" : "Success"}
42+
</h4>
43+
{toolResult.content.map((item, index) => (
44+
<div key={index} className="mb-2">
45+
{item.type === "text" && (
46+
<pre className="bg-gray-50 p-4 rounded text-sm overflow-auto max-h-64">
47+
{item.text}
48+
</pre>
49+
)}
50+
{item.type === "image" && (
51+
<img
52+
src={`data:${item.mimeType};base64,${item.data}`}
53+
alt="Tool result image"
54+
className="max-w-full h-auto"
55+
/>
56+
)}
57+
{item.type === "resource" && (
58+
<pre className="bg-gray-50 p-4 rounded text-sm overflow-auto max-h-64">
59+
{JSON.stringify(item.resource, null, 2)}
60+
</pre>
61+
)}
62+
</div>
63+
))}
64+
</>
65+
);
66+
} else if ("toolResult" in toolResult) {
67+
return (
68+
<>
69+
<h4 className="font-semibold mb-2">Tool Result (Legacy):</h4>
70+
<pre className="bg-gray-50 p-4 rounded text-sm overflow-auto max-h-64">
71+
{JSON.stringify(toolResult.toolResult, null, 2)}
72+
</pre>
73+
</>
74+
);
75+
}
76+
};
77+
3278
return (
3379
<TabsContent value="tools" className="grid grid-cols-2 gap-4">
3480
<ListPane
@@ -100,14 +146,7 @@ const ToolsTab = ({
100146
<Send className="w-4 h-4 mr-2" />
101147
Run Tool
102148
</Button>
103-
{toolResult && (
104-
<>
105-
<h4 className="font-semibold mb-2">Tool Result:</h4>
106-
<pre className="bg-gray-50 p-4 rounded text-sm overflow-auto max-h-64">
107-
{toolResult}
108-
</pre>
109-
</>
110-
)}
149+
{toolResult && renderToolResult()}
111150
</div>
112151
) : (
113152
<Alert>

0 commit comments

Comments
 (0)