server.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. from __future__ import annotations
  2. import logging
  3. from pathlib import Path
  4. from fastapi import FastAPI
  5. from fastapi.staticfiles import StaticFiles
  6. from fastapi.templating import Jinja2Templates
  7. from mcp.server.fastmcp import FastMCP
  8. from mcp.server.transport_security import TransportSecuritySettings
  9. from . import config
  10. from . import __version__
  11. logger = logging.getLogger("astro-mcp")
  12. mcp = FastMCP(
  13. "astro-mcp",
  14. transport_security=TransportSecuritySettings(
  15. enable_dns_rebinding_protection=False,
  16. ),
  17. )
  18. # Import tools module to register all @mcp.tool() handlers
  19. from . import tools # noqa: E402, F401
  20. # Templates and static files
  21. TEMPLATES_DIR = Path(__file__).resolve().parent.parent.parent / "templates"
  22. STATIC_DIR = Path(__file__).resolve().parent.parent.parent / "static"
  23. GUIDES_DIR = Path(__file__).resolve().parent.parent.parent / "agent-guides"
  24. templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
  25. def _tool_names() -> list[str]:
  26. return [
  27. "get_planetary_positions",
  28. "calculate_natal_chart",
  29. "calculate_transit_chart",
  30. "calculate_synastry_chart",
  31. "calculate_composite_chart",
  32. "calculate_davison_chart",
  33. "get_transit_preview",
  34. "get_composite_transit_preview",
  35. "get_davison_transit_preview",
  36. "get_karmic_relationship_summary",
  37. "person_manage",
  38. "list_house_systems",
  39. "calculate_natal_chart_by_id",
  40. "calculate_transit_chart_by_id",
  41. "calculate_synastry_chart_by_id",
  42. "calculate_composite_chart_by_id",
  43. "calculate_davison_chart_by_id",
  44. "get_transit_preview_by_id",
  45. # Chart rendering tools
  46. "render_natal_chart",
  47. "render_natal_chart_by_id",
  48. "render_transit_chart",
  49. "render_transit_chart_by_id",
  50. "render_synastry_chart",
  51. "render_synastry_chart_by_id",
  52. "render_composite_chart",
  53. "render_composite_chart_by_id",
  54. "render_davison_chart",
  55. "render_davison_chart_by_id",
  56. ]
  57. def _read_guide(filename: str) -> str:
  58. """Read an agent guide markdown file."""
  59. path = GUIDES_DIR / filename
  60. if path.exists():
  61. return path.read_text(encoding="utf-8")
  62. return f"# Error\n\nGuide not found: {filename}"
  63. @mcp.resource("astro://guides/natal-astrology")
  64. def natal_astrology_guide() -> str:
  65. """Agent interpretation guide for natal chart analysis."""
  66. return _read_guide("natal-astrology.md")
  67. @mcp.resource("astro://guides/karmic-astrology")
  68. def karmic_astrology_guide() -> str:
  69. """Agent interpretation guide for karmic chart analysis."""
  70. return _read_guide("karmic-astrology.md")
  71. @mcp.resource("astro://guides/relationship-astrology")
  72. def relationship_astrology_guide() -> str:
  73. """Agent interpretation guide for relationship chart analysis."""
  74. return _read_guide("relationship-astrology.md")
  75. @mcp.resource("astro://guides/financial-astrology")
  76. def financial_astrology_guide() -> str:
  77. """Agent interpretation guide for financial astrology — business cycles, stock market forecasting, planetary correlations."""
  78. return _read_guide("financial-astrology.md")
  79. def create_app() -> FastAPI:
  80. config.LOG_DIR.mkdir(parents=True, exist_ok=True)
  81. logging.basicConfig(
  82. filename=str(config.LOG_DIR / "server.log"),
  83. level=logging.INFO,
  84. format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
  85. )
  86. app = FastAPI(title="astro-mcp")
  87. # Mount static files
  88. if STATIC_DIR.exists():
  89. app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
  90. # Mount MCP SSE
  91. app.mount("/mcp", mcp.sse_app())
  92. # Dashboard routes
  93. from .dashboard import router as dashboard_router # noqa: E402
  94. app.include_router(dashboard_router)
  95. @app.get("/health")
  96. def health() -> dict:
  97. return {"ok": True, "server": "astro-mcp", "version": __version__, "port": config.PORT}
  98. @app.get("/")
  99. def root() -> dict:
  100. return {
  101. "server": "astro-mcp",
  102. "version": __version__,
  103. "status": "ready",
  104. "tools": _tool_names(),
  105. "mcp": {
  106. "sse": "/mcp/sse",
  107. "messages": "/mcp/messages",
  108. },
  109. }
  110. return app