[{'Text': '# Point Topic MCP Server\n\nUK broadband data analysis server via Model Context Protocol. Simple stdio-based server for local development and Claude Desktop integration.\n\n## ✅ what\'s implemented\n\n**database tools** (requires Snowflake credentials):\n- `assemble_dataset_context()` - get schemas and examples for datasets (upc, upc_take_up, upc_forecast, tariffs, ontology)\n- `execute_query()` - run safe read-only SQL queries; returns `query_id` on the first line of every response\n- `get_query_status()` - check whether a query is RUNNING, SUCCESS, CANCELLED, or ERROR\n- `cancel_query()` - cancel a running query by query_id (for human-initiated cancellation via UI)\n- `describe_table()` - get table schema details\n- `get_la_code()` / `get_la_list_full()` - local authority lookups\n\n**chart tools**:\n- `get_point_topic_public_chart_catalog()` - browse public charts (no auth needed)\n- `get_point_topic_public_chart_csv()` - get public chart data as CSV (no auth needed)\n- `get_point_topic_chart_catalog()` - get complete catalog including private charts (requires API key)\n- `get_point_topic_chart_csv()` - get any chart data as CSV with authentication (requires API key)\n- `generate_authenticated_chart_url()` - create signed URLs for private charts (requires API key)\n\n**server info**:\n- `get_mcp_server_capabilities()` - check which tools are available and debug missing credentials\n\n**prompts** (reusable message templates):\n- UPC analysis: analyze coverage, adoption, forecasts, and market dynamics\n- SQL assistance: generate, debug, and optimize Snowflake queries\n\n**conditional availability**: tools only appear if required environment variables are set\n\n## installation (for end users)\n\n**option 1: pip install (recommended)**:\n\n```bash\npip install point-topic-mcp\n```\n\nif you encounter `cmake` build errors during installation (for pyarrow), install with Snowflake support explicitly:\n\n```bash\npip install "point-topic-mcp[snowflake]"\n```\n\nor provide pre-built wheels:\n\n```bash\npip install --only-binary :all: point-topic-mcp[snowflake]\n```\n\n**option 2: from source (with uv)**:\n\n```bash\ngit clone https://github.com/point-topic/point-topic-mcp.git\ncd point-topic-mcp\nuv sync\nuv run point-topic-mcp\n```\n\n**add to your MCP client** (Claude Desktop, Cursor, etc.):\n\n```json\n{\n  "mcpServers": {\n    "point-topic": {\n      "command": "point-topic-mcp",\n      "env": {\n        "SNOWFLAKE_USER": "your_user", \n        "SNOWFLAKE_PASSWORD": "your_password",\n        "CHART_API_KEY": "your_chart_api_key"\n      }\n    }\n  }\n}\n```\n\n## remote deployment (FastMCP Cloud)\n\nDeploy the MCP server remotely to FastMCP Cloud for access from any MCP client.\n\n**requirements:**\n- FastMCP Cloud account (https://fastmcp.cloud)\n- GitHub repository connected to FastMCP Cloud\n\n**deployment:**\n\n1. Sign up at https://fastmcp.cloud\n2. Connect repository: `Point-Topic/point-topic-mcp`\n3. Configure environment variables (Snowflake credentials)\n4. Deploy - FastMCP Cloud handles HTTPS, scaling, monitoring automatically\n\n**client connections:**\n\n*Claude Desktop (via mcp-remote):*\n```json\n{\n  "mcpServers": {\n    "point-topic": {\n      "command": "npx",\n      "args": ["-y", "mcp-remote", "https://your-url.fastmcp.cloud/mcp"]\n    }\n  }\n}\n```\n\n*Cursor (native remote):*\n```json\n{\n  "mcpServers": {\n    "point-topic": {\n      "url": "https://your-url.fastmcp.cloud/mcp",\n      "transport": "http"\n    }\n  }\n}\n```\n\nSee [docs/REMOTE_SERVER.md](docs/REMOTE_SERVER.md) for complete deployment guide.\n\n**note**: environment variables are optional - tools will only appear if credentials are provided. use `get_mcp_server_capabilities()` to check which tools are available.\n\n**Claude Desktop config location**:\n- Mac: `~/Library/Application Support/Claude/claude_desktop_config.json`\n- Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n## development setup\n\nsetup: `uv sync`\n\n**for local development with claude desktop**:\n\nThis will add the server to your claude desktop config.\n\n```bash\nuv run mcp install src/point_topic_mcp/server_local.py --with "snowflake-connector-python[pandas]" -f .env\n```\n\n**for mcp inspector**:\n\n```bash\nuv run mcp dev src/point_topic_mcp/server_local.py\n```\n\n**environment configuration**:\n\ncreate `.env` file with your credentials:\n\n```bash\n# Snowflake database credentials (for database tools)\nSNOWFLAKE_USER=your_user\nSNOWFLAKE_PASSWORD=your_password\n\n# Chart API key (for authenticated chart generation)\nCHART_API_KEY=your_chart_api_key\n\n# PT Research MongoDB (for GBS tools: list_operators, get_gbs_status, add_statistic, create_source)\nPT_RESEARCH_DATABASE_URI=mongodb+srv://...\n```\n\n**GBS tools** (list_operators, get_gbs_status, add_statistic, create_source) require mongosh installed:\n\n```bash\n# After pip/uv install, run (sudo needed on Linux):\npoint-topic-mcp-install-mongosh\n```\n\n## architecture\n\n**stdio transport**: communicates with MCP clients via standard input/output for local integration\n\n**auto-discovery**: tools and datasets are automatically discovered from module files - no manual registration needed\n\n**conditional tools**: tools only register if required environment variables are present - use `get_mcp_server_capabilities()` to debug\n\n**modular design**:\n- `src/point_topic_mcp/tools/` - tool modules auto-discovered and registered\n- `src/point_topic_mcp/context/datasets/` - dataset modules auto-discovered for context assembly\n\n## adding new tools\n\nthis project uses auto-discovery for tools - just add a function and it becomes available.\n\n### tool structure\n\ncreate a file in `src/point_topic_mcp/tools/` ending with `_tools.py`:\n\n```python\n# src/point_topic_mcp/tools/my_feature_tools.py\n\nfrom typing import Optional\nfrom mcp.server.fastmcp import Context\nfrom mcp.server.session import ServerSession\n\ndef my_new_tool(param: str, ctx: Optional[Context[ServerSession, None]] = None) -> str:\n    """Tool description visible to agents."""\n    # your implementation\n    return "result"\n```\n\n**that\'s it!** the tool is automatically discovered and registered.\n\n### conditional tools (require credentials)\n\nuse `check_env_vars()` to conditionally define tools:\n\n```python\nfrom point_topic_mcp.core.utils import check_env_vars\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nif check_env_vars(\'my_feature\', [\'MY_API_KEY\']):\n    def authenticated_tool(ctx: Optional[Context[ServerSession, None]] = None) -> str:\n        """Only available if MY_API_KEY is set."""\n        import os\n        api_key = os.getenv(\'MY_API_KEY\')\n        # use api_key...\n        return "result"\n```\n\n### key principles\n\n1. **auto-discovery**: any public function in `*_tools.py` files becomes a tool\n2. **conditional registration**: wrap in `if check_env_vars()` for authenticated tools\n3. **clear docstrings**: visible to agents at all times - keep concise and actionable\n4. **type hints**: use for better agent understanding\n\n### dynamic tool registration\n\nfor registering tools at runtime (e.g., when new datasets become available), use the `ToolManager` class:\n\n```python\nfrom point_topic_mcp.core.tool_manager import ToolManager\nfrom mcp.server.fastmcp import Context\nimport mcp.types\n\n# Create tool manager\ntool_manager = ToolManager(mcp)\n\n# Define a new tool\nasync def analyze_new_dataset(dataset_id: str) -> dict:\n    """Analyze data from a newly added dataset."""\n    return {"status": "analyzed", "dataset": dataset_id}\n\n# Register it\ntool_manager.register_tool(\n    name="analyze_dataset",\n    description="Analyze newly added datasets",\n    function=analyze_new_dataset\n)\n\n# Optional: notify clients of the change\n@mcp.tool()\nasync def notify_new_tool(ctx: Context) -> str:\n    """Notify clients that tools have changed."""\n    await ctx.send_notification(mcp.types.ToolListChangedNotification())\n    return "Clients notified"\n```\n\nwhen you call `tool_manager.register_tool()`, the tool is added to FastMCP. to notify connected MCP clients about the change (so they can refresh their tool lists), call `ctx.send_notification()` with `ToolListChangedNotification()` from within a tool function. see the [MCP specification](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#list-changed-notification) for more details.\n\n## prompts\n\nthe server exposes reusable message templates and workflows via MCP prompts. prompts appear in the prompt picker in MCP clients (Cursor, Claude Desktop, etc.) and provide standardized workflows for common analysis tasks.\n\n### available prompts\n\n**UPC Analysis Prompts** (UK broadband coverage data):\n- `upc_analysis_prompt` - analyze coverage, take-up, forecasts, or market dynamics for a local authority\n- `upc_regional_comparison_prompt` - compare metrics across multiple regions\n- `upc_forecasting_prompt` - generate forward-looking coverage and adoption forecasts\n- `upc_market_analysis_prompt` - understand competitive dynamics and ISP strategies\n\n**SQL Query Assistance Prompts** (Snowflake query generation & optimization):\n- `sql_query_generation_prompt` - generate SQL queries for analysis\n- `sql_query_debugging_prompt` - debug failing SQL queries\n- `sql_optimization_prompt` - optimize queries for speed, cost, or readability\n- `sql_data_exploration_prompt` - explore dataset structure and contents\n- `sql_join_construction_prompt` - generate multi-dataset JOIN queries\n\n### adding new prompts\n\nthis project uses auto-discovery for prompts - just add a function and it becomes available.\n\n#### prompt structure\n\ncreate a file in `src/point_topic_mcp/prompts/` ending with `_prompts.py`:\n\n```python\n# src/point_topic_mcp/prompts/my_analysis_prompts.py\n\nfrom typing import List\nfrom mcp.types import PromptMessage, TextContent\n\ndef my_analysis_prompt(\n    region: str,\n    metric: str = "default"\n) -> List[PromptMessage]:\n    """Brief description of what this prompt helps with.\n    \n    Args:\n        region: Description of the region parameter\n        metric: Description of the metric parameter\n    \n    Returns:\n        List of PromptMessage objects that form the prompt\n    """\n    return [\n        PromptMessage(\n            role="user",\n            content=TextContent(\n                type="text",\n                text="System context or instructions here"\n            )\n        ),\n        PromptMessage(\n            role="user",\n            content=TextContent(\n                type="text",\n                text=f"User query incorporating {region} and {metric}"\n            )\n        ),\n        # Add more messages as needed\n    ]\n```\n\n**that\'s it!** the prompt is automatically discovered and registered.\n\n#### key principles\n\n1. **auto-discovery**: any public function in `*_prompts.py` files becomes a prompt\n2. **clear docstrings**: visible to agents - describe what the prompt helps with\n3. **message structure**: return `List[PromptMessage]` where each message has role ("user" or "assistant") and TextContent\n4. **parameters**: use type hints for parameters - they become prompt arguments in the MCP client\n5. **context-aware**: include system context as the first message(s) to guide the LLM\n\nsee the [MCP Prompts specification](https://modelcontextprotocol.io/specification/draft/server/prompts/) for more details.\n\n### prompt notifications\n\nthe server automatically notifies MCP clients when the list of prompts or tools changes. this is handled through the MCP change notifications protocol.\n\n**supported notifications**:\n- `prompts/list_changed` - fired when prompts are added or removed\n- `tools/list_changed` - fired when tools are added or removed (via ToolManager)\n\nclients can subscribe to these notifications to refresh their prompt/tool lists dynamically. this is useful for:\n- dynamic tool registration (see `ToolManager` in Issue #12)\n- conditional prompts/tools based on environment variables\n- future resource management\n\n**for developers**: to send a notification when you add/remove prompts or tools at runtime, use:\n\n```python\n@mcp.tool()\nasync def refresh_capabilities(ctx: Context) -> str:\n    """Notify clients about capability changes."""\n    await ctx.send_notification(PromptListChangedNotification())\n    # or for tools:\n    await ctx.send_notification(ToolListChangedNotification())\n    return "Clients notified"\n```\n\nsee the [MCP specification](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#list-changed-notification) for more details.\n\n## adding new datasets\n\nthis project uses a modular dataset system that allows easy addition of new data sources. each dataset is self-contained and automatically discovered by the MCP server.\n\n### dataset structure\n\neach dataset is a python module in `src/point_topic_mcp/context/datasets/` with two required functions:\n\n```python\ndef get_dataset_summary():\n    """Brief description visible to agents at all times.\n    Keep concise - this goes in every agent prompt."""\n    return "short description of what data is available"\n\ndef get_db_info():\n    """Complete context: schema, instructions, examples.\n    Only loaded when agent requests this dataset."""\n    return f"""\n    {DB_INFO}\n    \n    {DB_SCHEMA}\n    \n    {SQL_EXAMPLES}\n    """\n```\n\n### key principles\n\n1. **context window efficiency**: keep `get_dataset_summary()` extremely concise - it\'s always visible to agents\n2. **lazy loading**: full context via `get_db_info()` only loads when needed\n3. **self-contained**: each dataset module includes all its own schema, examples, and usage notes\n4. **auto-discovery**: new `.py` files in the datasets directory are automatically available\n\n### adding a new dataset\n\n1. **create the module**: `src/point_topic_mcp/context/datasets/your_dataset.py`\n2. **implement required functions**: `get_dataset_summary()` and `get_db_info()`\n3. **test locally**: `uv run mcp dev src/point_topic_mcp/server_local.py`\n4. **verify discovery**: agent should see your dataset in `assemble_dataset_context()` tool description\n\nsee existing modules (`upc.py`, `upc_take_up.py`, `upc_forecast.py`) for structure examples.\n\n### optimization tips\n\n- prioritize essential info in summaries\n- use clear table descriptions that help agents choose the right dataset\n- include common query patterns in examples\n- sanity check data against known UK facts in instructions\n\n## publishing to PyPI (for maintainers)\n\n**build and test locally**:\n\n```bash\n# Build the package with UV (super fast!)\nuv build\n\n# Test installation locally\npip install dist/point_topic_mcp-*.whl\n\n# Test the command works\npoint-topic-mcp\n```\n\n**publish to PyPI**:\n\nPoint Topic developers: authenticate with AWS, then either run (overwrites `~/.pypirc` if it exists - back up first if you have other tokens):\n\n```bash\naws secretsmanager get-secret-value --secret-id pypirc --query SecretString --output text > ~/.pypirc\n```\n\nor manually copy the secret from AWS Secrets Manager into `~/.pypirc`.\n\nthen publish:\n\n```bash\n./publish_to_pypi.sh\n```\n\n**test installation from PyPI**:\n\n```bash\npip install point-topic-mcp\npoint-topic-mcp\n```'}]