This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Browser Logging System - A lightweight MCP-integrated logging system that captures frontend console logs and makes them accessible via the Model Context Protocol. Designed for development and debugging environments.
The system consists of three independent components that work together:
Frontend App (with mcp-logger.js)
↓ HTTP POST
Backend Server (logger-server.js) :22345
↓ HTTP GET / SSE Stream
MCP Server (mcp-server.js)
↓ STDIO
AI Assistant (Claude Desktop)
mcp-logger.js - Frontend logger (browser-side)
- Console interception for all console methods (log, error, warn, info, debug)
- Application logging API:
logger.log('namespace', data) - HTTP transmission to backend with buffering and retry logic
- Auto-configuration when loaded from backend server
- Host identification via
window.location.host - Fallback buffer for backend unavailability (500 entries max)
- Duplicate log filtering and batching
logger-server.js - Backend HTTP server (Node.js)
- In-memory log storage (500 entries per namespace per host)
- Multi-host support (localhost:3000, localhost:5173, etc.)
- Rate limiting (200 req/10s for localhost, 1000 req/min for others)
- Serves mcp-logger.js with auto-configuration injection
- SSE streaming endpoint for real-time log delivery
- Duplicate filtering (5-second window)
- Web UI log viewer at http://localhost:22345
mcp-server.js - MCP server (Node.js)
- STDIO transport for Claude Desktop integration
get_logstool with intelligent host/namespace selection- SSE-based real-time log streaming with HTTP fallback
- Auto-selection logic (single host → auto-select, multiple → prompt)
- Tool annotations: readOnlyHint=true, idempotentHint=false, openWorldHint=true
- Pagination support with offset parameter
- CHARACTER_LIMIT (5,000 chars) with automatic truncation
shell-log.sh - Shell command logging wrapper
- Captures stdout/stderr from shell commands in real-time
- Uses nohup with process substitution for background process capture
- Memory-only logging (no disk files)
- Auto-generates namespace from command name (truncated to 30 chars)
- Usage:
./shell-log.sh app-name command [args...]
# Install dependencies
npm install
# Start backend server (Terminal 1)
npm run start-backend
# or
node logger-server.js
# Start backend with auto-reload (Terminal 1)
npm run dev-backend
# Start MCP server (Terminal 2)
npm run start-mcp
# or
node mcp-server.js
# Run both in parallel (development)
npm run dev
# Test backend health
curl http://localhost:22345/api/health
# Test log status
curl http://localhost:22345/api/logs/status
# Open web log viewer
open http://localhost:22345# Serve test HTML files locally
python -m http.server 3000
# Then open: http://localhost:3000/test-frontend.html
# Test shell logging
./shell-log.sh test-app echo "Hello from shell"
# MCP inspector test
npx @modelcontextprotocol/inspector --cli \
node mcp-server.js \
-e FILTER_APP=my-app --method tools/call --tool-name 'get_logs'Set before loading mcp-logger.js:
window.MCP_LOGGING_ENABLED = true; // Enable/disable logging
window.MCP_LOGGING_APP_NAME = 'my-app'; // Application name (REQUIRED)
window.MCP_LOGGING_BACKEND_URL = 'http://localhost:22345'; // Backend URL
window.MCP_LOGGING_BUFFER_SIZE = 100; // Log buffer size
window.MCP_LOGGING_BATCH_INTERVAL = 100; // Batch interval (ms)Important: Application name (MCP_LOGGING_APP_NAME) is required for proper log organization and retrieval via MCP.
Environment variables (.env file):
PORT=22345 # Server port (default: 22345)
HOST=localhost # Server host (use 0.0.0.0 for external access)
MAX_LOG_ENTRIES=500 # Max logs per namespace per hostQuick Setup (using Claude Code CLI):
# Show help and copy commands
node mcp-server.js mcp-help
# Basic setup (app parameter required in get_logs)
claude mcp add FE-logs node /absolute/path/to/mcp-server.js
# With default app (recommended for single-app projects)
claude mcp add FE-logs node /absolute/path/to/mcp-server.js --env FILTER_APP=my-app
# Remove if needed
claude mcp remove FE-logsManual Configuration (Claude Desktop):
Add to config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"browser-logs": {
"command": "node",
"args": ["/absolute/path/to/mcp-logger/mcp-server.js"],
"env": {
"FILTER_APP": "my-app-name"
}
}
}
}FILTER_APP Environment Variable:
- Sets a default app for
get_logstool - Makes
appparameter optional in queries - Useful for single-application projects
- Can still override by passing
appexplicitly
Uses inject-logger.js for automatic backend detection and script loading. Place in HTML:
<script src="inject-logger.js"></script><script>
window.MCP_LOGGING_ENABLED = true;
window.MCP_LOGGING_APP_NAME = 'my-app'; // Required!
window.MCP_LOGGING_BACKEND_URL = 'http://localhost:22345';
</script>
<script src="mcp-logger.js"></script>Load directly from backend for auto-configuration:
<script src="http://localhost:22345/mcp-logger.js"></script>- In-memory storage: Map of apps → Map of hosts → Map of namespaces → Array of logs
- Circular buffer: Oldest logs removed when limit (500) exceeded per namespace
- Multi-app support: Each application tracked separately by name
- Multi-host support: Each frontend host (e.g., localhost:3000) tracked separately within an app
- Namespace isolation: browser, user-actions, api-calls, etc. stored independently
Frontend logger implements sophisticated retry:
- Initial transmission attempt via fetch()
- On failure: logs moved to fallbackBuffer (max 500 entries)
- Exponential backoff retry (5s × retry count)
- Max 3 retry attempts before giving up
- On successful reconnection: fallbackBuffer flushed to backend
Backend filters duplicates with 5-second window:
- Browser logs: filtered by
level:message:sourcekey - Application logs: filtered by namespace + JSON.stringify(data)
- Automatic cleanup of filter cache when >1000 entries
Real-time log delivery via Server-Sent Events:
- MCP server maintains persistent SSE connection to backend
- New logs pushed immediately via
new_logsevent - Keep-alive messages every 30 seconds
- Fallback to HTTP polling if SSE unavailable
When mcp-logger.js is served from backend (GET /mcp-logger.js):
- Server injects configuration snippet at top of script
- Sets
window.MCP_LOGGING_BACKEND_URLbased on request host - Auto-enables logging if not explicitly configured
- Client registration sent on first connection
STDIO Transport Protocol:
- MCP servers using STDIO transport communicate via stdin/stdout
- NEVER use
console.log()- it writes to stdout and breaks MCP JSON protocol - ALWAYS use
console.error()for logging - writes to stderr (safe) - Claude Desktop reads MCP protocol messages from stdout
- Any non-JSON output to stdout causes "Unexpected token" JSON parsing errors
Tool Annotations:
- readOnlyHint: true (tool doesn't modify data)
- destructiveHint: false (tool doesn't delete anything)
- idempotentHint: false (results change as new logs stream in)
- openWorldHint: true (connects to external backend server)
Pagination and Limits:
- Default lines: 5 (reduced from 20 for better readability)
- Max lines: 20 (reduced from 100)
- Offset parameter: supports pagination (default: 0)
- CHARACTER_LIMIT: 5,000 chars (automatic truncation with helpful message)
POST /api/logs/submit # Submit logs from frontend (requires app, host, logs)
GET /api/logs/status # Get all apps, hosts, and namespaces
GET /api/logs/:app/:host/:namespace # Retrieve specific logs (query: ?lines=5&offset=0&filter=text)
GET /api/health # Health check
GET /mcp-logger.js # Serve logger script with auto-config
GET /api/logs/stream # SSE streaming endpoint (supports app, frontend_host, namespace params)
GET / # Web log viewer UI
Without FILTER_APP (app parameter required):
get_logs(app="my-app")
get_logs(app="my-app", filter="error", lines=10, offset=0)
get_logs(app="my-app", frontend_host="localhost:3000", namespace="user-actions")With FILTER_APP set (app parameter optional):
get_logs() // Uses default app from FILTER_APP
get_logs(filter="error", lines=10) // Uses default app
get_logs(offset=5) // Pagination with default app
get_logs(app="other-app") // Override default app
get_logs(app="other-app", namespace="api-calls") // Override with specific namespaceParameters:
- app: Application name (required unless FILTER_APP set)
- lines: Number of log lines (1-20, default: 5)
- offset: Pagination offset (min: 0, default: 0)
- filter: Text search filter
- frontend_host: Specific host to query
- namespace: Specific namespace to query
Important:
- The
appparameter is mandatory unlessFILTER_APPis set FILTER_APPprovides a default but can be overridden per query- Pagination works by skipping
offsetentries and returninglinesentries
Namespaces are created automatically when first log is sent:
// Frontend
logger.log('my-new-namespace', { custom: 'data' });- Check backend health:
curl http://localhost:22345/api/health - Check frontend console for "BrowserLogger:" messages
- Verify CORS headers in network tab
- Check rate limiting status in backend logs
- Verify SSE connection in MCP server logs
- JSON parsing errors: Check for
console.log()usage (must beconsole.error()) - Tool not found: Verify MCP server is running and path is correct in Claude Desktop config
- Empty responses: Check backend server is running on correct port
- SSE connection fails: Backend may be down, will fall back to HTTP
Frontend: Edit addConsoleLog() in mcp-logger.js for browser logs, or pass custom data structure to logger.log()
Backend: Edit buildLogOutput() in mcp-server.js for MCP output formatting
Backend: Modify maxEntries parameter in LogStorage constructor (logger-server.js:11)
Frontend: Set window.MCP_LOGGING_BUFFER_SIZE before loading script
MCP Server: Modify this.CHARACTER_LIMIT in constructor (mcp-server.js:31)
mcp-logger/
├── mcp-logger.js # Frontend logger (browser-side)
├── inject-logger.js # Auto-loading injection script
├── logger-server.js # Backend HTTP server
├── mcp-server.js # MCP server (STDIO transport, SSE-based)
├── shell-log.sh # Shell command logging wrapper
├── .env.example # Example environment configuration
├── test-*.html # Test/demo HTML files
├── assets/ # Web viewer assets (CSS, JS)
├── templates/ # HTML templates
├── docs/ # Documentation
│ ├── SHELL_LOGGING.md # Shell logging guide
│ └── CRs/ # Change requests (gitignored)
├── test/ # Test files
├── package.json # Dependencies and scripts
└── README.md # User documentation
Use provided test HTML files for verification:
test-frontend.html- Full integration testtest-simple.html- Basic console logging test- Web viewer at http://localhost:22345
Runtime:
express- Backend HTTP server@modelcontextprotocol/sdk- MCP protocol implementationeventsource- SSE client for MCP serverdotenv- Environment variable management
Development:
nodemon- Auto-reload during developmentconcurrently- Run multiple processes in parallel
- Never include references to Claude, AI tools, or automated assistance in git commits (messages, co-author tags, or any metadata)
- Never mention Co-Authored-By: Claude noreply@anthropic.com
- MCP servers using STDIO must use console.error() for all logging (never console.log())
- Application name is required for proper log organization
- This system is for development environments only - not production-ready (no auth, uses HTTP)