saju-mcp-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -0
- package/package.json +42 -0
- package/server.js +287 -0
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Saju MCP Server — Korean Four Pillars Astrology for AI
|
|
2
|
+
|
|
3
|
+
> Calculate Korean Saju (사주/四柱) birth charts from Claude, ChatGPT, Cursor, or any MCP-compatible AI assistant.
|
|
4
|
+
|
|
5
|
+
**Zero API keys. Zero cost. Pure calculation engine.**
|
|
6
|
+
|
|
7
|
+
Saju is Korea's traditional Four Pillars of Destiny astrology — mapping birth year, month, day, and hour into Heavenly Stems and Earthly Branches to reveal personality, strengths, and compatibility through the Five Elements (Wood, Fire, Earth, Metal, Water). Over **518,400 unique chart combinations** vs. Western astrology's 12 Sun signs.
|
|
8
|
+
|
|
9
|
+
## Tools
|
|
10
|
+
|
|
11
|
+
| Tool | Description |
|
|
12
|
+
|------|-------------|
|
|
13
|
+
| `calculate_saju` | Full birth chart: Four Pillars (Hanja + Korean), Day Master, zodiac animal, Five Elements balance |
|
|
14
|
+
| `get_day_master` | Personality profile for any of the 10 Day Masters (甲乙丙丁戊己庚辛壬癸) |
|
|
15
|
+
| `check_compatibility` | Two-person compatibility: element harmony, Six Harmony/Six Clash, elemental complements |
|
|
16
|
+
| `list_stems_and_branches` | Reference: all 10 Heavenly Stems (천간) and 12 Earthly Branches (지지) |
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
### Claude Desktop / Cursor
|
|
21
|
+
|
|
22
|
+
Add to your MCP config (`claude_desktop_config.json` or Cursor MCP settings):
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"saju": {
|
|
28
|
+
"command": "npx",
|
|
29
|
+
"args": ["-y", "saju-mcp-server"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Manual
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -g saju-mcp-server
|
|
39
|
+
saju-mcp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Example Usage
|
|
43
|
+
|
|
44
|
+
Ask your AI assistant:
|
|
45
|
+
|
|
46
|
+
- *"What's the Saju chart for someone born July 18, 1993?"*
|
|
47
|
+
- *"Compare compatibility between a person born 1995-10-26 and 1997-02-14"*
|
|
48
|
+
- *"What does a 庚 (Gyeong) Day Master personality look like?"*
|
|
49
|
+
- *"List all the Heavenly Stems and their elements"*
|
|
50
|
+
|
|
51
|
+
### Sample Output — `calculate_saju`
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Four Pillars: 癸酉 己未 庚子 ??
|
|
55
|
+
Day Master: 庚 (Gyeong) — Yang Metal — "The Sword"
|
|
56
|
+
Zodiac: Rooster (酉)
|
|
57
|
+
Elements: Wood 0 · Fire 0 · Earth 2 · Metal 2 · Water 2
|
|
58
|
+
Dominant: Earth | Weakest: Wood & Fire
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## What is Saju?
|
|
62
|
+
|
|
63
|
+
**Saju (사주, 四柱)** — literally "Four Pillars" — is Korea's ancient astrology system. Each pillar contains a Heavenly Stem (천간) and Earthly Branch (지지), creating an eight-character birth chart that encodes:
|
|
64
|
+
|
|
65
|
+
- **Day Master** — your core personality archetype (10 types)
|
|
66
|
+
- **Five Elements** — Wood, Fire, Earth, Metal, Water balance
|
|
67
|
+
- **Zodiac Animal** — one of 12 animals from the Earthly Branches
|
|
68
|
+
- **Energy Flow** — ascending or descending life pattern
|
|
69
|
+
|
|
70
|
+
Used by millions in Korea for personality insight, relationship compatibility, career guidance, and life timing.
|
|
71
|
+
|
|
72
|
+
## For Deeper Readings
|
|
73
|
+
|
|
74
|
+
This MCP server provides the **calculation engine** — pure Four Pillars math with no AI interpretation. For full AI-powered destiny readings, compatibility reports, and Korean name generation:
|
|
75
|
+
|
|
76
|
+
**[sajufromseoul.com](https://sajufromseoul.com)** — Free Saju readings, compatibility analysis, and Korean astrology blog.
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT — Built by [Saju from Seoul](https://sajufromseoul.com)
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "saju-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Korean Four Pillars (Saju/사주) astrology MCP server — calculate birth charts, Day Masters, Five Elements, and compatibility. No API keys needed.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"saju-mcp": "./server.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node server.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"server.js",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
19
|
+
"zod": "^3.24.0"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"mcp-server",
|
|
24
|
+
"saju",
|
|
25
|
+
"korean-astrology",
|
|
26
|
+
"four-pillars",
|
|
27
|
+
"birth-chart",
|
|
28
|
+
"five-elements",
|
|
29
|
+
"day-master",
|
|
30
|
+
"compatibility",
|
|
31
|
+
"korean-fortune",
|
|
32
|
+
"bazi",
|
|
33
|
+
"sajufromseoul"
|
|
34
|
+
],
|
|
35
|
+
"author": "Saju from Seoul <contact@sajufromseoul.com> (https://sajufromseoul.com)",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/heungmangoo-art/saju-mcp-server"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://sajufromseoul.com"
|
|
42
|
+
}
|
package/server.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
// ============================================
|
|
8
|
+
// SAJU ENGINE — Four Pillars Calculation
|
|
9
|
+
// Self-contained: no external API needed
|
|
10
|
+
// ============================================
|
|
11
|
+
|
|
12
|
+
const HEAVENLY_STEMS = [
|
|
13
|
+
{ hanja: "甲", korean: "갑", english: "Gap", element: "Wood", yin_yang: "Yang" },
|
|
14
|
+
{ hanja: "乙", korean: "을", english: "Eul", element: "Wood", yin_yang: "Yin" },
|
|
15
|
+
{ hanja: "丙", korean: "병", english: "Byeong", element: "Fire", yin_yang: "Yang" },
|
|
16
|
+
{ hanja: "丁", korean: "정", english: "Jeong", element: "Fire", yin_yang: "Yin" },
|
|
17
|
+
{ hanja: "戊", korean: "무", english: "Mu", element: "Earth", yin_yang: "Yang" },
|
|
18
|
+
{ hanja: "己", korean: "기", english: "Gi", element: "Earth", yin_yang: "Yin" },
|
|
19
|
+
{ hanja: "庚", korean: "경", english: "Gyeong", element: "Metal", yin_yang: "Yang" },
|
|
20
|
+
{ hanja: "辛", korean: "신", english: "Sin", element: "Metal", yin_yang: "Yin" },
|
|
21
|
+
{ hanja: "壬", korean: "임", english: "Im", element: "Water", yin_yang: "Yang" },
|
|
22
|
+
{ hanja: "癸", korean: "계", english: "Gye", element: "Water", yin_yang: "Yin" },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const EARTHLY_BRANCHES = [
|
|
26
|
+
{ hanja: "子", korean: "자", english: "Ja", animal: "Rat", element: "Water", yin_yang: "Yang", hours: "23:00-01:00" },
|
|
27
|
+
{ hanja: "丑", korean: "축", english: "Chuk",animal: "Ox", element: "Earth", yin_yang: "Yin", hours: "01:00-03:00" },
|
|
28
|
+
{ hanja: "寅", korean: "인", english: "In", animal: "Tiger", element: "Wood", yin_yang: "Yang", hours: "03:00-05:00" },
|
|
29
|
+
{ hanja: "卯", korean: "묘", english: "Myo", animal: "Rabbit", element: "Wood", yin_yang: "Yin", hours: "05:00-07:00" },
|
|
30
|
+
{ hanja: "辰", korean: "진", english: "Jin", animal: "Dragon", element: "Earth", yin_yang: "Yang", hours: "07:00-09:00" },
|
|
31
|
+
{ hanja: "巳", korean: "사", english: "Sa", animal: "Snake", element: "Fire", yin_yang: "Yin", hours: "09:00-11:00" },
|
|
32
|
+
{ hanja: "午", korean: "오", english: "O", animal: "Horse", element: "Fire", yin_yang: "Yang", hours: "11:00-13:00" },
|
|
33
|
+
{ hanja: "未", korean: "미", english: "Mi", animal: "Goat", element: "Earth", yin_yang: "Yin", hours: "13:00-15:00" },
|
|
34
|
+
{ hanja: "申", korean: "신", english: "Sin", animal: "Monkey", element: "Metal", yin_yang: "Yang", hours: "15:00-17:00" },
|
|
35
|
+
{ hanja: "酉", korean: "유", english: "Yu", animal: "Rooster", element: "Metal", yin_yang: "Yin", hours: "17:00-19:00" },
|
|
36
|
+
{ hanja: "戌", korean: "술", english: "Sul", animal: "Dog", element: "Earth", yin_yang: "Yang", hours: "19:00-21:00" },
|
|
37
|
+
{ hanja: "亥", korean: "해", english: "Hae", animal: "Pig", element: "Water", yin_yang: "Yin", hours: "21:00-23:00" },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const DAY_MASTER_DESCRIPTIONS = {
|
|
41
|
+
"甲": { name: "Gap Wood (Yang)", image: "A tall tree", traits: "Leadership, ambition, directness, pioneering spirit. Like a great tree growing upward — principled, sometimes stubborn, always reaching." },
|
|
42
|
+
"乙": { name: "Eul Wood (Yin)", image: "Grass and vines", traits: "Flexibility, diplomacy, artistic grace, adaptability. Like a vine wrapping around obstacles — gentle but persistent." },
|
|
43
|
+
"丙": { name: "Byeong Fire (Yang)", image: "The Sun", traits: "Charisma, optimism, warmth, visibility. Like the sun — generous, bright, attracts attention naturally." },
|
|
44
|
+
"丁": { name: "Jeong Fire (Yin)", image: "A candle flame", traits: "Warmth, sensitivity, refinement, inner passion. Like a candle — intimate, perceptive, illuminates subtly." },
|
|
45
|
+
"戊": { name: "Mu Earth (Yang)", image: "A mountain", traits: "Stability, reliability, patience, grounding presence. Like a mountain — immovable, trustworthy, sheltering." },
|
|
46
|
+
"己": { name: "Gi Earth (Yin)", image: "Fertile farmland", traits: "Nurturing, practical, resourceful, steady growth. Like rich soil — cultivates others, modest but productive." },
|
|
47
|
+
"庚": { name: "Gyeong Metal (Yang)", image: "A sword / raw iron", traits: "Decisiveness, discipline, justice, sharpness. Like a blade — principled, cuts through ambiguity, perfectionistic." },
|
|
48
|
+
"辛": { name: "Sin Metal (Yin)", image: "A polished jewel", traits: "Refinement, sensitivity, aesthetic sense, high standards. Like a gem — beautiful under pressure, discerning." },
|
|
49
|
+
"壬": { name: "Im Water (Yang)", image: "The ocean / a great river", traits: "Wisdom, breadth, ambition, adaptability. Like the ocean — deep, powerful, encompassing." },
|
|
50
|
+
"癸": { name: "Gye Water (Yin)", image: "Rain / morning dew", traits: "Intuition, empathy, creativity, quiet depth. Like rainwater — nourishing, perceptive, flows into hidden places." },
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// --- Core calculation functions ---
|
|
54
|
+
|
|
55
|
+
function getSolarMonth(month, day) {
|
|
56
|
+
const boundaries = [
|
|
57
|
+
[12, 7, 11], [11, 7, 10], [10, 8, 9], [9, 8, 8],
|
|
58
|
+
[8, 7, 7], [7, 7, 6], [6, 6, 5], [5, 6, 4],
|
|
59
|
+
[4, 5, 3], [3, 6, 2], [2, 4, 1], [1, 6, 12],
|
|
60
|
+
];
|
|
61
|
+
for (const [bm, bd, sajuMonth] of boundaries) {
|
|
62
|
+
if (month > bm || (month === bm && day >= bd)) return sajuMonth;
|
|
63
|
+
}
|
|
64
|
+
return 12;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getYearPillar(year, month, day) {
|
|
68
|
+
let sajuYear = year;
|
|
69
|
+
if (month < 2 || (month === 2 && day < 4)) sajuYear = year - 1;
|
|
70
|
+
const stemIndex = (sajuYear - 4) % 10;
|
|
71
|
+
const branchIndex = (sajuYear - 4) % 12;
|
|
72
|
+
return {
|
|
73
|
+
stem: HEAVENLY_STEMS[stemIndex >= 0 ? stemIndex : stemIndex + 10],
|
|
74
|
+
branch: EARTHLY_BRANCHES[branchIndex >= 0 ? branchIndex : branchIndex + 12],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getMonthPillar(yearStemIndex, month, day) {
|
|
79
|
+
const solarMonth = getSolarMonth(month, day);
|
|
80
|
+
const yearStemGroup = yearStemIndex % 5;
|
|
81
|
+
const monthStemStart = [2, 4, 6, 8, 0][yearStemGroup];
|
|
82
|
+
const monthStemIndex = (monthStemStart + (solarMonth - 1)) % 10;
|
|
83
|
+
const monthBranchIndex = (solarMonth + 1) % 12;
|
|
84
|
+
return { stem: HEAVENLY_STEMS[monthStemIndex], branch: EARTHLY_BRANCHES[monthBranchIndex], solarMonth };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getDayPillar(year, month, day) {
|
|
88
|
+
const a = Math.floor((14 - month) / 12);
|
|
89
|
+
const y = year + 4800 - a;
|
|
90
|
+
const m = month + 12 * a - 3;
|
|
91
|
+
const jdn = day + Math.floor((153 * m + 2) / 5) + 365 * y +
|
|
92
|
+
Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045;
|
|
93
|
+
const dayIndex = (jdn - 2451551) % 60;
|
|
94
|
+
const adj = ((dayIndex % 60) + 60) % 60;
|
|
95
|
+
return { stem: HEAVENLY_STEMS[adj % 10], branch: EARTHLY_BRANCHES[adj % 12], dayIndex: adj };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getHourPillar(dayStemIndex, hour) {
|
|
99
|
+
if (hour === null || hour === undefined) return null;
|
|
100
|
+
let bi;
|
|
101
|
+
if (hour >= 23 || hour < 1) bi = 0;
|
|
102
|
+
else if (hour < 3) bi = 1; else if (hour < 5) bi = 2;
|
|
103
|
+
else if (hour < 7) bi = 3; else if (hour < 9) bi = 4;
|
|
104
|
+
else if (hour < 11) bi = 5; else if (hour < 13) bi = 6;
|
|
105
|
+
else if (hour < 15) bi = 7; else if (hour < 17) bi = 8;
|
|
106
|
+
else if (hour < 19) bi = 9; else if (hour < 21) bi = 10;
|
|
107
|
+
else bi = 11;
|
|
108
|
+
const dg = dayStemIndex % 5;
|
|
109
|
+
const si = ([0, 2, 4, 6, 8][dg] + bi) % 10;
|
|
110
|
+
return { stem: HEAVENLY_STEMS[si], branch: EARTHLY_BRANCHES[bi] };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function analyzeElements(pillars) {
|
|
114
|
+
const counts = { Wood: 0, Fire: 0, Earth: 0, Metal: 0, Water: 0 };
|
|
115
|
+
const parts = [pillars.year, pillars.month, pillars.day];
|
|
116
|
+
if (pillars.hour) parts.push(pillars.hour);
|
|
117
|
+
for (const p of parts) { counts[p.stem.element]++; counts[p.branch.element]++; }
|
|
118
|
+
const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);
|
|
119
|
+
return {
|
|
120
|
+
counts,
|
|
121
|
+
dominant: { element: sorted[0][0], count: sorted[0][1] },
|
|
122
|
+
weakest: { element: sorted[sorted.length - 1][0], count: sorted[sorted.length - 1][1] },
|
|
123
|
+
total: Object.values(counts).reduce((a, b) => a + b, 0),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function calculateSaju(year, month, day, hour = null, gender = "other") {
|
|
128
|
+
const yearPillar = getYearPillar(year, month, day);
|
|
129
|
+
const yearStemIndex = HEAVENLY_STEMS.indexOf(yearPillar.stem);
|
|
130
|
+
const monthPillar = getMonthPillar(yearStemIndex, month, day);
|
|
131
|
+
const dayPillar = getDayPillar(year, month, day);
|
|
132
|
+
const dayStemIndex = HEAVENLY_STEMS.indexOf(dayPillar.stem);
|
|
133
|
+
const hourPillar = hour !== null ? getHourPillar(dayStemIndex, hour) : null;
|
|
134
|
+
|
|
135
|
+
const pillars = { year: yearPillar, month: monthPillar, day: dayPillar, hour: hourPillar };
|
|
136
|
+
const elements = analyzeElements(pillars);
|
|
137
|
+
|
|
138
|
+
const yearYY = yearPillar.stem.yin_yang;
|
|
139
|
+
const energyFlow =
|
|
140
|
+
(yearYY === "Yang" && gender === "male") ||
|
|
141
|
+
(yearYY === "Yin" && gender === "female")
|
|
142
|
+
? "Ascending" : "Descending";
|
|
143
|
+
|
|
144
|
+
const fmt = (p) => `${p.stem.hanja}${p.branch.hanja}`;
|
|
145
|
+
return {
|
|
146
|
+
pillars, elements, energyFlow, gender,
|
|
147
|
+
dayMaster: dayPillar.stem,
|
|
148
|
+
animal: yearPillar.branch.animal,
|
|
149
|
+
birthInfo: { year, month, day, hour },
|
|
150
|
+
display: {
|
|
151
|
+
hanja: [fmt(yearPillar), fmt(monthPillar), fmt(dayPillar), hourPillar ? fmt(hourPillar) : "??"].join(" "),
|
|
152
|
+
korean: [
|
|
153
|
+
`${yearPillar.stem.korean}${yearPillar.branch.korean}`,
|
|
154
|
+
`${monthPillar.stem.korean}${monthPillar.branch.korean}`,
|
|
155
|
+
`${dayPillar.stem.korean}${dayPillar.branch.korean}`,
|
|
156
|
+
hourPillar ? `${hourPillar.stem.korean}${hourPillar.branch.korean}` : "??",
|
|
157
|
+
].join(" "),
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function checkCompatibility(saju1, saju2) {
|
|
163
|
+
const generating = { Wood: "Fire", Fire: "Earth", Earth: "Metal", Metal: "Water", Water: "Wood" };
|
|
164
|
+
const controlling = { Wood: "Earth", Earth: "Water", Water: "Fire", Fire: "Metal", Metal: "Wood" };
|
|
165
|
+
|
|
166
|
+
const e1 = saju1.dayMaster.element;
|
|
167
|
+
const e2 = saju2.dayMaster.element;
|
|
168
|
+
|
|
169
|
+
let harmony = "Neutral";
|
|
170
|
+
if (generating[e1] === e2) harmony = "Generating (Person 1 feeds Person 2)";
|
|
171
|
+
else if (generating[e2] === e1) harmony = "Generating (Person 2 feeds Person 1)";
|
|
172
|
+
else if (e1 === e2) harmony = "Same element — mirror energy";
|
|
173
|
+
else if (controlling[e1] === e2) harmony = "Controlling (Person 1 challenges Person 2)";
|
|
174
|
+
else if (controlling[e2] === e1) harmony = "Controlling (Person 2 challenges Person 1)";
|
|
175
|
+
|
|
176
|
+
const b1 = saju1.pillars.year.branch.hanja;
|
|
177
|
+
const b2 = saju2.pillars.year.branch.hanja;
|
|
178
|
+
const sixHarmonies = [["子","丑"],["寅","亥"],["卯","戌"],["辰","酉"],["巳","申"],["午","未"]];
|
|
179
|
+
const sixClashes = [["子","午"],["丑","未"],["寅","申"],["卯","酉"],["辰","戌"],["巳","亥"]];
|
|
180
|
+
|
|
181
|
+
let zodiacRelation = "Neutral";
|
|
182
|
+
for (const [a, b] of sixHarmonies) {
|
|
183
|
+
if ((b1 === a && b2 === b) || (b1 === b && b2 === a)) { zodiacRelation = "Six Harmony (육합) — naturally complementary"; break; }
|
|
184
|
+
}
|
|
185
|
+
for (const [a, b] of sixClashes) {
|
|
186
|
+
if ((b1 === a && b2 === b) || (b1 === b && b2 === a)) { zodiacRelation = "Six Clash (육충) — tension that can spark growth"; break; }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const missing1 = Object.entries(saju1.elements.counts).filter(([, v]) => v === 0).map(([k]) => k);
|
|
190
|
+
const missing2 = Object.entries(saju2.elements.counts).filter(([, v]) => v === 0).map(([k]) => k);
|
|
191
|
+
const complements = [];
|
|
192
|
+
for (const el of missing1) { if (saju2.elements.counts[el] > 0) complements.push(`Person 2 supplies ${el} that Person 1 lacks`); }
|
|
193
|
+
for (const el of missing2) { if (saju1.elements.counts[el] > 0) complements.push(`Person 1 supplies ${el} that Person 2 lacks`); }
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
dayMasterHarmony: harmony,
|
|
197
|
+
person1: { dayMaster: `${saju1.dayMaster.hanja} (${saju1.dayMaster.element} ${saju1.dayMaster.yin_yang})`, animal: saju1.animal },
|
|
198
|
+
person2: { dayMaster: `${saju2.dayMaster.hanja} (${saju2.dayMaster.element} ${saju2.dayMaster.yin_yang})`, animal: saju2.animal },
|
|
199
|
+
zodiacRelation,
|
|
200
|
+
elementalComplements: complements.length > 0 ? complements : ["No direct element gaps filled — look to month/day branches for subtler links"],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ============================================
|
|
205
|
+
// MCP SERVER DEFINITION
|
|
206
|
+
// ============================================
|
|
207
|
+
|
|
208
|
+
const server = new McpServer({
|
|
209
|
+
name: "saju-from-seoul",
|
|
210
|
+
version: "1.0.0",
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// --- Tool 1: calculate_saju ---
|
|
214
|
+
server.tool(
|
|
215
|
+
"calculate_saju",
|
|
216
|
+
"Calculate Korean Four Pillars (Saju/사주) birth chart from a date of birth. Returns pillars in Hanja and Korean, Day Master, zodiac animal, Five Elements balance, and energy flow.",
|
|
217
|
+
{
|
|
218
|
+
year: z.number().int().min(1900).max(2100).describe("Birth year (solar calendar, e.g. 1995)"),
|
|
219
|
+
month: z.number().int().min(1).max(12).describe("Birth month (1-12, solar calendar)"),
|
|
220
|
+
day: z.number().int().min(1).max(31).describe("Birth day (1-31, solar calendar)"),
|
|
221
|
+
hour: z.number().int().min(0).max(23).optional().describe("Birth hour (0-23, optional — omit if unknown)"),
|
|
222
|
+
gender: z.enum(["male", "female", "other"]).optional().describe("Gender for energy flow calculation (default: other)"),
|
|
223
|
+
},
|
|
224
|
+
async ({ year, month, day, hour, gender }) => {
|
|
225
|
+
const saju = calculateSaju(year, month, day, hour ?? null, gender ?? "other");
|
|
226
|
+
return {
|
|
227
|
+
content: [{ type: "text", text: JSON.stringify(saju, null, 2) }],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// --- Tool 2: get_day_master ---
|
|
233
|
+
server.tool(
|
|
234
|
+
"get_day_master",
|
|
235
|
+
"Look up the personality profile for any of the 10 Day Masters (일간/日干) in Korean Saju astrology. Pass a Heavenly Stem character (甲乙丙丁戊己庚辛壬癸) to get the archetype, image, and personality traits.",
|
|
236
|
+
{
|
|
237
|
+
stem: z.string().length(1).describe("Heavenly Stem in Hanja (one of: 甲乙丙丁戊己庚辛壬癸)"),
|
|
238
|
+
},
|
|
239
|
+
async ({ stem }) => {
|
|
240
|
+
const info = DAY_MASTER_DESCRIPTIONS[stem];
|
|
241
|
+
if (!info) {
|
|
242
|
+
return { content: [{ type: "text", text: `Unknown stem "${stem}". Valid stems: 甲 乙 丙 丁 戊 己 庚 辛 壬 癸` }] };
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
content: [{ type: "text", text: JSON.stringify({ stem, ...info }, null, 2) }],
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// --- Tool 3: check_compatibility ---
|
|
251
|
+
server.tool(
|
|
252
|
+
"check_compatibility",
|
|
253
|
+
"Compare two people's Saju charts for compatibility. Analyzes Day Master element harmony, zodiac (Six Harmony / Six Clash), and elemental complements.",
|
|
254
|
+
{
|
|
255
|
+
person1_year: z.number().int(), person1_month: z.number().int(), person1_day: z.number().int(),
|
|
256
|
+
person1_hour: z.number().int().optional(), person1_gender: z.enum(["male","female","other"]).optional(),
|
|
257
|
+
person2_year: z.number().int(), person2_month: z.number().int(), person2_day: z.number().int(),
|
|
258
|
+
person2_hour: z.number().int().optional(), person2_gender: z.enum(["male","female","other"]).optional(),
|
|
259
|
+
},
|
|
260
|
+
async (args) => {
|
|
261
|
+
const s1 = calculateSaju(args.person1_year, args.person1_month, args.person1_day, args.person1_hour ?? null, args.person1_gender ?? "other");
|
|
262
|
+
const s2 = calculateSaju(args.person2_year, args.person2_month, args.person2_day, args.person2_hour ?? null, args.person2_gender ?? "other");
|
|
263
|
+
const compat = checkCompatibility(s1, s2);
|
|
264
|
+
return {
|
|
265
|
+
content: [{ type: "text", text: JSON.stringify({ person1_chart: s1.display.hanja, person2_chart: s2.display.hanja, ...compat }, null, 2) }],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
// --- Tool 4: list_stems_and_branches ---
|
|
271
|
+
server.tool(
|
|
272
|
+
"list_stems_and_branches",
|
|
273
|
+
"Reference table: all 10 Heavenly Stems (천간) and 12 Earthly Branches (지지) with Hanja, Korean, English, element, and yin/yang.",
|
|
274
|
+
{},
|
|
275
|
+
async () => {
|
|
276
|
+
return {
|
|
277
|
+
content: [{
|
|
278
|
+
type: "text",
|
|
279
|
+
text: JSON.stringify({ heavenly_stems: HEAVENLY_STEMS, earthly_branches: EARTHLY_BRANCHES }, null, 2),
|
|
280
|
+
}],
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
// --- Start ---
|
|
286
|
+
const transport = new StdioServerTransport();
|
|
287
|
+
await server.connect(transport);
|