@openephemeris/mcp-server 3.3.1 → 3.3.3

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
  }
@@ -17,9 +17,11 @@ registerTool({
17
17
  type: "object",
18
18
  properties: {
19
19
  datetime_a: { type: "string", description: DATETIME_DESC },
20
+ timezone_a: { type: "string", description: "IANA timezone name for subject A, e.g. 'America/Denver'." },
20
21
  latitude_a: { type: "number", description: "Latitude for subject A" },
21
22
  longitude_a: { type: "number", description: "Longitude for subject A" },
22
23
  datetime_b: { type: "string", description: DATETIME_DESC },
24
+ timezone_b: { type: "string", description: "IANA timezone name for subject B, e.g. 'Europe/London'." },
23
25
  latitude_b: { type: "number", description: "Latitude for subject B" },
24
26
  longitude_b: { type: "number", description: "Longitude for subject B" },
25
27
  house_system: {
@@ -56,6 +58,12 @@ registerTool({
56
58
  },
57
59
  }
58
60
  };
61
+ if (args.timezone_a) {
62
+ body.subject_a.birth_location.timezone = { iana_name: args.timezone_a };
63
+ }
64
+ if (args.timezone_b) {
65
+ body.subject_b.birth_location.timezone = { iana_name: args.timezone_b };
66
+ }
59
67
  if (args.house_system) {
60
68
  const mappedCode = HOUSE_SYSTEM_MAP[args.house_system] || "P";
61
69
  body.configuration = { house_system: mappedCode };
@@ -20,6 +20,10 @@ registerTool({
20
20
  type: "string",
21
21
  description: DATETIME_DESC,
22
22
  },
23
+ timezone: {
24
+ type: "string",
25
+ description: "IANA timezone name, e.g. 'America/Denver'. Use this if passing local time without a UTC offset in the datetime string.",
26
+ },
23
27
  latitude: {
24
28
  type: "number",
25
29
  description: "Geographic latitude of birth location in decimal degrees (positive = North).",
@@ -54,6 +58,9 @@ registerTool({
54
58
  },
55
59
  }
56
60
  };
61
+ if (args.timezone) {
62
+ body.subject.birth_location.timezone = { iana_name: args.timezone };
63
+ }
57
64
  if (args.house_system) {
58
65
  const mappedCode = HOUSE_SYSTEM_MAP[args.house_system] || "P";
59
66
  body.configuration = { house_system: mappedCode };
@@ -1,7 +1,7 @@
1
1
  import { registerTool, validateRequired } from "../index.js";
2
2
  import { backendClient } from "../../backend/client.js";
3
- function buildSubject(name, datetime, lat, lon) {
4
- return {
3
+ function buildSubject(name, datetime, lat, lon, timezone) {
4
+ const subj = {
5
5
  name,
6
6
  birth_datetime: { iso: datetime },
7
7
  birth_location: {
@@ -9,6 +9,10 @@ function buildSubject(name, datetime, lat, lon) {
9
9
  longitude: { decimal: lon },
10
10
  },
11
11
  };
12
+ if (timezone) {
13
+ subj.birth_location.timezone = { iana_name: timezone };
14
+ }
15
+ return subj;
12
16
  }
13
17
  // POST /comparative/composite
14
18
  registerTool({
@@ -23,11 +27,14 @@ registerTool({
23
27
  type: "object",
24
28
  properties: {
25
29
  person_a_datetime: { type: "string", description: "Person A birth datetime (ISO 8601)." },
30
+ person_a_timezone: { type: "string", description: "IANA timezone name for Person A, e.g. 'America/Denver'." },
26
31
  person_a_latitude: { type: "number", description: "Person A birth latitude." },
27
32
  person_a_longitude: { type: "number", description: "Person A birth longitude." },
28
33
  person_b_datetime: { type: "string", description: "Person B birth datetime (ISO 8601)." },
34
+ person_b_timezone: { type: "string", description: "IANA timezone name for Person B, e.g. 'Europe/London'." },
29
35
  person_b_latitude: { type: "number", description: "Person B birth latitude." },
30
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." },
31
38
  },
32
39
  required: [
33
40
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
@@ -40,11 +47,17 @@ registerTool({
40
47
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
41
48
  "person_b_datetime", "person_b_latitude", "person_b_longitude",
42
49
  ]);
43
- return await backendClient.post("/comparative/composite", {
44
- subjects: [
45
- buildSubject("Person A", args.person_a_datetime, args.person_a_latitude, args.person_a_longitude),
46
- buildSubject("Person B", args.person_b_datetime, args.person_b_latitude, args.person_b_longitude),
47
- ],
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
+ }
48
61
  });
49
62
  },
50
63
  });
@@ -58,11 +71,14 @@ registerTool({
58
71
  type: "object",
59
72
  properties: {
60
73
  person_a_datetime: { type: "string", description: "Person A birth datetime (ISO 8601)." },
74
+ person_a_timezone: { type: "string", description: "IANA timezone name for Person A, e.g. 'America/Denver'." },
61
75
  person_a_latitude: { type: "number", description: "Person A birth latitude." },
62
76
  person_a_longitude: { type: "number", description: "Person A birth longitude." },
63
77
  person_b_datetime: { type: "string", description: "Person B birth datetime (ISO 8601)." },
78
+ person_b_timezone: { type: "string", description: "IANA timezone name for Person B, e.g. 'Europe/London'." },
64
79
  person_b_latitude: { type: "number", description: "Person B birth latitude." },
65
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." },
66
82
  },
67
83
  required: [
68
84
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
@@ -75,11 +91,17 @@ registerTool({
75
91
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
76
92
  "person_b_datetime", "person_b_latitude", "person_b_longitude",
77
93
  ]);
78
- return await backendClient.post("/comparative/composite/midpoint", {
79
- subjects: [
80
- buildSubject("Person A", args.person_a_datetime, args.person_a_latitude, args.person_a_longitude),
81
- buildSubject("Person B", args.person_b_datetime, args.person_b_latitude, args.person_b_longitude),
82
- ],
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
+ }
83
105
  });
84
106
  },
85
107
  });
@@ -93,11 +115,14 @@ registerTool({
93
115
  type: "object",
94
116
  properties: {
95
117
  person_a_datetime: { type: "string", description: "Person A birth datetime (ISO 8601)." },
118
+ person_a_timezone: { type: "string", description: "IANA timezone name for Person A, e.g. 'America/Denver'." },
96
119
  person_a_latitude: { type: "number", description: "Person A birth latitude." },
97
120
  person_a_longitude: { type: "number", description: "Person A birth longitude." },
98
121
  person_b_datetime: { type: "string", description: "Person B birth datetime (ISO 8601)." },
122
+ person_b_timezone: { type: "string", description: "IANA timezone name for Person B, e.g. 'Europe/London'." },
99
123
  person_b_latitude: { type: "number", description: "Person B birth latitude." },
100
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." },
101
126
  },
102
127
  required: [
103
128
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
@@ -110,11 +135,17 @@ registerTool({
110
135
  "person_a_datetime", "person_a_latitude", "person_a_longitude",
111
136
  "person_b_datetime", "person_b_latitude", "person_b_longitude",
112
137
  ]);
113
- return await backendClient.post("/comparative/overlay", {
114
- subjects: [
115
- buildSubject("Person A", args.person_a_datetime, args.person_a_latitude, args.person_a_longitude),
116
- buildSubject("Person B", args.person_b_datetime, args.person_b_latitude, args.person_b_longitude),
117
- ],
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
+ }
118
149
  });
119
150
  },
120
151
  });
@@ -130,9 +161,12 @@ registerTool({
130
161
  type: "object",
131
162
  properties: {
132
163
  natal_datetime: { type: "string", description: "Natal birth datetime (ISO 8601)." },
164
+ natal_timezone: { type: "string", description: "IANA timezone name for Natal, e.g. 'America/Denver'." },
133
165
  natal_latitude: { type: "number", description: "Natal birth latitude." },
134
166
  natal_longitude: { type: "number", description: "Natal birth longitude." },
135
167
  transit_datetime: { type: "string", description: "Transit moment (ISO 8601). Defaults to now." },
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." },
136
170
  },
137
171
  required: ["natal_datetime", "natal_latitude", "natal_longitude"],
138
172
  additionalProperties: false,
@@ -140,14 +174,17 @@ registerTool({
140
174
  handler: async (args) => {
141
175
  validateRequired(args, ["natal_datetime", "natal_latitude", "natal_longitude"]);
142
176
  const subjects = [
143
- buildSubject("Natal", args.natal_datetime, args.natal_latitude, args.natal_longitude),
177
+ buildSubject("Natal", args.natal_datetime, args.natal_latitude, args.natal_longitude, args.natal_timezone),
144
178
  ];
145
- // The transit subject uses the transit datetime or current time
146
179
  if (args.transit_datetime) {
147
- subjects.push(buildSubject("Transit", args.transit_datetime, args.natal_latitude, args.natal_longitude));
180
+ subjects.push(buildSubject("Transit", args.transit_datetime, args.natal_latitude, args.natal_longitude, args.transit_timezone));
148
181
  }
149
- return await backendClient.post("/comparative/natal-transits", {
150
- 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 }
151
188
  });
152
189
  },
153
190
  });
@@ -6,7 +6,8 @@ registerTool({
6
6
  description: "Get the precise ecliptic longitude, latitude, distance, speed, and retrograde status " +
7
7
  "for a single planet/body at a given date and time. " +
8
8
  "Planet IDs: 0=Sun, 1=Moon, 2=Mercury, 3=Venus, 4=Mars, 5=Jupiter, 6=Saturn, " +
9
- "7=Uranus, 8=Neptune, 9=Pluto, 10=North Node, 11=South Node, 12=Lilith.\n\n" +
9
+ "7=Uranus, 8=Neptune, 9=Pluto, 10=North Node, 11=South Node, 12=Lilith, " +
10
+ "15=Chiron, 17=Ceres, 18=Pallas, 19=Juno, 20=Vesta.\n\n" +
10
11
  "CREDIT COST: 1 credit per call.\n\n" +
11
12
  "EXAMPLE: Where is Mars on 2026-03-20 at noon UTC?\n" +
12
13
  " planet_id=4, datetime='2026-03-20T12:00:00Z'",
@@ -15,11 +16,11 @@ registerTool({
15
16
  properties: {
16
17
  planet_id: {
17
18
  type: "integer",
18
- description: "Planet/body ID (0=Sun, 1=Moon, 2=Mercury, 3=Venus, 4=Mars, 5=Jupiter, 6=Saturn, 7=Uranus, 8=Neptune, 9=Pluto, 10=MeanNode, 11=TrueNode, 12=Lilith).",
19
+ description: "Planet/body ID (0=Sun, 1=Moon, 2=Mercury, 3=Venus, 4=Mars, 5=Jupiter, 6=Saturn, 7=Uranus, 8=Neptune, 9=Pluto, 10=MeanNode, 11=TrueNode, 12=Lilith, 15=Chiron, 17=Ceres, 18=Pallas, 19=Juno, 20=Vesta).",
19
20
  },
20
21
  datetime: {
21
22
  type: "string",
22
- description: "ISO 8601 date/time (e.g. '2026-03-20T12:00:00Z').",
23
+ description: "ISO 8601 date/time in UTC or with offset (e.g. '2026-03-20T12:00:00Z' or '2026-03-20T12:00:00-05:00').",
23
24
  },
24
25
  latitude: {
25
26
  type: "number",
@@ -60,7 +61,7 @@ registerTool({
60
61
  properties: {
61
62
  datetime: {
62
63
  type: "string",
63
- description: "ISO 8601 date/time.",
64
+ description: "ISO 8601 date/time in UTC or with offset (e.g. '2026-03-20T12:00:00Z').",
64
65
  },
65
66
  latitude: {
66
67
  type: "number",
@@ -12,8 +12,8 @@ registerTool({
12
12
  name: "ephemeris_natal_chart",
13
13
  description: "Calculate a full natal (birth) chart for a person. Returns planetary positions, house cusps, " +
14
14
  "aspects, and chart patterns. Use format='llm' for a compact, token-efficient output ideal for " +
15
- "interpretation (available on all tiers). The result includes all major planets, angles (ASC/MC/DSC/IC), " +
16
- "essential dignities, retrograde status, house system data, and major aspect grid.\n\n" +
15
+ "interpretation (available on all tiers). The result includes all major planets, Chiron and major asteroids like Ceres, angles (ASC/MC/DSC/IC), " +
16
+ "essential dignities, retrograde status, house system data, and major aspect grid. Asteroids are automatically included.\n\n" +
17
17
  "CREDIT COST: 1 credit per call.\n\n" +
18
18
  "EXAMPLE: Calculate the natal chart for someone born April 15, 1990 at 2:30 PM in Chicago:\n" +
19
19
  " datetime='1990-04-15T14:30:00', latitude=41.8781, longitude=-87.6298",
@@ -24,6 +24,10 @@ registerTool({
24
24
  type: "string",
25
25
  description: DATETIME_DESC,
26
26
  },
27
+ timezone: {
28
+ type: "string",
29
+ description: "IANA timezone name, e.g. 'America/Denver'. Use this if passing local time without a UTC offset in the datetime string.",
30
+ },
27
31
  latitude: {
28
32
  type: "number",
29
33
  description: "Geographic latitude of birth location in decimal degrees (positive = North).",
@@ -66,6 +70,9 @@ registerTool({
66
70
  },
67
71
  }
68
72
  };
73
+ if (args.timezone) {
74
+ body.subject.birth_location.timezone = { iana_name: args.timezone };
75
+ }
69
76
  if (args.house_system) {
70
77
  const code = HOUSE_SYSTEM_MAP[args.house_system] ?? args.house_system;
71
78
  body.configuration = { house_system: code };
@@ -13,11 +13,11 @@ registerTool({
13
13
  properties: {
14
14
  birth_datetime: {
15
15
  type: "string",
16
- description: "ISO 8601 birth date/time (e.g. '1985-06-21T14:00:00').",
16
+ description: "ISO 8601 birth date/time. MUST include offset or Z (e.g. '1985-06-21T14:00:00-05:00').",
17
17
  },
18
18
  target_datetime: {
19
19
  type: "string",
20
- description: "Date/time near which to find the Solar Return (ISO 8601). Required.",
20
+ description: "Date/time near which to find the Solar Return (ISO 8601). MUST include offset or Z. Required.",
21
21
  },
22
22
  birth_latitude: {
23
23
  type: "number",
@@ -73,11 +73,11 @@ registerTool({
73
73
  properties: {
74
74
  birth_datetime: {
75
75
  type: "string",
76
- description: "ISO 8601 birth date/time.",
76
+ description: "ISO 8601 birth date/time. MUST include offset or Z.",
77
77
  },
78
78
  target_datetime: {
79
79
  type: "string",
80
- description: "Date/time near which to find the Lunar Return (ISO 8601). Required.",
80
+ description: "Date/time near which to find the Lunar Return (ISO 8601). MUST include offset or Z. Required.",
81
81
  },
82
82
  birth_latitude: {
83
83
  type: "number",
@@ -123,11 +123,11 @@ registerTool({
123
123
  },
124
124
  birth_datetime: {
125
125
  type: "string",
126
- description: "ISO 8601 birth date/time.",
126
+ description: "ISO 8601 birth date/time. MUST include offset or Z.",
127
127
  },
128
128
  target_datetime: {
129
129
  type: "string",
130
- description: "Date/time near which to find the return (ISO 8601). Required.",
130
+ description: "Date/time near which to find the return (ISO 8601). MUST include offset or Z. Required.",
131
131
  },
132
132
  },
133
133
  required: ["body", "birth_datetime", "target_datetime"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openephemeris/mcp-server",
3
- "version": "3.3.1",
3
+ "version": "3.3.3",
4
4
  "description": "Model Context Protocol server for the Open Ephemeris astronomical computation API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",