caelus-mcp 0.13.0 → 0.14.0

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.
package/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # caelus-mcp
2
2
 
3
3
  MCP server for the [caelus](https://github.com/heavyblotto/caelus) ephemeris
4
- engine: eighteen chart tools over stdio. Computation only — positions, houses,
4
+ engine: twenty-two chart tools over stdio. Computation only — positions, houses,
5
5
  aspects with orbs, event search, electional, returns, progressions, composite,
6
6
  dignities, Hermetic lots, the Hellenistic time-lords (profections, firdaria,
7
- zodiacal releasing), and primary directions the model does the interpreting.
7
+ zodiacal releasing), primary directions, and the Vedic layer (nakshatras,
8
+ dashas, vargas, yogas) — the model does the interpreting.
8
9
  It needs
9
10
  no API keys, ephemeris files, or network calls; the engine data ships inside
10
11
  the package.
@@ -55,7 +56,11 @@ A hosted Streamable HTTP endpoint is also live at
55
56
  | `profections` | Annual and monthly profections to a target date: age, the profected signs and their whole-sign houses, and the lord of the year |
56
57
  | `firdaria` | The firdaria planetary time-lord periods: the full 75-year timeline (nine periods, seven sub-periods each) and the lords active at a target date |
57
58
  | `releasing` | Zodiacal releasing (aphesis) from the Lot of Spirit or Fortune: the L1–L4 period timeline and the lords active at a target date, with loosing of the bond |
58
- | `directions` | Primary directions of the seven traditional planets to the four angles (MC/IC/Asc/Desc) by the Naibod or Ptolemy time key, sorted by age |
59
+ | `directions` | Primary directions of the seven traditional planets to the four angles (MC/IC/Asc/Desc), and optionally between the planets (mundane), by the Naibod or Ptolemy time key, sorted by age |
60
+ | `nakshatras` | The nakshatra (lunar mansion), pada, and ruling planet of each classical planet and the Ascendant on the sidereal zodiac (Lahiri by default) |
61
+ | `dasha` | Vedic dasha periods from the Moon's nakshatra — Vimshottari (120y), Yogini (36y), or Ashtottari (108y): the maha → antar timeline and the lords active at a target date |
62
+ | `vargas` | Parashari divisional charts (D1 rasi, D2, D3, D9 navamsa, D10, D12, D30): the divisional sign of each planet and the Ascendant |
63
+ | `yogas` | Vedic yogas on the rasi chart: Pancha Mahapurusha, Gajakesari, Budha-Aditya, Chandra-Mangala, Kemadruma, plus raja/dhana yogas and yogakarakas |
59
64
 
60
65
  `natal_chart` and `current_sky` also tag each body with its solar phase
61
66
  (cazimi/combust/under-the-beams) and each aspect with applying/separating.
@@ -3,7 +3,7 @@
3
3
  * caelus-mcp -- MCP server for the caelus ephemeris engine.
4
4
  *
5
5
  * Design (per 2026 MCP practice): one bounded context (chart computation),
6
- * a small curated tool surface (eighteen outcome-level tools, not API wrappers),
6
+ * a small curated tool surface (twenty-two outcome-level tools, not API wrappers),
7
7
  * and token-frugal outputs (positions to 0.01 deg, terse keys, no prose --
8
8
  * the model does the interpreting, the server does the math).
9
9
  *
@@ -1591,6 +1591,25 @@ export declare const directionsOut: z.ZodObject<{
1591
1591
  angle: "MC" | "IC" | "ASC" | "DSC";
1592
1592
  arc: number;
1593
1593
  }>, "many">;
1594
+ mundane: z.ZodOptional<z.ZodArray<z.ZodObject<{
1595
+ promissor: z.ZodString;
1596
+ significator: z.ZodString;
1597
+ arc: z.ZodNumber;
1598
+ years: z.ZodNumber;
1599
+ date: z.ZodString;
1600
+ }, "strip", z.ZodTypeAny, {
1601
+ date: string;
1602
+ years: number;
1603
+ arc: number;
1604
+ promissor: string;
1605
+ significator: string;
1606
+ }, {
1607
+ date: string;
1608
+ years: number;
1609
+ arc: number;
1610
+ promissor: string;
1611
+ significator: string;
1612
+ }>, "many">>;
1594
1613
  }, "strip", z.ZodTypeAny, {
1595
1614
  natal_utc: string;
1596
1615
  key: "ptolemy" | "naibod";
@@ -1601,6 +1620,13 @@ export declare const directionsOut: z.ZodObject<{
1601
1620
  angle: "MC" | "IC" | "ASC" | "DSC";
1602
1621
  arc: number;
1603
1622
  }[];
1623
+ mundane?: {
1624
+ date: string;
1625
+ years: number;
1626
+ arc: number;
1627
+ promissor: string;
1628
+ significator: string;
1629
+ }[] | undefined;
1604
1630
  }, {
1605
1631
  natal_utc: string;
1606
1632
  key: "ptolemy" | "naibod";
@@ -1611,6 +1637,272 @@ export declare const directionsOut: z.ZodObject<{
1611
1637
  angle: "MC" | "IC" | "ASC" | "DSC";
1612
1638
  arc: number;
1613
1639
  }[];
1640
+ mundane?: {
1641
+ date: string;
1642
+ years: number;
1643
+ arc: number;
1644
+ promissor: string;
1645
+ significator: string;
1646
+ }[] | undefined;
1647
+ }>;
1648
+ export declare const nakshatrasOut: z.ZodObject<{
1649
+ natal_utc: z.ZodString;
1650
+ zodiac: z.ZodEnum<["tropical", "sidereal:lahiri", "sidereal:fagan_bradley", "sidereal:krishnamurti", "sidereal:raman", "sidereal:yukteshwar", "sidereal:galcent_0sag", "sidereal:true_citra"]>;
1651
+ points: z.ZodRecord<z.ZodString, z.ZodObject<{
1652
+ nakshatra: z.ZodString;
1653
+ pada: z.ZodNumber;
1654
+ lord: z.ZodString;
1655
+ deg: z.ZodNumber;
1656
+ }, "strip", z.ZodTypeAny, {
1657
+ deg: number;
1658
+ lord: string;
1659
+ nakshatra: string;
1660
+ pada: number;
1661
+ }, {
1662
+ deg: number;
1663
+ lord: string;
1664
+ nakshatra: string;
1665
+ pada: number;
1666
+ }>>;
1667
+ }, "strip", z.ZodTypeAny, {
1668
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
1669
+ natal_utc: string;
1670
+ points: Record<string, {
1671
+ deg: number;
1672
+ lord: string;
1673
+ nakshatra: string;
1674
+ pada: number;
1675
+ }>;
1676
+ }, {
1677
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
1678
+ natal_utc: string;
1679
+ points: Record<string, {
1680
+ deg: number;
1681
+ lord: string;
1682
+ nakshatra: string;
1683
+ pada: number;
1684
+ }>;
1685
+ }>;
1686
+ export declare const dashaOut: z.ZodObject<{
1687
+ natal_utc: z.ZodString;
1688
+ system: z.ZodEnum<["vimshottari", "yogini", "ashtottari"]>;
1689
+ moon_nakshatra: z.ZodString;
1690
+ start_lord: z.ZodOptional<z.ZodString>;
1691
+ start_yogini: z.ZodOptional<z.ZodString>;
1692
+ balance_years: z.ZodNumber;
1693
+ periods: z.ZodArray<z.ZodObject<{
1694
+ lord: z.ZodOptional<z.ZodString>;
1695
+ yogini: z.ZodOptional<z.ZodString>;
1696
+ start: z.ZodString;
1697
+ end: z.ZodString;
1698
+ sub: z.ZodOptional<z.ZodArray<z.ZodObject<{
1699
+ lord: z.ZodOptional<z.ZodString>;
1700
+ yogini: z.ZodOptional<z.ZodString>;
1701
+ start: z.ZodString;
1702
+ end: z.ZodString;
1703
+ }, "strip", z.ZodTypeAny, {
1704
+ start: string;
1705
+ end: string;
1706
+ lord?: string | undefined;
1707
+ yogini?: string | undefined;
1708
+ }, {
1709
+ start: string;
1710
+ end: string;
1711
+ lord?: string | undefined;
1712
+ yogini?: string | undefined;
1713
+ }>, "many">>;
1714
+ }, "strip", z.ZodTypeAny, {
1715
+ start: string;
1716
+ end: string;
1717
+ lord?: string | undefined;
1718
+ sub?: {
1719
+ start: string;
1720
+ end: string;
1721
+ lord?: string | undefined;
1722
+ yogini?: string | undefined;
1723
+ }[] | undefined;
1724
+ yogini?: string | undefined;
1725
+ }, {
1726
+ start: string;
1727
+ end: string;
1728
+ lord?: string | undefined;
1729
+ sub?: {
1730
+ start: string;
1731
+ end: string;
1732
+ lord?: string | undefined;
1733
+ yogini?: string | undefined;
1734
+ }[] | undefined;
1735
+ yogini?: string | undefined;
1736
+ }>, "many">;
1737
+ active: z.ZodOptional<z.ZodObject<{
1738
+ target_utc: z.ZodString;
1739
+ maha: z.ZodNullable<z.ZodString>;
1740
+ antar: z.ZodNullable<z.ZodString>;
1741
+ pratyantar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1742
+ }, "strip", z.ZodTypeAny, {
1743
+ target_utc: string;
1744
+ maha: string | null;
1745
+ antar: string | null;
1746
+ pratyantar?: string | null | undefined;
1747
+ }, {
1748
+ target_utc: string;
1749
+ maha: string | null;
1750
+ antar: string | null;
1751
+ pratyantar?: string | null | undefined;
1752
+ }>>;
1753
+ }, "strip", z.ZodTypeAny, {
1754
+ natal_utc: string;
1755
+ periods: {
1756
+ start: string;
1757
+ end: string;
1758
+ lord?: string | undefined;
1759
+ sub?: {
1760
+ start: string;
1761
+ end: string;
1762
+ lord?: string | undefined;
1763
+ yogini?: string | undefined;
1764
+ }[] | undefined;
1765
+ yogini?: string | undefined;
1766
+ }[];
1767
+ system: "yogini" | "vimshottari" | "ashtottari";
1768
+ moon_nakshatra: string;
1769
+ balance_years: number;
1770
+ active?: {
1771
+ target_utc: string;
1772
+ maha: string | null;
1773
+ antar: string | null;
1774
+ pratyantar?: string | null | undefined;
1775
+ } | undefined;
1776
+ start_lord?: string | undefined;
1777
+ start_yogini?: string | undefined;
1778
+ }, {
1779
+ natal_utc: string;
1780
+ periods: {
1781
+ start: string;
1782
+ end: string;
1783
+ lord?: string | undefined;
1784
+ sub?: {
1785
+ start: string;
1786
+ end: string;
1787
+ lord?: string | undefined;
1788
+ yogini?: string | undefined;
1789
+ }[] | undefined;
1790
+ yogini?: string | undefined;
1791
+ }[];
1792
+ system: "yogini" | "vimshottari" | "ashtottari";
1793
+ moon_nakshatra: string;
1794
+ balance_years: number;
1795
+ active?: {
1796
+ target_utc: string;
1797
+ maha: string | null;
1798
+ antar: string | null;
1799
+ pratyantar?: string | null | undefined;
1800
+ } | undefined;
1801
+ start_lord?: string | undefined;
1802
+ start_yogini?: string | undefined;
1803
+ }>;
1804
+ export declare const vargasOut: z.ZodObject<{
1805
+ natal_utc: z.ZodString;
1806
+ zodiac: z.ZodEnum<["tropical", "sidereal:lahiri", "sidereal:fagan_bradley", "sidereal:krishnamurti", "sidereal:raman", "sidereal:yukteshwar", "sidereal:galcent_0sag", "sidereal:true_citra"]>;
1807
+ charts: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
1808
+ sign: z.ZodString;
1809
+ sign_index: z.ZodNumber;
1810
+ division: z.ZodNumber;
1811
+ }, "strip", z.ZodTypeAny, {
1812
+ sign: string;
1813
+ sign_index: number;
1814
+ division: number;
1815
+ }, {
1816
+ sign: string;
1817
+ sign_index: number;
1818
+ division: number;
1819
+ }>>>;
1820
+ }, "strip", z.ZodTypeAny, {
1821
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
1822
+ natal_utc: string;
1823
+ charts: Record<string, Record<string, {
1824
+ sign: string;
1825
+ sign_index: number;
1826
+ division: number;
1827
+ }>>;
1828
+ }, {
1829
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
1830
+ natal_utc: string;
1831
+ charts: Record<string, Record<string, {
1832
+ sign: string;
1833
+ sign_index: number;
1834
+ division: number;
1835
+ }>>;
1836
+ }>;
1837
+ export declare const yogasOut: z.ZodObject<{
1838
+ natal_utc: z.ZodString;
1839
+ zodiac: z.ZodEnum<["tropical", "sidereal:lahiri", "sidereal:fagan_bradley", "sidereal:krishnamurti", "sidereal:raman", "sidereal:yukteshwar", "sidereal:galcent_0sag", "sidereal:true_citra"]>;
1840
+ yogas: z.ZodArray<z.ZodObject<{
1841
+ yoga: z.ZodString;
1842
+ planets: z.ZodArray<z.ZodString, "many">;
1843
+ }, "strip", z.ZodTypeAny, {
1844
+ yoga: string;
1845
+ planets: string[];
1846
+ }, {
1847
+ yoga: string;
1848
+ planets: string[];
1849
+ }>, "many">;
1850
+ kemadruma: z.ZodBoolean;
1851
+ raja_yogas: z.ZodArray<z.ZodObject<{
1852
+ lords: z.ZodArray<z.ZodString, "many">;
1853
+ via: z.ZodString;
1854
+ }, "strip", z.ZodTypeAny, {
1855
+ lords: string[];
1856
+ via: string;
1857
+ }, {
1858
+ lords: string[];
1859
+ via: string;
1860
+ }>, "many">;
1861
+ dhana_yogas: z.ZodArray<z.ZodObject<{
1862
+ lords: z.ZodArray<z.ZodString, "many">;
1863
+ via: z.ZodString;
1864
+ }, "strip", z.ZodTypeAny, {
1865
+ lords: string[];
1866
+ via: string;
1867
+ }, {
1868
+ lords: string[];
1869
+ via: string;
1870
+ }>, "many">;
1871
+ yogakarakas: z.ZodArray<z.ZodString, "many">;
1872
+ }, "strip", z.ZodTypeAny, {
1873
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
1874
+ natal_utc: string;
1875
+ yogas: {
1876
+ yoga: string;
1877
+ planets: string[];
1878
+ }[];
1879
+ kemadruma: boolean;
1880
+ raja_yogas: {
1881
+ lords: string[];
1882
+ via: string;
1883
+ }[];
1884
+ dhana_yogas: {
1885
+ lords: string[];
1886
+ via: string;
1887
+ }[];
1888
+ yogakarakas: string[];
1889
+ }, {
1890
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
1891
+ natal_utc: string;
1892
+ yogas: {
1893
+ yoga: string;
1894
+ planets: string[];
1895
+ }[];
1896
+ kemadruma: boolean;
1897
+ raja_yogas: {
1898
+ lords: string[];
1899
+ via: string;
1900
+ }[];
1901
+ dhana_yogas: {
1902
+ lords: string[];
1903
+ via: string;
1904
+ }[];
1905
+ yogakarakas: string[];
1614
1906
  }>;
1615
1907
  export declare const OUTPUT_SCHEMAS: {
1616
1908
  readonly natal_chart: z.ZodObject<{
@@ -3312,6 +3604,25 @@ export declare const OUTPUT_SCHEMAS: {
3312
3604
  angle: "MC" | "IC" | "ASC" | "DSC";
3313
3605
  arc: number;
3314
3606
  }>, "many">;
3607
+ mundane: z.ZodOptional<z.ZodArray<z.ZodObject<{
3608
+ promissor: z.ZodString;
3609
+ significator: z.ZodString;
3610
+ arc: z.ZodNumber;
3611
+ years: z.ZodNumber;
3612
+ date: z.ZodString;
3613
+ }, "strip", z.ZodTypeAny, {
3614
+ date: string;
3615
+ years: number;
3616
+ arc: number;
3617
+ promissor: string;
3618
+ significator: string;
3619
+ }, {
3620
+ date: string;
3621
+ years: number;
3622
+ arc: number;
3623
+ promissor: string;
3624
+ significator: string;
3625
+ }>, "many">>;
3315
3626
  }, "strip", z.ZodTypeAny, {
3316
3627
  natal_utc: string;
3317
3628
  key: "ptolemy" | "naibod";
@@ -3322,6 +3633,13 @@ export declare const OUTPUT_SCHEMAS: {
3322
3633
  angle: "MC" | "IC" | "ASC" | "DSC";
3323
3634
  arc: number;
3324
3635
  }[];
3636
+ mundane?: {
3637
+ date: string;
3638
+ years: number;
3639
+ arc: number;
3640
+ promissor: string;
3641
+ significator: string;
3642
+ }[] | undefined;
3325
3643
  }, {
3326
3644
  natal_utc: string;
3327
3645
  key: "ptolemy" | "naibod";
@@ -3332,6 +3650,272 @@ export declare const OUTPUT_SCHEMAS: {
3332
3650
  angle: "MC" | "IC" | "ASC" | "DSC";
3333
3651
  arc: number;
3334
3652
  }[];
3653
+ mundane?: {
3654
+ date: string;
3655
+ years: number;
3656
+ arc: number;
3657
+ promissor: string;
3658
+ significator: string;
3659
+ }[] | undefined;
3660
+ }>;
3661
+ readonly nakshatras: z.ZodObject<{
3662
+ natal_utc: z.ZodString;
3663
+ zodiac: z.ZodEnum<["tropical", "sidereal:lahiri", "sidereal:fagan_bradley", "sidereal:krishnamurti", "sidereal:raman", "sidereal:yukteshwar", "sidereal:galcent_0sag", "sidereal:true_citra"]>;
3664
+ points: z.ZodRecord<z.ZodString, z.ZodObject<{
3665
+ nakshatra: z.ZodString;
3666
+ pada: z.ZodNumber;
3667
+ lord: z.ZodString;
3668
+ deg: z.ZodNumber;
3669
+ }, "strip", z.ZodTypeAny, {
3670
+ deg: number;
3671
+ lord: string;
3672
+ nakshatra: string;
3673
+ pada: number;
3674
+ }, {
3675
+ deg: number;
3676
+ lord: string;
3677
+ nakshatra: string;
3678
+ pada: number;
3679
+ }>>;
3680
+ }, "strip", z.ZodTypeAny, {
3681
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
3682
+ natal_utc: string;
3683
+ points: Record<string, {
3684
+ deg: number;
3685
+ lord: string;
3686
+ nakshatra: string;
3687
+ pada: number;
3688
+ }>;
3689
+ }, {
3690
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
3691
+ natal_utc: string;
3692
+ points: Record<string, {
3693
+ deg: number;
3694
+ lord: string;
3695
+ nakshatra: string;
3696
+ pada: number;
3697
+ }>;
3698
+ }>;
3699
+ readonly dasha: z.ZodObject<{
3700
+ natal_utc: z.ZodString;
3701
+ system: z.ZodEnum<["vimshottari", "yogini", "ashtottari"]>;
3702
+ moon_nakshatra: z.ZodString;
3703
+ start_lord: z.ZodOptional<z.ZodString>;
3704
+ start_yogini: z.ZodOptional<z.ZodString>;
3705
+ balance_years: z.ZodNumber;
3706
+ periods: z.ZodArray<z.ZodObject<{
3707
+ lord: z.ZodOptional<z.ZodString>;
3708
+ yogini: z.ZodOptional<z.ZodString>;
3709
+ start: z.ZodString;
3710
+ end: z.ZodString;
3711
+ sub: z.ZodOptional<z.ZodArray<z.ZodObject<{
3712
+ lord: z.ZodOptional<z.ZodString>;
3713
+ yogini: z.ZodOptional<z.ZodString>;
3714
+ start: z.ZodString;
3715
+ end: z.ZodString;
3716
+ }, "strip", z.ZodTypeAny, {
3717
+ start: string;
3718
+ end: string;
3719
+ lord?: string | undefined;
3720
+ yogini?: string | undefined;
3721
+ }, {
3722
+ start: string;
3723
+ end: string;
3724
+ lord?: string | undefined;
3725
+ yogini?: string | undefined;
3726
+ }>, "many">>;
3727
+ }, "strip", z.ZodTypeAny, {
3728
+ start: string;
3729
+ end: string;
3730
+ lord?: string | undefined;
3731
+ sub?: {
3732
+ start: string;
3733
+ end: string;
3734
+ lord?: string | undefined;
3735
+ yogini?: string | undefined;
3736
+ }[] | undefined;
3737
+ yogini?: string | undefined;
3738
+ }, {
3739
+ start: string;
3740
+ end: string;
3741
+ lord?: string | undefined;
3742
+ sub?: {
3743
+ start: string;
3744
+ end: string;
3745
+ lord?: string | undefined;
3746
+ yogini?: string | undefined;
3747
+ }[] | undefined;
3748
+ yogini?: string | undefined;
3749
+ }>, "many">;
3750
+ active: z.ZodOptional<z.ZodObject<{
3751
+ target_utc: z.ZodString;
3752
+ maha: z.ZodNullable<z.ZodString>;
3753
+ antar: z.ZodNullable<z.ZodString>;
3754
+ pratyantar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
3755
+ }, "strip", z.ZodTypeAny, {
3756
+ target_utc: string;
3757
+ maha: string | null;
3758
+ antar: string | null;
3759
+ pratyantar?: string | null | undefined;
3760
+ }, {
3761
+ target_utc: string;
3762
+ maha: string | null;
3763
+ antar: string | null;
3764
+ pratyantar?: string | null | undefined;
3765
+ }>>;
3766
+ }, "strip", z.ZodTypeAny, {
3767
+ natal_utc: string;
3768
+ periods: {
3769
+ start: string;
3770
+ end: string;
3771
+ lord?: string | undefined;
3772
+ sub?: {
3773
+ start: string;
3774
+ end: string;
3775
+ lord?: string | undefined;
3776
+ yogini?: string | undefined;
3777
+ }[] | undefined;
3778
+ yogini?: string | undefined;
3779
+ }[];
3780
+ system: "yogini" | "vimshottari" | "ashtottari";
3781
+ moon_nakshatra: string;
3782
+ balance_years: number;
3783
+ active?: {
3784
+ target_utc: string;
3785
+ maha: string | null;
3786
+ antar: string | null;
3787
+ pratyantar?: string | null | undefined;
3788
+ } | undefined;
3789
+ start_lord?: string | undefined;
3790
+ start_yogini?: string | undefined;
3791
+ }, {
3792
+ natal_utc: string;
3793
+ periods: {
3794
+ start: string;
3795
+ end: string;
3796
+ lord?: string | undefined;
3797
+ sub?: {
3798
+ start: string;
3799
+ end: string;
3800
+ lord?: string | undefined;
3801
+ yogini?: string | undefined;
3802
+ }[] | undefined;
3803
+ yogini?: string | undefined;
3804
+ }[];
3805
+ system: "yogini" | "vimshottari" | "ashtottari";
3806
+ moon_nakshatra: string;
3807
+ balance_years: number;
3808
+ active?: {
3809
+ target_utc: string;
3810
+ maha: string | null;
3811
+ antar: string | null;
3812
+ pratyantar?: string | null | undefined;
3813
+ } | undefined;
3814
+ start_lord?: string | undefined;
3815
+ start_yogini?: string | undefined;
3816
+ }>;
3817
+ readonly vargas: z.ZodObject<{
3818
+ natal_utc: z.ZodString;
3819
+ zodiac: z.ZodEnum<["tropical", "sidereal:lahiri", "sidereal:fagan_bradley", "sidereal:krishnamurti", "sidereal:raman", "sidereal:yukteshwar", "sidereal:galcent_0sag", "sidereal:true_citra"]>;
3820
+ charts: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
3821
+ sign: z.ZodString;
3822
+ sign_index: z.ZodNumber;
3823
+ division: z.ZodNumber;
3824
+ }, "strip", z.ZodTypeAny, {
3825
+ sign: string;
3826
+ sign_index: number;
3827
+ division: number;
3828
+ }, {
3829
+ sign: string;
3830
+ sign_index: number;
3831
+ division: number;
3832
+ }>>>;
3833
+ }, "strip", z.ZodTypeAny, {
3834
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
3835
+ natal_utc: string;
3836
+ charts: Record<string, Record<string, {
3837
+ sign: string;
3838
+ sign_index: number;
3839
+ division: number;
3840
+ }>>;
3841
+ }, {
3842
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
3843
+ natal_utc: string;
3844
+ charts: Record<string, Record<string, {
3845
+ sign: string;
3846
+ sign_index: number;
3847
+ division: number;
3848
+ }>>;
3849
+ }>;
3850
+ readonly yogas: z.ZodObject<{
3851
+ natal_utc: z.ZodString;
3852
+ zodiac: z.ZodEnum<["tropical", "sidereal:lahiri", "sidereal:fagan_bradley", "sidereal:krishnamurti", "sidereal:raman", "sidereal:yukteshwar", "sidereal:galcent_0sag", "sidereal:true_citra"]>;
3853
+ yogas: z.ZodArray<z.ZodObject<{
3854
+ yoga: z.ZodString;
3855
+ planets: z.ZodArray<z.ZodString, "many">;
3856
+ }, "strip", z.ZodTypeAny, {
3857
+ yoga: string;
3858
+ planets: string[];
3859
+ }, {
3860
+ yoga: string;
3861
+ planets: string[];
3862
+ }>, "many">;
3863
+ kemadruma: z.ZodBoolean;
3864
+ raja_yogas: z.ZodArray<z.ZodObject<{
3865
+ lords: z.ZodArray<z.ZodString, "many">;
3866
+ via: z.ZodString;
3867
+ }, "strip", z.ZodTypeAny, {
3868
+ lords: string[];
3869
+ via: string;
3870
+ }, {
3871
+ lords: string[];
3872
+ via: string;
3873
+ }>, "many">;
3874
+ dhana_yogas: z.ZodArray<z.ZodObject<{
3875
+ lords: z.ZodArray<z.ZodString, "many">;
3876
+ via: z.ZodString;
3877
+ }, "strip", z.ZodTypeAny, {
3878
+ lords: string[];
3879
+ via: string;
3880
+ }, {
3881
+ lords: string[];
3882
+ via: string;
3883
+ }>, "many">;
3884
+ yogakarakas: z.ZodArray<z.ZodString, "many">;
3885
+ }, "strip", z.ZodTypeAny, {
3886
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
3887
+ natal_utc: string;
3888
+ yogas: {
3889
+ yoga: string;
3890
+ planets: string[];
3891
+ }[];
3892
+ kemadruma: boolean;
3893
+ raja_yogas: {
3894
+ lords: string[];
3895
+ via: string;
3896
+ }[];
3897
+ dhana_yogas: {
3898
+ lords: string[];
3899
+ via: string;
3900
+ }[];
3901
+ yogakarakas: string[];
3902
+ }, {
3903
+ zodiac: "tropical" | "sidereal:lahiri" | "sidereal:fagan_bradley" | "sidereal:krishnamurti" | "sidereal:raman" | "sidereal:yukteshwar" | "sidereal:galcent_0sag" | "sidereal:true_citra";
3904
+ natal_utc: string;
3905
+ yogas: {
3906
+ yoga: string;
3907
+ planets: string[];
3908
+ }[];
3909
+ kemadruma: boolean;
3910
+ raja_yogas: {
3911
+ lords: string[];
3912
+ via: string;
3913
+ }[];
3914
+ dhana_yogas: {
3915
+ lords: string[];
3916
+ via: string;
3917
+ }[];
3918
+ yogakarakas: string[];
3335
3919
  }>;
3336
3920
  };
3337
3921
  export interface BuildServerOptions {
@@ -3,7 +3,7 @@
3
3
  * caelus-mcp -- MCP server for the caelus ephemeris engine.
4
4
  *
5
5
  * Design (per 2026 MCP practice): one bounded context (chart computation),
6
- * a small curated tool surface (eighteen outcome-level tools, not API wrappers),
6
+ * a small curated tool surface (twenty-two outcome-level tools, not API wrappers),
7
7
  * and token-frugal outputs (positions to 0.01 deg, terse keys, no prose --
8
8
  * the model does the interpreting, the server does the math).
9
9
  *
@@ -17,7 +17,7 @@ import { dirname, join } from "node:path";
17
17
  import { fileURLToPath } from "node:url";
18
18
  import { createRequire } from "node:module";
19
19
  import { realpathSync } from "node:fs";
20
- import { Engine, BODIES, julianDay, mod, riseSet, crossings, lunarPhases, stations, lunarEclipses, solarEclipses, ASPECTS, DEFAULT_ORBS, SIGNS as SIGN_NAMES, dignities, solarPhase, aspectPhase, planetaryHour, voidOfCourse, CAZIMI_DEG, COMBUST_DEG, UNDER_BEAMS_DEG, solarReturn, lunarReturn, progressedLongitude, directedLongitude, solarArc, progressedJd, compositeLongitudes, davisonParams, midpointLon, dignityOf, isDayChart, planetarySect, inSect, lots, HERMETIC_LOTS, profectionAt, firdaria, firdariaActive, zrRelease, zrActive, lotSpirit, lotFortune, primaryDirections, } from "caelus";
20
+ import { Engine, BODIES, julianDay, mod, riseSet, crossings, lunarPhases, stations, lunarEclipses, solarEclipses, ASPECTS, DEFAULT_ORBS, SIGNS as SIGN_NAMES, dignities, solarPhase, aspectPhase, planetaryHour, voidOfCourse, CAZIMI_DEG, COMBUST_DEG, UNDER_BEAMS_DEG, solarReturn, lunarReturn, progressedLongitude, directedLongitude, solarArc, progressedJd, compositeLongitudes, davisonParams, midpointLon, dignityOf, isDayChart, planetarySect, inSect, lots, HERMETIC_LOTS, profectionAt, firdaria, firdariaActive, zrRelease, zrActive, lotSpirit, lotFortune, primaryDirections, mundaneDirections, nakshatra, vimshottariDashas, vimshottariAt, yoginiDashas, yoginiAt, ashtottariDashas, ashtottariAt, varga, VARGA_DIVISIONS, yogasAt, kemadrumaAt, rajaYogasAt, dhanaYogasAt, } from "caelus";
21
21
  import { loadNodeData } from "caelus/node";
22
22
  const require = createRequire(import.meta.url);
23
23
  // Read our own version relative to this file (dist/src/server.js -> the package
@@ -80,6 +80,10 @@ const ZODIACS = [
80
80
  ];
81
81
  const zodiacSchema = z.enum(ZODIACS).default("tropical")
82
82
  .describe("tropical (default) or sidereal:<ayanamsa>");
83
+ // Jyotish techniques (nakshatras, vargas, dashas, yogas) are sidereal by
84
+ // definition; default these tools to Lahiri rather than tropical.
85
+ const siderealZodiac = z.enum(ZODIACS).default("sidereal:lahiri")
86
+ .describe("sidereal ayanamsa (default sidereal:lahiri); these are sidereal techniques");
83
87
  // ----------------------------------------------------- resource payloads
84
88
  // Loaded lazily on first read of the accuracy resource and memoized. Kept off
85
89
  // the module top level so importing this file into the bundled Streamable HTTP
@@ -177,6 +181,40 @@ function chartPayload(engine, iso, lat, lon, hs, zodiac = "tropical") {
177
181
  };
178
182
  }
179
183
  const text = (obj) => ({ content: [{ type: "text", text: JSON.stringify(obj) }] });
184
+ // ---------------------------------------------------------------- chart widget (MCP Apps / Apps SDK)
185
+ // natal_chart and current_sky can render the chart wheel in-host (ChatGPT and
186
+ // other MCP-UI / Apps-SDK hosts). The widget loads a self-contained bundle
187
+ // (apps/web/widget -> /embed/chart-widget.js) directly in the host's own
188
+ // sandbox — no nested iframe — and mounts caelus-wheel from the tool's
189
+ // structuredContent. Server-side half only; see docs/mcp-app-wiring.md.
190
+ const EMBED_ORIGIN = process.env.CAELUS_EMBED_ORIGIN ?? "https://www.ephemengine.com";
191
+ const CHART_WIDGET_URI = "ui://widget/chart.html";
192
+ // The MCP Apps UI MIME type (current). Hosts only enable the UI bridge for it;
193
+ // ChatGPT additionally honours the legacy openai/* _meta aliases set below.
194
+ const CHART_WIDGET_MIME = "text/html;profile=mcp-app";
195
+ // Bind the two chart tools to the widget. Both the standard (_meta.ui.resourceUri)
196
+ // and the ChatGPT compatibility alias (openai/outputTemplate) point at the same
197
+ // resource, so the binding survives across hosts.
198
+ const CHART_TOOL_META = {
199
+ ui: { resourceUri: CHART_WIDGET_URI },
200
+ "openai/outputTemplate": CHART_WIDGET_URI,
201
+ };
202
+ // The widget shell: a root element plus the bundle loaded directly from the
203
+ // embed origin (a script, not an iframe — so the CSP needs only resourceDomains,
204
+ // never the heavily-scrutinised frameDomains). The bundle reads the chart from
205
+ // the MCP Apps tool-result message / window.openai.toolOutput. `version` is a
206
+ // cache-buster so a release always loads fresh JS.
207
+ const chartWidgetHtml = (version) => `<!doctype html><meta charset="utf-8">
208
+ <style>html,body{margin:0;height:100%;background:#0e0e14}#caelus-chart-root{position:fixed;inset:0;display:grid;place-items:center;overflow:hidden}</style>
209
+ <div id="caelus-chart-root"></div>
210
+ <script src="${EMBED_ORIGIN}/embed/chart-widget.js?v=${encodeURIComponent(version)}"></script>`;
211
+ // Tool result for the two chart tools: the existing text payload (unchanged for
212
+ // non-UI clients) plus structuredContent, which UI hosts hand to the widget as
213
+ // its tool output. The two carry the same object.
214
+ const chartResult = (payload) => ({
215
+ content: [{ type: "text", text: JSON.stringify(payload) }],
216
+ structuredContent: payload,
217
+ });
180
218
  // ---------------------------------------------------------------- output schemas
181
219
  // Exported so the integration test validates responses against the same shape
182
220
  // the server promises. Kept permissive on optional keys (rx, fallback fields).
@@ -360,10 +398,74 @@ const directionOut = z.object({
360
398
  years: z.number(),
361
399
  date: z.string(),
362
400
  });
401
+ const mundaneDirectionOut = z.object({
402
+ promissor: z.string(),
403
+ significator: z.string(),
404
+ arc: z.number(),
405
+ years: z.number(),
406
+ date: z.string(),
407
+ });
363
408
  export const directionsOut = z.object({
364
409
  natal_utc: z.string(),
365
410
  key: z.enum(["ptolemy", "naibod"]),
366
411
  directions: z.array(directionOut),
412
+ mundane: z.array(mundaneDirectionOut).optional(),
413
+ });
414
+ export const nakshatrasOut = z.object({
415
+ natal_utc: z.string(),
416
+ zodiac: z.enum(ZODIACS),
417
+ points: z.record(z.string(), z.object({
418
+ nakshatra: z.string(),
419
+ pada: z.number().int().min(1).max(4),
420
+ lord: z.string(),
421
+ deg: z.number(),
422
+ })),
423
+ });
424
+ const dashaSubOut = z.object({
425
+ lord: z.string().optional(),
426
+ yogini: z.string().optional(),
427
+ start: z.string(),
428
+ end: z.string(),
429
+ });
430
+ export const dashaOut = z.object({
431
+ natal_utc: z.string(),
432
+ system: z.enum(["vimshottari", "yogini", "ashtottari"]),
433
+ moon_nakshatra: z.string(),
434
+ start_lord: z.string().optional(),
435
+ start_yogini: z.string().optional(),
436
+ balance_years: z.number(),
437
+ periods: z.array(z.object({
438
+ lord: z.string().optional(),
439
+ yogini: z.string().optional(),
440
+ start: z.string(),
441
+ end: z.string(),
442
+ sub: z.array(dashaSubOut).optional(),
443
+ })),
444
+ active: z.object({
445
+ target_utc: z.string(),
446
+ maha: z.string().nullable(),
447
+ antar: z.string().nullable(),
448
+ pratyantar: z.string().nullable().optional(),
449
+ }).optional(),
450
+ });
451
+ export const vargasOut = z.object({
452
+ natal_utc: z.string(),
453
+ zodiac: z.enum(ZODIACS),
454
+ charts: z.record(z.string(), z.record(z.string(), z.object({
455
+ sign: z.string(),
456
+ sign_index: z.number().int().min(0).max(11),
457
+ division: z.number().int(),
458
+ }))),
459
+ });
460
+ const lordPairOut = z.object({ lords: z.array(z.string()), via: z.string() });
461
+ export const yogasOut = z.object({
462
+ natal_utc: z.string(),
463
+ zodiac: z.enum(ZODIACS),
464
+ yogas: z.array(z.object({ yoga: z.string(), planets: z.array(z.string()) })),
465
+ kemadruma: z.boolean(),
466
+ raja_yogas: z.array(lordPairOut),
467
+ dhana_yogas: z.array(lordPairOut),
468
+ yogakarakas: z.array(z.string()),
367
469
  });
368
470
  export const OUTPUT_SCHEMAS = {
369
471
  natal_chart: chartOut,
@@ -384,13 +486,19 @@ export const OUTPUT_SCHEMAS = {
384
486
  firdaria: firdariaOut,
385
487
  releasing: releasingOut,
386
488
  directions: directionsOut,
489
+ nakshatras: nakshatrasOut,
490
+ dasha: dashaOut,
491
+ vargas: vargasOut,
492
+ yogas: yogasOut,
387
493
  };
388
494
  export function buildServer(engine = defaultEngine(), opts = {}) {
389
- const server = new McpServer({ name: "caelus", version: opts.version ?? VERSION });
495
+ const version = opts.version ?? VERSION;
496
+ const server = new McpServer({ name: "caelus", version });
390
497
  server.registerTool("natal_chart", {
391
498
  description: "A person's birth chart. Requires their exact birth date+time and birthplace (all three: date, lat, lon). Use this — not current_sky — whenever the question is about someone's natal/birth chart. Returns 13 bodies (sun–pluto, chiron, nodes) with sign, house, retrograde, speed; ASC/MC; cusps; major aspects with orbs. Vs Swiss Ephemeris (1900–2099): Sun–Saturn ≤1″, Uranus ≤1.9″, Neptune ≤4.6″, Moon ≤2.5″, Pluto ≤2.5″ (series valid 1885–2099), Chiron ≤1″, mean node ≤1″, true node ≤ 1′ vs SE's built-in ephemeris.",
392
499
  inputSchema: { ...birth, house_system: houseSys, zodiac: zodiacSchema },
393
- }, async ({ date, lat, lon, house_system, zodiac }) => text(chartPayload(engine, date, lat, lon, house_system, zodiac)));
500
+ _meta: CHART_TOOL_META,
501
+ }, async ({ date, lat, lon, house_system, zodiac }) => chartResult(chartPayload(engine, date, lat, lon, house_system, zodiac)));
394
502
  server.registerTool("current_sky", {
395
503
  description: "The sky at a moment and place — not tied to any person. Use for \"what's the sky/transits right now\" or the chart of a non-birth event. Date defaults to now; lat/lon default to 0,0 (geocentric on the equator at the prime meridian), where houses and ASC/MC are nominal — pass a real location if houses matter. For a specific person's birth chart use natal_chart instead. Returns positions, houses, retrogrades, aspects.",
396
504
  inputSchema: {
@@ -400,7 +508,8 @@ export function buildServer(engine = defaultEngine(), opts = {}) {
400
508
  house_system: houseSys,
401
509
  zodiac: zodiacSchema,
402
510
  },
403
- }, async ({ date, lat, lon, house_system, zodiac }) => text(chartPayload(engine, date ?? new Date().toISOString(), lat, lon, house_system, zodiac)));
511
+ _meta: CHART_TOOL_META,
512
+ }, async ({ date, lat, lon, house_system, zodiac }) => chartResult(chartPayload(engine, date ?? new Date().toISOString(), lat, lon, house_system, zodiac)));
404
513
  server.registerTool("transits", {
405
514
  description: "Transiting planets vs natal chart: aspects within orb (applying/separating), natal house per transiting body.",
406
515
  inputSchema: {
@@ -891,16 +1000,17 @@ export function buildServer(engine = defaultEngine(), opts = {}) {
891
1000
  return text(payload);
892
1001
  });
893
1002
  server.registerTool("directions", {
894
- description: "Primary (mundane) directions of the seven traditional planets to the four angles (MC, IC, Ascendant, Descendant). The diurnal rotation carries each body to an angle; the arc of rotation, converted by a time key (Naibod 0.9856473°/yr by default, or Ptolemy 1°/yr), gives the age of the direction. Returns the directions within max_years, sorted by age, each with its arc, age in years, and UTC date. Circumpolar bodies have no Ascendant/Descendant directions. Needs the birth time and place; equatorial, so zodiac is irrelevant.",
1003
+ description: "Primary (mundane) directions of the seven traditional planets to the four angles (MC, IC, Ascendant, Descendant), and optionally between the planets themselves. The diurnal rotation carries a body to the angle (or a promissor to a significator); the arc of rotation, converted by a time key (Naibod 0.9856473°/yr by default, or Ptolemy 1°/yr), gives the age of the direction. Returns the directions within max_years, sorted by age, each with its arc, age in years, and UTC date. With include_mundane, also returns the planet-to-planet (promissor → significator) directions. Circumpolar bodies have no Ascendant/Descendant directions. Needs the birth time and place; equatorial, so zodiac is irrelevant.",
895
1004
  inputSchema: {
896
1005
  ...birth,
897
1006
  key: z.enum(["naibod", "ptolemy"]).optional().describe("time key: naibod (0.9856473°/yr, default) or ptolemy (1°/yr)"),
898
1007
  max_years: z.number().positive().optional().describe("only directions reached within this many years of life (default 90)"),
1008
+ include_mundane: z.boolean().optional().describe("also return inter-planetary (promissor → significator) directions (default false)"),
899
1009
  },
900
- }, async ({ date, lat, lon, key = "naibod", max_years = 90 }) => {
1010
+ }, async ({ date, lat, lon, key = "naibod", max_years = 90, include_mundane = false }) => {
901
1011
  const natalJd = jdFromIso(date);
902
1012
  const dirs = primaryDirections(engine, natalJd, lat, lon, undefined, key, max_years);
903
- return text({
1013
+ const payload = {
904
1014
  natal_utc: date,
905
1015
  key,
906
1016
  directions: dirs.map((d) => ({
@@ -910,9 +1020,167 @@ export function buildServer(engine = defaultEngine(), opts = {}) {
910
1020
  years: r2(d.years),
911
1021
  date: isoFromJd(d.jd),
912
1022
  })),
1023
+ };
1024
+ if (include_mundane) {
1025
+ const mundane = mundaneDirections(engine, natalJd, lat, lon, undefined, key, max_years);
1026
+ payload.mundane = mundane.map((d) => ({
1027
+ promissor: d.promissor,
1028
+ significator: d.significator,
1029
+ arc: r2(d.arc),
1030
+ years: r2(d.years),
1031
+ date: isoFromJd(d.jd),
1032
+ }));
1033
+ }
1034
+ return text(payload);
1035
+ });
1036
+ server.registerTool("nakshatras", {
1037
+ description: "The nakshatra (one of the 27 lunar mansions of 13°20′) of each classical point on the sidereal zodiac: the seven traditional planets and the Ascendant (lagna). Per point: the nakshatra name, its pada (quarter, 1–4), the ruling planet (the Vimshottari lord), and degrees into the nakshatra. The Moon's nakshatra (janma nakshatra) anchors the Vimshottari dasha. Sidereal by definition; Lahiri ayanamsa by default. Needs the birth time and place for the Ascendant.",
1038
+ inputSchema: { ...birth, zodiac: siderealZodiac },
1039
+ }, async ({ date, lat, lon, zodiac }) => {
1040
+ const natalJd = jdFromIso(date);
1041
+ const chart = engine.chartAt(natalJd, lat, lon, { zodiac });
1042
+ const desc = (lonDeg) => {
1043
+ const n = nakshatra(lonDeg);
1044
+ return { nakshatra: n.name, pada: n.pada, lord: n.lord, deg: r2(n.pos) };
1045
+ };
1046
+ const points = {};
1047
+ for (const b of TRADITIONAL)
1048
+ points[b] = desc(chart.bodies[b].lon);
1049
+ points.asc = desc(chart.angles.asc);
1050
+ return text({ natal_utc: date, zodiac, points });
1051
+ });
1052
+ server.registerTool("dasha", {
1053
+ description: "Vedic dasha periods — planetary time-lord cycles started from the Moon's birth nakshatra. system selects Vimshottari (120-year, the standard), Yogini (36-year, eight yoginis), or Ashtottari (108-year). Returns the period timeline (mahadasha → antardasha) with UTC start/end, the balance of the first period at birth, and — when target_date is given — the lords active then (Vimshottari also gives the pratyantardasha). Sidereal; Lahiri by default. Needs the birth time and place.",
1054
+ inputSchema: {
1055
+ ...birth,
1056
+ system: z.enum(["vimshottari", "yogini", "ashtottari"]).default("vimshottari")
1057
+ .describe("dasha system: vimshottari (120y), yogini (36y), or ashtottari (108y)"),
1058
+ target_date: z.string().optional().describe("UTC ISO date to read the active lords for; omit for the timeline only"),
1059
+ levels: z.number().int().min(1).max(2).optional().describe("deepest timeline level: 1 (maha) or 2 (maha+antar, default)"),
1060
+ zodiac: siderealZodiac,
1061
+ },
1062
+ }, async ({ date, lat, lon, system, target_date, levels = 2, zodiac }) => {
1063
+ const natalJd = jdFromIso(date);
1064
+ const moonLon = engine.longitude("moon", natalJd, { zodiac });
1065
+ const moonNak = nakshatra(moonLon).name;
1066
+ const targetJd = target_date !== undefined ? jdFromIso(target_date) : undefined;
1067
+ const isoSub = (s) => ({ lord: s.lord, start: isoFromJd(s.start), end: isoFromJd(s.end) });
1068
+ switch (system) {
1069
+ case "vimshottari": {
1070
+ const tl = vimshottariDashas(moonLon, natalJd, levels);
1071
+ const periods = tl.dashas.map((d) => ({
1072
+ lord: d.lord, start: isoFromJd(d.start), end: isoFromJd(d.end),
1073
+ ...(levels >= 2 ? { sub: d.sub.map(isoSub) } : {}),
1074
+ }));
1075
+ const payload = {
1076
+ natal_utc: date, system, moon_nakshatra: moonNak,
1077
+ start_lord: tl.start_lord, balance_years: r2(tl.balance_years), periods,
1078
+ };
1079
+ if (targetJd !== undefined) {
1080
+ const a = vimshottariAt(engine, natalJd, targetJd, zodiac);
1081
+ payload.active = { target_utc: target_date, maha: a.maha ?? null, antar: a.antar ?? null, pratyantar: a.pratyantar ?? null };
1082
+ }
1083
+ return text(payload);
1084
+ }
1085
+ case "yogini": {
1086
+ const tl = yoginiDashas(moonLon, natalJd, levels);
1087
+ const periods = tl.dashas.map((d) => ({
1088
+ yogini: d.yogini, lord: d.lord, start: isoFromJd(d.start), end: isoFromJd(d.end),
1089
+ ...(levels >= 2 ? { sub: d.sub.map((s) => ({ yogini: s.yogini, lord: s.lord, start: isoFromJd(s.start), end: isoFromJd(s.end) })) } : {}),
1090
+ }));
1091
+ const payload = {
1092
+ natal_utc: date, system, moon_nakshatra: moonNak,
1093
+ start_yogini: tl.start_yogini, balance_years: r2(tl.balance_years), periods,
1094
+ };
1095
+ if (targetJd !== undefined) {
1096
+ const a = yoginiAt(engine, natalJd, targetJd, zodiac);
1097
+ payload.active = { target_utc: target_date, maha: a.maha ?? null, antar: a.antar ?? null };
1098
+ }
1099
+ return text(payload);
1100
+ }
1101
+ case "ashtottari": {
1102
+ const tl = ashtottariDashas(moonLon, natalJd, levels);
1103
+ const periods = tl.dashas.map((d) => ({
1104
+ lord: d.lord, start: isoFromJd(d.start), end: isoFromJd(d.end),
1105
+ ...(levels >= 2 ? { sub: d.sub.map(isoSub) } : {}),
1106
+ }));
1107
+ const payload = {
1108
+ natal_utc: date, system, moon_nakshatra: moonNak,
1109
+ start_lord: tl.start_lord, balance_years: r2(tl.balance_years), periods,
1110
+ };
1111
+ if (targetJd !== undefined) {
1112
+ const a = ashtottariAt(engine, natalJd, targetJd, zodiac);
1113
+ payload.active = { target_utc: target_date, maha: a.maha ?? null, antar: a.antar ?? null };
1114
+ }
1115
+ return text(payload);
1116
+ }
1117
+ default: {
1118
+ const _exhaustive = system;
1119
+ throw new Error(`unknown dasha system: ${String(_exhaustive)}`);
1120
+ }
1121
+ }
1122
+ });
1123
+ server.registerTool("vargas", {
1124
+ description: "Parashari divisional charts (vargas): the sign of each of the seven planets and the Ascendant in the requested D-charts — D1 rasi, D2 hora, D3 drekkana, D9 navamsa, D10 dasamsa, D12 dwadasamsa, D30 trimsamsa. Per point in each chart: the divisional sign and the division number within the rasi. The navamsa (D9) is the most consulted after the rasi. Sidereal; Lahiri by default. Needs the birth time and place for the Ascendant.",
1125
+ inputSchema: {
1126
+ ...birth,
1127
+ divisions: z.array(z.number().int().refine((n) => VARGA_DIVISIONS.includes(n), "unsupported division"))
1128
+ .optional().describe(`subset of ${VARGA_DIVISIONS.join("/")} (default all)`),
1129
+ zodiac: siderealZodiac,
1130
+ },
1131
+ }, async ({ date, lat, lon, divisions, zodiac }) => {
1132
+ const natalJd = jdFromIso(date);
1133
+ const chart = engine.chartAt(natalJd, lat, lon, { zodiac });
1134
+ const ns = divisions && divisions.length ? divisions : VARGA_DIVISIONS;
1135
+ const lons = { asc: chart.angles.asc };
1136
+ for (const b of TRADITIONAL)
1137
+ lons[b] = chart.bodies[b].lon;
1138
+ const charts = {};
1139
+ for (const n of ns) {
1140
+ const c = {};
1141
+ for (const [name, lonDeg] of Object.entries(lons)) {
1142
+ const v = varga(lonDeg, n);
1143
+ c[name] = { sign: v.sign, sign_index: v.sign_index, division: v.division };
1144
+ }
1145
+ charts[`D${n}`] = c;
1146
+ }
1147
+ return text({ natal_utc: date, zodiac, charts });
1148
+ });
1149
+ server.registerTool("yogas", {
1150
+ description: "Vedic yogas (planetary combinations) on the sidereal rasi chart: the five Pancha Mahapurusha yogas (Ruchaka, Bhadra, Hamsa, Malavya, Shasha), Gajakesari, Budha-Aditya, and Chandra-Mangala; whether Kemadruma (the isolated-Moon yoga) is present; the raja yogas (a kendra lord associating with a trikona lord) and dhana (wealth) yogas, each as the lord pair and how they associate (conjunction, aspect, or exchange); and the chart's yogakarakas (a planet ruling both a kendra and a trikona). Sidereal; Lahiri by default. Needs the birth time and place.",
1151
+ inputSchema: { ...birth, zodiac: siderealZodiac },
1152
+ }, async ({ date, lat, lon, zodiac }) => {
1153
+ const natalJd = jdFromIso(date);
1154
+ const placement = yogasAt(engine, natalJd, lat, lon, zodiac);
1155
+ const kema = kemadrumaAt(engine, natalJd, lat, lon, false, false, zodiac);
1156
+ const { raja, yogakarakas } = rajaYogasAt(engine, natalJd, lat, lon, zodiac);
1157
+ const dhana = dhanaYogasAt(engine, natalJd, lat, lon, zodiac);
1158
+ return text({
1159
+ natal_utc: date, zodiac,
1160
+ yogas: placement,
1161
+ kemadruma: kema.present,
1162
+ raja_yogas: raja,
1163
+ dhana_yogas: dhana,
1164
+ yogakarakas,
913
1165
  });
914
1166
  });
915
1167
  // --------------------------------------------------------- resources
1168
+ // Chart wheel widget (MCP Apps / Apps SDK). The CSP allowlists the embed
1169
+ // origin only for loading the widget script (resourceDomains) — no
1170
+ // frameDomains, since the bundle renders directly in the host sandbox rather
1171
+ // than nesting an iframe. The legacy openai/widgetCSP mirror keeps older
1172
+ // ChatGPT happy.
1173
+ server.registerResource("chart-widget", CHART_WIDGET_URI, {
1174
+ title: "Chart wheel",
1175
+ description: "Renders the natal_chart / current_sky payload as a caelus-wheel chart.",
1176
+ mimeType: CHART_WIDGET_MIME,
1177
+ _meta: {
1178
+ ui: { csp: { connectDomains: [], resourceDomains: [EMBED_ORIGIN] } },
1179
+ "openai/widgetCSP": { connect_domains: [], resource_domains: [EMBED_ORIGIN] },
1180
+ },
1181
+ }, async (uri) => ({
1182
+ contents: [{ uri: uri.href, mimeType: CHART_WIDGET_MIME, text: chartWidgetHtml(version) }],
1183
+ }));
916
1184
  server.registerResource("accuracy", "caelus://accuracy", {
917
1185
  title: "Validation table",
918
1186
  description: "Per-body accuracy: vs Swiss Ephemeris (swiss) and JPL Horizons apparent positions (jpl).",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caelus-mcp",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "mcpName": "io.github.heavyblotto/caelus-mcp",
5
5
  "description": "MCP server for caelus chart computation.",
6
6
  "type": "module",
@@ -15,12 +15,16 @@
15
15
  "dependencies": {
16
16
  "@modelcontextprotocol/sdk": "^1.26.0",
17
17
  "zod": "^3.24.0",
18
- "caelus": "^0.13.0"
18
+ "caelus": "^0.14.0"
19
19
  },
20
20
  "devDependencies": {
21
21
  "ajv": "^8.17.1"
22
22
  },
23
23
  "license": "MIT",
24
+ "publishConfig": {
25
+ "access": "public",
26
+ "provenance": true
27
+ },
24
28
  "files": [
25
29
  "dist/src"
26
30
  ],