@openephemeris/mcp-server 3.2.3 → 3.2.4

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/README.md CHANGED
@@ -179,8 +179,8 @@ Generated by `npm run sync:readme` from `config/dev-allowlist.json` and the live
179
179
 
180
180
  - Allowlisted operations: **97**
181
181
  - Methods: `GET=41`, `POST=56`, `PUT=0`, `PATCH=0`, `DELETE=0`
182
- - Registered tools (`OPENEPHEMERIS_PROFILE=dev`): **13**
183
- - Typed tools: `auth_login`, `auth_logout`, `auth_status`, `dev_call`, `dev_list_allowed`, `ephemeris_electional`, `ephemeris_moon_phase`, `ephemeris_natal_chart`, `ephemeris_next_eclipse`, `ephemeris_relocation`, `ephemeris_synastry`, `ephemeris_transits`, `human_design_chart`
182
+ - Registered tools (`OPENEPHEMERIS_PROFILE=dev`): **15**
183
+ - Typed tools: `auth_login`, `auth_logout`, `auth_status`, `chinese_bazi`, `dev_call`, `dev_list_allowed`, `ephemeris_electional`, `ephemeris_moon_phase`, `ephemeris_natal_chart`, `ephemeris_next_eclipse`, `ephemeris_relocation`, `ephemeris_synastry`, `ephemeris_transits`, `human_design_chart`, `vedic_chart`
184
184
  - Generic tools:
185
185
 
186
186
  ### Allowlist Families
@@ -29,6 +29,8 @@ export async function initTools(profile) {
29
29
  await import("./specialized/synastry.js");
30
30
  await import("./specialized/relocation.js");
31
31
  await import("./specialized/electional.js");
32
+ await import("./specialized/vedic.js");
33
+ await import("./specialized/bazi.js");
32
34
  }
33
35
  }
34
36
  /**
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,67 @@
1
+ import { registerTool } from "../index.js";
2
+ import { backendClient } from "../../backend/client.js";
3
+ registerTool({
4
+ name: "chinese_bazi",
5
+ description: "Calculate a Chinese Ba Zi (Four Pillars of Destiny) chart. Returns the Year, Month, Day, " +
6
+ "and Hour pillars, each containing a Heavenly Stem and Earthly Branch pair. Also includes " +
7
+ "the Day Master element, Wu Xing element breakdown, and basic interpretation context.\n\n" +
8
+ "CREDIT COST: 1 credit per call.\n\n" +
9
+ "You can provide either a datetime string (which will be decomposed automatically) " +
10
+ "or explicit year/month/day/hour values.\n\n" +
11
+ "EXAMPLE: Ba Zi for someone born July 15, 1987 at 2:00 PM:\n" +
12
+ " year=1987, month=7, day=15, hour=14",
13
+ inputSchema: {
14
+ type: "object",
15
+ properties: {
16
+ year: {
17
+ type: "integer",
18
+ description: "Gregorian birth year, e.g. 1987.",
19
+ },
20
+ month: {
21
+ type: "integer",
22
+ description: "Birth month (1-12).",
23
+ },
24
+ day: {
25
+ type: "integer",
26
+ description: "Birth day of month (1-31).",
27
+ },
28
+ hour: {
29
+ type: "integer",
30
+ description: "Birth hour (0-23). Optional, defaults to 12 (noon). " +
31
+ "Chinese hours (shí) are 2-hour blocks, so precision within a 2-hour window is sufficient.",
32
+ },
33
+ datetime: {
34
+ type: "string",
35
+ description: "Alternative: ISO 8601 datetime (e.g. '1987-07-15T14:00:00'). " +
36
+ "If provided, year/month/day/hour are extracted automatically. " +
37
+ "Use this OR the individual year/month/day fields, not both.",
38
+ },
39
+ },
40
+ additionalProperties: false,
41
+ },
42
+ handler: async (args) => {
43
+ let year = args.year;
44
+ let month = args.month;
45
+ let day = args.day;
46
+ let hour = args.hour;
47
+ // If datetime provided, extract components
48
+ if (args.datetime && (!year || !month || !day)) {
49
+ const dt = new Date(args.datetime);
50
+ if (!isNaN(dt.getTime())) {
51
+ year = dt.getUTCFullYear();
52
+ month = dt.getUTCMonth() + 1;
53
+ day = dt.getUTCDate();
54
+ if (hour == null)
55
+ hour = dt.getUTCHours();
56
+ }
57
+ }
58
+ if (!year || !month || !day) {
59
+ throw new Error("Either provide year/month/day fields, or a datetime string. " +
60
+ "Example: year=1987, month=7, day=15 OR datetime='1987-07-15T14:00:00'");
61
+ }
62
+ const body = { year, month, day };
63
+ if (hour != null)
64
+ body.hour = hour;
65
+ return await backendClient.request("POST", "/chinese/bazi", { data: body });
66
+ },
67
+ });
@@ -2,52 +2,49 @@ import { registerTool, validateRequired, validateCoordinates } from "../index.js
2
2
  import { backendClient } from "../../backend/client.js";
3
3
  registerTool({
4
4
  name: "ephemeris_next_eclipse",
5
- description: "Find the next solar or lunar eclipse visible from a given location (or globally). " +
5
+ description: "Find the next solar or lunar eclipse visible from a given location. " +
6
6
  "Returns the eclipse type, date/time of maximum, magnitude, duration of totality (if any), " +
7
- "and local contact times if coordinates are provided.\n\n" +
7
+ "and local contact times.\n\n" +
8
8
  "CREDIT COST: 1 credit per call.\n\n" +
9
9
  "EXAMPLE: Find the next solar eclipse visible from New York:\n" +
10
10
  " eclipse_type='solar', latitude=40.7128, longitude=-74.006\n\n" +
11
- "EXAMPLE: Find the next lunar eclipse globally:\n" +
12
- " eclipse_type='lunar'",
11
+ "EXAMPLE: Find the next lunar eclipse from London:\n" +
12
+ " eclipse_type='lunar', latitude=51.5074, longitude=-0.1278",
13
13
  inputSchema: {
14
14
  type: "object",
15
15
  properties: {
16
16
  eclipse_type: {
17
17
  type: "string",
18
- enum: ["solar", "lunar"],
19
- description: "Eclipse type to search for.",
18
+ enum: ["solar", "lunar", "any"],
19
+ description: "Eclipse type to search for. Use 'any' for whichever comes first.",
20
20
  },
21
21
  latitude: {
22
22
  type: "number",
23
- description: "Observer latitude in decimal degrees. If provided, returns local visibility and contact times.",
23
+ description: "Observer latitude in decimal degrees (required). Returns local visibility and contact times.",
24
24
  },
25
25
  longitude: {
26
26
  type: "number",
27
- description: "Observer longitude in decimal degrees.",
27
+ description: "Observer longitude in decimal degrees (required).",
28
28
  },
29
29
  after_date: {
30
30
  type: "string",
31
31
  description: "ISO 8601 date to search after (e.g. '2026-01-01'). Defaults to today if omitted.",
32
32
  },
33
33
  },
34
- required: ["eclipse_type"],
34
+ required: ["eclipse_type", "latitude", "longitude"],
35
35
  additionalProperties: false,
36
36
  },
37
37
  handler: async (args) => {
38
- validateRequired(args, ["eclipse_type"]);
38
+ validateRequired(args, ["eclipse_type", "latitude", "longitude"]);
39
39
  validateCoordinates(args, "latitude", "longitude");
40
- const params = {};
41
- if (args.latitude != null)
42
- params.lat = args.latitude;
43
- if (args.longitude != null)
44
- params.lon = args.longitude;
40
+ const params = {
41
+ lat: args.latitude,
42
+ lon: args.longitude,
43
+ };
45
44
  if (args.after_date)
46
45
  params.date = args.after_date;
47
- if (args.eclipse_type)
46
+ if (args.eclipse_type && args.eclipse_type !== "any")
48
47
  params.type = args.eclipse_type;
49
- // Use /eclipse/next-visible which is the unified endpoint
50
- // that accepts lat/lon and type filters
51
48
  return await backendClient.request("GET", "/eclipse/next-visible", { params, timeoutMs: 60_000 });
52
49
  },
53
50
  });
@@ -1,5 +1,17 @@
1
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
16
  name: "human_design_chart",
5
17
  description: "Calculate a full Human Design bodygraph chart from birth data. Returns the person's Type " +
@@ -9,14 +21,15 @@ registerTool({
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",
@@ -33,16 +46,18 @@ registerTool({
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) => {
40
- validateRequired(args, ["datetime", "latitude", "longitude"]);
53
+ validateRequired(args, ["datetime"]);
41
54
  const body = {
42
- birth_datetime_utc: args.datetime,
43
- latitude: args.latitude,
44
- longitude: args.longitude,
55
+ birth_datetime_utc: ensureTimezone(args.datetime),
45
56
  };
57
+ if (args.latitude != null)
58
+ body.latitude = args.latitude;
59
+ if (args.longitude != null)
60
+ body.longitude = args.longitude;
46
61
  const query = {};
47
62
  if (args.format)
48
63
  query.format = args.format;
@@ -2,6 +2,12 @@ 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
12
  name: "ephemeris_natal_chart",
7
13
  description: "Calculate a full natal (birth) chart for a person. Returns planetary positions, house cusps, " +
@@ -61,7 +67,8 @@ registerTool({
61
67
  }
62
68
  };
63
69
  if (args.house_system) {
64
- body.configuration = { house_system: args.house_system };
70
+ const code = HOUSE_SYSTEM_MAP[args.house_system] ?? args.house_system;
71
+ body.configuration = { house_system: code };
65
72
  }
66
73
  if (args.include_arabic_parts || args.include_fixed_stars) {
67
74
  body.options = {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,48 @@
1
+ import { registerTool, validateRequired, validateCoordinates } from "../index.js";
2
+ import { backendClient } from "../../backend/client.js";
3
+ registerTool({
4
+ name: "vedic_chart",
5
+ description: "Calculate a Vedic (Jyotish) natal chart with sidereal positions. Returns planet placements " +
6
+ "in rashis (sidereal signs), nakshatras with pada, navamsa placements, and bhavas (houses). " +
7
+ "Uses Whole Sign houses, sidereal zodiac with configurable ayanamsa.\n\n" +
8
+ "CREDIT COST: 1 credit per call.\n\n" +
9
+ "SUPPORTED AYANAMSA: lahiri (default), fagan_bradley, krishnamurti, raman, yukteshwar\n\n" +
10
+ "EXAMPLE: Vedic chart for someone born Jan 15, 1990 at 8:30 AM UTC in Mumbai:\n" +
11
+ " datetime='1990-01-15T08:30:00Z', latitude=19.076, longitude=72.8777",
12
+ inputSchema: {
13
+ type: "object",
14
+ properties: {
15
+ datetime: {
16
+ type: "string",
17
+ description: "ISO 8601 birth datetime in UTC, e.g. '1990-01-15T08:30:00Z'. Include 'Z' or timezone offset.",
18
+ },
19
+ latitude: {
20
+ type: "number",
21
+ description: "Birth latitude in decimal degrees (north positive).",
22
+ },
23
+ longitude: {
24
+ type: "number",
25
+ description: "Birth longitude in decimal degrees (east positive).",
26
+ },
27
+ ayanamsa: {
28
+ type: "string",
29
+ enum: ["lahiri", "fagan_bradley", "krishnamurti", "raman", "yukteshwar"],
30
+ description: "Ayanamsa system for sidereal conversion. Defaults to 'lahiri'.",
31
+ },
32
+ },
33
+ required: ["datetime", "latitude", "longitude"],
34
+ additionalProperties: false,
35
+ },
36
+ handler: async (args) => {
37
+ validateRequired(args, ["datetime", "latitude", "longitude"]);
38
+ validateCoordinates(args, "latitude", "longitude");
39
+ const body = {
40
+ datetime_utc: args.datetime,
41
+ latitude: args.latitude,
42
+ longitude: args.longitude,
43
+ };
44
+ if (args.ayanamsa)
45
+ body.ayanamsa = args.ayanamsa;
46
+ return await backendClient.request("POST", "/vedic/chart", { data: body });
47
+ },
48
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openephemeris/mcp-server",
3
- "version": "3.2.3",
3
+ "version": "3.2.4",
4
4
  "description": "Model Context Protocol server for the Open Ephemeris astronomical computation API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",