[{'Text': 'Showing matches 1-20 (there were more matches found; use offset: 20 to see next page):\n\n## Matches in point-topic-mcp/src/point_topic_mcp/connectors/clickhouse.py\n\n### L1-11\n```\n"""ClickHouse connector for the ontology database."""\n\nimport io\nimport json\nimport os\nimport re\n\nimport clickhouse_connect\nfrom dotenv import load_dotenv\n\nload_dotenv()\n```\n\n210 lines remaining in ancestor node. Read the file to see all.\n\n### def _tls_enabled › L14-18\n```\ndef _tls_enabled(secure_raw: str | None, port: int) -> bool:\n    """Use HTTPS for clickhouse-connect when port is 443 or CLICKHOUSE_SECURE is set."""\n    if secure_raw is not None and str(secure_raw).strip() != "":\n        return str(secure_raw).strip().lower() in ("1", "true", "yes", "on")\n    return port == 443\n```\n\n### L22-26\n```\n# Credential resolution\n# Env vars take priority (local dev / MCP client config).\n# If CLICKHOUSE_HOST is absent, fall back to AWS Secrets Manager\n# (ptserver-secret) — works transparently when running on the server.\n# ---------------------------------------------------------------------------\n```\n\n### def _resolve_credentials › L28-39\n```\ndef _resolve_credentials() -> dict:\n    host = os.environ.get("CLICKHOUSE_HOST")\n    if host:\n        port = int(os.environ.get("CLICKHOUSE_PORT", "443"))\n        return {\n            "host": host,\n            "port": port,\n            "username": os.environ.get("CLICKHOUSE_USER", "default"),\n            "password": os.environ.get("CLICKHOUSE_PASSWORD", ""),\n            "database": os.environ.get("CLICKHOUSE_DATABASE", "ontology"),\n            "secure": _tls_enabled(os.environ.get("CLICKHOUSE_SECURE"), port),\n        }\n```\n\n21 lines remaining in ancestor node. Read the file to see all.\n\n### def _resolve_credentials › L42-55\n```\n        import boto3\n        secret = json.loads(\n            boto3.client("secretsmanager", region_name="eu-west-1")\n                .get_secret_value(SecretId="ptserver-secret")["SecretString"]\n        )\n        port = int(secret.get("CLICKHOUSE_PORT", "443"))\n        return {\n            "host": secret["CLICKHOUSE_HOST"],\n            "port": port,\n            "username": secret.get("CLICKHOUSE_USER", "default"),\n            "password": secret["CLICKHOUSE_PASSWORD"],\n            "database": secret.get("CLICKHOUSE_DATABASE", "ontology"),\n            "secure": _tls_enabled(secret.get("CLICKHOUSE_SECURE"), port),\n        }\n```\n\n### def _resolve_credentials › L57-60\n```\n        raise RuntimeError(\n            "ClickHouse credentials not found. Set CLICKHOUSE_HOST/CLICKHOUSE_PASSWORD "\n            f"env vars, or ensure ptserver-secret is accessible. Detail: {e}"\n        )\n```\n\n### L89-93\n```\n\n# ---------------------------------------------------------------------------\n# ClickHouseDB\n# ---------------------------------------------------------------------------\n\n```\n\n### class ClickHouseDB › L94-103\n```\nclass ClickHouseDB:\n    """\n    Thin ClickHouse wrapper for the ontology database.\n\n    Example usage:\n        db = ClickHouseDB()\n        db.connect()\n        result = db.execute_safe_query("SELECT count() FROM foaf_organization")\n        db.close_connection()\n    """\n```\n\n117 lines remaining in ancestor node. Read the file to see all.\n\n### class ClickHouseDB › def connect › L109-121\n```\n    def connect(self):\n        """Connect to ClickHouse with detailed error reporting for debugging."""\n        try:\n            self.client = clickhouse_connect.get_client(\n                host=self._creds["host"],\n                port=self._creds["port"],\n                username=self._creds["username"],\n                password=self._creds["password"],\n                database=self._creds["database"],\n                secure=self._creds["secure"],\n                connect_timeout=10,\n                send_receive_timeout=300,\n            )\n```\n\n19 lines remaining in ancestor node. Read the file to see all.\n\n### class ClickHouseDB › def connect › L128-138\n```\n            diagnostic = (\n                f"CONNECTION FAILED to ClickHouse at {protocol}://{host_port}\\n"\n                f"Database: {self._creds[\'database\']}\\n"\n                f"Username: {self._creds[\'username\']}\\n"\n                f"Secure: {self._creds[\'secure\']}\\n"\n                f"Error: {error_msg}\\n\\n"\n                f"DIAGNOSTIC CHECKS:\\n"\n                f"• If timeout: check network connectivity and firewall rules for port {self._creds[\'port\']}\\n"\n                f"• If auth error: verify CLICKHOUSE_PASSWORD and CLICKHOUSE_USER\\n"\n                f"• If TLS error: check CLICKHOUSE_SECURE setting (should match server config)\\n"\n                f"• If host not found: verify CLICKHOUSE_HOST is correct ({self._creds[\'host\']})"\n```\n\n1 lines remaining in ancestor node. Read the file to see all.\n\n### class ClickHouseDB › def execute_safe_query › L165-166\n```\n        if not self.client:\n            return "ERROR: ClickHouse client not connected. Call connect() first."\n```\n\n### class ClickHouseDB › def execute_safe_query › L176-183\n```\n                    f"DATABASE ERROR: Query execution failed\\n\\n"\n                    f"CAUSE: {error_msg}\\n\\n"\n                    f"DIAGNOSTICS:\\n"\n                    f"• Network timeout: Check if ClickHouse server is responsive\\n"\n                    f"• Connection refused: Verify server is running on {self._creds[\'host\']}:{self._creds[\'port\']}\\n"\n                    f"• Connection reset: Server may have crashed or disconnected\\n"\n                    f"• TLS error: Verify secure={self._creds[\'secure\']} matches server config\\n\\n"\n                    f"QUERY: {query[:200]}{\'...\' if len(query) > 200 else \'\'}"\n```\n\n### class ClickHouseDB › def execute_safe_queries › L196-197\n```\n        if not self.client:\n            return "ERROR: ClickHouse client not connected. Call connect() first."\n```\n\n## Matches in point-topic-mcp/src/point_topic_mcp/context/datasets/ontology.py\n\n### L36-46\n```\nDB_INFO = """\nSTRUCTURED KNOWLEDGE BASE FOR UK TELECOM MARKET.\nIMPLEMENTED IN CLICKHOUSE (COLUMNAR OLAP) FOR SPEED AND SCALABILITY.\n\nDatabase: ontology\nTables are unqualified lowercase — no schema prefix needed.\ne.g. SELECT * FROM foaf_organization  (NOT ontology.class.foaf_organization)\n\nONTOLOGY VIEWS (informational only — no separate schemas in ClickHouse):\n- core: foundational entities (~10-500 rows), enforced constraints, stable schema\n- business: core + business data (thousands-millions rows), coverage/subscriber/service data\n```\n\n34 lines remaining in ancestor node. Read the file to see all.\n\n### L71-75\n```\nSELECT DISTINCT ASPECT_NAME FROM cto_measurement_record;\n\nCOLUMN NAMES ARE UPPERCASE in ClickHouse (loaded from Snowflake exports).\nUse uppercase column names: ORGANIZATION_NAME, VALID_FROM, VALID_TO, ENTITY_NAME, etc.\n\n```\n\n## Matches in point-topic-mcp/src/point_topic_mcp/tools/database_tools.py\n\n### L1-11\n```\n"""Snowflake and ClickHouse database query tools."""\n\nimport json\nfrom typing import List, Optional\nfrom fastmcp import Context\nfrom point_topic_mcp.core.context_assembly import get_ontology_summary, list_datasets, assemble_context\nfrom point_topic_mcp.core.utils import dynamic_docstring, check_env_vars\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\n```\n\n337 lines remaining in ancestor node. Read the file to see all.\n\n### L241-252\n```\n\n\n# ── ClickHouse ontology tools ────────────────────────────────────────────────\n# Requires CLICKHOUSE_HOST + CLICKHOUSE_PASSWORD env vars,\n# OR will auto-fetch from AWS Secrets Manager (ptserver-secret) if running on\n# the server (109.169.53.90).\n\n# def _clickhouse_available() -> bool:\n#     """True if ClickHouse creds are resolvable (env vars or Secrets Manager)."""\n#     if os.environ.get("CLICKHOUSE_HOST") and os.environ.get("CLICKHOUSE_PASSWORD"):\n#         return True\n#     # Try Secrets Manager silently\n```\n\n### L257-261\n```\n#                 .get_secret_value(SecretId="ptserver-secret")["SecretString"]\n#         )\n#         return bool(secret.get("CLICKHOUSE_HOST") and secret.get("CLICKHOUSE_PASSWORD"))\n#     except Exception:\n#         return False\n```\n\n### L263-279\n```\nif False:\n# if check_env_vars(\'clickhouse_tools\', [\'CLICKHOUSE_HOST\', \'CLICKHOUSE_PASSWORD\']) or _clickhouse_available():\n    @dynamic_docstring([("SUMMARY", get_ontology_summary)])\n    def assemble_ontology_context(\n        ctx: Optional[Context] = None,\n    ) -> str:\n        """\n        Assemble the full context (instructions, schema, SQL examples) for the\n        Point Topic Ontology dataset stored in ClickHouse.\n\n        Call this before executing any ontology query so the agent understands\n        the schema, available tables, and correct SQL dialect.\n\n        Returns the complete context needed for querying the ontology.\n\n        Summary: {SUMMARY}\n        """\n```\n\n68 lines remaining in ancestor node. Read the file to see all.\n\n### def execute_ontology_query › L286-296\n```\n        """\n        Execute a read-only SQL query against the Point Topic Ontology ClickHouse database.\n\n        Only SELECT/WITH/SHOW/DESCRIBE queries are allowed.\n        Call assemble_ontology_context first to understand the schema.\n\n        The ontology database contains UK telecom market data:\n        organisations, networks, ISP-network relationships, broadband services,\n        coverage measurements, and more.\n\n        Args:\n```\n\n7 lines remaining in ancestor node. Read the file to see all.\n'}]