@openephemeris/mcp-server 3.3.2 → 3.4.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.
@@ -277,6 +277,8 @@ export class BackendClient {
277
277
  return new BackendError(`Resource not found: ${msg}`, 404, "not_found", false);
278
278
  if (status === 400)
279
279
  return new BackendError(`Invalid request: ${msg}`, 400, "bad_request", false);
280
+ if (status === 422)
281
+ return new BackendError(`Validation error: ${msg}`, 422, "unprocessable_entity", false);
280
282
  if (status >= 500)
281
283
  return new BackendError(`Backend error (${status}): ${msg}`, status, "server_error", true);
282
284
  }
@@ -34,6 +34,7 @@ registerTool({
34
34
  person_b_timezone: { type: "string", description: "IANA timezone name for Person B, e.g. 'Europe/London'." },
35
35
  person_b_latitude: { type: "number", description: "Person B birth latitude." },
36
36
  person_b_longitude: { type: "number", description: "Person B birth longitude." },
37
+ format: { type: "string", enum: ["json", "llm"], description: "Use 'llm' for token-efficient LLM projection." },
37
38
  },
38
39
  required: [
39
40
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
@@ -46,11 +47,17 @@ registerTool({
46
47
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
47
48
  "person_b_datetime", "person_b_latitude", "person_b_longitude",
48
49
  ]);
49
- return await backendClient.post("/comparative/composite", {
50
- subjects: [
51
- buildSubject("Person A", args.person_a_datetime, args.person_a_latitude, args.person_a_longitude, args.person_a_timezone),
52
- buildSubject("Person B", args.person_b_datetime, args.person_b_latitude, args.person_b_longitude, args.person_b_timezone),
53
- ],
50
+ const query = {};
51
+ if (args.format)
52
+ query.format = args.format;
53
+ return await backendClient.request("POST", "/comparative/composite", {
54
+ params: query,
55
+ data: {
56
+ subjects: [
57
+ buildSubject("Person A", args.person_a_datetime, args.person_a_latitude, args.person_a_longitude, args.person_a_timezone),
58
+ buildSubject("Person B", args.person_b_datetime, args.person_b_latitude, args.person_b_longitude, args.person_b_timezone),
59
+ ],
60
+ }
54
61
  });
55
62
  },
56
63
  });
@@ -71,6 +78,7 @@ registerTool({
71
78
  person_b_timezone: { type: "string", description: "IANA timezone name for Person B, e.g. 'Europe/London'." },
72
79
  person_b_latitude: { type: "number", description: "Person B birth latitude." },
73
80
  person_b_longitude: { type: "number", description: "Person B birth longitude." },
81
+ format: { type: "string", enum: ["json", "llm"], description: "Use 'llm' for token-efficient LLM projection." },
74
82
  },
75
83
  required: [
76
84
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
@@ -83,11 +91,17 @@ registerTool({
83
91
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
84
92
  "person_b_datetime", "person_b_latitude", "person_b_longitude",
85
93
  ]);
86
- return await backendClient.post("/comparative/composite/midpoint", {
87
- subjects: [
88
- buildSubject("Person A", args.person_a_datetime, args.person_a_latitude, args.person_a_longitude, args.person_a_timezone),
89
- buildSubject("Person B", args.person_b_datetime, args.person_b_latitude, args.person_b_longitude, args.person_b_timezone),
90
- ],
94
+ const query = {};
95
+ if (args.format)
96
+ query.format = args.format;
97
+ return await backendClient.request("POST", "/comparative/composite/midpoint", {
98
+ params: query,
99
+ data: {
100
+ subjects: [
101
+ buildSubject("Person A", args.person_a_datetime, args.person_a_latitude, args.person_a_longitude, args.person_a_timezone),
102
+ buildSubject("Person B", args.person_b_datetime, args.person_b_latitude, args.person_b_longitude, args.person_b_timezone),
103
+ ],
104
+ }
91
105
  });
92
106
  },
93
107
  });
@@ -108,6 +122,7 @@ registerTool({
108
122
  person_b_timezone: { type: "string", description: "IANA timezone name for Person B, e.g. 'Europe/London'." },
109
123
  person_b_latitude: { type: "number", description: "Person B birth latitude." },
110
124
  person_b_longitude: { type: "number", description: "Person B birth longitude." },
125
+ format: { type: "string", enum: ["json", "llm"], description: "Use 'llm' for token-efficient LLM projection." },
111
126
  },
112
127
  required: [
113
128
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
@@ -120,11 +135,17 @@ registerTool({
120
135
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
121
136
  "person_b_datetime", "person_b_latitude", "person_b_longitude",
122
137
  ]);
123
- return await backendClient.post("/comparative/overlay", {
124
- subjects: [
125
- buildSubject("Person A", args.person_a_datetime, args.person_a_latitude, args.person_a_longitude, args.person_a_timezone),
126
- buildSubject("Person B", args.person_b_datetime, args.person_b_latitude, args.person_b_longitude, args.person_b_timezone),
127
- ],
138
+ const query = {};
139
+ if (args.format)
140
+ query.format = args.format;
141
+ return await backendClient.request("POST", "/comparative/overlay", {
142
+ params: query,
143
+ data: {
144
+ subjects: [
145
+ buildSubject("Person A", args.person_a_datetime, args.person_a_latitude, args.person_a_longitude, args.person_a_timezone),
146
+ buildSubject("Person B", args.person_b_datetime, args.person_b_latitude, args.person_b_longitude, args.person_b_timezone),
147
+ ],
148
+ }
128
149
  });
129
150
  },
130
151
  });
@@ -145,6 +166,7 @@ registerTool({
145
166
  natal_longitude: { type: "number", description: "Natal birth longitude." },
146
167
  transit_datetime: { type: "string", description: "Transit moment (ISO 8601). Defaults to now." },
147
168
  transit_timezone: { type: "string", description: "IANA timezone name for Transit, e.g. 'America/Denver'. Only applicable if transit_datetime is provided without offset." },
169
+ format: { type: "string", enum: ["json", "llm"], description: "Use 'llm' for token-efficient LLM projection." },
148
170
  },
149
171
  required: ["natal_datetime", "natal_latitude", "natal_longitude"],
150
172
  additionalProperties: false,
@@ -154,12 +176,15 @@ registerTool({
154
176
  const subjects = [
155
177
  buildSubject("Natal", args.natal_datetime, args.natal_latitude, args.natal_longitude, args.natal_timezone),
156
178
  ];
157
- // The transit subject uses the transit datetime or current time
158
179
  if (args.transit_datetime) {
159
180
  subjects.push(buildSubject("Transit", args.transit_datetime, args.natal_latitude, args.natal_longitude, args.transit_timezone));
160
181
  }
161
- return await backendClient.post("/comparative/natal-transits", {
162
- subjects,
182
+ const query = {};
183
+ if (args.format)
184
+ query.format = args.format;
185
+ return await backendClient.request("POST", "/comparative/natal-transits", {
186
+ params: query,
187
+ data: { subjects }
163
188
  });
164
189
  },
165
190
  });
@@ -110,16 +110,19 @@ registerTool({
110
110
  registerTool({
111
111
  name: "ephemeris_planetary_return",
112
112
  description: "Calculate a planetary return — when any planet returns to its natal longitude. " +
113
- "Useful for Jupiter returns (~12 years), Saturn returns (~29 years), etc.\n\n" +
113
+ "Useful for Jupiter returns (~12 years), Saturn returns (~29 years), Chiron returns (~50 years), " +
114
+ "Uranus returns (~84 years), etc.\n\n" +
114
115
  "CREDIT COST: 1 credit per call.\n\n" +
115
116
  "EXAMPLE: Saturn return for birth 1990-04-15 near year 2019:\n" +
116
- " body='saturn', birth_datetime='1990-04-15T14:30:00', target_datetime='2019-01-01'",
117
+ " body='saturn', birth_datetime='1990-04-15T14:30:00', target_datetime='2019-01-01'\n" +
118
+ "EXAMPLE: Chiron return for birth 1975-03-10 near age 50:\n" +
119
+ " body='chiron', birth_datetime='1975-03-10T08:00:00', target_datetime='2025-01-01'",
117
120
  inputSchema: {
118
121
  type: "object",
119
122
  properties: {
120
123
  body: {
121
124
  type: "string",
122
- description: "Planet name: sun, moon, mercury, venus, mars, jupiter, saturn.",
125
+ description: "Planet name: sun, moon, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto, chiron, pholus.",
123
126
  },
124
127
  birth_datetime: {
125
128
  type: "string",
@@ -8,6 +8,13 @@ registerTool({
8
8
  "finding optimal timing windows.\n\n" +
9
9
  "HOW IT WORKS: This tool first calculates the natal chart to extract your actual planetary " +
10
10
  "ecliptic longitudes, then searches for transiting planets crossing those exact degrees.\n\n" +
11
+ "ASPECT ANGLES: Use aspect_angle to search for specific aspects to natal positions:\n" +
12
+ " 0 = conjunction/return (default), 180 = opposition, 90 = square, 120 = trine.\n" +
13
+ " Example: Saturn Return → transiting_planets=['saturn'], natal_points=['saturn'], aspect_angle=0\n" +
14
+ " Example: Uranus Opposition → transiting_planets=['uranus'], natal_points=['uranus'], aspect_angle=180\n" +
15
+ " Example: Chiron Return → transiting_planets=['chiron'], natal_points=['chiron'], aspect_angle=0\n\n" +
16
+ "SUPPORTED BODIES: Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, " +
17
+ "Pluto, Chiron, Pholus, Ceres, Pallas, Juno, Vesta, MeanNode.\n\n" +
11
18
  "CREDIT COST: 6 credits per call (1 for natal + 5 for transit search).\n\n" +
12
19
  "EXAMPLE: Find all Saturn transits to the natal Sun/Moon for the next 6 months:\n" +
13
20
  " natal_datetime='1990-04-15T14:30:00', natal_latitude=41.8781, natal_longitude=-87.6298,\n" +
@@ -38,13 +45,19 @@ registerTool({
38
45
  transiting_planets: {
39
46
  type: "array",
40
47
  items: { type: "string" },
41
- description: "List of transiting planet IDs to search. E.g. ['saturn', 'jupiter', 'uranus', 'pluto']. " +
48
+ description: "List of transiting planet IDs to search. E.g. ['saturn', 'jupiter', 'uranus', 'pluto', 'chiron']. " +
42
49
  "Omit to search all outer planets.",
43
50
  },
44
51
  natal_points: {
45
52
  type: "array",
46
53
  items: { type: "string" },
47
- description: "Natal point IDs whose exact longitudes should be targeted. E.g. ['sun', 'moon', 'asc', 'mc']. Omit for all core points.",
54
+ description: "Natal point IDs whose exact longitudes should be targeted. E.g. ['sun', 'moon', 'asc', 'mc', 'saturn', 'chiron']. Omit for all core points.",
55
+ },
56
+ aspect_angle: {
57
+ type: "number",
58
+ description: "Aspect angle in degrees to apply to each natal longitude. 0 = conjunction/return (default), " +
59
+ "180 = opposition, 90 = square, 120 = trine. The effective search target becomes " +
60
+ "(natal_longitude + aspect_angle) mod 360.",
48
61
  },
49
62
  },
50
63
  required: ["natal_datetime", "natal_latitude", "natal_longitude", "start_date", "end_date"],
@@ -119,11 +132,21 @@ registerTool({
119
132
  };
120
133
  if (args.transiting_planets)
121
134
  transitBody.planet_names = args.transiting_planets;
135
+ // When aspect_angle is provided, apply offset to compute effective target degrees
136
+ // (natal_lon + aspect_angle) mod 360 for each natal point
137
+ let effectiveTargets = targetDegrees;
138
+ if (typeof args.aspect_angle === "number" && args.aspect_angle !== 0) {
139
+ effectiveTargets = targetDegrees.map((deg) => ((deg + args.aspect_angle) % 360 + 360) % 360);
140
+ transitBody.target_degrees = effectiveTargets;
141
+ // Also include aspect metadata for labeling in results
142
+ transitBody.search_criteria = { aspect_angle: args.aspect_angle };
143
+ }
122
144
  const transitResult = await backendClient.request("POST", "/predictive/transits/search", { data: transitBody });
123
145
  // Return combined context so the LLM knows what natal positions were targeted
124
146
  return {
125
147
  natal_positions: natalPositionMap,
126
- target_degrees_used: targetDegrees,
148
+ target_degrees_used: effectiveTargets,
149
+ aspect_angle: args.aspect_angle ?? 0,
127
150
  transit_results: transitResult,
128
151
  };
129
152
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openephemeris/mcp-server",
3
- "version": "3.3.2",
3
+ "version": "3.4.0",
4
4
  "description": "Model Context Protocol server for the Open Ephemeris astronomical computation API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",