@xzkcz/iztro-mcp-server
Version:
MCP server exposing iztro astrology methods for Zi Wei Dou Shu astrolabe generation and horoscope analysis
275 lines • 11.9 kB
JavaScript
#!/usr/bin/env node
import { FastMCP } from 'fastmcp';
import { astro } from 'iztro';
import { z } from 'zod';
import { getAstroYear } from './getAstroYear.js';
import { getAstroMonth } from './getAstroMonth.js';
import { genAndSaveAstrolabe } from './genAndSaveAstrolabe.js';
import versionJson from './version.json' with { type: 'json' };
import { hourToTimeIndex } from './utils/hourToTimeIndex.js';
import { getMutagedPlaces } from './getMutagedPlaces.js';
const VERSION = versionJson.version;
// Create the FastMCP server instance
const server = new FastMCP({
name: 'iztro-astrology-server',
version: VERSION
});
// Tool: Generate astrolabe by solar date
server.addTool({
name: 'get_astrolabe',
description: 'Get an astrolabe using solar calendar date for Zi Wei Dou Shu astrology analysis',
parameters: z.object({
date: z.string().describe('Birth date in YYYY-M-D format (e.g., "2000-8-16")'),
hour: z.number().min(0).max(23).describe('Birth hour in 24-hour format (0-23)'),
gender: z.enum(['male', 'female']).describe('Gender of the person'),
fixLeap: z.boolean().optional().default(true).describe('Whether to fix the leap month'),
locale: z.enum(['zh-CN', 'en-US']).optional().default('zh-CN').describe('Locale for the output ("zh-CN" or "en-US")')
}),
execute: async (args) => {
try {
// Convert hour to timeIndex for iztro library
const timeIndex = hourToTimeIndex(args.hour);
// Generate astrolabe using iztro's bySolar method
const astrolabe = astro.bySolar(args.date, timeIndex, args.gender, args.fixLeap, args.locale);
return JSON.stringify({
success: true,
data: {
astrolabe: astrolabe,
metadata: {
birthDate: args.date,
birthHour: args.hour,
timeIndex: timeIndex,
gender: args.gender,
fixLeap: args.fixLeap,
locale: args.locale,
generatedAt: new Date().toISOString()
}
},
message: 'Astrolabe generated successfully'
}, null, 2);
}
catch (error) {
return JSON.stringify({
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
message: 'Failed to generate astrolabe'
}, null, 2);
}
}
});
// Tool 1: Get horoscope decades
server.addTool({
name: 'get_horoscope_decades',
description: 'Get all decadal data for whole life',
parameters: z.object({
date: z.string().describe('Birth date in YYYY-M-D format (e.g., "2000-8-16")'),
hour: z.number().min(0).max(23).describe('Birth hour in 24-hour format (0-23)'),
gender: z.enum(['male', 'female']).describe('Gender of the person'),
fixLeap: z.boolean().optional().default(true).describe('Whether to fix the leap month'),
locale: z.enum(['zh-CN', 'en-US']).optional().default('zh-CN').describe('Locale for the output ("zh-CN" or "en-US")')
}),
execute: async (args) => {
try {
const timeIndex = hourToTimeIndex(args.hour);
const result = getAstroYear({
solarBirthDate: args.date,
timeIndex,
gender: args.gender,
fixLeap: args.fixLeap,
locale: args.locale
});
return JSON.stringify(result.decadePeriods, null, 2);
}
catch (error) {
return JSON.stringify({
error: error instanceof Error ? error.message : 'Unknown error occurred',
message: 'Failed to retrieve decadal periods'
}, null, 2);
}
}
});
// Tool 2: Get horoscope ages
server.addTool({
name: 'get_horoscope_ages',
description: 'Get all age data for whole life',
parameters: z.object({
date: z.string().describe('Birth date in YYYY-M-D format (e.g., "2000-8-16")'),
hour: z.number().min(0).max(23).describe('Birth hour in 24-hour format (0-23)'),
gender: z.enum(['male', 'female']).describe('Gender of the person'),
fixLeap: z.boolean().optional().default(true).describe('Whether to fix the leap month'),
locale: z.enum(['zh-CN', 'en-US']).optional().default('zh-CN').describe('Locale for the output ("zh-CN" or "en-US")')
}),
execute: async (args) => {
try {
const timeIndex = hourToTimeIndex(args.hour);
const result = getAstroYear({
solarBirthDate: args.date,
timeIndex,
gender: args.gender,
fixLeap: args.fixLeap,
locale: args.locale
});
return JSON.stringify(result.agePeriods, null, 2);
}
catch (error) {
return JSON.stringify({
error: error instanceof Error ? error.message : 'Unknown error occurred',
message: 'Failed to retrieve age periods'
}, null, 2);
}
}
});
// Tool 3: Get horoscope years
server.addTool({
name: 'get_horoscope_years',
description: 'Get all yearly data for whole life',
parameters: z.object({
date: z.string().describe('Birth date in YYYY-M-D format (e.g., "2000-8-16")'),
hour: z.number().min(0).max(23).describe('Birth hour in 24-hour format (0-23)'),
gender: z.enum(['male', 'female']).describe('Gender of the person'),
fixLeap: z.boolean().optional().default(true).describe('Whether to fix the leap month'),
locale: z.enum(['zh-CN', 'en-US']).optional().default('zh-CN').describe('Locale for the output ("zh-CN" or "en-US")')
}),
execute: async (args) => {
try {
const timeIndex = hourToTimeIndex(args.hour);
const result = getAstroYear({
solarBirthDate: args.date,
timeIndex,
gender: args.gender,
fixLeap: args.fixLeap,
locale: args.locale
});
return JSON.stringify(result.yearPeriods, null, 2);
}
catch (error) {
return JSON.stringify({
error: error instanceof Error ? error.message : 'Unknown error occurred',
message: 'Failed to retrieve year periods'
}, null, 2);
}
}
});
// Tool 4: Get horoscope months
server.addTool({
name: 'get_horoscope_months',
description: 'Get monthly data for one year',
parameters: z.object({
year: z.number().min(1900).max(2100).describe('Year to get monthly data for (e.g., 2024)'),
fixLeap: z.boolean().optional().default(true).describe('Whether to fix the leap month'),
locale: z.enum(['zh-CN', 'en-US']).optional().default('zh-CN').describe('Locale for the output ("zh-CN" or "en-US")')
}),
execute: async (args) => {
try {
const result = getAstroMonth({
year: args.year,
fixLeap: args.fixLeap,
locale: args.locale
});
return JSON.stringify(result, null, 2);
}
catch (error) {
return JSON.stringify({
error: error instanceof Error ? error.message : 'Unknown error occurred',
message: 'Failed to retrieve monthly periods'
}, null, 2);
}
}
});
// Tool: Get mutaged places for a specific palace
server.addTool({
name: 'get_mutaged_places',
description: 'Get the 4 palaces that are affected by the four transformations (禄权科忌) from a specific palace',
parameters: z.object({
date: z.string().describe('Birth date in YYYY-M-D format (e.g., "2000-8-16")'),
hour: z.number().min(0).max(23).describe('Birth hour in 24-hour format (0-23)'),
gender: z.enum(['male', 'female']).describe('Gender of the person'),
palaceName: z.union([
z.number().min(0).max(11).describe('Palace index (0-11)'),
z.enum([
'命宫', '身宫', '兄弟', '夫妻', '子女', '财帛', '疾厄', '迁移', '仆役', '官禄', '田宅', '福德', '父母', '来因',
'soul', 'body', 'siblings', 'spouse', 'children', 'wealth', 'health', 'surface', 'friends', 'career', 'property', 'spirit', 'parents', 'origin'
]).describe('Palace name (Chinese or English)')
]).describe('Palace to analyze - can be either palace index (0-11) or palace name (Chinese: "命宫", "财帛", "官禄" or English: "soul", "wealth", "career")'),
fixLeap: z.boolean().optional().default(true).describe('Whether to fix the leap month'),
locale: z.enum(['zh-CN', 'en-US']).optional().default('zh-CN').describe('Locale for the output ("zh-CN" or "en-US")')
}),
execute: async (args) => {
try {
return JSON.stringify({
success: true,
data: {
sourcePalace: args.palaceName,
mutagedPlaces: getMutagedPlaces(args),
metadata: {
birthDate: args.date,
birthHour: args.hour,
gender: args.gender,
fixLeap: args.fixLeap,
locale: args.locale,
generatedAt: new Date().toISOString()
}
},
message: `Mutaged places for ${args.palaceName} retrieved successfully`
}, null, 2);
}
catch (error) {
return JSON.stringify({
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
message: 'Failed to get mutaged places'
}, null, 2);
}
}
});
// Tool: Generate astrolabe, decades, years, ages in separate files
server.addTool({
name: 'gen_astrolabe',
description: 'Generate an astrolabe, decades, years, ages in specific path',
parameters: z.object({
date: z.string().describe('Birth date in YYYY-M-D format (e.g., "2000-8-16")'),
hour: z.number().min(0).max(23).describe('Birth hour in 24-hour format (0-23)'),
gender: z.enum(['male', 'female']).describe('Gender of the person'),
path: z.string().describe('File path'),
fixLeap: z.boolean().optional().default(true).describe('Whether to fix the leap month'),
locale: z.enum(['zh-CN', 'en-US']).optional().default('zh-CN').describe('Locale for the output ("zh-CN" or "en-US")')
}),
execute: async (args) => {
try {
const result = genAndSaveAstrolabe(args);
return JSON.stringify(result, null, 2);
}
catch (error) {
return JSON.stringify({
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
message: 'Failed to generate astrolabe files'
}, null, 2);
}
}
});
// Start the server
async function main() {
try {
await server.start({
transportType: 'stdio'
});
console.error(`🌟iztro MCP Server @${VERSION} started successfully!`);
}
catch (error) {
console.error('Failed to start server:', error);
process.exit(1);
}
}
// Handle graceful shutdown
process.on('SIGINT', async () => {
console.error('\n🛑 Shutting down iztro MCP Server...');
process.exit(0);
});
process.on('SIGTERM', async () => {
console.error('\n🛑 Shutting down iztro MCP Server...');
process.exit(0);
});
// Start the server
main().catch(console.error);
//# sourceMappingURL=index.js.map