Skip to content

Commit 0cc21d5

Browse files
committed
wip
1 parent 3b5f21b commit 0cc21d5

File tree

1 file changed

+91
-87
lines changed

1 file changed

+91
-87
lines changed

examples/inspector/src/components/McpServers.tsx

Lines changed: 91 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)