@openephemeris/mcp-server 3.2.13 → 3.3.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.
@@ -1,5 +1,7 @@
1
1
  import { registerTool, validateRequired } from "../index.js";
2
2
  import { backendClient } from "../../backend/client.js";
3
+ // All electional endpoints are GET endpoints with query params.
4
+ // GET /electional/find-window — already existed, keeping it
3
5
  registerTool({
4
6
  name: "ephemeris_electional",
5
7
  description: "Find optimal planetary timing windows (electional astrology). Scans a date range to find the " +
@@ -78,3 +80,179 @@ registerTool({
78
80
  });
79
81
  },
80
82
  });
83
+ // GET /electional/moment-analysis
84
+ registerTool({
85
+ name: "electional_moment_analysis",
86
+ description: "Analyze the astrological quality of a specific moment: planet positions, aspects, " +
87
+ "void of course status, lunar phase, day ruler, and an overall electional score (0-100). " +
88
+ "Perfect for evaluating whether 'right now' or a specific date/time is good for action.\n\n" +
89
+ "CREDIT COST: 2 credits per call.\n\n" +
90
+ "EXAMPLE: Analyze March 21, 2026 at noon:\n" +
91
+ " date='2026-03-21T12:00:00'",
92
+ inputSchema: {
93
+ type: "object",
94
+ properties: {
95
+ date: {
96
+ type: "string",
97
+ description: "ISO 8601 datetime to analyze (e.g., '2026-03-21T12:00:00'). Defaults to now.",
98
+ },
99
+ format: {
100
+ type: "string",
101
+ enum: ["json", "llm"],
102
+ description: "Output format.",
103
+ },
104
+ },
105
+ required: [],
106
+ additionalProperties: false,
107
+ },
108
+ handler: async (args) => {
109
+ const query = {};
110
+ if (args.date)
111
+ query.date = args.date;
112
+ if (args.format)
113
+ query.format = args.format;
114
+ return await backendClient.request("GET", "/electional/moment-analysis", {
115
+ data: {},
116
+ params: query,
117
+ });
118
+ },
119
+ });
120
+ // GET /electional/station-tracker
121
+ registerTool({
122
+ name: "electional_station_tracker",
123
+ description: "Find all upcoming retrograde and direct stations for planets in a date range. " +
124
+ "Returns exact station times, longitudes, and signs. Essential for timing around " +
125
+ "Mercury retrograde, Venus retrograde, etc.\n\n" +
126
+ "CREDIT COST: 3 credits per call.\n\n" +
127
+ "EXAMPLE: Stations in the next 3 months:\n" +
128
+ " start_date='2026-03-01', end_date='2026-06-01'",
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {
132
+ start_date: {
133
+ type: "string",
134
+ description: "Start date (ISO 8601). Defaults to now.",
135
+ },
136
+ end_date: {
137
+ type: "string",
138
+ description: "End date (ISO 8601). Defaults to +90 days.",
139
+ },
140
+ planets: {
141
+ type: "string",
142
+ description: "Comma-separated planet IDs (e.g., '2,3,4'). Defaults to Mercury-Saturn.",
143
+ },
144
+ format: {
145
+ type: "string",
146
+ enum: ["json", "llm"],
147
+ description: "Output format.",
148
+ },
149
+ },
150
+ required: [],
151
+ additionalProperties: false,
152
+ },
153
+ handler: async (args) => {
154
+ const query = {};
155
+ if (args.start_date)
156
+ query.start_date = args.start_date;
157
+ if (args.end_date)
158
+ query.end_date = args.end_date;
159
+ if (args.planets)
160
+ query.planets = args.planets;
161
+ if (args.format)
162
+ query.format = args.format;
163
+ return await backendClient.request("GET", "/electional/station-tracker", {
164
+ data: {},
165
+ params: query,
166
+ });
167
+ },
168
+ });
169
+ // GET /electional/ingress-calendar
170
+ registerTool({
171
+ name: "electional_ingress_calendar",
172
+ description: "Get a calendar of sign ingresses — when planets change zodiac signs. " +
173
+ "Returns each ingress event with exact time, from/to signs, and retrograde flag.\n\n" +
174
+ "CREDIT COST: 3 credits per call.\n\n" +
175
+ "EXAMPLE: Ingresses in March 2026:\n" +
176
+ " start_date='2026-03-01', end_date='2026-03-31'",
177
+ inputSchema: {
178
+ type: "object",
179
+ properties: {
180
+ start_date: {
181
+ type: "string",
182
+ description: "Start date (ISO 8601). Defaults to now.",
183
+ },
184
+ end_date: {
185
+ type: "string",
186
+ description: "End date (ISO 8601). Defaults to +30 days.",
187
+ },
188
+ planets: {
189
+ type: "string",
190
+ description: "Comma-separated planet IDs. Defaults to all major planets.",
191
+ },
192
+ format: {
193
+ type: "string",
194
+ enum: ["json", "llm"],
195
+ description: "Output format.",
196
+ },
197
+ },
198
+ required: [],
199
+ additionalProperties: false,
200
+ },
201
+ handler: async (args) => {
202
+ const query = {};
203
+ if (args.start_date)
204
+ query.start_date = args.start_date;
205
+ if (args.end_date)
206
+ query.end_date = args.end_date;
207
+ if (args.planets)
208
+ query.planets = args.planets;
209
+ if (args.format)
210
+ query.format = args.format;
211
+ return await backendClient.request("GET", "/electional/ingress-calendar", {
212
+ data: {},
213
+ params: query,
214
+ });
215
+ },
216
+ });
217
+ // GET /electional/aspect-search
218
+ registerTool({
219
+ name: "electional_aspect_search",
220
+ description: "Find all active aspects between planets at a specific moment. Returns aspect type, " +
221
+ "orb, quality score, and whether it's applying or separating. Great for checking " +
222
+ "the 'weather' of a given day.\n\n" +
223
+ "CREDIT COST: 2 credits per call.\n\n" +
224
+ "EXAMPLE: What aspects are active on March 21, 2026?\n" +
225
+ " date='2026-03-21T12:00:00'",
226
+ inputSchema: {
227
+ type: "object",
228
+ properties: {
229
+ date: {
230
+ type: "string",
231
+ description: "ISO 8601 datetime to check. Defaults to now.",
232
+ },
233
+ max_orb: {
234
+ type: "number",
235
+ description: "Maximum orb in degrees. Default uses standard orbs per aspect type.",
236
+ },
237
+ aspects: {
238
+ type: "string",
239
+ description: "Comma-separated aspect types to filter (e.g., 'conjunction,trine,square'). Default all.",
240
+ },
241
+ },
242
+ required: [],
243
+ additionalProperties: false,
244
+ },
245
+ handler: async (args) => {
246
+ const query = {};
247
+ if (args.date)
248
+ query.date = args.date;
249
+ if (args.max_orb != null)
250
+ query.max_orb = args.max_orb;
251
+ if (args.aspects)
252
+ query.aspects = args.aspects;
253
+ return await backendClient.request("GET", "/electional/aspect-search", {
254
+ data: {},
255
+ params: query,
256
+ });
257
+ },
258
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,93 @@
1
+ import { registerTool, validateRequired } from "../index.js";
2
+ import { backendClient } from "../../backend/client.js";
3
+ // POST /ephemeris/planet-position — OE-016
4
+ registerTool({
5
+ name: "ephemeris_planet_position",
6
+ description: "Get the precise ecliptic longitude, latitude, distance, speed, and retrograde status " +
7
+ "for a single planet/body at a given date and time. " +
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" +
10
+ "CREDIT COST: 1 credit per call.\n\n" +
11
+ "EXAMPLE: Where is Mars on 2026-03-20 at noon UTC?\n" +
12
+ " planet_id=4, datetime='2026-03-20T12:00:00Z'",
13
+ inputSchema: {
14
+ type: "object",
15
+ properties: {
16
+ planet_id: {
17
+ 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
+ },
20
+ datetime: {
21
+ type: "string",
22
+ description: "ISO 8601 date/time (e.g. '2026-03-20T12:00:00Z').",
23
+ },
24
+ latitude: {
25
+ type: "number",
26
+ description: "Observer latitude for topocentric position (optional). Omit for geocentric.",
27
+ },
28
+ longitude: {
29
+ type: "number",
30
+ description: "Observer longitude for topocentric position (optional). Omit for geocentric.",
31
+ },
32
+ },
33
+ required: ["planet_id", "datetime"],
34
+ additionalProperties: false,
35
+ },
36
+ handler: async (args) => {
37
+ validateRequired(args, ["planet_id", "datetime"]);
38
+ const body = {
39
+ planet_id: args.planet_id,
40
+ date_time: { iso: args.datetime },
41
+ };
42
+ if (args.latitude != null)
43
+ body.latitude = args.latitude;
44
+ if (args.longitude != null)
45
+ body.longitude = args.longitude;
46
+ return await backendClient.post("/ephemeris/planet-position", body);
47
+ },
48
+ });
49
+ // POST /ephemeris/house-cusps — OE-017
50
+ registerTool({
51
+ name: "ephemeris_house_cusps",
52
+ description: "Calculate house cusps and angles (ASC, MC, DSC, IC) for a given date, time, and location " +
53
+ "using one or more house systems.\n\n" +
54
+ "House system codes: P=Placidus, K=Koch, O=Porphyry, R=Regiomontanus, C=Campanus, E=Equal, W=Whole Sign.\n\n" +
55
+ "CREDIT COST: 1 credit per call.\n\n" +
56
+ "EXAMPLE: Placidus houses for London at 2026-03-20 noon UTC:\n" +
57
+ " datetime='2026-03-20T12:00:00Z', latitude=51.5074, longitude=-0.1278, house_systems=['P']",
58
+ inputSchema: {
59
+ type: "object",
60
+ properties: {
61
+ datetime: {
62
+ type: "string",
63
+ description: "ISO 8601 date/time.",
64
+ },
65
+ latitude: {
66
+ type: "number",
67
+ description: "Observer latitude in decimal degrees.",
68
+ },
69
+ longitude: {
70
+ type: "number",
71
+ description: "Observer longitude in decimal degrees.",
72
+ },
73
+ house_systems: {
74
+ type: "array",
75
+ items: { type: "string" },
76
+ description: "List of house system codes. E.g. ['P', 'W']. Defaults to ['P'] (Placidus).",
77
+ },
78
+ },
79
+ required: ["datetime", "latitude", "longitude"],
80
+ additionalProperties: false,
81
+ },
82
+ handler: async (args) => {
83
+ validateRequired(args, ["datetime", "latitude", "longitude"]);
84
+ const body = {
85
+ date_time: { iso: args.datetime },
86
+ latitude: args.latitude,
87
+ longitude: args.longitude,
88
+ };
89
+ if (args.house_systems)
90
+ body.house_systems = args.house_systems;
91
+ return await backendClient.post("/ephemeris/house-cusps", body);
92
+ },
93
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -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 @@
1
+ export {};
@@ -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
+ });
@@ -0,0 +1 @@
1
+ export {};