@openephemeris/mcp-server 3.2.12 → 3.3.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 (88) hide show
  1. package/README.md +2 -2
  2. package/dist/backend/client.d.ts +23 -1
  3. package/dist/backend/client.js +144 -25
  4. package/dist/index.js +94 -2
  5. package/dist/tools/dev.js +29 -21
  6. package/dist/tools/index.d.ts +8 -0
  7. package/dist/tools/index.js +38 -1
  8. package/dist/tools/specialized/acg.js +143 -0
  9. package/dist/tools/specialized/comparative.js +153 -0
  10. package/dist/tools/specialized/eclipse.js +20 -26
  11. package/dist/tools/specialized/electional.js +182 -3
  12. package/dist/tools/specialized/ephemeris_core.js +93 -0
  13. package/dist/tools/specialized/ephemeris_extended.js +234 -0
  14. package/dist/tools/specialized/hd_group.js +107 -0
  15. package/dist/tools/specialized/human_design.js +27 -11
  16. package/dist/tools/specialized/moon.js +3 -2
  17. package/dist/tools/specialized/natal.js +36 -15
  18. package/dist/tools/specialized/progressed.js +65 -0
  19. package/dist/tools/specialized/relocation.js +14 -10
  20. package/dist/tools/specialized/returns.js +144 -0
  21. package/dist/tools/specialized/synastry.js +22 -12
  22. package/dist/tools/specialized/transits.js +78 -28
  23. package/dist/tools/specialized/venus_star_points.js +187 -0
  24. package/package.json +4 -3
  25. package/dist/scripts/dev-allowlist.js +0 -287
  26. package/dist/scripts/pack-audit.js +0 -45
  27. package/dist/scripts/schema-packs.js +0 -150
  28. package/dist/scripts/smoke-dev-profile.js +0 -25
  29. package/dist/scripts/sync-readme.js +0 -141
  30. package/dist/scripts/test-all-tools.js +0 -104
  31. package/dist/scripts/test-client.js +0 -69
  32. package/dist/scripts/test-sse-client.js +0 -221
  33. package/dist/src/backend/client.d.ts +0 -67
  34. package/dist/src/backend/client.js +0 -363
  35. package/dist/src/index.d.ts +0 -2
  36. package/dist/src/index.js +0 -184
  37. package/dist/src/schema-packs/llm.d.ts +0 -105
  38. package/dist/src/schema-packs/llm.js +0 -429
  39. package/dist/src/tools/dev.d.ts +0 -1
  40. package/dist/src/tools/dev.js +0 -195
  41. package/dist/src/tools/index.d.ts +0 -33
  42. package/dist/src/tools/index.js +0 -60
  43. package/dist/src/tools/specialized/eclipse.d.ts +0 -1
  44. package/dist/src/tools/specialized/eclipse.js +0 -50
  45. package/dist/src/tools/specialized/electional.d.ts +0 -1
  46. package/dist/src/tools/specialized/electional.js +0 -80
  47. package/dist/src/tools/specialized/human_design.d.ts +0 -1
  48. package/dist/src/tools/specialized/human_design.js +0 -69
  49. package/dist/src/tools/specialized/moon.d.ts +0 -1
  50. package/dist/src/tools/specialized/moon.js +0 -51
  51. package/dist/src/tools/specialized/natal.d.ts +0 -1
  52. package/dist/src/tools/specialized/natal.js +0 -92
  53. package/dist/src/tools/specialized/relocation.d.ts +0 -1
  54. package/dist/src/tools/specialized/relocation.js +0 -76
  55. package/dist/src/tools/specialized/synastry.d.ts +0 -1
  56. package/dist/src/tools/specialized/synastry.js +0 -73
  57. package/dist/src/tools/specialized/transits.d.ts +0 -1
  58. package/dist/src/tools/specialized/transits.js +0 -130
  59. package/dist/test/allowlist-and-tools.test.d.ts +0 -1
  60. package/dist/test/allowlist-and-tools.test.js +0 -96
  61. package/dist/test/backend-client.test.d.ts +0 -1
  62. package/dist/test/backend-client.test.js +0 -284
  63. package/dist/test/credentials.test.d.ts +0 -1
  64. package/dist/test/credentials.test.js +0 -143
  65. /package/dist/{src/auth → auth}/credentials.d.ts +0 -0
  66. /package/dist/{src/auth → auth}/credentials.js +0 -0
  67. /package/dist/{src/auth → auth}/device-auth.d.ts +0 -0
  68. /package/dist/{src/auth → auth}/device-auth.js +0 -0
  69. /package/dist/{src/server-sse.d.ts → server-sse.d.ts} +0 -0
  70. /package/dist/{src/server-sse.js → server-sse.js} +0 -0
  71. /package/dist/{src/tools → tools}/auth.d.ts +0 -0
  72. /package/dist/{src/tools → tools}/auth.js +0 -0
  73. /package/dist/{scripts/dev-allowlist.d.ts → tools/specialized/acg.d.ts} +0 -0
  74. /package/dist/{src/tools → tools}/specialized/bazi.d.ts +0 -0
  75. /package/dist/{src/tools → tools}/specialized/bazi.js +0 -0
  76. /package/dist/{src/tools → tools}/specialized/bi_wheel.d.ts +0 -0
  77. /package/dist/{src/tools → tools}/specialized/bi_wheel.js +0 -0
  78. /package/dist/{src/tools → tools}/specialized/chart_wheel.d.ts +0 -0
  79. /package/dist/{src/tools → tools}/specialized/chart_wheel.js +0 -0
  80. /package/dist/{scripts/pack-audit.d.ts → tools/specialized/comparative.d.ts} +0 -0
  81. /package/dist/{scripts/schema-packs.d.ts → tools/specialized/ephemeris_core.d.ts} +0 -0
  82. /package/dist/{scripts/smoke-dev-profile.d.ts → tools/specialized/ephemeris_extended.d.ts} +0 -0
  83. /package/dist/{scripts/sync-readme.d.ts → tools/specialized/hd_group.d.ts} +0 -0
  84. /package/dist/{scripts/test-all-tools.d.ts → tools/specialized/progressed.d.ts} +0 -0
  85. /package/dist/{scripts/test-client.d.ts → tools/specialized/returns.d.ts} +0 -0
  86. /package/dist/{src/tools → tools}/specialized/vedic.d.ts +0 -0
  87. /package/dist/{src/tools → tools}/specialized/vedic.js +0 -0
  88. /package/dist/{scripts/test-sse-client.d.ts → tools/specialized/venus_star_points.d.ts} +0 -0
@@ -0,0 +1,234 @@
1
+ import { registerTool, validateRequired } from "../index.js";
2
+ import { backendClient } from "../../backend/client.js";
3
+ function buildSubject(name, datetime, lat, lon) {
4
+ return {
5
+ name,
6
+ birth_datetime: { iso: datetime },
7
+ birth_location: {
8
+ latitude: { decimal: lat },
9
+ longitude: { decimal: lon },
10
+ },
11
+ };
12
+ }
13
+ // POST /ephemeris/natal/batch — OE-028
14
+ registerTool({
15
+ name: "ephemeris_natal_batch",
16
+ description: "Calculate natal charts for multiple subjects in a single request. " +
17
+ "Supports up to 100 subjects. Returns enhanced natal chart data for each.\n\n" +
18
+ "CREDIT COST: 1 credit per subject.\n\n" +
19
+ "EXAMPLE: Two people batch:\n" +
20
+ " subjects: [\n" +
21
+ " { name: 'Alice', datetime: '1990-04-15T14:30:00', latitude: 41.88, longitude: -87.63 },\n" +
22
+ " { name: 'Bob', datetime: '1988-09-22T08:15:00', latitude: 34.05, longitude: -118.24 }\n" +
23
+ " ]",
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {
27
+ subjects: {
28
+ type: "array",
29
+ items: {
30
+ type: "object",
31
+ properties: {
32
+ name: { type: "string", description: "Subject name." },
33
+ datetime: { type: "string", description: "Birth datetime (ISO 8601)." },
34
+ latitude: { type: "number", description: "Birth latitude." },
35
+ longitude: { type: "number", description: "Birth longitude." },
36
+ },
37
+ required: ["name", "datetime", "latitude", "longitude"],
38
+ },
39
+ description: "Array of subjects (1-100).",
40
+ },
41
+ },
42
+ required: ["subjects"],
43
+ additionalProperties: false,
44
+ },
45
+ handler: async (args) => {
46
+ validateRequired(args, ["subjects"]);
47
+ const items = args.subjects.map((s) => ({
48
+ subject: buildSubject(s.name, s.datetime, s.latitude, s.longitude),
49
+ }));
50
+ return await backendClient.post("/ephemeris/natal/batch", { items });
51
+ },
52
+ });
53
+ // POST /ephemeris/dignities
54
+ registerTool({
55
+ name: "ephemeris_dignities",
56
+ description: "Calculate essential dignities (domicile, exaltation, detriment, fall, peregrine) " +
57
+ "for all planets at a given date/time. Essential for traditional astrology.\n\n" +
58
+ "CREDIT COST: 1 credit per call.",
59
+ inputSchema: {
60
+ type: "object",
61
+ properties: {
62
+ datetime: { type: "string", description: "ISO 8601 date/time." },
63
+ },
64
+ required: ["datetime"],
65
+ additionalProperties: false,
66
+ },
67
+ handler: async (args) => {
68
+ validateRequired(args, ["datetime"]);
69
+ return await backendClient.post("/ephemeris/dignities", {
70
+ date_time: { iso: args.datetime },
71
+ });
72
+ },
73
+ });
74
+ // POST /ephemeris/retrograde-status
75
+ registerTool({
76
+ name: "ephemeris_retrograde_status",
77
+ description: "Get retrograde/direct status and speed for all planets at a given date/time. " +
78
+ "Returns is_retrograde flag, longitude speed, and station proximity.\n\n" +
79
+ "CREDIT COST: 1 credit per call.",
80
+ inputSchema: {
81
+ type: "object",
82
+ properties: {
83
+ datetime: { type: "string", description: "ISO 8601 date/time." },
84
+ planet_id: { type: "integer", description: "Optional single planet ID (0-9). Omit for all planets." },
85
+ },
86
+ required: ["datetime"],
87
+ additionalProperties: false,
88
+ },
89
+ handler: async (args) => {
90
+ validateRequired(args, ["datetime"]);
91
+ const body = {
92
+ date_time: { iso: args.datetime },
93
+ };
94
+ if (args.planet_id != null)
95
+ body.planet_id = args.planet_id;
96
+ return await backendClient.post("/ephemeris/retrograde-status", body);
97
+ },
98
+ });
99
+ // POST /ephemeris/midpoints
100
+ registerTool({
101
+ name: "ephemeris_midpoints",
102
+ description: "Calculate midpoints between all planet pairs for a given date/time and location. " +
103
+ "Returns midpoint longitude, the two planets involved, and any planet at the midpoint.\n\n" +
104
+ "CREDIT COST: 1 credit per call.",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ datetime: { type: "string", description: "ISO 8601 date/time." },
109
+ latitude: { type: "number", description: "Observer latitude." },
110
+ longitude: { type: "number", description: "Observer longitude." },
111
+ },
112
+ required: ["datetime", "latitude", "longitude"],
113
+ additionalProperties: false,
114
+ },
115
+ handler: async (args) => {
116
+ validateRequired(args, ["datetime", "latitude", "longitude"]);
117
+ return await backendClient.post("/ephemeris/midpoints", {
118
+ date_time: { iso: args.datetime },
119
+ latitude: args.latitude,
120
+ longitude: args.longitude,
121
+ });
122
+ },
123
+ });
124
+ // POST /ephemeris/fixed-stars
125
+ registerTool({
126
+ name: "ephemeris_fixed_stars",
127
+ description: "Calculate positions of fixed stars and conjunctions to natal planets. " +
128
+ "Returns star longitude, magnitude, and any planets within orb.\n\n" +
129
+ "CREDIT COST: 1 credit per call.",
130
+ inputSchema: {
131
+ type: "object",
132
+ properties: {
133
+ datetime: { type: "string", description: "ISO 8601 date/time." },
134
+ star_names: {
135
+ type: "array",
136
+ items: { type: "string" },
137
+ description: "Optional list of star names (e.g. ['Regulus', 'Spica']). Omit for default list.",
138
+ },
139
+ orb: { type: "number", description: "Conjunction orb in degrees. Default 1°." },
140
+ },
141
+ required: ["datetime"],
142
+ additionalProperties: false,
143
+ },
144
+ handler: async (args) => {
145
+ validateRequired(args, ["datetime"]);
146
+ const body = {
147
+ date_time: { iso: args.datetime },
148
+ };
149
+ if (args.star_names)
150
+ body.star_names = args.star_names;
151
+ if (args.orb != null)
152
+ body.orb = args.orb;
153
+ return await backendClient.post("/ephemeris/fixed-stars", body);
154
+ },
155
+ });
156
+ // POST /ephemeris/hermetic-lots
157
+ registerTool({
158
+ name: "ephemeris_hermetic_lots",
159
+ description: "Calculate Arabic Parts / Hermetic Lots (Lot of Fortune, Spirit, etc.) " +
160
+ "for a given chart.\n\n" +
161
+ "CREDIT COST: 1 credit per call.",
162
+ inputSchema: {
163
+ type: "object",
164
+ properties: {
165
+ datetime: { type: "string", description: "ISO 8601 date/time." },
166
+ latitude: { type: "number", description: "Observer latitude." },
167
+ longitude: { type: "number", description: "Observer longitude." },
168
+ },
169
+ required: ["datetime", "latitude", "longitude"],
170
+ additionalProperties: false,
171
+ },
172
+ handler: async (args) => {
173
+ validateRequired(args, ["datetime", "latitude", "longitude"]);
174
+ return await backendClient.post("/ephemeris/hermetic-lots", {
175
+ date_time: { iso: args.datetime },
176
+ latitude: args.latitude,
177
+ longitude: args.longitude,
178
+ });
179
+ },
180
+ });
181
+ // POST /ephemeris/angles-points
182
+ registerTool({
183
+ name: "ephemeris_angles_points",
184
+ description: "Calculate chart angles and sensitive points (ASC, MC, DSC, IC, Vertex, " +
185
+ "East Point, etc.) for a given date/time and location.\n\n" +
186
+ "CREDIT COST: 1 credit per call.",
187
+ inputSchema: {
188
+ type: "object",
189
+ properties: {
190
+ datetime: { type: "string", description: "ISO 8601 date/time." },
191
+ latitude: { type: "number", description: "Observer latitude." },
192
+ longitude: { type: "number", description: "Observer longitude." },
193
+ },
194
+ required: ["datetime", "latitude", "longitude"],
195
+ additionalProperties: false,
196
+ },
197
+ handler: async (args) => {
198
+ validateRequired(args, ["datetime", "latitude", "longitude"]);
199
+ return await backendClient.post("/ephemeris/angles-points", {
200
+ date_time: { iso: args.datetime },
201
+ latitude: args.latitude,
202
+ longitude: args.longitude,
203
+ });
204
+ },
205
+ });
206
+ // POST /ephemeris/aspect-check
207
+ registerTool({
208
+ name: "ephemeris_aspect_check",
209
+ description: "Check the aspect between two ecliptic longitudes. Returns the angular separation " +
210
+ "and any aspects within orb (conjunction, sextile, square, trine, opposition, etc.).\n\n" +
211
+ "CREDIT COST: 1 credit per call.\n\n" +
212
+ "EXAMPLE: Check aspect between 15° Aries and 75° Gemini:\n" +
213
+ " longitude_1=15, longitude_2=75",
214
+ inputSchema: {
215
+ type: "object",
216
+ properties: {
217
+ longitude_1: { type: "number", description: "First ecliptic longitude (0-360)." },
218
+ longitude_2: { type: "number", description: "Second ecliptic longitude (0-360)." },
219
+ max_orb: { type: "number", description: "Maximum orb in degrees. Default 8°." },
220
+ },
221
+ required: ["longitude_1", "longitude_2"],
222
+ additionalProperties: false,
223
+ },
224
+ handler: async (args) => {
225
+ validateRequired(args, ["longitude_1", "longitude_2"]);
226
+ const body = {
227
+ longitude_1: args.longitude_1,
228
+ longitude_2: args.longitude_2,
229
+ };
230
+ if (args.max_orb != null)
231
+ body.max_orb = args.max_orb;
232
+ return await backendClient.post("/ephemeris/aspect-check", body);
233
+ },
234
+ });
@@ -0,0 +1,107 @@
1
+ import { registerTool, validateRequired } from "../index.js";
2
+ import { backendClient } from "../../backend/client.js";
3
+ // POST /human-design/composite — OE-027
4
+ registerTool({
5
+ name: "human_design_composite",
6
+ description: "Calculate a Human Design composite chart for two people. Merges both bodygraphs " +
7
+ "to show shared channels, authority dynamics, and relationship type.\n\n" +
8
+ "CREDIT COST: 4 credits per call.\n\n" +
9
+ "EXAMPLE:\n" +
10
+ " person_a_datetime='1990-04-15T14:30:00Z', person_b_datetime='1988-09-22T08:15:00Z'",
11
+ inputSchema: {
12
+ type: "object",
13
+ properties: {
14
+ person_a_datetime: {
15
+ type: "string",
16
+ description: "Person A birth datetime as ISO 8601 UTC (e.g. '1990-04-15T14:30:00Z').",
17
+ },
18
+ person_b_datetime: {
19
+ type: "string",
20
+ description: "Person B birth datetime as ISO 8601 UTC.",
21
+ },
22
+ format: {
23
+ type: "string",
24
+ enum: ["json", "llm"],
25
+ description: "Output format. 'llm' is compact.",
26
+ },
27
+ },
28
+ required: ["person_a_datetime", "person_b_datetime"],
29
+ additionalProperties: false,
30
+ },
31
+ handler: async (args) => {
32
+ validateRequired(args, ["person_a_datetime", "person_b_datetime"]);
33
+ const body = {
34
+ subject_1: {
35
+ birth_datetime_utc: args.person_a_datetime,
36
+ },
37
+ subject_2: {
38
+ birth_datetime_utc: args.person_b_datetime,
39
+ },
40
+ };
41
+ if (args.format)
42
+ body.format = args.format;
43
+ return await backendClient.post("/human-design/composite", body);
44
+ },
45
+ });
46
+ // POST /human-design/penta — OE-027
47
+ registerTool({
48
+ name: "human_design_penta",
49
+ description: "Calculate a Human Design Penta (group) chart for 3-5 people. Shows functional " +
50
+ "attributes, leadership dynamics, channels, redundancies, and a group stability score.\n\n" +
51
+ "CREDIT COST: 6 credits per call.\n\n" +
52
+ "EXAMPLE (3 people):\n" +
53
+ " group_name='Team Alpha',\n" +
54
+ " members=[\n" +
55
+ " {id: 'alice', name: 'Alice', datetime: '1990-04-15T14:30:00Z'},\n" +
56
+ " {id: 'bob', name: 'Bob', datetime: '1988-09-22T08:15:00Z'},\n" +
57
+ " {id: 'carol', name: 'Carol', datetime: '1995-01-10T11:00:00Z'}\n" +
58
+ " ]",
59
+ inputSchema: {
60
+ type: "object",
61
+ properties: {
62
+ group_name: {
63
+ type: "string",
64
+ description: "Name of the group.",
65
+ },
66
+ members: {
67
+ type: "array",
68
+ items: {
69
+ type: "object",
70
+ properties: {
71
+ id: { type: "string", description: "Unique member ID." },
72
+ name: { type: "string", description: "Member's name." },
73
+ datetime: { type: "string", description: "Birth datetime as ISO 8601 UTC." },
74
+ },
75
+ required: ["id", "name", "datetime"],
76
+ },
77
+ description: "Array of 3-5 members with id, name, and datetime.",
78
+ minItems: 3,
79
+ maxItems: 5,
80
+ },
81
+ format: {
82
+ type: "string",
83
+ enum: ["json", "llm"],
84
+ description: "Output format.",
85
+ },
86
+ },
87
+ required: ["group_name", "members"],
88
+ additionalProperties: false,
89
+ },
90
+ handler: async (args) => {
91
+ validateRequired(args, ["group_name", "members"]);
92
+ const members = args.members.map((m) => ({
93
+ id: m.id,
94
+ name: m.name,
95
+ birth_data: {
96
+ birth_datetime_utc: m.datetime,
97
+ },
98
+ }));
99
+ const body = {
100
+ group_name: args.group_name,
101
+ members,
102
+ };
103
+ if (args.format)
104
+ body.format = args.format;
105
+ return await backendClient.post("/human-design/penta", body);
106
+ },
107
+ });
@@ -1,22 +1,35 @@
1
- import { registerTool } from "../index.js";
1
+ import { registerTool, validateRequired } from "../index.js";
2
2
  import { backendClient } from "../../backend/client.js";
3
+ /**
4
+ * Ensure a datetime string has a timezone offset for Go's time.Time parsing.
5
+ * Go's encoding/json only accepts RFC 3339 (must have Z or +HH:MM offset).
6
+ */
7
+ function ensureTimezone(dt) {
8
+ if (!dt)
9
+ return dt;
10
+ // Already has timezone indicator
11
+ if (/[Zz]$/.test(dt) || /[+-]\d{2}:\d{2}$/.test(dt) || /[+-]\d{4}$/.test(dt))
12
+ return dt;
13
+ return dt + "Z";
14
+ }
3
15
  registerTool({
4
- name: "human_design.chart",
5
- description: "Calculate a full Human Design bodygraph chart from birth data. Returns the person's Type " +
16
+ name: "human_design_chart",
17
+ description: "Calculate a full Human Design I Ching hexagram chart from birth data. Returns the person's Type " +
6
18
  "(Generator, Manifesting Generator, Projector, Manifestor, Reflector), Strategy, Authority, " +
7
19
  "Profile (e.g. 1/3, 2/4), defined and undefined Centers, activated Gates and Channels, " +
8
20
  "Incarnation Cross, and both Personality (conscious) and Design (unconscious) planetary positions.\n\n" +
9
21
  "CREDIT COST: 1 credit per call.\n\n" +
10
22
  "Human Design uses two calculation moments: the birth time (Personality) and ~88° of Sun motion " +
11
23
  "before birth (~3 months prior, the Design calculation). The API handles this automatically.\n\n" +
12
- "EXAMPLE: Get the Human Design chart for someone born April 15, 1990 at 2:30 PM in Chicago:\n" +
13
- " datetime='1990-04-15T14:30:00', latitude=41.8781, longitude=-87.6298",
24
+ "IMPORTANT: The datetime should be in UTC. If you have local birth time, convert to UTC first.\n\n" +
25
+ "EXAMPLE: Get the Human Design chart for someone born April 15, 1990 at 7:30 PM UTC:\n" +
26
+ " datetime='1990-04-15T19:30:00Z', latitude=41.8781, longitude=-87.6298",
14
27
  inputSchema: {
15
28
  type: "object",
16
29
  properties: {
17
30
  datetime: {
18
31
  type: "string",
19
- description: "ISO 8601 birth datetime (local time at birth location), e.g. '1990-04-15T14:30:00'.",
32
+ description: "ISO 8601 birth datetime in UTC, e.g. '1990-04-15T19:30:00Z'. Must include 'Z' or timezone offset.",
20
33
  },
21
34
  latitude: {
22
35
  type: "number",
@@ -29,19 +42,22 @@ registerTool({
29
42
  format: {
30
43
  type: "string",
31
44
  enum: ["json", "llm"],
32
- description: "Output format. 'llm' returns compact array projection for token efficiency (Pro tier). " +
45
+ description: "Output format. 'llm' returns compact array projection for token efficiency (available on all tiers). " +
33
46
  "'json' returns verbose full output.",
34
47
  },
35
48
  },
36
- required: ["datetime", "latitude", "longitude"],
49
+ required: ["datetime"],
37
50
  additionalProperties: false,
38
51
  },
39
52
  handler: async (args) => {
53
+ validateRequired(args, ["datetime"]);
40
54
  const body = {
41
- datetime: args.datetime,
42
- latitude: args.latitude,
43
- longitude: args.longitude,
55
+ birth_datetime_utc: ensureTimezone(args.datetime),
44
56
  };
57
+ if (args.latitude != null)
58
+ body.latitude = args.latitude;
59
+ if (args.longitude != null)
60
+ body.longitude = args.longitude;
45
61
  const query = {};
46
62
  if (args.format)
47
63
  query.format = args.format;
@@ -1,7 +1,7 @@
1
- import { registerTool } from "../index.js";
1
+ import { registerTool, validateCoordinates } from "../index.js";
2
2
  import { backendClient } from "../../backend/client.js";
3
3
  registerTool({
4
- name: "ephemeris.moon_phase",
4
+ name: "ephemeris_moon_phase",
5
5
  description: "Get the current Moon phase and void-of-course status. Returns the Moon's sign, phase name " +
6
6
  "(New, Waxing Crescent, First Quarter, Waxing Gibbous, Full, Waning Gibbous, Last Quarter, " +
7
7
  "Waning Crescent), exact phase angle, illumination percentage, and the next void-of-course period.\n\n" +
@@ -31,6 +31,7 @@ registerTool({
31
31
  additionalProperties: false,
32
32
  },
33
33
  handler: async (args) => {
34
+ validateCoordinates(args, "latitude", "longitude");
34
35
  const params = {};
35
36
  if (args.datetime)
36
37
  params.datetime = args.datetime;
@@ -1,12 +1,18 @@
1
- import { registerTool } from "../index.js";
1
+ import { registerTool, validateRequired } from "../index.js";
2
2
  import { backendClient } from "../../backend/client.js";
3
3
  const DATETIME_DESC = "ISO 8601 datetime string, e.g. '1990-04-15T14:30:00' (local time at birth location). " +
4
4
  "Include timezone offset if known, e.g. '1990-04-15T14:30:00-05:00'.";
5
+ /** Map human-readable house system names to Swiss Ephemeris single-letter codes */
6
+ const HOUSE_SYSTEM_MAP = {
7
+ placidus: "P", whole_sign: "W", equal: "E", koch: "K",
8
+ campanus: "C", regiomontanus: "R", porphyry: "O",
9
+ alcabitius: "B", morinus: "M",
10
+ };
5
11
  registerTool({
6
- name: "ephemeris.natal_chart",
12
+ name: "ephemeris_natal_chart",
7
13
  description: "Calculate a full natal (birth) chart for a person. Returns planetary positions, house cusps, " +
8
14
  "aspects, and chart patterns. Use format='llm' for a compact, token-efficient output ideal for " +
9
- "interpretation (requires Pro tier). The result includes all major planets, angles (ASC/MC/DSC/IC), " +
15
+ "interpretation (available on all tiers). The result includes all major planets, angles (ASC/MC/DSC/IC), " +
10
16
  "essential dignities, retrograde status, house system data, and major aspect grid.\n\n" +
11
17
  "CREDIT COST: 1 credit per call.\n\n" +
12
18
  "EXAMPLE: Calculate the natal chart for someone born April 15, 1990 at 2:30 PM in Chicago:\n" +
@@ -34,32 +40,47 @@ registerTool({
34
40
  format: {
35
41
  type: "string",
36
42
  enum: ["json", "llm"],
37
- description: "Output format. 'llm' returns a compact array-based projection optimized for LLM token efficiency (Pro tier required). 'json' returns full verbose JSON.",
43
+ description: "Output format. 'llm' returns a compact array-based projection optimized for LLM token efficiency (available on all tiers). 'json' returns full verbose JSON.",
38
44
  },
39
45
  include_arabic_parts: {
40
46
  type: "boolean",
41
- description: "If true, include Hermetic Lots (Arabic Parts) in the response.",
47
+ description: "Reserved for future use. Hermetic Lots / Arabic Parts are currently available via the dedicated /ephemeris/hermetic-lots endpoint.",
42
48
  },
43
49
  include_fixed_stars: {
44
50
  type: "boolean",
45
- description: "If true, include fixed star conjunctions in the response.",
51
+ description: "Reserved for future use. Fixed star positions are currently available via the dedicated /ephemeris/fixed-stars endpoint.",
46
52
  },
47
53
  },
48
54
  required: ["datetime", "latitude", "longitude"],
49
55
  additionalProperties: false,
50
56
  },
51
57
  handler: async (args) => {
58
+ validateRequired(args, ["datetime", "latitude", "longitude"]);
52
59
  const body = {
53
- datetime: args.datetime,
54
- latitude: args.latitude,
55
- longitude: args.longitude,
60
+ subject: {
61
+ name: "MCP Request",
62
+ birth_datetime: { iso: args.datetime },
63
+ birth_location: {
64
+ latitude: { decimal: args.latitude },
65
+ longitude: { decimal: args.longitude }
66
+ },
67
+ }
56
68
  };
57
- if (args.house_system)
58
- body.house_system = args.house_system;
59
- if (args.include_arabic_parts)
60
- body.include_arabic_parts = args.include_arabic_parts;
61
- if (args.include_fixed_stars)
62
- body.include_fixed_stars = args.include_fixed_stars;
69
+ if (args.house_system) {
70
+ const code = HOUSE_SYSTEM_MAP[args.house_system] ?? args.house_system;
71
+ body.configuration = { house_system: code };
72
+ }
73
+ if (args.include_fixed_stars) {
74
+ body.configuration = {
75
+ ...(body.configuration || {}),
76
+ fixed_star_options: { include: true }
77
+ };
78
+ }
79
+ if (args.include_arabic_parts) {
80
+ body.options = {
81
+ include_hermetic_lots: true,
82
+ };
83
+ }
63
84
  const query = {};
64
85
  if (args.format)
65
86
  query.format = args.format;
@@ -0,0 +1,65 @@
1
+ import { registerTool, validateRequired } from "../index.js";
2
+ import { backendClient } from "../../backend/client.js";
3
+ registerTool({
4
+ name: "ephemeris_progressed_chart",
5
+ description: "Calculate a Secondary Progressed (or Solar Arc / Tertiary) chart. " +
6
+ "Advances the natal chart symbolically — 1 day = 1 year (secondary), or using solar arc motion. " +
7
+ "Returns progressed planet positions, house cusps, aspects, and retrograde status.\n\n" +
8
+ "CREDIT COST: 2 credits per call.\n\n" +
9
+ "EXAMPLE: Secondary progressions for someone born 1985-06-21, progressed to 2026-01-01:\n" +
10
+ " birth_datetime='1985-06-21T14:00:00', birth_latitude=51.5, birth_longitude=-0.12,\n" +
11
+ " target_datetime='2026-01-01', method='secondary'",
12
+ inputSchema: {
13
+ type: "object",
14
+ properties: {
15
+ birth_datetime: {
16
+ type: "string",
17
+ description: "ISO 8601 natal birth date/time.",
18
+ },
19
+ birth_latitude: {
20
+ type: "number",
21
+ description: "Birth latitude in decimal degrees.",
22
+ },
23
+ birth_longitude: {
24
+ type: "number",
25
+ description: "Birth longitude in decimal degrees.",
26
+ },
27
+ target_datetime: {
28
+ type: "string",
29
+ description: "Target date to progress the chart to (ISO 8601).",
30
+ },
31
+ method: {
32
+ type: "string",
33
+ enum: ["secondary", "solar_arc", "tertiary"],
34
+ description: "Progression method. Defaults to 'secondary' (1 day = 1 year).",
35
+ },
36
+ include_aspects: {
37
+ type: "boolean",
38
+ description: "Whether to include aspect grid in the response. Default false.",
39
+ },
40
+ },
41
+ required: ["birth_datetime", "birth_latitude", "birth_longitude", "target_datetime"],
42
+ additionalProperties: false,
43
+ },
44
+ handler: async (args) => {
45
+ validateRequired(args, ["birth_datetime", "birth_latitude", "birth_longitude", "target_datetime"]);
46
+ const body = {
47
+ subject: {
48
+ name: "Progressed Subject",
49
+ birth_datetime: { iso: args.birth_datetime },
50
+ birth_location: {
51
+ latitude: { decimal: args.birth_latitude },
52
+ longitude: { decimal: args.birth_longitude },
53
+ },
54
+ },
55
+ target_datetime: { iso: args.target_datetime },
56
+ };
57
+ if (args.method) {
58
+ body.progression_options = { method: args.method };
59
+ }
60
+ if (args.include_aspects != null) {
61
+ body.options = { include_aspects: args.include_aspects };
62
+ }
63
+ return await backendClient.post("/ephemeris/progressed", body);
64
+ },
65
+ });
@@ -1,7 +1,7 @@
1
- import { registerTool } from "../index.js";
1
+ import { registerTool, validateRequired } from "../index.js";
2
2
  import { backendClient } from "../../backend/client.js";
3
3
  registerTool({
4
- name: "ephemeris.relocation",
4
+ name: "ephemeris_relocation",
5
5
  description: "Calculate a relocation chart — the same natal planetary positions re-cast for a different " +
6
6
  "geographic location. Used to understand how living in a different city shifts house placements " +
7
7
  "and angles, without changing the planetary longitudes in the chart.\n\n" +
@@ -40,23 +40,27 @@ registerTool({
40
40
  format: {
41
41
  type: "string",
42
42
  enum: ["json", "llm"],
43
- description: "Output format. 'llm' = compact token-efficient output (Startup tier).",
43
+ description: "Output format. 'llm' = compact token-efficient output (available on all tiers).",
44
44
  },
45
45
  },
46
46
  required: ["natal_datetime", "natal_latitude", "natal_longitude", "relocation_latitude", "relocation_longitude"],
47
47
  additionalProperties: false,
48
48
  },
49
49
  handler: async (args) => {
50
+ validateRequired(args, ["natal_datetime", "natal_latitude", "natal_longitude", "relocation_latitude", "relocation_longitude"]);
50
51
  const body = {
51
52
  natal: {
52
- datetime: args.natal_datetime,
53
- latitude: args.natal_latitude,
54
- longitude: args.natal_longitude,
55
- },
56
- relocation: {
57
- latitude: args.relocation_latitude,
58
- longitude: args.relocation_longitude,
53
+ subject: {
54
+ name: "MCP Request",
55
+ birth_datetime: { iso: args.natal_datetime },
56
+ birth_location: {
57
+ latitude: { decimal: args.natal_latitude },
58
+ longitude: { decimal: args.natal_longitude },
59
+ },
60
+ },
59
61
  },
62
+ relocation_lat: args.relocation_latitude,
63
+ relocation_lon: args.relocation_longitude,
60
64
  };
61
65
  if (args.house_system)
62
66
  body.house_system = args.house_system;