{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "smithery-server-search", "title": "Smithery Server Search", "description": "A searchable combobox for finding and connecting to MCP servers from the Smithery registry.", "dependencies": ["@smithery/api", "@tanstack/react-query", "lucide-react"], "registryDependencies": ["avatar", "button", "combobox", "item"], "files": [ { "path": "registry/new-york/hooks/use-debounce.ts", "content": "import { useEffect, useState } from \"react\";\n\nexport function useDebounce(value: T, delay: number): T {\n\tconst [debouncedValue, setDebouncedValue] = useState(value);\n\n\tuseEffect(() => {\n\t\tconst handler = setTimeout(() => {\n\t\t\tsetDebouncedValue(value);\n\t\t}, delay);\n\n\t\treturn () => {\n\t\t\tclearTimeout(handler);\n\t\t};\n\t}, [value, delay]);\n\n\treturn debouncedValue;\n}\n", "type": "registry:hook", "target": "hooks/use-debounce.ts" }, { "path": "registry/new-york/smithery/query-client-wrapper.tsx", "content": "\"use client\";\n\nimport {\n\tQueryClient,\n\tQueryClientContext,\n\tQueryClientProvider,\n} from \"@tanstack/react-query\";\nimport { type ReactNode, useContext, useState } from \"react\";\n\n// Shared default QueryClient for all Smithery components\nlet sharedQueryClient: QueryClient | null = null;\n\nfunction getSharedQueryClient() {\n\tif (!sharedQueryClient) {\n\t\tsharedQueryClient = new QueryClient({\n\t\t\tdefaultOptions: {\n\t\t\t\tqueries: { staleTime: 60 * 1000 },\n\t\t\t},\n\t\t});\n\t}\n\treturn sharedQueryClient;\n}\n\n/**\n * Wrapper that auto-provides QueryClient if none exists.\n * - If a QueryClientProvider already exists, uses that client (user's takes precedence)\n * - If no provider exists, creates a shared client for Smithery components\n */\nexport function WithQueryClient({ children }: { children: ReactNode }) {\n\t// Check if we're already inside a QueryClientProvider\n\tconst existingClient = useContext(QueryClientContext);\n\n\t// Create our own client only if needed (lazy init, stable across renders)\n\tconst [fallbackClient] = useState(() => getSharedQueryClient());\n\n\t// If there's already a client, just render children\n\tif (existingClient) {\n\t\treturn <>{children};\n\t}\n\n\t// Otherwise, provide our own client\n\treturn (\n\t\t\n\t\t\t{children}\n\t\t\n\t);\n}\n", "type": "registry:component", "target": "components/smithery/query-client-wrapper.tsx" }, { "path": "registry/new-york/smithery/server-search.tsx", "content": "\"use client\";\n\nimport Smithery, { AuthenticationError } from \"@smithery/api\";\nimport type { Connection } from \"@smithery/api/resources/experimental/connect/connections.mjs\";\nimport type { ServerListResponse } from \"@smithery/api/resources/servers/servers.mjs\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport {\n\tAlertTriangle,\n\tArrowRight,\n\tCheckCircle,\n\tLink,\n\tLoader2,\n\tLock,\n} from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n\tCombobox,\n\tComboboxContent,\n\tComboboxEmpty,\n\tComboboxInput,\n\tComboboxItem,\n\tComboboxList,\n} from \"@/components/ui/combobox\";\nimport {\n\tItem,\n\tItemContent,\n\tItemDescription,\n\tItemMedia,\n\tItemTitle,\n} from \"@/components/ui/item\";\nimport { useDebounce } from \"@/hooks/use-debounce\";\nimport { WithQueryClient } from \"@/registry/new-york/smithery/query-client-wrapper\";\n\ninterface AuthRequiredBannerProps {\n\tserverName: string;\n\tauthorizationUrl?: string;\n\tcountdown: number | null;\n}\n\nconst AuthRequiredBanner = ({\n\tserverName,\n\tauthorizationUrl,\n\tcountdown,\n}: AuthRequiredBannerProps) => (\n\t
\n\t\t
\n\t\t\t

\n\t\t\t\t{\" \"}\n\t\t\t\tAuthorization required\n\t\t\t

\n\t\t\t

\n\t\t\t\tThis server requires you to authorize access. You should be\n\t\t\t\tautomatically redirected to complete authentication.{\" \"}\n\t\t\t\t{countdown !== null && countdown > 0\n\t\t\t\t\t? `Redirecting in ${countdown}...`\n\t\t\t\t\t: \"If not, click the link below.\"}\n\t\t\t

\n\t\t\t{authorizationUrl && (\n\t\t\t\t\n\t\t\t\t\tSign in to {serverName}{\" \"}\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t)}\n\t\t
\n\t
\n);\n\ninterface ExistingConnectionWarningBannerProps {\n\tserverName: string;\n\tonUseExisting: () => void;\n\tonCreateNew: () => void;\n\tisConnecting: boolean;\n}\n\nconst ExistingConnectionWarningBanner = ({\n\tserverName,\n\tonUseExisting,\n\tonCreateNew,\n\tisConnecting,\n}: ExistingConnectionWarningBannerProps) => (\n\t
\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t

\n\t\t\t\t\tConnection already exists\n\t\t\t\t

\n\t\t\t\t

\n\t\t\t\t\tA connection to {serverName} already exists. Would you like to use the\n\t\t\t\t\texisting connection or create a new one?\n\t\t\t\t

\n\t\t\t
\n\t\t
\n\t\t
\n\t\t\t\n\t\t\t\t{isConnecting ? (\n\t\t\t\t\t\n\t\t\t\t) : (\n\t\t\t\t\t\"Use existing\"\n\t\t\t\t)}\n\t\t\t\n\t\t\t\n\t\t\t\t{isConnecting ? (\n\t\t\t\t\t\n\t\t\t\t) : (\n\t\t\t\t\t\"Create new\"\n\t\t\t\t)}\n\t\t\t\n\t\t
\n\t
\n);\n\ninterface ConnectionButtonProps {\n\tconnectionStatus?:\n\t\t| \"connected\"\n\t\t| \"auth_required\"\n\t\t| \"existing_connection_warning\"\n\t\t| \"error\";\n\tisConnecting: boolean;\n\tonConnect: () => void;\n}\n\nconst ConnectionButton = ({\n\tconnectionStatus,\n\tisConnecting,\n\tonConnect,\n}: ConnectionButtonProps) => {\n\tswitch (connectionStatus) {\n\t\tcase \"connected\":\n\t\t\treturn (\n\t\t\t\t\n\t\t\t);\n\t\tcase \"auth_required\":\n\t\t\treturn (\n\t\t\t\t\n\t\t\t);\n\t\tcase \"existing_connection_warning\":\n\t\t\t// Buttons are rendered separately in ExistingConnectionWarningBanner\n\t\t\treturn null;\n\t\tdefault:\n\t\t\treturn (\n\t\t\t\t\n\t\t\t\t\t{isConnecting ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tConnecting...\n\t\t\t\t\t\t\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\tConnect \n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t\n\t\t\t);\n\t}\n};\n\ntype OnExistingConnectionMode = \"warn\" | \"error\" | \"use\" | \"create-new\";\n\ninterface ServerDisplayProps {\n\tserver: ServerListResponse;\n\ttoken: string;\n\tnamespace?: string;\n\tonExistingConnection: OnExistingConnectionMode;\n}\n\nconst ServerDisplay = ({\n\tserver,\n\ttoken,\n\tnamespace,\n\tonExistingConnection,\n}: ServerDisplayProps) => {\n\tconst queryClient = useQueryClient();\n\tconst [countdown, setCountdown] = useState(null);\n\n\tconst { data: activeNamespace } = useQuery({\n\t\tqueryKey: [\"defaultNamespace\", token],\n\t\tqueryFn: async () => {\n\t\t\tif (namespace) {\n\t\t\t\treturn namespace;\n\t\t\t}\n\t\t\tconst client = getSmitheryClient(token);\n\t\t\treturn await getDefaultNamespace(client);\n\t\t},\n\t\tenabled: !!token,\n\t});\n\n\tconst serverUrl =\n\t\tserver.qualifiedName.startsWith(\"http://\") ||\n\t\tserver.qualifiedName.startsWith(\"https://\")\n\t\t\t? server.qualifiedName\n\t\t\t: `https://server.smithery.ai/${server.qualifiedName}/mcp`;\n\tconst serverName = server.displayName || server.qualifiedName;\n\n\tconst {\n\t\tmutate: connect,\n\t\tisPending: isConnecting,\n\t\tdata: connectionData,\n\t\tmutateAsync: connectAsync,\n\t} = useMutation({\n\t\tmutationFn: async (\n\t\t\toverrideMode?: \"use\" | \"create-new\",\n\t\t): Promise => {\n\t\t\tif (!token || !activeNamespace) {\n\t\t\t\tthrow new Error(\"Token and namespace are required\");\n\t\t\t}\n\t\t\tconst client = getSmitheryClient(token);\n\t\t\treturn await createConnection(\n\t\t\t\tclient,\n\t\t\t\tactiveNamespace,\n\t\t\t\tserverUrl,\n\t\t\t\tserverName,\n\t\t\t\toverrideMode ?? onExistingConnection,\n\t\t\t);\n\t\t},\n\t\tonSuccess: () => {\n\t\t\tqueryClient.invalidateQueries({ queryKey: [\"connections\"] });\n\t\t},\n\t\tonError: (error) => {\n\t\t\tconsole.error(\n\t\t\t\t\"error connecting to server\",\n\t\t\t\t`server: ${server.qualifiedName}, namespace: ${activeNamespace}`,\n\t\t\t\terror,\n\t\t\t);\n\t\t},\n\t});\n\n\tconst handleUseExisting = () => {\n\t\tconnect(\"use\");\n\t};\n\n\tconst handleCreateNew = () => {\n\t\tconnect(\"create-new\");\n\t};\n\n\t// Countdown timer for auth_required state\n\tuseEffect(() => {\n\t\tif (connectionData?.status === \"auth_required\" && countdown === null) {\n\t\t\tsetCountdown(3);\n\t\t}\n\t}, [connectionData?.status, countdown]);\n\n\tuseEffect(() => {\n\t\tif (countdown === null || countdown <= 0) return;\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tsetCountdown(countdown - 1);\n\t\t}, 1000);\n\n\t\treturn () => clearTimeout(timer);\n\t}, [countdown]);\n\n\t// Poll connection status when auth_required\n\tconst authConnectionId =\n\t\tconnectionData?.status === \"auth_required\"\n\t\t\t? connectionData.connectionId\n\t\t\t: null;\n\n\tuseEffect(() => {\n\t\tif (!authConnectionId || !token || !activeNamespace) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst pollInterval = setInterval(async () => {\n\t\t\ttry {\n\t\t\t\tconst client = getSmitheryClient(token);\n\t\t\t\tconst status = await checkConnectionStatus(\n\t\t\t\t\tclient,\n\t\t\t\t\tauthConnectionId,\n\t\t\t\t\tactiveNamespace,\n\t\t\t\t);\n\n\t\t\t\tif (status.status === \"connected\") {\n\t\t\t\t\t// Update mutation data to trigger re-render\n\t\t\t\t\t// Use \"use\" mode to reuse the existing connection we're polling\n\t\t\t\t\tawait connectAsync(\"use\");\n\t\t\t\t\tsetCountdown(null);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"error checking connection status\", error);\n\t\t\t}\n\t\t}, 2000); // Check every 2 seconds\n\n\t\treturn () => clearInterval(pollInterval);\n\t}, [authConnectionId, token, activeNamespace, connectAsync]);\n\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{server.displayName?.charAt(0) ||\n\t\t\t\t\t\t\tserver.qualifiedName?.charAt(0) ||\n\t\t\t\t\t\t\t\"S\"}\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t

\n\t\t\t\t\t\t{server.displayName || server.qualifiedName}\n\t\t\t\t\t\t{server.verified && (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t)}\n\t\t\t\t\t

\n\t\t\t\t\t

\n\t\t\t\t\t\t{server.qualifiedName}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t{server.description &&

{server.description}

}\n\t\t\t
\n\n\t\t\t{connectionData?.status === \"auth_required\" && (\n\t\t\t\t\n\t\t\t)}\n\n\t\t\t
\n\t\t\t\t {\n\t\t\t\t\t\twindow.open(server.homepage, \"_blank\");\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\tView Details\n\t\t\t\t\n\t\t\t\t connect(undefined)}\n\t\t\t\t/>\n\t\t\t
\n\n\t\t\t{connectionData?.status === \"existing_connection_warning\" && (\n\t\t\t\t\n\t\t\t)}\n\n\t\t\t{connectionData?.status === \"error\" && (\n\t\t\t\t

\n\t\t\t\t\tError connecting to server\n\t\t\t\t

\n\t\t\t)}\n\t\t
\n\t);\n};\n\nasync function getDefaultNamespace(client: Smithery) {\n\tconst namespaces = await client.namespaces.list();\n\tif (namespaces.namespaces.length === 0) {\n\t\tthrow new Error(\"No namespaces found\");\n\t}\n\treturn namespaces.namespaces[0].name;\n}\n\nconst getSmitheryClient = (token: string) => {\n\treturn new Smithery({\n\t\tapiKey: token,\n\t\tbaseURL: process.env.NEXT_PUBLIC_SMITHERY_API_URL,\n\t});\n};\n\ntype ConnectionStatus =\n\t| { status: \"connected\"; connection: Connection }\n\t| { status: \"auth_required\"; connectionId: string; authorizationUrl?: string }\n\t| {\n\t\t\tstatus: \"existing_connection_warning\";\n\t\t\texistingConnectionId: string;\n\t\t\tname: string;\n\t }\n\t| { status: \"error\"; error: unknown };\n\nasync function checkConnectionStatus(\n\tclient: Smithery,\n\tconnectionId: string,\n\tnamespace: string,\n): Promise {\n\ttry {\n\t\tconst jsonRpcResponse = await client.experimental.connect.mcp.call(connectionId, {\n\t\t\tnamespace,\n\t\t\tjsonrpc: \"2.0\",\n\t\t\tmethod: \"tools/list\",\n\t\t});\n\t\tconsole.log(\"jsonRpcResponse\", jsonRpcResponse);\n\n\t\tconst connection = await client.experimental.connect.connections.get(connectionId, {\n\t\t\tnamespace: namespace,\n\t\t});\n\n\t\treturn {\n\t\t\tstatus: \"connected\",\n\t\t\tconnection,\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof AuthenticationError) {\n\t\t\tconst errorData = error.error as unknown as {\n\t\t\t\terror?: { data?: { authorizationUrl?: string } };\n\t\t\t\tdata?: { authorizationUrl?: string };\n\t\t\t};\n\t\t\tconst authorizationUrl =\n\t\t\t\terrorData?.error?.data?.authorizationUrl ||\n\t\t\t\terrorData?.data?.authorizationUrl;\n\t\t\treturn {\n\t\t\t\tstatus: \"auth_required\",\n\t\t\t\tconnectionId,\n\t\t\t\tauthorizationUrl,\n\t\t\t};\n\t\t}\n\t\tconsole.error(\n\t\t\t\"error connecting to server\",\n\t\t\t`connectionId: ${connectionId}, namespace: ${namespace}`,\n\t\t\terror,\n\t\t);\n\t\treturn {\n\t\t\tstatus: \"error\",\n\t\t\terror,\n\t\t};\n\t}\n}\n\nasync function createConnection(\n\tclient: Smithery,\n\tnamespace: string,\n\tmcpUrl: string,\n\tname: string,\n\tonExistingConnection: OnExistingConnectionMode,\n): Promise {\n\t// Check for existing connection by name\n\tconst existingList = await client.experimental.connect.connections.list(namespace, {\n\t\tname,\n\t});\n\tconst existing = existingList.connections[0];\n\n\tif (existing) {\n\t\tswitch (onExistingConnection) {\n\t\t\tcase \"error\":\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\terror: new Error(`Connection \"${name}\" already exists`),\n\t\t\t\t};\n\t\t\tcase \"warn\":\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"existing_connection_warning\",\n\t\t\t\t\texistingConnectionId: existing.connectionId,\n\t\t\t\t\tname,\n\t\t\t\t};\n\t\t\tcase \"use\":\n\t\t\t\treturn checkConnectionStatus(client, existing.connectionId, namespace);\n\t\t\tcase \"create-new\":\n\t\t\t\t// Continue to create new connection\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Create new connection (API auto-generates unique ID)\n\tconsole.log(\"creating connection\", name);\n\tconst connection = await client.experimental.connect.connections.create(namespace, {\n\t\tmcpUrl,\n\t\tname,\n\t});\n\tconsole.log(\"connection\", connection);\n\n\tif (connection.status?.state === \"auth_required\") {\n\t\treturn {\n\t\t\tstatus: \"auth_required\",\n\t\t\tconnectionId: connection.connectionId,\n\t\t\tauthorizationUrl: connection.status?.authorizationUrl,\n\t\t};\n\t}\n\n\treturn {\n\t\tstatus: \"connected\",\n\t\tconnection,\n\t};\n}\n\n// Helper function to detect if input is a URL\nconst isValidUrl = (str: string): boolean => {\n\tif (!str.trim()) return false;\n\ttry {\n\t\tconst url = new URL(str.trim());\n\t\treturn url.protocol === \"http:\" || url.protocol === \"https:\";\n\t} catch {\n\t\treturn false;\n\t}\n};\n\ninterface ExternalURLDisplayProps {\n\turl: string;\n\ttoken: string;\n\tnamespace?: string;\n\tonExistingConnection: OnExistingConnectionMode;\n}\n\nconst ExternalURLDisplay = ({\n\turl,\n\ttoken,\n\tnamespace,\n\tonExistingConnection,\n}: ExternalURLDisplayProps) => {\n\tconst queryClient = useQueryClient();\n\tconst [countdown, setCountdown] = useState(null);\n\n\tconst { data: defaultNamespace } = useQuery({\n\t\tqueryKey: [\"defaultNamespace\", token],\n\t\tqueryFn: async () => {\n\t\t\tconst client = getSmitheryClient(token);\n\t\t\treturn await getDefaultNamespace(client);\n\t\t},\n\t\tenabled: !!token,\n\t});\n\n\tconst activeNamespace = namespace || defaultNamespace;\n\n\tconst {\n\t\tmutate: connect,\n\t\tisPending: isConnecting,\n\t\tdata: connectionData,\n\t\tmutateAsync: connectAsync,\n\t} = useMutation({\n\t\tmutationFn: async (\n\t\t\toverrideMode?: \"use\" | \"create-new\",\n\t\t): Promise => {\n\t\t\tif (!token || !activeNamespace) {\n\t\t\t\tthrow new Error(\"Token and namespace are required\");\n\t\t\t}\n\t\t\tconst client = getSmitheryClient(token);\n\t\t\treturn await createConnection(\n\t\t\t\tclient,\n\t\t\t\tactiveNamespace,\n\t\t\t\turl,\n\t\t\t\turl,\n\t\t\t\toverrideMode ?? onExistingConnection,\n\t\t\t);\n\t\t},\n\t\tonSuccess: () => {\n\t\t\tqueryClient.invalidateQueries({ queryKey: [\"connections\"] });\n\t\t},\n\t\tonError: (error) => {\n\t\t\tconsole.error(\"error connecting to external URL\", error);\n\t\t},\n\t});\n\n\tconst handleUseExisting = () => {\n\t\tconnect(\"use\");\n\t};\n\n\tconst handleCreateNew = () => {\n\t\tconnect(\"create-new\");\n\t};\n\n\t// Countdown timer for auth_required state\n\tuseEffect(() => {\n\t\tif (connectionData?.status === \"auth_required\" && countdown === null) {\n\t\t\tsetCountdown(3);\n\t\t}\n\t}, [connectionData?.status, countdown]);\n\n\tuseEffect(() => {\n\t\tif (countdown === null || countdown <= 0) return;\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tsetCountdown(countdown - 1);\n\t\t}, 1000);\n\n\t\treturn () => clearTimeout(timer);\n\t}, [countdown]);\n\n\t// Auto-redirect when auth is required\n\tuseEffect(() => {\n\t\tif (\n\t\t\tconnectionData?.status === \"auth_required\" &&\n\t\t\tconnectionData.authorizationUrl &&\n\t\t\tcountdown === 0\n\t\t) {\n\t\t\twindow.open(connectionData.authorizationUrl, \"_blank\");\n\t\t}\n\t}, [connectionData, countdown]);\n\n\t// Poll connection status when auth_required\n\tconst authConnectionId =\n\t\tconnectionData?.status === \"auth_required\"\n\t\t\t? connectionData.connectionId\n\t\t\t: null;\n\n\tuseEffect(() => {\n\t\tif (!authConnectionId || !token || !activeNamespace) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst pollInterval = setInterval(async () => {\n\t\t\ttry {\n\t\t\t\tconst client = getSmitheryClient(token);\n\t\t\t\tconst status = await checkConnectionStatus(\n\t\t\t\t\tclient,\n\t\t\t\t\tauthConnectionId,\n\t\t\t\t\tactiveNamespace,\n\t\t\t\t);\n\n\t\t\t\tif (status.status === \"connected\") {\n\t\t\t\t\t// Update mutation data to trigger re-render\n\t\t\t\t\t// Use \"use\" mode to reuse the existing connection we're polling\n\t\t\t\t\tawait connectAsync(\"use\");\n\t\t\t\t\tsetCountdown(null);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"error checking connection status\", error);\n\t\t\t}\n\t\t}, 2000); // Check every 2 seconds\n\n\t\treturn () => clearInterval(pollInterval);\n\t}, [authConnectionId, token, activeNamespace, connectAsync]);\n\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t

External MCP Server

\n\t\t\t\t\t

{url}

\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t{connectionData?.status === \"auth_required\" && (\n\t\t\t\t\n\t\t\t)}\n\n\t\t\t
\n\t\t\t\t connect(undefined)}\n\t\t\t\t/>\n\t\t\t
\n\n\t\t\t{connectionData?.status === \"existing_connection_warning\" && (\n\t\t\t\t\n\t\t\t)}\n\n\t\t\t{connectionData?.status === \"error\" && (\n\t\t\t\t

\n\t\t\t\t\tError connecting to server\n\t\t\t\t

\n\t\t\t)}\n\t\t
\n\t);\n};\n\nconst ServerSearchInner = ({\n\ttoken,\n\tnamespace,\n\tonExistingConnection = \"warn\",\n}: {\n\ttoken?: string;\n\tnamespace?: string;\n\tonExistingConnection?: OnExistingConnectionMode;\n}) => {\n\tconst [query, setQuery] = useState(\"\");\n\tconst [selectedServer, setSelectedServer] =\n\t\tuseState(null);\n\tconst [selectedExternalUrl, setSelectedExternalUrl] = useState(\n\t\tnull,\n\t);\n\tconst debouncedQuery = useDebounce(query, 300);\n\tconst isUrl = isValidUrl(query);\n\n\tconst { data, isLoading } = useQuery({\n\t\tqueryKey: [\"servers\", token, debouncedQuery],\n\t\tqueryFn: async () => {\n\t\t\tif (!token) {\n\t\t\t\tthrow new Error(\"API token is required\");\n\t\t\t}\n\t\t\tconst client = getSmitheryClient(token);\n\t\t\tconsole.log(\"searching\", debouncedQuery);\n\t\t\tconst servers = debouncedQuery\n\t\t\t\t? await client.servers.list({ q: debouncedQuery, pageSize: 3 })\n\t\t\t\t: await client.servers.list();\n\t\t\tconsole.log(`servers for ${debouncedQuery}`, servers);\n\t\t\treturn servers;\n\t\t},\n\t\tenabled: !!token,\n\t});\n\n\tconst servers = data?.servers ?? [];\n\n\tconst handleKeyDown = (e: React.KeyboardEvent) => {\n\t\tif (e.key === \"Enter\") {\n\t\t\te.preventDefault();\n\t\t\tif (isUrl) {\n\t\t\t\tconst urlToConnect = query.trim();\n\t\t\t\tsetSelectedExternalUrl(urlToConnect);\n\t\t\t\tsetSelectedServer(null);\n\t\t\t\tsetTimeout(() => setQuery(\"\"), 0);\n\t\t\t} else if (servers.length > 0) {\n\t\t\t\tsetSelectedServer(servers[0]);\n\t\t\t\tsetSelectedExternalUrl(null);\n\t\t\t\tsetTimeout(() => setQuery(\"\"), 0);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t\tvalue={selectedServer}\n\t\t\t\tonValueChange={(server) => {\n\t\t\t\t\tsetSelectedServer(server);\n\t\t\t\t\t// Only clear external URL when a server is actually selected\n\t\t\t\t\t// (not when combobox fires null from closing without selection)\n\t\t\t\t\tif (server) {\n\t\t\t\t\t\tsetSelectedExternalUrl(null);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\tonInputValueChange={(value) => setQuery(value)}\n\t\t\t\titemToStringLabel={(server) =>\n\t\t\t\t\tserver.displayName || server.qualifiedName\n\t\t\t\t}\n\t\t\t\tdefaultOpen={true}\n\t\t\t>\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t{isUrl ? (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\tconst urlToConnect = query.trim();\n\t\t\t\t\t\t\t\t\tsetSelectedExternalUrl(urlToConnect);\n\t\t\t\t\t\t\t\t\tsetSelectedServer(null);\n\t\t\t\t\t\t\t\t\tsetTimeout(() => setQuery(\"\"), 0);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\tConnect to external MCP URL\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{query.trim()}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t{servers.length === 0 && (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{isLoading ? \"Loading...\" : \"No servers found.\"}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{servers.map((server) => (\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{server.displayName?.charAt(0) ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tserver.qualifiedName?.charAt(0) ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"S\"}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{server.displayName || server.qualifiedName}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{server.description || server.qualifiedName}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t\n\n\t\t\t{selectedServer && token && namespace && (\n\t\t\t\t\n\t\t\t)}\n\n\t\t\t{selectedExternalUrl && token && (\n\t\t\t\t\n\t\t\t)}\n\t\t
\n\t);\n};\n\nexport const ServerSearch = (props: {\n\ttoken?: string;\n\tnamespace?: string;\n\tonExistingConnection?: OnExistingConnectionMode;\n}) => (\n\t\n\t\t\n\t\n);\n", "type": "registry:component", "target": "components/smithery/server-search.tsx" } ], "type": "registry:block" }