@openephemeris/mcp-server 4.15.0 → 4.16.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +29 -13
  3. package/dist/backend/client.d.ts +2 -0
  4. package/dist/backend/client.js +15 -10
  5. package/dist/index.js +3 -12
  6. package/dist/instructions.d.ts +1 -1
  7. package/dist/instructions.js +5 -3
  8. package/dist/prompts.js +2 -2
  9. package/dist/server-sse.js +3 -12
  10. package/dist/tools/apps/bazi-app.js +10 -10
  11. package/dist/tools/apps/bi-wheel-app.js +14 -14
  12. package/dist/tools/apps/bodygraph-app.js +35 -29
  13. package/dist/tools/apps/chart-wheel-app.js +12 -9
  14. package/dist/tools/apps/location-tools.js +17 -4
  15. package/dist/tools/apps/moon-phase-app.js +12 -11
  16. package/dist/tools/apps/transit-timeline-app.js +13 -14
  17. package/dist/tools/apps/vedic-chart-app.js +9 -8
  18. package/dist/tools/auth.js +14 -12
  19. package/dist/tools/dev.js +17 -29
  20. package/dist/tools/invocation-status.d.ts +25 -0
  21. package/dist/tools/invocation-status.js +69 -0
  22. package/dist/tools/specialized/account.js +6 -10
  23. package/dist/tools/specialized/acg.js +16 -21
  24. package/dist/tools/specialized/bazi.js +7 -12
  25. package/dist/tools/specialized/bi_wheel.js +1 -1
  26. package/dist/tools/specialized/chart_wheel.js +1 -1
  27. package/dist/tools/specialized/eclipse.js +6 -13
  28. package/dist/tools/specialized/electional.js +30 -37
  29. package/dist/tools/specialized/ephemeris_core.js +21 -14
  30. package/dist/tools/specialized/ephemeris_extended.js +22 -19
  31. package/dist/tools/specialized/hd_bodygraph.js +2 -2
  32. package/dist/tools/specialized/human_design.js +11 -14
  33. package/dist/tools/specialized/moon.js +15 -26
  34. package/dist/tools/specialized/natal.js +9 -8
  35. package/dist/tools/specialized/progressed.js +6 -6
  36. package/dist/tools/specialized/relocation.js +6 -7
  37. package/dist/tools/specialized/returns.js +6 -8
  38. package/dist/tools/specialized/synastry.js +7 -8
  39. package/dist/tools/specialized/transits.js +15 -14
  40. package/dist/tools/specialized/vedic.js +7 -6
  41. package/dist/tools/ui-meta.d.ts +14 -0
  42. package/dist/tools/ui-meta.js +57 -0
  43. package/dist/ui/bazi.html +6 -1
  44. package/dist/ui/bi-wheel.html +505 -460
  45. package/dist/ui/bodygraph.html +176 -150
  46. package/dist/ui/chart-wheel.html +443 -396
  47. package/dist/ui/moon-phase.html +1 -1
  48. package/dist/ui/transit-timeline.html +2 -2
  49. package/dist/ui/vedic-chart.html +82 -56
  50. package/package.json +9 -4
  51. package/smithery.yaml +3 -1
@@ -92,21 +92,20 @@ function buildSummary(transits, aspectLabel, win) {
92
92
  // ── Tool: explore_transit_timeline ───────────────────────────────────────────
93
93
  registerTool({
94
94
  name: "explore_transit_timeline",
95
- description: "Generate an interactive Transit Timeline — a vertical, date-ordered list of " +
96
- "upcoming transit hits (transiting planets forming a chosen aspect to natal " +
97
- "chart positions) over a date range.\n\n" +
98
- "Returns a visual timeline with:\n" +
99
- " • Exact crossing dates grouped by month\n" +
100
- " • Transiting planet glyph, the natal point it contacts, and the aspect\n" +
101
- " • Zodiac position of each crossing and retrograde markers\n" +
102
- " • Click any transit for a focused interpretation\n\n" +
103
- "ASPECT ANGLES: 0 = conjunction/return (default), 180 = opposition, 90 = square, " +
104
- "120 = trine, 60 = sextile. EFFICIENCY: specify transiting_planets and natal_points " +
105
- "to keep compute fast. DEFAULT natal_points: sun, moon, mercury, venus, mars, jupiter, saturn. " +
106
- "SEARCH RANGE LIMITS: Explorer/PayG → 1 year; Pro → 5 years; Startup → 10 years.\n\n" +
95
+ description: "Use this when the user asks 'what transits are coming up for me', 'what's my forecast for the " +
96
+ "next year', 'when will Saturn hit my chart', 'show my upcoming transits' or 'what's happening " +
97
+ "astrologically for me in 2027' — a forecast they should SEE. Returns an interactive, " +
98
+ "date-ordered timeline (inline in Claude and ChatGPT; text summary elsewhere) of exact transit " +
99
+ "hits grouped by month — transiting planet glyph, the natal point it contacts, the aspect, zodiac " +
100
+ "position and retrograde markers — each clickable for a focused interpretation.\n\n" +
101
+ "ASPECT ANGLES: 0 = conjunction/return (default), 180 = opposition, 90 = square, 120 = trine, 60 " +
102
+ "= sextile. EFFICIENCY: specify transiting_planets and natal_points to keep compute fast. DEFAULT " +
103
+ "natal_points: sun, moon, mercury, venus, mars, jupiter, saturn. SEARCH RANGE LIMITS: " +
104
+ "Explorer/PayG → 1 year; Pro → 5 years; Startup → 10 years.\n\n" +
107
105
  "CREDIT COST: 6 credits per call (natal chart + predictive transit search).\n\n" +
108
- "Use this for a rich, interactive transit-forecast experience in MCP Apps-capable hosts " +
109
- "(Claude Desktop). Falls back to a text summary in other hosts.",
106
+ "Do not use for transit data as JSON — use ephemeris_transits; for the sky today with no birth " +
107
+ "data use electional_moment_analysis; to see one date's transits drawn around the natal wheel use " +
108
+ "explore_bi_wheel (mode='transit').",
110
109
  inputSchema: {
111
110
  type: "object",
112
111
  properties: {
@@ -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: "Generate an interactive Vedic (Jyotish) birth chart as a South Indian fixed-sign Rashi grid, " +
100
- "with clickable rashis showing sidereal placements, nakshatras, and the Lagna.\n\n" +
101
- "CREDIT COST: 3 credits per call (chart calculation + visual render).\n\n" +
102
- "Returns an embedded visual explorer that lets you click any rashi cell for its themes and " +
103
- "any planets placed there. Shows sidereal (Lahiri by default) planet placements, nakshatra with " +
104
- "pada, navamsa, and the Lagna (Ascendant) rashi. Uses NASA JPL DE440 ephemerides. " +
105
- "Use this for a rich, interactive Jyotish experience in MCP Apps-capable hosts (Claude Desktop). " +
106
- "Falls back to a text summary in other hosts.",
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: {
@@ -8,12 +8,12 @@ const credentialManager = new CredentialManager();
8
8
  // ────────────────────────────────────────────────────────
9
9
  registerTool({
10
10
  name: "auth_login",
11
- description: "Start the device authorization flow to connect this MCP server to your OpenEphemeris account " +
12
- "(free tier, no credit card needed). " +
13
- "Returns a verification URL and code for the user to enter in their browser. " +
14
- "The MCP server will then automatically receive credentials and all API calls will be " +
15
- "linked to the user's account (tier, credits, rate limits). " +
16
- "Only needed if no OPENEPHEMERIS_API_KEY env var is set and no cached credentials exist.",
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: "Check the current authentication status of this MCP server. " +
100
- "Shows whether the server is authenticated, which account it's linked to, " +
101
- "the authentication method (API key, JWT, device auth), and token expiry.",
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: "Disconnect this MCP server from your OpenEphemeris account by clearing " +
188
- "cached credentials. Does NOT revoke the API key if one is set via environment " +
189
- "variable — only clears device-auth cached credentials.",
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
- "AUTH: Set OPENEPHEMERIS_API_KEY in your environment. See openephemeris.com/dashboard for active plan limits.\n\n" +
48
- "CREDIT COSTS:\n" +
49
- " • Standard chart math (natal, progressed, bazi, vedic, iching): 1 credit\n" +
50
- " • Human Design: 2 credits\n" +
51
- " • Visualization rendering (chart-wheel, bi-wheel, charts/*): 2 credits\n" +
52
- " • Comparative math (synastry, composite, overlay): 3 credits\n" +
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: "Read from any allowlisted Open Ephemeris API endpoint via HTTP GET. This is the read-only " +
200
- "power-user escape hatch — use the typed tools (ephemeris_natal_chart, ephemeris_transits, etc.) " +
201
- "first for common operations. Endpoints that compute via POST (natal-chart, synastry, etc.) " +
202
- "are covered by the typed tools; the generic POST proxy is only exposed on the full tool " +
203
- "surface (`?profile=full`).\n\n" +
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: "List all API operations (method + path) that this MCP instance is authorized to call. " +
237
- "Returns endpoint entries grouped by method, plus the active deny rules. " +
238
- "Use this to discover what's available before calling dev_read_api, or to verify an endpoint path. " +
239
- "Typed shortcut tools (ephemeris_natal_chart, ephemeris_transits, etc.) cover the most common operations — " +
240
- "check those first before reaching for the generic proxies.",
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: "Check the user's OpenEphemeris account usage and remaining credits. " +
9
- "Returns their plan tier, billing period, credits used / included / remaining, " +
10
- "percent of quota used, total API calls, and subscription status with renewal date.\n\n" +
11
- "✅ USE THIS TOOL FOR: 'How many credits do I have left?', 'What's my usage this month?', " +
12
- "'Am I close to my limit?', 'What plan am I on?', 'How do I upgrade or top up?'\n\n" +
13
- "CREDIT COST: Free (0 credits).\n\n" +
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: {
@@ -10,17 +10,14 @@ const ACG_BODY_DESCRIPTION = "List of celestial bodies for line calculation. " +
10
10
  // POST /acg/power-lines — OE-018
11
11
  registerTool({
12
12
  name: "acg_power_lines",
13
- description: "Calculate Astrocartography power lines (MC, IC, AC/ASC, DC/DSC) for a natal chart. " +
14
- "Returns GeoJSON LineStrings tracing each planetary angle line around the globe. " +
15
- "These are the latitudinal lines where a planet was rising (AC), setting (DC), " +
16
- "culminating (MC), or anti-culminating (IC) at birth.\n\n" +
17
- "❌ NOT FOR: 'Is [city] good for me?' or 'What planets affect me in Tokyo?' " +
18
- "→ For a specific city/location analysis, use acg_hits instead (faster and more relevant).\n" +
19
- "✅ USE FOR: Getting the full global GeoJSON line geometry for map rendering or bulk geographic analysis.\n\n" +
20
- "CREDIT COST: 10 credits per call.\n\n" +
21
- "EXAMPLE: Saturn and Jupiter power lines for a chart born 1990-04-15 at 2:30 PM in Chicago:\n" +
22
- " birth_datetime='1990-04-15T14:30:00', timezone='America/Chicago',\n" +
23
- " birth_latitude=41.8781, birth_longitude=-87.6298, bodies=['Saturn', 'Jupiter']",
13
+ description: "Use this when the user asks 'show my astrocartography map', 'where are my Jupiter lines', 'where " +
14
+ "in the world is my Sun on the MC', or wants the whole-world list of their lines. Returns, per " +
15
+ "planet, its MC, IC, AC and DC lines (where it was culminating, anti-culminating, rising or " +
16
+ "setting at birth) with sign and equator-crossing longitude — geometry stripped, not for map " +
17
+ "rendering.\n\n" +
18
+ "CREDIT COST: 10 credits per call. Pro tier.\n\n" +
19
+ "Do not use for 'is [city] good for me' or 'what lines run through Paris' — use acg_hits (faster " +
20
+ "and more relevant); for house changes in a new city use ephemeris_relocation.",
24
21
  inputSchema: {
25
22
  type: "object",
26
23
  properties: {
@@ -104,16 +101,14 @@ registerTool({
104
101
  // POST /acg/hits — OE-018
105
102
  registerTool({
106
103
  name: "acg_hits",
107
- description: "Find all Astrocartography lines (power lines + aspect lines) passing near a specific location. " +
108
- "Returns features sorted by distance, making it easy to interpret planetary influences at a place.\n\n" +
109
- "✅ USE THIS TOOL FOR: 'Is [city] good for me?', 'What planets affect me in Tokyo?', " +
110
- "'What ACG lines run through Paris for my chart?', 'Which cities are under my Jupiter line?'\n" +
111
- "❌ NOT FOR: Full global map geometry → use acg_power_lines for that instead.\n\n" +
112
- "CREDIT COST: 10 credits per call.\n\n" +
113
- "EXAMPLE: All ACG lines within 3° of Paris for a chart born 1990-04-15 in Chicago:\n" +
114
- " birth_datetime='1990-04-15T14:30:00', timezone='America/Chicago',\n" +
115
- " birth_latitude=41.8781, birth_longitude=-87.6298,\n" +
116
- " query_latitude=48.8566, query_longitude=2.3522, radius_deg=3",
104
+ description: "Use this when the user asks 'is Tokyo good for me', 'where should I live according to my chart' " +
105
+ "(needs a candidate place — ask for one), 'what planets affect me in Paris', 'which of my lines " +
106
+ "run through Lisbon' or 'should I move to Austin'. Returns every astrocartography line (power " +
107
+ "lines and aspect lines) within radius_deg of a query location, sorted by distance, for a given " +
108
+ "birth chart.\n\n" +
109
+ "CREDIT COST: 10 credits per call. Pro tier.\n\n" +
110
+ "Do not use for the whole global map — use acg_power_lines; for a relocated chart use " +
111
+ "ephemeris_relocation.",
117
112
  inputSchema: {
118
113
  type: "object",
119
114
  properties: {
@@ -385,19 +385,14 @@ registerTool({
385
385
  // ─────────────────────────────────────────────────────────────────────────────
386
386
  registerTool({
387
387
  name: "bazi_annual_pillar",
388
- description: "Look up the sexagenary pillar for any Gregorian year (1–9999). Returns the Heavenly Stem, " +
389
- "Earthly Branch, Chinese characters, zodiac animal, element, polarity, and NaYin (纳音) " +
390
- "poetic resonance image.\n\n" +
391
- "NaYin maps each pair in the 60-cycle sexagenary sequence to one of 30 elemental images " +
392
- "(e.g. '海中金 Metal in the Sea', '炉中火 Fire in the Furnace'). It is traditionally " +
393
- "applied to the Year and Day pillars to reveal deeper elemental character.\n\n" +
394
- "Use this to:\n" +
395
- " • Identify the energetic quality of any given year\n" +
396
- " • Determine a person's birth year pillar for compatibility context\n" +
397
- " • Find the NaYin element for year or day interpretations\n\n" +
388
+ description: "Use this when the user asks 'what element is the year of the Snake', 'what's the pillar, stem " +
389
+ "and branch or NaYin for my birth year' or 'what's the energy of this year' — a single-year " +
390
+ "lookup beyond the animal. Returns the year's Heavenly Stem, Earthly Branch, Chinese characters, " +
391
+ "zodiac animal, element, polarity and NaYin (纳音) resonance image for any Gregorian year 1-9999.\n\n" +
398
392
  "CREDIT COST: 3 credits per call.\n\n" +
399
- "EXAMPLE: Year pillar for 2025:\n" +
400
- " year=2025",
393
+ "Do not use for just the zodiac animal — dev_read_api path='/chinese/zodiac' (1 credit, all " +
394
+ "tiers); for a Jan-Feb birth the animal turns at Lunar New Year, so use explore_bazi_chart; for a " +
395
+ "person's full Four Pillars use explore_bazi_chart.",
401
396
  inputSchema: {
402
397
  type: "object",
403
398
  properties: {
@@ -10,7 +10,7 @@ const HOUSE_SYSTEM_MAP = {
10
10
  };
11
11
  registerTool({
12
12
  name: "ephemeris_bi_wheel",
13
- description: "Generate a Bi-Wheel (Synastry/Transit) image (SVG) comparing two charts. Draws Subject A's planets on the inside wheel and Subject B's on the outside wheel. Returns a native SVG that Claude displays inline in the conversation.\n\n" +
13
+ description: "Generate a Bi-Wheel (Synastry/Transit) image (SVG) comparing two charts. Draws Subject A's planets on the inside wheel and Subject B's on the outside wheel. Returns a native SVG the host displays inline in the conversation.\n\n" +
14
14
  "For a user-facing interactive bi-wheel, use explore_bi_wheel instead.\n\n" +
15
15
  "CREDIT COST: 2 credits per call.\n\n" +
16
16
  "EXAMPLE: Compare someone born April 15, 1990 (A) to someone born June 10, 1992 (B):\n" +
@@ -10,7 +10,7 @@ const HOUSE_SYSTEM_MAP = {
10
10
  };
11
11
  registerTool({
12
12
  name: "ephemeris_chart_wheel",
13
- description: "Generate a classic astrological Chart Wheel image (SVG) for a person/event. This draws a standard circular chart wheel with planets, aspects, and house cusps. The tool returns a native SVG image that Claude displays inline in the conversation — no external tools needed.\n\n" +
13
+ description: "Generate a classic astrological Chart Wheel image (SVG) for a person/event. This draws a standard circular chart wheel with planets, aspects, and house cusps. The tool returns a native SVG image the host displays inline in the conversation — no external tools needed.\n\n" +
14
14
  "For a user-facing interactive chart wheel, use explore_natal_chart instead.\n\n" +
15
15
  "CREDIT COST: 2 credits per call.\n\n" +
16
16
  "EXAMPLE: born 15 April 1990 at 2:30 PM local time in Chicago:\n" +
@@ -3,20 +3,13 @@ import { getActiveClient } from "../../backend/client.js";
3
3
  import { OUTPUT_SCHEMA_JSON } from "../output-schemas.js";
4
4
  registerTool({
5
5
  name: "ephemeris_next_eclipse",
6
- description: "Find the next solar or lunar eclipse. " +
7
- "Returns the eclipse type, date/time of maximum, magnitude, and duration of totality (if any).\n\n" +
8
- "📍 LOCATION OPTIONAL:\n" +
9
- " • WITH latitude+longitude → returns local contact times and visibility for that specific location.\n" +
10
- " • WITHOUT latitude+longitude → returns the next global eclipse of that type (no location needed).\n\n" +
11
- "USE THIS TOOL FOR: 'When is the next solar eclipse?', 'When is the next total lunar eclipse?', " +
12
- "'Will there be an eclipse visible from Tokyo?'\n\n" +
6
+ description: "Use this when the user asks 'when is the next eclipse', 'when is the next solar / lunar " +
7
+ "eclipse', 'is there an eclipse visible from Tokyo' or 'when is the next total lunar eclipse'. " +
8
+ "Returns the eclipse type, date/time of maximum, magnitude and duration of totality; with " +
9
+ "latitude+longitude it adds local contact times and visibility for that place, without them it " +
10
+ "returns the next global eclipse of that type.\n\n" +
13
11
  "CREDIT COST: 1 credit per call.\n\n" +
14
- "EXAMPLE: Next solar eclipse globally (no location needed):\n" +
15
- " eclipse_type='solar'\n\n" +
16
- "EXAMPLE: Next solar eclipse visible from New York:\n" +
17
- " eclipse_type='solar', latitude=40.7128, longitude=-74.006\n\n" +
18
- "EXAMPLE: Next lunar eclipse from London:\n" +
19
- " eclipse_type='lunar', latitude=51.5074, longitude=-0.1278",
12
+ "Do not use for ordinary new/full moon dates — use ephemeris_next_lunar_phase.",
20
13
  inputSchema: {
21
14
  type: "object",
22
15
  properties: {
@@ -6,13 +6,13 @@ import { DATETIME_DESC, WINDOW_DATE_DESC, localToUtcIso, timezoneProperty } from
6
6
  // GET /electional/find-window — already existed, keeping it
7
7
  registerTool({
8
8
  name: "ephemeris_electional",
9
- description: "Find optimal planetary timing windows (electional astrology). Scans a date range to find the " +
10
- "best times for an event based on essential dignity, aspect quality, sect, and void-of-course " +
11
- "moon penalties. Evaluates every hour and clusters the best continuous windows.\n\n" +
12
- "CREDIT COST: 5 credits per call (heavy calculation).\n\n" +
13
- "EXAMPLE: Find the best time to launch a business in early March 2026.\n" +
14
- " start_date='2026-03-01', end_date='2026-03-10', latitude=40.7128, longitude=-74.0060,\n" +
15
- " avoid_voc=true, lunar_phase='waxing'",
9
+ description: "Use this when the user asks 'when is the best time to launch / sign / marry / start X' within a " +
10
+ "date range, 'find me a good day next month' or 'pick an auspicious window'. Returns the best " +
11
+ "continuous timing windows in the range, scored hourly on essential dignity, aspect quality, sect " +
12
+ "and void-of-course Moon penalties, with optional filters (avoid_voc, lunar_phase).\n\n" +
13
+ "CREDIT COST: 5 credits per call. Pro tier.\n\n" +
14
+ "Do not use to score one specific moment or 'right now' — use electional_moment_analysis; for " +
15
+ "retrograde dates use electional_station_tracker.",
16
16
  inputSchema: {
17
17
  type: "object",
18
18
  properties: {
@@ -100,12 +100,14 @@ registerTool({
100
100
  // omits them gets a wrong-half-of-the-day score that is at least labelled.
101
101
  registerTool({
102
102
  name: "electional_moment_analysis",
103
- description: "Analyze the astrological quality of a specific moment: planet positions, aspects, " +
104
- "void of course status, lunar phase, day ruler, sect, and an overall electional score (0-100). " +
105
- "Perfect for evaluating whether 'right now' or a specific date/time is good for action.\n\n" +
103
+ description: "Use this when the user asks 'is now a good time to …', 'what's the sky doing today', 'what's the " +
104
+ "astrological weather', 'how is this date astrologically' — or has no birth data (it works with " +
105
+ "no arguments). Returns planet positions, " +
106
+ "aspects, void-of-course status, lunar phase, day ruler, sect and an overall electional score " +
107
+ "(0-100) for one moment.\n\n" +
106
108
  "CREDIT COST: 5 credits per call.\n\n" +
107
- "EXAMPLE: Analyze March 21, 2026 at noon in New York:\n" +
108
- " date='2026-03-21T12:00:00Z', latitude=40.7128, longitude=-74.0060",
109
+ "Do not use to search a date range for the best window — use ephemeris_electional; for the Moon " +
110
+ "alone use explore_moon_phase.",
109
111
  inputSchema: {
110
112
  type: "object",
111
113
  properties: {
@@ -153,18 +155,14 @@ registerTool({
153
155
  });
154
156
  registerTool({
155
157
  name: "electional_station_tracker",
156
- description: "Find all upcoming retrograde and direct stations for planets in a date range. " +
157
- "Returns exact station times, longitudes, and signs.\n\n" +
158
- "USE THIS TOOL FOR: 'When does Mercury go retrograde?', 'Is Venus retrograde this year?', " +
159
- "'What planets station this month?', 'When does Mars go direct?'\n\n" +
160
- "❌ NOT FOR: 'Is Mercury retrograde right now?' — that is the state at a single instant, " +
161
- "so use ephemeris_retrograde_status (1 credit for one planet, vs 5 here).\n\n" +
162
- "All required fields have smart defaults (searches the next 90 days from today).\n\n" +
158
+ description: "Use this when the user asks 'when does Mercury go retrograde', 'when does Mercury retrograde " +
159
+ "end', 'when does Mars go direct', 'when does Pluto go direct', 'is Venus retrograde this year', " +
160
+ "'Mercury retrograde dates for 2026' or 'what planets station this month'. Returns every " +
161
+ "retrograde and direct station in the range with exact time, longitude and sign — Mercury " +
162
+ "through Pluto; default range the next 90 days, maximum one year.\n\n" +
163
163
  "CREDIT COST: 5 credits per call.\n\n" +
164
- "EXAMPLE: Mercury and Venus stations in the next 3 months (all defaults):\n" +
165
- " (no args required, will auto-scan next 90 days for all inner planets)\n\n" +
166
- "EXAMPLE: Outer planet stations in 2026:\n" +
167
- " start_date='2026-01-01', end_date='2026-12-31', planets='jupiter,saturn,uranus,neptune,pluto'",
164
+ "Do not use for 'is Mercury retrograde right now' — that is one instant, use " +
165
+ "ephemeris_retrograde_status (1 credit).",
168
166
  inputSchema: {
169
167
  type: "object",
170
168
  properties: {
@@ -178,9 +176,9 @@ registerTool({
178
176
  },
179
177
  planets: {
180
178
  type: "string",
181
- description: "Comma-separated planet names to track. " +
182
- "E.g. 'mercury,venus,mars' or 'jupiter,saturn,uranus,neptune,pluto'. " +
183
- "Defaults to Mercury through Saturn (inner + classical planets).",
179
+ description: "Comma-separated planet names from: mercury, venus, mars, jupiter, saturn, " +
180
+ "uranus, neptune, pluto. Defaults to Mercury through Saturn; name outer " +
181
+ "planets explicitly.",
184
182
  },
185
183
  format: {
186
184
  type: "string",
@@ -195,24 +193,19 @@ registerTool({
195
193
  annotations: { title: "Retrograde Station Tracker", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
196
194
  handler: async (args) => {
197
195
  const query = {};
198
- // Map human-readable planet names to IDs if needed
199
- const PLANET_NAME_MAP = {
200
- mercury: "2", venus: "3", mars: "4",
201
- jupiter: "5", saturn: "6", uranus: "7",
202
- neptune: "8", pluto: "9",
203
- };
204
196
  if (args.start_date)
205
197
  query.start_date = args.start_date;
206
198
  if (args.end_date)
207
199
  query.end_date = args.end_date;
208
200
  if (args.planets) {
209
- // Accept both names ('mercury,venus') and numeric IDs ('2,3')
201
+ // The engine matches lowercase NAMES (elePlanetList in
202
+ // handlers/handler_electional.go) and silently falls back to the
203
+ // whole Mercury-Saturn set when nothing matches — so numeric IDs,
204
+ // which this used to send, made the filter a no-op.
210
205
  query.planets = args.planets
211
206
  .split(",")
212
- .map((p) => {
213
- const name = p.trim().toLowerCase();
214
- return PLANET_NAME_MAP[name] ?? p.trim();
215
- })
207
+ .map((p) => p.trim().toLowerCase())
208
+ .filter(Boolean)
216
209
  .join(",");
217
210
  }
218
211
  if (args.format)