From 3549220acc8ba301c87bb4d7085b08c25059aaca Mon Sep 17 00:00:00 2001 From: olaservo Date: Sun, 25 Jan 2026 06:09:08 -0700 Subject: [PATCH] feat(client): display tool annotation badges in Tools tab Add visual badges to show tool annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) when a tool is selected. Badges appear below the tool description with color-coded styling: - Read-only: blue - Destructive: red - Idempotent: gray outline - Open-world: purple Co-Authored-By: Claude Opus 4.5 --- client/src/components/ToolsTab.tsx | 61 ++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/client/src/components/ToolsTab.tsx b/client/src/components/ToolsTab.tsx index d872e629..19d8fa0c 100644 --- a/client/src/components/ToolsTab.tsx +++ b/client/src/components/ToolsTab.tsx @@ -24,6 +24,7 @@ import { CompatibilityCallToolResult, ListToolsResult, Tool, + ToolAnnotations, } from "@modelcontextprotocol/sdk/types.js"; import { Loader2, @@ -56,6 +57,63 @@ import { const hasMeta = (tool: Tool): tool is Tool & { _meta: unknown } => typeof (tool as { _meta?: unknown })._meta !== "undefined"; +// Type guard to safely detect the optional annotations field +const hasAnnotations = ( + tool: Tool, +): tool is Tool & { annotations: ToolAnnotations } => + typeof (tool as { annotations?: unknown }).annotations !== "undefined" && + (tool as { annotations?: unknown }).annotations !== null; + +// Helper to render annotation badges +const AnnotationBadges = ({ + annotations, +}: { + annotations: ToolAnnotations; +}) => { + const badges: { + label: string; + variant: "default" | "destructive" | "secondary" | "outline"; + }[] = []; + + if (annotations.readOnlyHint === true) { + badges.push({ label: "Read-only", variant: "secondary" }); + } + if (annotations.destructiveHint === true) { + badges.push({ label: "Destructive", variant: "destructive" }); + } + if (annotations.idempotentHint === true) { + badges.push({ label: "Idempotent", variant: "outline" }); + } + if (annotations.openWorldHint === true) { + badges.push({ label: "Open-world", variant: "default" }); + } + + if (badges.length === 0) return null; + + return ( +
+ {badges.map(({ label, variant }) => ( + + {label} + + ))} +
+ ); +}; + const ToolsTab = ({ tools, listTools, @@ -213,6 +271,9 @@ const ToolsTab = ({

{selectedTool.description}

+ {hasAnnotations(selectedTool) && ( + + )} {Object.entries(selectedTool.inputSchema.properties ?? []).map( ([key, value]) => { // First resolve any $ref references