Spaces:
Sleeping
Sleeping
| """ | |
| Astro.com (AstroDienst) MCP Integration | |
| Fetches classical Hellenistic charts from astro.com | |
| """ | |
| import requests | |
| from typing import Dict, List, Optional | |
| import time | |
| from urllib.parse import urlencode | |
| from bs4 import BeautifulSoup | |
| import json | |
| import re | |
| class AstroDienstMCP: | |
| """MCP Server for Astro.com integration""" | |
| BASE_URL = "https://www.astro.com" | |
| RATE_LIMIT = 2.0 # seconds between requests (be respectful) | |
| def __init__(self): | |
| self.last_request_time = 0 | |
| self.session = requests.Session() | |
| self.session.headers.update({ | |
| 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' | |
| }) | |
| def _rate_limit(self): | |
| """Enforce rate limiting""" | |
| elapsed = time.time() - self.last_request_time | |
| if elapsed < self.RATE_LIMIT: | |
| time.sleep(self.RATE_LIMIT - elapsed) | |
| self.last_request_time = time.time() | |
| def generate_chart( | |
| self, | |
| birth_date: str, # Format: "DD.MM.YYYY" | |
| birth_time: str, # Format: "HH:MM" | |
| city: str, | |
| country: str = "", | |
| house_system: str = "W" # W = Whole Sign, P = Placidus, K = Koch, etc. | |
| ) -> Dict: | |
| """ | |
| Generate classical chart from Astro.com | |
| Args: | |
| birth_date: Birth date in DD.MM.YYYY format | |
| birth_time: Birth time in HH:MM format | |
| city: City name | |
| country: Country name (optional) | |
| house_system: House system (W=Whole Sign, P=Placidus, K=Koch, E=Equal) | |
| Returns: | |
| Chart data including planet positions, houses, aspects | |
| """ | |
| self._rate_limit() | |
| # Astro.com guest chart URL | |
| # Note: This is a simplified version - actual implementation would need | |
| # to handle astro.com's location database and chart generation properly | |
| return { | |
| "service": "Astro.com Chart Generator", | |
| "birth_date": birth_date, | |
| "birth_time": birth_time, | |
| "location": f"{city}, {country}" if country else city, | |
| "house_system": self._get_house_system_name(house_system), | |
| "chart_url": self._build_chart_url(birth_date, birth_time, city, house_system), | |
| "note": "For full chart data, please visit astro.com directly. This integration provides chart URL generation.", | |
| "instructions": [ | |
| "1. Visit the generated chart URL", | |
| "2. Chart will be displayed with classical aspects", | |
| "3. Use Whole Sign houses (W) for Hellenistic astrology", | |
| "4. Planet positions shown in traditional format" | |
| ], | |
| "hellenistic_features": { | |
| "whole_sign_houses": house_system == "W", | |
| "traditional_aspects": True, | |
| "sect_aware": "Manual calculation needed", | |
| "essential_dignities": "Available in extended chart" | |
| }, | |
| "source": "AstroDienst MCP Integration" | |
| } | |
| def _get_house_system_name(self, code: str) -> str: | |
| """Convert house system code to name""" | |
| systems = { | |
| "W": "Whole Sign (Hellenistic)", | |
| "P": "Placidus", | |
| "K": "Koch", | |
| "E": "Equal House", | |
| "R": "Regiomontanus", | |
| "C": "Campanus", | |
| "B": "Alcabitius" | |
| } | |
| return systems.get(code, "Unknown") | |
| def _build_chart_url(self, birth_date: str, birth_time: str, city: str, house_system: str) -> str: | |
| """ | |
| Build astro.com chart URL | |
| Note: This is a simplified version. Astro.com requires proper location codes | |
| and has a complex URL structure. For production, use their API or manual entry. | |
| """ | |
| # Simplified URL - users would need to fill in details manually | |
| return f"{self.BASE_URL}/cgi/genchart.cgi?btyp=w2gw&rs=3&usechpref=1&hsy={house_system}" | |
| def get_chart_interpretation( | |
| self, | |
| chart_data: Dict, | |
| focus: str = "general" | |
| ) -> Dict: | |
| """ | |
| Get classical Hellenistic interpretation guidance | |
| Args: | |
| chart_data: Chart data from generate_chart() | |
| focus: Interpretation focus (general, career, relationships, health) | |
| Returns: | |
| Interpretation guidance based on Hellenistic principles | |
| """ | |
| interpretations = { | |
| "general": { | |
| "steps": [ | |
| "1. **Identify the Sect**: Determine if day or night chart (Sun above/below horizon)", | |
| "2. **Assess Sect Light**: Evaluate Sun (day) or Moon (night) as primary luminary", | |
| "3. **Find Benefics/Malefics**: Identify Jupiter/Venus (benefic) and Saturn/Mars (malefic)", | |
| "4. **Check Essential Dignities**: Evaluate planetary strength by sign placement", | |
| "5. **Examine House Rulers**: Note which planets rule key houses (1st, 10th, 7th, 4th)", | |
| "6. **Analyze Aspects**: Traditional aspects (conjunction, sextile, square, trine, opposition)", | |
| "7. **Consider Lots**: Calculate Lot of Fortune and Lot of Spirit" | |
| ], | |
| "key_principles": [ | |
| "Sect determines benefic/malefic team alignment", | |
| "Stronger planet (by dignity) wins conflicting testimonies", | |
| "House rulers show life area conditions", | |
| "Aspects by degree and sign, with orbs" | |
| ] | |
| }, | |
| "career": { | |
| "focus_points": [ | |
| "**10th House**: Career, reputation, public life", | |
| "**10th Ruler**: Shows career significations", | |
| "**MC Sign**: Public image and recognition", | |
| "**Lot of Fortune**: Material success indicators", | |
| "**Sun** (day) or **Moon** (night): Overall life direction" | |
| ] | |
| }, | |
| "relationships": { | |
| "focus_points": [ | |
| "**7th House**: Marriage and partnerships", | |
| "**7th Ruler**: Partner characteristics", | |
| "**Venus**: Love, beauty, harmony", | |
| "**Lot of Eros**: Romantic desires", | |
| "**Moon**: Emotional connections" | |
| ] | |
| }, | |
| "health": { | |
| "focus_points": [ | |
| "**6th House**: Illness and obstacles", | |
| "**1st House**: Physical body and vitality", | |
| "**Lot of Fortune**: Physical health", | |
| "**Malefics**: Areas of potential difficulty", | |
| "**Moon**: Bodily fluctuations" | |
| ] | |
| } | |
| } | |
| result = interpretations.get(focus, interpretations["general"]) | |
| result["focus"] = focus | |
| result["methodology"] = "Classical Hellenistic Astrology (1st-7th century CE)" | |
| result["sources"] = [ | |
| "Vettius Valens - Anthology", | |
| "Claudius Ptolemy - Tetrabiblos", | |
| "Dorotheus of Sidon - Carmen Astrologicum", | |
| "Paulus Alexandrinus - Introductory Matters" | |
| ] | |
| result["modern_reference"] = "Chris Brennan - Hellenistic Astrology: The Study of Fate and Fortune (2017)" | |
| return result | |
| def calculate_hellenistic_aspects( | |
| self, | |
| planet_positions: Dict[str, float] | |
| ) -> List[Dict]: | |
| """ | |
| Calculate traditional Hellenistic aspects | |
| Traditional aspects in Hellenistic astrology: | |
| - Conjunction (0°): Same sign, close degrees | |
| - Sextile (60°): 2-3 signs apart | |
| - Square (90°): 3-4 signs apart | |
| - Trine (120°): 4-5 signs apart | |
| - Opposition (180°): 6-7 signs apart | |
| Args: | |
| planet_positions: Dict of planet names to absolute degrees (0-360) | |
| Returns: | |
| List of aspects with orbs and interpretations | |
| """ | |
| aspects = [] | |
| aspect_types = { | |
| 0: {"name": "Conjunction", "nature": "Blending", "orb": 8}, | |
| 60: {"name": "Sextile", "nature": "Harmonious", "orb": 6}, | |
| 90: {"name": "Square", "nature": "Tension", "orb": 7}, | |
| 120: {"name": "Trine", "nature": "Flowing", "orb": 8}, | |
| 180: {"name": "Opposition", "nature": "Polarity", "orb": 8} | |
| } | |
| planets = list(planet_positions.keys()) | |
| for i, planet1 in enumerate(planets): | |
| for planet2 in planets[i+1:]: | |
| angle = abs(planet_positions[planet1] - planet_positions[planet2]) | |
| if angle > 180: | |
| angle = 360 - angle | |
| for aspect_angle, aspect_info in aspect_types.items(): | |
| if abs(angle - aspect_angle) <= aspect_info["orb"]: | |
| aspects.append({ | |
| "planet1": planet1, | |
| "planet2": planet2, | |
| "aspect": aspect_info["name"], | |
| "angle": round(angle, 2), | |
| "orb": round(abs(angle - aspect_angle), 2), | |
| "nature": aspect_info["nature"], | |
| "interpretation": f"{planet1} {aspect_info['name'].lower()} {planet2}: {aspect_info['nature']} connection" | |
| }) | |
| return aspects | |
| def get_whole_sign_houses( | |
| self, | |
| ascendant_sign: str, | |
| ascendant_degree: float = 0 | |
| ) -> Dict: | |
| """ | |
| Calculate Whole Sign house system (traditional Hellenistic) | |
| In Whole Sign houses: | |
| - Ascendant sign = entire 1st house | |
| - Next sign = entire 2nd house | |
| - And so on... | |
| Args: | |
| ascendant_sign: Rising sign | |
| ascendant_degree: Degree of ascendant (for reference, not used in house calculation) | |
| Returns: | |
| Complete house system with signs | |
| """ | |
| signs = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", | |
| "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"] | |
| try: | |
| start_index = signs.index(ascendant_sign) | |
| except ValueError: | |
| return {"error": f"Invalid sign: {ascendant_sign}"} | |
| houses = {} | |
| for house_num in range(1, 13): | |
| sign_index = (start_index + house_num - 1) % 12 | |
| houses[house_num] = { | |
| "sign": signs[sign_index], | |
| "start_degree": 0, | |
| "end_degree": 30, | |
| "system": "Whole Sign" | |
| } | |
| return { | |
| "ascendant": ascendant_sign, | |
| "ascendant_degree": ascendant_degree, | |
| "house_system": "Whole Sign (Hellenistic)", | |
| "houses": houses, | |
| "note": "In Whole Sign houses, each house occupies an entire sign. This is the original house system used by Hellenistic astrologers.", | |
| "source": "Classical Hellenistic tradition" | |
| } | |
| def get_mcp_tools() -> List[Dict]: | |
| """Return list of available AstroDienst MCP tools""" | |
| return [ | |
| { | |
| "name": "generate_chart", | |
| "description": "Generate classical Hellenistic chart from Astro.com", | |
| "parameters": { | |
| "birth_date": "string (DD.MM.YYYY)", | |
| "birth_time": "string (HH:MM)", | |
| "city": "string", | |
| "country": "string (optional)", | |
| "house_system": "string (W=Whole Sign, P=Placidus, K=Koch, E=Equal)" | |
| }, | |
| "returns": "Chart URL and configuration for astro.com" | |
| }, | |
| { | |
| "name": "get_chart_interpretation", | |
| "description": "Get Hellenistic interpretation guidance", | |
| "parameters": { | |
| "chart_data": "dict (from generate_chart)", | |
| "focus": "string (general, career, relationships, health)" | |
| }, | |
| "returns": "Step-by-step interpretation guide with classical principles" | |
| }, | |
| { | |
| "name": "calculate_hellenistic_aspects", | |
| "description": "Calculate traditional aspects with orbs", | |
| "parameters": { | |
| "planet_positions": "dict {planet_name: degree_0_to_360}" | |
| }, | |
| "returns": "List of aspects with interpretations" | |
| }, | |
| { | |
| "name": "get_whole_sign_houses", | |
| "description": "Calculate Whole Sign house system (Hellenistic)", | |
| "parameters": { | |
| "ascendant_sign": "string (zodiac sign)", | |
| "ascendant_degree": "float (optional, for reference)" | |
| }, | |
| "returns": "Complete 12-house layout in Whole Sign system" | |
| } | |
| ] | |
| # Main entry point | |
| if __name__ == "__main__": | |
| import sys | |
| if len(sys.argv) > 1 and sys.argv[1] == "tools": | |
| print(json.dumps(get_mcp_tools(), indent=2)) | |
| else: | |
| print("=" * 70) | |
| print("AstroDienst (Astro.com) MCP Server") | |
| print("Classical Hellenistic Chart Integration") | |
| print("=" * 70) | |
| print("\nAvailable Tools:") | |
| print(json.dumps(get_mcp_tools(), indent=2)) | |
| # Test example | |
| print("\n" + "=" * 70) | |
| print("TEST: Generate Chart") | |
| print("=" * 70) | |
| server = AstroDienstMCP() | |
| result = server.generate_chart( | |
| birth_date="15.01.1990", | |
| birth_time="12:00", | |
| city="New York", | |
| country="USA", | |
| house_system="W" | |
| ) | |
| print(json.dumps(result, indent=2)) | |
| print("\n" + "=" * 70) | |
| print("TEST: Whole Sign Houses") | |
| print("=" * 70) | |
| result = server.get_whole_sign_houses("Leo", 15.5) | |
| print(json.dumps(result, indent=2)) | |
| print("\n" + "=" * 70) | |
| print("TEST: Interpretation Guide") | |
| print("=" * 70) | |
| result = server.get_chart_interpretation({}, "general") | |
| print(json.dumps(result, indent=2)) | |