@@ -19,33 +19,98 @@ function serverUrl(port: number): string {
1919 return `http://localhost:${ port } /mcp` ;
2020}
2121
22+ // Cache server connections to avoid reconnecting when switching between servers
23+ const serverInfoCache = new Map < number , Promise < ServerInfo > > ( ) ;
24+
25+ function getServerInfo ( port : number ) : Promise < ServerInfo > {
26+ let promise = serverInfoCache . get ( port ) ;
27+ if ( ! promise ) {
28+ promise = connectToServer ( new URL ( serverUrl ( port ) ) ) ;
29+ // Remove from cache on failure so retry is possible
30+ promise . catch ( ( ) => serverInfoCache . delete ( port ) ) ;
31+ serverInfoCache . set ( port , promise ) ;
32+ }
33+ return promise ;
34+ }
2235
23- interface HostProps {
24- serverInfoPromise : Promise < ServerInfo > ;
36+
37+ // Wrapper to track server name with each tool call
38+ interface ToolCallEntry {
39+ serverName : string ;
40+ info : ToolCallInfo ;
2541}
26- function Host ( { serverInfoPromise } : HostProps ) {
27- const serverInfo = use ( serverInfoPromise ) ;
28- const [ toolCallInfos , setToolCallInfos ] = useState < ToolCallInfo [ ] > ( [ ] ) ;
42+
43+ // Host just manages tool call results - no server dependency
44+ function Host ( ) {
45+ const [ toolCalls , setToolCalls ] = useState < ToolCallEntry [ ] > ( [ ] ) ;
2946
3047 return (
3148 < >
32- { toolCallInfos . map ( ( info , i ) => (
33- < ToolCallInfoPanel key = { i } toolCallInfo = { info } />
49+ { toolCalls . map ( ( entry , i ) => (
50+ < ToolCallInfoPanel key = { i } serverName = { entry . serverName } toolCallInfo = { entry . info } />
3451 ) ) }
3552 < CallToolPanel
36- serverInfo = { serverInfo }
37- addToolCallInfo = { ( info ) => setToolCallInfos ( [ ...toolCallInfos , info ] ) }
53+ addToolCall = { ( serverName , info ) => setToolCalls ( [ ...toolCalls , { serverName, info } ] ) }
3854 />
3955 </ >
4056 ) ;
4157}
4258
4359
60+ // CallToolPanel includes server selection with its own Suspense boundary
4461interface CallToolPanelProps {
45- serverInfo : ServerInfo ;
46- addToolCallInfo : ( toolCallInfo : ToolCallInfo ) => void ;
62+ addToolCall : ( serverName : string , info : ToolCallInfo ) => void ;
63+ }
64+ function CallToolPanel ( { addToolCall } : CallToolPanelProps ) {
65+ const [ selectedServer , setSelectedServer ] = useState ( SERVERS [ 0 ] ) ;
66+ const [ serverInfoPromise , setServerInfoPromise ] = useState (
67+ ( ) => getServerInfo ( selectedServer . port )
68+ ) ;
69+
70+ const handleServerChange = ( port : number ) => {
71+ const server = SERVERS . find ( s => s . port === port ) ?? SERVERS [ 0 ] ;
72+ setSelectedServer ( server ) ;
73+ setServerInfoPromise ( getServerInfo ( port ) ) ;
74+ } ;
75+
76+ return (
77+ < div className = { styles . callToolPanel } >
78+ < label >
79+ Server
80+ < select
81+ value = { selectedServer . port }
82+ onChange = { ( e ) => handleServerChange ( Number ( e . target . value ) ) }
83+ >
84+ { SERVERS . map ( ( { name, port } ) => (
85+ < option key = { port } value = { port } >
86+ { name } (:{ port } )
87+ </ option >
88+ ) ) }
89+ </ select >
90+ </ label >
91+ < ErrorBoundary >
92+ < Suspense fallback = { < p className = { styles . connecting } > Connecting to { serverUrl ( selectedServer . port ) } ...</ p > } >
93+ < ToolCallForm
94+ key = { selectedServer . port }
95+ serverName = { selectedServer . name }
96+ serverInfoPromise = { serverInfoPromise }
97+ addToolCall = { addToolCall }
98+ />
99+ </ Suspense >
100+ </ ErrorBoundary >
101+ </ div >
102+ ) ;
47103}
48- function CallToolPanel ( { serverInfo, addToolCallInfo } : CallToolPanelProps ) {
104+
105+
106+ // ToolCallForm renders inside Suspense - needs serverInfo for tool list
107+ interface ToolCallFormProps {
108+ serverName : string ;
109+ serverInfoPromise : Promise < ServerInfo > ;
110+ addToolCall : ( serverName : string , info : ToolCallInfo ) => void ;
111+ }
112+ function ToolCallForm ( { serverName, serverInfoPromise, addToolCall } : ToolCallFormProps ) {
113+ const serverInfo = use ( serverInfoPromise ) ;
49114 const toolNames = Array . from ( serverInfo . tools . keys ( ) ) ;
50115 const [ selectedTool , setSelectedTool ] = useState ( toolNames [ 0 ] ?? "" ) ;
51116 const [ inputJson , setInputJson ] = useState ( "{}" ) ;
@@ -61,48 +126,47 @@ function CallToolPanel({ serverInfo, addToolCallInfo }: CallToolPanelProps) {
61126
62127 const handleSubmit = ( ) => {
63128 const toolCallInfo = callTool ( serverInfo , selectedTool , JSON . parse ( inputJson ) ) ;
64- addToolCallInfo ( toolCallInfo ) ;
129+ addToolCall ( serverName , toolCallInfo ) ;
65130 } ;
66131
67132 return (
68- < div className = { styles . callToolPanel } >
69- < form onSubmit = { ( e ) => { e . preventDefault ( ) ; handleSubmit ( ) ; } } >
70- < label >
71- Tool Name
72- < select
73- value = { selectedTool }
74- onChange = { ( e ) => setSelectedTool ( e . target . value ) }
75- >
76- { toolNames . map ( ( name ) => (
77- < option key = { name } value = { name } > { name } </ option >
78- ) ) }
79- </ select >
80- </ label >
81- < label >
82- Tool Input
83- < textarea
84- aria-invalid = { ! isValidJson }
85- value = { inputJson }
86- onChange = { ( e ) => setInputJson ( e . target . value ) }
87- />
88- </ label >
89- < button type = "submit" disabled = { ! selectedTool || ! isValidJson } >
90- Call Tool
91- </ button >
92- </ form >
93- </ div >
133+ < form onSubmit = { ( e ) => { e . preventDefault ( ) ; handleSubmit ( ) ; } } >
134+ < label >
135+ Tool
136+ < select
137+ value = { selectedTool }
138+ onChange = { ( e ) => setSelectedTool ( e . target . value ) }
139+ >
140+ { toolNames . map ( ( name ) => (
141+ < option key = { name } value = { name } > { name } </ option >
142+ ) ) }
143+ </ select >
144+ </ label >
145+ < label >
146+ Input
147+ < textarea
148+ aria-invalid = { ! isValidJson }
149+ value = { inputJson }
150+ onChange = { ( e ) => setInputJson ( e . target . value ) }
151+ />
152+ </ label >
153+ < button type = "submit" disabled = { ! selectedTool || ! isValidJson } >
154+ Call Tool
155+ </ button >
156+ </ form >
94157 ) ;
95158}
96159
97160
98161interface ToolCallInfoPanelProps {
162+ serverName : string ;
99163 toolCallInfo : ToolCallInfo ;
100164}
101- function ToolCallInfoPanel ( { toolCallInfo } : ToolCallInfoPanelProps ) {
165+ function ToolCallInfoPanel ( { serverName , toolCallInfo } : ToolCallInfoPanelProps ) {
102166 return (
103167 < div className = { styles . toolCallInfoPanel } >
104168 < div className = { styles . inputInfoPanel } >
105- < h2 className = { styles . toolName } > { toolCallInfo . tool . name } </ h2 >
169+ < h2 className = { styles . toolName } > { serverName } : { toolCallInfo . tool . name } </ h2 >
106170 < JsonBlock value = { toolCallInfo . input } />
107171 </ div >
108172 < div className = { styles . outputInfoPanel } >
@@ -199,55 +263,8 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
199263}
200264
201265
202- interface ServerSelectorProps {
203- selectedPort : number ;
204- onSelect : ( port : number ) => void ;
205- }
206- function ServerSelector ( { selectedPort, onSelect } : ServerSelectorProps ) {
207- return (
208- < div className = { styles . serverSelector } >
209- < label >
210- Server
211- < select
212- value = { selectedPort }
213- onChange = { ( e ) => onSelect ( Number ( e . target . value ) ) }
214- >
215- { SERVERS . map ( ( { name, port } ) => (
216- < option key = { port } value = { port } >
217- { name } (:{ port } )
218- </ option >
219- ) ) }
220- </ select >
221- </ label >
222- </ div >
223- ) ;
224- }
225-
226-
227- function App ( ) {
228- const [ selectedPort , setSelectedPort ] = useState ( SERVERS [ 0 ] . port ) ;
229- const [ serverInfoPromise , setServerInfoPromise ] = useState (
230- ( ) => connectToServer ( new URL ( serverUrl ( selectedPort ) ) )
231- ) ;
232-
233- const handleServerChange = ( port : number ) => {
234- setSelectedPort ( port ) ;
235- setServerInfoPromise ( connectToServer ( new URL ( serverUrl ( port ) ) ) ) ;
236- } ;
237-
238- return (
239- < >
240- < ServerSelector selectedPort = { selectedPort } onSelect = { handleServerChange } />
241- < Suspense fallback = { < p className = { styles . connecting } > Connecting to { serverUrl ( selectedPort ) } ...</ p > } >
242- < Host key = { selectedPort } serverInfoPromise = { serverInfoPromise } />
243- </ Suspense >
244- </ >
245- ) ;
246- }
247-
248-
249266createRoot ( document . getElementById ( "root" ) ! ) . render (
250267 < StrictMode >
251- < App />
268+ < Host />
252269 </ StrictMode > ,
253270) ;
0 commit comments