import React, { useRef } from "react"; import { Box, Text, useInput, type Key } from "ink"; import { ScrollView, type ScrollViewRef } from "ink-scroll-view"; import type { MCPServerConfig, ServerState, } from "@inspector/core/mcp/index.js"; import type { InspectorServerSettings } from "@inspector/core/mcp/types.js"; interface InfoTabProps { serverName: string | null; serverConfig: MCPServerConfig | null; // HTTP headers live on the per-server settings (pair-array form), not on the // transport `serverConfig` — `mcpConfigToServerEntries` lifts the on-disk // `headers` map into `InspectorServerSettings.headers`. Read them here so the // SSE / streamable-http header display reflects the real source. serverSettings?: InspectorServerSettings | null; serverState: ServerState | null; width: number; height: number; focused?: boolean; } export function InfoTab({ serverName, serverConfig, serverSettings = null, serverState, width, height, focused = false, }: InfoTabProps) { const headerPairs = serverSettings?.headers ?? []; // Shared header display for the sse / streamable-http branches (identical for // both transports — the header source is `serverSettings`, not the transport). const headersBlock = headerPairs.length > 0 ? ( Headers:{" "} {headerPairs.map(({ key, value }) => `${key}=${value}`).join(", ")} ) : null; const scrollViewRef = useRef(null); // Handle keyboard input for scrolling useInput( (input: string, key: Key) => { if (focused) { if (key.upArrow) { scrollViewRef.current?.scrollBy(-1); } else if (key.downArrow) { scrollViewRef.current?.scrollBy(1); } else if (key.pageUp) { const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1; scrollViewRef.current?.scrollBy(-viewportHeight); } else if (key.pageDown) { const viewportHeight = scrollViewRef.current?.getViewportHeight() || 1; scrollViewRef.current?.scrollBy(viewportHeight); } } }, { isActive: focused }, ); return ( Info {serverName ? ( <> {/* Scrollable content area - takes remaining space */} {/* Server Configuration */} Server Configuration {serverConfig ? ( {serverConfig.type === undefined || serverConfig.type === "stdio" ? ( <> Type: stdio Command: {serverConfig.command} {serverConfig.args && serverConfig.args.length > 0 && ( Args: {serverConfig.args.map((arg: string, idx: number) => ( {arg} ))} )} {serverConfig.env && Object.keys(serverConfig.env).length > 0 && ( Env:{" "} {Object.entries(serverConfig.env) .map(([k, v]) => `${k}=${v}`) .join(", ")} )} {serverConfig.cwd && ( CWD: {serverConfig.cwd} )} ) : serverConfig.type === "sse" ? ( <> Type: sse URL: {serverConfig.url} {headersBlock} ) : serverConfig.type === "streamable-http" ? ( <> Type: streamable-http URL: {serverConfig.url} {headersBlock} ) : null} ) : ( No configuration available )} {/* Server Info */} {serverState && serverState.status === "connected" && serverState.serverInfo && ( <> Server Information {serverState.serverInfo.name && ( Name: {serverState.serverInfo.name} )} {serverState.serverInfo.version && ( Version: {serverState.serverInfo.version} )} {serverState.instructions && ( Instructions: {serverState.instructions} )} )} {serverState && serverState.status === "error" && ( Error {serverState.error && ( {serverState.error} )} )} {serverState && serverState.status === "disconnected" && ( Server not connected )} {/* Fixed keyboard help footer at bottom - only show when focused */} {focused && ( ↑/↓ to scroll, + to zoom )} ) : null} ); }