wip
This commit is contained in:
parent
3b5f21bef7
commit
0cc21d519b
1 changed files with 91 additions and 87 deletions
|
|
@ -58,7 +58,7 @@ export function McpServers({
|
||||||
clearStorage: () => {},
|
clearStorage: () => {},
|
||||||
})
|
})
|
||||||
const [toolForms, setToolForms] = useState<Record<string, Record<string, any>>>({})
|
const [toolForms, setToolForms] = useState<Record<string, Record<string, any>>>({})
|
||||||
const [executionLog, setExecutionLog] = useState<string>('')
|
const [toolExecutionLogs, setToolExecutionLogs] = useState<Record<string, string>>({})
|
||||||
const logRef = useRef<HTMLDivElement>(null)
|
const logRef = useRef<HTMLDivElement>(null)
|
||||||
const executionLogRef = useRef<HTMLTextAreaElement>(null)
|
const executionLogRef = useRef<HTMLTextAreaElement>(null)
|
||||||
|
|
||||||
|
|
@ -218,27 +218,32 @@ export function McpServers({
|
||||||
|
|
||||||
// Add execution start message
|
// Add execution start message
|
||||||
const startMessage = `Calling ${tool.name}(${argsStr})\n`
|
const startMessage = `Calling ${tool.name}(${argsStr})\n`
|
||||||
setExecutionLog(prev => prev + startMessage)
|
setToolExecutionLogs(prev => ({
|
||||||
|
...prev,
|
||||||
|
[tool.name]: (prev[tool.name] || '') + startMessage
|
||||||
|
}))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await connectionData.callTool(tool.name, args)
|
const result = await connectionData.callTool(tool.name, args)
|
||||||
const resultStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2)
|
const resultStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2)
|
||||||
setExecutionLog(prev => prev + `${resultStr}\n\n`)
|
setToolExecutionLogs(prev => ({
|
||||||
|
...prev,
|
||||||
|
[tool.name]: (prev[tool.name] || '') + `${resultStr}\n\n`
|
||||||
|
}))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setExecutionLog(prev => prev + `Error: ${error}\n\n`)
|
setToolExecutionLogs(prev => ({
|
||||||
|
...prev,
|
||||||
|
[tool.name]: (prev[tool.name] || '') + `Error: ${error}\n\n`
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-scroll to bottom
|
|
||||||
setTimeout(() => {
|
|
||||||
if (executionLogRef.current) {
|
|
||||||
executionLogRef.current.scrollTop = executionLogRef.current.scrollHeight
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear execution log
|
// Clear execution log for specific tool
|
||||||
const clearExecutionLog = () => {
|
const clearExecutionLog = (toolName: string) => {
|
||||||
setExecutionLog('')
|
setToolExecutionLogs(prev => ({
|
||||||
|
...prev,
|
||||||
|
[toolName]: ''
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render form field based on schema
|
// Render form field based on schema
|
||||||
|
|
@ -249,13 +254,15 @@ export function McpServers({
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
className="w-1/4 p-2 border border-gray-200 rounded text-sm focus:outline-none focus:ring-1 focus:ring-blue-300"
|
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"
|
||||||
value={value}
|
value={value}
|
||||||
step={schema.type === 'integer' ? 1 : 'any'}
|
step={schema.type === 'integer' ? 1 : 'any'}
|
||||||
required={isRequired}
|
required={isRequired}
|
||||||
onChange={(e) => handleFormChange(toolName, fieldName,
|
onChange={(e) => {
|
||||||
schema.type === 'integer' ? parseInt(e.target.value) || 0 : parseFloat(e.target.value) || 0
|
const newValue = e.target.value === '' ? '' :
|
||||||
)}
|
(schema.type === 'integer' ? parseInt(e.target.value) || 0 : parseFloat(e.target.value) || 0)
|
||||||
|
handleFormChange(toolName, fieldName, newValue)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else if (schema.type === 'boolean') {
|
} else if (schema.type === 'boolean') {
|
||||||
|
|
@ -272,7 +279,7 @@ export function McpServers({
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="w-full p-2 border border-gray-200 rounded text-sm focus:outline-none focus:ring-1 focus:ring-blue-300"
|
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"
|
||||||
value={value}
|
value={value}
|
||||||
required={isRequired}
|
required={isRequired}
|
||||||
placeholder={schema.description || ''}
|
placeholder={schema.description || ''}
|
||||||
|
|
@ -357,84 +364,81 @@ export function McpServers({
|
||||||
No tools available. Connect to an MCP server to see available tools.
|
No tools available. Connect to an MCP server to see available tools.
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="border border-gray-200 rounded p-4 bg-gray-50 space-y-6">
|
<div className="space-y-6">
|
||||||
{tools.map((tool: Tool, index: number) => (
|
{tools.map((tool: Tool, index: number) => (
|
||||||
<div
|
<div key={index}>
|
||||||
key={index}
|
{/* Tool title outside the card */}
|
||||||
className="bg-white p-4 rounded border border-gray-100 shadow-sm"
|
<h4 className="font-bold text-base text-black mb-2">
|
||||||
>
|
|
||||||
<div className="font-mono font-medium text-sm text-blue-700 mb-2">
|
|
||||||
{tool.name}
|
{tool.name}
|
||||||
</div>
|
</h4>
|
||||||
{tool.description && (
|
|
||||||
<p className="text-gray-600 mb-4 text-sm leading-relaxed">
|
|
||||||
{tool.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Form for tool parameters */}
|
<div className="bg-white p-4 rounded border border-gray-100 shadow-sm">
|
||||||
{tool.inputSchema && tool.inputSchema.properties && (
|
{tool.description && (
|
||||||
<div className="space-y-3 mb-4">
|
<p className="text-gray-600 mb-4 text-sm leading-relaxed">
|
||||||
{Object.entries(tool.inputSchema.properties).map(([fieldName, schema]: [string, any]) => {
|
{tool.description}
|
||||||
const isRequired = tool.inputSchema.required?.includes(fieldName) || false
|
</p>
|
||||||
return (
|
)}
|
||||||
<div key={fieldName} className="space-y-1">
|
|
||||||
<div className="flex items-center gap-2">
|
{/* Form for tool parameters */}
|
||||||
<label className="text-xs font-medium text-gray-700">
|
{tool.inputSchema && tool.inputSchema.properties && (
|
||||||
{fieldName}
|
<div className="space-y-3 mb-4">
|
||||||
{isRequired && <span className="text-red-500">*</span>}
|
{Object.entries(tool.inputSchema.properties).map(([fieldName, schema]: [string, any]) => {
|
||||||
</label>
|
const isRequired = tool.inputSchema.required?.includes(fieldName) || false
|
||||||
{schema.description && (
|
return (
|
||||||
<div className="relative group">
|
<div key={fieldName} className="space-y-1">
|
||||||
<Info size={12} className="text-gray-400 cursor-help" />
|
<div className="flex items-center gap-2">
|
||||||
<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">
|
<label className="text-xs font-medium text-gray-700">
|
||||||
{schema.description}
|
{fieldName}
|
||||||
|
</label>
|
||||||
|
{schema.description && (
|
||||||
|
<div className="relative group">
|
||||||
|
<Info size={12} className="text-gray-400 cursor-help" />
|
||||||
|
<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">
|
||||||
|
{schema.description}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
|
<div className={schema.type === 'number' || schema.type === 'integer' ? 'flex flex-wrap items-center' : ''}>
|
||||||
|
{renderFormField(tool.name, fieldName, schema, isRequired)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={schema.type === 'number' || schema.type === 'integer' ? 'inline-block' : ''}>
|
)
|
||||||
{renderFormField(tool.name, fieldName, schema, isRequired)}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)
|
|
||||||
})}
|
{/* Run button */}
|
||||||
|
<button
|
||||||
|
onClick={() => handleRunTool(tool)}
|
||||||
|
disabled={state !== 'ready'}
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
Run
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Per-tool execution log */}
|
||||||
|
<div className="border-t border-gray-100 pt-4">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<h5 className="font-medium text-sm text-gray-700">Execution Log</h5>
|
||||||
|
<button
|
||||||
|
onClick={() => clearExecutionLog(tool.name)}
|
||||||
|
className="flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700"
|
||||||
|
>
|
||||||
|
<X size={12} />
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<textarea
|
||||||
|
value={toolExecutionLogs[tool.name] || ''}
|
||||||
|
readOnly
|
||||||
|
className="w-full h-32 p-2 border border-gray-200 rounded text-xs font-mono bg-gray-50 resize-none placeholder-gray-300"
|
||||||
|
placeholder="Tool execution results will appear here..."
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
{/* Run button */}
|
|
||||||
<button
|
|
||||||
onClick={() => handleRunTool(tool)}
|
|
||||||
disabled={state !== 'ready'}
|
|
||||||
className="bg-green-600 hover:bg-green-700 disabled:bg-gray-400 text-white rounded py-2 px-4 text-sm font-medium"
|
|
||||||
>
|
|
||||||
Run
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Execution log */}
|
|
||||||
{tools.length > 0 && (
|
|
||||||
<div className="bg-white p-4 rounded border border-gray-100 shadow-sm">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<h4 className="font-medium text-sm text-gray-700">Execution Log</h4>
|
|
||||||
<button
|
|
||||||
onClick={clearExecutionLog}
|
|
||||||
className="flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700"
|
|
||||||
>
|
|
||||||
<X size={12} />
|
|
||||||
Clear
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<textarea
|
|
||||||
ref={executionLogRef}
|
|
||||||
value={executionLog}
|
|
||||||
readOnly
|
|
||||||
className="w-full h-32 p-2 border border-gray-200 rounded text-xs font-mono bg-gray-50 resize-none"
|
|
||||||
placeholder="Tool execution results will appear here..."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue