Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ const App = () => {
const {
connectionStatus,
serverCapabilities,
serverImplementation,
Copy link
Member

@cliffhall cliffhall Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just call this serverInfo since it is that is what we're storing in this variable?

Screenshot 2025-10-21 at 3 09 24 PM Image

mcpClient,
requestHistory,
clearRequestHistory,
Expand Down Expand Up @@ -920,6 +921,7 @@ const App = () => {
loggingSupported={!!serverCapabilities?.logging || false}
connectionType={connectionType}
setConnectionType={setConnectionType}
serverImplementation={serverImplementation}
/>
<div
onMouseDown={handleSidebarDragStart}
Expand Down
1 change: 1 addition & 0 deletions client/src/__tests__/App.routing.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const disconnectedConnectionState = {
completionsSupported: false,
connect: jest.fn(),
disconnect: jest.fn(),
serverImplementation: null,
};

// Connected state for tests that need an active connection
Expand Down
57 changes: 57 additions & 0 deletions client/src/components/IconDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Define Icon type locally since it might not be exported yet
interface Icon {
src: string;
mimeType?: string;
sizes?: string[];
}

// Helper type for objects that may have icons
export interface WithIcons {
icons?: Icon[];
}

interface IconDisplayProps {
icons?: Icon[];
className?: string;
size?: "sm" | "md" | "lg";
}

const IconDisplay = ({
icons,
className = "",
size = "md",
}: IconDisplayProps) => {
if (!icons || icons.length === 0) {
return null;
}

const sizeClasses = {
sm: "w-4 h-4",
md: "w-6 h-6",
lg: "w-8 h-8",
};

const sizeClass = sizeClasses[size];

return (
<div className={`flex gap-1 ${className}`}>
{icons.map((icon, index) => (
<img
key={index}
src={icon.src}
alt=""
className={`${sizeClass} object-contain flex-shrink-0`}
style={{
imageRendering: "auto",
}}
onError={(e) => {
// Hide broken images
e.currentTarget.style.display = "none";
}}
/>
))}
</div>
);
};

export default IconDisplay;
3 changes: 3 additions & 0 deletions client/src/components/PromptsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useEffect, useState } from "react";
import ListPane from "./ListPane";
import { useCompletionState } from "@/lib/hooks/useCompletionState";
import JsonView from "./JsonView";
import IconDisplay from "./IconDisplay";

export type Prompt = {
name: string;
Expand All @@ -23,6 +24,7 @@ export type Prompt = {
description?: string;
required?: boolean;
}[];
icons?: { src: string; mimeType?: string; sizes?: string[] }[];
};

const PromptsTab = ({
Expand Down Expand Up @@ -109,6 +111,7 @@ const PromptsTab = ({
}}
renderItem={(prompt) => (
<div className="flex flex-col items-start">
<IconDisplay icons={prompt.icons} size="sm" />
<span className="flex-1">{prompt.name}</span>
<span className="text-sm text-gray-500 text-left">
{prompt.description}
Expand Down
45 changes: 45 additions & 0 deletions client/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
RefreshCwOff,
Copy,
CheckCheck,
Server,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
Expand All @@ -40,6 +41,7 @@ import {
import CustomHeaders from "./CustomHeaders";
import { CustomHeaders as CustomHeadersType } from "@/lib/types/customHeaders";
import { useToast } from "../lib/hooks/useToast";
import IconDisplay, { WithIcons } from "./IconDisplay";

interface SidebarProps {
connectionStatus: ConnectionStatus;
Expand Down Expand Up @@ -71,6 +73,9 @@ interface SidebarProps {
setConfig: (config: InspectorConfig) => void;
connectionType: "direct" | "proxy";
setConnectionType: (type: "direct" | "proxy") => void;
serverImplementation?:
| (WithIcons & { name?: string; version?: string; websiteUrl?: string })
| null;
}

const Sidebar = ({
Expand Down Expand Up @@ -102,6 +107,7 @@ const Sidebar = ({
setConfig,
connectionType,
setConnectionType,
serverImplementation,
}: SidebarProps) => {
const [theme, setTheme] = useTheme();
const [showEnvVars, setShowEnvVars] = useState(false);
Expand Down Expand Up @@ -776,6 +782,45 @@ const Sidebar = ({
</span>
</div>

{connectionStatus === "connected" && serverImplementation && (
<div className="bg-gray-50 dark:bg-gray-900 p-3 rounded-lg mb-4">
<div className="flex items-center gap-2 mb-1">
{(serverImplementation as WithIcons).icons &&
(serverImplementation as WithIcons).icons!.length > 0 ? (
<IconDisplay
icons={(serverImplementation as WithIcons).icons}
size="sm"
/>
) : (
<Server className="w-4 h-4 text-gray-500" />
)}
{(serverImplementation as { websiteUrl?: string })
.websiteUrl ? (
<a
href={
(serverImplementation as { websiteUrl?: string })
.websiteUrl
}
target="_blank"
rel="noopener noreferrer"
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"
>
{serverImplementation.name || "MCP Server"}
</a>
) : (
<span className="text-sm font-medium text-gray-800 dark:text-gray-200">
{serverImplementation.name || "MCP Server"}
</span>
)}
</div>
{serverImplementation.version && (
<div className="text-xs text-gray-500 dark:text-gray-400">
Version: {serverImplementation.version}
</div>
)}
</div>
)}

{loggingSupported && connectionStatus === "connected" && (
<div className="space-y-2">
<label
Expand Down
13 changes: 13 additions & 0 deletions client/src/components/ToolsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import JsonView from "./JsonView";
import ToolResults from "./ToolResults";
import { useToast } from "@/lib/hooks/useToast";
import useCopy from "@/lib/hooks/useCopy";
import IconDisplay, { WithIcons } from "./IconDisplay";

// Type guard to safely detect the optional _meta field without using `any`
const hasMeta = (tool: Tool): tool is Tool & { _meta: unknown } =>
Expand Down Expand Up @@ -120,6 +121,7 @@ const ToolsTab = ({
setSelectedItem={setSelectedTool}
renderItem={(tool) => (
<div className="flex flex-col items-start">
<IconDisplay icons={(tool as WithIcons).icons} size="sm" />
<span className="flex-1">{tool.name}</span>
<span className="text-sm text-gray-500 text-left line-clamp-3">
{tool.description}
Expand All @@ -136,6 +138,17 @@ const ToolsTab = ({
<h3 className="font-semibold">
{selectedTool ? selectedTool.name : "Select a tool"}
</h3>
<div className="flex items-center gap-2">
{selectedTool && (
<IconDisplay
icons={(selectedTool as WithIcons).icons}
size="md"
/>
)}
<h3 className="font-semibold">
{selectedTool ? selectedTool.name : "Select a tool"}
</h3>
</div>
</div>
<div className="p-4">
{selectedTool ? (
Expand Down
8 changes: 8 additions & 0 deletions client/src/lib/hooks/useConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
Progress,
LoggingLevel,
ElicitRequestSchema,
Implementation,
} from "@modelcontextprotocol/sdk/types.js";
import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
import { useEffect, useState } from "react";
Expand Down Expand Up @@ -80,6 +81,7 @@ interface UseConnectionOptions {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getRoots?: () => any[];
defaultLoggingLevel?: LoggingLevel;
serverImplementation?: Implementation;
}

export function useConnection({
Expand Down Expand Up @@ -117,6 +119,8 @@ export function useConnection({
const [mcpProtocolVersion, setMcpProtocolVersion] = useState<string | null>(
null,
);
const [serverImplementation, setServerImplementation] =
useState<Implementation | null>(null);

useEffect(() => {
if (!oauthClientId) {
Expand Down Expand Up @@ -684,6 +688,8 @@ export function useConnection({
setClientTransport(transport);

capabilities = client.getServerCapabilities();
const serverInfo = client.getServerVersion();
setServerImplementation(serverInfo || null);
const initializeRequest = {
method: "initialize",
};
Expand Down Expand Up @@ -801,11 +807,13 @@ export function useConnection({

const clearRequestHistory = () => {
setRequestHistory([]);
setServerImplementation(null);
};

return {
connectionStatus,
serverCapabilities,
serverImplementation,
mcpClient,
requestHistory,
clearRequestHistory,
Expand Down