Standalone, hosted MCP server that lets the decision & analytics team query
the fleet database (reporting.* / tracksolid.*) from Claude — read-only, for
reporting and decisions, never edit/delete.
- analytics_mcp.py: FastMCP streamable-HTTP server. Tools: query (guarded
single SELECT/WITH, auto-LIMIT, write/DDL blocked), list_schemas,
list_tables, describe_table, list_functions, sample_table. Per-analyst
Bearer auth; /healthz exempt. No ts_shared_rev import (carries no ingestion
secrets).
- Read-only enforced at four layers: analytics_ro GRANTs,
default_transaction_read_only=on, rolled-back txn, SQL keyword guard.
- scripts/: analytics_ro_role.sql + bootstrap_analytics_ro.sh (dedicated
least-privilege role, password in host-only ~/.analytics_ro.pw).
- Dockerfile + pyproject (uv, package=false) for Coolify build; deploy.sh
manual host fallback (standalone Traefik bridge on the tracksolid_db host).
- docs/ANALYTICS_MCP.{md,html} + README: architecture, deploy runbook,
add-to-Claude, verification, security notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
35 lines
1 KiB
TOML
35 lines
1 KiB
TOML
[project]
|
|
name = "fleetanalytics-mcp"
|
|
version = "1.0.0"
|
|
description = "Fireside Communications — read-only Fleet Analytics MCP server (decision & analytics team)"
|
|
readme = "README.md"
|
|
requires-python = ">=3.12"
|
|
authors = [
|
|
{ name = "Fireside DevOps", email = "devops@firesideafrica.cloud" }
|
|
]
|
|
dependencies = [
|
|
"mcp[cli]>=1.2", # MCP server SDK (FastMCP, streamable HTTP)
|
|
"psycopg2-binary>=2.9.9", # Postgres driver (binary wheels — easy in Docker)
|
|
"uvicorn[standard]>=0.30.0", # ASGI server
|
|
"starlette>=0.37", # Bearer-auth middleware + /healthz route (pulled in by mcp, pinned for clarity)
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"ruff>=0.4",
|
|
"mypy>=1.10",
|
|
]
|
|
|
|
[tool.uv]
|
|
# Flat single-module project (analytics_mcp.py) — don't try to build/install it as
|
|
# a package; just manage the dependency venv.
|
|
package = false
|
|
|
|
[tool.ruff]
|
|
target-version = "py312"
|
|
line-length = 100
|
|
select = ["E", "W", "F", "B", "UP", "SIM"]
|
|
|
|
[tool.mypy]
|
|
python_version = "3.12"
|
|
ignore_missing_imports = true
|