import React, { useState, useEffect } from 'react';
import { useMCP } from './useMCP';
import ErrorDisplay from './ErrorDisplay';
const MasterTester = () => {
const {
isConnected,
isLoading,
error,
availableTools,
availableModels,
capabilities,
checkConnection,
mcpService,
testConnection
} = useMCP();
// State management
const [selectedTool, setSelectedTool] = useState(null);
const [showModal, setShowModal] = useState(false);
const [toolInputs, setToolInputs] = useState({});
const [toolResults, setToolResults] = useState({});
const [validationResults, setValidationResults] = useState({});
const [testingMode, setTestingMode] = useState('individual'); // 'individual' or 'batch'
const [batchProgress, setBatchProgress] = useState({ current: 0, total: 0 });
const [isBatchTesting, setIsBatchTesting] = useState(false);
// Filter and search state
const [searchFilter, setSearchFilter] = useState('');
const [categoryFilter, setCategoryFilter] = useState('all');
// Get tool visual representation with enhanced categories
const getToolVisuals = (toolName) => {
const name = toolName.toLowerCase();
const toolCategories = {
search: { icon: '๐', color: '#28a745', bgColor: '#d4edda', category: 'Search & Query' },
memory: { icon: '๐ง ', color: '#6f42c1', bgColor: '#e2d9f3', category: 'Memory & Storage' },
database: { icon: '๐๏ธ', color: '#fd7e14', bgColor: '#fde2d1', category: 'Database' },
file: { icon: '๐', color: '#20c997', bgColor: '#c3f0e4', category: 'File System' },
ai: { icon: '๐ค', color: '#007bff', bgColor: '#cce7ff', category: 'AI & Chat' },
web: { icon: '๐', color: '#17a2b8', bgColor: '#bee5eb', category: 'Web & API' },
analytics: { icon: '๐', color: '#e83e8c', bgColor: '#f8d7da', category: 'Analytics' },
processing: { icon: 'โ๏ธ', color: '#6c757d', bgColor: '#e2e3e5', category: 'Processing' },
default: { icon: '๐ง', color: '#495057', bgColor: '#f8f9fa', category: 'General' }
};
// Enhanced pattern matching
if (name.includes('search') || name.includes('query') || name.includes('find')) {
return toolCategories.search;
} else if (name.includes('memory') || name.includes('store') || name.includes('remember') || name.includes('cache')) {
return toolCategories.memory;
} else if (name.includes('database') || name.includes('sql') || name.includes('db') || name.includes('postgres')) {
return toolCategories.database;
} else if (name.includes('file') || name.includes('read') || name.includes('write') || name.includes('document')) {
return toolCategories.file;
} else if (name.includes('chat') || name.includes('llm') || name.includes('ai') || name.includes('generate')) {
return toolCategories.ai;
} else if (name.includes('web') || name.includes('http') || name.includes('url') || name.includes('api')) {
return toolCategories.web;
} else if (name.includes('analyze') || name.includes('report') || name.includes('metric') || name.includes('stat')) {
return toolCategories.analytics;
} else if (name.includes('process') || name.includes('compute') || name.includes('transform')) {
return toolCategories.processing;
} else {
return toolCategories.default;
}
};
// Get smart example values
const getExampleValue = (paramName, paramSchema) => {
const name = paramName.toLowerCase();
const type = paramSchema.type;
if (paramSchema.example) return paramSchema.example;
// Smart examples based on parameter name patterns
const examples = {
string: {
query: 'SELECT * FROM users LIMIT 10',
search: 'test search query',
message: 'Hello, this is a test message',
text: 'Sample text content',
path: '/path/to/test/file.txt',
url: 'https://api.example.com/test',
id: 'test_id_' + Date.now(),
name: 'test_name',
content: 'Test content for validation',
conversation_id: 'test_conversation_123',
session_id: 'session_' + Date.now(),
role: 'user',
default: 'test_value'
},
number: {
limit: 10,
max: 100,
min: 1,
count: 5,
timeout: 30,
temperature: 0.7,
default: 1
},
integer: {
limit: 10,
max: 100,
min: 1,
count: 5,
timeout: 30,
default: 1
},
boolean: {
default: true
}
};
if (type && examples[type]) {
for (const [pattern, value] of Object.entries(examples[type])) {
if (pattern !== 'default' && name.includes(pattern)) {
return String(value);
}
}
return String(examples[type].default);
}
return '';
};
// Filter tools based on search and category
const filteredTools = availableTools ? availableTools.filter(tool => {
const matchesSearch = tool.name.toLowerCase().includes(searchFilter.toLowerCase()) ||
(tool.description && tool.description.toLowerCase().includes(searchFilter.toLowerCase()));
const toolCategory = getToolVisuals(tool.name).category;
const matchesCategory = categoryFilter === 'all' || toolCategory === categoryFilter;
return matchesSearch && matchesCategory;
}) : [];
// Get unique categories for filter dropdown
const uniqueCategories = [...new Set(
(availableTools || []).map(tool => getToolVisuals(tool.name).category)
)].sort();
// Handle tool selection
const handleToolSelect = (tool) => {
setSelectedTool(tool);
setShowModal(true);
// Pre-populate with example values
if (tool.inputSchema && tool.inputSchema.properties) {
const inputs = {};
Object.entries(tool.inputSchema.properties).forEach(([paramName, paramSchema]) => {
const inputKey = `${tool.name}_${paramName}`;
inputs[inputKey] = getExampleValue(paramName, paramSchema);
});
setToolInputs(prev => ({ ...prev, ...inputs }));
}
};
// Close modal
const closeModal = () => {
setShowModal(false);
setSelectedTool(null);
};
// Update tool input
const updateToolInput = (toolName, paramName, value) => {
const inputKey = `${toolName}_${paramName}`;
setToolInputs(prev => ({ ...prev, [inputKey]: value }));
};
// Test individual tool
const testTool = async (tool) => {
try {
setToolResults(prev => ({ ...prev, [tool.name]: 'Testing...' }));
// Get tool parameters from inputs
const params = {};
if (tool.inputSchema && tool.inputSchema.properties) {
Object.keys(tool.inputSchema.properties).forEach(paramName => {
const inputKey = `${tool.name}_${paramName}`;
params[paramName] = toolInputs[inputKey] || '';
});
}
// Call the tool via MCP service
const startTime = Date.now();
const response = await mcpService.callTool(tool.name, params);
const endTime = Date.now();
const duration = endTime - startTime;
setToolResults(prev => ({
...prev,
[tool.name]: {
status: 'โ
Success',
result: response,
timestamp: new Date().toLocaleTimeString(),
duration: duration,
params: params
}
}));
setValidationResults(prev => ({
...prev,
[tool.name]: 'success'
}));
return { success: true, duration };
} catch (err) {
setToolResults(prev => ({
...prev,
[tool.name]: {
status: `โ Failed: ${err.message}`,
result: null,
timestamp: new Date().toLocaleTimeString(),
error: err.message
}
}));
setValidationResults(prev => ({
...prev,
[tool.name]: 'error'
}));
return { success: false, error: err.message };
}
};
// Batch test all tools
const runBatchValidation = async () => {
if (!availableTools || availableTools.length === 0) return;
setIsBatchTesting(true);
setValidationResults({});
setBatchProgress({ current: 0, total: filteredTools.length });
let successCount = 0;
let errorCount = 0;
const startTime = Date.now();
for (let i = 0; i < filteredTools.length; i++) {
const tool = filteredTools[i];
setBatchProgress({ current: i + 1, total: filteredTools.length });
// Set up example inputs for validation
if (tool.inputSchema && tool.inputSchema.properties) {
const inputs = {};
Object.entries(tool.inputSchema.properties).forEach(([paramName, paramSchema]) => {
const inputKey = `${tool.name}_${paramName}`;
inputs[inputKey] = getExampleValue(paramName, paramSchema);
});
setToolInputs(prev => ({ ...prev, ...inputs }));
}
// Test the tool
const result = await testTool(tool);
if (result.success) {
successCount++;
} else {
errorCount++;
}
// Small delay between tests to prevent overwhelming the server
await new Promise(resolve => setTimeout(resolve, 300));
}
const totalTime = Date.now() - startTime;
// Show batch results summary
alert(`Batch Validation Complete!\n\nโ
Success: ${successCount}\nโ Failed: ${errorCount}\nโฑ๏ธ Total Time: ${(totalTime/1000).toFixed(1)}s`);
setIsBatchTesting(false);
};
// Clear all results
const clearAllResults = () => {
setToolResults({});
setValidationResults({});
};
return (
<div style={{ padding: '20px', maxWidth: '1400px', margin: '0 auto' }}>
{/* Header */}
<div style={{
textAlign: 'center',
marginBottom: '30px',
padding: '20px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white',
borderRadius: '12px'
}}>
<h1 style={{ margin: '0 0 10px 0', fontSize: '2.5rem' }}>๐ฌ Master Tester</h1>
<p style={{ margin: '0', fontSize: '1.2rem', opacity: '0.9' }}>
Comprehensive MCP Server Validation & Testing Suite
</p>
</div>
{/* Connection & Control Panel */}
<div style={{
padding: '20px',
marginBottom: '25px',
backgroundColor: isConnected ? '#d4edda' : '#f8d7da',
color: isConnected ? '#155724' : '#721c24',
borderRadius: '12px',
border: `2px solid ${isConnected ? '#c3e6cb' : '#f5c6cb'}`
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '15px' }}>
<div>
<div style={{ fontSize: '1.2rem', fontWeight: 'bold', marginBottom: '5px' }}>
๐ Server Status: {isConnected ? '๐ข Connected' : '๐ด Disconnected'}
</div>
{isConnected && availableTools && (
<div style={{ fontSize: '0.9rem' }}>
๐ {availableTools.length} tools available | ๐ค {Object.keys(availableModels).length} model providers
</div>
)}
{error && (
<div style={{ marginTop: '10px' }}>
<ErrorDisplay
error={new Error(error)}
onRetry={checkConnection}
onDismiss={() => {}}
showDetails={true}
/>
</div>
)}
</div>
<div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
<button
onClick={checkConnection}
style={{
padding: '10px 16px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontWeight: 'bold'
}}
>
๐ Reconnect
</button>
<button
onClick={runBatchValidation}
disabled={!isConnected || !availableTools || availableTools.length === 0 || isBatchTesting}
style={{
padding: '10px 16px',
backgroundColor: '#28a745',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: isConnected && !isBatchTesting ? 'pointer' : 'not-allowed',
opacity: isConnected && !isBatchTesting ? 1 : 0.6,
fontWeight: 'bold'
}}
>
{isBatchTesting ? '๐ Testing...' : '๐ Run All Tests'}
</button>
<button
onClick={clearAllResults}
style={{
padding: '10px 16px',
backgroundColor: '#6c757d',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontWeight: 'bold'
}}
>
๐๏ธ Clear Results
</button>
</div>
</div>
{/* Batch Progress */}
{isBatchTesting && (
<div style={{ marginTop: '15px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '5px' }}>
<span>Testing Progress</span>
<span>{batchProgress.current} / {batchProgress.total}</span>
</div>
<div style={{
width: '100%',
height: '8px',
backgroundColor: 'rgba(255,255,255,0.3)',
borderRadius: '4px',
overflow: 'hidden'
}}>
<div style={{
width: `${(batchProgress.current / batchProgress.total) * 100}%`,
height: '100%',
backgroundColor: '#007bff',
transition: 'width 0.3s ease'
}} />
</div>
</div>
)}
</div>
{/* Filters and Search */}
<div style={{
padding: '20px',
backgroundColor: '#f8f9fa',
borderRadius: '12px',
marginBottom: '25px',
border: '1px solid #dee2e6'
}}>
<div style={{ display: 'flex', gap: '15px', alignItems: 'center', flexWrap: 'wrap' }}>
<div style={{ flex: '1', minWidth: '200px' }}>
<input
type="text"
placeholder="๐ Search tools by name or description..."
value={searchFilter}
onChange={(e) => setSearchFilter(e.target.value)}
style={{
width: '100%',
padding: '10px 15px',
border: '2px solid #ced4da',
borderRadius: '8px',
fontSize: '1rem'
}}
/>
</div>
<div>
<select
value={categoryFilter}
onChange={(e) => setCategoryFilter(e.target.value)}
style={{
padding: '10px 15px',
border: '2px solid #ced4da',
borderRadius: '8px',
fontSize: '1rem',
backgroundColor: 'white'
}}
>
<option value="all">All Categories</option>
{uniqueCategories.map(category => (
<option key={category} value={category}>{category}</option>
))}
</select>
</div>
<div style={{ fontSize: '0.9rem', color: '#6c757d' }}>
Showing {filteredTools.length} of {availableTools?.length || 0} tools
</div>
</div>
</div>
{/* Tools Grid */}
{filteredTools.length > 0 && (
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(350px, 1fr))',
gap: '20px',
marginBottom: '30px'
}}>
{filteredTools.map((tool, index) => {
const visuals = getToolVisuals(tool.name);
const validationStatus = validationResults[tool.name];
const result = toolResults[tool.name];
return (
<div
key={tool.name || index}
onClick={() => handleToolSelect(tool)}
style={{
padding: '20px',
backgroundColor: 'white',
borderRadius: '12px',
border: `3px solid ${validationStatus === 'success' ? '#28a745' : validationStatus === 'error' ? '#dc3545' : visuals.color}`,
cursor: 'pointer',
transition: 'all 0.3s ease',
position: 'relative',
minHeight: '160px',
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
}}
onMouseOver={(e) => {
e.currentTarget.style.transform = 'translateY(-3px)';
e.currentTarget.style.boxShadow = '0 6px 20px rgba(0,0,0,0.15)';
}}
onMouseOut={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
}}
>
{/* Status Badge */}
<div style={{
position: 'absolute',
top: '15px',
right: '15px',
display: 'flex',
gap: '5px'
}}>
{validationStatus && (
<div style={{
padding: '4px 8px',
borderRadius: '12px',
fontSize: '0.75rem',
fontWeight: 'bold',
backgroundColor: validationStatus === 'success' ? '#28a745' : '#dc3545',
color: 'white'
}}>
{validationStatus === 'success' ? 'โ
' : 'โ'}
</div>
)}
{result && result.duration && (
<div style={{
padding: '4px 8px',
borderRadius: '12px',
fontSize: '0.75rem',
fontWeight: 'bold',
backgroundColor: '#17a2b8',
color: 'white'
}}>
{result.duration}ms
</div>
)}
</div>
{/* Tool Header */}
<div style={{ display: 'flex', alignItems: 'flex-start', marginBottom: '15px', marginRight: '80px' }}>
<div style={{ fontSize: '2.5rem', marginRight: '15px' }}>
{visuals.icon}
</div>
<div style={{ flex: 1 }}>
<h3 style={{
margin: '0 0 8px 0',
color: visuals.color,
fontSize: '1.2rem',
lineHeight: '1.2'
}}>
{tool.name}
</h3>
<span style={{
fontSize: '0.75rem',
backgroundColor: visuals.color,
color: 'white',
padding: '3px 8px',
borderRadius: '12px',
fontWeight: 'bold'
}}>
{visuals.category}
</span>
</div>
</div>
{/* Tool Description */}
{tool.description && (
<p style={{
fontSize: '0.9rem',
color: '#555',
margin: '15px 0',
lineHeight: '1.4',
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
overflow: 'hidden'
}}>
{tool.description}
</p>
)}
{/* Tool Info */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 'auto' }}>
<div style={{ fontSize: '0.85rem', color: '#666' }}>
{tool.inputSchema && tool.inputSchema.properties && (
<span>๐ {Object.keys(tool.inputSchema.properties).length} params</span>
)}
</div>
{result && result.timestamp && (
<div style={{ fontSize: '0.8rem', color: '#999' }}>
๐ {result.timestamp}
</div>
)}
</div>
</div>
);
})}
</div>
)}
{/* No Tools Message */}
{(!availableTools || availableTools.length === 0) && isConnected && (
<div style={{
padding: '60px',
textAlign: 'center',
backgroundColor: '#fff3cd',
color: '#856404',
borderRadius: '12px',
border: '2px solid #ffeaa7'
}}>
<div style={{ fontSize: '3rem', marginBottom: '20px' }}>โ ๏ธ</div>
<h3>No Tools Available</h3>
<p>No MCP tools were found. Check your server configuration and ensure tools are properly registered.</p>
</div>
)}
{/* Filtered Results Empty */}
{availableTools && availableTools.length > 0 && filteredTools.length === 0 && (
<div style={{
padding: '40px',
textAlign: 'center',
backgroundColor: '#e2e3e5',
color: '#495057',
borderRadius: '12px'
}}>
<div style={{ fontSize: '2rem', marginBottom: '15px' }}>๐</div>
<h3>No Tools Match Your Filter</h3>
<p>Try adjusting your search or category filter to see more tools.</p>
</div>
)}
{/* Tool Testing Modal */}
{showModal && selectedTool && (
<div style={{
position: 'fixed',
top: '0',
left: '0',
right: '0',
bottom: '0',
backgroundColor: 'rgba(0, 0, 0, 0.8)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1000,
padding: '20px'
}}>
<div style={{
backgroundColor: 'white',
borderRadius: '16px',
padding: '30px',
maxWidth: '700px',
maxHeight: '90vh',
width: '100%',
overflowY: 'auto',
position: 'relative',
boxShadow: '0 20px 60px rgba(0,0,0,0.3)'
}}>
{/* Modal Header */}
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '25px',
borderBottom: '2px solid #e9ecef',
paddingBottom: '20px'
}}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<span style={{ fontSize: '2.5rem', marginRight: '15px' }}>
{getToolVisuals(selectedTool.name).icon}
</span>
<div>
<h2 style={{ margin: '0', color: getToolVisuals(selectedTool.name).color, fontSize: '1.8rem' }}>
{selectedTool.name}
</h2>
<span style={{
fontSize: '0.85rem',
backgroundColor: getToolVisuals(selectedTool.name).color,
color: 'white',
padding: '4px 12px',
borderRadius: '12px',
fontWeight: 'bold'
}}>
{getToolVisuals(selectedTool.name).category}
</span>
</div>
</div>
<button
onClick={closeModal}
style={{
background: 'none',
border: 'none',
fontSize: '2rem',
cursor: 'pointer',
color: '#999',
padding: '5px'
}}
>
โ
</button>
</div>
{/* Tool Description */}
{selectedTool.description && (
<div style={{
padding: '20px',
backgroundColor: '#f8f9fa',
borderRadius: '10px',
marginBottom: '25px',
borderLeft: `5px solid ${getToolVisuals(selectedTool.name).color}`
}}>
<strong style={{ color: '#495057' }}>Description:</strong>
<div style={{ marginTop: '8px', lineHeight: '1.5' }}>{selectedTool.description}</div>
</div>
)}
{/* Parameters */}
{selectedTool.inputSchema && selectedTool.inputSchema.properties && (
<div style={{ marginBottom: '30px' }}>
<h3 style={{ marginBottom: '20px', color: '#495057', display: 'flex', alignItems: 'center' }}>
โ๏ธ Parameters
</h3>
{Object.entries(selectedTool.inputSchema.properties).map(([paramName, paramSchema]) => {
const isRequired = selectedTool.inputSchema.required && selectedTool.inputSchema.required.includes(paramName);
const currentValue = toolInputs[`${selectedTool.name}_${paramName}`] || '';
return (
<div key={paramName} style={{ marginBottom: '20px' }}>
<label style={{
display: 'block',
fontWeight: 'bold',
marginBottom: '8px',
color: isRequired ? '#dc3545' : '#495057',
fontSize: '1rem'
}}>
{paramName}
{isRequired && <span style={{ color: '#dc3545' }}>*</span>}
<span style={{
marginLeft: '10px',
fontSize: '0.8em',
fontWeight: 'normal',
color: '#6c757d',
backgroundColor: '#e9ecef',
padding: '2px 8px',
borderRadius: '4px'
}}>
{paramSchema.type || 'string'}
</span>
</label>
{paramSchema.description && (
<div style={{
fontSize: '0.9em',
color: '#6c757d',
marginBottom: '8px',
fontStyle: 'italic',
lineHeight: '1.4'
}}>
{paramSchema.description}
</div>
)}
<input
type={paramSchema.type === 'number' || paramSchema.type === 'integer' ? 'number' : 'text'}
value={currentValue}
onChange={(e) => updateToolInput(selectedTool.name, paramName, e.target.value)}
placeholder={`Enter ${paramName}...`}
style={{
width: '100%',
padding: '12px 16px',
border: `2px solid ${isRequired ? '#dc3545' : '#ced4da'}`,
borderRadius: '8px',
fontSize: '1rem',
backgroundColor: isRequired ? '#fff5f5' : 'white',
transition: 'border-color 0.2s ease'
}}
onFocus={(e) => {
e.target.style.borderColor = getToolVisuals(selectedTool.name).color;
}}
onBlur={(e) => {
e.target.style.borderColor = isRequired ? '#dc3545' : '#ced4da';
}}
/>
</div>
);
})}
</div>
)}
{/* Test Button */}
<div style={{ textAlign: 'center', marginBottom: '25px' }}>
<button
onClick={() => testTool(selectedTool)}
disabled={isLoading || !isConnected}
style={{
padding: '15px 40px',
backgroundColor: getToolVisuals(selectedTool.name).color,
color: 'white',
border: 'none',
borderRadius: '10px',
fontSize: '1.2rem',
fontWeight: 'bold',
cursor: !isLoading && isConnected ? 'pointer' : 'not-allowed',
opacity: !isLoading && isConnected ? 1 : 0.6,
minWidth: '200px',
transition: 'all 0.2s ease'
}}
>
๐งช Test {selectedTool.name}
</button>
</div>
{/* Results */}
{toolResults[selectedTool.name] && (
<div style={{
padding: '25px',
backgroundColor: '#f8f9fa',
borderRadius: '12px',
border: '2px solid #e9ecef'
}}>
<h4 style={{ marginTop: '0', color: '#495057', display: 'flex', alignItems: 'center' }}>
๐ Test Results
</h4>
<div style={{
padding: '15px',
marginBottom: '20px',
borderRadius: '8px',
fontWeight: 'bold',
backgroundColor: toolResults[selectedTool.name].status?.includes('โ
') ? '#d4edda' : '#f8d7da',
color: toolResults[selectedTool.name].status?.includes('โ
') ? '#155724' : '#721c24',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<span>{toolResults[selectedTool.name].status || toolResults[selectedTool.name]}</span>
{toolResults[selectedTool.name].timestamp && (
<span style={{ fontWeight: 'normal', fontSize: '0.9em' }}>
{toolResults[selectedTool.name].timestamp}
</span>
)}
</div>
{toolResults[selectedTool.name].duration && (
<div style={{ marginBottom: '15px', fontSize: '0.9em', color: '#666' }}>
โฑ๏ธ Execution time: {toolResults[selectedTool.name].duration}ms
</div>
)}
{toolResults[selectedTool.name].result && (
<div>
<div style={{ fontWeight: 'bold', marginBottom: '10px', color: '#495057' }}>
๐ Response Data:
</div>
<pre style={{
backgroundColor: 'white',
padding: '20px',
borderRadius: '8px',
border: '1px solid #dee2e6',
fontSize: '0.9rem',
lineHeight: '1.5',
maxHeight: '300px',
overflowY: 'auto',
whiteSpace: 'pre-wrap',
wordBreak: 'break-word'
}}>
{typeof toolResults[selectedTool.name].result === 'string'
? toolResults[selectedTool.name].result
: JSON.stringify(toolResults[selectedTool.name].result, null, 2)
}
</pre>
</div>
)}
</div>
)}
</div>
</div>
)}
{/* Loading Overlay */}
{isLoading && (
<div style={{
position: 'fixed',
top: '20px',
right: '20px',
padding: '15px 25px',
backgroundColor: '#007bff',
color: 'white',
borderRadius: '10px',
fontWeight: 'bold',
zIndex: 999,
boxShadow: '0 4px 12px rgba(0,0,0,0.2)'
}}>
๐ Loading...
</div>
)}
</div>
);
};
export default MasterTester;