@openephemeris/mcp-server 4.15.1 → 4.17.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/CHANGELOG.md +33 -0
- package/README.md +26 -27
- package/config/dev-allowlist.json +31 -19
- package/dist/backend/client.d.ts +2 -0
- package/dist/backend/client.js +15 -10
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +5 -3
- package/dist/prompts.js +2 -2
- package/dist/tools/apps/bazi-app.js +10 -10
- package/dist/tools/apps/bi-wheel-app.js +14 -14
- package/dist/tools/apps/bodygraph-app.js +35 -29
- package/dist/tools/apps/chart-wheel-app.js +12 -9
- package/dist/tools/apps/location-tools.js +17 -4
- package/dist/tools/apps/moon-phase-app.js +12 -11
- package/dist/tools/apps/transit-timeline-app.d.ts +3 -0
- package/dist/tools/apps/transit-timeline-app.js +86 -18
- package/dist/tools/apps/vedic-chart-app.js +9 -8
- package/dist/tools/auth.js +14 -12
- package/dist/tools/dev.js +17 -29
- package/dist/tools/invocation-status.d.ts +25 -0
- package/dist/tools/invocation-status.js +69 -0
- package/dist/tools/specialized/account.js +6 -10
- package/dist/tools/specialized/acg.js +16 -21
- package/dist/tools/specialized/bazi.js +7 -12
- package/dist/tools/specialized/eclipse.js +6 -13
- package/dist/tools/specialized/electional.js +30 -37
- package/dist/tools/specialized/ephemeris_core.js +21 -17
- package/dist/tools/specialized/ephemeris_extended.js +22 -19
- package/dist/tools/specialized/human_design.js +11 -14
- package/dist/tools/specialized/moon.js +15 -26
- package/dist/tools/specialized/natal.js +9 -8
- package/dist/tools/specialized/progressed.js +6 -6
- package/dist/tools/specialized/relocation.js +6 -7
- package/dist/tools/specialized/returns.js +6 -8
- package/dist/tools/specialized/synastry.js +7 -8
- package/dist/tools/specialized/transits.js +15 -14
- package/dist/tools/specialized/vedic.js +7 -6
- package/dist/tools/ui-meta.d.ts +3 -28
- package/dist/tools/ui-meta.js +25 -16
- package/dist/ui/bi-wheel.html +468 -450
- package/dist/ui/chart-wheel.html +407 -389
- package/dist/ui/transit-timeline.html +2 -2
- package/package.json +3 -2
|
@@ -120,15 +120,18 @@ function buildNatalBody(datetime, lat, lon, houseSystem, timezone, additionalObj
|
|
|
120
120
|
// ── Tool: explore_natal_chart ──────────────────────────────────────────────
|
|
121
121
|
registerTool({
|
|
122
122
|
name: "explore_natal_chart",
|
|
123
|
-
description: "
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
"
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
123
|
+
description: "Use this when the user asks 'show me my birth chart', 'what's my rising sign', 'my big 3', " +
|
|
124
|
+
"'what's my sun, moon and rising', 'what sign is my Moon / Venus / Mars in', 'what does my chart " +
|
|
125
|
+
"say about my career / love', 'read my chart', or shares birth details and wants to explore them " +
|
|
126
|
+
"— the PRIMARY tool for any natal / birth-chart / astrology-chart request. Returns an interactive " +
|
|
127
|
+
"chart wheel rendered inline (Claude and ChatGPT; text summary elsewhere) where any planet, house " +
|
|
128
|
+
"or aspect line can be clicked for interpretation, with switchable house systems. Pass the " +
|
|
129
|
+
"birthplace as `location` (a place name — resolved server-side) or coordinates, plus the birth " +
|
|
130
|
+
"date and local time with its zone.\n\n" +
|
|
131
|
+
"CREDIT COST: 1 credit per call (+1 to resolve a place name).\n\n" +
|
|
132
|
+
"Do not use when the chart must be consumed as raw numbers for further computation — use " +
|
|
133
|
+
"ephemeris_natal_chart; for two charts together use explore_bi_wheel; for Human Design use " +
|
|
134
|
+
"explore_human_design; for Vedic use explore_vedic_chart.",
|
|
132
135
|
inputSchema: {
|
|
133
136
|
type: "object",
|
|
134
137
|
properties: {
|
|
@@ -127,7 +127,16 @@ function offsetAtLocalNoon(timezone, date) {
|
|
|
127
127
|
}
|
|
128
128
|
registerTool({
|
|
129
129
|
name: "location_search",
|
|
130
|
-
description: "
|
|
130
|
+
description: "Use this when the user gives a birth city or any place name — 'born in Chicago', 'Portland', " +
|
|
131
|
+
"'Mumbai, India' — and a chart needs coordinates and a timezone. Always resolve here; NEVER " +
|
|
132
|
+
"recall coordinates from memory. Returns display name, region (state/province), latitude, " +
|
|
133
|
+
"longitude and IANA timezone. Pass the birth date as `date` to also get `utcOffsetAtDate`, the " +
|
|
134
|
+
"historically-correct UTC offset for that place on that date; post-1970 dates resolve locally at " +
|
|
135
|
+
"no extra cost, pre-1970 dates consult the historical overlay for the top match (+1 credit) and " +
|
|
136
|
+
"return its provenance. When the result is `ambiguous` (several places share the name, e.g. " +
|
|
137
|
+
"\"portland\"), ASK the user which one they mean rather than assuming the first. A trailing \"City, " +
|
|
138
|
+
"ST\" qualifier in the query is honored. CREDIT COST: 1 credit per call. Do not use when you " +
|
|
139
|
+
"already have coordinates and only need the zone — use timezone_resolve.",
|
|
131
140
|
inputSchema: {
|
|
132
141
|
type: "object",
|
|
133
142
|
properties: {
|
|
@@ -280,12 +289,16 @@ registerTool({
|
|
|
280
289
|
});
|
|
281
290
|
registerTool({
|
|
282
291
|
name: "timezone_resolve",
|
|
283
|
-
description: "
|
|
292
|
+
description: "Use this when you have latitude/longitude but need the IANA timezone to interpret a local birth " +
|
|
293
|
+
"time, or the user asks 'what timezone is this location in' or 'what was the UTC offset there on " +
|
|
294
|
+
"that date'. Returns the IANA zone and, with `date`, the historically-correct UTC offset that " +
|
|
295
|
+
"applied on that date. CREDIT COST: 1 credit per call. Do not use when starting from a place name " +
|
|
296
|
+
"— use location_search, which returns the timezone too, in the same single call.",
|
|
284
297
|
inputSchema: {
|
|
285
298
|
type: "object",
|
|
286
299
|
properties: {
|
|
287
|
-
latitude: { type: "number" },
|
|
288
|
-
longitude: { type: "number" },
|
|
300
|
+
latitude: { type: "number", description: "Latitude in decimal degrees, -90 to 90 (north positive)." },
|
|
301
|
+
longitude: { type: "number", description: "Longitude in decimal degrees, -180 to 180 (east positive)." },
|
|
289
302
|
date: {
|
|
290
303
|
type: "string",
|
|
291
304
|
description: "Optional birth/event date 'YYYY-MM-DD'. Adds utcOffsetAtDate / utcOffsetMinutes / isDst / tzConfidence. Post-1970 resolves locally (free); pre-1970 consults the API's historical correction overlay (1 extra credit).",
|
|
@@ -193,18 +193,19 @@ async function computeMoonData(args) {
|
|
|
193
193
|
// ── Tool: explore_moon_phase ─────────────────────────────────────────────────
|
|
194
194
|
registerTool({
|
|
195
195
|
name: "explore_moon_phase",
|
|
196
|
-
description: "
|
|
197
|
-
"
|
|
198
|
-
"Returns
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
"
|
|
196
|
+
description: "Use this when the user asks 'what's the moon phase tonight', 'is tonight a full moon', 'show me " +
|
|
197
|
+
"the moon right now', 'what sign is the Moon in', 'is the Moon void of course' — or has no birth " +
|
|
198
|
+
"data and wants somewhere to start; it needs no arguments. Returns an interactive Moon dial " +
|
|
199
|
+
"(inline in Claude and ChatGPT; text summary elsewhere) with:\n" +
|
|
200
|
+
"• SVG crescent Moon showing real-time illumination percentage\n" +
|
|
201
|
+
"• Phase name and waxing/waning indicator\n" +
|
|
202
|
+
"• Current Moon sign with degree\n" +
|
|
203
|
+
"• Void-of-Course status with timing details\n" +
|
|
204
|
+
"• Lunar age (days in the synodic cycle) and days to the next New and Full Moon\n" +
|
|
205
|
+
"• The tightest applying lunar aspects\n\n" +
|
|
205
206
|
"CREDIT COST: 3 credits per call (phase + void-of-course + aspects, 1 each).\n\n" +
|
|
206
|
-
"
|
|
207
|
-
"
|
|
207
|
+
"Do not use when only the numbers are needed — use ephemeris_moon_phase; for the exact dates of " +
|
|
208
|
+
"upcoming phases use ephemeris_next_lunar_phase; for eclipses use ephemeris_next_eclipse.",
|
|
208
209
|
inputSchema: {
|
|
209
210
|
type: "object",
|
|
210
211
|
properties: {
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
* chart to extract real planetary longitudes, searches predictive transits
|
|
9
9
|
* against those targets, then flattens the (planet@target → hits) result map
|
|
10
10
|
* into a single chronological array the iframe renders as a vertical timeline.
|
|
11
|
+
* The search asks for orb windows (orb_deg) too, so every exact hit carries the
|
|
12
|
+
* in-orb window it belongs to — when the transit starts and ends, and which
|
|
13
|
+
* pass it is when a retrograde brings the planet back over the same point.
|
|
11
14
|
*
|
|
12
15
|
* Also exports resource helpers (getTransitTimelineBundle, etc.) for use in
|
|
13
16
|
* index.ts and server-sse.ts.
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
* chart to extract real planetary longitudes, searches predictive transits
|
|
9
9
|
* against those targets, then flattens the (planet@target → hits) result map
|
|
10
10
|
* into a single chronological array the iframe renders as a vertical timeline.
|
|
11
|
+
* The search asks for orb windows (orb_deg) too, so every exact hit carries the
|
|
12
|
+
* in-orb window it belongs to — when the transit starts and ends, and which
|
|
13
|
+
* pass it is when a retrograde brings the planet back over the same point.
|
|
11
14
|
*
|
|
12
15
|
* Also exports resource helpers (getTransitTimelineBundle, etc.) for use in
|
|
13
16
|
* index.ts and server-sse.ts.
|
|
@@ -67,18 +70,32 @@ function normDeg(d) {
|
|
|
67
70
|
return ((d % 360) + 360) % 360;
|
|
68
71
|
}
|
|
69
72
|
// ── Summary builder ──────────────────────────────────────────────────────────
|
|
70
|
-
function
|
|
73
|
+
function shortDate(iso) {
|
|
74
|
+
return iso ? new Date(iso).toISOString().slice(0, 10) : "?";
|
|
75
|
+
}
|
|
76
|
+
function buildSummary(transits, windows, aspectLabel, orbDeg, win) {
|
|
77
|
+
const active = windows.filter((w) => w.phase_at_start);
|
|
78
|
+
const activeLines = active.slice(0, 6).map((w) => " • " + w.planet_name + " " + (w.aspect ?? aspectLabel) + (w.natal_point ? " natal " + w.natal_point : "") +
|
|
79
|
+
" — " + w.phase_at_start + ", " + (Math.round((w.orb_at_start ?? 0) * 100) / 100) + "° from exact, in orb until " +
|
|
80
|
+
shortDate(w.leave));
|
|
81
|
+
const activeBlock = activeLines.length
|
|
82
|
+
? "\n\nAlready in orb (" + orbDeg + "°) at the start:\n" + activeLines.join("\n")
|
|
83
|
+
: "";
|
|
71
84
|
if (transits.length === 0) {
|
|
72
|
-
return "**Transit Timeline** — no exact transit hits matched the search window and criteria.";
|
|
85
|
+
return "**Transit Timeline** — no exact transit hits matched the search window and criteria." + activeBlock;
|
|
73
86
|
}
|
|
74
87
|
const planets = [...new Set(transits.map((t) => t.planet_name).filter(Boolean))];
|
|
75
88
|
const lines = transits.slice(0, 8).map((t) => {
|
|
76
89
|
const asp = t.aspect ?? aspectLabel;
|
|
77
90
|
const natal = t.natal_point ? " natal " + t.natal_point : "";
|
|
78
91
|
const when = new Date(t.date).toISOString().slice(0, 10);
|
|
92
|
+
const passes = t.pass_count > 1 ? ", pass " + t.pass_index + " of " + t.pass_count : "";
|
|
93
|
+
const inOrb = t.window_start !== undefined
|
|
94
|
+
? " — in orb " + shortDate(t.window_start) + " → " + shortDate(t.window_end) + passes
|
|
95
|
+
: "";
|
|
79
96
|
return " • " + when + " — " + t.planet_name + " " + asp + natal +
|
|
80
97
|
(t.zodiac_sign ? " (" + (t.zodiac_degree ?? 0) + "° " + t.zodiac_sign + ")" : "") +
|
|
81
|
-
(t.is_retrograde ? " ℞" : "");
|
|
98
|
+
(t.is_retrograde ? " ℞" : "") + inOrb;
|
|
82
99
|
});
|
|
83
100
|
let summary = "**Transit Timeline** — " + transits.length + (transits.length === 1 ? " event" : " events") +
|
|
84
101
|
" from " + win.start_date + " to " + win.end_date +
|
|
@@ -86,27 +103,29 @@ function buildSummary(transits, aspectLabel, win) {
|
|
|
86
103
|
lines.join("\n");
|
|
87
104
|
if (transits.length > 8)
|
|
88
105
|
summary += "\n … and " + (transits.length - 8) + " more.";
|
|
106
|
+
summary += activeBlock;
|
|
89
107
|
summary += "\n\nClick any transit for details and interpretation.";
|
|
90
108
|
return summary;
|
|
91
109
|
}
|
|
92
110
|
// ── Tool: explore_transit_timeline ───────────────────────────────────────────
|
|
93
111
|
registerTool({
|
|
94
112
|
name: "explore_transit_timeline",
|
|
95
|
-
description: "
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"ASPECT ANGLES: 0 = conjunction/return (default), 180 = opposition, 90 = square, " +
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
113
|
+
description: "Use this when the user asks 'what transits are coming up for me', 'what's my forecast for the " +
|
|
114
|
+
"next year', 'when will Saturn hit my chart', 'show my upcoming transits' or 'what's happening " +
|
|
115
|
+
"astrologically for me in 2027' — a forecast they should SEE. Returns an interactive, " +
|
|
116
|
+
"date-ordered timeline (inline in Claude and ChatGPT; text summary elsewhere) of exact transit " +
|
|
117
|
+
"hits grouped by month — transiting planet glyph, the natal point it contacts, the aspect, zodiac " +
|
|
118
|
+
"position and retrograde markers — each clickable for a focused interpretation. Every hit carries its " +
|
|
119
|
+
"in-orb window (when the transit starts and ends, and which pass it is when a retrograde repeats it), " +
|
|
120
|
+
"and transits already in orb at the start are listed with applying/separating.\n\n" +
|
|
121
|
+
"ASPECT ANGLES: 0 = conjunction/return (default), 180 = opposition, 90 = square, 120 = trine, 60 " +
|
|
122
|
+
"= sextile. EFFICIENCY: specify transiting_planets and natal_points to keep compute fast. DEFAULT " +
|
|
123
|
+
"natal_points: sun, moon, mercury, venus, mars, jupiter, saturn. SEARCH RANGE LIMITS: " +
|
|
124
|
+
"Explorer/PayG → 1 year; Pro → 5 years; Startup → 10 years.\n\n" +
|
|
107
125
|
"CREDIT COST: 6 credits per call (natal chart + predictive transit search).\n\n" +
|
|
108
|
-
"
|
|
109
|
-
"
|
|
126
|
+
"Do not use for transit data as JSON — use ephemeris_transits; for the sky today with no birth " +
|
|
127
|
+
"data use electional_moment_analysis; to see one date's transits drawn around the natal wheel use " +
|
|
128
|
+
"explore_bi_wheel (mode='transit').",
|
|
110
129
|
inputSchema: {
|
|
111
130
|
type: "object",
|
|
112
131
|
properties: {
|
|
@@ -128,6 +147,10 @@ registerTool({
|
|
|
128
147
|
type: "number",
|
|
129
148
|
description: "Aspect angle in degrees. 0 = conjunction/return (default), 180 = opposition, 90 = square, 120 = trine, 60 = sextile.",
|
|
130
149
|
},
|
|
150
|
+
orb_deg: {
|
|
151
|
+
type: "number",
|
|
152
|
+
description: "Orb in degrees that defines when a transit starts and ends (the in-orb window around each exact hit). Default 1. Greater than 0, at most 15.",
|
|
153
|
+
},
|
|
131
154
|
},
|
|
132
155
|
required: ["natal_datetime", "natal_latitude", "natal_longitude", "start_date", "end_date"],
|
|
133
156
|
additionalProperties: false,
|
|
@@ -223,10 +246,12 @@ registerTool({
|
|
|
223
246
|
let endStr = args.end_date;
|
|
224
247
|
if (/^\d{4}-\d{2}-\d{2}$/.test(endStr))
|
|
225
248
|
endStr += "T23:59:59Z";
|
|
249
|
+
const orbDeg = typeof args.orb_deg === "number" && args.orb_deg > 0 ? Math.min(args.orb_deg, 15) : 1;
|
|
226
250
|
const transitBody = {
|
|
227
251
|
start_date: startStr,
|
|
228
252
|
end_date: endStr,
|
|
229
253
|
target_degrees: effectiveTargets,
|
|
254
|
+
orb_deg: orbDeg,
|
|
230
255
|
};
|
|
231
256
|
if (args.transiting_planets)
|
|
232
257
|
transitBody.planet_names = args.transiting_planets;
|
|
@@ -279,10 +304,53 @@ registerTool({
|
|
|
279
304
|
}
|
|
280
305
|
}
|
|
281
306
|
transits.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
|
307
|
+
// ── Step 5: orb windows → attach each exact hit to its window ────────────
|
|
308
|
+
// A window groups every pass the planet makes without leaving the orb, so
|
|
309
|
+
// a retrograde triple pass is one window with three exacts. Hits are
|
|
310
|
+
// matched to windows by planet, target degree and exact time.
|
|
311
|
+
const rawWindows = Array.isArray(transitResult?.windows) ? transitResult.windows : [];
|
|
312
|
+
const windows = rawWindows.map((w) => {
|
|
313
|
+
const target = typeof w?.target_longitude === "number" ? w.target_longitude : null;
|
|
314
|
+
return {
|
|
315
|
+
planet: String(w?.planet_name ?? "").toLowerCase(),
|
|
316
|
+
planet_name: String(w?.planet_name ?? ""),
|
|
317
|
+
natal_point: target != null ? nearestPoint(target) : "",
|
|
318
|
+
aspect: aspectLabel,
|
|
319
|
+
target_longitude: target,
|
|
320
|
+
orb_deg: w?.orb_deg ?? orbDeg,
|
|
321
|
+
enter: w?.enter ?? null,
|
|
322
|
+
leave: w?.leave ?? null,
|
|
323
|
+
exacts: Array.isArray(w?.exacts)
|
|
324
|
+
? w.exacts.map((e) => ({ time: e?.time, is_retrograde: Boolean(e?.is_retrograde) }))
|
|
325
|
+
: [],
|
|
326
|
+
duration_days: w?.duration_days ?? null,
|
|
327
|
+
phase_at_start: w?.phase_at_start ?? null,
|
|
328
|
+
orb_at_start: w?.orb_at_start ?? null,
|
|
329
|
+
};
|
|
330
|
+
});
|
|
331
|
+
for (const t of transits) {
|
|
332
|
+
const hitMs = new Date(t.date).getTime();
|
|
333
|
+
for (const w of windows) {
|
|
334
|
+
if (w.planet_name !== t.planet_name || w.target_longitude == null || t.target_longitude == null)
|
|
335
|
+
continue;
|
|
336
|
+
if (Math.abs(w.target_longitude - t.target_longitude) > 0.01)
|
|
337
|
+
continue;
|
|
338
|
+
const idx = w.exacts.findIndex((e) => Math.abs(new Date(e.time).getTime() - hitMs) <= 60_000);
|
|
339
|
+
if (idx < 0)
|
|
340
|
+
continue;
|
|
341
|
+
t.window_start = w.enter;
|
|
342
|
+
t.window_end = w.leave;
|
|
343
|
+
t.pass_index = idx + 1;
|
|
344
|
+
t.pass_count = w.exacts.length;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
282
348
|
const win = { start_date: startStr.slice(0, 10), end_date: endStr.slice(0, 10) };
|
|
283
|
-
const summary = buildSummary(transits, aspectLabel, win);
|
|
349
|
+
const summary = buildSummary(transits, windows, aspectLabel, orbDeg, win);
|
|
284
350
|
const uiPayload = {
|
|
285
351
|
transits,
|
|
352
|
+
windows,
|
|
353
|
+
orb_deg: orbDeg,
|
|
286
354
|
natal_positions: natalPositionMap,
|
|
287
355
|
aspect_angle: aspectAngle,
|
|
288
356
|
aspect_label: aspectLabel,
|
|
@@ -96,14 +96,15 @@ function buildVedicSummary(payload, location) {
|
|
|
96
96
|
// ── Tool: explore_vedic_chart ────────────────────────────────────────────────
|
|
97
97
|
registerTool({
|
|
98
98
|
name: "explore_vedic_chart",
|
|
99
|
-
description: "
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
99
|
+
description: "Use this when the user asks 'show my Vedic chart', 'my Jyotish chart', 'what's my nakshatra', " +
|
|
100
|
+
"'my sidereal birth chart', 'my rashi chart' or 'what's my lagna' — the PRIMARY tool for a Vedic " +
|
|
101
|
+
"chart. Returns an interactive South Indian fixed-sign Rashi grid (inline in Claude and ChatGPT; " +
|
|
102
|
+
"text summary elsewhere) with sidereal (Lahiri by default) placements, nakshatra with pada, " +
|
|
103
|
+
"navamsa and the Lagna (Ascendant) rashi; click any rashi cell for its themes and the planets " +
|
|
104
|
+
"placed there. Birthplace may be a place name (`location`).\n\n" +
|
|
105
|
+
"CREDIT COST: 3 credits per call (chart + render; +1 to resolve a place name).\n\n" +
|
|
106
|
+
"Do not use when raw Jyotish data is enough — use vedic_chart (1 credit); for a Western tropical " +
|
|
107
|
+
"chart use explore_natal_chart.",
|
|
107
108
|
inputSchema: {
|
|
108
109
|
type: "object",
|
|
109
110
|
properties: {
|
package/dist/tools/auth.js
CHANGED
|
@@ -8,12 +8,12 @@ const credentialManager = new CredentialManager();
|
|
|
8
8
|
// ────────────────────────────────────────────────────────
|
|
9
9
|
registerTool({
|
|
10
10
|
name: "auth_login",
|
|
11
|
-
description: "
|
|
12
|
-
"
|
|
13
|
-
"Returns a verification URL and code for the user to
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
11
|
+
description: "Use this when the user asks to 'connect', 'sign in', 'log in' or 'link my OpenEphemeris " +
|
|
12
|
+
"account', or when a call fails for lack of credentials and no OPENEPHEMERIS_API_KEY or cached " +
|
|
13
|
+
"login exists (free tier, no card needed). Returns a verification URL and code for the user to " +
|
|
14
|
+
"enter in their browser; the server then receives credentials automatically and every call is " +
|
|
15
|
+
"billed to that account (tier, credits, rate limits). Do not use to check whether you are already " +
|
|
16
|
+
"signed in — use auth_status; to disconnect use auth_logout.",
|
|
17
17
|
inputSchema: {
|
|
18
18
|
type: "object",
|
|
19
19
|
properties: {},
|
|
@@ -96,9 +96,10 @@ registerTool({
|
|
|
96
96
|
// ────────────────────────────────────────────────────────
|
|
97
97
|
registerTool({
|
|
98
98
|
name: "auth_status",
|
|
99
|
-
description: "
|
|
100
|
-
"
|
|
101
|
-
"the
|
|
99
|
+
description: "Use this when the user asks 'am I logged in', 'which account is this connected to', 'how am I " +
|
|
100
|
+
"authenticated' or 'when does my token expire'. Returns whether the server is authenticated, the " +
|
|
101
|
+
"linked account, the method (API key, JWT, device auth) and token expiry. Do not use for credits, " +
|
|
102
|
+
"plan or usage — use account_usage; to sign in use auth_login.",
|
|
102
103
|
inputSchema: {
|
|
103
104
|
type: "object",
|
|
104
105
|
properties: {},
|
|
@@ -184,9 +185,10 @@ registerTool({
|
|
|
184
185
|
// ────────────────────────────────────────────────────────
|
|
185
186
|
registerTool({
|
|
186
187
|
name: "auth_logout",
|
|
187
|
-
description: "
|
|
188
|
-
"
|
|
189
|
-
"
|
|
188
|
+
description: "Use this when the user asks to 'log out', 'sign out', 'disconnect' or 'switch accounts' on this " +
|
|
189
|
+
"MCP server. Returns confirmation that cached device-auth credentials were cleared; an " +
|
|
190
|
+
"OPENEPHEMERIS_API_KEY set in the environment is not revoked. Do not use to check the current " +
|
|
191
|
+
"login — use auth_status.",
|
|
190
192
|
inputSchema: {
|
|
191
193
|
type: "object",
|
|
192
194
|
properties: {},
|
package/dist/tools/dev.js
CHANGED
|
@@ -44,25 +44,12 @@ function isAllowedOperation(method, pathname, allow) {
|
|
|
44
44
|
// actually make.
|
|
45
45
|
const DEV_API_REFERENCE = "Target API: Open Ephemeris REST API (https://api.openephemeris.com). " +
|
|
46
46
|
"Call dev_list_allowed to see all currently available endpoint paths.\n\n" +
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
" • Predictive ops (transits/search, returns): 5 credits\n" +
|
|
54
|
-
" • Predictive transit-chart: 1 credit\n" +
|
|
55
|
-
" • ACG / astrocartography: 10 credits (acg/hits: 15 credits)\n" +
|
|
56
|
-
" • Calendar endpoints: 10 credits\n" +
|
|
57
|
-
" • Catalog / metadata / health endpoints: 0 credits\n" +
|
|
58
|
-
" • Compute surcharge: requests > 30s add 1 credit per 30s (predictive, acg, calendar, electional)\n" +
|
|
59
|
-
" • format=llm (token-optimized output): available on all tiers\n\n" +
|
|
60
|
-
"BINARY RESPONSES:\n" +
|
|
61
|
-
" • Binary/image endpoints return {content_type, content_length, encoding, data_base64}\n" +
|
|
62
|
-
" so callers can decode bytes deterministically.\n\n" +
|
|
63
|
-
"ECLIPSE NOTE: Eclipse endpoints accept format=llm via the query param like other endpoints.\n\n" +
|
|
64
|
-
"format=llm NOTE: Add query: {format: 'llm'} to natal/synastry/composite/HD endpoints for " +
|
|
65
|
-
"compact columnar output optimized for LLM token budgets (availability depends on your current plan).";
|
|
47
|
+
// Prices mirror go-sidecar/internal/api/auth/usage_meter.go; tiers mirror
|
|
48
|
+
// auth/middleware.go requiredTierForPath. /iching/* aliases /human-design/*.
|
|
49
|
+
"CREDIT COST: 1 for most GET calls; /calendar/* 10; /acg/* 10 (Pro tier); /electional/* 5 " +
|
|
50
|
+
"(find-window and aspect-search are Pro tier; moment-analysis and station-tracker are free); " +
|
|
51
|
+
"/human-design/* and /iching/* 2; catalogs/health 0; requests over 30s add 1 " +
|
|
52
|
+
"per 30s. Pass query {format: 'llm'} for compact output.\n\n";
|
|
66
53
|
const READ_COMMON_CALLS = "COMMON CALLS:\n" +
|
|
67
54
|
" GET /ephemeris/moon/phase — Current/queried moon phase\n" +
|
|
68
55
|
" GET /ephemeris/moon/void-of-course — Next void-of-course period\n" +
|
|
@@ -196,11 +183,12 @@ function makeProxyHandler(allowedMethods, defaultMethod) {
|
|
|
196
183
|
}
|
|
197
184
|
registerTool({
|
|
198
185
|
name: "dev_read_api",
|
|
199
|
-
description: "
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"
|
|
186
|
+
description: "Use this when the user asks for something no typed tool covers — 'is today a good biodynamic " +
|
|
187
|
+
"planting day', 'what's the tidal forcing', 'what Chinese zodiac animal is 2026', 'which bodies " +
|
|
188
|
+
"do you support' — or any other allowlisted GET endpoint. Returns the endpoint's JSON. Read-only " +
|
|
189
|
+
"HTTP GET; call dev_list_allowed to see every path. Do not use for birth charts, transits, moon " +
|
|
190
|
+
"phase, eclipses, Human Design, synastry or anything an ephemeris_* / explore_* tool already " +
|
|
191
|
+
"does. POST endpoints are only on the full tool surface (`?profile=full`).\n\n" +
|
|
204
192
|
DEV_API_REFERENCE + "\n\n" + READ_COMMON_CALLS,
|
|
205
193
|
inputSchema: makeProxyInputSchema(READ_METHODS),
|
|
206
194
|
outputSchema: OUTPUT_SCHEMA_JSON,
|
|
@@ -233,11 +221,11 @@ registerTool({
|
|
|
233
221
|
});
|
|
234
222
|
registerTool({
|
|
235
223
|
name: "dev_list_allowed",
|
|
236
|
-
description: "
|
|
237
|
-
"
|
|
238
|
-
"
|
|
239
|
-
"
|
|
240
|
-
"
|
|
224
|
+
description: "Use this when the user asks 'what endpoints can you call', 'what else can this server do', 'is " +
|
|
225
|
+
"/some/path available', or before a dev_read_api call whose path you are unsure of. Returns every " +
|
|
226
|
+
"allowlisted API operation (method + path) grouped by method, plus the active deny rules; read " +
|
|
227
|
+
"locally, no API call. Do not use to fetch data — use dev_read_api, or the typed ephemeris_* / " +
|
|
228
|
+
"explore_* tool when one exists.",
|
|
241
229
|
inputSchema: {
|
|
242
230
|
type: "object",
|
|
243
231
|
properties: {},
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* invocation-status.ts — the status text ChatGPT shows while a tool runs and
|
|
3
|
+
* after it completes.
|
|
4
|
+
*
|
|
5
|
+
* Wire form (OpenAI Apps SDK reference, "Tool descriptor parameters"):
|
|
6
|
+
* _meta["openai/toolInvocation/invoking"] ≤ 64 chars, shown during the call
|
|
7
|
+
* _meta["openai/toolInvocation/invoked"] ≤ 64 chars, shown when it returns
|
|
8
|
+
* Both are ChatGPT-only; other hosts ignore the keys. Emitted by buildUiMeta
|
|
9
|
+
* (src/tools/ui-meta.ts), the one path both transports' tools/list share.
|
|
10
|
+
*
|
|
11
|
+
* Copy rules: name the thing being made in the user's words, so the wait reads
|
|
12
|
+
* as progress rather than plumbing. `explore_*` tools say "ready" (a thing to
|
|
13
|
+
* look at); `ephemeris_*` tools say "computed" / "found" (numbers) — the same
|
|
14
|
+
* picture-vs-data split the descriptions make. Never promise a render:
|
|
15
|
+
* `explore_*` fall back to text in hosts without MCP Apps.
|
|
16
|
+
*
|
|
17
|
+
* Every core tool must have an entry (test/invocation-status.test.ts); tools
|
|
18
|
+
* outside core may have one. Keep both strings under MAX_STATUS_CHARS.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MAX_STATUS_CHARS = 64;
|
|
21
|
+
export interface InvocationStatus {
|
|
22
|
+
readonly invoking: string;
|
|
23
|
+
readonly invoked: string;
|
|
24
|
+
}
|
|
25
|
+
export declare const INVOCATION_STATUS: Readonly<Record<string, InvocationStatus>>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* invocation-status.ts — the status text ChatGPT shows while a tool runs and
|
|
3
|
+
* after it completes.
|
|
4
|
+
*
|
|
5
|
+
* Wire form (OpenAI Apps SDK reference, "Tool descriptor parameters"):
|
|
6
|
+
* _meta["openai/toolInvocation/invoking"] ≤ 64 chars, shown during the call
|
|
7
|
+
* _meta["openai/toolInvocation/invoked"] ≤ 64 chars, shown when it returns
|
|
8
|
+
* Both are ChatGPT-only; other hosts ignore the keys. Emitted by buildUiMeta
|
|
9
|
+
* (src/tools/ui-meta.ts), the one path both transports' tools/list share.
|
|
10
|
+
*
|
|
11
|
+
* Copy rules: name the thing being made in the user's words, so the wait reads
|
|
12
|
+
* as progress rather than plumbing. `explore_*` tools say "ready" (a thing to
|
|
13
|
+
* look at); `ephemeris_*` tools say "computed" / "found" (numbers) — the same
|
|
14
|
+
* picture-vs-data split the descriptions make. Never promise a render:
|
|
15
|
+
* `explore_*` fall back to text in hosts without MCP Apps.
|
|
16
|
+
*
|
|
17
|
+
* Every core tool must have an entry (test/invocation-status.test.ts); tools
|
|
18
|
+
* outside core may have one. Keep both strings under MAX_STATUS_CHARS.
|
|
19
|
+
*/
|
|
20
|
+
export const MAX_STATUS_CHARS = 64;
|
|
21
|
+
export const INVOCATION_STATUS = {
|
|
22
|
+
// Interactive apps
|
|
23
|
+
explore_natal_chart: { invoking: "Casting your birth chart…", invoked: "Birth chart ready" },
|
|
24
|
+
explore_bi_wheel: { invoking: "Comparing the two charts…", invoked: "Bi-wheel ready" },
|
|
25
|
+
explore_human_design: { invoking: "Drawing your bodygraph…", invoked: "Bodygraph ready" },
|
|
26
|
+
explore_human_design_transit: { invoking: "Overlaying the transits on your bodygraph…", invoked: "Transit overlay ready" },
|
|
27
|
+
explore_human_design_connection: { invoking: "Overlaying both bodygraphs…", invoked: "Connection chart ready" },
|
|
28
|
+
explore_moon_phase: { invoking: "Reading the Moon…", invoked: "Moon phase ready" },
|
|
29
|
+
explore_transit_timeline: { invoking: "Searching your upcoming transits…", invoked: "Transit timeline ready" },
|
|
30
|
+
explore_vedic_chart: { invoking: "Casting your Vedic chart…", invoked: "Vedic chart ready" },
|
|
31
|
+
explore_bazi_chart: { invoking: "Building your Four Pillars…", invoked: "Four Pillars ready" },
|
|
32
|
+
// Ephemeris data
|
|
33
|
+
ephemeris_natal_chart: { invoking: "Computing natal positions…", invoked: "Natal positions computed" },
|
|
34
|
+
ephemeris_planet_position: { invoking: "Locating the planet…", invoked: "Planet position computed" },
|
|
35
|
+
ephemeris_house_cusps: { invoking: "Computing house cusps…", invoked: "House cusps computed" },
|
|
36
|
+
ephemeris_angles_points: { invoking: "Computing chart angles…", invoked: "Chart angles computed" },
|
|
37
|
+
ephemeris_aspect_check: { invoking: "Measuring the aspect…", invoked: "Aspect measured" },
|
|
38
|
+
ephemeris_moon_phase: { invoking: "Checking the Moon…", invoked: "Moon phase found" },
|
|
39
|
+
ephemeris_next_lunar_phase: { invoking: "Finding the next lunar phase…", invoked: "Lunar phase dates found" },
|
|
40
|
+
ephemeris_next_eclipse: { invoking: "Finding the next eclipse…", invoked: "Eclipse found" },
|
|
41
|
+
ephemeris_retrograde_status: { invoking: "Checking retrograde motion…", invoked: "Retrograde status checked" },
|
|
42
|
+
ephemeris_transits: { invoking: "Searching for exact transit dates…", invoked: "Transit dates found" },
|
|
43
|
+
ephemeris_synastry: { invoking: "Computing synastry aspects…", invoked: "Synastry computed" },
|
|
44
|
+
ephemeris_relocation: { invoking: "Relocating your chart…", invoked: "Relocated chart computed" },
|
|
45
|
+
ephemeris_progressed_chart: { invoking: "Progressing your chart…", invoked: "Progressed chart computed" },
|
|
46
|
+
ephemeris_solar_return: { invoking: "Finding your solar return…", invoked: "Solar return computed" },
|
|
47
|
+
// Traditions
|
|
48
|
+
human_design_chart: { invoking: "Computing Human Design data…", invoked: "Human Design data computed" },
|
|
49
|
+
vedic_chart: { invoking: "Computing Vedic positions…", invoked: "Vedic positions computed" },
|
|
50
|
+
bazi_annual_pillar: { invoking: "Looking up the year pillar…", invoked: "Year pillar found" },
|
|
51
|
+
// Timing
|
|
52
|
+
ephemeris_electional: { invoking: "Scanning for the best timing windows…", invoked: "Timing windows found" },
|
|
53
|
+
electional_moment_analysis: { invoking: "Scoring this moment…", invoked: "Moment scored" },
|
|
54
|
+
electional_station_tracker: { invoking: "Finding retrograde and direct stations…", invoked: "Station dates found" },
|
|
55
|
+
// Astrocartography
|
|
56
|
+
acg_power_lines: { invoking: "Tracing your planetary lines…", invoked: "Planetary lines traced" },
|
|
57
|
+
acg_hits: { invoking: "Checking your lines at this location…", invoked: "Location lines found" },
|
|
58
|
+
// Geocoding
|
|
59
|
+
location_search: { invoking: "Looking up the place…", invoked: "Place found" },
|
|
60
|
+
timezone_resolve: { invoking: "Resolving the timezone…", invoked: "Timezone resolved" },
|
|
61
|
+
// Account + escape hatch
|
|
62
|
+
account_usage: { invoking: "Checking your account…", invoked: "Usage retrieved" },
|
|
63
|
+
dev_read_api: { invoking: "Calling the Open Ephemeris API…", invoked: "API response ready" },
|
|
64
|
+
dev_list_allowed: { invoking: "Listing available endpoints…", invoked: "Endpoints listed" },
|
|
65
|
+
// Auth (stdio only)
|
|
66
|
+
auth_login: { invoking: "Connecting your account…", invoked: "Account connected" },
|
|
67
|
+
auth_status: { invoking: "Checking the connection…", invoked: "Connection status ready" },
|
|
68
|
+
auth_logout: { invoking: "Disconnecting your account…", invoked: "Account disconnected" },
|
|
69
|
+
};
|
|
@@ -5,16 +5,12 @@ import { OUTPUT_SCHEMA_JSON } from "../output-schemas.js";
|
|
|
5
5
|
const WALLET_TIERS = new Set(["explorer", "free", "payg", "wallet"]);
|
|
6
6
|
registerTool({
|
|
7
7
|
name: "account_usage",
|
|
8
|
-
description: "
|
|
9
|
-
"
|
|
10
|
-
"percent of quota used, total API calls, and
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"EXAMPLE: Check current usage:\n" +
|
|
15
|
-
" (call with no arguments)\n\n" +
|
|
16
|
-
"EXAMPLE: Check a past month:\n" +
|
|
17
|
-
" month='2026-06'",
|
|
8
|
+
description: "Use this when the user asks 'how many credits do I have left', 'what's my usage this month', 'am " +
|
|
9
|
+
"I close to my limit', 'what plan am I on' or 'how do I upgrade or top up'. Returns plan tier, " +
|
|
10
|
+
"billing period, credits used / included / remaining, percent of quota used, total API calls, and " +
|
|
11
|
+
"subscription status with renewal date; pass month='YYYY-MM' for a past period.\n\n" +
|
|
12
|
+
"CREDIT COST: 0.\n\n" +
|
|
13
|
+
"Do not use to sign in or to check the login state — that is auth, not usage.",
|
|
18
14
|
inputSchema: {
|
|
19
15
|
type: "object",
|
|
20
16
|
properties: {
|