Skip to main content
Glama
fritzprix
by fritzprix
app.ts8.19 kB
#!/usr/bin/env node import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { RubiksCube } from './cubeLogic.js'; import { VisualizationServer } from './visualizationServer.js'; import { GameSession, CubeMove, CubeResponse } from './types.js'; class RubiksCubeMCPServer { private mcpServer: McpServer; private visualizationServer: VisualizationServer; private games: Map<string, { cube: RubiksCube; session: GameSession }>; constructor() { this.mcpServer = new McpServer({ name: "rubiks-cube-mcp-server", version: "1.0.0" }); this.visualizationServer = new VisualizationServer(); this.games = new Map(); this.setupTools(); } private setupTools(): void { // 큐브 게임 시작 this.mcpServer.tool( "startCube", "Initialize a new Rubik's Cube game session", { scramble: z.boolean().optional().describe("Whether to scramble the cube initially"), difficulty: z.number().min(1).max(100).optional().describe("Number of scramble moves (1-100)") }, async ({ scramble = true, difficulty = 20 }: { scramble?: boolean; difficulty?: number }) => { const gameId = `cube_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const cube = new RubiksCube(); if (scramble) { cube.scramble(difficulty); } const session: GameSession = { id: gameId, cubeState: cube.getState(), createdAt: Date.now(), lastActivity: Date.now(), status: 'active', scrambleMoves: difficulty }; this.games.set(gameId, { cube, session }); this.visualizationServer.registerSession(session); const currentState = cube.getState(); const response: CubeResponse = { gameId, cube: currentState, scrambleMoves: difficulty, nextAction: currentState.solved ? "finish" : "manipulateCube" }; // MCP UI 리소스 생성 (예: 게임 링크) const gameUrl = `http://localhost:3000/game/${gameId}`; const uiResponse = { type: "resource", resource: { uri: `ui://game-link/${gameId}`, mimeType: "text/html", text: `<a href="${gameUrl}" target="_blank">Click to Play!</a>` } } as const; return { content: [ uiResponse, { type: "text", text: JSON.stringify(response, null, 2) } ] }; } ); // 게임 참여 this.mcpServer.tool( "joinGame", "Join an existing Rubik's Cube game session", { gameId: z.string().describe("The game session ID to join"), }, async ({ gameId }: { gameId: string }) => { const game = this.games.get(gameId); if (!game) { throw new Error(`Game session ${gameId} not found`); } const { cube, session } = game; const currentState = cube.getState(); const response: CubeResponse = { gameId, cube: currentState, scrambleMoves: session.scrambleMoves, nextAction: currentState.solved ? "finish" : "manipulateCube", }; return { content: [ { type: "text", text: "Joined game successfully." }, { type: "text", text: JSON.stringify(response, null, 2) }, ], }; } ); // 큐브 조작 this.mcpServer.tool( "manipulateCube", "Execute a move on the Rubik's Cube", { gameId: z.string().describe("The game session ID"), move: z.enum(['U', 'D', 'L', 'R', 'F', 'B', 'U\'', 'D\'', 'L\'', 'R\'', 'F\'', 'B\'', 'U2', 'D2', 'L2', 'R2', 'F2', 'B2']).describe("The cube move to execute") }, async ({ gameId, move }: { gameId: string; move: string }) => { const game = this.games.get(gameId); if (!game) { throw new Error(`Game session ${gameId} not found`); } const { cube, session } = game; // 이미 해결된 큐브인지 확인 if (session.status === 'completed') { const response: CubeResponse = { gameId, cube: cube.getState(), nextAction: "finish" }; return { content: [ { type: "text", text: JSON.stringify(response, null, 2) } ] }; } // 움직임 실행 cube.executeMove(move as CubeMove); const newState = cube.getState(); // 세션 업데이트 session.cubeState = newState; session.lastActivity = Date.now(); if (newState.solved) { session.status = 'completed'; } // 시각화 서버 업데이트 this.visualizationServer.updateSession(gameId, newState); const response: CubeResponse = { gameId, cube: newState, nextAction: newState.solved ? "finish" : "manipulateCube" }; return { content: [ { type: "text", text: JSON.stringify(response, null, 2) } ] }; } ); // 게임 완료 this.mcpServer.tool( "finish", "Complete the Rubik's Cube game session", { gameId: z.string().describe("The game session ID") }, async ({ gameId }: { gameId: string }) => { const game = this.games.get(gameId); if (!game) { throw new Error(`Game session ${gameId} not found`); } const { cube, session } = game; const finalState = cube.getState(); session.status = 'completed'; session.lastActivity = Date.now(); const response: CubeResponse = { gameId, cube: finalState, nextAction: null, }; const message = finalState.solved ? `🎉 Congratulations! You solved the cube for game ${gameId}.` : `Game ${gameId} finished. The cube was not solved.` return { content: [ { type: "text", text: message }, { type: "text", text: JSON.stringify(response, null, 2) }, ], }; } ); } async start(): Promise<void> { // 시각화 서버 시작 - 환경변수 PORT 또는 기본값 3000 사용 const port = parseInt(process.env.PORT || '3000'); this.visualizationServer.start(port); // MCP 서버 시작 const transport = new StdioServerTransport(); // Process exit handlers - parent process가 죽으면 함께 종료 process.on('SIGINT', () => { console.error("🛑 SIGINT received, shutting down..."); this.shutdown(); }); process.on('SIGTERM', () => { console.error("🛑 SIGTERM received, shutting down..."); this.shutdown(); }); // Stdio disconnect handler - MCP client 연결이 끊어지면 종료 process.stdin.on('end', () => { console.error("🛑 Stdin disconnected, shutting down..."); this.shutdown(); }); process.stdin.on('close', () => { console.error("🛑 Stdin closed, shutting down..."); this.shutdown(); }); await this.mcpServer.connect(transport); console.error("🎲 Rubik's Cube MCP Server started!"); console.error("🌐 Visualization available at: http://localhost:3000"); } private shutdown(): void { console.error("🔄 Shutting down servers..."); try { // Visualization server 종료 this.visualizationServer.stop(); console.error("✅ Visualization server stopped"); } catch (error) { console.error("❌ Error stopping visualization server:", error); } // Process 종료 process.exit(0); } } // 서버 시작 const server = new RubiksCubeMCPServer(); server.start().catch((error) => { console.error("Failed to start server:", error); process.exit(1); });

Implementation Reference

Latest Blog Posts

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/fritzprix/rubiks-cube-mcp-server'

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