Skip to content

Development

Mert Cobanov edited this page Aug 1, 2026 · 1 revision

Development

git clone https://github.com/cobanov/teslamate-mcp.git
cd teslamate-mcp
cp env.example .env          # set DATABASE_URL
uv sync                      # install with dev dependencies

Everyday commands

uv run ruff check src tests      # lint
uv run ruff format src tests     # format
uv run pytest                    # tests
uv run teslamate-mcp list-tools  # what the registry discovered

Tests

The suite mixes fast unit tests with integration tests that spin up a real PostgreSQL through testcontainers. Docker-backed tests skip automatically when no daemon is reachable, so uv run pytest works on a laptop without Docker — you just cover less.

If you use a non-standard Docker socket (OrbStack, Colima, rootless Docker), point testcontainers at it:

DOCKER_HOST=unix://$HOME/.orbstack/run/docker.sock uv run pytest

Integration tests seed a miniature TeslaMate-shaped schema and run every bundled query against it, so a query that references a column that does not exist fails in CI rather than in someone's conversation.

Layout

src/teslamate_mcp/
├── cli.py            # click entry points: stdio, http, gen-token, list-tools
├── server.py         # MCPServer factory, lifespan, connection pool ownership
├── config.py         # pydantic-settings; every env var lives here
├── db.py             # pool construction and the three execution paths
├── schema.py         # information_schema introspection, cached at startup
├── auth.py           # bearer middleware (timing-safe, /health exempt)
├── prompts.py        # the six workflow prompts
├── resources.py      # teslamate:// catalog resources
├── telemetry.py      # optional OpenTelemetry wiring
├── apps/             # self-contained HTML for the MCP Apps charts
├── queries/          # the .sql + .toml pairs — where most changes go
└── tools/
    ├── registry.py     # discovery, param validation, handler factory
    ├── custom_sql.py   # run_sql and its read-only guards
    ├── schema_tool.py  # get_database_schema
    ├── apps_ui.py      # spec-driven MCP Apps registration
    └── charging_write.py  # the opt-in write tool

The three database paths

Knowing which one you are on matters:

Function Used by Guards
fetch_all Predefined queries, schema introspection Connection-level STATEMENT_TIMEOUT_MS
fetch_readonly run_sql READ ONLY transaction, forced rollback, QUERY_TIMEOUT_MS, lock_timeout
execute_write set_charging_cost only Commits. Gated by config; the column grant is the real boundary

Never route user-supplied SQL through fetch_all or execute_write.

Adding a query

See Writing Queries — it needs no Python.

Releasing

Tagging v* triggers .github/workflows/release.yml, which builds a multi-arch (linux/amd64, linux/arm64) image, pushes it to GHCR, and opens a GitHub release.

# bump version in pyproject.toml and add a CHANGELOG entry first
git tag -a v0.10.0 -m "v0.10.0"
git push origin v0.10.0

Clone this wiki locally