-
Notifications
You must be signed in to change notification settings - Fork 2
Development and Testing
This project is intentionally small and educational — contributions should keep that spirit: prefer clarity over cleverness, and reuse existing patterns (see Architecture) rather than introducing new ones for a one-off feature. The canonical version of this guidance lives in CONTRIBUTING.md; this page expands on it.
python -m venv .venv
.venv/Scripts/activate # or `source .venv/bin/activate` on Linux/macOS
pip install -r requirements.txt
pip install -e .
pip install pytest ruff mypyruff check .
mypy src
pytest -qAll three must pass — this mirrors .github/workflows/ci.yml exactly (Python 3.10 and 3.11). mypy is kept at zero errors deliberately; suppress with a targeted # type: ignore only if genuinely unavoidable, not as a first resort.
Unit tests mock the CAN bus (tests/test_cli.py's FakeBus) — nothing in the suite starts a real server or virtual bus, so wiring/transport-level regressions won't show up there. For a real sanity check:
mcp-can demo --port 6278then, in another shell, use MCP Inspector (npx @modelcontextprotocol/inspector, connect to http://localhost:6278/sse) or a small script with mcp.client.sse.sse_client/ClientSession to call a tool end-to-end.
-
bus.py,dbc.py,obd.py,diagnostics.py— protocol/bus logic, no MCP or CLI awareness. New protocol behavior belongs here, not inserver/orcli.py. -
server/fastmcp_server.py— MCP tool/resource definitions.server/schemas.py— the Pydantic models those tools return.server/live_state.py— the single background listener backing both the passive-listening tools and the dashboard SSE stream; passive tools should querylive_state.frames_since(...), not open their own bus connection (see Architecture for why that used to be a real bug). Request/response tools (send_obd_request,send_diagnostic_request,activate_fault_scenario) are the exception — they open their own short-livedmake_bus()instance. -
simulator/runner.py— the simulator threads (SimThread,OBDResponderThread,DiagnosticResponderThread,FaultListenerThread). Each bus listener thread needs its ownmake_bus(...)instance — see Architecture. -
cli.py— themcp-canTyper CLI; mirrors the MCP tool surface where practical so both interfaces stay in sync.
- Add the protocol logic (encode/decode/whatever) to a plain module first — testable without any bus or MCP machinery.
- Add a Pydantic return model to
server/schemas.py. - Register the tool in
server/fastmcp_server.py::create_app(). Passive tool → read fromlive_state.frames_since(...)(seeread_can_frames). Request/response tool → open its own bus withmake_bus(), alwaysshutdown_bus()in afinally(seesend_obd_request). Either way, cap anyduration_s/timeout_sagainstsettings.max_duration_s. - If the CLI should expose the same capability, mirror it as a Typer command in
cli.py. - Add a test: pure-logic tests belong next to the module they test (
tests/test_diagnostics.py,tests/test_obd.py,tests/test_faults.pyare good examples); if the tool touches the bus, prefer aFakeBus-style unit test (tests/test_cli.py) or the in-processTestClient(app.sse_app())pattern (tests/test_server_app.py) over spinning up a real server/subprocess in the suite.
| File | Covers |
|---|---|
test_dbc.py |
DBC loading, decode_frame, signal_int
|
test_obd.py |
OBD-II request/response parsing, PID decoding, DTC encode/decode |
test_diagnostics.py |
UDS-style handle_service logic |
test_decode_roundtrip.py |
Encode → decode roundtrip via cantools
|
test_state.py |
Correlated driving-state tick(), and a regression test that actually encodes CORRELATED_SIGNALS output via cantools (not just range-checks it) — see Architecture for the bug this class of test catches |
test_faults.py |
Fault preset activation, control-frame roundtrip, every preset's overrides target a real signal and actually encode |
test_cli.py |
CLI commands against a FakeBus
|
test_server_app.py |
MCP tool registration, /healthz//dashboard routes, CORS behavior, the frame-history-buffer tools against an in-process TestClient
|
Open a GitHub issue. Include your OS, Python version, and — if it's a CAN/MCP issue — whether you're using the virtual backend or real hardware (MCP_CAN_CAN_INTERFACE).
Getting started
Reference
Simulation features
Operating it
Contributing