UNPKG

@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
#!/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