UNPKG

mcp-rand

Version:

MCP server providing random generation utilities including UUID, numbers, strings, passwords, Gaussian distribution, dice rolling, and card drawing

121 lines 4.35 kB
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; export const toolSpec = { name: 'draw_cards', description: 'Draw cards from a standard deck of playing cards', inputSchema: { type: 'object', properties: { count: { type: 'number', description: 'Number of cards to draw', }, deckState: { type: 'string', description: 'Optional base64 encoded string representing the current deck state', } }, required: ['count'] } }; const SUITS = ['hearts', 'diamonds', 'clubs', 'spades']; const VALUES = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']; function createDeck() { const deck = []; for (const suit of SUITS) { for (const value of VALUES) { deck.push({ suit, value }); } } return deck; } function shuffleArray(array) { const shuffled = [...array]; for (let i = shuffled.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } return shuffled; } function getCardIndex(card) { const suitIndex = SUITS.indexOf(card.suit); const valueIndex = VALUES.indexOf(card.value); return suitIndex * VALUES.length + valueIndex; } function getDeckStateFromCards(availableCards) { // Create a binary array where 1 represents available cards const binaryArray = new Uint8Array(7); // 52 bits rounded up to bytes for (const card of availableCards) { const index = getCardIndex(card); const byteIndex = Math.floor(index / 8); const bitIndex = index % 8; binaryArray[byteIndex] |= 1 << bitIndex; } return Buffer.from(binaryArray).toString('base64'); } function getAvailableCardsFromState(deckState) { try { const binaryArray = Buffer.from(deckState, 'base64'); if (binaryArray.length !== 7) { throw new McpError(ErrorCode.InvalidParams, 'Invalid deck state: incorrect length'); } const availableCards = []; for (let i = 0; i < 52; i++) { const byteIndex = Math.floor(i / 8); const bitIndex = i % 8; if ((binaryArray[byteIndex] & (1 << bitIndex)) !== 0) { const suitIndex = Math.floor(i / VALUES.length); const valueIndex = i % VALUES.length; availableCards.push({ suit: SUITS[suitIndex], value: VALUES[valueIndex] }); } } return availableCards; } catch (error) { throw new McpError(ErrorCode.InvalidParams, 'Invalid deck state: must be base64 encoded'); } } export const drawCardsHandler = async (request) => { const args = request.params.arguments; if (args.count <= 0) { throw new McpError(ErrorCode.InvalidParams, 'Must draw at least one card'); } // Validate and process deck state if provided let availableCards; if (args.deckState) { // Validate base64 format if (!/^[A-Za-z0-9+/]+=*$/.test(args.deckState)) { throw new McpError(ErrorCode.InvalidParams, 'Invalid deck state: must be base64 encoded'); } availableCards = getAvailableCardsFromState(args.deckState); } else { availableCards = createDeck(); } if (args.count > availableCards.length) { throw new McpError(ErrorCode.InvalidParams, `Cannot draw ${args.count} cards from deck with ${availableCards.length} cards remaining`); } // Shuffle the available cards const shuffledCards = shuffleArray(availableCards); // Draw the requested number of cards const drawnCards = shuffledCards.slice(0, args.count); // Calculate remaining cards and get their state const remainingCards = shuffledCards.slice(args.count); const newDeckState = getDeckStateFromCards(remainingCards); const result = { drawnCards, remainingCount: remainingCards.length, deckState: newDeckState }; return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; }; //# sourceMappingURL=draw-cards.handler.js.map