@openephemeris/mcp-server 3.5.2 → 3.6.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 +41 -0
- package/README.md +2 -2
- package/config/dev-allowlist.json +2 -2
- package/dist/backend/client.d.ts +10 -0
- package/dist/backend/client.js +24 -1
- package/dist/server-sse.js +21 -5
- package/dist/tools/dev.js +2 -2
- package/dist/tools/specialized/acg.js +9 -3
- package/dist/tools/specialized/bazi.js +2 -2
- package/dist/tools/specialized/bi_wheel.js +4 -4
- package/dist/tools/specialized/chart_wheel.js +4 -4
- package/dist/tools/specialized/comparative.js +5 -5
- package/dist/tools/specialized/eclipse.js +38 -20
- package/dist/tools/specialized/electional.js +33 -14
- package/dist/tools/specialized/ephemeris_core.js +3 -3
- package/dist/tools/specialized/ephemeris_extended.js +9 -9
- package/dist/tools/specialized/hd_cycles.js +3 -3
- package/dist/tools/specialized/hd_group.js +3 -3
- package/dist/tools/specialized/human_design.js +2 -2
- package/dist/tools/specialized/moon.js +95 -6
- package/dist/tools/specialized/natal.js +2 -2
- package/dist/tools/specialized/progressed.js +2 -2
- package/dist/tools/specialized/relocation.js +2 -2
- package/dist/tools/specialized/returns.js +34 -21
- package/dist/tools/specialized/synastry.js +2 -2
- package/dist/tools/specialized/transits.js +3 -3
- package/dist/tools/specialized/vedic.js +2 -2
- package/dist/tools/specialized/venus_star_points.js +13 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,47 @@ Version numbering follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [3.6.0] — 2026-04-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `ephemeris_next_lunar_phase` — new purpose-built tool for "when is the next new/full/quarter moon?" queries.
|
|
14
|
+
Replaces a 4–7 step multi-call chain with a single, credit-efficient call. Internally calculates a rolling
|
|
15
|
+
search window and filters the calendar API response so the LLM receives a clean, normalized answer.
|
|
16
|
+
|
|
17
|
+
### Changed (Tool Descriptions & LLM Routing)
|
|
18
|
+
- `ephemeris_moon_phase` — sharpened description to clarify it is point-in-time only and cannot answer
|
|
19
|
+
"upcoming date" questions. Adds explicit `→ use ephemeris_next_lunar_phase` redirect.
|
|
20
|
+
- `ephemeris_next_eclipse` — latitude/longitude are now **optional**. Omitting them triggers a global eclipse
|
|
21
|
+
search (`/eclipse/solar/global` or `/eclipse/lunar/global`), allowing Claude to answer "when is the next
|
|
22
|
+
total solar eclipse?" without forcing the user to specify a location.
|
|
23
|
+
- `acg_power_lines` — added explicit ❌/✅ routing guidance distinguishing it from `acg_hits`. This prevents
|
|
24
|
+
Claude from fetching full global GeoJSON geometry when the user's question is about a specific city.
|
|
25
|
+
- `acg_hits` — symmetric routing guidance added (mirrors `acg_power_lines` change above).
|
|
26
|
+
- `ephemeris_solar_return` — `target_datetime` is now optional; defaults to `new Date().toISOString()` so
|
|
27
|
+
Claude can answer "What does my solar return look like?" for the current year without stalling for input.
|
|
28
|
+
- `ephemeris_lunar_return` — same treatment; defaults to today so "what's my next lunar return?" resolves
|
|
29
|
+
in one call.
|
|
30
|
+
- `electional_station_tracker` — `planets` parameter now accepts human-readable names (`mercury,venus,mars`)
|
|
31
|
+
in addition to numeric IDs. Names are mapped to IDs in the handler. Adds `USE THIS TOOL FOR:` examples
|
|
32
|
+
for "Mercury retrograde" queries to aid routing.
|
|
33
|
+
- `venus_phase` — `date` is now optional (was incorrectly marked required despite the description
|
|
34
|
+
saying "Defaults to now"). Handler auto-fills today's date when omitted.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## [3.5.3] — 2026-04-10
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- `ephemeris_chart_wheel` and `ephemeris_bi_wheel` tools now correctly advertise the
|
|
42
|
+
`style` values accepted by the backend (`light`, `dark`, `mono`). The previous enum
|
|
43
|
+
(`modern`, `classic`, `dark`) caused every non-dark chart request to fail with `400`.
|
|
44
|
+
- SSE sessions are now fully isolated: each connection creates its own `BackendClient`
|
|
45
|
+
instance keyed to the connecting user's API key. Concurrent sessions no longer
|
|
46
|
+
overwrite each other's API key on the shared singleton, preventing cross-user
|
|
47
|
+
billing attribution errors.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
10
51
|
## [3.5.1] — 2026-03-31
|
|
11
52
|
|
|
12
53
|
### Changed
|
package/README.md
CHANGED
|
@@ -226,8 +226,8 @@ Generated by `npm run sync:readme` from `config/dev-allowlist.json` and the live
|
|
|
226
226
|
|
|
227
227
|
- Allowlisted operations: **22**
|
|
228
228
|
- Methods: `GET=4`, `POST=18`, `PUT=0`, `PATCH=0`, `DELETE=0`
|
|
229
|
-
- Registered tools (`OPENEPHEMERIS_PROFILE=dev`): **
|
|
230
|
-
- Typed tools: `auth_login`, `auth_logout`, `auth_status`, `chinese_bazi`, `dev_call`, `dev_list_allowed`, `electional_aspect_search`, `electional_moment_analysis`, `electional_station_tracker`, `ephemeris_angles_points`, `ephemeris_aspect_check`, `ephemeris_bi_wheel`, `ephemeris_chart_wheel`, `ephemeris_composite`, `ephemeris_composite_midpoint`, `ephemeris_dignities`, `ephemeris_electional`, `ephemeris_fixed_stars`, `ephemeris_hermetic_lots`, `ephemeris_house_cusps`, `ephemeris_lunar_return`, `ephemeris_midpoints`, `ephemeris_moon_phase`, `ephemeris_natal_batch`, `ephemeris_natal_chart`, `ephemeris_natal_transits`, `ephemeris_next_eclipse`, `ephemeris_overlay`, `ephemeris_planet_position`, `ephemeris_planetary_return`, `ephemeris_progressed_chart`, `ephemeris_relocation`, `ephemeris_retrograde_status`, `ephemeris_solar_return`, `ephemeris_synastry`, `ephemeris_transits`, `hd_opposition`, `hd_planetary_return`, `human_design_chart`, `human_design_composite`, `human_design_penta`, `vedic_chart`, `venus_eight_year_star`, `venus_elongations`, `venus_phase`, `venus_star_points`, `venus_star_points_conjunctions`, `venus_stations`
|
|
229
|
+
- Registered tools (`OPENEPHEMERIS_PROFILE=dev`): **49**
|
|
230
|
+
- Typed tools: `auth_login`, `auth_logout`, `auth_status`, `chinese_bazi`, `dev_call`, `dev_list_allowed`, `electional_aspect_search`, `electional_moment_analysis`, `electional_station_tracker`, `ephemeris_angles_points`, `ephemeris_aspect_check`, `ephemeris_bi_wheel`, `ephemeris_chart_wheel`, `ephemeris_composite`, `ephemeris_composite_midpoint`, `ephemeris_dignities`, `ephemeris_electional`, `ephemeris_fixed_stars`, `ephemeris_hermetic_lots`, `ephemeris_house_cusps`, `ephemeris_lunar_return`, `ephemeris_midpoints`, `ephemeris_moon_phase`, `ephemeris_natal_batch`, `ephemeris_natal_chart`, `ephemeris_natal_transits`, `ephemeris_next_eclipse`, `ephemeris_next_lunar_phase`, `ephemeris_overlay`, `ephemeris_planet_position`, `ephemeris_planetary_return`, `ephemeris_progressed_chart`, `ephemeris_relocation`, `ephemeris_retrograde_status`, `ephemeris_solar_return`, `ephemeris_synastry`, `ephemeris_transits`, `hd_opposition`, `hd_planetary_return`, `human_design_chart`, `human_design_composite`, `human_design_penta`, `vedic_chart`, `venus_eight_year_star`, `venus_elongations`, `venus_phase`, `venus_star_points`, `venus_star_points_conjunctions`, `venus_stations`
|
|
231
231
|
- Generic tools:
|
|
232
232
|
|
|
233
233
|
### Allowlist Families
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
"path": "/visualization/chart-wheel"
|
|
105
105
|
}
|
|
106
106
|
],
|
|
107
|
-
"last_generated_at": "2026-04-
|
|
108
|
-
"openapi_sha256": "
|
|
107
|
+
"last_generated_at": "2026-04-16T23:21:31.665Z",
|
|
108
|
+
"openapi_sha256": "2349bd8e3e36dad609b75cc61acb1722206990581085d986b0cdad79317296aa",
|
|
109
109
|
"candidates_get": [
|
|
110
110
|
{
|
|
111
111
|
"method": "GET",
|
package/dist/backend/client.d.ts
CHANGED
|
@@ -65,3 +65,13 @@ export declare class BackendClient {
|
|
|
65
65
|
getUserId(): string;
|
|
66
66
|
}
|
|
67
67
|
export declare const backendClient: BackendClient;
|
|
68
|
+
/**
|
|
69
|
+
* Run `fn` in an async context where `getActiveClient()` returns `client`.
|
|
70
|
+
* Used by the SSE server to scope each connection to its own BackendClient.
|
|
71
|
+
*/
|
|
72
|
+
export declare function runWithClient<T>(client: BackendClient, fn: () => T): T;
|
|
73
|
+
/**
|
|
74
|
+
* Returns the per-session BackendClient when called inside `runWithClient`,
|
|
75
|
+
* otherwise falls back to the module-level singleton (stdio mode).
|
|
76
|
+
*/
|
|
77
|
+
export declare function getActiveClient(): BackendClient;
|
package/dist/backend/client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1
2
|
import axios, { AxiosError } from "axios";
|
|
2
3
|
import { CredentialManager } from "../auth/credentials.js";
|
|
3
4
|
import { DeviceAuthFlow } from "../auth/device-auth.js";
|
|
@@ -352,7 +353,8 @@ export class BackendClient {
|
|
|
352
353
|
return this.userId;
|
|
353
354
|
}
|
|
354
355
|
}
|
|
355
|
-
// Singleton instance
|
|
356
|
+
// Singleton instance — used by stdio server and as the fallback when no
|
|
357
|
+
// session context is active.
|
|
356
358
|
export const backendClient = new BackendClient({
|
|
357
359
|
baseURL: process.env.OPENEPHEMERIS_BACKEND_URL ||
|
|
358
360
|
process.env.ASTROMCP_BACKEND_URL ||
|
|
@@ -363,3 +365,24 @@ export const backendClient = new BackendClient({
|
|
|
363
365
|
serviceKey: process.env.OPENEPHEMERIS_SERVICE_KEY || process.env.ASTROMCP_SERVICE_KEY || process.env.MERIDIAN_SERVICE_KEY,
|
|
364
366
|
apiKey: process.env.OPENEPHEMERIS_API_KEY || process.env.ASTROMCP_API_KEY || process.env.MERIDIAN_API_KEY,
|
|
365
367
|
});
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
// Per-session client context (SSE isolation)
|
|
370
|
+
// ---------------------------------------------------------------------------
|
|
371
|
+
// Each SSE connection creates its own BackendClient keyed to the user's API
|
|
372
|
+
// key. AsyncLocalStorage propagates that client through the entire async call
|
|
373
|
+
// chain of a tool invocation without touching the shared singleton.
|
|
374
|
+
const _sessionClientStore = new AsyncLocalStorage();
|
|
375
|
+
/**
|
|
376
|
+
* Run `fn` in an async context where `getActiveClient()` returns `client`.
|
|
377
|
+
* Used by the SSE server to scope each connection to its own BackendClient.
|
|
378
|
+
*/
|
|
379
|
+
export function runWithClient(client, fn) {
|
|
380
|
+
return _sessionClientStore.run(client, fn);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Returns the per-session BackendClient when called inside `runWithClient`,
|
|
384
|
+
* otherwise falls back to the module-level singleton (stdio mode).
|
|
385
|
+
*/
|
|
386
|
+
export function getActiveClient() {
|
|
387
|
+
return _sessionClientStore.getStore() ?? backendClient;
|
|
388
|
+
}
|
package/dist/server-sse.js
CHANGED
|
@@ -21,7 +21,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
21
21
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
22
22
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
23
23
|
import { initTools, toolRegistry, formatToolResponse } from "./tools/index.js";
|
|
24
|
-
import {
|
|
24
|
+
import { BackendClient, runWithClient } from "./backend/client.js";
|
|
25
25
|
// ---------------------------------------------------------------------------
|
|
26
26
|
// Helpers
|
|
27
27
|
// ---------------------------------------------------------------------------
|
|
@@ -61,6 +61,7 @@ async function validateApiKey(apiKey) {
|
|
|
61
61
|
try {
|
|
62
62
|
const resp = await axios.get(`${BACKEND_URL}/ephemeris/moon/phase`, {
|
|
63
63
|
headers: { "X-API-Key": apiKey },
|
|
64
|
+
params: { datetime: new Date().toISOString() }, // required param per OpenAPI spec
|
|
64
65
|
timeout: 5_000,
|
|
65
66
|
validateStatus: () => true, // don't throw on any status
|
|
66
67
|
});
|
|
@@ -153,6 +154,8 @@ async function main() {
|
|
|
153
154
|
});
|
|
154
155
|
// Track active transports by session ID
|
|
155
156
|
const transports = new Map();
|
|
157
|
+
// Per-session BackendClient — each connection authenticates independently.
|
|
158
|
+
const sessionClients = new Map();
|
|
156
159
|
// Health check
|
|
157
160
|
app.get("/health", (_req, res) => {
|
|
158
161
|
res.json({ ok: true, version, transport: "sse", sessions: transports.size });
|
|
@@ -208,12 +211,16 @@ async function main() {
|
|
|
208
211
|
});
|
|
209
212
|
return;
|
|
210
213
|
}
|
|
211
|
-
// Inject the validated API key into the singleton BackendClient so all
|
|
212
|
-
// tool calls authenticate as this user and usage is metered correctly.
|
|
213
|
-
backendClient.setApiKey(apiKey);
|
|
214
214
|
const transport = new SSEServerTransport("/message", res);
|
|
215
215
|
const sessionId = transport.sessionId;
|
|
216
216
|
transports.set(sessionId, transport);
|
|
217
|
+
// Create a per-session BackendClient so concurrent SSE connections
|
|
218
|
+
// don't overwrite each other's API key on the shared singleton.
|
|
219
|
+
const sessionClient = new BackendClient({
|
|
220
|
+
baseURL: BACKEND_URL,
|
|
221
|
+
apiKey,
|
|
222
|
+
});
|
|
223
|
+
sessionClients.set(sessionId, sessionClient);
|
|
217
224
|
const server = createMcpServer();
|
|
218
225
|
// Guard: prevent double-close from crashing the process
|
|
219
226
|
let closed = false;
|
|
@@ -222,6 +229,7 @@ async function main() {
|
|
|
222
229
|
return;
|
|
223
230
|
closed = true;
|
|
224
231
|
transports.delete(sessionId);
|
|
232
|
+
sessionClients.delete(sessionId);
|
|
225
233
|
try {
|
|
226
234
|
server.close().catch(() => { });
|
|
227
235
|
}
|
|
@@ -243,6 +251,7 @@ async function main() {
|
|
|
243
251
|
app.post("/message", async (req, res) => {
|
|
244
252
|
const sessionId = req.query.sessionId;
|
|
245
253
|
const transport = transports.get(sessionId);
|
|
254
|
+
const sessionClient = sessionClients.get(sessionId);
|
|
246
255
|
if (!transport) {
|
|
247
256
|
res.status(400).json({
|
|
248
257
|
error: "invalid_session",
|
|
@@ -250,7 +259,14 @@ async function main() {
|
|
|
250
259
|
});
|
|
251
260
|
return;
|
|
252
261
|
}
|
|
253
|
-
|
|
262
|
+
// Run handlePostMessage inside the session's client context so all tool
|
|
263
|
+
// handlers reach getActiveClient() and get this user's BackendClient.
|
|
264
|
+
if (sessionClient) {
|
|
265
|
+
await runWithClient(sessionClient, () => transport.handlePostMessage(req, res));
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
await transport.handlePostMessage(req, res);
|
|
269
|
+
}
|
|
254
270
|
});
|
|
255
271
|
app.listen(PORT, "0.0.0.0", () => {
|
|
256
272
|
console.log(`OpenEphemeris MCP SSE server v${version} listening on port ${PORT}`);
|
package/dist/tools/dev.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "./index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../backend/client.js";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -171,7 +171,7 @@ registerTool({
|
|
|
171
171
|
if (isVisualizationPath && !query.format) {
|
|
172
172
|
query.format = "png";
|
|
173
173
|
}
|
|
174
|
-
return await
|
|
174
|
+
return await getActiveClient().request(method, pathname, {
|
|
175
175
|
params: query,
|
|
176
176
|
data: body,
|
|
177
177
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
const ACG_BODY_DESCRIPTION = "List of celestial bodies for line calculation. " +
|
|
4
4
|
"E.g. ['Sun', 'Moon', 'Venus', 'Mars', 'Jupiter', 'Saturn']. " +
|
|
5
5
|
"Aliases: 'NorthNode'/'Node'/'Rahu' → MeanNode, 'SouthNode'/'Ketu' → SouthNode. " +
|
|
@@ -11,6 +11,9 @@ registerTool({
|
|
|
11
11
|
"Returns GeoJSON LineStrings tracing each planetary angle line around the globe. " +
|
|
12
12
|
"These are the latitudinal lines where a planet was rising (AC), setting (DC), " +
|
|
13
13
|
"culminating (MC), or anti-culminating (IC) at birth.\n\n" +
|
|
14
|
+
"❌ NOT FOR: 'Is [city] good for me?' or 'What planets affect me in Tokyo?' " +
|
|
15
|
+
"→ For a specific city/location analysis, use acg_hits instead (faster and more relevant).\n" +
|
|
16
|
+
"✅ USE FOR: Getting the full global GeoJSON line geometry for map rendering or bulk geographic analysis.\n\n" +
|
|
14
17
|
"CREDIT COST: 10 credits per call.\n\n" +
|
|
15
18
|
"EXAMPLE: Saturn and Jupiter power lines for a chart born 1990-04-15 in Chicago:\n" +
|
|
16
19
|
" birth_datetime='1990-04-15T14:30:00', birth_latitude=41.8781, birth_longitude=-87.6298,\n" +
|
|
@@ -68,7 +71,7 @@ registerTool({
|
|
|
68
71
|
body.query_lon = args.query_longitude;
|
|
69
72
|
if (args.radius_deg != null)
|
|
70
73
|
body.radius_deg = args.radius_deg;
|
|
71
|
-
return await
|
|
74
|
+
return await getActiveClient().post("/acg/power-lines", body);
|
|
72
75
|
},
|
|
73
76
|
});
|
|
74
77
|
// POST /acg/hits — OE-018
|
|
@@ -76,6 +79,9 @@ registerTool({
|
|
|
76
79
|
name: "acg_hits",
|
|
77
80
|
description: "Find all Astrocartography lines (power lines + aspect lines) passing near a specific location. " +
|
|
78
81
|
"Returns features sorted by distance, making it easy to interpret planetary influences at a place.\n\n" +
|
|
82
|
+
"✅ USE THIS TOOL FOR: 'Is [city] good for me?', 'What planets affect me in Tokyo?', " +
|
|
83
|
+
"'What ACG lines run through Paris for my chart?', 'Which cities are under my Jupiter line?'\n" +
|
|
84
|
+
"❌ NOT FOR: Full global map geometry → use acg_power_lines for that instead.\n\n" +
|
|
79
85
|
"CREDIT COST: 15 credits per call.\n\n" +
|
|
80
86
|
"EXAMPLE: All ACG lines within 3° of Paris for a chart born 1990-04-15 in Chicago:\n" +
|
|
81
87
|
" birth_datetime='1990-04-15T14:30:00', birth_latitude=41.8781, birth_longitude=-87.6298,\n" +
|
|
@@ -138,6 +144,6 @@ registerTool({
|
|
|
138
144
|
if (args.include_aspects != null) {
|
|
139
145
|
body.options = { include_aspects: args.include_aspects };
|
|
140
146
|
}
|
|
141
|
-
return await
|
|
147
|
+
return await getActiveClient().post("/acg/hits", body);
|
|
142
148
|
},
|
|
143
149
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
registerTool({
|
|
4
4
|
name: "chinese_bazi",
|
|
5
5
|
description: "Calculate a Chinese Ba Zi (Four Pillars of Destiny) chart. Returns the Year, Month, Day, " +
|
|
@@ -62,6 +62,6 @@ registerTool({
|
|
|
62
62
|
const body = { year, month, day };
|
|
63
63
|
if (hour != null)
|
|
64
64
|
body.hour = hour;
|
|
65
|
-
return await
|
|
65
|
+
return await getActiveClient().request("POST", "/chinese/bazi", { data: body });
|
|
66
66
|
},
|
|
67
67
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
const DATETIME_DESC = "ISO 8601 datetime string, e.g. '1990-04-15T14:30:00' (local time at birth location). " +
|
|
4
4
|
"Include timezone offset if known, e.g. '1990-04-15T14:30:00-05:00'.";
|
|
5
5
|
const HOUSE_SYSTEM_MAP = {
|
|
@@ -31,8 +31,8 @@ registerTool({
|
|
|
31
31
|
},
|
|
32
32
|
style: {
|
|
33
33
|
type: "string",
|
|
34
|
-
enum: ["
|
|
35
|
-
description: "Aesthetic style of the chart
|
|
34
|
+
enum: ["light", "dark", "mono"],
|
|
35
|
+
description: "Aesthetic style of the chart: 'light' (default, warm cream background), 'dark' (deep navy), or 'mono' (black-and-white print-ready).",
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
required: ["datetime_a", "latitude_a", "longitude_a", "datetime_b", "latitude_b", "longitude_b"],
|
|
@@ -69,6 +69,6 @@ registerTool({
|
|
|
69
69
|
body.configuration = { house_system: mappedCode };
|
|
70
70
|
}
|
|
71
71
|
const styleParam = args.style ? `&style=${args.style}` : "";
|
|
72
|
-
return await
|
|
72
|
+
return await getActiveClient().post(`/visualization/bi-wheel?format=png&size=800${styleParam}`, body);
|
|
73
73
|
},
|
|
74
74
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
const DATETIME_DESC = "ISO 8601 datetime string, e.g. '1990-04-15T14:30:00' (local time at birth location). " +
|
|
4
4
|
"Include timezone offset if known, e.g. '1990-04-15T14:30:00-05:00'.";
|
|
5
5
|
const HOUSE_SYSTEM_MAP = {
|
|
@@ -39,8 +39,8 @@ registerTool({
|
|
|
39
39
|
},
|
|
40
40
|
style: {
|
|
41
41
|
type: "string",
|
|
42
|
-
enum: ["
|
|
43
|
-
description: "Aesthetic style of the chart
|
|
42
|
+
enum: ["light", "dark", "mono"],
|
|
43
|
+
description: "Aesthetic style of the chart: 'light' (default, warm cream background), 'dark' (deep navy), or 'mono' (black-and-white print-ready).",
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
required: ["datetime", "latitude", "longitude"],
|
|
@@ -66,6 +66,6 @@ registerTool({
|
|
|
66
66
|
body.configuration = { house_system: mappedCode };
|
|
67
67
|
}
|
|
68
68
|
const styleParam = args.style ? `&style=${args.style}` : "";
|
|
69
|
-
return await
|
|
69
|
+
return await getActiveClient().post(`/visualization/chart-wheel?format=png&size=800${styleParam}`, body);
|
|
70
70
|
},
|
|
71
71
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
function buildSubject(name, datetime, lat, lon, timezone) {
|
|
4
4
|
const subj = {
|
|
5
5
|
name,
|
|
@@ -50,7 +50,7 @@ registerTool({
|
|
|
50
50
|
const query = {};
|
|
51
51
|
if (args.format)
|
|
52
52
|
query.format = args.format;
|
|
53
|
-
return await
|
|
53
|
+
return await getActiveClient().request("POST", "/comparative/composite", {
|
|
54
54
|
params: query,
|
|
55
55
|
data: {
|
|
56
56
|
subjects: [
|
|
@@ -94,7 +94,7 @@ registerTool({
|
|
|
94
94
|
const query = {};
|
|
95
95
|
if (args.format)
|
|
96
96
|
query.format = args.format;
|
|
97
|
-
return await
|
|
97
|
+
return await getActiveClient().request("POST", "/comparative/composite/midpoint", {
|
|
98
98
|
params: query,
|
|
99
99
|
data: {
|
|
100
100
|
subjects: [
|
|
@@ -138,7 +138,7 @@ registerTool({
|
|
|
138
138
|
const query = {};
|
|
139
139
|
if (args.format)
|
|
140
140
|
query.format = args.format;
|
|
141
|
-
return await
|
|
141
|
+
return await getActiveClient().request("POST", "/comparative/overlay", {
|
|
142
142
|
params: query,
|
|
143
143
|
data: {
|
|
144
144
|
subjects: [
|
|
@@ -182,7 +182,7 @@ registerTool({
|
|
|
182
182
|
const query = {};
|
|
183
183
|
if (args.format)
|
|
184
184
|
query.format = args.format;
|
|
185
|
-
return await
|
|
185
|
+
return await getActiveClient().request("POST", "/comparative/natal-transits", {
|
|
186
186
|
params: query,
|
|
187
187
|
data: { subjects }
|
|
188
188
|
});
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { registerTool, validateRequired, validateCoordinates } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
registerTool({
|
|
4
4
|
name: "ephemeris_next_eclipse",
|
|
5
|
-
description: "Find the next solar or lunar eclipse
|
|
6
|
-
"Returns the eclipse type, date/time of maximum, magnitude, duration of totality (if any)
|
|
7
|
-
"
|
|
5
|
+
description: "Find the next solar or lunar eclipse. " +
|
|
6
|
+
"Returns the eclipse type, date/time of maximum, magnitude, and duration of totality (if any).\n\n" +
|
|
7
|
+
"📍 LOCATION OPTIONAL:\n" +
|
|
8
|
+
" • WITH latitude+longitude → returns local contact times and visibility for that specific location.\n" +
|
|
9
|
+
" • WITHOUT latitude+longitude → returns the next global eclipse of that type (no location needed).\n\n" +
|
|
10
|
+
"USE THIS TOOL FOR: 'When is the next solar eclipse?', 'When is the next total lunar eclipse?', " +
|
|
11
|
+
"'Will there be an eclipse visible from Tokyo?'\n\n" +
|
|
8
12
|
"CREDIT COST: 1 credit per call.\n\n" +
|
|
9
|
-
"EXAMPLE:
|
|
13
|
+
"EXAMPLE: Next solar eclipse globally (no location needed):\n" +
|
|
14
|
+
" eclipse_type='solar'\n\n" +
|
|
15
|
+
"EXAMPLE: Next solar eclipse visible from New York:\n" +
|
|
10
16
|
" eclipse_type='solar', latitude=40.7128, longitude=-74.006\n\n" +
|
|
11
|
-
"EXAMPLE:
|
|
17
|
+
"EXAMPLE: Next lunar eclipse from London:\n" +
|
|
12
18
|
" eclipse_type='lunar', latitude=51.5074, longitude=-0.1278",
|
|
13
19
|
inputSchema: {
|
|
14
20
|
type: "object",
|
|
@@ -16,35 +22,47 @@ registerTool({
|
|
|
16
22
|
eclipse_type: {
|
|
17
23
|
type: "string",
|
|
18
24
|
enum: ["solar", "lunar", "any"],
|
|
19
|
-
description: "Eclipse type to search for. Use 'any' for whichever comes first.",
|
|
25
|
+
description: "Eclipse type to search for. Use 'any' for whichever comes first (solar or lunar).",
|
|
20
26
|
},
|
|
21
27
|
latitude: {
|
|
22
28
|
type: "number",
|
|
23
|
-
description: "Observer latitude in decimal degrees (
|
|
29
|
+
description: "Observer latitude in decimal degrees (optional). When provided with longitude, returns local contact times and visibility.",
|
|
24
30
|
},
|
|
25
31
|
longitude: {
|
|
26
32
|
type: "number",
|
|
27
|
-
description: "Observer longitude in decimal degrees (
|
|
33
|
+
description: "Observer longitude in decimal degrees (optional, pair with latitude).",
|
|
28
34
|
},
|
|
29
35
|
after_date: {
|
|
30
36
|
type: "string",
|
|
31
37
|
description: "ISO 8601 date to search after (e.g. '2026-01-01'). Defaults to today if omitted.",
|
|
32
38
|
},
|
|
33
39
|
},
|
|
34
|
-
required: ["eclipse_type"
|
|
40
|
+
required: ["eclipse_type"],
|
|
35
41
|
additionalProperties: false,
|
|
36
42
|
},
|
|
37
43
|
handler: async (args) => {
|
|
38
|
-
validateRequired(args, ["eclipse_type"
|
|
44
|
+
validateRequired(args, ["eclipse_type"]);
|
|
39
45
|
validateCoordinates(args, "latitude", "longitude");
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const hasLocation = args.latitude != null && args.longitude != null;
|
|
47
|
+
if (hasLocation) {
|
|
48
|
+
// Local visibility query
|
|
49
|
+
const params = {
|
|
50
|
+
lat: args.latitude,
|
|
51
|
+
lon: args.longitude,
|
|
52
|
+
};
|
|
53
|
+
if (args.after_date)
|
|
54
|
+
params.date = args.after_date;
|
|
55
|
+
if (args.eclipse_type && args.eclipse_type !== "any")
|
|
56
|
+
params.type = args.eclipse_type;
|
|
57
|
+
return await getActiveClient().request("GET", "/eclipse/next-visible", { params, timeoutMs: 60_000 });
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Global query — route to global endpoint
|
|
61
|
+
const params = {};
|
|
62
|
+
if (args.after_date)
|
|
63
|
+
params.date = args.after_date;
|
|
64
|
+
const endpoint = args.eclipse_type === "lunar" ? "/eclipse/lunar/global" : "/eclipse/solar/global";
|
|
65
|
+
return await getActiveClient().request("GET", endpoint, { params, timeoutMs: 60_000 });
|
|
66
|
+
}
|
|
49
67
|
},
|
|
50
68
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
// All electional endpoints are GET endpoints with query params.
|
|
4
4
|
// GET /electional/find-window — already existed, keeping it
|
|
5
5
|
registerTool({
|
|
@@ -74,7 +74,7 @@ registerTool({
|
|
|
74
74
|
query.avoid_voc = args.avoid_voc;
|
|
75
75
|
if (args.format)
|
|
76
76
|
query.format = args.format;
|
|
77
|
-
return await
|
|
77
|
+
return await getActiveClient().request("GET", "/electional/find-window", {
|
|
78
78
|
data: {},
|
|
79
79
|
params: query,
|
|
80
80
|
});
|
|
@@ -111,27 +111,30 @@ registerTool({
|
|
|
111
111
|
query.date = args.date;
|
|
112
112
|
if (args.format)
|
|
113
113
|
query.format = args.format;
|
|
114
|
-
return await
|
|
114
|
+
return await getActiveClient().request("GET", "/electional/moment-analysis", {
|
|
115
115
|
data: {},
|
|
116
116
|
params: query,
|
|
117
117
|
});
|
|
118
118
|
},
|
|
119
119
|
});
|
|
120
|
-
// GET /electional/station-tracker
|
|
121
120
|
registerTool({
|
|
122
121
|
name: "electional_station_tracker",
|
|
123
122
|
description: "Find all upcoming retrograde and direct stations for planets in a date range. " +
|
|
124
|
-
"Returns exact station times, longitudes, and signs
|
|
125
|
-
"Mercury retrograde, Venus retrograde,
|
|
123
|
+
"Returns exact station times, longitudes, and signs.\n\n" +
|
|
124
|
+
"USE THIS TOOL FOR: 'When does Mercury go retrograde?', 'Is Venus retrograde this year?', " +
|
|
125
|
+
"'What planets station this month?', 'When does Mars go direct?'\n\n" +
|
|
126
|
+
"All required fields have smart defaults (searches the next 90 days from today).\n\n" +
|
|
126
127
|
"CREDIT COST: 3 credits per call.\n\n" +
|
|
127
|
-
"EXAMPLE:
|
|
128
|
-
"
|
|
128
|
+
"EXAMPLE: Mercury and Venus stations in the next 3 months (all defaults):\n" +
|
|
129
|
+
" (no args required, will auto-scan next 90 days for all inner planets)\n\n" +
|
|
130
|
+
"EXAMPLE: Outer planet stations in 2026:\n" +
|
|
131
|
+
" start_date='2026-01-01', end_date='2026-12-31', planets='jupiter,saturn,uranus,neptune,pluto'",
|
|
129
132
|
inputSchema: {
|
|
130
133
|
type: "object",
|
|
131
134
|
properties: {
|
|
132
135
|
start_date: {
|
|
133
136
|
type: "string",
|
|
134
|
-
description: "Start date (ISO 8601). Defaults to
|
|
137
|
+
description: "Start date (ISO 8601). Defaults to today.",
|
|
135
138
|
},
|
|
136
139
|
end_date: {
|
|
137
140
|
type: "string",
|
|
@@ -139,7 +142,9 @@ registerTool({
|
|
|
139
142
|
},
|
|
140
143
|
planets: {
|
|
141
144
|
type: "string",
|
|
142
|
-
description: "Comma-separated planet
|
|
145
|
+
description: "Comma-separated planet names to track. " +
|
|
146
|
+
"E.g. 'mercury,venus,mars' or 'jupiter,saturn,uranus,neptune,pluto'. " +
|
|
147
|
+
"Defaults to Mercury through Saturn (inner + classical planets).",
|
|
143
148
|
},
|
|
144
149
|
format: {
|
|
145
150
|
type: "string",
|
|
@@ -152,15 +157,29 @@ registerTool({
|
|
|
152
157
|
},
|
|
153
158
|
handler: async (args) => {
|
|
154
159
|
const query = {};
|
|
160
|
+
// Map human-readable planet names to IDs if needed
|
|
161
|
+
const PLANET_NAME_MAP = {
|
|
162
|
+
mercury: "2", venus: "3", mars: "4",
|
|
163
|
+
jupiter: "5", saturn: "6", uranus: "7",
|
|
164
|
+
neptune: "8", pluto: "9",
|
|
165
|
+
};
|
|
155
166
|
if (args.start_date)
|
|
156
167
|
query.start_date = args.start_date;
|
|
157
168
|
if (args.end_date)
|
|
158
169
|
query.end_date = args.end_date;
|
|
159
|
-
if (args.planets)
|
|
160
|
-
|
|
170
|
+
if (args.planets) {
|
|
171
|
+
// Accept both names ('mercury,venus') and numeric IDs ('2,3')
|
|
172
|
+
query.planets = args.planets
|
|
173
|
+
.split(",")
|
|
174
|
+
.map((p) => {
|
|
175
|
+
const name = p.trim().toLowerCase();
|
|
176
|
+
return PLANET_NAME_MAP[name] ?? p.trim();
|
|
177
|
+
})
|
|
178
|
+
.join(",");
|
|
179
|
+
}
|
|
161
180
|
if (args.format)
|
|
162
181
|
query.format = args.format;
|
|
163
|
-
return await
|
|
182
|
+
return await getActiveClient().request("GET", "/electional/station-tracker", {
|
|
164
183
|
data: {},
|
|
165
184
|
params: query,
|
|
166
185
|
});
|
|
@@ -202,7 +221,7 @@ registerTool({
|
|
|
202
221
|
query.max_orb = args.max_orb;
|
|
203
222
|
if (args.aspects)
|
|
204
223
|
query.aspects = args.aspects;
|
|
205
|
-
return await
|
|
224
|
+
return await getActiveClient().request("GET", "/electional/aspect-search", {
|
|
206
225
|
data: {},
|
|
207
226
|
params: query,
|
|
208
227
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
// POST /ephemeris/planet-position — OE-016
|
|
4
4
|
registerTool({
|
|
5
5
|
name: "ephemeris_planet_position",
|
|
@@ -44,7 +44,7 @@ registerTool({
|
|
|
44
44
|
body.latitude = args.latitude;
|
|
45
45
|
if (args.longitude != null)
|
|
46
46
|
body.longitude = args.longitude;
|
|
47
|
-
return await
|
|
47
|
+
return await getActiveClient().post("/ephemeris/planet-position", body);
|
|
48
48
|
},
|
|
49
49
|
});
|
|
50
50
|
// POST /ephemeris/house-cusps — OE-017
|
|
@@ -89,6 +89,6 @@ registerTool({
|
|
|
89
89
|
};
|
|
90
90
|
if (args.house_systems)
|
|
91
91
|
body.house_systems = args.house_systems;
|
|
92
|
-
return await
|
|
92
|
+
return await getActiveClient().post("/ephemeris/house-cusps", body);
|
|
93
93
|
},
|
|
94
94
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
function buildSubject(name, datetime, lat, lon) {
|
|
4
4
|
return {
|
|
5
5
|
name,
|
|
@@ -47,7 +47,7 @@ registerTool({
|
|
|
47
47
|
const items = args.subjects.map((s) => ({
|
|
48
48
|
subject: buildSubject(s.name, s.datetime, s.latitude, s.longitude),
|
|
49
49
|
}));
|
|
50
|
-
return await
|
|
50
|
+
return await getActiveClient().post("/ephemeris/natal/batch", { items });
|
|
51
51
|
},
|
|
52
52
|
});
|
|
53
53
|
// POST /ephemeris/dignities
|
|
@@ -66,7 +66,7 @@ registerTool({
|
|
|
66
66
|
},
|
|
67
67
|
handler: async (args) => {
|
|
68
68
|
validateRequired(args, ["datetime"]);
|
|
69
|
-
return await
|
|
69
|
+
return await getActiveClient().post("/ephemeris/dignities", {
|
|
70
70
|
date_time: { iso: args.datetime },
|
|
71
71
|
});
|
|
72
72
|
},
|
|
@@ -93,7 +93,7 @@ registerTool({
|
|
|
93
93
|
};
|
|
94
94
|
if (args.planet_id != null)
|
|
95
95
|
body.planet_id = args.planet_id;
|
|
96
|
-
return await
|
|
96
|
+
return await getActiveClient().post("/ephemeris/retrograde-status", body);
|
|
97
97
|
},
|
|
98
98
|
});
|
|
99
99
|
// POST /ephemeris/midpoints
|
|
@@ -114,7 +114,7 @@ registerTool({
|
|
|
114
114
|
},
|
|
115
115
|
handler: async (args) => {
|
|
116
116
|
validateRequired(args, ["datetime", "latitude", "longitude"]);
|
|
117
|
-
return await
|
|
117
|
+
return await getActiveClient().post("/ephemeris/midpoints", {
|
|
118
118
|
date_time: { iso: args.datetime },
|
|
119
119
|
latitude: args.latitude,
|
|
120
120
|
longitude: args.longitude,
|
|
@@ -150,7 +150,7 @@ registerTool({
|
|
|
150
150
|
body.star_names = args.star_names;
|
|
151
151
|
if (args.orb != null)
|
|
152
152
|
body.orb = args.orb;
|
|
153
|
-
return await
|
|
153
|
+
return await getActiveClient().post("/ephemeris/fixed-stars", body);
|
|
154
154
|
},
|
|
155
155
|
});
|
|
156
156
|
// POST /ephemeris/hermetic-lots
|
|
@@ -171,7 +171,7 @@ registerTool({
|
|
|
171
171
|
},
|
|
172
172
|
handler: async (args) => {
|
|
173
173
|
validateRequired(args, ["datetime", "latitude", "longitude"]);
|
|
174
|
-
return await
|
|
174
|
+
return await getActiveClient().post("/ephemeris/hermetic-lots", {
|
|
175
175
|
date_time: { iso: args.datetime },
|
|
176
176
|
latitude: args.latitude,
|
|
177
177
|
longitude: args.longitude,
|
|
@@ -196,7 +196,7 @@ registerTool({
|
|
|
196
196
|
},
|
|
197
197
|
handler: async (args) => {
|
|
198
198
|
validateRequired(args, ["datetime", "latitude", "longitude"]);
|
|
199
|
-
return await
|
|
199
|
+
return await getActiveClient().post("/ephemeris/angles-points", {
|
|
200
200
|
date_time: { iso: args.datetime },
|
|
201
201
|
latitude: args.latitude,
|
|
202
202
|
longitude: args.longitude,
|
|
@@ -229,6 +229,6 @@ registerTool({
|
|
|
229
229
|
};
|
|
230
230
|
if (args.max_orb != null)
|
|
231
231
|
body.max_orb = args.max_orb;
|
|
232
|
-
return await
|
|
232
|
+
return await getActiveClient().post("/ephemeris/aspect-check", body);
|
|
233
233
|
},
|
|
234
234
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
/**
|
|
4
4
|
* Ensure a datetime string has a timezone offset for Go's time.Time parsing.
|
|
5
5
|
*/
|
|
@@ -51,7 +51,7 @@ registerTool({
|
|
|
51
51
|
},
|
|
52
52
|
handler: async (args) => {
|
|
53
53
|
validateRequired(args, ["planet", "datetime", "return_year"]);
|
|
54
|
-
return await
|
|
54
|
+
return await getActiveClient().request("POST", "/human-design/cycles/return", {
|
|
55
55
|
data: {
|
|
56
56
|
planet: args.planet,
|
|
57
57
|
birth_datetime_utc: ensureTimezone(args.datetime),
|
|
@@ -101,7 +101,7 @@ registerTool({
|
|
|
101
101
|
},
|
|
102
102
|
handler: async (args) => {
|
|
103
103
|
validateRequired(args, ["planet", "datetime", "target_year"]);
|
|
104
|
-
return await
|
|
104
|
+
return await getActiveClient().request("POST", "/human-design/cycles/opposition", {
|
|
105
105
|
data: {
|
|
106
106
|
planet: args.planet,
|
|
107
107
|
birth_datetime_utc: ensureTimezone(args.datetime),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
// POST /human-design/composite — OE-027
|
|
4
4
|
registerTool({
|
|
5
5
|
name: "human_design_composite",
|
|
@@ -40,7 +40,7 @@ registerTool({
|
|
|
40
40
|
};
|
|
41
41
|
if (args.format)
|
|
42
42
|
body.format = args.format;
|
|
43
|
-
return await
|
|
43
|
+
return await getActiveClient().post("/human-design/composite", body);
|
|
44
44
|
},
|
|
45
45
|
});
|
|
46
46
|
// POST /human-design/penta — OE-027
|
|
@@ -102,6 +102,6 @@ registerTool({
|
|
|
102
102
|
};
|
|
103
103
|
if (args.format)
|
|
104
104
|
body.format = args.format;
|
|
105
|
-
return await
|
|
105
|
+
return await getActiveClient().post("/human-design/penta", body);
|
|
106
106
|
},
|
|
107
107
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
/**
|
|
4
4
|
* Ensure a datetime string has a timezone offset for Go's time.Time parsing.
|
|
5
5
|
* Go's encoding/json only accepts RFC 3339 (must have Z or +HH:MM offset).
|
|
@@ -61,7 +61,7 @@ registerTool({
|
|
|
61
61
|
const query = {};
|
|
62
62
|
if (args.format)
|
|
63
63
|
query.format = args.format;
|
|
64
|
-
return await
|
|
64
|
+
return await getActiveClient().request("POST", "/human-design/chart", {
|
|
65
65
|
data: body,
|
|
66
66
|
params: query,
|
|
67
67
|
});
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { registerTool, validateCoordinates } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
registerTool({
|
|
4
4
|
name: "ephemeris_moon_phase",
|
|
5
|
-
description: "Get the current
|
|
6
|
-
"(New, Waxing Crescent, First Quarter,
|
|
7
|
-
"
|
|
5
|
+
description: "Get the Moon's current phase angle, illumination, and void-of-course status AT a specific " +
|
|
6
|
+
"point in time. Returns phase name (New, Waxing Crescent, First Quarter, etc.), exact angle, " +
|
|
7
|
+
"illumination %, and next void-of-course period.\n\n" +
|
|
8
|
+
"⚠️ THIS TOOL ANSWERS: 'What phase is the moon in right now (or at a given datetime)?'\n" +
|
|
9
|
+
"❌ THIS TOOL DOES NOT ANSWER: 'When is the next new moon / full moon?'\n" +
|
|
10
|
+
"→ For upcoming phase DATES use ephemeris_next_lunar_phase instead.\n\n" +
|
|
8
11
|
"CREDIT COST: 1 credit per call.\n\n" +
|
|
9
12
|
"If no datetime is provided, returns the current (live) moon phase.\n\n" +
|
|
10
13
|
"EXAMPLE: Get moon phase for a specific date/time:\n" +
|
|
@@ -40,8 +43,8 @@ registerTool({
|
|
|
40
43
|
if (args.longitude != null)
|
|
41
44
|
params.longitude = args.longitude;
|
|
42
45
|
const [phase, voc] = await Promise.allSettled([
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
getActiveClient().request("GET", "/ephemeris/moon/phase", { params }),
|
|
47
|
+
getActiveClient().request("GET", "/ephemeris/moon/void-of-course", { params }),
|
|
45
48
|
]);
|
|
46
49
|
return {
|
|
47
50
|
phase: phase.status === "fulfilled" ? phase.value : { error: phase.reason?.message },
|
|
@@ -49,3 +52,89 @@ registerTool({
|
|
|
49
52
|
};
|
|
50
53
|
},
|
|
51
54
|
});
|
|
55
|
+
registerTool({
|
|
56
|
+
name: "ephemeris_next_lunar_phase",
|
|
57
|
+
description: "Find the next occurrence of a specific Moon phase after a given date. " +
|
|
58
|
+
"Returns the exact UTC datetime, zodiac sign, and degree.\n\n" +
|
|
59
|
+
"✅ USE THIS TOOL FOR: 'When is the next new moon?', 'When is the next full moon?', " +
|
|
60
|
+
"'What date is the next quarter moon?', or any question about UPCOMING phase dates.\n\n" +
|
|
61
|
+
"CREDIT COST: 1 credit per call.\n\n" +
|
|
62
|
+
"EXAMPLE: Find the next new moon:\n" +
|
|
63
|
+
" phase='new_moon'\n\n" +
|
|
64
|
+
"EXAMPLE: Find the next full moon after a specific date:\n" +
|
|
65
|
+
" phase='full_moon', after_date='2026-06-01'\n\n" +
|
|
66
|
+
"EXAMPLE: Find the next 3 full moons:\n" +
|
|
67
|
+
" phase='full_moon', count=3",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
phase: {
|
|
72
|
+
type: "string",
|
|
73
|
+
enum: ["new_moon", "full_moon", "first_quarter", "last_quarter"],
|
|
74
|
+
description: "The lunar phase to find. " +
|
|
75
|
+
"'new_moon' = conjunction (0° Sun-Moon, start of cycle). " +
|
|
76
|
+
"'full_moon' = opposition (180°). " +
|
|
77
|
+
"'first_quarter' = 90° waxing. " +
|
|
78
|
+
"'last_quarter' = 270° waning.",
|
|
79
|
+
},
|
|
80
|
+
after_date: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description: "ISO 8601 date to search from (e.g. '2026-06-01'). Defaults to today (UTC) if omitted.",
|
|
83
|
+
},
|
|
84
|
+
count: {
|
|
85
|
+
type: "integer",
|
|
86
|
+
description: "Number of upcoming occurrences to return (1-12). Default 1.",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
required: ["phase"],
|
|
90
|
+
additionalProperties: false,
|
|
91
|
+
},
|
|
92
|
+
handler: async (args) => {
|
|
93
|
+
// Map our clean enum to display names the calendar API returns
|
|
94
|
+
const phaseNameMap = {
|
|
95
|
+
new_moon: "New Moon",
|
|
96
|
+
full_moon: "Full Moon",
|
|
97
|
+
first_quarter: "First Quarter",
|
|
98
|
+
last_quarter: "Last Quarter",
|
|
99
|
+
};
|
|
100
|
+
const phaseLabel = phaseNameMap[args.phase] ?? args.phase;
|
|
101
|
+
const count = Math.max(1, Math.min(12, args.count ?? 1));
|
|
102
|
+
// Build a search window wide enough to capture `count` occurrences.
|
|
103
|
+
// Lunar cycle is ~29.5 days, so count * 35 days guarantees coverage.
|
|
104
|
+
const startDate = args.after_date
|
|
105
|
+
? args.after_date.substring(0, 10)
|
|
106
|
+
: new Date().toISOString().substring(0, 10);
|
|
107
|
+
const endMs = new Date(startDate + "T00:00:00Z").getTime() + count * 35 * 24 * 60 * 60 * 1000;
|
|
108
|
+
const endDate = new Date(endMs).toISOString().substring(0, 10);
|
|
109
|
+
const result = await getActiveClient().request("GET", "/calendar/astrology/moon-phases", {
|
|
110
|
+
params: { start_date: startDate, end_date: endDate },
|
|
111
|
+
});
|
|
112
|
+
// Normalise response shape — API may return array, or { phases: [...] }, or { data: [...] }
|
|
113
|
+
const allPhases = Array.isArray(result)
|
|
114
|
+
? result
|
|
115
|
+
: result?.phases ?? result?.data ?? result?.events ?? [];
|
|
116
|
+
const matching = allPhases
|
|
117
|
+
.filter((p) => {
|
|
118
|
+
const name = (p.phase_name ?? p.name ?? p.phase ?? "").toLowerCase();
|
|
119
|
+
return (name === phaseLabel.toLowerCase() ||
|
|
120
|
+
name === args.phase.replace("_", " ").toLowerCase());
|
|
121
|
+
})
|
|
122
|
+
.slice(0, count);
|
|
123
|
+
if (matching.length === 0) {
|
|
124
|
+
// Return full calendar as fallback so the LLM can inspect it
|
|
125
|
+
return {
|
|
126
|
+
requested_phase: phaseLabel,
|
|
127
|
+
search_window: { start_date: startDate, end_date: endDate },
|
|
128
|
+
result_count: 0,
|
|
129
|
+
note: "No matching phase found in window. Full calendar returned for inspection.",
|
|
130
|
+
full_calendar: allPhases,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
requested_phase: phaseLabel,
|
|
135
|
+
after_date: startDate,
|
|
136
|
+
results: matching,
|
|
137
|
+
result_count: matching.length,
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
const DATETIME_DESC = "ISO 8601 datetime string, e.g. '1990-04-15T14:30:00' (local time at birth location). " +
|
|
4
4
|
"Include timezone offset if known, e.g. '1990-04-15T14:30:00-05:00'.";
|
|
5
5
|
/** Map human-readable house system names to Swiss Ephemeris single-letter codes */
|
|
@@ -91,7 +91,7 @@ registerTool({
|
|
|
91
91
|
const query = {};
|
|
92
92
|
if (args.format)
|
|
93
93
|
query.format = args.format;
|
|
94
|
-
return await
|
|
94
|
+
return await getActiveClient().request("POST", "/ephemeris/natal-chart", {
|
|
95
95
|
data: body,
|
|
96
96
|
params: query,
|
|
97
97
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
registerTool({
|
|
4
4
|
name: "ephemeris_progressed_chart",
|
|
5
5
|
description: "Calculate a Secondary Progressed (or Solar Arc / Tertiary) chart. " +
|
|
@@ -60,6 +60,6 @@ registerTool({
|
|
|
60
60
|
if (args.include_aspects != null) {
|
|
61
61
|
body.options = { include_aspects: args.include_aspects };
|
|
62
62
|
}
|
|
63
|
-
return await
|
|
63
|
+
return await getActiveClient().post("/ephemeris/progressed", body);
|
|
64
64
|
},
|
|
65
65
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
registerTool({
|
|
4
4
|
name: "ephemeris_relocation",
|
|
5
5
|
description: "Calculate a relocation chart — the same natal planetary positions re-cast for a different " +
|
|
@@ -67,7 +67,7 @@ registerTool({
|
|
|
67
67
|
const query = {};
|
|
68
68
|
if (args.format)
|
|
69
69
|
query.format = args.format;
|
|
70
|
-
return await
|
|
70
|
+
return await getActiveClient().request("POST", "/ephemeris/relocation", {
|
|
71
71
|
data: body,
|
|
72
72
|
params: query,
|
|
73
73
|
});
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
// POST /predictive/returns/solar
|
|
4
4
|
registerTool({
|
|
5
5
|
name: "ephemeris_solar_return",
|
|
6
|
-
description: "Calculate the exact Solar Return date/time — when the Sun returns to its natal ecliptic longitude
|
|
7
|
-
"
|
|
6
|
+
description: "Calculate the exact Solar Return date/time — when the Sun returns to its natal ecliptic longitude " +
|
|
7
|
+
"(happens once per year near the birthday). Returns the Solar Return chart for the year ahead.\n\n" +
|
|
8
8
|
"CREDIT COST: 5 credits per call.\n\n" +
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"TARGET DATE: Provide target_datetime near the desired birthday year. " +
|
|
10
|
+
"If omitted, defaults to the current year's solar return.\n\n" +
|
|
11
|
+
"EXAMPLE: Solar return for someone born 1985-06-21 (current year):\n" +
|
|
12
|
+
" birth_datetime='1985-06-21T14:00:00Z'\n\n" +
|
|
13
|
+
"EXAMPLE: Solar return for 2027 specifically:\n" +
|
|
14
|
+
" birth_datetime='1985-06-21T14:00:00Z', target_datetime='2027-01-01T00:00:00Z'",
|
|
11
15
|
inputSchema: {
|
|
12
16
|
type: "object",
|
|
13
17
|
properties: {
|
|
14
18
|
birth_datetime: {
|
|
15
19
|
type: "string",
|
|
16
|
-
description: "ISO 8601 birth date/time.
|
|
20
|
+
description: "ISO 8601 birth date/time. Include offset or Z (e.g. '1985-06-21T14:00:00-05:00').",
|
|
17
21
|
},
|
|
18
22
|
target_datetime: {
|
|
19
23
|
type: "string",
|
|
20
|
-
description: "Date/time near which to find the Solar Return (ISO 8601
|
|
24
|
+
description: "Date/time near which to find the Solar Return (ISO 8601 with offset or Z). " +
|
|
25
|
+
"Defaults to approximately 1 year from now if omitted.",
|
|
21
26
|
},
|
|
22
27
|
birth_latitude: {
|
|
23
28
|
type: "number",
|
|
@@ -29,21 +34,23 @@ registerTool({
|
|
|
29
34
|
},
|
|
30
35
|
return_latitude: {
|
|
31
36
|
type: "number",
|
|
32
|
-
description: "Location latitude for the return chart (optional).",
|
|
37
|
+
description: "Location latitude for the return chart (optional, if different from birth location).",
|
|
33
38
|
},
|
|
34
39
|
return_longitude: {
|
|
35
40
|
type: "number",
|
|
36
41
|
description: "Location longitude for the return chart (optional).",
|
|
37
42
|
},
|
|
38
43
|
},
|
|
39
|
-
required: ["birth_datetime"
|
|
44
|
+
required: ["birth_datetime"],
|
|
40
45
|
additionalProperties: false,
|
|
41
46
|
},
|
|
42
47
|
handler: async (args) => {
|
|
43
|
-
validateRequired(args, ["birth_datetime"
|
|
48
|
+
validateRequired(args, ["birth_datetime"]);
|
|
49
|
+
// Default target to current year if not provided
|
|
50
|
+
const targetDt = args.target_datetime ?? new Date().toISOString();
|
|
44
51
|
const body = {
|
|
45
52
|
birth_datetime: { iso: args.birth_datetime },
|
|
46
|
-
target_datetime: { iso:
|
|
53
|
+
target_datetime: { iso: targetDt },
|
|
47
54
|
};
|
|
48
55
|
if (args.birth_latitude != null && args.birth_longitude != null) {
|
|
49
56
|
body.location = {
|
|
@@ -57,7 +64,7 @@ registerTool({
|
|
|
57
64
|
longitude: { decimal: args.return_longitude },
|
|
58
65
|
};
|
|
59
66
|
}
|
|
60
|
-
return await
|
|
67
|
+
return await getActiveClient().post("/predictive/returns/solar", body);
|
|
61
68
|
},
|
|
62
69
|
});
|
|
63
70
|
// POST /predictive/returns/lunar
|
|
@@ -66,18 +73,23 @@ registerTool({
|
|
|
66
73
|
description: "Calculate the exact Lunar Return date/time — when the Moon returns to its natal ecliptic longitude (~every 27.3 days). " +
|
|
67
74
|
"Used to cast the monthly Lunar Return chart.\n\n" +
|
|
68
75
|
"CREDIT COST: 5 credits per call.\n\n" +
|
|
69
|
-
"
|
|
70
|
-
"
|
|
76
|
+
"TARGET DATE: Provide target_datetime near the month you want. " +
|
|
77
|
+
"If omitted, defaults to the next upcoming lunar return from today.\n\n" +
|
|
78
|
+
"EXAMPLE: Next lunar return after today for someone born 1990-04-15:\n" +
|
|
79
|
+
" birth_datetime='1990-04-15T14:30:00Z'\n\n" +
|
|
80
|
+
"EXAMPLE: Lunar return nearest March 2026:\n" +
|
|
81
|
+
" birth_datetime='1990-04-15T14:30:00Z', target_datetime='2026-03-15T00:00:00Z'",
|
|
71
82
|
inputSchema: {
|
|
72
83
|
type: "object",
|
|
73
84
|
properties: {
|
|
74
85
|
birth_datetime: {
|
|
75
86
|
type: "string",
|
|
76
|
-
description: "ISO 8601 birth date/time.
|
|
87
|
+
description: "ISO 8601 birth date/time. Include offset or Z.",
|
|
77
88
|
},
|
|
78
89
|
target_datetime: {
|
|
79
90
|
type: "string",
|
|
80
|
-
description: "Date/time near which to find the Lunar Return (ISO 8601
|
|
91
|
+
description: "Date/time near which to find the Lunar Return (ISO 8601 with offset or Z). " +
|
|
92
|
+
"Defaults to today if omitted (finds next return).",
|
|
81
93
|
},
|
|
82
94
|
birth_latitude: {
|
|
83
95
|
type: "number",
|
|
@@ -88,14 +100,15 @@ registerTool({
|
|
|
88
100
|
description: "Birth longitude in decimal degrees (optional).",
|
|
89
101
|
},
|
|
90
102
|
},
|
|
91
|
-
required: ["birth_datetime"
|
|
103
|
+
required: ["birth_datetime"],
|
|
92
104
|
additionalProperties: false,
|
|
93
105
|
},
|
|
94
106
|
handler: async (args) => {
|
|
95
|
-
validateRequired(args, ["birth_datetime"
|
|
107
|
+
validateRequired(args, ["birth_datetime"]);
|
|
108
|
+
const targetDt = args.target_datetime ?? new Date().toISOString();
|
|
96
109
|
const body = {
|
|
97
110
|
birth_datetime: { iso: args.birth_datetime },
|
|
98
|
-
target_datetime: { iso:
|
|
111
|
+
target_datetime: { iso: targetDt },
|
|
99
112
|
};
|
|
100
113
|
if (args.birth_latitude != null && args.birth_longitude != null) {
|
|
101
114
|
body.location = {
|
|
@@ -103,7 +116,7 @@ registerTool({
|
|
|
103
116
|
longitude: { decimal: args.birth_longitude },
|
|
104
117
|
};
|
|
105
118
|
}
|
|
106
|
-
return await
|
|
119
|
+
return await getActiveClient().post("/predictive/returns/lunar", body);
|
|
107
120
|
},
|
|
108
121
|
});
|
|
109
122
|
// POST /predictive/returns (generic planetary return)
|
|
@@ -138,7 +151,7 @@ registerTool({
|
|
|
138
151
|
},
|
|
139
152
|
handler: async (args) => {
|
|
140
153
|
validateRequired(args, ["body", "birth_datetime", "target_datetime"]);
|
|
141
|
-
return await
|
|
154
|
+
return await getActiveClient().post("/predictive/returns", {
|
|
142
155
|
body: args.body,
|
|
143
156
|
birth_datetime: { iso: args.birth_datetime },
|
|
144
157
|
target_datetime: { iso: args.target_datetime },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
registerTool({
|
|
4
4
|
name: "ephemeris_synastry",
|
|
5
5
|
description: "Calculate a synastry chart comparing two people's natal charts. Returns inter-aspects " +
|
|
@@ -63,7 +63,7 @@ registerTool({
|
|
|
63
63
|
const query = {};
|
|
64
64
|
if (args.format)
|
|
65
65
|
query.format = args.format;
|
|
66
|
-
return await
|
|
66
|
+
return await getActiveClient().request("POST", "/comparative/synastry", {
|
|
67
67
|
data: body,
|
|
68
68
|
params: query,
|
|
69
69
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
registerTool({
|
|
4
4
|
name: "ephemeris_transits",
|
|
5
5
|
description: "Search for astrological transit events affecting a natal chart over a date range. " +
|
|
@@ -76,7 +76,7 @@ registerTool({
|
|
|
76
76
|
},
|
|
77
77
|
},
|
|
78
78
|
};
|
|
79
|
-
const natalResult = await
|
|
79
|
+
const natalResult = await getActiveClient().post("/ephemeris/natal-chart", natalBody);
|
|
80
80
|
// Extract ecliptic longitudes from natal chart planets
|
|
81
81
|
const targetDegrees = [];
|
|
82
82
|
const natalPositionMap = {};
|
|
@@ -141,7 +141,7 @@ registerTool({
|
|
|
141
141
|
// Also include aspect metadata for labeling in results
|
|
142
142
|
transitBody.search_criteria = { aspect_angle: args.aspect_angle };
|
|
143
143
|
}
|
|
144
|
-
const transitResult = await
|
|
144
|
+
const transitResult = await getActiveClient().request("POST", "/predictive/transits/search", { data: transitBody });
|
|
145
145
|
// Return combined context so the LLM knows what natal positions were targeted
|
|
146
146
|
return {
|
|
147
147
|
natal_positions: natalPositionMap,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired, validateCoordinates } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
registerTool({
|
|
4
4
|
name: "vedic_chart",
|
|
5
5
|
description: "Calculate a Vedic (Jyotish) natal chart with sidereal positions. Returns planet placements " +
|
|
@@ -43,6 +43,6 @@ registerTool({
|
|
|
43
43
|
};
|
|
44
44
|
if (args.ayanamsa)
|
|
45
45
|
body.ayanamsa = args.ayanamsa;
|
|
46
|
-
return await
|
|
46
|
+
return await getActiveClient().request("POST", "/vedic/chart", { data: body });
|
|
47
47
|
},
|
|
48
48
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerTool, validateRequired } from "../index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
function buildSubject(datetime, lat, lon) {
|
|
4
4
|
return {
|
|
5
5
|
name: "Subject",
|
|
@@ -34,7 +34,7 @@ registerTool({
|
|
|
34
34
|
const query = {};
|
|
35
35
|
if (args.format)
|
|
36
36
|
query.format = args.format;
|
|
37
|
-
return await
|
|
37
|
+
return await getActiveClient().request("POST", "/ephemeris/venus-star-points", {
|
|
38
38
|
data: { subject: buildSubject(args.datetime, args.latitude, args.longitude) },
|
|
39
39
|
params: query,
|
|
40
40
|
});
|
|
@@ -63,7 +63,7 @@ registerTool({
|
|
|
63
63
|
const query = {};
|
|
64
64
|
if (args.format)
|
|
65
65
|
query.format = args.format;
|
|
66
|
-
return await
|
|
66
|
+
return await getActiveClient().request("POST", "/ephemeris/venus-star-points/conjunctions", {
|
|
67
67
|
data: { start_date: args.start_date, end_date: args.end_date },
|
|
68
68
|
params: query,
|
|
69
69
|
});
|
|
@@ -92,7 +92,7 @@ registerTool({
|
|
|
92
92
|
const query = {};
|
|
93
93
|
if (args.format)
|
|
94
94
|
query.format = args.format;
|
|
95
|
-
return await
|
|
95
|
+
return await getActiveClient().request("POST", "/ephemeris/venus-star-points/eight-year-star", {
|
|
96
96
|
data: { date: args.date },
|
|
97
97
|
params: query,
|
|
98
98
|
});
|
|
@@ -104,24 +104,25 @@ registerTool({
|
|
|
104
104
|
description: "Get the current Venus phase — morning star, evening star, or combust. Returns Venus " +
|
|
105
105
|
"longitude, Sun longitude, elongation, retrograde/cazimi status.\n\n" +
|
|
106
106
|
"CREDIT COST: 1 credit per call.\n\n" +
|
|
107
|
-
"EXAMPLE: Venus phase right now:\n" +
|
|
107
|
+
"EXAMPLE: Venus phase right now (omit date):\n" +
|
|
108
|
+
" (no args required)\n\n" +
|
|
109
|
+
"EXAMPLE: Venus phase on a specific date:\n" +
|
|
108
110
|
" date='2026-03-20'",
|
|
109
111
|
inputSchema: {
|
|
110
112
|
type: "object",
|
|
111
113
|
properties: {
|
|
112
|
-
date: { type: "string", description: "Date to check (ISO 8601).
|
|
114
|
+
date: { type: "string", description: "Date to check (ISO 8601). Omit for today's phase." },
|
|
113
115
|
format: { type: "string", enum: ["json", "llm"], description: "Output format." },
|
|
114
116
|
},
|
|
115
|
-
required: [
|
|
117
|
+
required: [],
|
|
116
118
|
additionalProperties: false,
|
|
117
119
|
},
|
|
118
120
|
handler: async (args) => {
|
|
119
|
-
validateRequired(args, ["date"]);
|
|
120
121
|
const query = {};
|
|
121
122
|
if (args.format)
|
|
122
123
|
query.format = args.format;
|
|
123
|
-
return await
|
|
124
|
-
data: { date: args.date },
|
|
124
|
+
return await getActiveClient().request("POST", "/ephemeris/venus-star-points/phase", {
|
|
125
|
+
data: { date: args.date ?? new Date().toISOString().split("T")[0] },
|
|
125
126
|
params: query,
|
|
126
127
|
});
|
|
127
128
|
},
|
|
@@ -147,7 +148,7 @@ registerTool({
|
|
|
147
148
|
const query = {};
|
|
148
149
|
if (args.format)
|
|
149
150
|
query.format = args.format;
|
|
150
|
-
return await
|
|
151
|
+
return await getActiveClient().request("POST", "/ephemeris/venus-star-points/elongations", {
|
|
151
152
|
data: { start_date: args.start_date, end_date: args.end_date },
|
|
152
153
|
params: query,
|
|
153
154
|
});
|
|
@@ -176,7 +177,7 @@ registerTool({
|
|
|
176
177
|
const query = {};
|
|
177
178
|
if (args.format)
|
|
178
179
|
query.format = args.format;
|
|
179
|
-
return await
|
|
180
|
+
return await getActiveClient().request("POST", "/ephemeris/venus-star-points/stations", {
|
|
180
181
|
data: {
|
|
181
182
|
start_date: args.start_date,
|
|
182
183
|
end_date: args.end_date || "",
|