fleetanalytics_mcp/pyproject.toml
david kiania 1eda59fe06 feat: read-only Fleet Analytics MCP server
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>
2026-06-16 23:43:24 +03:00

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