mcp-quiz-server
Version:
๐ง AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.
254 lines (226 loc) โข 8.66 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE MCP Client Test</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.status {
padding: 10px;
border-radius: 4px;
margin: 10px 0;
font-weight: bold;
}
.connected { background: #d4edda; color: #155724; }
.disconnected { background: #f8d7da; color: #721c24; }
.warning { background: #fff3cd; color: #856404; }
.log {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 15px;
height: 300px;
overflow-y: auto;
font-family: 'Courier New', monospace;
font-size: 12px;
white-space: pre-wrap;
}
.controls {
margin: 20px 0;
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
margin: 5px;
}
button:hover { background: #0056b3; }
button:disabled { background: #6c757d; cursor: not-allowed; }
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.stat-card {
background: #e9ecef;
padding: 15px;
border-radius: 4px;
text-align: center;
}
.stat-number { font-size: 24px; font-weight: bold; color: #007bff; }
</style>
</head>
<body>
<div class="container">
<h1>๐ SSE MCP Transport Test Client</h1>
<div id="status" class="status disconnected">
โ Disconnected
</div>
<div class="stats">
<div class="stat-card">
<div class="stat-number" id="messageCount">0</div>
<div>Messages Received</div>
</div>
<div class="stat-card">
<div class="stat-number" id="uptime">00:00:00</div>
<div>Connection Time</div>
</div>
<div class="stat-card">
<div class="stat-number" id="mcpCalls">0</div>
<div>MCP Calls Made</div>
</div>
</div>
<div class="controls">
<button id="connectBtn" onclick="connect()">๐ Connect</button>
<button id="disconnectBtn" onclick="disconnect()" disabled>๐ Disconnect</button>
<button id="testMcpBtn" onclick="testMCP()" disabled>๐งช Test MCP Call</button>
<button onclick="clearLog()">๐งน Clear Log</button>
</div>
<h3>๐ Event Log</h3>
<div id="log" class="log"></div>
</div>
<script>
let eventSource = null;
let messageCount = 0;
let mcpCalls = 0;
let startTime = null;
let uptimeInterval = null;
const SSE_URL = 'http://localhost:3001/sse';
const MCP_URL = 'http://localhost:3001/mcp';
function log(message) {
const logEl = document.getElementById('log');
const timestamp = new Date().toLocaleTimeString();
logEl.textContent += `[${timestamp}] ${message}\\n`;
logEl.scrollTop = logEl.scrollHeight;
}
function updateStatus(connected) {
const statusEl = document.getElementById('status');
const connectBtn = document.getElementById('connectBtn');
const disconnectBtn = document.getElementById('disconnectBtn');
const testMcpBtn = document.getElementById('testMcpBtn');
if (connected) {
statusEl.className = 'status connected';
statusEl.textContent = 'โ
Connected to SSE Transport';
connectBtn.disabled = true;
disconnectBtn.disabled = false;
testMcpBtn.disabled = false;
startTime = Date.now();
uptimeInterval = setInterval(updateUptime, 1000);
} else {
statusEl.className = 'status disconnected';
statusEl.textContent = 'โ Disconnected';
connectBtn.disabled = false;
disconnectBtn.disabled = true;
testMcpBtn.disabled = true;
if (uptimeInterval) {
clearInterval(uptimeInterval);
uptimeInterval = null;
}
}
}
function updateUptime() {
if (!startTime) return;
const elapsed = Math.floor((Date.now() - startTime) / 1000);
const hours = Math.floor(elapsed / 3600).toString().padStart(2, '0');
const minutes = Math.floor((elapsed % 3600) / 60).toString().padStart(2, '0');
const seconds = (elapsed % 60).toString().padStart(2, '0');
document.getElementById('uptime').textContent = `${hours}:${minutes}:${seconds}`;
}
function connect() {
if (eventSource) {
log('โ Already connected');
return;
}
log('๐ Connecting to SSE server...');
eventSource = new EventSource(SSE_URL);
eventSource.onopen = function() {
log('โ
SSE connection established');
updateStatus(true);
};
eventSource.onmessage = function(event) {
messageCount++;
document.getElementById('messageCount').textContent = messageCount;
try {
const data = JSON.parse(event.data);
log(`๐จ ${data.type}: ${data.message || JSON.stringify(data)}`);
} catch (e) {
log(`๐จ Raw message: ${event.data}`);
}
};
eventSource.onerror = function(error) {
log('โ SSE connection error');
console.error('SSE Error:', error);
updateStatus(false);
eventSource = null;
};
}
function disconnect() {
if (eventSource) {
eventSource.close();
eventSource = null;
log('๐ Disconnected from SSE server');
updateStatus(false);
}
}
async function testMCP() {
if (!eventSource) {
log('โ Not connected to SSE server');
return;
}
const request = {
jsonrpc: '2.0',
id: Date.now(),
method: 'tools/list',
params: {}
};
try {
log('๐งช Sending MCP request...');
const response = await fetch(MCP_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(request)
});
const result = await response.json();
mcpCalls++;
document.getElementById('mcpCalls').textContent = mcpCalls;
log(`โ
MCP response: ${JSON.stringify(result, null, 2)}`);
} catch (error) {
log(`โ MCP request failed: ${error.message}`);
}
}
function clearLog() {
document.getElementById('log').textContent = '';
}
// Auto-connect on page load
window.onload = function() {
log('๐ SSE MCP Test Client loaded');
log('Click "Connect" to start SSE transport test');
};
// Cleanup on page unload
window.onbeforeunload = function() {
if (eventSource) {
eventSource.close();
}
};
</script>
</body>
</html>