@openephemeris/mcp-server 3.13.9 → 3.13.10
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 +2 -2
- package/config/dev-allowlist.json +28 -4
- package/dist/index.js +45 -0
- package/dist/oauth/dcr.d.ts +11 -0
- package/dist/oauth/dcr.js +49 -0
- package/dist/oauth/discovery.d.ts +11 -0
- package/dist/oauth/discovery.js +40 -0
- package/dist/oauth/pkce.d.ts +18 -0
- package/dist/oauth/pkce.js +34 -0
- package/dist/oauth/rate-limit.d.ts +20 -0
- package/dist/oauth/rate-limit.js +65 -0
- package/dist/oauth/store.d.ts +68 -0
- package/dist/oauth/store.js +247 -0
- package/dist/oauth/supabase-jwt.d.ts +39 -0
- package/dist/oauth/supabase-jwt.js +194 -0
- package/dist/oauth/token.d.ts +21 -0
- package/dist/oauth/token.js +213 -0
- package/dist/prompts.js +171 -159
- package/dist/server-sse.js +134 -36
- package/dist/tools/apps/bazi-app.d.ts +23 -0
- package/dist/tools/apps/bazi-app.js +224 -0
- package/dist/tools/apps/bi-wheel-app.d.ts +14 -7
- package/dist/tools/apps/bi-wheel-app.js +293 -108
- package/dist/tools/apps/bodygraph-app.js +38 -3
- package/dist/tools/apps/moon-phase-app.d.ts +19 -0
- package/dist/tools/apps/moon-phase-app.js +190 -0
- package/dist/tools/apps/transit-timeline-app.d.ts +20 -0
- package/dist/tools/apps/transit-timeline-app.js +243 -0
- package/dist/tools/apps/vedic-chart-app.d.ts +17 -0
- package/dist/tools/apps/vedic-chart-app.js +226 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/specialized/acg.js +2 -2
- package/dist/tools/specialized/bazi.js +367 -44
- package/dist/tools/specialized/bi_wheel.js +1 -1
- package/dist/tools/specialized/chart_wheel.js +1 -1
- package/dist/tools/specialized/comparative.js +4 -4
- package/dist/tools/specialized/eclipse.js +1 -1
- package/dist/tools/specialized/electional.js +4 -4
- package/dist/tools/specialized/ephemeris_core.js +2 -2
- package/dist/tools/specialized/ephemeris_extended.js +8 -8
- package/dist/tools/specialized/hd_bodygraph.js +1 -1
- package/dist/tools/specialized/hd_cycles.js +2 -2
- package/dist/tools/specialized/hd_group.js +2 -2
- package/dist/tools/specialized/human_design.js +1 -1
- package/dist/tools/specialized/moon.js +2 -2
- package/dist/tools/specialized/natal.js +1 -1
- package/dist/tools/specialized/progressed.js +1 -1
- package/dist/tools/specialized/relocation.js +1 -1
- package/dist/tools/specialized/returns.js +3 -3
- package/dist/tools/specialized/synastry.js +1 -1
- package/dist/tools/specialized/transits.js +1 -1
- package/dist/tools/specialized/vedic.js +1 -1
- package/dist/tools/specialized/venus_star_points.js +6 -6
- package/dist/ui/bazi.html +213 -0
- package/dist/ui/bi-wheel.html +762 -489
- package/dist/ui/bodygraph.html +1788 -1649
- package/dist/ui/chart-wheel.html +1772 -1593
- package/dist/ui/moon-phase.html +6758 -0
- package/dist/ui/transit-timeline.html +6835 -0
- package/dist/ui/vedic-chart.html +210 -0
- package/package.json +2 -2
|
@@ -1,68 +1,391 @@
|
|
|
1
1
|
import { registerTool } from "../index.js";
|
|
2
2
|
import { getActiveClient } from "../../backend/client.js";
|
|
3
|
+
function parseBaziArgs(args) {
|
|
4
|
+
let { year, month, day, hour } = args;
|
|
5
|
+
if (args.datetime && (!year || !month || !day)) {
|
|
6
|
+
const dt = new Date(args.datetime);
|
|
7
|
+
if (!isNaN(dt.getTime())) {
|
|
8
|
+
year = dt.getUTCFullYear();
|
|
9
|
+
month = dt.getUTCMonth() + 1;
|
|
10
|
+
day = dt.getUTCDate();
|
|
11
|
+
if (hour == null)
|
|
12
|
+
hour = dt.getUTCHours();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (!year || !month || !day) {
|
|
16
|
+
throw new Error("Provide year/month/day fields, or a datetime ISO string. " +
|
|
17
|
+
"Example: year=1987, month=7, day=15 OR datetime='1987-07-15T14:00:00'");
|
|
18
|
+
}
|
|
19
|
+
const out = { year, month, day };
|
|
20
|
+
if (hour != null)
|
|
21
|
+
out.hour = hour;
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
// Shared datetime input schema fragment — used across all BaZiRequest tools.
|
|
25
|
+
const DATETIME_PROPERTIES = {
|
|
26
|
+
year: {
|
|
27
|
+
type: "integer",
|
|
28
|
+
description: "Gregorian birth year, e.g. 1987.",
|
|
29
|
+
},
|
|
30
|
+
month: {
|
|
31
|
+
type: "integer",
|
|
32
|
+
description: "Birth month (1–12).",
|
|
33
|
+
},
|
|
34
|
+
day: {
|
|
35
|
+
type: "integer",
|
|
36
|
+
description: "Birth day of month (1–31).",
|
|
37
|
+
},
|
|
38
|
+
hour: {
|
|
39
|
+
type: "integer",
|
|
40
|
+
description: "Birth hour (0–23). Optional, defaults to 12 (noon). " +
|
|
41
|
+
"Chinese shí hours are 2-hour blocks — precision within a 2-hour window is sufficient.",
|
|
42
|
+
},
|
|
43
|
+
datetime: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "Alternative to year/month/day: ISO 8601 datetime (e.g. '1987-07-15T14:00:00'). " +
|
|
46
|
+
"year/month/day/hour are extracted automatically. Use this OR the individual fields.",
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
// Shared visual input schema fragment — mirrors natal.ts pattern.
|
|
50
|
+
const VISUAL_PROPERTIES = {
|
|
51
|
+
include_visual: {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
description: "When true, the response includes a rendered SVG chart of the Four Pillars. " +
|
|
54
|
+
"Costs an extra 2 credits (total 3 credits). Ideal for delivering a visual alongside text analysis.",
|
|
55
|
+
},
|
|
56
|
+
visual_config: {
|
|
57
|
+
type: "object",
|
|
58
|
+
description: "Optional rendering preferences. Only relevant when include_visual=true.",
|
|
59
|
+
properties: {
|
|
60
|
+
theme: {
|
|
61
|
+
type: "string",
|
|
62
|
+
enum: ["light", "dark", "mono"],
|
|
63
|
+
description: "Chart color theme. Defaults to 'light'.",
|
|
64
|
+
},
|
|
65
|
+
size: {
|
|
66
|
+
type: "integer",
|
|
67
|
+
description: "Chart width in pixels (400–1600). Defaults to 800.",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
additionalProperties: false,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
// buildVisualBody appends include_visual and visual_config to a request body
|
|
74
|
+
// when the caller passes include_visual=true.
|
|
75
|
+
function applyVisualConfig(body, args) {
|
|
76
|
+
if (args.include_visual) {
|
|
77
|
+
body.include_visual = true;
|
|
78
|
+
body.visual_config = {
|
|
79
|
+
format: "svg", // always SVG for MCP — no resvg dependency in prod
|
|
80
|
+
theme: args.visual_config?.theme ?? "light",
|
|
81
|
+
size: args.visual_config?.size ?? 800,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
86
|
+
// chinese_bazi — base Four Pillars chart
|
|
87
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
3
88
|
registerTool({
|
|
4
89
|
name: "chinese_bazi",
|
|
5
|
-
description: "Calculate a Chinese
|
|
6
|
-
"and Hour pillars, each
|
|
7
|
-
"the Day Master element
|
|
8
|
-
"CREDIT COST: 1 credit
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
90
|
+
description: "Calculate a Chinese BaZi (四柱命盘 Four Pillars of Destiny) chart. Returns the Year, Month, Day, " +
|
|
91
|
+
"and Hour pillars, each with a Heavenly Stem (天干) and Earthly Branch (地支) pair. " +
|
|
92
|
+
"Includes the Day Master element and basic metadata.\n\n" +
|
|
93
|
+
"CREDIT COST: 1 credit (3 credits when include_visual=true).\n\n" +
|
|
94
|
+
"Set include_visual=true to receive a rendered SVG chart alongside the text data.\n\n" +
|
|
95
|
+
"For deep analysis, follow up with:\n" +
|
|
96
|
+
" • bazi_ten_gods() — Ten Gods (十神) per pillar including hidden stems\n" +
|
|
97
|
+
" • bazi_element_balance() — Weighted Wu Xing (五行) element scores + Yong Shen\n" +
|
|
98
|
+
" • bazi_luck_pillars() — 8 Da Yun 10-year luck cycles\n\n" +
|
|
99
|
+
"EXAMPLE: BaZi chart for someone born July 15, 1987 at 2 PM:\n" +
|
|
100
|
+
" year=1987, month=7, day=15, hour=14",
|
|
101
|
+
inputSchema: {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {
|
|
104
|
+
...DATETIME_PROPERTIES,
|
|
105
|
+
...VISUAL_PROPERTIES,
|
|
106
|
+
},
|
|
107
|
+
additionalProperties: false,
|
|
108
|
+
},
|
|
109
|
+
annotations: { title: "BaZi Four Pillars Chart", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
110
|
+
handler: async (args) => {
|
|
111
|
+
const body = { ...parseBaziArgs(args) };
|
|
112
|
+
applyVisualConfig(body, args);
|
|
113
|
+
return await getActiveClient().request("POST", "/chinese/bazi", { data: body });
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
// bazi_ten_gods — Ten Gods (十神) analysis
|
|
118
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
119
|
+
registerTool({
|
|
120
|
+
name: "bazi_ten_gods",
|
|
121
|
+
description: "Calculate the Ten Gods (十神 Shí Shén) for a BaZi chart. Each of the 10 gods describes " +
|
|
122
|
+
"the relationship between the Day Master (日主, the person's core identity element) and " +
|
|
123
|
+
"every other Heavenly Stem in the chart — including visible stems and the hidden stems " +
|
|
124
|
+
"stored within each Earthly Branch.\n\n" +
|
|
125
|
+
"The 10 gods and their categories:\n" +
|
|
126
|
+
" COMPANION: Friend (比肩), Rob Wealth (劫财)\n" +
|
|
127
|
+
" OUTPUT: Eating God (食神), Hurting Officer (伤官)\n" +
|
|
128
|
+
" WEALTH: Indirect Wealth (偏财), Direct Wealth (正财)\n" +
|
|
129
|
+
" POWER: Seven Killings (七杀), Direct Officer (正官)\n" +
|
|
130
|
+
" RESOURCE: Indirect Resource (偏印), Direct Resource (正印)\n\n" +
|
|
131
|
+
"The Day Pillar itself has no Ten God (it IS the Day Master). " +
|
|
132
|
+
"Hidden stems in the Day Branch still receive Ten God labels.\n\n" +
|
|
133
|
+
"CREDIT COST: 1 credit (3 credits when include_visual=true).\n\n" +
|
|
134
|
+
"Set include_visual=true to receive an SVG Four Pillars chart alongside the Ten Gods data.\n\n" +
|
|
135
|
+
"EXAMPLE: Ten Gods for someone born July 15, 1987 at 2 PM:\n" +
|
|
12
136
|
" year=1987, month=7, day=15, hour=14",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: "object",
|
|
139
|
+
properties: {
|
|
140
|
+
...DATETIME_PROPERTIES,
|
|
141
|
+
...VISUAL_PROPERTIES,
|
|
142
|
+
},
|
|
143
|
+
additionalProperties: false,
|
|
144
|
+
},
|
|
145
|
+
annotations: { title: "BaZi Day Master Analysis", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
146
|
+
handler: async (args) => {
|
|
147
|
+
const body = { ...parseBaziArgs(args) };
|
|
148
|
+
applyVisualConfig(body, args);
|
|
149
|
+
return await getActiveClient().request("POST", "/chinese/bazi/ten-gods", { data: body });
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
153
|
+
// bazi_element_balance — Wu Xing (五行) weighted element analysis
|
|
154
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
155
|
+
registerTool({
|
|
156
|
+
name: "bazi_element_balance",
|
|
157
|
+
description: "Calculate the weighted Wu Xing (五行 Five Elements) distribution for a BaZi chart. " +
|
|
158
|
+
"Returns percentage scores for Wood, Fire, Earth, Metal, and Water across all chart positions.\n\n" +
|
|
159
|
+
"Scoring is weighted — hidden stems inside Earthly Branches count toward element totals:\n" +
|
|
160
|
+
" • Visible Heavenly Stems: 1.0 weight each (4 positions)\n" +
|
|
161
|
+
" • Hidden Stem main (本气): 1.0 weight\n" +
|
|
162
|
+
" • Hidden Stem secondary (中气): 0.6 weight\n" +
|
|
163
|
+
" • Hidden Stem residual (余气): 0.3 weight\n\n" +
|
|
164
|
+
"Also returns:\n" +
|
|
165
|
+
" • Day Master strength: 'strong' (旺, ≥50% own+resource elements) or 'weak' (弱)\n" +
|
|
166
|
+
" • Yong Shen (用神): the favorable element — what the chart needs most\n\n" +
|
|
167
|
+
"CREDIT COST: 1 credit (3 credits when include_visual=true).\n\n" +
|
|
168
|
+
"Set include_visual=true to receive an SVG Four Pillars chart alongside the element data.\n\n" +
|
|
169
|
+
"EXAMPLE: Element balance for 1987-07-15 at 14:00:\n" +
|
|
170
|
+
" year=1987, month=7, day=15, hour=14",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
type: "object",
|
|
173
|
+
properties: {
|
|
174
|
+
...DATETIME_PROPERTIES,
|
|
175
|
+
...VISUAL_PROPERTIES,
|
|
176
|
+
},
|
|
177
|
+
additionalProperties: false,
|
|
178
|
+
},
|
|
179
|
+
annotations: { title: "BaZi Clash & Combination", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
180
|
+
handler: async (args) => {
|
|
181
|
+
const body = { ...parseBaziArgs(args) };
|
|
182
|
+
applyVisualConfig(body, args);
|
|
183
|
+
return await getActiveClient().request("POST", "/chinese/bazi/element-balance", { data: body });
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
187
|
+
// bazi_luck_pillars — Da Yun (大运) 10-year luck cycles
|
|
188
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
189
|
+
registerTool({
|
|
190
|
+
name: "bazi_luck_pillars",
|
|
191
|
+
description: "Calculate the 8 Da Yun (大运) 10-year luck pillar cycles for a BaZi chart. " +
|
|
192
|
+
"Luck pillars sequence through the sexagenary cycle starting from a person's " +
|
|
193
|
+
"'starting age' (起运岁数), calculated by counting days from birth to the nearest " +
|
|
194
|
+
"solar term boundary (Jié 节). Each 3 days = 1 year of starting age (Zi Ping rule).\n\n" +
|
|
195
|
+
"Direction (forward/backward) depends on gender and year polarity:\n" +
|
|
196
|
+
" • Male in Yang year → forward\n" +
|
|
197
|
+
" • Male in Yin year → backward\n" +
|
|
198
|
+
" • Female in Yang year → backward\n" +
|
|
199
|
+
" • Female in Yin year → forward\n\n" +
|
|
200
|
+
"gender is REQUIRED — the direction of luck pillars is gender-dependent.\n\n" +
|
|
201
|
+
"Returns: starting_age, direction, direction_reason, and 8 pillars each with:\n" +
|
|
202
|
+
" stem, branch, Chinese characters, element, start_age, end_age\n\n" +
|
|
203
|
+
"CREDIT COST: 1 credit per call.\n\n" +
|
|
204
|
+
"EXAMPLE: Luck pillars for a female born July 15, 1987 at 2 PM:\n" +
|
|
205
|
+
" year=1987, month=7, day=15, hour=14, gender='female'",
|
|
206
|
+
inputSchema: {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties: {
|
|
209
|
+
...DATETIME_PROPERTIES,
|
|
210
|
+
gender: {
|
|
211
|
+
type: "string",
|
|
212
|
+
enum: ["male", "female"],
|
|
213
|
+
description: "Required. Determines the direction of Da Yun progression. " +
|
|
214
|
+
"Male in Yang year / Female in Yin year → forward. " +
|
|
215
|
+
"Male in Yin year / Female in Yang year → backward.",
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
required: ["gender"],
|
|
219
|
+
additionalProperties: false,
|
|
220
|
+
},
|
|
221
|
+
annotations: { title: "BaZi Ten Gods", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
222
|
+
handler: async (args) => {
|
|
223
|
+
if (!args.gender) {
|
|
224
|
+
throw new Error("gender is required for luck pillar calculation. " +
|
|
225
|
+
"Provide 'male' or 'female' — the direction of Da Yun depends on gender and year polarity.");
|
|
226
|
+
}
|
|
227
|
+
const { year, month, day, hour } = parseBaziArgs(args);
|
|
228
|
+
const body = { year, month, day, gender: args.gender };
|
|
229
|
+
if (hour != null)
|
|
230
|
+
body.hour = hour;
|
|
231
|
+
return await getActiveClient().request("POST", "/chinese/bazi/luck-pillars", { data: body });
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
235
|
+
// bazi_annual_pillar — sexagenary year pillar + NaYin
|
|
236
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
237
|
+
registerTool({
|
|
238
|
+
name: "bazi_annual_pillar",
|
|
239
|
+
description: "Look up the sexagenary pillar for any Gregorian year (1–9999). Returns the Heavenly Stem, " +
|
|
240
|
+
"Earthly Branch, Chinese characters, zodiac animal, element, polarity, and NaYin (纳音) " +
|
|
241
|
+
"poetic resonance image.\n\n" +
|
|
242
|
+
"NaYin maps each pair in the 60-cycle sexagenary sequence to one of 30 elemental images " +
|
|
243
|
+
"(e.g. '海中金 Metal in the Sea', '炉中火 Fire in the Furnace'). It is traditionally " +
|
|
244
|
+
"applied to the Year and Day pillars to reveal deeper elemental character.\n\n" +
|
|
245
|
+
"Use this to:\n" +
|
|
246
|
+
" • Identify the energetic quality of any given year\n" +
|
|
247
|
+
" • Determine a person's birth year pillar for compatibility context\n" +
|
|
248
|
+
" • Find the NaYin element for year or day interpretations\n\n" +
|
|
249
|
+
"CREDIT COST: 1 credit per call.\n\n" +
|
|
250
|
+
"EXAMPLE: Year pillar for 2025:\n" +
|
|
251
|
+
" year=2025",
|
|
13
252
|
inputSchema: {
|
|
14
253
|
type: "object",
|
|
15
254
|
properties: {
|
|
16
255
|
year: {
|
|
17
256
|
type: "integer",
|
|
18
|
-
description: "Gregorian
|
|
257
|
+
description: "Gregorian year (1–9999). e.g. 2025.",
|
|
19
258
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
259
|
+
},
|
|
260
|
+
required: ["year"],
|
|
261
|
+
additionalProperties: false,
|
|
262
|
+
},
|
|
263
|
+
annotations: { title: "BaZi Luck Pillars", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
264
|
+
handler: async (args) => {
|
|
265
|
+
if (!args.year) {
|
|
266
|
+
throw new Error("year is required. Provide a Gregorian year (1–9999), e.g. year=2025.");
|
|
267
|
+
}
|
|
268
|
+
return await getActiveClient().request("POST", "/chinese/bazi/annual-pillar", {
|
|
269
|
+
data: { year: args.year },
|
|
270
|
+
});
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
274
|
+
// bazi_compatibility — pair chart scoring
|
|
275
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
276
|
+
registerTool({
|
|
277
|
+
name: "bazi_compatibility",
|
|
278
|
+
description: "Calculate BaZi compatibility between two people by comparing their Four Pillars charts. " +
|
|
279
|
+
"Returns a score 0–100, an assessment, and a breakdown of the harmony/clash/penalty " +
|
|
280
|
+
"factors found between the two charts.\n\n" +
|
|
281
|
+
"Scoring factors include:\n" +
|
|
282
|
+
" +5 Six Harmony (六合 Liù Hé) — branch pair bonds\n" +
|
|
283
|
+
" +8 Three Harmonies (三合 Sān Hé) — branch trinity formations\n" +
|
|
284
|
+
" -8 Six Clash (六冲 Liù Chōng) — opposing branch pairs\n" +
|
|
285
|
+
" -5 Six Harm (六害 Liù Hài) — branch interference pairs\n" +
|
|
286
|
+
" -6 Three Penalties (三刑 Sān Xíng) — branch penalty formations\n\n" +
|
|
287
|
+
"Assessment grades:\n" +
|
|
288
|
+
" 90–100: excellent | 70–89: good | 50–69: moderate | below 50: challenging\n\n" +
|
|
289
|
+
"CREDIT COST: 2 credits per call.\n\n" +
|
|
290
|
+
"EXAMPLE: Compatibility between two people:\n" +
|
|
291
|
+
" chart_a_year=1987, chart_a_month=7, chart_a_day=15, chart_a_hour=14\n" +
|
|
292
|
+
" chart_b_year=1990, chart_b_month=3, chart_b_day=22, chart_b_hour=8",
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: "object",
|
|
295
|
+
properties: {
|
|
296
|
+
// Chart A
|
|
297
|
+
chart_a_year: { type: "integer", description: "Chart A birth year." },
|
|
298
|
+
chart_a_month: { type: "integer", description: "Chart A birth month (1–12)." },
|
|
299
|
+
chart_a_day: { type: "integer", description: "Chart A birth day (1–31)." },
|
|
300
|
+
chart_a_hour: { type: "integer", description: "Chart A birth hour (0–23). Optional." },
|
|
301
|
+
chart_a_datetime: {
|
|
302
|
+
type: "string",
|
|
303
|
+
description: "Chart A alternative: ISO 8601 datetime. Extracts year/month/day/hour automatically.",
|
|
23
304
|
},
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
305
|
+
// Chart B
|
|
306
|
+
chart_b_year: { type: "integer", description: "Chart B birth year." },
|
|
307
|
+
chart_b_month: { type: "integer", description: "Chart B birth month (1–12)." },
|
|
308
|
+
chart_b_day: { type: "integer", description: "Chart B birth day (1–31)." },
|
|
309
|
+
chart_b_hour: { type: "integer", description: "Chart B birth hour (0–23). Optional." },
|
|
310
|
+
chart_b_datetime: {
|
|
311
|
+
type: "string",
|
|
312
|
+
description: "Chart B alternative: ISO 8601 datetime. Extracts year/month/day/hour automatically.",
|
|
27
313
|
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
314
|
+
},
|
|
315
|
+
additionalProperties: false,
|
|
316
|
+
},
|
|
317
|
+
annotations: { title: "BaZi Annual Pillars", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
318
|
+
handler: async (args) => {
|
|
319
|
+
const chartAComponents = parseBaziArgs({
|
|
320
|
+
year: args.chart_a_year,
|
|
321
|
+
month: args.chart_a_month,
|
|
322
|
+
day: args.chart_a_day,
|
|
323
|
+
hour: args.chart_a_hour,
|
|
324
|
+
datetime: args.chart_a_datetime,
|
|
325
|
+
});
|
|
326
|
+
const chartBComponents = parseBaziArgs({
|
|
327
|
+
year: args.chart_b_year,
|
|
328
|
+
month: args.chart_b_month,
|
|
329
|
+
day: args.chart_b_day,
|
|
330
|
+
hour: args.chart_b_hour,
|
|
331
|
+
datetime: args.chart_b_datetime,
|
|
332
|
+
});
|
|
333
|
+
return await getActiveClient().request("POST", "/chinese/bazi/compatibility", {
|
|
334
|
+
data: {
|
|
335
|
+
chart_a: chartAComponents,
|
|
336
|
+
chart_b: chartBComponents,
|
|
32
337
|
},
|
|
33
|
-
|
|
338
|
+
});
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
342
|
+
// bazi_chart — dedicated Four Pillars SVG chart visual
|
|
343
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
344
|
+
registerTool({
|
|
345
|
+
name: "bazi_chart",
|
|
346
|
+
description: "Render a self-contained SVG chart of a BaZi Four Pillars chart. " +
|
|
347
|
+
"Returns a production-quality visualization showing:\n" +
|
|
348
|
+
" • All four pillars (Year, Month, Day, Hour) with Heavenly Stems and Earthly Branches\n" +
|
|
349
|
+
" • Ten Gods (十神) labels for each non-Day pillar\n" +
|
|
350
|
+
" • Wu Xing (五行) element balance bar chart\n" +
|
|
351
|
+
" • Day Master identity with element and polarity\n\n" +
|
|
352
|
+
"The chart is rendered in the Open Ephemeris design system (OKLCH color tokens) " +
|
|
353
|
+
"and is fully self-contained — suitable for embedding or display without dependencies.\n\n" +
|
|
354
|
+
"Theme options: 'light' (default), 'dark', 'mono'.\n\n" +
|
|
355
|
+
"CREDIT COST: 3 credits per call (1 base + 2 visual render).\n\n" +
|
|
356
|
+
"Use this tool when the user asks to 'show', 'visualize', 'draw', or 'render' " +
|
|
357
|
+
"a BaZi / Four Pillars chart. For text-only data, use chinese_bazi() instead.\n\n" +
|
|
358
|
+
"EXAMPLE: Render a chart for someone born July 15, 1987 at 2 PM:\n" +
|
|
359
|
+
" year=1987, month=7, day=15, hour=14",
|
|
360
|
+
inputSchema: {
|
|
361
|
+
type: "object",
|
|
362
|
+
properties: {
|
|
363
|
+
...DATETIME_PROPERTIES,
|
|
364
|
+
theme: {
|
|
34
365
|
type: "string",
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
366
|
+
enum: ["light", "dark", "mono"],
|
|
367
|
+
description: "Chart color theme. Defaults to 'light'.",
|
|
368
|
+
},
|
|
369
|
+
size: {
|
|
370
|
+
type: "integer",
|
|
371
|
+
description: "Chart width in pixels (400–1600). Defaults to 800.",
|
|
38
372
|
},
|
|
39
373
|
},
|
|
40
374
|
additionalProperties: false,
|
|
41
375
|
},
|
|
42
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
376
|
+
annotations: { title: "BaZi Compatibility", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
43
377
|
handler: async (args) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
month = dt.getUTCMonth() + 1;
|
|
54
|
-
day = dt.getUTCDate();
|
|
55
|
-
if (hour == null)
|
|
56
|
-
hour = dt.getUTCHours();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (!year || !month || !day) {
|
|
60
|
-
throw new Error("Either provide year/month/day fields, or a datetime string. " +
|
|
61
|
-
"Example: year=1987, month=7, day=15 OR datetime='1987-07-15T14:00:00'");
|
|
62
|
-
}
|
|
63
|
-
const body = { year, month, day };
|
|
378
|
+
const { year, month, day, hour } = parseBaziArgs(args);
|
|
379
|
+
const body = {
|
|
380
|
+
year, month, day,
|
|
381
|
+
visual_config: {
|
|
382
|
+
format: "svg",
|
|
383
|
+
theme: args.theme ?? "light",
|
|
384
|
+
size: args.size ?? 800,
|
|
385
|
+
},
|
|
386
|
+
};
|
|
64
387
|
if (hour != null)
|
|
65
388
|
body.hour = hour;
|
|
66
|
-
return await getActiveClient().request("POST", "/chinese/bazi", { data: body });
|
|
389
|
+
return await getActiveClient().request("POST", "/chinese/bazi/chart", { data: body });
|
|
67
390
|
},
|
|
68
391
|
});
|
|
@@ -38,7 +38,7 @@ registerTool({
|
|
|
38
38
|
required: ["datetime_a", "latitude_a", "longitude_a", "datetime_b", "latitude_b", "longitude_b"],
|
|
39
39
|
additionalProperties: false,
|
|
40
40
|
},
|
|
41
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
41
|
+
annotations: { title: "Bi-Wheel Chart", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
42
42
|
handler: async (args) => {
|
|
43
43
|
validateRequired(args, ["datetime_a", "latitude_a", "longitude_a", "datetime_b", "latitude_b", "longitude_b"]);
|
|
44
44
|
const body = {
|
|
@@ -46,7 +46,7 @@ registerTool({
|
|
|
46
46
|
required: ["datetime", "latitude", "longitude"],
|
|
47
47
|
additionalProperties: false,
|
|
48
48
|
},
|
|
49
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
49
|
+
annotations: { title: "Chart Wheel SVG", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
50
50
|
handler: async (args) => {
|
|
51
51
|
validateRequired(args, ["datetime", "latitude", "longitude"]);
|
|
52
52
|
const body = {
|
|
@@ -42,7 +42,7 @@ registerTool({
|
|
|
42
42
|
],
|
|
43
43
|
additionalProperties: false,
|
|
44
44
|
},
|
|
45
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
45
|
+
annotations: { title: "Davison Relationship Chart", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
46
46
|
handler: async (args) => {
|
|
47
47
|
validateRequired(args, [
|
|
48
48
|
"person_a_datetime", "person_a_latitude", "person_a_longitude",
|
|
@@ -87,7 +87,7 @@ registerTool({
|
|
|
87
87
|
],
|
|
88
88
|
additionalProperties: false,
|
|
89
89
|
},
|
|
90
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
90
|
+
annotations: { title: "Midpoint Composite Chart", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
91
91
|
handler: async (args) => {
|
|
92
92
|
validateRequired(args, [
|
|
93
93
|
"person_a_datetime", "person_a_latitude", "person_a_longitude",
|
|
@@ -132,7 +132,7 @@ registerTool({
|
|
|
132
132
|
],
|
|
133
133
|
additionalProperties: false,
|
|
134
134
|
},
|
|
135
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
135
|
+
annotations: { title: "Planet Compatibility Score", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
136
136
|
handler: async (args) => {
|
|
137
137
|
validateRequired(args, [
|
|
138
138
|
"person_a_datetime", "person_a_latitude", "person_a_longitude",
|
|
@@ -174,7 +174,7 @@ registerTool({
|
|
|
174
174
|
required: ["natal_datetime", "natal_latitude", "natal_longitude"],
|
|
175
175
|
additionalProperties: false,
|
|
176
176
|
},
|
|
177
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
177
|
+
annotations: { title: "Composite Chart Aspects", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
178
178
|
handler: async (args) => {
|
|
179
179
|
validateRequired(args, ["natal_datetime", "natal_latitude", "natal_longitude"]);
|
|
180
180
|
// Backend expects: { subject: {...}, transit_datetime?: {...} }
|
|
@@ -40,7 +40,7 @@ registerTool({
|
|
|
40
40
|
required: ["eclipse_type"],
|
|
41
41
|
additionalProperties: false,
|
|
42
42
|
},
|
|
43
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
43
|
+
annotations: { title: "Eclipse Finder", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
44
44
|
handler: async (args) => {
|
|
45
45
|
validateRequired(args, ["eclipse_type"]);
|
|
46
46
|
validateCoordinates(args, "latitude", "longitude");
|
|
@@ -56,7 +56,7 @@ registerTool({
|
|
|
56
56
|
required: ["start_date", "end_date", "latitude", "longitude"],
|
|
57
57
|
additionalProperties: false,
|
|
58
58
|
},
|
|
59
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
59
|
+
annotations: { title: "Electional Timing Window", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
60
60
|
handler: async (args) => {
|
|
61
61
|
validateRequired(args, ["start_date", "end_date", "latitude", "longitude"]);
|
|
62
62
|
const query = {
|
|
@@ -106,7 +106,7 @@ registerTool({
|
|
|
106
106
|
required: [],
|
|
107
107
|
additionalProperties: false,
|
|
108
108
|
},
|
|
109
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
109
|
+
annotations: { title: "Moment Quality Analysis", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
110
110
|
handler: async (args) => {
|
|
111
111
|
const query = {};
|
|
112
112
|
if (args.date)
|
|
@@ -157,7 +157,7 @@ registerTool({
|
|
|
157
157
|
required: [],
|
|
158
158
|
additionalProperties: false,
|
|
159
159
|
},
|
|
160
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
160
|
+
annotations: { title: "Retrograde Station Tracker", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
161
161
|
handler: async (args) => {
|
|
162
162
|
const query = {};
|
|
163
163
|
// Map human-readable planet names to IDs if needed
|
|
@@ -216,7 +216,7 @@ registerTool({
|
|
|
216
216
|
required: [],
|
|
217
217
|
additionalProperties: false,
|
|
218
218
|
},
|
|
219
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
219
|
+
annotations: { title: "Active Aspects Search", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
220
220
|
handler: async (args) => {
|
|
221
221
|
const query = {};
|
|
222
222
|
if (args.date)
|
|
@@ -34,7 +34,7 @@ registerTool({
|
|
|
34
34
|
required: ["planet_id", "datetime"],
|
|
35
35
|
additionalProperties: false,
|
|
36
36
|
},
|
|
37
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
37
|
+
annotations: { title: "Planet Position", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
38
38
|
handler: async (args) => {
|
|
39
39
|
validateRequired(args, ["planet_id", "datetime"]);
|
|
40
40
|
const body = {
|
|
@@ -81,7 +81,7 @@ registerTool({
|
|
|
81
81
|
required: ["datetime", "latitude", "longitude"],
|
|
82
82
|
additionalProperties: false,
|
|
83
83
|
},
|
|
84
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
84
|
+
annotations: { title: "House Cusps", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
85
85
|
handler: async (args) => {
|
|
86
86
|
validateRequired(args, ["datetime", "latitude", "longitude"]);
|
|
87
87
|
const body = {
|
|
@@ -42,7 +42,7 @@ registerTool({
|
|
|
42
42
|
required: ["subjects"],
|
|
43
43
|
additionalProperties: false,
|
|
44
44
|
},
|
|
45
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
45
|
+
annotations: { title: "Natal Batch Charts", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
46
46
|
handler: async (args) => {
|
|
47
47
|
validateRequired(args, ["subjects"]);
|
|
48
48
|
const items = args.subjects.map((s) => ({
|
|
@@ -65,7 +65,7 @@ registerTool({
|
|
|
65
65
|
required: ["datetime"],
|
|
66
66
|
additionalProperties: false,
|
|
67
67
|
},
|
|
68
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
68
|
+
annotations: { title: "Essential Dignities", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
69
69
|
handler: async (args) => {
|
|
70
70
|
validateRequired(args, ["datetime"]);
|
|
71
71
|
return await getActiveClient().post("/ephemeris/dignities", {
|
|
@@ -92,7 +92,7 @@ registerTool({
|
|
|
92
92
|
required: ["datetime"],
|
|
93
93
|
additionalProperties: false,
|
|
94
94
|
},
|
|
95
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
95
|
+
annotations: { title: "Retrograde Status", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
96
96
|
handler: async (args) => {
|
|
97
97
|
validateRequired(args, ["datetime"]);
|
|
98
98
|
const client = getActiveClient();
|
|
@@ -141,7 +141,7 @@ registerTool({
|
|
|
141
141
|
required: ["datetime", "latitude", "longitude"],
|
|
142
142
|
additionalProperties: false,
|
|
143
143
|
},
|
|
144
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
144
|
+
annotations: { title: "Midpoints", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
145
145
|
handler: async (args) => {
|
|
146
146
|
validateRequired(args, ["datetime", "latitude", "longitude"]);
|
|
147
147
|
return await getActiveClient().post("/ephemeris/midpoints", {
|
|
@@ -171,7 +171,7 @@ registerTool({
|
|
|
171
171
|
required: ["datetime"],
|
|
172
172
|
additionalProperties: false,
|
|
173
173
|
},
|
|
174
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
174
|
+
annotations: { title: "Fixed Stars", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
175
175
|
handler: async (args) => {
|
|
176
176
|
validateRequired(args, ["datetime"]);
|
|
177
177
|
const body = {
|
|
@@ -231,7 +231,7 @@ registerTool({
|
|
|
231
231
|
required: ["datetime", "latitude", "longitude"],
|
|
232
232
|
additionalProperties: false,
|
|
233
233
|
},
|
|
234
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
234
|
+
annotations: { title: "Hermetic Lots", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
235
235
|
handler: async (args) => {
|
|
236
236
|
validateRequired(args, ["datetime", "latitude", "longitude"]);
|
|
237
237
|
return await getActiveClient().post("/ephemeris/hermetic-lots", {
|
|
@@ -257,7 +257,7 @@ registerTool({
|
|
|
257
257
|
required: ["datetime", "latitude", "longitude"],
|
|
258
258
|
additionalProperties: false,
|
|
259
259
|
},
|
|
260
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
260
|
+
annotations: { title: "Angles & Points", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
261
261
|
handler: async (args) => {
|
|
262
262
|
validateRequired(args, ["datetime", "latitude", "longitude"]);
|
|
263
263
|
return await getActiveClient().post("/ephemeris/angles-points", {
|
|
@@ -285,7 +285,7 @@ registerTool({
|
|
|
285
285
|
required: ["longitude_1", "longitude_2"],
|
|
286
286
|
additionalProperties: false,
|
|
287
287
|
},
|
|
288
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
288
|
+
annotations: { title: "Aspect Check", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
289
289
|
handler: async (args) => {
|
|
290
290
|
validateRequired(args, ["longitude_1", "longitude_2"]);
|
|
291
291
|
// Backend field names are longitude1 / longitude2 (no underscore)
|
|
@@ -39,7 +39,7 @@ registerTool({
|
|
|
39
39
|
required: ["datetime"],
|
|
40
40
|
additionalProperties: false,
|
|
41
41
|
},
|
|
42
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
42
|
+
annotations: { title: "Human Design Bodygraph", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
43
43
|
handler: async (args) => {
|
|
44
44
|
validateRequired(args, ["datetime"]);
|
|
45
45
|
const body = {
|