Skip to content

Commit 718bd10

Browse files
committed
mcp: add icon + website for testing SEP-974
1 parent 3772110 commit 718bd10

File tree

11 files changed

+193
-23
lines changed

11 files changed

+193
-23
lines changed

cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
},
2525
"devDependencies": {},
2626
"dependencies": {
27-
"@modelcontextprotocol/sdk": "^1.17.5",
27+
"@modelcontextprotocol/sdk": "file:../../typescript-sdk",
2828
"commander": "^13.1.0",
2929
"spawn-rx": "^5.1.2"
3030
}

client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"cleanup:e2e": "node e2e/global-teardown.js"
2626
},
2727
"dependencies": {
28-
"@modelcontextprotocol/sdk": "^1.17.5",
28+
"@modelcontextprotocol/sdk": "file:../../typescript-sdk",
2929
"@radix-ui/react-checkbox": "^1.1.4",
3030
"@radix-ui/react-dialog": "^1.1.3",
3131
"@radix-ui/react-icons": "^1.3.0",

client/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ const App = () => {
198198
const {
199199
connectionStatus,
200200
serverCapabilities,
201+
serverImplementation,
201202
mcpClient,
202203
requestHistory,
203204
makeRequest,
@@ -818,6 +819,7 @@ const App = () => {
818819
logLevel={logLevel}
819820
sendLogLevelRequest={sendLogLevelRequest}
820821
loggingSupported={!!serverCapabilities?.logging || false}
822+
serverImplementation={serverImplementation}
821823
/>
822824
<div
823825
onMouseDown={handleSidebarDragStart}

client/src/__tests__/App.routing.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ jest.mock("../utils/configUtils", () => ({
4040
const disconnectedConnectionState = {
4141
connectionStatus: "disconnected" as const,
4242
serverCapabilities: null,
43+
serverImplementation: null,
4344
mcpClient: null,
4445
requestHistory: [],
4546
makeRequest: jest.fn(),
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Define Icon type locally since it might not be exported yet
2+
interface Icon {
3+
src: string;
4+
mimeType?: string;
5+
sizes?: string;
6+
}
7+
8+
// Helper type for objects that may have icons
9+
export interface WithIcons {
10+
icons?: Icon[];
11+
}
12+
13+
interface IconDisplayProps {
14+
icons?: Icon[];
15+
className?: string;
16+
size?: "sm" | "md" | "lg";
17+
}
18+
19+
const IconDisplay = ({
20+
icons,
21+
className = "",
22+
size = "md",
23+
}: IconDisplayProps) => {
24+
if (!icons || icons.length === 0) {
25+
return null;
26+
}
27+
28+
const sizeClasses = {
29+
sm: "w-4 h-4",
30+
md: "w-6 h-6",
31+
lg: "w-8 h-8",
32+
};
33+
34+
const sizeClass = sizeClasses[size];
35+
36+
return (
37+
<div className={`flex gap-1 ${className}`}>
38+
{icons.map((icon, index) => (
39+
<img
40+
key={index}
41+
src={icon.src}
42+
alt=""
43+
className={`${sizeClass} object-contain flex-shrink-0`}
44+
style={{
45+
imageRendering: "auto",
46+
}}
47+
onError={(e) => {
48+
// Hide broken images
49+
e.currentTarget.style.display = "none";
50+
}}
51+
/>
52+
))}
53+
</div>
54+
);
55+
};
56+
57+
export default IconDisplay;

client/src/components/PromptsTab.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { useEffect, useState } from "react";
1414
import ListPane from "./ListPane";
1515
import { useCompletionState } from "@/lib/hooks/useCompletionState";
1616
import JsonView from "./JsonView";
17+
import IconDisplay from "./IconDisplay";
1718

1819
export type Prompt = {
1920
name: string;
@@ -23,6 +24,7 @@ export type Prompt = {
2324
description?: string;
2425
required?: boolean;
2526
}[];
27+
icons?: { src: string; mimeType?: string; sizes?: string }[];
2628
};
2729

2830
const PromptsTab = ({
@@ -101,7 +103,10 @@ const PromptsTab = ({
101103
}}
102104
renderItem={(prompt) => (
103105
<div className="flex flex-col items-start">
104-
<span className="flex-1">{prompt.name}</span>
106+
<div className="flex items-center gap-2 w-full">
107+
<IconDisplay icons={prompt.icons} size="sm" />
108+
<span className="flex-1">{prompt.name}</span>
109+
</div>
105110
<span className="text-sm text-gray-500 text-left">
106111
{prompt.description}
107112
</span>
@@ -114,9 +119,14 @@ const PromptsTab = ({
114119

115120
<div className="bg-card border border-border rounded-lg shadow">
116121
<div className="p-4 border-b border-gray-200 dark:border-border">
117-
<h3 className="font-semibold">
118-
{selectedPrompt ? selectedPrompt.name : "Select a prompt"}
119-
</h3>
122+
<div className="flex items-center gap-2">
123+
{selectedPrompt && (
124+
<IconDisplay icons={selectedPrompt.icons} size="md" />
125+
)}
126+
<h3 className="font-semibold">
127+
{selectedPrompt ? selectedPrompt.name : "Select a prompt"}
128+
</h3>
129+
</div>
120130
</div>
121131
<div className="p-4">
122132
{error ? (

client/src/components/ResourcesTab.tsx

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { useEffect, useState } from "react";
1717
import { useCompletionState } from "@/lib/hooks/useCompletionState";
1818
import JsonView from "./JsonView";
1919
import { UriTemplate } from "@modelcontextprotocol/sdk/shared/uriTemplate.js";
20+
import IconDisplay, { WithIcons } from "./IconDisplay";
2021

2122
const ResourcesTab = ({
2223
resources,
@@ -129,7 +130,16 @@ const ResourcesTab = ({
129130
}}
130131
renderItem={(resource) => (
131132
<div className="flex items-center w-full">
132-
<FileText className="w-4 h-4 mr-2 flex-shrink-0 text-gray-500" />
133+
{(resource as WithIcons).icons &&
134+
(resource as WithIcons).icons!.length > 0 ? (
135+
<IconDisplay
136+
icons={(resource as WithIcons).icons}
137+
size="sm"
138+
className="mr-2"
139+
/>
140+
) : (
141+
<FileText className="w-4 h-4 mr-2 flex-shrink-0 text-gray-500" />
142+
)}
133143
<span className="flex-1 truncate" title={resource.uri.toString()}>
134144
{resource.name}
135145
</span>
@@ -159,7 +169,16 @@ const ResourcesTab = ({
159169
}}
160170
renderItem={(template) => (
161171
<div className="flex items-center w-full">
162-
<FileText className="w-4 h-4 mr-2 flex-shrink-0 text-gray-500" />
172+
{(template as WithIcons).icons &&
173+
(template as WithIcons).icons!.length > 0 ? (
174+
<IconDisplay
175+
icons={(template as WithIcons).icons}
176+
size="sm"
177+
className="mr-2"
178+
/>
179+
) : (
180+
<FileText className="w-4 h-4 mr-2 flex-shrink-0 text-gray-500" />
181+
)}
163182
<span className="flex-1 truncate" title={template.uriTemplate}>
164183
{template.name}
165184
</span>
@@ -175,16 +194,30 @@ const ResourcesTab = ({
175194

176195
<div className="bg-card border border-border rounded-lg shadow">
177196
<div className="p-4 border-b border-gray-200 dark:border-border flex justify-between items-center">
178-
<h3
179-
className="font-semibold truncate"
180-
title={selectedResource?.name || selectedTemplate?.name}
181-
>
182-
{selectedResource
183-
? selectedResource.name
184-
: selectedTemplate
185-
? selectedTemplate.name
186-
: "Select a resource or template"}
187-
</h3>
197+
<div className="flex items-center gap-2 min-w-0">
198+
{selectedResource && (selectedResource as WithIcons).icons && (
199+
<IconDisplay
200+
icons={(selectedResource as WithIcons).icons}
201+
size="md"
202+
/>
203+
)}
204+
{selectedTemplate && (selectedTemplate as WithIcons).icons && (
205+
<IconDisplay
206+
icons={(selectedTemplate as WithIcons).icons}
207+
size="md"
208+
/>
209+
)}
210+
<h3
211+
className="font-semibold truncate"
212+
title={selectedResource?.name || selectedTemplate?.name}
213+
>
214+
{selectedResource
215+
? selectedResource.name
216+
: selectedTemplate
217+
? selectedTemplate.name
218+
: "Select a resource or template"}
219+
</h3>
220+
</div>
188221
{selectedResource && (
189222
<div className="flex row-auto gap-1 justify-end w-2/5">
190223
{resourceSubscriptionsSupported &&

client/src/components/Sidebar.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
RefreshCwOff,
1515
Copy,
1616
CheckCheck,
17+
Server,
1718
} from "lucide-react";
1819
import { Button } from "@/components/ui/button";
1920
import { Input } from "@/components/ui/input";
@@ -38,6 +39,7 @@ import {
3839
TooltipContent,
3940
} from "@/components/ui/tooltip";
4041
import { useToast } from "../lib/hooks/useToast";
42+
import IconDisplay, { WithIcons } from "./IconDisplay";
4143

4244
interface SidebarProps {
4345
connectionStatus: ConnectionStatus;
@@ -66,6 +68,9 @@ interface SidebarProps {
6668
loggingSupported: boolean;
6769
config: InspectorConfig;
6870
setConfig: (config: InspectorConfig) => void;
71+
serverImplementation?:
72+
| (WithIcons & { name?: string; version?: string; websiteUrl?: string })
73+
| null;
6974
}
7075

7176
const Sidebar = ({
@@ -95,6 +100,7 @@ const Sidebar = ({
95100
loggingSupported,
96101
config,
97102
setConfig,
103+
serverImplementation,
98104
}: SidebarProps) => {
99105
const [theme, setTheme] = useTheme();
100106
const [showEnvVars, setShowEnvVars] = useState(false);
@@ -729,6 +735,45 @@ const Sidebar = ({
729735
</span>
730736
</div>
731737

738+
{connectionStatus === "connected" && serverImplementation && (
739+
<div className="bg-gray-50 dark:bg-gray-900 p-3 rounded-lg mb-4">
740+
<div className="flex items-center gap-2 mb-1">
741+
{(serverImplementation as WithIcons).icons &&
742+
(serverImplementation as WithIcons).icons!.length > 0 ? (
743+
<IconDisplay
744+
icons={(serverImplementation as WithIcons).icons}
745+
size="sm"
746+
/>
747+
) : (
748+
<Server className="w-4 h-4 text-gray-500" />
749+
)}
750+
{(serverImplementation as { websiteUrl?: string })
751+
.websiteUrl ? (
752+
<a
753+
href={
754+
(serverImplementation as { websiteUrl?: string })
755+
.websiteUrl
756+
}
757+
target="_blank"
758+
rel="noopener noreferrer"
759+
className="text-sm font-medium text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 hover:underline transition-colors"
760+
>
761+
{serverImplementation.name || "MCP Server"}
762+
</a>
763+
) : (
764+
<span className="text-sm font-medium text-gray-800 dark:text-gray-200">
765+
{serverImplementation.name || "MCP Server"}
766+
</span>
767+
)}
768+
</div>
769+
{serverImplementation.version && (
770+
<div className="text-xs text-gray-500 dark:text-gray-400">
771+
Version: {serverImplementation.version}
772+
</div>
773+
)}
774+
</div>
775+
)}
776+
732777
{loggingSupported && connectionStatus === "connected" && (
733778
<div className="space-y-2">
734779
<label

client/src/components/ToolsTab.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { useEffect, useState } from "react";
2222
import ListPane from "./ListPane";
2323
import JsonView from "./JsonView";
2424
import ToolResults from "./ToolResults";
25+
import IconDisplay, { WithIcons } from "./IconDisplay";
2526

2627
// Type guard to safely detect the optional _meta field without using `any`
2728
const hasMeta = (tool: Tool): tool is Tool & { _meta: unknown } =>
@@ -83,7 +84,10 @@ const ToolsTab = ({
8384
setSelectedItem={setSelectedTool}
8485
renderItem={(tool) => (
8586
<div className="flex flex-col items-start">
86-
<span className="flex-1">{tool.name}</span>
87+
<div className="flex items-center gap-2 w-full">
88+
<IconDisplay icons={(tool as WithIcons).icons} size="sm" />
89+
<span className="flex-1">{tool.name}</span>
90+
</div>
8791
<span className="text-sm text-gray-500 text-left">
8892
{tool.description}
8993
</span>
@@ -96,9 +100,17 @@ const ToolsTab = ({
96100

97101
<div className="bg-card border border-border rounded-lg shadow">
98102
<div className="p-4 border-b border-gray-200 dark:border-border">
99-
<h3 className="font-semibold">
100-
{selectedTool ? selectedTool.name : "Select a tool"}
101-
</h3>
103+
<div className="flex items-center gap-2">
104+
{selectedTool && (
105+
<IconDisplay
106+
icons={(selectedTool as WithIcons).icons}
107+
size="md"
108+
/>
109+
)}
110+
<h3 className="font-semibold">
111+
{selectedTool ? selectedTool.name : "Select a tool"}
112+
</h3>
113+
</div>
102114
</div>
103115
<div className="p-4">
104116
{selectedTool ? (

client/src/lib/hooks/useConnection.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ export function useConnection({
110110
{ request: string; response?: string }[]
111111
>([]);
112112
const [completionsSupported, setCompletionsSupported] = useState(false);
113+
const [serverImplementation, setServerImplementation] = useState<{
114+
name?: string;
115+
version?: string;
116+
websiteUrl?: string;
117+
icons?: { src: string; mimeType?: string; sizes?: string }[];
118+
} | null>(null);
113119

114120
useEffect(() => {
115121
if (!oauthClientId) {
@@ -542,6 +548,8 @@ export function useConnection({
542548
setClientTransport(transport);
543549

544550
capabilities = client.getServerCapabilities();
551+
const serverInfo = client.getServerVersion();
552+
setServerImplementation(serverInfo || null);
545553
const initializeRequest = {
546554
method: "initialize",
547555
};
@@ -651,11 +659,13 @@ export function useConnection({
651659
setConnectionStatus("disconnected");
652660
setCompletionsSupported(false);
653661
setServerCapabilities(null);
662+
setServerImplementation(null);
654663
};
655664

656665
return {
657666
connectionStatus,
658667
serverCapabilities,
668+
serverImplementation,
659669
mcpClient,
660670
requestHistory,
661671
makeRequest,

0 commit comments

Comments
 (0)