ConsoleSpy
by mgsrevolver
Verified
console.log('Starting MCP server initialization...');
try {
console.log('Loading express...');
const express = require('express');
console.log('Express loaded successfully');
console.log('Loading cors...');
const cors = require('cors');
console.log('CORS loaded successfully');
console.log('Creating express app...');
const app = express();
console.log('Express app created');
const port = 3333;
// In-memory storage for logs with improved structure
const sessions = {};
const MAX_LOGS_PER_SESSION = 500; // Increased from 100
console.log('Setting up middleware...');
app.use(
cors({
origin: '*', // Allow all origins
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true,
preflightContinue: false,
optionsSuccessStatus: 204,
})
);
console.log('CORS middleware added');
app.use(express.json({ limit: '1mb' }));
console.log('JSON middleware added');
// Add a simple root route with strict CSP
app.get('/', (req, res) => {
console.log('Root endpoint hit');
// Set strict Content-Security-Policy header
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'"
);
res.send(`
<html>
<head>
<title>Console to Cursor MCP</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 { color: #333; }
p { line-height: 1.6; }
code {
background: #f4f4f4;
padding: 2px 5px;
border-radius: 3px;
font-family: monospace;
}
button {
background: #4CAF50;
border: none;
color: white;
padding: 10px 15px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 4px;
}
.manual-logging {
margin-top: 30px;
padding: 15px;
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 5px;
}
</style>
</head>
<body>
<h1>Console to Cursor MCP</h1>
<p>This server is running correctly. Available endpoints:</p>
<ul>
<li><code>/console-logs</code> - POST endpoint for receiving logs from browser extension</li>
<li><code>/mcp</code> - GET endpoint for Cursor to retrieve logs</li>
<li><code>/test</code> - GET endpoint for testing server connectivity</li>
<li><code>/view-logs</code> - GET endpoint for viewing logs in browser</li>
</ul>
<h2>Test Console Logging</h2>
<p>Click the button below to test console logging:</p>
<button id="test-log">Test Console Log</button>
<div class="manual-logging">
<h2>Manual Logging</h2>
<p>If automatic console capture isn't working, you can use these manual logging functions:</p>
<ul>
<li><code>window.mcpLog("Your message here")</code> - Send a log message</li>
<li><code>window.mcpWarn("Your warning here")</code> - Send a warning message</li>
<li><code>window.mcpError("Your error here")</code> - Send an error message</li>
</ul>
<p>Example: Open your browser console and type <code>mcpLog("Hello from manual logging")</code></p>
</div>
<script>
document.getElementById('test-log').addEventListener('click', function() {
console.log('Test log from button click', new Date().toISOString());
console.warn('Test warning from button click');
console.error('Test error from button click');
alert('Test logs sent! Check the /view-logs endpoint to see them.');
});
</script>
</body>
</html>
`);
});
console.log('Setting up /console-logs endpoint...');
// Endpoint to receive logs from browser extension
app.post('/console-logs', (req, res) => {
console.log('Received request to /console-logs');
try {
const { logs, sessionId, url } = req.body;
if (!logs || !sessionId || !url) {
console.log('Missing required fields in request');
return res
.status(400)
.json({ success: false, error: 'Missing required fields' });
}
// Log the content for debugging
logs.forEach((log) => {
console.log(`Received ${log.type} log from ${url}:`, log.content);
});
if (!sessions[sessionId]) {
sessions[sessionId] = {
logs: [],
url: url,
firstSeen: new Date(),
lastSeen: new Date(),
};
} else {
sessions[sessionId].lastSeen = new Date();
}
// Add new logs
sessions[sessionId].logs.push(...logs);
// Trim if too many
if (sessions[sessionId].logs.length > MAX_LOGS_PER_SESSION) {
sessions[sessionId].logs = sessions[sessionId].logs.slice(
-MAX_LOGS_PER_SESSION
);
}
res.json({ success: true, logCount: sessions[sessionId].logs.length });
} catch (error) {
console.error('Error processing /console-logs request:', error);
res.status(500).json({ success: false, error: 'Server error' });
}
});
console.log('Setting up /mcp endpoint...');
// MCP endpoint for Cursor
app.get('/mcp', (req, res) => {
try {
// Get all sessions sorted by last activity
const allSessions = Object.values(sessions).sort(
(a, b) => new Date(b.lastSeen) - new Date(a.lastSeen)
);
if (allSessions.length === 0) {
return res.json({
content:
'No console logs captured. Toggle the Console to Cursor extension on your localhost tab.',
});
}
// Use the most recently active session
const activeSession = allSessions[0];
// Format logs for Cursor
const formattedLogs = activeSession.logs
.map(
(log) =>
`[${log.timestamp}] ${log.type.toUpperCase()}: ${JSON.stringify(
log.content
)}`
)
.join('\n');
// Make sure we're returning the exact format Cursor expects
res.json({
content: `Console logs from ${activeSession.url} (${activeSession.logs.length} logs):\n\n${formattedLogs}`,
});
} catch (error) {
console.error('Error processing /mcp request:', error);
res.status(200).json({
// Still return 200 with error message
content: 'Error retrieving logs: ' + error.message,
});
}
});
// New endpoint to view logs in browser
app.get('/view-logs', (req, res) => {
try {
// Get all sessions sorted by last activity
const allSessions = Object.values(sessions).sort(
(a, b) => new Date(b.lastSeen) - new Date(a.lastSeen)
);
if (allSessions.length === 0) {
return res.send(`
<html>
<head>
<title>MCP Logs</title>
<style>
body { font-family: sans-serif; padding: 20px; }
h1 { color: #333; }
.refresh { margin-bottom: 20px; }
</style>
</head>
<body>
<h1>MCP Logs</h1>
<div class="refresh">
<button onclick="location.reload()">Refresh</button>
</div>
<p>No logs captured yet. Make sure the console capture script is running.</p>
<p><a href="/">Back to home</a></p>
</body>
</html>
`);
}
// Generate HTML for logs
let html = `
<html>
<head>
<title>MCP Logs</title>
<style>
body { font-family: sans-serif; padding: 20px; }
h1, h2 { color: #333; }
.refresh { margin-bottom: 20px; }
.log-entry { margin-bottom: 5px; font-family: monospace; white-space: pre-wrap; }
.log { color: black; }
.warn { color: orange; }
.error { color: red; }
.info { color: blue; }
.debug { color: gray; }
.timestamp { color: #666; font-size: 0.8em; }
.session { margin-bottom: 30px; border: 1px solid #ddd; padding: 15px; border-radius: 5px; }
.controls { margin-bottom: 20px; }
.controls button { margin-right: 10px; }
.log-content { max-height: 500px; overflow: auto; }
.filter { margin-bottom: 15px; }
.filter label { margin-right: 10px; }
.nav { margin-bottom: 20px; }
.nav a { margin-right: 15px; }
</style>
</head>
<body>
<h1>MCP Logs</h1>
<div class="nav">
<a href="/">Home</a>
<a href="/test">Test Server</a>
</div>
<div class="refresh">
<button onclick="location.reload()">Refresh</button>
<span>(Auto-refreshes every 5 seconds)</span>
<button onclick="window.location.href='/clear-logs'" style="margin-left: 20px; background-color: #f44336;">Clear All Logs</button>
</div>
<div class="filter">
<label><input type="checkbox" class="filter-type" value="log" checked> Logs</label>
<label><input type="checkbox" class="filter-type" value="warn" checked> Warnings</label>
<label><input type="checkbox" class="filter-type" value="error" checked> Errors</label>
<label><input type="checkbox" class="filter-type" value="info" checked> Info</label>
<label><input type="checkbox" class="filter-type" value="debug" checked> Debug</label>
</div>
`;
// Add each session
allSessions.forEach((session, index) => {
const firstSeen = new Date(session.firstSeen).toLocaleString();
const lastSeen = new Date(session.lastSeen).toLocaleString();
html += `
<div class="session">
<h2>Session ${index + 1}: ${session.url}</h2>
<p>First seen: ${firstSeen} | Last seen: ${lastSeen} | Total logs: ${
session.logs.length
}</p>
<div class="log-content">
`;
// Add logs
if (session.logs.length === 0) {
html += `<p>No logs in this session.</p>`;
} else {
session.logs.forEach((log) => {
const timestamp = new Date(log.timestamp).toLocaleTimeString();
let content;
try {
content = JSON.stringify(log.content, null, 2);
} catch (e) {
content = String(log.content);
}
html += `
<div class="log-entry ${log.type}" data-type="${log.type}">
<span class="timestamp">[${timestamp}]</span>
<span class="${log.type}">${log.type.toUpperCase()}:</span>
${content}
</div>
`;
});
}
html += `
</div>
</div>`;
});
html += `
<script>
// Auto-refresh every 5 seconds
setTimeout(() => {
location.reload();
}, 5000);
// Set up filtering
document.querySelectorAll('.filter-type').forEach(checkbox => {
checkbox.addEventListener('change', function() {
const type = this.value;
const checked = this.checked;
document.querySelectorAll(\`.log-entry[data-type="\${type}"]\`).forEach(entry => {
entry.style.display = checked ? 'block' : 'none';
});
});
});
</script>
</body>
</html>
`;
res.send(html);
} catch (error) {
console.error('Error generating logs view:', error);
res.status(500).send('Error generating logs view: ' + error.message);
}
});
app.listen(port, () => {
console.log(`MCP server is running on port ${port}`);
});
} catch (error) {
console.error('Error initializing MCP server:', error);
}