Ver Fonte

feat(garden): optional cycle_uri + expose garden tool schemas

Lukas Goldschmidt há 1 mês atrás
pai
commit
8f650695f1
2 ficheiros alterados com 65 adições e 4 exclusões
  1. 51 1
      src/garden_layer/__init__.py
  2. 14 3
      src/garden_layer/domain_tools.py

+ 51 - 1
src/garden_layer/__init__.py

@@ -10,7 +10,10 @@ def _make_tool(func: Callable[..., object]) -> Callable[[Dict[str, object]], obj
     return lambda input_data: func(**input_data)
 
 
-def register_layer(tools: Dict[str, Callable[[Dict[str, object]], object]]) -> None:
+def register_layer(
+    tools: Dict[str, Callable[[Dict[str, object]], object]],
+    tool_schemas: Dict[str, object] = None,
+) -> None:
     garden = GardenLayer()
     tools["garden_add_seedling"] = _make_tool(garden.add_seedling)
     tools["garden_describe_subject"] = _make_tool(garden.describe_subject)
@@ -24,3 +27,50 @@ def register_layer(tools: Dict[str, Callable[[Dict[str, object]], object]]) -> N
     tools["garden_load_examples"] = _make_tool(load_examples)
 
     tools["garden_reassign_cycle"] = _make_tool(reassign_cycle)
+
+    # Register MCP tool input schemas (best-effort). The MCP server will
+    # expose these during initialize/tools/list.
+    if tool_schemas is None:
+        return
+
+    # Common schema helper
+    def _obj_schema(props: dict, required: list[str] = None) -> dict:
+        return {
+            "type": "object",
+            "additionalProperties": True,
+            "properties": props,
+            "required": required or [],
+        }
+
+    tool_schemas["garden_latest_cycle_by_dates"] = _obj_schema(
+        {
+            "limit": {"type": "integer", "minimum": 1, "maximum": 500},
+        },
+        [],
+    )
+
+    tool_schemas["garden_cycle_plants"] = _obj_schema(
+        {
+            "cycle_uri": {"type": "string", "description": "ProductionCycle URI (optional; defaults to latest)"},
+            "limit": {"type": "integer", "minimum": 1, "maximum": 500},
+        },
+        [],
+    )
+
+    tool_schemas["garden_cycle_list_detailed"] = _obj_schema(
+        {
+            "target_cycle_uri": {"type": "string", "description": "ProductionCycle URI (optional; defaults to latest by dates)"},
+            "limit": {"type": "integer", "minimum": 1, "maximum": 500},
+        },
+        [],
+    )
+
+    tool_schemas["garden_clone_to"] = _obj_schema(
+        {
+            "mother_label": {"type": "string"},
+            "new_label": {"type": "string"},
+            "target_cycle_uri": {"type": "string", "description": "Optional override for cb:inCycle"},
+            "limit": {"type": "integer", "description": "Optional cap on copied triples"},
+        },
+        ["mother_label", "new_label"],
+    )

+ 14 - 3
src/garden_layer/domain_tools.py

@@ -27,10 +27,21 @@ def _safe_limit(value: Any, default: int = 50) -> int:
     return min(max(parsed, 1), SPARQL_MAX_LIMIT)
 
 
-def cycle_plants(cycle_uri: str, limit: Optional[int] = 50) -> Dict[str, Any]:
-    if not cycle_uri:
-        raise ValueError("Missing 'cycle_uri' field")
+def cycle_plants(cycle_uri: Optional[str] = None, limit: Optional[int] = 50) -> Dict[str, Any]:
+    """List plants in a cycle.
+
+    If cycle_uri is omitted, it resolves to the latest cycle by
+    productionStartDate/productionEndDate.
+    """
     bounded_limit = _safe_limit(limit)
+
+    if not cycle_uri:
+        latest = latest_cycle_by_dates(limit=1)
+        bindings = latest.get("results", {}).get("bindings", [])
+        if not bindings:
+            raise ValueError("Could not resolve latest cycle")
+        cycle_uri = bindings[0]["cycle"]["value"]
+
     query = f"""
     SELECT ?plant ?plantLabel ?parent WHERE {{
         ?plant <{IN_CYCLE}> <{cycle_uri}> .