// src/components/MCPTestComponent.jsx
// React component to test all MCP APIs
import React, { useState, useEffect } from 'react';
import { useMCP } from '../hooks/useMCP';
const MCPTestComponent = () => {
const {
isConnected,
isLoading,
error,
availableModels,
availableTools,
capabilities,
checkConnection,
chat,
storeMemory,
getMemory,
testConnection,
mcpService
} = useMCP();
// Component state
const [chatMessage, setChatMessage] = useState('What is the capital of France?');
const [chatResponse, setChatResponse] = useState('');
const [selectedModel, setSelectedModel] = useState('mistral:latest');
const [memoryConvId, setMemoryConvId] = useState('test_conversation');
const [memoryContent, setMemoryContent] = useState('User asked about France');
const [retrievedMemories, setRetrievedMemories] = useState([]);
const [testResults, setTestResults] = useState({});
const [toolInputs, setToolInputs] = useState({});
const [toolResults, setToolResults] = useState({});
// Handle chat test
const handleChatTest = async () => {
try {
setTestResults(prev => ({ ...prev, chat: 'Loading...' }));
const response = await chat(chatMessage, selectedModel);
setChatResponse(response.response);
setTestResults(prev => ({
...prev,
chat: `✅ Success - ${response.model} via ${response.provider}`
}));
} catch (err) {
setTestResults(prev => ({
...prev,
chat: `❌ Failed: ${err.message}`
}));
}
};
// Handle memory storage test
const handleStoreMemory = async () => {
try {
setTestResults(prev => ({ ...prev, storeMemory: 'Loading...' }));
const response = await storeMemory(memoryConvId, memoryContent, {
source: 'react_test',
timestamp: new Date().toISOString()
});
setTestResults(prev => ({
...prev,
storeMemory: `✅ Stored with ID: ${response.memory_id}`
}));
} catch (err) {
setTestResults(prev => ({
...prev,
storeMemory: `❌ Failed: ${err.message}`
}));
}
};
// Handle memory retrieval test
const handleGetMemory = async () => {
try {
setTestResults(prev => ({ ...prev, getMemory: 'Loading...' }));
const response = await getMemory(memoryConvId);
setRetrievedMemories(response.memories || []);
setTestResults(prev => ({
...prev,
getMemory: `✅ Retrieved ${response.count} memories`
}));
} catch (err) {
setTestResults(prev => ({
...prev,
getMemory: `❌ Failed: ${err.message}`
}));
}
};
// Handle connection test
const handleConnectionTest = async () => {
try {
setTestResults(prev => ({ ...prev, connection: 'Testing...' }));
await testConnection();
setTestResults(prev => ({
...prev,
connection: '✅ Connection test passed'
}));
} catch (err) {
setTestResults(prev => ({
...prev,
connection: `❌ Connection failed: ${err.message}`
}));
}
};
// Handle tool testing
const handleToolTest = async (toolName, toolSchema) => {
try {
setToolResults(prev => ({ ...prev, [toolName]: 'Testing...' }));
// Get tool parameters from inputs
const params = {};
if (toolSchema.inputSchema && toolSchema.inputSchema.properties) {
Object.keys(toolSchema.inputSchema.properties).forEach(paramName => {
const inputKey = `${toolName}_${paramName}`;
params[paramName] = toolInputs[inputKey] || '';
});
}
// Call the tool via MCP service
const response = await mcpService.callTool(toolName, params);
setToolResults(prev => ({
...prev,
[toolName]: {
status: '✅ Success',
result: response
}
}));
} catch (err) {
setToolResults(prev => ({
...prev,
[toolName]: {
status: `❌ Failed: ${err.message}`,
result: null
}
}));
}
};
// Update tool input
const updateToolInput = (toolName, paramName, value) => {
const inputKey = `${toolName}_${paramName}`;
setToolInputs(prev => ({ ...prev, [inputKey]: value }));
};
// Get tool icon and color based on tool name/type
const getToolVisuals = (toolName) => {
const name = toolName.toLowerCase();
if (name.includes('search') || name.includes('query')) {
return { icon: '🔍', color: '#28a745', bgColor: '#d4edda', category: 'Search' };
} else if (name.includes('memory') || name.includes('store') || name.includes('remember')) {
return { icon: '🧠', color: '#6f42c1', bgColor: '#e2d9f3', category: 'Memory' };
} else if (name.includes('database') || name.includes('sql') || name.includes('db')) {
return { icon: '🗄️', color: '#fd7e14', bgColor: '#fde2d1', category: 'Database' };
} else if (name.includes('file') || name.includes('read') || name.includes('write')) {
return { icon: '📁', color: '#20c997', bgColor: '#c3f0e4', category: 'File System' };
} else if (name.includes('chat') || name.includes('llm') || name.includes('ai')) {
return { icon: '🤖', color: '#007bff', bgColor: '#cce7ff', category: 'AI/Chat' };
} else if (name.includes('web') || name.includes('http') || name.includes('url')) {
return { icon: '🌐', color: '#17a2b8', bgColor: '#bee5eb', category: 'Web/API' };
} else if (name.includes('analyze') || name.includes('process') || name.includes('compute')) {
return { icon: '⚙️', color: '#6c757d', bgColor: '#e2e3e5', category: 'Processing' };
} else {
return { icon: '🔧', color: '#495057', bgColor: '#f8f9fa', category: 'General' };
}
};
// Get example values for parameters
const getExampleValue = (paramName, paramSchema) => {
const name = paramName.toLowerCase();
const type = paramSchema.type;
if (paramSchema.example) return paramSchema.example;
if (type === 'string') {
if (name.includes('query') || name.includes('search')) return 'search term example';
if (name.includes('message') || name.includes('text')) return 'Hello, this is a test message';
if (name.includes('path') || name.includes('file')) return '/path/to/file.txt';
if (name.includes('url')) return 'https://example.com';
if (name.includes('id')) return 'test_id_123';
if (name.includes('name')) return 'test_name';
return 'example value';
} else if (type === 'number' || type === 'integer') {
if (name.includes('limit') || name.includes('max')) return '10';
if (name.includes('min')) return '1';
if (name.includes('count')) return '5';
return '1';
} else if (type === 'boolean') {
return 'true';
}
return '';
};
// Get available Ollama models for dropdown
const ollamaModels = availableModels.ollama || ['mistral:latest'];
return (
<div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
<h2>MCP API Test Component</h2>
{/* Connection Status */}
<div style={{
padding: '10px',
marginBottom: '20px',
backgroundColor: isConnected ? '#d4edda' : '#f8d7da',
color: isConnected ? '#155724' : '#721c24',
borderRadius: '4px',
border: `1px solid ${isConnected ? '#c3e6cb' : '#f5c6cb'}`
}}>
<strong>Connection Status:</strong> {isConnected ? '🟢 Connected' : '🔴 Disconnected'}
{error && <div>Error: {error}</div>}
<button
onClick={checkConnection}
style={{ marginLeft: '10px', padding: '5px 10px' }}
>
Reconnect
</button>
<button
onClick={handleConnectionTest}
style={{ marginLeft: '10px', padding: '5px 10px' }}
>
Test Connection
</button>
{testResults.connection && <div>{testResults.connection}</div>}
</div>
{/* Available Models */}
{Object.keys(availableModels).length > 0 && (
<div style={{ marginBottom: '20px', padding: '10px', backgroundColor: '#e2f3ff', borderRadius: '4px' }}>
<strong>Available Models:</strong>
<ul>
{Object.entries(availableModels).map(([provider, models]) => (
<li key={provider}>
<strong>{provider}:</strong> {Array.isArray(models) ? models.join(', ') : 'Loading...'}
</li>
))}
</ul>
</div>
)}
{/* Chat Test */}
<div style={{ marginBottom: '30px', padding: '15px', border: '1px solid #ddd', borderRadius: '4px' }}>
<h3>1. Chat Test</h3>
<div>
<textarea
value={chatMessage}
onChange={(e) => setChatMessage(e.target.value)}
placeholder="Enter your message"
style={{ width: '100%', height: '60px', marginBottom: '10px', padding: '8px' }}
/>
<select
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
style={{ marginRight: '10px', padding: '5px' }}
>
{ollamaModels.map(model => (
<option key={model} value={model}>{model}</option>
))}
</select>
<button
onClick={handleChatTest}
disabled={isLoading || !isConnected}
style={{ padding: '5px 15px' }}
>
Send Chat
</button>
</div>
{testResults.chat && <div style={{ marginTop: '10px' }}>{testResults.chat}</div>}
{chatResponse && (
<div style={{
marginTop: '10px',
padding: '10px',
backgroundColor: '#f8f9fa',
borderRadius: '4px',
borderLeft: '4px solid #007bff'
}}>
<strong>Response:</strong> {chatResponse}
</div>
)}
</div>
{/* Memory Tests */}
<div style={{ marginBottom: '30px', padding: '15px', border: '1px solid #ddd', borderRadius: '4px' }}>
<h3>2. Memory Tests</h3>
<div style={{ marginBottom: '15px' }}>
<input
type="text"
value={memoryConvId}
onChange={(e) => setMemoryConvId(e.target.value)}
placeholder="Conversation ID"
style={{ marginRight: '10px', padding: '5px' }}
/>
<input
type="text"
value={memoryContent}
onChange={(e) => setMemoryContent(e.target.value)}
placeholder="Content to store"
style={{ marginRight: '10px', padding: '5px', width: '200px' }}
/>
<button
onClick={handleStoreMemory}
disabled={isLoading || !isConnected}
style={{ marginRight: '10px', padding: '5px 15px' }}
>
Store Memory
</button>
<button
onClick={handleGetMemory}
disabled={isLoading || !isConnected}
style={{ padding: '5px 15px' }}
>
Get Memory
</button>
</div>
{testResults.storeMemory && <div>{testResults.storeMemory}</div>}
{testResults.getMemory && <div>{testResults.getMemory}</div>}
{retrievedMemories.length > 0 && (
<div style={{
marginTop: '10px',
padding: '10px',
backgroundColor: '#f8f9fa',
borderRadius: '4px'
}}>
<strong>Retrieved Memories:</strong>
{retrievedMemories.map((memory, index) => (
<div key={index} style={{ marginTop: '5px', fontSize: '0.9em' }}>
[{memory.timestamp}] {memory.role}: {memory.content}
</div>
))}
</div>
)}
</div>
{/* Tools Testing Section */}
{availableTools && availableTools.length > 0 && (
<div style={{ marginBottom: '30px', padding: '15px', border: '1px solid #ddd', borderRadius: '4px' }}>
<h3>3. MCP Tools Testing</h3>
<div style={{
marginBottom: '20px',
padding: '15px',
backgroundColor: '#e3f2fd',
borderRadius: '4px',
border: '1px solid #bbdefb'
}}>
<h4 style={{ margin: '0 0 10px 0', color: '#1565c0' }}>📋 How to Test Tools</h4>
<ol style={{ margin: '0', paddingLeft: '20px', fontSize: '0.9em', color: '#424242' }}>
<li>Each tool below has a unique <strong>color and icon</strong> to help identify its purpose</li>
<li>Fill in the <strong>required parameters</strong> (marked with <span style={{color: '#dc3545'}}>*</span>)</li>
<li>Optional parameters have <strong>example values</strong> you can use or modify</li>
<li>Click <strong>"Test [tool name]"</strong> to execute the tool</li>
<li>View the results below each tool's test button</li>
</ol>
</div>
{availableTools.map((tool, index) => {
const visuals = getToolVisuals(tool.name);
return (
<div
key={tool.name || index}
style={{
marginBottom: '25px',
padding: '20px',
backgroundColor: visuals.bgColor,
borderRadius: '8px',
border: `2px solid ${visuals.color}`,
position: 'relative'
}}
>
{/* Tool Header */}
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '15px' }}>
<div style={{
fontSize: '2rem',
marginRight: '15px',
display: 'flex',
alignItems: 'center'
}}>
{visuals.icon}
</div>
<div style={{ flex: 1 }}>
<h4 style={{
margin: '0 0 5px 0',
color: visuals.color,
fontSize: '1.3rem',
fontWeight: 'bold'
}}>
{tool.name}
</h4>
<div style={{
display: 'inline-block',
padding: '2px 8px',
backgroundColor: visuals.color,
color: 'white',
borderRadius: '12px',
fontSize: '0.75rem',
fontWeight: 'bold'
}}>
{visuals.category}
</div>
</div>
</div>
{/* Tool Description */}
{tool.description && (
<div style={{
padding: '12px',
backgroundColor: 'rgba(255, 255, 255, 0.7)',
borderRadius: '4px',
marginBottom: '15px',
fontSize: '0.9em',
color: '#424242',
borderLeft: `4px solid ${visuals.color}`
}}>
<strong>Purpose:</strong> {tool.description}
</div>
)}
{/* Tool Parameters */}
{tool.inputSchema && tool.inputSchema.properties && (
<div style={{ marginBottom: '20px' }}>
<div style={{
display: 'flex',
alignItems: 'center',
marginBottom: '12px',
fontSize: '1rem',
fontWeight: 'bold',
color: visuals.color
}}>
⚙️ Parameters
</div>
<div style={{
display: 'grid',
gap: '15px',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))'
}}>
{Object.entries(tool.inputSchema.properties).map(([paramName, paramSchema]) => {
const isRequired = tool.inputSchema.required && tool.inputSchema.required.includes(paramName);
const exampleValue = getExampleValue(paramName, paramSchema);
const currentValue = toolInputs[`${tool.name}_${paramName}`];
return (
<div key={paramName} style={{
padding: '12px',
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderRadius: '6px',
border: `1px solid ${isRequired ? '#dc3545' : '#dee2e6'}`
}}>
<label style={{
display: 'block',
fontSize: '0.9em',
fontWeight: 'bold',
marginBottom: '6px',
color: isRequired ? '#dc3545' : '#495057'
}}>
{paramName}
{isRequired && <span style={{ color: '#dc3545', marginLeft: '2px' }}>*</span>}
<span style={{
marginLeft: '8px',
fontSize: '0.75em',
fontWeight: 'normal',
color: '#6c757d',
backgroundColor: '#f8f9fa',
padding: '1px 6px',
borderRadius: '3px'
}}>
{paramSchema.type || 'string'}
</span>
</label>
{paramSchema.description && (
<div style={{
fontSize: '0.8em',
color: '#6c757d',
marginBottom: '8px',
fontStyle: 'italic'
}}>
{paramSchema.description}
</div>
)}
<input
type={paramSchema.type === 'number' || paramSchema.type === 'integer' ? 'number' : 'text'}
value={currentValue || ''}
onChange={(e) => updateToolInput(tool.name, paramName, e.target.value)}
placeholder={exampleValue}
style={{
width: '100%',
padding: '8px 12px',
border: `2px solid ${isRequired ? '#dc3545' : '#ced4da'}`,
borderRadius: '4px',
fontSize: '0.9em',
backgroundColor: isRequired ? '#fff5f5' : 'white'
}}
/>
{exampleValue && !currentValue && (
<button
type="button"
onClick={() => updateToolInput(tool.name, paramName, exampleValue)}
style={{
marginTop: '6px',
padding: '4px 8px',
fontSize: '0.75em',
backgroundColor: '#f8f9fa',
border: '1px solid #dee2e6',
borderRadius: '3px',
cursor: 'pointer',
color: '#6c757d'
}}
>
Use example: {exampleValue}
</button>
)}
</div>
);
})}
</div>
</div>
)}
{/* Test Button */}
<div style={{ textAlign: 'center', marginBottom: '15px' }}>
<button
onClick={() => handleToolTest(tool.name, tool)}
disabled={isLoading || !isConnected}
style={{
padding: '12px 24px',
backgroundColor: visuals.color,
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: !isLoading && isConnected ? 'pointer' : 'not-allowed',
fontSize: '1rem',
fontWeight: 'bold',
opacity: !isLoading && isConnected ? 1 : 0.6,
minWidth: '200px',
transition: 'all 0.2s ease'
}}
onMouseOver={(e) => {
if (!isLoading && isConnected) {
e.target.style.transform = 'translateY(-1px)';
e.target.style.boxShadow = `0 4px 8px rgba(0,0,0,0.2)`;
}
}}
onMouseOut={(e) => {
e.target.style.transform = 'translateY(0)';
e.target.style.boxShadow = 'none';
}}
>
{visuals.icon} Test {tool.name}
</button>
</div>
{/* Tool Results */}
{toolResults[tool.name] && (
<div style={{
marginTop: '15px',
padding: '15px',
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderRadius: '6px',
border: `2px solid ${visuals.color}`
}}>
<div style={{
display: 'flex',
alignItems: 'center',
marginBottom: '10px',
fontSize: '1rem',
fontWeight: 'bold',
color: visuals.color
}}>
📊 Test Results
</div>
<div style={{
fontSize: '0.9em',
marginBottom: '12px',
padding: '8px 12px',
backgroundColor: toolResults[tool.name].status?.includes('✅') ? '#d4edda' : '#f8d7da',
borderRadius: '4px',
fontWeight: 'bold'
}}>
{toolResults[tool.name].status || toolResults[tool.name]}
</div>
{toolResults[tool.name].result && (
<div style={{
padding: '15px',
backgroundColor: '#f8f9fa',
borderRadius: '4px',
border: '1px solid #dee2e6'
}}>
<div style={{
fontWeight: 'bold',
marginBottom: '8px',
color: '#495057',
fontSize: '0.9em'
}}>
📄 Response Data:
</div>
<pre style={{
margin: '0',
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
fontSize: '0.8em',
lineHeight: '1.4',
maxHeight: '300px',
overflowY: 'auto',
backgroundColor: 'white',
padding: '10px',
borderRadius: '3px',
border: '1px solid #e9ecef'
}}>
{typeof toolResults[tool.name].result === 'string'
? toolResults[tool.name].result
: JSON.stringify(toolResults[tool.name].result, null, 2)
}
</pre>
</div>
)}
</div>
)}
</div>
);
})}
</div>
)}
{/* No tools available message */}
{availableTools && availableTools.length === 0 && (
<div style={{
marginBottom: '30px',
padding: '15px',
border: '1px solid #ffc107',
borderRadius: '4px',
backgroundColor: '#fff3cd',
color: '#856404'
}}>
<h3>3. MCP Tools Testing</h3>
<p>No MCP tools are currently available. Check your server configuration.</p>
</div>
)}
{/* Loading indicator */}
{isLoading && (
<div style={{
position: 'fixed',
top: '10px',
right: '10px',
padding: '10px',
backgroundColor: '#007bff',
color: 'white',
borderRadius: '4px'
}}>
Loading...
</div>
)}
</div>
);
};
export default MCPTestComponent;