@@ -58,7 +58,7 @@ export function McpServers({
5858 clearStorage : ( ) => { } ,
5959 } )
6060 const [ toolForms , setToolForms ] = useState < Record < string , Record < string , any > > > ( { } )
61- const [ executionLog , setExecutionLog ] = useState < string > ( '' )
61+ const [ toolExecutionLogs , setToolExecutionLogs ] = useState < Record < string , string > > ( { } )
6262 const logRef = useRef < HTMLDivElement > ( null )
6363 const executionLogRef = useRef < HTMLTextAreaElement > ( null )
6464
@@ -218,27 +218,32 @@ export function McpServers({
218218
219219 // Add execution start message
220220 const startMessage = `Calling ${ tool . name } (${ argsStr } )\n`
221- setExecutionLog ( prev => prev + startMessage )
221+ setToolExecutionLogs ( prev => ( {
222+ ...prev ,
223+ [ tool . name ] : ( prev [ tool . name ] || '' ) + startMessage
224+ } ) )
222225
223226 try {
224227 const result = await connectionData . callTool ( tool . name , args )
225228 const resultStr = typeof result === 'string' ? result : JSON . stringify ( result , null , 2 )
226- setExecutionLog ( prev => prev + `${ resultStr } \n\n` )
229+ setToolExecutionLogs ( prev => ( {
230+ ...prev ,
231+ [ tool . name ] : ( prev [ tool . name ] || '' ) + `${ resultStr } \n\n`
232+ } ) )
227233 } catch ( error ) {
228- setExecutionLog ( prev => prev + `Error: ${ error } \n\n` )
234+ setToolExecutionLogs ( prev => ( {
235+ ...prev ,
236+ [ tool . name ] : ( prev [ tool . name ] || '' ) + `Error: ${ error } \n\n`
237+ } ) )
229238 }
230-
231- // Auto-scroll to bottom
232- setTimeout ( ( ) => {
233- if ( executionLogRef . current ) {
234- executionLogRef . current . scrollTop = executionLogRef . current . scrollHeight
235- }
236- } , 0 )
237239 }
238240
239- // Clear execution log
240- const clearExecutionLog = ( ) => {
241- setExecutionLog ( '' )
241+ // Clear execution log for specific tool
242+ const clearExecutionLog = ( toolName : string ) => {
243+ setToolExecutionLogs ( prev => ( {
244+ ...prev ,
245+ [ toolName ] : ''
246+ } ) )
242247 }
243248
244249 // Render form field based on schema
@@ -249,13 +254,15 @@ export function McpServers({
249254 return (
250255 < input
251256 type = "number"
252- className = "w-1/4 p-2 border border-gray-200 rounded text-sm focus:outline-none focus:ring-1 focus:ring-blue-300"
257+ className = "w-1/4 p-2 pr-6 mr-3 border border-gray-200 rounded text-sm focus:outline-none focus:ring-1 focus:ring-blue-300 placeholder-gray -300"
253258 value = { value }
254259 step = { schema . type === 'integer' ? 1 : 'any' }
255260 required = { isRequired }
256- onChange = { ( e ) => handleFormChange ( toolName , fieldName ,
257- schema . type === 'integer' ? parseInt ( e . target . value ) || 0 : parseFloat ( e . target . value ) || 0
258- ) }
261+ onChange = { ( e ) => {
262+ const newValue = e . target . value === '' ? '' :
263+ ( schema . type === 'integer' ? parseInt ( e . target . value ) || 0 : parseFloat ( e . target . value ) || 0 )
264+ handleFormChange ( toolName , fieldName , newValue )
265+ } }
259266 />
260267 )
261268 } else if ( schema . type === 'boolean' ) {
@@ -272,7 +279,7 @@ export function McpServers({
272279 return (
273280 < input
274281 type = "text"
275- className = "w-full p-2 border border-gray-200 rounded text-sm focus:outline-none focus:ring-1 focus:ring-blue-300"
282+ className = "w-full p-2 border border-gray-200 rounded text-sm focus:outline-none focus:ring-1 focus:ring-blue-300 placeholder-gray-300 "
276283 value = { value }
277284 required = { isRequired }
278285 placeholder = { schema . description || '' }
@@ -357,84 +364,81 @@ export function McpServers({
357364 No tools available. Connect to an MCP server to see available tools.
358365 </ div >
359366 ) : (
360- < div className = "border border-gray-200 rounded p-4 bg-gray-50 space-y-6" >
367+ < div className = "space-y-6" >
361368 { tools . map ( ( tool : Tool , index : number ) => (
362- < div
363- key = { index }
364- className = "bg-white p-4 rounded border border-gray-100 shadow-sm"
365- >
366- < div className = "font-mono font-medium text-sm text-blue-700 mb-2" >
369+ < div key = { index } >
370+ { /* Tool title outside the card */ }
371+ < h4 className = "font-bold text-base text-black mb-2" >
367372 { tool . name }
368- </ div >
369- { tool . description && (
370- < p className = "text-gray-600 mb-4 text-sm leading-relaxed" >
371- { tool . description }
372- </ p >
373- ) }
373+ </ h4 >
374374
375- { /* Form for tool parameters */ }
376- { tool . inputSchema && tool . inputSchema . properties && (
377- < div className = "space-y-3 mb-4" >
378- { Object . entries ( tool . inputSchema . properties ) . map ( ( [ fieldName , schema ] : [ string , any ] ) => {
379- const isRequired = tool . inputSchema . required ?. includes ( fieldName ) || false
380- return (
381- < div key = { fieldName } className = "space-y-1" >
382- < div className = "flex items-center gap-2" >
383- < label className = "text-xs font-medium text-gray-700" >
384- { fieldName }
385- { isRequired && < span className = "text-red-500" > *</ span > }
386- </ label >
387- { schema . description && (
388- < div className = "relative group" >
389- < Info size = { 12 } className = "text-gray-400 cursor-help" />
390- < div className = "absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap z-10" >
391- { schema . description }
375+ < div className = "bg-white p-4 rounded border border-gray-100 shadow-sm" >
376+ { tool . description && (
377+ < p className = "text-gray-600 mb-4 text-sm leading-relaxed" >
378+ { tool . description }
379+ </ p >
380+ ) }
381+
382+ { /* Form for tool parameters */ }
383+ { tool . inputSchema && tool . inputSchema . properties && (
384+ < div className = "space-y-3 mb-4" >
385+ { Object . entries ( tool . inputSchema . properties ) . map ( ( [ fieldName , schema ] : [ string , any ] ) => {
386+ const isRequired = tool . inputSchema . required ?. includes ( fieldName ) || false
387+ return (
388+ < div key = { fieldName } className = "space-y-1" >
389+ < div className = "flex items-center gap-2" >
390+ < label className = "text-xs font-medium text-gray-700" >
391+ { fieldName }
392+ </ label >
393+ { schema . description && (
394+ < div className = "relative group" >
395+ < Info size = { 12 } className = "text-gray-400 cursor-help" />
396+ < div className = "absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap z-10" >
397+ { schema . description }
398+ </ div >
392399 </ div >
393- </ div >
394- ) }
395- </ div >
396- < div className = { schema . type === 'number' || schema . type === 'integer' ? 'inline-block' : '' } >
397- { renderFormField ( tool . name , fieldName , schema , isRequired ) }
400+ ) }
401+ </ div >
402+ < div className = { schema . type === 'number' || schema . type === 'integer' ? 'flex flex-wrap items-center' : '' } >
403+ { renderFormField ( tool . name , fieldName , schema , isRequired ) }
404+ </ div >
398405 </ div >
399- </ div >
400- )
401- } ) }
402- </ div >
403- ) }
404-
405- { /* Run button */ }
406- < button
407- onClick = { ( ) => handleRunTool ( tool ) }
408- disabled = { state !== 'ready' }
409- className = "bg-green-600 hover:bg-green-700 disabled:bg-gray-400 text-white rounded py-2 px-4 text-sm font-medium"
410- >
411- Run
412- </ button >
413- </ div >
414- ) ) }
415-
416- { /* Execution log */ }
417- { tools . length > 0 && (
418- < div className = "bg-white p-4 rounded border border-gray-100 shadow-sm" >
419- < div className = "flex items-center justify-between mb-2" >
420- < h4 className = "font-medium text-sm text-gray-700" > Execution Log</ h4 >
406+ )
407+ } ) }
408+ </ div >
409+ ) }
410+
411+ { /* Run button */ }
421412 < button
422- onClick = { clearExecutionLog }
423- className = "flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700"
413+ onClick = { ( ) => handleRunTool ( tool ) }
414+ disabled = { state !== 'ready' }
415+ className = "bg-green-600 hover:bg-green-700 disabled:bg-gray-400 text-white rounded py-2 px-4 text-sm font-medium mb-4"
424416 >
425- < X size = { 12 } />
426- Clear
417+ Run
427418 </ button >
419+
420+ { /* Per-tool execution log */ }
421+ < div className = "border-t border-gray-100 pt-4" >
422+ < div className = "flex items-center justify-between mb-2" >
423+ < h5 className = "font-medium text-sm text-gray-700" > Execution Log</ h5 >
424+ < button
425+ onClick = { ( ) => clearExecutionLog ( tool . name ) }
426+ className = "flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700"
427+ >
428+ < X size = { 12 } />
429+ Clear
430+ </ button >
431+ </ div >
432+ < textarea
433+ value = { toolExecutionLogs [ tool . name ] || '' }
434+ readOnly
435+ className = "w-full h-32 p-2 border border-gray-200 rounded text-xs font-mono bg-gray-50 resize-none placeholder-gray-300"
436+ placeholder = "Tool execution results will appear here..."
437+ />
438+ </ div >
428439 </ div >
429- < textarea
430- ref = { executionLogRef }
431- value = { executionLog }
432- readOnly
433- className = "w-full h-32 p-2 border border-gray-200 rounded text-xs font-mono bg-gray-50 resize-none"
434- placeholder = "Tool execution results will appear here..."
435- />
436440 </ div >
437- ) }
441+ ) ) }
438442 </ div >
439443 ) }
440444 </ div >
0 commit comments