[{'Text': '        """Execute safe read-only SQL."""\n        ...\n```\n\n- Tools only appear when required env vars are set.\n- `check_env_vars()` tracks requirements for `get_mcp_server_capabilities()`.\n- Skipped tools log: `[MCP] Skipping {name}: missing env vars [...]`.\n\n## 3. Dynamic Docstrings\n\n```python\nfrom point_topic_mcp.core.utils import dynamic_docstring\nfrom point_topic_mcp.core.context_assembly import list_datasets\n\n@dynamic_docstring([("{DATASETS}", list_datasets)])\ndef assemble_dataset_context(dataset_names: list[str], ctx=None) -> str:\n    """Full context for one or more datasets.\\n{DATASETS}"""\n```\n\nPlaceholders replaced at module load time (not at call time).\n\n## 4. Dynamic Tool Registration (Runtime)\n\n```python\nfrom point_topic_mcp.core.tool_manager import ToolManager\ntool_manager = ToolManager(mcp)\n\nasync def my_analyzer(dataset_id: str) -> dict: ...\ntool_manager.register_tool(name="analyze_dataset", description="...", function=my_analyzer)\n\n# Notify clients (call from within a tool)\nawait ctx.send_notification(mcp.types.ToolListChangedNotification())\n```\n\n## 5. Permission Middleware (Three-Tier Access)\n\n| Level | What They See |\n|-------|--------------|\n| **Unauthenticated** | Only `@public` tools (stdio: admin via ADMIN_API_KEY) |\n| **Authenticated** | `@public` + tools matching their `@requires_permissions` |\n| **ptAdmin** | All tools |\n\nStdio mode: `ADMIN_API_KEY` grants ptAdmin. HTTP mode: Auth0 JWT.\n\n```python\n# auth/decorators.py\n@public                          # Everyone\n@requires_permissions(["read:data"])  # Specific scope\n```\n\n## 6. Dataset System (Lazy Loading)\n\n```python\n# context/datasets/upc.py\ndef get_dataset_summary():\n    """Short. Always visible in agent prompt."""\n    return "UK broadband availability at postcode level."\n\ndef get_db_info():\n    """Full schema + instructions + examples. Only loaded on demand."""\n    return f"{DB_INFO}\\n\\n{DB_SCHEMA}\\n\\n{SQL_EXAMPLES}"\n```\n\n# Tool Reference\n\n## Database Tools (requires: `SNOWFLAKE_USER`, `SNOWFLAKE_PASSWORD`)\n\n| Function | Description | Key Args |\n|----------|-------------|----------|\n| `database_tools_assemble_dataset_context` | Get schema/instructions/examples | `dataset_names: list[str]` |\n| `database_tools_execute_query` | Safe SQL (SELECT/WITH/SHOW/DESCRIBE/EXPLAIN only) | `sql_query: str` |\n| `database_tools_execute_multi_query` | Multiple semicolon-separated queries | `queries: str` |\n| `database_tools_get_query_status` | Check query state | `query_id: str` |\n| `database_tools_cancel_query` | Cancel by query_id | `query_id: str` |\n| `database_tools_describe_table` | Get table schema | `table_name: str` |\n| `database_tools_get_la_code` | LA lookup | various |\n| `database_tools_get_la_list_full` | All local authorities | - |\n\n**Async execution**: Queries submit async, return `query_id:` immediately. Poll with `get_query_status`.\n\n## Chart Tools (public: always; private: `CHART_API_KEY`)\n\n| Function | Auth | Description |\n|----------|------|-------------|\n| `chart_tools_get_point_topic_public_chart_catalog` | None | Browse public charts |\n| `chart_tools_get_point_topic_public_chart_csv` | None | Get chart CSV |\n| `chart_tools_get_point_topic_chart_catalog` | Key | Private catalog (currently disabled) |\n| `chart_tools_get_point_topic_chart_csv` | Key | Private chart CSV (currently disabled) |\n| `chart_tools_generate_authenticated_chart_url` | Key | Signed URLs (currently disabled) |\n\n## GBS Tools (requires: `PT_RESEARCH_DATABASE_URI`, mongosh)\n\n| Function | Description |\n|----------|-------------|\n| `gbs_tools_list_operators` | List by country, returns Operator collection docs |\n| `gbs_tools_get_gbs_status` | Statistics status for operator (with quarter distance) |\n| `gbs_tools_add_statistic` | Insert Statistics document |\n| `gbs_tools_create_source` | Create Source record (with optional S3 archival) |\n'}]