Skip to content
57 changes: 52 additions & 5 deletions client/src/components/ToolsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,41 @@ import { Textarea } from "@/components/ui/textarea";
import {
ListToolsResult,
Tool,
CallToolResultSchema,
TextContentSchema,
ImageContentSchema,
ResultSchema,
EmbeddedResourceSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { AlertCircle, Send } from "lucide-react";
import { useState } from "react";
import ListPane from "./ListPane";

import { CompatibilityCallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

// Define the AudioContent schema
export const AudioContentSchema = z.object({
type: z.literal("audio"),
data: z.string().base64(),
mimeType: z.string(),
}).passthrough();

// Extend the CallToolResult schema to include audio content
export const ExtendedCallToolResultSchema = ResultSchema.extend({
content: z.array(
z.discriminatedUnion("type", [
TextContentSchema,
ImageContentSchema,
AudioContentSchema,
EmbeddedResourceSchema,
])
),
isError: z.boolean().default(false).optional(),
});

// Export the types
export type AudioContent = z.infer<typeof AudioContentSchema>;
export type ExtendedCallToolResult = z.infer<typeof ExtendedCallToolResultSchema>;

const ToolsTab = ({
tools,
Expand All @@ -40,7 +68,7 @@ const ToolsTab = ({
if (!toolResult) return null;

if ("content" in toolResult) {
const parsedResult = CallToolResultSchema.safeParse(toolResult);
const parsedResult = ExtendedCallToolResultSchema.safeParse(toolResult);
if (!parsedResult.success) {
return (
<>
Expand Down Expand Up @@ -82,10 +110,29 @@ const ToolsTab = ({
className="max-w-full h-auto"
/>
)}
{item.type === "audio" && (
<audio
controls
src={`data:${item.mimeType};base64,${item.data}`}
className="w-full"
>
<p>Your browser does not support audio playback</p>
</audio>
)}
{item.type === "resource" && (
<pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 whitespace-pre-wrap break-words p-4 rounded text-sm overflow-auto max-h-64">
{JSON.stringify(item.resource, null, 2)}
</pre>
item.resource?.mimeType?.startsWith("audio/") ? (
<audio
controls
src={`data:${item.resource.mimeType};base64,${item.resource.blob}`}
className="w-full"
>
<p>Your browser does not support audio playback</p>
</audio>
) : (
<pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 whitespace-pre-wrap break-words p-4 rounded text-sm overflow-auto max-h-64">
{JSON.stringify(item.resource, null, 2)}
</pre>
)
)}
</div>
))}
Expand Down