@openephemeris/mcp-server 4.10.0 → 4.11.1
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 +50 -0
- package/dist/tools/apps/_location-resolver.d.ts +1 -1
- package/dist/tools/apps/_location-resolver.js +23 -2
- package/dist/tools/apps/bazi-app.js +10 -3
- package/dist/tools/apps/chart-wheel-app.js +8 -4
- package/dist/tools/datetime.js +10 -1
- package/dist/ui/bazi.html +1604 -1601
- package/dist/ui/bi-wheel.html +1886 -1881
- package/dist/ui/bodygraph.html +1352 -1337
- package/dist/ui/chart-wheel.html +406 -401
- package/dist/ui/moon-phase.html +38 -35
- package/dist/ui/transit-timeline.html +38 -35
- package/dist/ui/vedic-chart.html +38 -35
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,56 @@ Version numbering follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [4.11.1] — 2026-08-12
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Location resolver didn't recognize US state abbreviations.**
|
|
14
|
+
`location: "San Francisco, CA"` was rejected as ambiguous while the
|
|
15
|
+
equivalent `"San Francisco, California"` resolved fine — the API returns
|
|
16
|
+
the full state name in `region`, and the ambiguity qualifier only matched
|
|
17
|
+
full-name substrings or ISO country-code tokens. Added a USPS abbreviation
|
|
18
|
+
lookup (50 states + DC + territories) so `"City, ST"` qualifies the same
|
|
19
|
+
as `"City, State Name"`.
|
|
20
|
+
- **`bazi_recalculate` double-charged for theme-only re-renders.** Its only
|
|
21
|
+
caller is the BaZi app's automatic host-theme reconciliation — never a
|
|
22
|
+
birth-data change — but it billed the full 3 credits as if it were a fresh
|
|
23
|
+
chart, on top of the identical charge already paid via `explore_bazi_chart`
|
|
24
|
+
moments earlier. Theme-only reconcile now skips the 2-credit visual-render
|
|
25
|
+
reservation.
|
|
26
|
+
- **Datetime tool descriptions could invite a silent 1-hour-off chart.**
|
|
27
|
+
Extended the shared datetime contract instructions to explicitly tell
|
|
28
|
+
callers to pass local wall-clock time + IANA `timezone` rather than
|
|
29
|
+
converting to UTC themselves — a calling model's own conversion mistake
|
|
30
|
+
(e.g. treating July San Francisco as PST instead of PDT) previously sailed
|
|
31
|
+
through with zero server-side errors and a confidently wrong chart.
|
|
32
|
+
|
|
33
|
+
## [4.11.0] — 2026-08-07
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- **`explore_natal_chart` never drew aspect lines.** `buildNatalBody()` didn't
|
|
37
|
+
set `options.include_aspects`, so the API returned an empty aspects array
|
|
38
|
+
for the single-wheel chart (the bi-wheel was unaffected — it computes
|
|
39
|
+
cross-aspects client-side).
|
|
40
|
+
- **HD synastry connection panel showed only one person's data, unlabeled.**
|
|
41
|
+
`explore_human_design_connection` computed both people's type/profile/
|
|
42
|
+
authority/definition/incarnation-cross but only used Person B's to feed the
|
|
43
|
+
connection-channel classifier, then discarded it. The bodygraph panel now
|
|
44
|
+
shows both, labeled "Person A"/"Person B" to match the overlay legend.
|
|
45
|
+
- **Inconsistent planet/sign glyph weight** (Sun/Moon bold, Venus/Mars thin)
|
|
46
|
+
in the natal and bi-wheel apps. A bare `font-family: "serif"` resolves
|
|
47
|
+
per-glyph on Windows — Times New Roman covers ☉/☽ but not ♀/♂, so those
|
|
48
|
+
silently fell back to a different, thinner font. Pinned an explicit
|
|
49
|
+
astrological symbol-font stack everywhere these glyphs render.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
- **Welcome popup → on-demand info button.** All 7 MCP apps (chart-wheel,
|
|
53
|
+
bi-wheel, bodygraph, moon-phase, transit-timeline, vedic-chart, bazi) no
|
|
54
|
+
longer auto-pop a modal on every load/reload — a small "i" button opens the
|
|
55
|
+
same guide on demand.
|
|
56
|
+
- **House ring now reads with visible depth.** The band between the zodiac
|
|
57
|
+
ring and the center previously matched the background exactly; it now uses
|
|
58
|
+
a step-up `--surface` shade on both the natal and bi-wheel apps.
|
|
59
|
+
|
|
10
60
|
## [4.10.0] — 2026-08-05
|
|
11
61
|
|
|
12
62
|
### Fixed
|
|
@@ -10,7 +10,7 @@ export interface ResolvedLocation {
|
|
|
10
10
|
*
|
|
11
11
|
* Ambiguity is detected the same way `location_search` reports it — a
|
|
12
12
|
* count of *rivals* for the top short_name, unless the query itself
|
|
13
|
-
* qualifies the place (e.g. "portland uk", "dallas texas").
|
|
13
|
+
* qualifies the place (e.g. "portland uk", "dallas texas", "dallas, tx").
|
|
14
14
|
*/
|
|
15
15
|
export declare function resolveLocationOrThrow(location: string): Promise<ResolvedLocation>;
|
|
16
16
|
/**
|
|
@@ -15,12 +15,31 @@
|
|
|
15
15
|
// produces a chart nothing downstream can detect as wrong. Same failure
|
|
16
16
|
// class as the naive-UTC ASC and the unlabelled cusp longitudes.
|
|
17
17
|
import { getActiveClient } from "../../backend/client.js";
|
|
18
|
+
// The API returns the full state name in `region` ("Oregon", not "OR"), but
|
|
19
|
+
// most callers naturally type the USPS abbreviation ("Portland, OR"). Without
|
|
20
|
+
// this table, "San Francisco, CA" was flagged ambiguous while the equivalent
|
|
21
|
+
// "San Francisco, California" resolved fine — same query, arbitrarily
|
|
22
|
+
// different outcome depending on which form the caller happened to type.
|
|
23
|
+
const US_STATE_ABBREVIATIONS = {
|
|
24
|
+
al: "alabama", ak: "alaska", az: "arizona", ar: "arkansas", ca: "california",
|
|
25
|
+
co: "colorado", ct: "connecticut", de: "delaware", fl: "florida", ga: "georgia",
|
|
26
|
+
hi: "hawaii", id: "idaho", il: "illinois", in: "indiana", ia: "iowa",
|
|
27
|
+
ks: "kansas", ky: "kentucky", la: "louisiana", me: "maine", md: "maryland",
|
|
28
|
+
ma: "massachusetts", mi: "michigan", mn: "minnesota", ms: "mississippi", mo: "missouri",
|
|
29
|
+
mt: "montana", ne: "nebraska", nv: "nevada", nh: "new hampshire", nj: "new jersey",
|
|
30
|
+
nm: "new mexico", ny: "new york", nc: "north carolina", nd: "north dakota", oh: "ohio",
|
|
31
|
+
ok: "oklahoma", or: "oregon", pa: "pennsylvania", ri: "rhode island", sc: "south carolina",
|
|
32
|
+
sd: "south dakota", tn: "tennessee", tx: "texas", ut: "utah", vt: "vermont",
|
|
33
|
+
va: "virginia", wa: "washington", wv: "west virginia", wi: "wisconsin", wy: "wyoming",
|
|
34
|
+
dc: "district of columbia", pr: "puerto rico", vi: "virgin islands", gu: "guam",
|
|
35
|
+
as: "american samoa", mp: "northern mariana islands",
|
|
36
|
+
};
|
|
18
37
|
/**
|
|
19
38
|
* Resolve a place name to lat/lon/tz. Throws if ambiguous or unresolvable.
|
|
20
39
|
*
|
|
21
40
|
* Ambiguity is detected the same way `location_search` reports it — a
|
|
22
41
|
* count of *rivals* for the top short_name, unless the query itself
|
|
23
|
-
* qualifies the place (e.g. "portland uk", "dallas texas").
|
|
42
|
+
* qualifies the place (e.g. "portland uk", "dallas texas", "dallas, tx").
|
|
24
43
|
*/
|
|
25
44
|
export async function resolveLocationOrThrow(location) {
|
|
26
45
|
const query = location.trim();
|
|
@@ -40,7 +59,9 @@ export async function resolveLocationOrThrow(location) {
|
|
|
40
59
|
const rivals = list.filter((s) => norm(s.short_name) === norm(top.short_name));
|
|
41
60
|
const q = norm(query);
|
|
42
61
|
const qTokens = new Set(q.split(/[^a-z0-9]+/).filter(Boolean));
|
|
43
|
-
const
|
|
62
|
+
const topRegion = norm(top.region);
|
|
63
|
+
const qualified = (topRegion !== "" && q.includes(topRegion)) ||
|
|
64
|
+
(topRegion !== "" && [...qTokens].some((t) => US_STATE_ABBREVIATIONS[t] === topRegion)) ||
|
|
44
65
|
(norm(top.country_code) !== "" && qTokens.has(norm(top.country_code)));
|
|
45
66
|
const ambiguous = rivals.length > 1 && !qualified;
|
|
46
67
|
if (ambiguous) {
|
|
@@ -66,13 +66,16 @@ export function clearBaziBundleCache() {
|
|
|
66
66
|
* Go-rendered SVG (bazi.RenderBaziChartSVG), same shape the strict
|
|
67
67
|
* /chinese/bazi handler returns with a `visual` key attached.
|
|
68
68
|
*/
|
|
69
|
-
async function fetchBaziChart(components, theme = "dark", conventionFields = {}) {
|
|
69
|
+
async function fetchBaziChart(components, theme = "dark", conventionFields = {}, reconcile = false) {
|
|
70
70
|
const client = getActiveClient();
|
|
71
71
|
const body = {
|
|
72
72
|
...components,
|
|
73
73
|
...conventionFields,
|
|
74
74
|
include_visual: true,
|
|
75
75
|
visual_config: { format: "svg", theme, size: 800 },
|
|
76
|
+
// Set only by bazi_recalculate's theme-reconcile path below — waives the
|
|
77
|
+
// visual surcharge server-side for a same-birth-data re-render (#551).
|
|
78
|
+
...(reconcile ? { _visual_reconcile: true } : {}),
|
|
76
79
|
};
|
|
77
80
|
return await client.request("POST", "/chinese/bazi", { data: body });
|
|
78
81
|
}
|
|
@@ -194,7 +197,11 @@ registerTool({
|
|
|
194
197
|
// ── Tool: bazi_recalculate [app-only] ────────────────────────────────────────
|
|
195
198
|
registerTool({
|
|
196
199
|
name: "bazi_recalculate",
|
|
197
|
-
description: "Recalculates a BaZi Four Pillars chart with new birth data or theme."
|
|
200
|
+
description: "Recalculates a BaZi Four Pillars chart with new birth data or theme. " +
|
|
201
|
+
"App-only: called by the embedded chart itself to reconcile the initial " +
|
|
202
|
+
"server-rendered SVG to the host's actual light/dark theme. Billed at 1 " +
|
|
203
|
+
"credit (base only) — the visual-render surcharge is waived because this " +
|
|
204
|
+
"re-renders already-computed data, not a new chart.",
|
|
198
205
|
inputSchema: {
|
|
199
206
|
type: "object",
|
|
200
207
|
properties: {
|
|
@@ -216,7 +223,7 @@ registerTool({
|
|
|
216
223
|
handler: async (args) => {
|
|
217
224
|
const components = parseBaziArgs(args);
|
|
218
225
|
const theme = args.theme === "light" ? "light" : "dark";
|
|
219
|
-
const data = await fetchBaziChart(components, theme);
|
|
226
|
+
const data = await fetchBaziChart(components, theme, {}, true);
|
|
220
227
|
const payload = buildModelPayload(data, components, theme);
|
|
221
228
|
return {
|
|
222
229
|
content: [{ type: "text", text: JSON.stringify({ ...payload, server_version: SERVER_VERSION }) }],
|
|
@@ -101,6 +101,9 @@ function buildNatalBody(datetime, lat, lon, houseSystem, timezone) {
|
|
|
101
101
|
configuration: {
|
|
102
102
|
house_system: HOUSE_SYSTEM_MAP[houseSystem] ?? "P",
|
|
103
103
|
},
|
|
104
|
+
options: {
|
|
105
|
+
include_aspects: true,
|
|
106
|
+
},
|
|
104
107
|
};
|
|
105
108
|
if (timezone) {
|
|
106
109
|
body.subject.birth_location.timezone = { iana_name: timezone };
|
|
@@ -474,10 +477,11 @@ const ALL_KNOWN_BODIES = new Set([...CLASSICAL_PLANETS, ...EXTENDED_BODIES]);
|
|
|
474
477
|
* solar-return / progressed responses) to the AspectData shape the chart-wheel
|
|
475
478
|
* UI consumes: { planet1, planet2, type, angle, orb, applying }.
|
|
476
479
|
*
|
|
477
|
-
* The Go API computes aspects
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
480
|
+
* The Go API only computes aspects when asked (options.include_aspects = true,
|
|
481
|
+
* set in buildNatalBody above), but once requested it includes a proper
|
|
482
|
+
* ephemeris-based is_applying flag — so no client-side longitude heuristic is
|
|
483
|
+
* needed. Aspects are optionally filtered to the set of bodies actually
|
|
484
|
+
* displayed so lines never reference a hidden planet.
|
|
481
485
|
*/
|
|
482
486
|
function mapServerAspects(rawAspects, allowedNames) {
|
|
483
487
|
if (!Array.isArray(rawAspects))
|
package/dist/tools/datetime.js
CHANGED
|
@@ -81,7 +81,16 @@ export const DATETIME_CONTRACT_INSTRUCTIONS = "Datetime contract: every clock ti
|
|
|
81
81
|
"the naive local time and name the zone in the sibling `timezone` argument (IANA name, " +
|
|
82
82
|
"e.g. `America/Chicago`). A zone-less clock time is a hard 400 — the engine never " +
|
|
83
83
|
"guesses UTC, because an unstated zone shifts the Ascendant by ~15° per hour and moves " +
|
|
84
|
-
"every house placement. A bare date (no clock time) resolves to 12:00 UTC
|
|
84
|
+
"every house placement. A bare date (no clock time) resolves to 12:00 UTC.\n\n" +
|
|
85
|
+
"PREFER the local-time + `timezone` form over converting to UTC yourself. The server " +
|
|
86
|
+
"resolves the IANA zone against the actual date, including historical DST rules — that " +
|
|
87
|
+
"arithmetic (which offset applied on that specific day, in that specific year) is easy " +
|
|
88
|
+
"to get wrong by hand and the server will not catch it: a value that already carries a " +
|
|
89
|
+
"`Z`/±HH:MM suffix is trusted as the stated instant, so a self-computed offset that is " +
|
|
90
|
+
"off by an hour (e.g. daylight vs. standard time) produces a chart that is confidently " +
|
|
91
|
+
"wrong with no error raised. If the user states a local clock time (\"2:30 PM in San " +
|
|
92
|
+
"Francisco\"), pass that wall-clock string verbatim with `timezone` — do not do the " +
|
|
93
|
+
"UTC conversion mentally first.";
|
|
85
94
|
// ─── Enforcement ─────────────────────────────────────────────────────────────
|
|
86
95
|
/**
|
|
87
96
|
* The rejection message. Names both remedies, rewriting the caller's own value
|