feat: Add custom spread creation functionality and corresponding API endpoint
This commit is contained in:
85
README.md
85
README.md
@@ -7,6 +7,7 @@ A professional-grade Model Context Protocol (MCP) server for Rider-Waite tarot c
|
|||||||
**✅ FULLY IMPLEMENTED AND WORKING:**
|
**✅ FULLY IMPLEMENTED AND WORKING:**
|
||||||
- Complete 78-card Rider-Waite deck with detailed interpretations
|
- 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)
|
- 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)
|
- Multi-transport MCP server (stdio, HTTP, SSE)
|
||||||
- Advanced interpretation engine with elemental analysis
|
- Advanced interpretation engine with elemental analysis
|
||||||
- Cryptographically secure card shuffling and drawing
|
- 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)
|
- **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
|
- **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
|
- **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
|
- **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
|
- **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"
|
"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
|
- `GET /api/spreads` - List all available spread types with descriptions
|
||||||
|
|
||||||
### Advanced Features
|
### Advanced Features
|
||||||
@@ -215,7 +232,7 @@ When running in HTTP mode, the following endpoints are available:
|
|||||||
|
|
||||||
## 🛠️ MCP Tools
|
## 🛠️ 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_card_info`
|
||||||
Get comprehensive information about a specific tarot card including symbolism, astrology, and numerology.
|
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
|
- Optional filtering by suit, arcana, or element
|
||||||
- Customizable card count
|
- 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
|
## 🔧 Configuration
|
||||||
|
|
||||||
### Command Line Options
|
### 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
|
**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
|
### Card Information Queries
|
||||||
|
|
||||||
#### Detailed Card Information
|
#### Detailed Card Information
|
||||||
|
@@ -109,6 +109,23 @@ export class TarotHttpServer {
|
|||||||
res.status(500).json({ error: error instanceof Error ? error.message : String(error) });
|
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) });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -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":
|
case "get_random_cards":
|
||||||
return this.handleGetRandomCards(args);
|
return this.handleGetRandomCards(args);
|
||||||
|
|
||||||
|
case "create_custom_spread":
|
||||||
|
return this.handleCreateCustomSpread(args);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown tool: ${toolName}`);
|
throw new Error(`Unknown tool: ${toolName}`);
|
||||||
}
|
}
|
||||||
@@ -396,4 +445,58 @@ export class TarotServer {
|
|||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle custom spread creation and reading
|
||||||
|
*/
|
||||||
|
private handleCreateCustomSpread(args: Record<string, any>): 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)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -76,10 +76,58 @@ export class TarotReadingManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
result += "Use the `perform_reading` tool with one of these spread types to get a reading.";
|
result += "Use the `perform_reading` tool with one of these spread types to get a reading.";
|
||||||
|
|
||||||
return result;
|
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
|
* Interpret a combination of cards
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user