File size: 15,229 Bytes
8e283f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
"""
Local HERMES MCP Server
Demonstrates MCP integration with core HERMES features
"""

import json
from typing import Dict, List, Optional
from datetime import datetime
import sys

class HermesLocalMCP:
    """Local MCP Server for HERMES core features"""

    def __init__(self):
        self.server_name = "HERMES Local MCP"
        self.version = "1.0.0"

    # Essential Dignities Data (from app.py)
    DOMICILES = {
        "Sun": ["Leo"],
        "Moon": ["Cancer"],
        "Mercury": ["Gemini", "Virgo"],
        "Venus": ["Taurus", "Libra"],
        "Mars": ["Aries", "Scorpio"],
        "Jupiter": ["Sagittarius", "Pisces"],
        "Saturn": ["Capricorn", "Aquarius"]
    }

    EXALTATIONS = {
        "Sun": "Aries",
        "Moon": "Taurus",
        "Mercury": "Virgo",
        "Venus": "Pisces",
        "Mars": "Capricorn",
        "Jupiter": "Cancer",
        "Saturn": "Libra"
    }

    DETRIMENTS = {
        "Sun": ["Aquarius"],
        "Moon": ["Capricorn"],
        "Mercury": ["Sagittarius", "Pisces"],
        "Venus": ["Aries", "Scorpio"],
        "Mars": ["Taurus", "Libra"],
        "Jupiter": ["Gemini", "Virgo"],
        "Saturn": ["Cancer", "Leo"]
    }

    FALLS = {
        "Sun": ["Libra"],
        "Moon": ["Scorpio"],
        "Mercury": ["Pisces"],
        "Venus": ["Virgo"],
        "Mars": ["Cancer"],
        "Jupiter": ["Capricorn"],
        "Saturn": ["Aries"]
    }

    BOUNDS = {
        "Aries": [
            {"start": 0, "end": 6, "ruler": "Jupiter"},
            {"start": 6, "end": 12, "ruler": "Venus"},
            {"start": 12, "end": 20, "ruler": "Mercury"},
            {"start": 20, "end": 25, "ruler": "Mars"},
            {"start": 25, "end": 30, "ruler": "Saturn"}
        ],
        "Taurus": [
            {"start": 0, "end": 8, "ruler": "Venus"},
            {"start": 8, "end": 14, "ruler": "Mercury"},
            {"start": 14, "end": 22, "ruler": "Jupiter"},
            {"start": 22, "end": 27, "ruler": "Saturn"},
            {"start": 27, "end": 30, "ruler": "Mars"}
        ],
        # Add remaining signs...
    }

    def calculate_essential_dignity(
        self,
        planet: str,
        sign: str,
        degree: float,
        is_day_chart: bool = True
    ) -> Dict:
        """
        Calculate essential dignity for a planet

        Args:
            planet: Planet name (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn)
            sign: Zodiac sign
            degree: Degree within sign (0-30)
            is_day_chart: Whether it's a day or night chart

        Returns:
            Complete dignity assessment with score and interpretation
        """
        dignity_score = 0
        dignities = []
        debilities = []

        # Check domicile (+5)
        if sign in self.DOMICILES.get(planet, []):
            dignity_score += 5
            dignities.append("Domicile (+5)")

        # Check exaltation (+4)
        if sign == self.EXALTATIONS.get(planet):
            dignity_score += 4
            dignities.append("Exaltation (+4)")

        # Check detriment (-5)
        if sign in self.DETRIMENTS.get(planet, []):
            dignity_score -= 5
            debilities.append("Detriment (-5)")

        # Check fall (-4)
        if sign in self.FALLS.get(planet, []):
            dignity_score -= 4
            debilities.append("Fall (-4)")

        # Determine planetary condition
        if dignity_score >= 4:
            condition = "Very Strong"
        elif dignity_score >= 2:
            condition = "Strong"
        elif dignity_score >= -1:
            condition = "Moderate/Peregrine"
        elif dignity_score >= -4:
            condition = "Weak/Debilitated"
        else:
            condition = "Very Weak"

        return {
            "planet": planet,
            "sign": sign,
            "degree": degree,
            "chart_type": "Day Chart" if is_day_chart else "Night Chart",
            "dignity_score": dignity_score,
            "dignities": dignities,
            "debilities": debilities,
            "condition": condition,
            "interpretation": self._get_dignity_interpretation(planet, sign, condition),
            "source": "HERMES Local MCP v1.0"
        }

    def _get_dignity_interpretation(self, planet: str, sign: str, condition: str) -> str:
        """Generate interpretation text"""
        if condition == "Very Strong":
            return f"{planet} in {sign} is very strong, able to express its significations powerfully and effectively."
        elif condition == "Strong":
            return f"{planet} in {sign} is well-placed and can express its nature with strength."
        elif condition == "Moderate/Peregrine":
            return f"{planet} in {sign} is peregrine (wandering), having neutral strength without major dignities or debilities."
        elif condition == "Weak/Debilitated":
            return f"{planet} in {sign} is debilitated, struggling to express its significations effectively."
        else:
            return f"{planet} in {sign} is severely debilitated, facing major obstacles in expressing its nature."

    def calculate_annual_profection(
        self,
        birth_date: str,
        current_age: int
    ) -> Dict:
        """
        Calculate annual profection for a given age

        Args:
            birth_date: Birth date in YYYY-MM-DD format
            current_age: Current age in years

        Returns:
            Profected house and time-lord information
        """
        # Profections cycle through 12 houses
        profected_house = (current_age % 12) + 1

        house_themes = {
            1: "Self, body, vitality, new beginnings",
            2: "Resources, possessions, values, income",
            3: "Siblings, communication, short journeys, learning",
            4: "Home, family, foundations, parents",
            5: "Creativity, children, pleasure, romance",
            6: "Health, service, daily work, obstacles",
            7: "Partnerships, marriage, open enemies",
            8: "Death, inheritance, transformation, others' resources",
            9: "Philosophy, long journeys, higher education, religion",
            10: "Career, public reputation, status, achievements",
            11: "Friends, hopes, wishes, groups, benefactors",
            12: "Isolation, hidden matters, spirituality, undoing"
        }

        return {
            "birth_date": birth_date,
            "current_age": current_age,
            "profected_house": profected_house,
            "house_theme": house_themes.get(profected_house, "Unknown"),
            "interpretation": f"At age {current_age}, the profection activates the {profected_house}th house. This year's themes center around: {house_themes.get(profected_house, 'Unknown')}.",
            "lord_of_year": f"The ruler of the {profected_house}th house is the Lord of the Year",
            "source": "HERMES Local MCP v1.0"
        }

    def calculate_lot_of_fortune(
        self,
        asc_degree: float,
        sun_degree: float,
        moon_degree: float,
        is_day_chart: bool = True
    ) -> Dict:
        """
        Calculate Lot of Fortune

        Formula:
        - Day chart: Ascendant + Moon - Sun
        - Night chart: Ascendant + Sun - Moon

        Args:
            asc_degree: Ascendant in absolute degrees (0-360)
            sun_degree: Sun position in absolute degrees
            moon_degree: Moon position in absolute degrees
            is_day_chart: Whether it's a day chart

        Returns:
            Lot of Fortune position and interpretation
        """
        if is_day_chart:
            fortune = (asc_degree + moon_degree - sun_degree) % 360
            formula = "ASC + Moon - Sun"
        else:
            fortune = (asc_degree + sun_degree - moon_degree) % 360
            formula = "ASC + Sun - Moon"

        # Convert to sign and degree
        sign_num = int(fortune / 30)
        degree = fortune % 30

        signs = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
                "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"]
        sign = signs[sign_num]

        return {
            "lot": "Fortune",
            "position": f"{degree:.2f}° {sign}",
            "absolute_degree": fortune,
            "formula": formula,
            "chart_type": "Day Chart" if is_day_chart else "Night Chart",
            "interpretation": f"The Lot of Fortune represents the body, health, and material fortune. Placed at {degree:.2f}° {sign}, it shows where fortune and prosperity manifest.",
            "source": "HERMES Local MCP v1.0"
        }

    def get_technique_explanation(self, technique: str) -> Dict:
        """
        Get educational explanation for a technique

        Args:
            technique: Name of technique (dignities, profections, lots, etc.)

        Returns:
            Educational content about the technique
        """
        explanations = {
            "essential_dignities": {
                "name": "Essential Dignities",
                "summary": "A system for evaluating planetary strength based on zodiacal position",
                "description": """Essential dignities measure how well-placed a planet is in the zodiac.
                The system includes five major categories:

                1. **Domicile** (+5): Planet in its home sign
                2. **Exaltation** (+4): Planet in its sign of greatest strength
                3. **Triplicity** (+3): Planet ruling the element (day/night)
                4. **Bound/Term** (+2): Planet ruling a specific degree range
                5. **Decan/Face** (+1): Planet ruling a 10-degree section

                Debilities are the opposites:
                - **Detriment** (-5): Opposite of domicile
                - **Fall** (-4): Opposite of exaltation

                A planet's dignity score indicates its ability to act effectively.""",
                "source": "Ptolemy's Tetrabiblos, Vettius Valens",
                "period": "Hellenistic (1st-7th century CE)"
            },
            "annual_profections": {
                "name": "Annual Profections",
                "summary": "A timing technique that activates different life areas each year",
                "description": """Annual profections move the Ascendant forward one house per year of life.

                The technique works by:
                1. Starting at the Ascendant at birth (age 0)
                2. Moving one house forward each birthday
                3. The profected house becomes active that year
                4. The ruler of that house becomes 'Lord of the Year'

                This creates a 12-year cycle where each house theme is activated in sequence.
                The Lord of the Year becomes especially important for predicting events.""",
                "source": "Vettius Valens, Firmicus Maternus",
                "period": "Hellenistic (2nd century CE)"
            },
            "lots": {
                "name": "Lots/Parts",
                "summary": "Calculated sensitive points derived from planet positions",
                "description": """Lots (Arabic Parts) are sensitive points calculated from planetary positions.

                The most important lot is the **Lot of Fortune**:
                - Day chart: ASC + Moon - Sun
                - Night chart: ASC + Sun - Moon

                Other important lots include:
                - **Lot of Spirit**: Opposite calculation to Fortune
                - **Lot of Eros**: Related to desire and love
                - **Lot of Necessity**: Related to constraint and obligation

                Lots represent abstract topics and show where certain life themes manifest.""",
                "source": "Vettius Valens, Paulus Alexandrinus",
                "period": "Hellenistic (1st-4th century CE)"
            }
        }

        result = explanations.get(technique.lower().replace(" ", "_"), {
            "name": technique,
            "summary": "Technique not found in database",
            "description": "No explanation available for this technique.",
            "source": "Unknown",
            "period": "Unknown"
        })

        result["requested_technique"] = technique
        result["server"] = "HERMES Local MCP v1.0"

        return result


def get_mcp_tools() -> List[Dict]:
    """Return list of available HERMES Local MCP tools"""
    return [
        {
            "name": "calculate_essential_dignity",
            "description": "Calculate essential dignity score for a planet in a sign",
            "parameters": {
                "planet": "string (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn)",
                "sign": "string (Aries, Taurus, Gemini, etc.)",
                "degree": "float (0-30)",
                "is_day_chart": "boolean (optional, default True)"
            },
            "returns": "Dignity score, condition, and interpretation"
        },
        {
            "name": "calculate_annual_profection",
            "description": "Calculate annual profection for a given age",
            "parameters": {
                "birth_date": "string (YYYY-MM-DD)",
                "current_age": "integer"
            },
            "returns": "Profected house and Lord of the Year information"
        },
        {
            "name": "calculate_lot_of_fortune",
            "description": "Calculate the Lot of Fortune position",
            "parameters": {
                "asc_degree": "float (0-360)",
                "sun_degree": "float (0-360)",
                "moon_degree": "float (0-360)",
                "is_day_chart": "boolean (optional, default True)"
            },
            "returns": "Lot position and interpretation"
        },
        {
            "name": "get_technique_explanation",
            "description": "Get educational explanation for a traditional technique",
            "parameters": {
                "technique": "string (essential_dignities, annual_profections, lots, etc.)"
            },
            "returns": "Educational content with historical sources"
        }
    ]


# Main entry point for MCP server
if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "tools":
        print(json.dumps(get_mcp_tools(), indent=2))
    else:
        print("=" * 70)
        print("HERMES Local MCP Server v1.0")
        print("=" * 70)
        print("\nAvailable Tools:")
        print(json.dumps(get_mcp_tools(), indent=2))

        # Test examples
        print("\n" + "=" * 70)
        print("TEST: Essential Dignity - Venus in Aries")
        print("=" * 70)

        server = HermesLocalMCP()
        result = server.calculate_essential_dignity("Venus", "Aries", 8.5, True)
        print(json.dumps(result, indent=2))

        print("\n" + "=" * 70)
        print("TEST: Annual Profection - Age 35")
        print("=" * 70)

        result = server.calculate_annual_profection("1990-01-15", 35)
        print(json.dumps(result, indent=2))

        print("\n" + "=" * 70)
        print("TEST: Lot of Fortune")
        print("=" * 70)

        result = server.calculate_lot_of_fortune(120, 295, 210, True)
        print(json.dumps(result, indent=2))