UK broadband data analysis MCP server with Snowflake integration
Project description
Point Topic MCP Server
UK broadband data analysis server via Model Context Protocol. Queries the Point Topic ontology (ClickHouse) for all UK broadband data — footprint, speeds, take-up, forecasts, tariffs, and demographics.
✅ what's implemented
database tools (ClickHouse — requires CLICKHOUSE_HOST + CLICKHOUSE_PASSWORD):
execute_query()- run safe read-only SQL against the ontology. The tool description embeds the ontology data model (key tables, aspects, ClickHouse dialect) — no context assembly needed.get_onto_network_overview()- comprehensive overview of a single networkget_onto_net_op_overview()- overview of a network operatorget_onto_isp_overview()- overview of an ISPget_onto_schema()- list all ontology tables with columns and descriptionsassemble_dataset_context()- get supplementary schemas and examples for specific datasets
database tools (Snowflake — dev only, requires ENABLE_SNOWFLAKE=true):
execute_snowflake_query()- run SQL against legacy Snowflake tablesget_query_status()/cancel_query()- manage long-running queriesdescribe_table()/get_la_code()/get_la_list_full()- table schema & LA lookupsget_dataset_status()- check latest data availability dates
chart tools:
get_point_topic_public_chart_catalog()- browse public charts (no auth needed)get_point_topic_public_chart_csv()- get public chart data as CSV (no auth needed)get_point_topic_chart_catalog()- get complete catalog including private charts (requires API key)get_point_topic_chart_csv()- get any chart data as CSV with authentication (requires API key)generate_authenticated_chart_url()- create signed URLs for private charts (requires API key)
server info:
get_mcp_server_capabilities()- check which tools are available and debug missing credentials
prompts (reusable message templates):
- UPC analysis: analyze coverage, adoption, forecasts, and market dynamics
conditional availability: tools only appear if required environment variables are set
installation (for end users)
option 1: pip install (recommended):
pip install point-topic-mcp
if you encounter cmake build errors during installation (for pyarrow), install with Snowflake support explicitly:
pip install "point-topic-mcp[snowflake]"
or provide pre-built wheels:
pip install --only-binary :all: point-topic-mcp[snowflake]
option 2: from source (with uv):
git clone https://github.com/point-topic/point-topic-mcp.git
cd point-topic-mcp
uv sync
uv run point-topic-mcp
add to your MCP client (Claude Desktop, Cursor, etc.):
{
"mcpServers": {
"point-topic": {
"command": "point-topic-mcp",
"env": {
"CLICKHOUSE_HOST": "db.point-topic.com",
"CLICKHOUSE_PORT": "443",
"CLICKHOUSE_DATABASE": "ontology",
"CLICKHOUSE_USER": "your_user",
"CLICKHOUSE_PASSWORD": "your_password"
}
}
}
}
For Snowflake dev access, add:
"ENABLE_SNOWFLAKE": "true",
"SNOWFLAKE_USER": "your_user",
"SNOWFLAKE_PASSWORD": "your_password"
remote deployment (FastMCP Cloud)
Deploy the MCP server remotely to FastMCP Cloud for access from any MCP client.
requirements:
- FastMCP Cloud account (https://fastmcp.cloud)
- GitHub repository connected to FastMCP Cloud
deployment:
- Sign up at https://fastmcp.cloud
- Connect repository:
Point-Topic/point-topic-mcp - Configure environment variables (ClickHouse credentials)
- Deploy - FastMCP Cloud handles HTTPS, scaling, monitoring automatically
client connections:
Claude Desktop (via mcp-remote):
{
"mcpServers": {
"point-topic": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://your-url.fastmcp.cloud/mcp"]
}
}
}
Cursor (native remote):
{
"mcpServers": {
"point-topic": {
"url": "https://your-url.fastmcp.cloud/mcp",
"transport": "http"
}
}
}
See docs/REMOTE_SERVER.md for complete deployment guide.
note: environment variables are optional - tools will only appear if credentials are provided. use get_mcp_server_capabilities() to check which tools are available.
Claude Desktop config location:
- Mac:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
development setup
setup: uv sync
for local development with claude desktop:
This will add the server to your claude desktop config.
uv run mcp install src/point_topic_mcp/server_local.py -f .env
For Snowflake dev access, add `--with "snowflake-connector-python[pandas]"`:
```bash
uv run mcp install src/point_topic_mcp/server_local.py --with "snowflake-connector-python[pandas]" -f .env
**for mcp inspector**:
```bash
uv run mcp dev src/point_topic_mcp/server_local.py
environment configuration:
create .env file with your credentials.
Required for all users:
# ClickHouse credentials for ontology queries (primary data path — always needed)
CLICKHOUSE_HOST=db.point-topic.com
CLICKHOUSE_PORT=443
CLICKHOUSE_DATABASE=ontology
CLICKHOUSE_USER=your_user
CLICKHOUSE_PASSWORD=your_password
Optional — Snowflake dev access:
# Enable legacy Snowflake tools (dev only — set ENABLE_SNOWFLAKE=true to show them)
ENABLE_SNOWFLAKE=true
SNOWFLAKE_USER=your_user
SNOWFLAKE_PASSWORD=your_password
Optional — other tools:
# Chart API key (for authenticated chart generation)
CHART_API_KEY=your_chart_api_key
# PT Research MongoDB (for GBS tools)
PT_RESEARCH_DATABASE_URI=mongodb+srv://...
# GitHub token (for GitHub org tools)
GITHUB_TOKEN=your_github_token
GBS tools require mongosh installed:
# After pip/uv install, run (sudo needed on Linux):
point-topic-mcp-install-mongosh
architecture
stdio transport: communicates with MCP clients via standard input/output for local integration
auto-discovery: tools and datasets are automatically discovered from module files - no manual registration needed
conditional tools: tools only register if required environment variables are present - use get_mcp_server_capabilities() to debug
modular design:
src/point_topic_mcp/tools/- tool modules auto-discovered and registeredsrc/point_topic_mcp/context/datasets/- dataset modules auto-discovered for context assembly
adding new tools
this project uses auto-discovery for tools - just add a function and it becomes available.
tool structure
create a file in src/point_topic_mcp/tools/ ending with _tools.py:
# src/point_topic_mcp/tools/my_feature_tools.py
from typing import Optional
from mcp.server.fastmcp import Context
from mcp.server.session import ServerSession
def my_new_tool(param: str, ctx: Optional[Context[ServerSession, None]] = None) -> str:
"""Tool description visible to agents."""
# your implementation
return "result"
that's it! the tool is automatically discovered and registered.
conditional tools (require credentials)
use check_env_vars() to conditionally define tools:
from point_topic_mcp.core.utils import check_env_vars
from dotenv import load_dotenv
load_dotenv()
if check_env_vars('my_feature', ['MY_API_KEY']):
def authenticated_tool(ctx: Optional[Context[ServerSession, None]] = None) -> str:
"""Only available if MY_API_KEY is set."""
import os
api_key = os.getenv('MY_API_KEY')
# use api_key...
return "result"
key principles
- auto-discovery: any public function in
*_tools.pyfiles becomes a tool - conditional registration: wrap in
if check_env_vars()for authenticated tools - clear docstrings: visible to agents at all times - keep concise and actionable
- type hints: use for better agent understanding
dynamic tool registration
for registering tools at runtime (e.g., when new datasets become available), use the ToolManager class:
from point_topic_mcp.core.tool_manager import ToolManager
from mcp.server.fastmcp import Context
import mcp.types
# Create tool manager
tool_manager = ToolManager(mcp)
# Define a new tool
async def analyze_new_dataset(dataset_id: str) -> dict:
"""Analyze data from a newly added dataset."""
return {"status": "analyzed", "dataset": dataset_id}
# Register it
tool_manager.register_tool(
name="analyze_dataset",
description="Analyze newly added datasets",
function=analyze_new_dataset
)
# Optional: notify clients of the change
@mcp.tool()
async def notify_new_tool(ctx: Context) -> str:
"""Notify clients that tools have changed."""
await ctx.send_notification(mcp.types.ToolListChangedNotification())
return "Clients notified"
when 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 for more details.
prompts
the 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.
available prompts
UPC Analysis Prompts (UK broadband coverage data):
upc_analysis_prompt- analyze coverage, take-up, forecasts, or market dynamics for a local authorityupc_regional_comparison_prompt- compare metrics across multiple regionsupc_forecasting_prompt- generate forward-looking coverage and adoption forecastsupc_market_analysis_prompt- understand competitive dynamics and ISP strategies
The ontology data model and ClickHouse dialect are embedded in execute_query's tool description — no SQL assistance prompts needed for the primary data path.
adding new prompts
this project uses auto-discovery for prompts - just add a function and it becomes available.
prompt structure
create a file in src/point_topic_mcp/prompts/ ending with _prompts.py:
# src/point_topic_mcp/prompts/my_analysis_prompts.py
from typing import List
from mcp.types import PromptMessage, TextContent
def my_analysis_prompt(
region: str,
metric: str = "default"
) -> List[PromptMessage]:
"""Brief description of what this prompt helps with.
Args:
region: Description of the region parameter
metric: Description of the metric parameter
Returns:
List of PromptMessage objects that form the prompt
"""
return [
PromptMessage(
role="user",
content=TextContent(
type="text",
text="System context or instructions here"
)
),
PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"User query incorporating {region} and {metric}"
)
),
# Add more messages as needed
]
that's it! the prompt is automatically discovered and registered.
key principles
- auto-discovery: any public function in
*_prompts.pyfiles becomes a prompt - clear docstrings: visible to agents - describe what the prompt helps with
- message structure: return
List[PromptMessage]where each message has role ("user" or "assistant") and TextContent - parameters: use type hints for parameters - they become prompt arguments in the MCP client
- context-aware: include system context as the first message(s) to guide the LLM
see the MCP Prompts specification for more details.
prompt notifications
the server automatically notifies MCP clients when the list of prompts or tools changes. this is handled through the MCP change notifications protocol.
supported notifications:
prompts/list_changed- fired when prompts are added or removedtools/list_changed- fired when tools are added or removed (via ToolManager)
clients can subscribe to these notifications to refresh their prompt/tool lists dynamically. this is useful for:
- dynamic tool registration (see
ToolManagerin Issue #12) - conditional prompts/tools based on environment variables
- future resource management
for developers: to send a notification when you add/remove prompts or tools at runtime, use:
@mcp.tool()
async def refresh_capabilities(ctx: Context) -> str:
"""Notify clients about capability changes."""
await ctx.send_notification(PromptListChangedNotification())
# or for tools:
await ctx.send_notification(ToolListChangedNotification())
return "Clients notified"
see the MCP specification for more details.
adding new datasets
this 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.
dataset structure
each dataset is a python module in src/point_topic_mcp/context/datasets/ with two required functions:
def get_summary():
"""Brief description visible to agents at all times.
Keep concise - this goes in every agent prompt."""
return "short description of what data is available"
def get_full_context():
"""Complete context: schema, instructions, examples.
Only loaded when agent requests this dataset."""
return f"""
{DB_INFO}
{DB_SCHEMA}
{SQL_EXAMPLES}
"""
key principles
- context window efficiency: keep
get_summary()extremely concise - it's always visible to agents - lazy loading: full context via
get_full_context()only loads when needed - self-contained: each dataset module includes all its own schema, examples, and usage notes
- auto-discovery: new
.pyfiles in the datasets directory are automatically available
adding a new dataset
- create the module:
src/point_topic_mcp/context/datasets/your_dataset.py - implement required functions:
get_summary()andget_full_context() - test locally:
uv run mcp dev src/point_topic_mcp/server_local.py - verify discovery: agent should see your dataset in
assemble_dataset_context()tool description
see existing modules (upc.py, upc_take_up.py, upc_forecast.py) for structure examples.
optimization tips
- prioritize essential info in summaries
- use clear table descriptions that help agents choose the right dataset
- include common query patterns in ClickHouse SQL
- sanity check data against known UK facts in instructions
- the ontology data model is always visible in
execute_query's description — datasets are supplementary
publishing to PyPI (for maintainers)
build and test locally:
# Build the package with UV (super fast!)
uv build
# Test installation locally
pip install dist/point_topic_mcp-*.whl
# Test the command works
point-topic-mcp
publish to PyPI:
Point Topic developers: authenticate with AWS, then either run (overwrites ~/.pypirc if it exists - back up first if you have other tokens):
aws secretsmanager get-secret-value --secret-id pypirc --query SecretString --output text > ~/.pypirc
or manually copy the secret from AWS Secrets Manager into ~/.pypirc.
then publish:
./publish_to_pypi.sh
test installation from PyPI:
pip install point-topic-mcp
point-topic-mcp
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file point_topic_mcp-0.2.2.tar.gz.
File metadata
- Download URL: point_topic_mcp-0.2.2.tar.gz
- Upload date:
- Size: 289.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1af1dedc09d32d1deb95d8c375429c5766267c29b34ec25d0bd7c8977bbdc18
|
|
| MD5 |
1fe719782d6cfa3829593bc45b7ffafd
|
|
| BLAKE2b-256 |
ccf79844bde3db054196b7a238dbc8a468635a34c908438dc4387fc219f49881
|
File details
Details for the file point_topic_mcp-0.2.2-py3-none-any.whl.
File metadata
- Download URL: point_topic_mcp-0.2.2-py3-none-any.whl
- Upload date:
- Size: 79.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a45d58497d9ee0655a3519a3be0c0162b0b7e8b6e1ece018e64590ae94ea0b7
|
|
| MD5 |
f4420439d54209439ea5bfca44aa0c42
|
|
| BLAKE2b-256 |
104c39119d4567cde93e776991f940395f64a52276bcce3cd14d53530dc736aa
|