@openephemeris/mcp-server 4.15.1 → 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 (40) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +21 -22
  3. package/dist/backend/client.d.ts +2 -0
  4. package/dist/backend/client.js +15 -10
  5. package/dist/instructions.d.ts +1 -1
  6. package/dist/instructions.js +5 -3
  7. package/dist/prompts.js +2 -2
  8. package/dist/tools/apps/bazi-app.js +10 -10
  9. package/dist/tools/apps/bi-wheel-app.js +14 -14
  10. package/dist/tools/apps/bodygraph-app.js +35 -29
  11. package/dist/tools/apps/chart-wheel-app.js +12 -9
  12. package/dist/tools/apps/location-tools.js +17 -4
  13. package/dist/tools/apps/moon-phase-app.js +12 -11
  14. package/dist/tools/apps/transit-timeline-app.js +13 -14
  15. package/dist/tools/apps/vedic-chart-app.js +9 -8
  16. package/dist/tools/auth.js +14 -12
  17. package/dist/tools/dev.js +17 -29
  18. package/dist/tools/invocation-status.d.ts +25 -0
  19. package/dist/tools/invocation-status.js +69 -0
  20. package/dist/tools/specialized/account.js +6 -10
  21. package/dist/tools/specialized/acg.js +16 -21
  22. package/dist/tools/specialized/bazi.js +7 -12
  23. package/dist/tools/specialized/eclipse.js +6 -13
  24. package/dist/tools/specialized/electional.js +30 -37
  25. package/dist/tools/specialized/ephemeris_core.js +21 -17
  26. package/dist/tools/specialized/ephemeris_extended.js +22 -19
  27. package/dist/tools/specialized/human_design.js +11 -14
  28. package/dist/tools/specialized/moon.js +15 -26
  29. package/dist/tools/specialized/natal.js +9 -8
  30. package/dist/tools/specialized/progressed.js +6 -6
  31. package/dist/tools/specialized/relocation.js +6 -7
  32. package/dist/tools/specialized/returns.js +6 -8
  33. package/dist/tools/specialized/synastry.js +7 -8
  34. package/dist/tools/specialized/transits.js +15 -14
  35. package/dist/tools/specialized/vedic.js +7 -6
  36. package/dist/tools/ui-meta.d.ts +3 -28
  37. package/dist/tools/ui-meta.js +25 -16
  38. package/dist/ui/bi-wheel.html +468 -450
  39. package/dist/ui/chart-wheel.html +407 -389
  40. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ Version numbering follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## [4.16.0] — 2026-09-21
11
+
12
+ ### Changed
13
+ - **Synastry, composite, bi-wheel, returns, progressions, relocation, HD transit/connection overlays
14
+ and advanced BaZi are now available on the free tier (credits only).** Tool descriptions no longer
15
+ say "Pro tier" for any of them; astrocartography and electional window search stay Pro.
16
+ - **Credit-exhausted responses now link directly to a one-tap $5 top-up.** A `402` from the API hands
17
+ the model a single link (`https://openephemeris.com/topup?pack=payg_5`) that signs the user in if
18
+ needed and goes straight to checkout, instead of a dashboard wallet tab.
19
+
20
+ ### Added
21
+ - **Listed in the ChatGPT app directory** (approved 2026-09-14). ChatGPT users install Open Ephemeris
22
+ in one click from [the listing](https://chatgpt.com/plugins/plugin_asdk_app_6a9c2787bc48819197698e71b29ef7c2)
23
+ — no Developer Mode, no URL to paste. Same server, same interactive charts as Claude.
24
+
10
25
  ## [4.15.1] — 2026-09-10
11
26
 
12
27
  ### Fixed
package/README.md CHANGED
@@ -177,7 +177,8 @@ Cursor deeplink payload:
177
177
  | Claude Desktop (macOS) | Manual | `~/Library/Application Support/Claude/claude_desktop_config.json` |
178
178
  | Claude Desktop (Windows) | Manual | `%APPDATA%\Claude\claude_desktop_config.json` |
179
179
  | Windsurf | Manual | `~/.codeium/windsurf/mcp_config.json` (or legacy `~/.codeium/mcp_config.json`) |
180
- | Claude Web / ChatGPT / remote clients | Hosted URL | `https://mcp.openephemeris.com/mcp` |
180
+ | ChatGPT | One-click | [Open Ephemeris in the ChatGPT app directory](https://chatgpt.com/plugins/plugin_asdk_app_6a9c2787bc48819197698e71b29ef7c2) |
181
+ | Claude Web / remote clients | Hosted URL | `https://mcp.openephemeris.com/mcp` |
181
182
 
182
183
  ### Client install walkthroughs
183
184
 
@@ -198,14 +199,12 @@ Cursor deeplink payload:
198
199
  The server is hosted at `https://mcp.openephemeris.com/mcp` with full Streamable HTTP support (MCP 2025-11-25 spec). Remote-only clients can connect directly — no bridge/proxy required:
199
200
 
200
201
  - **Claude Web**: Add `https://mcp.openephemeris.com/mcp` as a custom connector URL — leave OAuth Client ID and Secret **blank**. The server uses OAuth 2.1 + PKCE (Dynamic Client Registration), so Claude handles authentication via a browser popup automatically.
201
- - **ChatGPT**: OpenEphemeris is not in the ChatGPT app directory — you add it yourself.
202
- Turn on **Settings → Plugins → Advanced → Developer mode**, then use the **+ (Create app)**
203
- button on [chatgpt.com/plugins](https://chatgpt.com/plugins). Server URL:
204
- `https://mcp.openephemeris.com/mcp` (append `?profile=core` for the curated 39-tool
205
- surface, which still includes every interactive chart). Leave Authentication on **OAuth**;
206
- the same PKCE + Dynamic Client Registration flow applies, so there is no client ID or
207
- secret to enter. Charts render inline, exactly as they do in Claude. *Developer mode was
208
- available on a Free plan when this was last checked (2026-09-04); availability may vary.*
202
+ - **ChatGPT**: Open Ephemeris is an approved app in the ChatGPT app directory. Open
203
+ [the listing](https://chatgpt.com/plugins/plugin_asdk_app_6a9c2787bc48819197698e71b29ef7c2), click **Install plugin**, approve the sign-in (that also creates your
204
+ free OpenEphemeris account), then type `@Open Ephemeris` in any chat. Charts render inline,
205
+ exactly as they do in Claude. Prefer to wire it yourself? **Settings → Plugins → Advanced →
206
+ Developer mode → + Create app** accepts `https://mcp.openephemeris.com/mcp` with
207
+ Authentication on **OAuth** — no client ID or secret to enter.
209
208
  - **Via Smithery**: Use the [Smithery listing](https://smithery.ai/servers/open-ephemeris/openephemeris) for managed connections with any client
210
209
  - **Legacy SSE**: retired in 3.20.0 — use Streamable HTTP at `/mcp`
211
210
 
@@ -246,8 +245,8 @@ This matters more than it sounds. A natal chart returned as JSON is a list of nu
246
245
  These need a host that supports MCP Apps. **Claude and ChatGPT both do**, and they render
247
246
  the same widget — there is no separate ChatGPT build. MCP Apps ([SEP-1865][sep1865]) was
248
247
  co-authored by Anthropic and OpenAI and became the first official MCP extension in January
249
- 2026, so one `ui://` resource serves both. In ChatGPT you add the server yourself as a
250
- custom app (see [Setup](#setup)); OpenEphemeris is not in the ChatGPT app directory.
248
+ 2026, so one `ui://` resource serves both. In ChatGPT, install it from
249
+ [the app directory](https://chatgpt.com/plugins/plugin_asdk_app_6a9c2787bc48819197698e71b29ef7c2); in Claude, add the connector (see [Setup](#setup)).
251
250
 
252
251
  In a client without app support the same tools still work; you get the underlying data
253
252
  instead of the picture, so nothing breaks, you just don't get the wheel.
@@ -282,24 +281,24 @@ Screenshots of each are on the way.
282
281
  | Moon phase / VOC | `ephemeris_moon_phase` | Explorer |
283
282
  | Eclipse next visible | `ephemeris_next_eclipse` | Explorer |
284
283
  | Electional window | `ephemeris_electional` | Developer |
285
- | Moment analysis | `electional_moment_analysis` | Developer |
286
- | Station tracker | `electional_station_tracker` | Developer |
284
+ | Moment analysis | `electional_moment_analysis` | Explorer |
285
+ | Station tracker | `electional_station_tracker` | Explorer |
287
286
  | Aspect search | `electional_aspect_search` | Developer |
288
287
  | Human Design chart | `human_design_chart` | Explorer |
289
- | HD composite | `human_design_composite` | Developer |
290
- | HD transit overlay | `explore_human_design_transit` | Developer |
291
- | HD connection (synastry) | `explore_human_design_connection` | Developer |
288
+ | HD composite | `human_design_composite` | Explorer |
289
+ | HD transit overlay | `explore_human_design_transit` | Explorer |
290
+ | HD connection (synastry) | `explore_human_design_connection` | Explorer |
292
291
  | HD penta | `human_design_penta` | Explorer |
293
292
  | HD return / opposition | `hd_planetary_return`, `hd_opposition` | Explorer |
294
293
  | Vedic chart | `vedic_chart` | Explorer |
295
294
  | BaZi (Chinese) | `chinese_bazi` | Explorer |
296
- | Synastry | `ephemeris_synastry` | Developer |
297
- | Composite chart | `ephemeris_composite` | Developer |
298
- | Relocation chart | `ephemeris_relocation` | Developer |
295
+ | Synastry | `ephemeris_synastry` | Explorer |
296
+ | Composite chart | `ephemeris_composite` | Explorer |
297
+ | Relocation chart | `ephemeris_relocation` | Explorer |
299
298
  | Progressed chart | `ephemeris_progressed_chart` | Explorer |
300
- | Solar return | `ephemeris_solar_return` | Developer |
301
- | Lunar return | `ephemeris_lunar_return` | Developer |
302
- | Planetary return | `ephemeris_planetary_return` | Developer |
299
+ | Solar return | `ephemeris_solar_return` | Explorer |
300
+ | Lunar return | `ephemeris_lunar_return` | Explorer |
301
+ | Planetary return | `ephemeris_planetary_return` | Explorer |
303
302
  | Astrocartography lines | `acg_power_lines` | Developer |
304
303
  | ACG hits at location | `acg_hits` | Scale |
305
304
  | Venus Star Points | `venus_star_points` + 4 more | Explorer |
@@ -24,6 +24,8 @@ export interface BinaryBackendResponse {
24
24
  export declare const DASHBOARD_ACCOUNT_URL = "https://openephemeris.com/dashboard?tab=account";
25
25
  export declare const LOGIN_SIGNUP_URL = "https://openephemeris.com/login?signup=true&redirect=%2Fdashboard%3Ftab%3Daccount";
26
26
  export declare const UPGRADE_URL = "https://openephemeris.com/pricing";
27
+ /** One-tap $5 → 150-credit top-up (signs the user in if needed, then redirects to the prefilled Stripe Payment Link). */
28
+ export declare const TOPUP_URL = "https://openephemeris.com/topup?pack=payg_5";
27
29
  export declare const WALLET_TOPUP_URL = "https://openephemeris.com/wallet";
28
30
  export declare class BackendError extends Error {
29
31
  readonly status: number;
@@ -22,6 +22,8 @@ const BINARY_ENDPOINT_PREFIXES = [
22
22
  export const DASHBOARD_ACCOUNT_URL = "https://openephemeris.com/dashboard?tab=account";
23
23
  export const LOGIN_SIGNUP_URL = "https://openephemeris.com/login?signup=true&redirect=%2Fdashboard%3Ftab%3Daccount";
24
24
  export const UPGRADE_URL = "https://openephemeris.com/pricing";
25
+ /** One-tap $5 → 150-credit top-up (signs the user in if needed, then redirects to the prefilled Stripe Payment Link). */
26
+ export const TOPUP_URL = "https://openephemeris.com/topup?pack=payg_5";
25
27
  export const WALLET_TOPUP_URL = "https://openephemeris.com/wallet";
26
28
  function sleep(ms) {
27
29
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -277,7 +279,6 @@ export class BackendClient {
277
279
  // suggest enabling overages; suggest the next tier instead. Keep every
278
280
  // number below in sync with that file; do NOT hand-edit independently.
279
281
  const dataObj = data && typeof data === "object" ? data : {};
280
- const walletUrl = dataObj.upgrade?.wallets_url || "https://openephemeris.com/dashboard?tab=wallet";
281
282
  const currentTier = dataObj.upgrade?.current_tier || dataObj.tier || "explorer";
282
283
  // Next-tier upgrade path (SSOT: BILLING_PLANS order in billing-plans.ts).
283
284
  const NEXT_TIER = {
@@ -285,25 +286,29 @@ export class BackendClient {
285
286
  startup: { plan: "scale", label: "Scale ($199/mo, 2,000,000 credits)" },
286
287
  scale: { plan: "enterprise", label: "Enterprise (custom volume)" },
287
288
  };
289
+ // One direct link, not a dashboard tab: /topup?pack=payg_5 signs the
290
+ // user in if needed and redirects straight to the prefilled Stripe
291
+ // Payment Link for the $5 → 150-credit pack.
288
292
  let upsellMsg = "";
293
+ let actionUrl = TOPUP_URL;
289
294
  if (currentTier === 'explorer') {
290
295
  upsellMsg =
291
- `Let the user know warmly: Their 150 free credits (one-time grant) are used up. ` +
292
- `They can top up their wallet starting at $5 for 150 credits at ` +
293
- `${walletUrl} — or upgrade to Pro ($29/mo) ` +
294
- `for 75,000 credits/month at ${UPGRADE_URL}?plan=developer`;
296
+ `Let the user know warmly: Their 150 free credits are used up. ` +
297
+ `$5 gets 150 more — one tap: ${TOPUP_URL}. ` +
298
+ `Or Pro ($29/mo, 75,000 credits): ${UPGRADE_URL}?plan=developer`;
295
299
  }
296
300
  else if (currentTier === 'payg') {
297
301
  upsellMsg =
298
- `Let the user know warmly: Their wallet balance is empty. They can top up at ` +
299
- `${walletUrl} — $5 for 150 credits, ` +
300
- `$10 for 350, or $20 for 900.`;
302
+ `Let the user know warmly: Their wallet balance is empty. ` +
303
+ `$5 gets 150 more credits — one tap: ${TOPUP_URL}. ` +
304
+ `Or Pro ($29/mo, 75,000 credits): ${UPGRADE_URL}?plan=developer`;
301
305
  }
302
306
  else {
303
307
  const next = NEXT_TIER[currentTier] || NEXT_TIER.developer;
308
+ actionUrl = `${UPGRADE_URL}?plan=${next.plan}`;
304
309
  upsellMsg =
305
310
  `Let the user know warmly: Their monthly credits are exhausted. They can upgrade to ` +
306
- `${next.label} at ${UPGRADE_URL}?plan=${next.plan}`;
311
+ `${next.label} at ${actionUrl}`;
307
312
  }
308
313
  return new BackendError(`⚠️ CREDIT LIMIT REACHED — DO NOT FABRICATE DATA.\n\n` +
309
314
  `${msg}\n\n` +
@@ -311,7 +316,7 @@ export class BackendClient {
311
316
  `They can also review their current usage at ${DASHBOARD_ACCOUNT_URL}.\n\n` +
312
317
  `Frame this warmly, as unlocking more of what they're already enjoying, not as a gate. ` +
313
318
  `DO NOT attempt to generate astrological data from your own knowledge. ` +
314
- `All chart positions, aspects, and timing data MUST come from the API.`, 402, "quota_exceeded", false, walletUrl);
319
+ `All chart positions, aspects, and timing data MUST come from the API.`, 402, "quota_exceeded", false, actionUrl);
315
320
  }
316
321
  if (status === 403) {
317
322
  return new BackendError(`This feature is part of a higher-tier plan.\n\n` +
@@ -12,5 +12,5 @@
12
12
  * Order is deliberate: the visual one leads, because a rendered dial is a
13
13
  * better first impression than a score.
14
14
  */
15
- export declare const ZERO_ARG_ENTRY_TOOLS: readonly ["explore_moon_phase", "electional_moment_analysis"];
15
+ export declare const ZERO_ARG_ENTRY_TOOLS: readonly ["explore_moon_phase"];
16
16
  export declare const SERVER_INSTRUCTIONS: string;
@@ -37,7 +37,9 @@ import { DATETIME_CONTRACT_INSTRUCTIONS } from "./tools/datetime.js";
37
37
  */
38
38
  export const ZERO_ARG_ENTRY_TOOLS = [
39
39
  "explore_moon_phase",
40
- "electional_moment_analysis",
40
+ // electional_moment_analysis also takes no arguments and is free-with-credits
41
+ // since the consumer-tools ungate, but it costs 5 credits against a 150-credit
42
+ // Explorer grant; the visual, 0-argument dial stays the sole cold-open.
41
43
  ];
42
44
  export const SERVER_INSTRUCTIONS = "Open Ephemeris computes real astronomy (JPL DE440, sub-arcsecond) — never guess " +
43
45
  "or approximate positions yourself; always call a tool. " +
@@ -45,7 +47,7 @@ export const SERVER_INSTRUCTIONS = "Open Ephemeris computes real astronomy (JPL
45
47
  "prefer the explore_* tools — they render interactive visuals inline. " +
46
48
  "ephemeris_* tools return data; use format='llm' on them for compact output. " +
47
49
  "If the user has no birth data handy, open with the sky right now: " +
48
- `${ZERO_ARG_ENTRY_TOOLS.join(" and ")} take no arguments at all — ` +
49
- "call one rather than asking for a birth date first. " +
50
+ `${ZERO_ARG_ENTRY_TOOLS.join(" and ")} ${ZERO_ARG_ENTRY_TOOLS.length === 1 ? "takes" : "take"} no arguments at all — ` +
51
+ `call ${ZERO_ARG_ENTRY_TOOLS.length === 1 ? "it" : "one"} rather than asking for a birth date first. ` +
50
52
  "See the 'welcome_to_open_ephemeris' prompt for orientation.\n\n" +
51
53
  DATETIME_CONTRACT_INSTRUCTIONS;
package/dist/prompts.js CHANGED
@@ -888,9 +888,9 @@ export const PROMPTS = [
888
888
  "| ACG city hits | `acg_hits` | `POST /acg/hits` | 15 |\n" +
889
889
  "| Eclipse finder | `ephemeris_next_eclipse` | `GET /eclipse/next-visible` | 1 |\n" +
890
890
  "| Moon phase (current) | `ephemeris_moon_phase` | `GET /ephemeris/moon/phase` | 1 |\n" +
891
- "| Next lunar phase | `ephemeris_next_lunar_phase` | `GET /ephemeris/moon/next-phase` | 1 |\n" +
891
+ "| Next lunar phase | `ephemeris_next_lunar_phase` | `GET /calendar/astrology/moon-phases` | 1 |\n" +
892
892
  "| Electional windows | `ephemeris_electional` | `GET /electional/find-window` | 5 |\n" +
893
- "| Electional score | `electional_moment_analysis` | `GET /electional/moment` | 1 |\n" +
893
+ "| Electional score | `electional_moment_analysis` | `GET /electional/moment-analysis` | 1 |\n" +
894
894
  "| Dignities | `ephemeris_dignities` | `POST /ephemeris/dignities` | 1 |\n" +
895
895
  "| Hermetic lots | `ephemeris_hermetic_lots` | `POST /ephemeris/hermetic-lots` | 1 |\n" +
896
896
  "| Midpoints | `ephemeris_midpoints` | `POST /ephemeris/midpoints` | 1 |\n\n" +
@@ -111,17 +111,17 @@ function buildSummary(payload) {
111
111
  // ── Tool: explore_bazi_chart ─────────────────────────────────────────────────
112
112
  registerTool({
113
113
  name: "explore_bazi_chart",
114
- description: "Generate an interactive BaZi (四柱命盘 Four Pillars of Destiny) chart with clickable pillars.\n\n" +
115
- "Returns an embedded visual Four Pillars explorer showing the Year, Month, Day, and Hour " +
116
- "pillars — each with its Heavenly Stem (天干) and Earthly Branch (地支), Ten God label, and " +
117
- "element color — plus the Day Master identity and a Wu Xing (五行) element balance bar. " +
118
- "Click any pillar for an instant interpretation of what it reveals about that life domain " +
119
- "(Year = ancestry/early life, Month = parents/career, Day = self/spouse, Hour = children/later life).\n\n" +
114
+ description: "Use this when the user asks 'what's my BaZi', 'my Four Pillars', 'what's my Day Master', 'my " +
115
+ "Chinese astrology chart', 'what's my Chinese element' or 'my Chinese birth chart' — the PRIMARY " +
116
+ "tool for BaZi, with or without a renderer. Returns an interactive Four Pillars (四柱命盘) explorer " +
117
+ "(inline in Claude and ChatGPT; text summary elsewhere): the Year, Month, Day and Hour pillars — " +
118
+ "each with Heavenly Stem (天干), Earthly Branch (地支), Ten God label and element color — plus the " +
119
+ "Day Master identity and a Wu Xing (五行) element balance bar; click any pillar for what it reveals " +
120
+ "about that life domain.\n\n" +
120
121
  "CREDIT COST: 3 credits per call (1 base + 2 visual render).\n\n" +
121
- "Use this for a rich, interactive BaZi experience in MCP Apps-capable hosts (Claude and ChatGPT). " +
122
- "Falls back to a text summary in other hosts, so it is also the right call without a " +
123
- "renderer. Deeper derivations (Ten Gods, element balance, luck pillars) and a plain " +
124
- "non-visual pillars lookup have dedicated tools on the full surface (`?profile=full`).",
122
+ "Do not use for a single year's zodiac animal or pillar — use bazi_annual_pillar. Deeper " +
123
+ "derivations (Ten Gods, element balance, luck pillars) and a plain non-visual pillars lookup have " +
124
+ "dedicated tools on the full surface (`?profile=full`).",
125
125
  inputSchema: {
126
126
  type: "object",
127
127
  properties: {
@@ -417,20 +417,20 @@ function buildBiWheelSummary(innerPlanets, outerPlanets, crossAspects, mode, loc
417
417
  // ── Tool: explore_bi_wheel ─────────────────────────────────────────────────────
418
418
  registerTool({
419
419
  name: "explore_bi_wheel",
420
- description: "Generate an interactive bi-wheel chart comparing two astrological chart positions.\n\n" +
421
- "CREDIT COST: 2 credits per call for most modes (1 per wheel computed); " +
422
- "solar_return/lunar_return modes cost 6 (5 for the return + 1 for the natal wheel).\n\n" +
423
- "The inner wheel is always Person 1's natal chart. The outer ring depends on mode:\n" +
424
- "• synastry — Person 2's natal chart (needs Person 2's own birthplace).\n" +
425
- "• transit — Transiting planets for a given date.\n" +
426
- "• progressed — Secondary progressed positions (person2_datetime = target date).\n" +
427
- "• solar_return — Nearest solar return chart (person2_datetime = year to return for).\n" +
428
- "• lunar_return — Nearest lunar return chart (person2_datetime = target month).\n" +
429
- "• solar_arc — Solar arc directed positions (person2_datetime = target date).\n" +
430
- "Cross-aspects are drawn as coloured dashed lines; click any planet, aspect line, or " +
431
- "house cusp for interpretation. Prefer this whenever the user should SEE the comparison " +
432
- "— it renders interactively in MCP Apps hosts and falls back to static SVG elsewhere. " +
433
- "For raw synastry data without a visual, use ephemeris_synastry.",
420
+ description: "Use this when the user asks 'are we compatible', 'compare my chart with my partner's', 'what's " +
421
+ "my horoscope for today', 'what's hitting my chart right now', 'what's my solar return chart this " +
422
+ "year' or 'my progressed chart on my birth chart' — any two-ring comparison they should SEE. " +
423
+ "Returns an interactive bi-wheel (inline in Claude and ChatGPT; text summary elsewhere): inner " +
424
+ "wheel Person 1's natal chart, outer ring by mode — synastry (Person 2's natal; needs their " +
425
+ "birthplace), transit (a date; today for a daily horoscope), progressed, solar_arc (target date), " +
426
+ "solar_return (target year), lunar_return (target month) — with cross-aspects drawn and every " +
427
+ "planet, aspect line and cusp clickable. Birthplaces may be place names (`location`, " +
428
+ "`person2_location`).\n\n" +
429
+ "CREDIT COST: 2 credits per call for most modes (1 per wheel; +1 to resolve a place name); " +
430
+ "solar_return/lunar_return modes cost 6.\n\n" +
431
+ "Do not use for synastry numbers without a visual — use ephemeris_synastry; for a dated list of " +
432
+ "upcoming transit hits use explore_transit_timeline; for a single chart use explore_natal_chart; " +
433
+ "with no birth data use explore_moon_phase.",
434
434
  inputSchema: {
435
435
  type: "object",
436
436
  properties: {
@@ -227,8 +227,10 @@ function buildHdModelPayload(data, birthParams) {
227
227
  const personality_gates = [...new Set(extractGateNums(pGateList))].sort((a, b) => a - b);
228
228
  const design_gates = [...new Set(extractGateNums(dGateList))].sort((a, b) => a - b);
229
229
  // Convert activation arrays or maps → planet-keyed Record<string, ActivationSummary>
230
- // The Go API returns arrays like [{ planet: "Sun", gate: 62, line: 1, color: 3, tone: 4 }].
231
- // The renderer needs a map keyed by planet name: { "Sun": { gate, line, color, tone } }.
230
+ // The Go API returns arrays like
231
+ // [{ planet: "Sun", gate: 62, line: 1, color: 3, tone: 4, base: 5 }]. The renderer needs a
232
+ // map keyed by planet name: { "Sun": { gate, line, color, tone, base } }. All five are kept
233
+ // so a gate click can quote the full gate.line.color.tone.base address.
232
234
  function toActivationMap(raw) {
233
235
  if (!raw || (typeof raw !== 'object'))
234
236
  return undefined;
@@ -258,6 +260,7 @@ function buildHdModelPayload(data, birthParams) {
258
260
  line: Number(item.line ?? 0),
259
261
  color: Number(item.color ?? 0),
260
262
  tone: Number(item.tone ?? 0),
263
+ base: Number(item.base ?? 0),
261
264
  };
262
265
  }
263
266
  return Object.keys(map).length > 0 ? map : undefined;
@@ -305,19 +308,17 @@ function buildHdSummary(payload, location) {
305
308
  // ── Tool: explore_human_design ────────────────────────────────────────────
306
309
  registerTool({
307
310
  name: "explore_human_design",
308
- description: "Generate an interactive Human Design Bodygraph with clickable centers, gates, and channels.\n\n" +
309
- "CREDIT COST: 4 credits where the bodygraph renders (2 for the chart + 2 for the " +
310
- "visual), 2 in text-only hosts that skip the render. For chart data alone at 2 " +
311
- "credits — including the activations, design, personality, strategy and variables " +
312
- "this tool does not return — use human_design_chart.\n\n" +
313
- "Returns an embedded visual bodygraph explorer that lets you click any center or gate " +
314
- "for instant Human Design interpretation. " +
315
- "Shows defined/undefined centers, active gates, channels, Type, Profile, Authority, " +
316
- "and Incarnation Cross. " +
317
- "The chart is calculated using NASA JPL DE440 ephemerides for both Personality and Design positions. " +
318
- "Use this instead of human_design_chart for a richer, interactive HD " +
319
- "experience in MCP Apps-capable hosts (Claude and ChatGPT). " +
320
- "Falls back to a text summary in other hosts." +
311
+ description: "Use this when the user asks 'what's my Human Design', 'am I a Generator / Projector / Manifestor " +
312
+ "/ Reflector', 'show my bodygraph', 'what's my HD type and profile', 'what's my strategy and " +
313
+ "authority' or 'what's my incarnation cross' — the PRIMARY tool for a Human Design chart. Returns " +
314
+ "an interactive bodygraph (inline in Claude and ChatGPT; text summary elsewhere) showing " +
315
+ "defined/undefined centers, active gates, channels, Type, Profile, Authority and Incarnation " +
316
+ "Cross, with any center or gate clickable for interpretation. Birthplace may be a place name " +
317
+ "(`location`).\n\n" +
318
+ "CREDIT COST: 4 credits per call (2 chart + 2 render; +1 to resolve a place name).\n\n" +
319
+ "Do not use when the user needs the activations, design/personality positions, strategy and " +
320
+ "variables this view omits — use human_design_chart (2 credits); for today's transits on their " +
321
+ "chart use explore_human_design_transit; for two people use explore_human_design_connection." +
321
322
  HD_DISCLAIMER,
322
323
  inputSchema: {
323
324
  type: "object",
@@ -1305,15 +1306,18 @@ registerTool({
1305
1306
  });
1306
1307
  // ── Tool: explore_human_design_transit ───────────────────────────────────────
1307
1308
  // Personalized Human Design transit overlay (natal chart + a transiting moment).
1308
- // Premium (Pro tier). Calls POST /human-design/transit-chart.
1309
+ // Calls POST /human-design/transit-chart.
1309
1310
  registerTool({
1310
1311
  name: "explore_human_design_transit",
1311
- description: "Overlay the current (or a chosen) planetary transit on a person's natal Human Design bodygraph.\n\n" +
1312
- "CREDIT COST: 3 credits per call.\n\n" +
1313
- "Highlights the channels a transit temporarily COMPLETES with the natal chart and any centers it " +
1314
- "newly defines — the core of a Human Design transit reading. " +
1315
- "Returns an interactive overlay bodygraph in MCP Apps-capable hosts (Claude and ChatGPT), with a text " +
1316
- "summary fallback elsewhere. Premium (Pro tier). Calculated with NASA JPL DE440 ephemerides." +
1312
+ description: "Use this when the user asks 'what are today's Human Design transits for me', 'what gates are " +
1313
+ "being activated for me right now', 'which channels does this transit complete in my chart' or " +
1314
+ "'how is the current sky affecting my bodygraph'. Returns an interactive overlay bodygraph " +
1315
+ "(inline in Claude and ChatGPT; text summary elsewhere) highlighting the channels the transit " +
1316
+ "temporarily COMPLETES with the natal chart and any centers it newly defines, for now or a chosen " +
1317
+ "datetime. Birthplace may be a place name (`location`).\n\n" +
1318
+ "CREDIT COST: 3 credits per call (+1 to resolve a place name).\n\n" +
1319
+ "Do not use for the person's own natal bodygraph — use explore_human_design; for another person's " +
1320
+ "chart overlaid on theirs use explore_human_design_connection." +
1317
1321
  HD_DISCLAIMER,
1318
1322
  inputSchema: {
1319
1323
  type: "object",
@@ -1439,17 +1443,19 @@ registerTool({
1439
1443
  },
1440
1444
  });
1441
1445
  // ── Tool: explore_human_design_connection ────────────────────────────────────
1442
- // Two-person Human Design connection (synastry) overlay. Premium (Pro tier).
1446
+ // Two-person Human Design connection (synastry) overlay.
1443
1447
  // Calls POST /human-design/composite.
1444
1448
  registerTool({
1445
1449
  name: "explore_human_design_connection",
1446
- description: "Compare two people's Human Design charts and classify every connected channel by HD connection " +
1447
- "theory.\n\n" +
1450
+ description: "Use this when the user asks 'are we compatible in Human Design', 'what's our connection chart', " +
1451
+ "'what channels do we make together' or 'show our composite bodygraph' — two people's HD charts " +
1452
+ "overlaid. Returns an interactive two-person overlay bodygraph (inline in Claude and ChatGPT; " +
1453
+ "text summary elsewhere) with every connected channel classified by HD connection theory: " +
1454
+ "electromagnetic (attraction), companionship (sameness), dominance (one defines), compromise " +
1455
+ "(friction).\n\n" +
1448
1456
  "CREDIT COST: 3 credits per call.\n\n" +
1449
- "Classifications: " +
1450
- "electromagnetic (attraction), companionship (sameness), dominance (one defines), and " +
1451
- "compromise (friction). Returns an interactive two-person overlay bodygraph in MCP Apps-capable " +
1452
- "hosts, with a text summary fallback elsewhere. Premium (Pro tier). NASA JPL DE440 ephemerides." +
1457
+ "Do not use for one person's chart — use explore_human_design; for the sky's transit on one chart " +
1458
+ "use explore_human_design_transit; for Western compatibility use explore_bi_wheel." +
1453
1459
  HD_DISCLAIMER,
1454
1460
  inputSchema: {
1455
1461
  type: "object",
@@ -120,15 +120,18 @@ function buildNatalBody(datetime, lat, lon, houseSystem, timezone, additionalObj
120
120
  // ── Tool: explore_natal_chart ──────────────────────────────────────────────
121
121
  registerTool({
122
122
  name: "explore_natal_chart",
123
- description: "PRIMARY tool for any natal/birth-chart request. Renders an interactive, clickable chart " +
124
- "wheel — prefer this over ephemeris_natal_chart when the user wants to SEE a chart. " +
125
- "Returns an embedded visual chart explorer that lets you click any planet, house, or aspect " +
126
- "line for instant astrological interpretation.\n\n" +
127
- "CREDIT COST: 1 credit per call.\n\n" +
128
- "Supports house system switching (Placidus, Whole Sign, Equal, Koch). " +
129
- "The chart is computed using NASA JPL DE440 ephemerides for sub-arcsecond precision. " +
130
- "Renders interactively in MCP Apps-capable hosts (Claude and ChatGPT) and falls back to " +
131
- "static SVG in other hosts.",
123
+ description: "Use this when the user asks 'show me my birth chart', 'what's my rising sign', 'my big 3', " +
124
+ "'what's my sun, moon and rising', 'what sign is my Moon / Venus / Mars in', 'what does my chart " +
125
+ "say about my career / love', 'read my chart', or shares birth details and wants to explore them " +
126
+ "— the PRIMARY tool for any natal / birth-chart / astrology-chart request. Returns an interactive " +
127
+ "chart wheel rendered inline (Claude and ChatGPT; text summary elsewhere) where any planet, house " +
128
+ "or aspect line can be clicked for interpretation, with switchable house systems. Pass the " +
129
+ "birthplace as `location` (a place name — resolved server-side) or coordinates, plus the birth " +
130
+ "date and local time with its zone.\n\n" +
131
+ "CREDIT COST: 1 credit per call (+1 to resolve a place name).\n\n" +
132
+ "Do not use when the chart must be consumed as raw numbers for further computation — use " +
133
+ "ephemeris_natal_chart; for two charts together use explore_bi_wheel; for Human Design use " +
134
+ "explore_human_design; for Vedic use explore_vedic_chart.",
132
135
  inputSchema: {
133
136
  type: "object",
134
137
  properties: {
@@ -127,7 +127,16 @@ function offsetAtLocalNoon(timezone, date) {
127
127
  }
128
128
  registerTool({
129
129
  name: "location_search",
130
- description: "Resolve a place name to coordinates and IANA timezone — use this first whenever a user gives a birth city rather than latitude/longitude. NEVER recall coordinates from memory; always resolve them here. CREDIT COST: 1 credit per call. Returns display name, region (state/province), latitude, longitude, and IANA timezone. Pass the birth date as `date` to also get `utcOffsetAtDate`, the historically-correct UTC offset for that place on that date (1987 DST rules differ from today's). Post-1970 dates resolve locally and cost no extra credits; a pre-1970 date consults the API's historical correction overlay for the top match (1 extra credit) and returns its provenance — tzRuleSource, tzRuleCitation, tzOverlayVersion. When the result is `ambiguous` (several places share the name, e.g. \"portland\"), ASK the user which one they mean rather than assuming the first. Optional bias params (country/region/near) improve ranking; a trailing \"City, ST\" qualifier in the query is also honored.",
130
+ description: "Use this when the user gives a birth city or any place name — 'born in Chicago', 'Portland', " +
131
+ "'Mumbai, India' — and a chart needs coordinates and a timezone. Always resolve here; NEVER " +
132
+ "recall coordinates from memory. Returns display name, region (state/province), latitude, " +
133
+ "longitude and IANA timezone. Pass the birth date as `date` to also get `utcOffsetAtDate`, the " +
134
+ "historically-correct UTC offset for that place on that date; post-1970 dates resolve locally at " +
135
+ "no extra cost, pre-1970 dates consult the historical overlay for the top match (+1 credit) and " +
136
+ "return its provenance. When the result is `ambiguous` (several places share the name, e.g. " +
137
+ "\"portland\"), ASK the user which one they mean rather than assuming the first. A trailing \"City, " +
138
+ "ST\" qualifier in the query is honored. CREDIT COST: 1 credit per call. Do not use when you " +
139
+ "already have coordinates and only need the zone — use timezone_resolve.",
131
140
  inputSchema: {
132
141
  type: "object",
133
142
  properties: {
@@ -280,12 +289,16 @@ registerTool({
280
289
  });
281
290
  registerTool({
282
291
  name: "timezone_resolve",
283
- description: "Resolve the IANA timezone for a latitude/longitude pair. Use when you have coordinates but need the timezone to interpret a local birth time. Pass `date` to also get the historically-correct UTC offset that applied on that date. CREDIT COST: 1 credit per call. If you are starting from a place name rather than coordinates, use location_search instead — it returns the timezone too, in the same single call.",
292
+ description: "Use this when you have latitude/longitude but need the IANA timezone to interpret a local birth " +
293
+ "time, or the user asks 'what timezone is this location in' or 'what was the UTC offset there on " +
294
+ "that date'. Returns the IANA zone and, with `date`, the historically-correct UTC offset that " +
295
+ "applied on that date. CREDIT COST: 1 credit per call. Do not use when starting from a place name " +
296
+ "— use location_search, which returns the timezone too, in the same single call.",
284
297
  inputSchema: {
285
298
  type: "object",
286
299
  properties: {
287
- latitude: { type: "number" },
288
- longitude: { type: "number" },
300
+ latitude: { type: "number", description: "Latitude in decimal degrees, -90 to 90 (north positive)." },
301
+ longitude: { type: "number", description: "Longitude in decimal degrees, -180 to 180 (east positive)." },
289
302
  date: {
290
303
  type: "string",
291
304
  description: "Optional birth/event date 'YYYY-MM-DD'. Adds utcOffsetAtDate / utcOffsetMinutes / isDst / tzConfidence. Post-1970 resolves locally (free); pre-1970 consults the API's historical correction overlay (1 extra credit).",
@@ -193,18 +193,19 @@ async function computeMoonData(args) {
193
193
  // ── Tool: explore_moon_phase ─────────────────────────────────────────────────
194
194
  registerTool({
195
195
  name: "explore_moon_phase",
196
- description: "Generate an interactive Moon Phase dial showing the current lunar illumination, " +
197
- "phase name, zodiac sign, and void-of-course status as a beautiful circular visualization.\n\n" +
198
- "Returns a visual dial with:\n" +
199
- " • SVG crescent Moon showing real-time illumination percentage\n" +
200
- " • Phase name and waxing/waning indicator\n" +
201
- " • Current Moon sign with degree\n" +
202
- " • Void-of-Course status with timing details\n" +
203
- " • Lunar age (days in the synodic cycle)\n" +
204
- " • Upcoming New Moon and Full Moon dates\n\n" +
196
+ description: "Use this when the user asks 'what's the moon phase tonight', 'is tonight a full moon', 'show me " +
197
+ "the moon right now', 'what sign is the Moon in', 'is the Moon void of course' — or has no birth " +
198
+ "data and wants somewhere to start; it needs no arguments. Returns an interactive Moon dial " +
199
+ "(inline in Claude and ChatGPT; text summary elsewhere) with:\n" +
200
+ "• SVG crescent Moon showing real-time illumination percentage\n" +
201
+ "• Phase name and waxing/waning indicator\n" +
202
+ "• Current Moon sign with degree\n" +
203
+ "• Void-of-Course status with timing details\n" +
204
+ "• Lunar age (days in the synodic cycle) and days to the next New and Full Moon\n" +
205
+ "• The tightest applying lunar aspects\n\n" +
205
206
  "CREDIT COST: 3 credits per call (phase + void-of-course + aspects, 1 each).\n\n" +
206
- "Use this instead of ephemeris_moon_phase for a rich, interactive lunar phase experience in " +
207
- "MCP Apps-capable hosts (Claude and ChatGPT). Falls back to a text summary in other hosts.",
207
+ "Do not use when only the numbers are needed — use ephemeris_moon_phase; for the exact dates of " +
208
+ "upcoming phases use ephemeris_next_lunar_phase; for eclipses use ephemeris_next_eclipse.",
208
209
  inputSchema: {
209
210
  type: "object",
210
211
  properties: {
@@ -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 and ChatGPT). 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 and ChatGPT). " +
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: {