File size: 3,277 Bytes
c05050c
 
9918f43
 
 
 
c05050c
9918f43
 
c05050c
 
 
 
 
 
 
 
 
e28d52e
c05050c
 
e28d52e
c05050c
 
 
1933348
 
 
 
 
c05050c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1933348
c05050c
e28d52e
c05050c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1933348
c05050c
 
 
 
 
 
 
 
 
 
 
 
1933348
 
 
c05050c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""MCP server stub.

The Model Context Protocol (MCP) is the standard for exposing tools to
any MCP-aware client (Claude Desktop, Cursor, ChatGPT, the Anthropic
SDK, …). This module is the wrapping seam: a single entry point that
turns this project's tools into an MCP server.

The official Python SDK is the ``mcp`` package, kept as an OPTIONAL
dependency so installs without MCP support do not pay the cost.

Usage (after ``pip install mcp``)::

    python -m mcp_server          # stdio transport for Claude Desktop / Cursor

To register with Claude Desktop, add to ``~/.config/claude/desktop.json``::

    {
      "mcpServers": {
        "mealgraph": {
          "command": "python",
          "args": ["-m", "mcp_server"],
          "cwd": "/path/to/mealgraph"
        }
      }
    }

Only the two genuinely portable surfaces are exposed: the LP-backed
:class:`tools.QuantitiesFinder` and the closed-form
:func:`nutrition_formulas.full_assessment`. There is no LLM-driven
computation tool to wrap — clinical math runs directly in Python.
"""

from __future__ import annotations

import json
from typing import Any, Dict


def _build_server() -> Any:
    """Construct the MCP server lazily so the import is optional."""
    try:
        from mcp.server.fastmcp import FastMCP  # type: ignore
    except ImportError as e:
        raise SystemExit(
            "MCP support requires `pip install mcp`. The rest of the system runs without it."
        ) from e

    from nutrition_formulas import full_assessment as _full_assessment
    from tools import QuantitiesFinder

    server = FastMCP("mealgraph")
    qf = QuantitiesFinder()

    # ----- QuantitiesFinder ------------------------------------------------
    @server.tool()
    def quantities_finder(payload: Dict[str, Any]) -> str:
        """Linear-program meal-quantity solver.

        Args:
            payload: {"foods": [{name, calories, protein, fat, carbohydrates,
                                  estimated_g, [min_g, max_g, meal_group, estimate_weight]}, ...],
                      "targets": {calories, protein, fat, carbohydrates},
                      "meal_constraints": [...]}

        Returns:
            JSON {"quantities": {...}, "achieved": {...}}
        """
        return qf.handle_task(json.dumps(payload))

    # ----- Closed-form clinical assessment ---------------------------------
    @server.tool()
    def assess_user(
        weight_kg: float,
        height_cm: float,
        age_years: float,
        sex: str,
        activity_level: str,
        goal: str,
    ) -> Dict[str, Any]:
        """One-shot anthropometric + nutritional assessment.

        Returns BMI, BMR, TDEE, daily_target_calories, and macro_targets
        (protein_g, fat_g, carbohydrates_g). Deterministic closed-form
        formulas (Mifflin-St Jeor BMR, ACSM activity multipliers); no LLM
        in the path.
        """
        return _full_assessment(
            weight_kg=weight_kg,
            height_cm=height_cm,
            age_years=age_years,
            sex=sex,
            activity_level=activity_level,
            goal=goal,
        )

    return server


def main() -> None:
    server = _build_server()
    server.run()


if __name__ == "__main__":  # pragma: no cover
    main()