瀏覽代碼

fix(garden): avoid MCP self-calls for describe/property_usage_statistics

Lukas Goldschmidt 1 月之前
父節點
當前提交
bc4f05292b
共有 2 個文件被更改,包括 45 次插入3 次删除
  1. 12 3
      src/garden_layer/__init__.py
  2. 33 0
      src/garden_layer/domain_tools.py

+ 12 - 3
src/garden_layer/__init__.py

@@ -1,7 +1,16 @@
 from typing import Callable, Dict
 
 from .helpers import GardenLayer
-from .domain_tools import cycle_plants, load_examples, reassign_cycle, latest_cycle_by_dates, clone_to, cycle_list_detailed
+from .domain_tools import (
+    cycle_plants,
+    load_examples,
+    reassign_cycle,
+    latest_cycle_by_dates,
+    clone_to,
+    cycle_list_detailed,
+    describe_subject,
+    property_usage_statistics,
+)
 
 __all__ = ["GardenLayer", "register_layer"]
 
@@ -16,9 +25,9 @@ def register_layer(
 ) -> None:
     garden = GardenLayer()
     tools["garden_add_seedling"] = _make_tool(garden.add_seedling)
-    tools["garden_describe_subject"] = _make_tool(garden.describe_subject)
+    tools["garden_describe_subject"] = _make_tool(describe_subject)
     tools["garden_path_traverse"] = _make_tool(garden.path_traverse)
-    tools["garden_property_usage_statistics"] = _make_tool(garden.property_usage_statistics)
+    tools["garden_property_usage_statistics"] = _make_tool(property_usage_statistics)
     tools["garden_batch_insert"] = _make_tool(garden.batch_insert)
     tools["garden_cycle_plants"] = _make_tool(cycle_plants)
     tools["garden_latest_cycle_by_dates"] = _make_tool(latest_cycle_by_dates)

+ 33 - 0
src/garden_layer/domain_tools.py

@@ -53,6 +53,39 @@ def cycle_plants(cycle_uri: Optional[str] = None, limit: Optional[int] = 50) ->
     return run_sparql(query)
 
 
+def describe_subject(subject_uri: str, limit: Optional[int] = 10) -> Dict[str, Any]:
+    """Direct SPARQL implementation (no HTTP self-calls)."""
+    bounded_limit = _safe_limit(limit, default=10)
+    query = f"""
+    SELECT ?predicate ?object ?objectLabel WHERE {{
+        <{subject_uri}> ?predicate ?object .
+        OPTIONAL {{ ?object rdfs:label ?objectLabel }}
+    }}
+    LIMIT {bounded_limit}
+    """
+    return run_sparql(query)
+
+
+def property_usage_statistics(property_uri: str, examples_limit: Optional[int] = 5) -> Dict[str, Any]:
+    """Direct SPARQL implementation (no HTTP self-calls)."""
+    bounded = _safe_limit(examples_limit, default=5)
+    count_query = f"""
+    SELECT (COUNT(DISTINCT ?subject) AS ?usageCount) WHERE {{
+        ?subject <{property_uri}> ?object .
+    }}
+    LIMIT {SPARQL_MAX_LIMIT}
+    """
+    usage_query = f"""
+    SELECT ?subject ?subjectLabel ?object ?objectLabel WHERE {{
+        ?subject <{property_uri}> ?object .
+        OPTIONAL {{ ?subject rdfs:label ?subjectLabel }}
+        OPTIONAL {{ ?object rdfs:label ?objectLabel }}
+    }}
+    LIMIT {bounded}
+    """
+    return {"count": run_sparql(count_query), "examples": run_sparql(usage_query)}
+
+
 def latest_cycle_by_dates(limit: Optional[int] = 1) -> Dict[str, Any]:
     """Pick the latest cycle using available start/end dates.