diff --git a/README.md b/README.md index 0972971..364f1d7 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A professional-grade Model Context Protocol (MCP) server for Rider-Waite tarot c **✅ FULLY IMPLEMENTED AND WORKING:** - Complete 78-card Rider-Waite deck with detailed interpretations - 11 professional tarot spreads (Single Card, Three Card, Celtic Cross, Horseshoe, Relationship Cross, Career Path, Decision Making, Spiritual Guidance, Year Ahead, Chakra Alignment, Shadow Work) +- **Custom Spread Creation**: AI can create custom tarot spreads when existing ones don't fit - Multi-transport MCP server (stdio, HTTP, SSE) - Advanced interpretation engine with elemental analysis - Cryptographically secure card shuffling and drawing @@ -24,6 +25,7 @@ A professional-grade Model Context Protocol (MCP) server for Rider-Waite tarot c - **Research-Based Accuracy**: Interpretations verified against professional tarot sources (Biddy Tarot, Labyrinthos, classical literature) - **Complete Rider-Waite Deck**: Comprehensive card database with detailed meanings, symbolism, astrology, and numerology - **11 Professional Spreads**: Celtic Cross, Relationship Cross, Career Path, Spiritual Guidance, Chakra Alignment, Year Ahead, and more +- **Custom Spread Creation**: AI can create unlimited custom spreads (1-15 positions) when existing spreads don't fit the specific question or context - **Specialized Reading Analysis**: Tailored interpretations for relationships, career, spiritual growth, and energy balancing - **Intelligent Card Combinations**: Multi-dimensional analysis including elemental balance, suit patterns, and numerical progressions @@ -197,6 +199,21 @@ When running in HTTP mode, the following endpoints are available: "sessionId": "optional-session-id-for-tracking" } ``` +- `POST /api/custom-spread` - Create and perform a custom tarot spread + ```json + { + "spreadName": "Your Custom Spread Name", + "description": "What this spread explores", + "positions": [ + { + "name": "Position Name", + "meaning": "What this position represents" + } + ], + "question": "Your specific question", + "sessionId": "optional-session-id" + } + ``` - `GET /api/spreads` - List all available spread types with descriptions ### Advanced Features @@ -215,7 +232,7 @@ When running in HTTP mode, the following endpoints are available: ## 🛠️ MCP Tools -The server provides **7 comprehensive MCP tools** for professional tarot reading and analysis: +The server provides **8 comprehensive MCP tools** for professional tarot reading and analysis: ### `get_card_info` Get comprehensive information about a specific tarot card including symbolism, astrology, and numerology. @@ -315,6 +332,38 @@ Get random cards with optional filtering for practice and exploration. - Optional filtering by suit, arcana, or element - Customizable card count +### `create_custom_spread` +Create a custom tarot spread and draw cards for it. Perfect for AI when no existing spread fits the specific needs. +```json +{ + "spreadName": "AI Decision Making Spread", + "description": "A custom spread designed to help AI make decisions when no existing spread fits the situation", + "positions": [ + { + "name": "Current Situation", + "meaning": "The present state of affairs that needs to be addressed" + }, + { + "name": "Hidden Influences", + "meaning": "Unseen factors affecting the situation" + }, + { + "name": "Guidance", + "meaning": "Wisdom and advice for making the best decision" + } + ], + "question": "What is the best approach for this unique situation?", + "sessionId": "optional-session-id" +} +``` +**Features**: +- Create custom spreads with 1-15 positions +- Define custom position names and meanings +- Automatic card drawing with cryptographically secure randomization +- Full interpretation with position-specific analysis +- Session management support +- Perfect for AI when existing spreads don't fit the specific question or context + ## 🔧 Configuration ### Command Line Options @@ -448,6 +497,40 @@ curl -X POST http://localhost:3000/api/reading \ ``` **Features**: 7-card chakra analysis, energy balance assessment, spiritual healing guidance +#### Custom Spread Creation +```bash +curl -X POST http://localhost:3000/api/custom-spread \ + -H "Content-Type: application/json" \ + -d '{ + "spreadName": "AI Decision Making Spread", + "description": "A custom spread designed to help AI make decisions when no existing spread fits the situation", + "positions": [ + { + "name": "Current Situation", + "meaning": "The present state of affairs that needs to be addressed" + }, + { + "name": "Hidden Influences", + "meaning": "Unseen factors affecting the situation" + }, + { + "name": "Option A", + "meaning": "One potential direction or choice" + }, + { + "name": "Option B", + "meaning": "An alternative direction or choice" + }, + { + "name": "Guidance", + "meaning": "Wisdom and advice for making the best decision" + } + ], + "question": "What is the best approach for creating a new tarot spread when existing ones don'\''t fit?" + }' +``` +**Features**: Unlimited custom spread creation (1-15 positions), AI-driven card drawing, position-specific interpretations + ### Card Information Queries #### Detailed Card Information diff --git a/src/http-server.ts b/src/http-server.ts index be556de..06df3c7 100644 --- a/src/http-server.ts +++ b/src/http-server.ts @@ -109,6 +109,23 @@ export class TarotHttpServer { res.status(500).json({ error: error instanceof Error ? error.message : String(error) }); } }); + + // Custom spread creation endpoint + this.app.post('/api/custom-spread', async (req, res) => { + try { + const { spreadName, description, positions, question, sessionId } = req.body; + const result = await this.tarotServer.executeTool('create_custom_spread', { + spreadName, + description, + positions, + question, + sessionId + }); + res.json({ result }); + } catch (error) { + res.status(500).json({ error: error instanceof Error ? error.message : String(error) }); + } + }); } /** diff --git a/src/tarot-server.ts b/src/tarot-server.ts index 020f05f..c4d7af4 100644 --- a/src/tarot-server.ts +++ b/src/tarot-server.ts @@ -198,6 +198,52 @@ export class TarotServer { }, }, }, + { + name: "create_custom_spread", + description: "Create a custom tarot spread and draw cards for it. Use this when no existing spread fits your needs and you want to create your own layout with specific positions and meanings.", + inputSchema: { + type: "object", + properties: { + spreadName: { + type: "string", + description: "Name for your custom spread", + }, + description: { + type: "string", + description: "Description of what this spread is designed to explore", + }, + positions: { + type: "array", + description: "Array of position objects defining each card position in the spread", + items: { + type: "object", + properties: { + name: { + type: "string", + description: "Name of this position (e.g., 'Past Influences', 'Current Challenge')", + }, + meaning: { + type: "string", + description: "What this position represents in the reading", + }, + }, + required: ["name", "meaning"], + }, + minItems: 1, + maxItems: 15, + }, + question: { + type: "string", + description: "The question or focus for this reading", + }, + sessionId: { + type: "string", + description: "Optional session ID to continue a previous reading", + }, + }, + required: ["spreadName", "description", "positions", "question"], + }, + }, ]; } @@ -231,6 +277,9 @@ export class TarotServer { case "get_random_cards": return this.handleGetRandomCards(args); + case "create_custom_spread": + return this.handleCreateCustomSpread(args); + default: throw new Error(`Unknown tool: ${toolName}`); } @@ -396,4 +445,58 @@ export class TarotServer { return response; } + + /** + * Handle custom spread creation and reading + */ + private handleCreateCustomSpread(args: Record): string { + const { spreadName, description, positions, question, sessionId } = args; + + // Validate input + if (!spreadName || typeof spreadName !== 'string') { + return "Error: spreadName is required and must be a string."; + } + + if (!description || typeof description !== 'string') { + return "Error: description is required and must be a string."; + } + + if (!Array.isArray(positions) || positions.length === 0) { + return "Error: positions must be a non-empty array."; + } + + if (positions.length > 15) { + return "Error: Maximum 15 positions allowed for a custom spread."; + } + + if (!question || typeof question !== 'string') { + return "Error: question is required and must be a string."; + } + + // Validate each position + for (let i = 0; i < positions.length; i++) { + const position = positions[i]; + if (!position || typeof position !== 'object') { + return `Error: Position ${i + 1} must be an object with 'name' and 'meaning' properties.`; + } + if (!position.name || typeof position.name !== 'string') { + return `Error: Position ${i + 1} must have a 'name' property that is a string.`; + } + if (!position.meaning || typeof position.meaning !== 'string') { + return `Error: Position ${i + 1} must have a 'meaning' property that is a string.`; + } + } + + try { + return this.readingManager.performCustomReading( + spreadName, + description, + positions, + question, + sessionId + ); + } catch (error) { + return `Error creating custom spread: ${error instanceof Error ? error.message : String(error)}`; + } + } } \ No newline at end of file diff --git a/src/tarot/reading-manager.ts b/src/tarot/reading-manager.ts index 0f11e2e..5ab2306 100644 --- a/src/tarot/reading-manager.ts +++ b/src/tarot/reading-manager.ts @@ -76,10 +76,58 @@ export class TarotReadingManager { }); result += "Use the `perform_reading` tool with one of these spread types to get a reading."; - + return result; } + /** + * Perform a custom tarot reading with user-defined spread + */ + public performCustomReading( + spreadName: string, + description: string, + positions: { name: string; meaning: string }[], + question: string, + sessionId?: string + ): string { + // Create a custom spread object + const customSpread = { + name: spreadName, + description: description, + positions: positions, + cardCount: positions.length + }; + + // Use cryptographically secure random card drawing + const cards = this.cardManager.getRandomCards(customSpread.cardCount); + + // Generate random orientations for each card using secure randomness + const drawnCards: DrawnCard[] = cards.map((card: any, index: number) => ({ + card, + orientation: this.getSecureRandomOrientation(), // Cryptographically secure orientation + position: customSpread.positions[index].name, + positionMeaning: customSpread.positions[index].meaning + })); + + // Create the reading + const reading: TarotReading = { + id: this.generateReadingId(), + spreadType: `custom_${spreadName.toLowerCase().replace(/\s+/g, '_')}`, + question, + cards: drawnCards, + interpretation: this.generateInterpretation(drawnCards, question, customSpread.name), + timestamp: new Date(), + sessionId + }; + + // Add to session if provided + if (sessionId) { + this.sessionManager.addReadingToSession(sessionId, reading); + } + + return this.formatReading(reading, customSpread.name, customSpread.description); + } + /** * Interpret a combination of cards */