Skip to main content
Glama

Curupira

by drzln
cli-stdio.ts.old4.2 kB
#!/usr/bin/env node /** * @fileoverview Stdio-based CLI for Curupira MCP server * This is what Claude Code will execute via .mcp.json */ import { Server } from '@modelcontextprotocol/sdk/server/index.js' import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { WebSocketServer } from 'ws' import { createLogger } from '@curupira/shared/logging' import { setupMCPHandlers } from './mcp/index.js' import type { ConsoleMessage, NetworkRequest, DOMElement } from '@curupira/shared/types' // Define missing types locally interface DOMSnapshot { timestamp: number url: string html: string elements: DOMElement[] } interface ComponentState { componentId: string name: string props: Record<string, any> state: Record<string, any> hooks?: any[] } const logger = createLogger({ level: 'debug', name: 'curupira-mcp' }) // Store browser state received from Chrome extension const browserState = { consoleLogs: [] as ConsoleMessage[], networkRequests: [] as NetworkRequest[], domSnapshot: null as DOMSnapshot | null, componentStates: new Map<string, ComponentState>() } async function main() { logger.info('Starting Curupira MCP server with stdio transport') // Create MCP server const server = new Server( { name: 'curupira-mcp-server', version: '1.0.0' }, { capabilities: { resources: {}, tools: {}, prompts: {} } } ) // Setup MCP handlers setupMCPHandlers(server) // Create stdio transport for Claude Code const transport = new StdioServerTransport() await server.connect(transport) logger.info('MCP server connected via stdio') // Start WebSocket server for Chrome extension const wsPort = parseInt(process.env.WS_PORT || '8080') const wss = new WebSocketServer({ port: wsPort, host: '0.0.0.0' }) logger.info({ port: wsPort }, 'WebSocket server listening for Chrome extension') wss.on('connection', (ws) => { logger.info('Chrome extension connected') ws.on('message', (data) => { try { const message = JSON.parse(data.toString()) logger.debug({ type: message.type }, 'Received message from extension') // Update browser state based on message type switch (message.type) { case 'console': browserState.consoleLogs.push(...(message.data || [])) // Keep last 1000 logs if (browserState.consoleLogs.length > 1000) { browserState.consoleLogs = browserState.consoleLogs.slice(-1000) } break case 'network': browserState.networkRequests.push(...(message.data || [])) // Keep last 500 requests if (browserState.networkRequests.length > 500) { browserState.networkRequests = browserState.networkRequests.slice(-500) } break case 'dom': browserState.domSnapshot = message.data break case 'state': if (message.data?.componentId) { browserState.componentStates.set( message.data.componentId, message.data ) } break default: logger.warn({ type: message.type }, 'Unknown message type') } // Send acknowledgment ws.send(JSON.stringify({ type: 'ack', id: message.id })) } catch (error) { logger.error({ error }, 'Failed to process extension message') } }) ws.on('close', () => { logger.info('Chrome extension disconnected') }) ws.on('error', (error) => { logger.error({ error }, 'WebSocket error') }) }) // Handle shutdown process.on('SIGINT', () => { logger.info('Shutting down') wss.close() process.exit(0) }) process.on('SIGTERM', () => { logger.info('Shutting down') wss.close() process.exit(0) }) // Export browser state for MCP handlers to access ;(global as any).curupiraBrowserState = browserState } main().catch((error) => { logger.error({ error }, 'Fatal error') process.exit(1) })

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/drzln/curupira'

If you have feedback or need assistance with the MCP directory API, please join our Discord server