mcp-can 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. mcp_can-0.1.0/LICENSE +23 -0
  2. mcp_can-0.1.0/PKG-INFO +284 -0
  3. mcp_can-0.1.0/README.md +246 -0
  4. mcp_can-0.1.0/pyproject.toml +79 -0
  5. mcp_can-0.1.0/setup.cfg +4 -0
  6. mcp_can-0.1.0/src/mcp_can/__init__.py +7 -0
  7. mcp_can-0.1.0/src/mcp_can/bus.py +41 -0
  8. mcp_can-0.1.0/src/mcp_can/cli.py +541 -0
  9. mcp_can-0.1.0/src/mcp_can/config.py +67 -0
  10. mcp_can-0.1.0/src/mcp_can/dbc.py +35 -0
  11. mcp_can-0.1.0/src/mcp_can/diagnostics.py +62 -0
  12. mcp_can-0.1.0/src/mcp_can/j1939.py +432 -0
  13. mcp_can-0.1.0/src/mcp_can/models.py +9 -0
  14. mcp_can-0.1.0/src/mcp_can/obd.py +176 -0
  15. mcp_can-0.1.0/src/mcp_can/py.typed +2 -0
  16. mcp_can-0.1.0/src/mcp_can/server/fastmcp_server.py +559 -0
  17. mcp_can-0.1.0/src/mcp_can/server/live_state.py +164 -0
  18. mcp_can-0.1.0/src/mcp_can/server/schemas.py +124 -0
  19. mcp_can-0.1.0/src/mcp_can/server/templates/dashboard.html +265 -0
  20. mcp_can-0.1.0/src/mcp_can/simulator/faults.py +135 -0
  21. mcp_can-0.1.0/src/mcp_can/simulator/j1939_runner.py +211 -0
  22. mcp_can-0.1.0/src/mcp_can/simulator/profiles.py +8 -0
  23. mcp_can-0.1.0/src/mcp_can/simulator/runner.py +229 -0
  24. mcp_can-0.1.0/src/mcp_can/simulator/state.py +133 -0
  25. mcp_can-0.1.0/src/mcp_can.egg-info/PKG-INFO +284 -0
  26. mcp_can-0.1.0/src/mcp_can.egg-info/SOURCES.txt +39 -0
  27. mcp_can-0.1.0/src/mcp_can.egg-info/dependency_links.txt +1 -0
  28. mcp_can-0.1.0/src/mcp_can.egg-info/entry_points.txt +2 -0
  29. mcp_can-0.1.0/src/mcp_can.egg-info/requires.txt +9 -0
  30. mcp_can-0.1.0/src/mcp_can.egg-info/top_level.txt +1 -0
  31. mcp_can-0.1.0/tests/test_cli.py +66 -0
  32. mcp_can-0.1.0/tests/test_dbc.py +32 -0
  33. mcp_can-0.1.0/tests/test_decode_roundtrip.py +27 -0
  34. mcp_can-0.1.0/tests/test_diagnostics.py +33 -0
  35. mcp_can-0.1.0/tests/test_faults.py +89 -0
  36. mcp_can-0.1.0/tests/test_j1939.py +113 -0
  37. mcp_can-0.1.0/tests/test_j1939_cli.py +79 -0
  38. mcp_can-0.1.0/tests/test_j1939_sim.py +44 -0
  39. mcp_can-0.1.0/tests/test_obd.py +89 -0
  40. mcp_can-0.1.0/tests/test_server_app.py +139 -0
  41. mcp_can-0.1.0/tests/test_state.py +119 -0
mcp_can-0.1.0/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+
mcp_can-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,284 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-can
3
+ Version: 0.1.0
4
+ Summary: MCP server exposing vehicle CAN bus, OBD-II and SAE J1939 diagnostics to LLMs, with a built-in virtual CAN simulator (no hardware required).
5
+ Author-email: farzad <farzadnadiri7@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/farzadnadiri/MCP-CAN
8
+ Project-URL: Repository, https://github.com/farzadnadiri/MCP-CAN
9
+ Project-URL: Issues, https://github.com/farzadnadiri/MCP-CAN/issues
10
+ Project-URL: Changelog, https://github.com/farzadnadiri/MCP-CAN/blob/main/CHANGELOG.md
11
+ Keywords: mcp,model-context-protocol,can-bus,canbus,obd-ii,obd2,j1939,uds,automotive,vehicle-diagnostics,ecu,dbc,socketcan,python-can,cantools,llm,ai-agents,simulator
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: License :: OSI Approved :: MIT License
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Classifier: Topic :: Scientific/Engineering
23
+ Classifier: Topic :: System :: Emulators
24
+ Classifier: Topic :: System :: Hardware
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: python-can>=4.0
29
+ Requires-Dist: cantools>=40.0
30
+ Requires-Dist: mcp<2.0.0,>=1.7.0
31
+ Requires-Dist: httpx-sse>=0.4.0
32
+ Requires-Dist: typer>=0.9.0
33
+ Requires-Dist: pydantic>=2.0
34
+ Requires-Dist: pydantic-settings>=2.0
35
+ Requires-Dist: uvicorn>=0.22.0
36
+ Requires-Dist: rich>=13.0.0
37
+ Dynamic: license-file
38
+
39
+ # ๐Ÿš— MCP-CAN: Vehicle CAN Bus, OBD-II and J1939 Diagnostics for LLMs (Model Context Protocol)
40
+
41
+ ๐Ÿ”Œ Virtual CAN + MCP Server
42
+
43
+ **MCP-CAN** is a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that exposes automotive **CAN bus**, **OBD-II** (SAE J1979), **UDS**, and **SAE J1939** diagnostic data to LLMs and AI agents. It ships a built-in **virtual CAN bus** with an **ECU simulator**, decodes traffic via a **DBC** database (`cantools`), and serves MCP tools over SSE or streamable-HTTP. No CAN hardware, adapter, or vehicle is required by default; optional SocketCAN/vCAN on Linux.
44
+
45
+ Use it to let an LLM read live CAN frames, decode signals, run OBD-II PID and UDS diagnostic requests, inspect J1939 PGNs/SPNs and DM1 trouble codes, and drive fault-injection scenarios, all against a simulated vehicle.
46
+
47
+ **Keywords:** MCP server, Model Context Protocol, CAN bus, CANbus, OBD-II, OBD2, on-board diagnostics, SAE J1939, UDS, ECU simulator, vehicle diagnostics, automotive, DBC, python-can, cantools, SocketCAN, LLM tools, AI agents.
48
+
49
+ ---
50
+
51
+ ## โœจ Highlights
52
+ - MCP server for CAN/OBD/UDS-diagnostics/J1939 โ†’ LLM/SLM (tools + DBC metadata, SSE or streamable-HTTP).
53
+ - Virtual CAN backend (python-can) out of the box; optional SocketCAN/vCAN on Linux.
54
+ - DBC-driven encoding/decoding via `cantools`.
55
+ - ECU simulator that streams multiple messages, plus OBD-II, UDS-style, and SAE J1939 responders.
56
+ - SAE J1939 (heavy-duty, 29-bit extended IDs): ID decomposition (priority/PGN/source+destination address), a curated PGN/SPN catalog (EEC1, EEC2, ET1, CCVS1, LFE1, DD1), Request PGN (`0xEA00`) round trips, and DM1 active-DTC (SPN/FMI) broadcasts. Runs alongside the 11-bit bus; toggle with `MCP_CAN_J1939_ENABLED`.
57
+ - Correlated driving-dynamics signal generation, plus named fault-injection scenarios (`overheat`, `abs_fault`, `low_fuel`) with matching DTCs.
58
+ - Typer CLI: `mcp-can` (simulate, server, demo, frames, decode, monitor, dbc-info, obd-request, diag-request, fault, j1939-decode, j1939-pgns, j1939-request, j1939-dtcs).
59
+ - Structured tool output (typed Pydantic models), duration-capped tool calls, `/healthz`, colorized logging.
60
+ - Read-only live web dashboard (`/dashboard`): signal values and recent frames, updated over SSE.
61
+ - Dockerfile + docker compose for server + simulator.
62
+ - Unit tests, type hints, lint config (ruff, mypy); see `CONTRIBUTING.md`.
63
+
64
+ ## ๐Ÿ“ Repository Layout
65
+ - `src/mcp_can/`
66
+ - `cli.py` โ€“ Typer commands
67
+ - `bus.py` โ€“ python-can helpers
68
+ - `dbc.py` โ€“ DBC loading/decoding
69
+ - `obd.py` โ€“ OBD-II (SAE J1979) request/response helpers
70
+ - `diagnostics.py` โ€“ UDS-style diagnostic service/response-code logic
71
+ - `j1939.py` โ€“ SAE J1939: 29-bit ID decomposition, PGN/SPN catalog, DM1 DTCs, Request PGN
72
+ - `config.py` โ€“ env settings (`MCP_CAN_*`) + logging setup
73
+ - `models.py` โ€“ internal bus-layer dataclass (`Frame`)
74
+ - `simulator/runner.py` โ€“ ECU simulator + OBD/diagnostic responders
75
+ - `simulator/j1939_runner.py` โ€“ J1939 broadcasters (EEC1/ET1/CCVS1/โ€ฆ), Request PGN responder, DM1 emitter
76
+ - `simulator/state.py` โ€“ correlated driving-dynamics state (RPM/speed/throttle/etc.)
77
+ - `simulator/faults.py` โ€“ named fault-injection presets + activation protocol
78
+ - `server/fastmcp_server.py` โ€“ MCP tools/resources + dashboard routes
79
+ - `server/schemas.py` โ€“ Pydantic models for MCP tool structured output
80
+ - `server/live_state.py` โ€“ background bus listener backing the dashboard
81
+ - `server/templates/dashboard.html` โ€“ the dashboard page itself
82
+ - `vehicle.dbc` โ€“ sample CAN database (incl. a UDS-like diagnostic schema)
83
+ - `simulate-ecus.py`, `can-mcp.py` โ€“ standalone run-without-installing entrypoints
84
+ - `docker/compose.yml`, `Dockerfile`
85
+ - `tests/` โ€“ unit tests
86
+ - `CONTRIBUTING.md`, `CHANGELOG.md`
87
+
88
+ ## โœ… Prerequisites
89
+ - Python 3.10+
90
+ - (Optional) Docker / Docker Compose
91
+ - (Optional) Ollama if you want a local LLM backend
92
+
93
+ ## ๐Ÿ“ฆ Install (Python)
94
+ From repo root:
95
+ ```bash
96
+ pip install -r requirements.txt
97
+ pip install -e .
98
+ ```
99
+
100
+ ## ๐Ÿš€ Quickstart (Simulator + MCP Server)
101
+ Two terminals:
102
+ ```bash
103
+ # Terminal A: start ECU simulator on virtual bus0
104
+ mcp-can simulate
105
+
106
+ # Terminal B: start MCP server (SSE on 6278)
107
+ mcp-can server --port 6278
108
+ ```
109
+
110
+ Single-process (helps on Windows if virtual backend doesn't share across processes):
111
+ ```bash
112
+ mcp-can demo --port 6278
113
+ ```
114
+
115
+ Sample interactions:
116
+ ```bash
117
+ mcp-can frames --seconds 2
118
+ mcp-can decode 0x100 "01 02 03 04 05 06 07 08" # pretty table by default, --json for scripting
119
+ mcp-can dbc-info # table of every message/signal in the DBC
120
+ mcp-can monitor ENGINE_SPEED --seconds 3
121
+ mcp-can obd-request --service 0x01 --pid 0x0D
122
+ mcp-can diag-request --service-id 0x22 --parameter-id 0x05 # READ_DATA_BY_ID
123
+ mcp-can j1939-pgns # J1939 PGN/SPN catalog
124
+ mcp-can j1939-request 0xF004 # ask ECUs to send EEC1 (engine speed)
125
+ mcp-can j1939-dtcs # read the latest DM1 active-DTC broadcast
126
+ ```
127
+
128
+ ## ๐Ÿ“Š Live Dashboard
129
+ With `mcp-can demo` (or `server`) running, open `http://localhost:6278/dashboard` in a browser: live signal values grouped by ECU message, and a scrolling feed of recent frames, updating ~2x/second over Server-Sent Events. It's read-only (view only, no controls to send frames) and self-contained: no build step, no external assets, works offline. Like everything bus-related here, it only shows data when the simulator shares the *same process* as the server (`mcp-can demo`); pointed at a bare `mcp-can server` with no simulator, it just shows "waiting for CAN traffic."
130
+
131
+ ![MCP-CAN live dashboard showing grouped ECU signal values and a recent-frames feed](docs/images/dashboard.png)
132
+
133
+ ## ๐Ÿ› ๏ธ Available MCP Tools & Resources
134
+ | Name | Type | Description |
135
+ |---|---|---|
136
+ | `read_can_frames` | tool | Raw frames from the last `duration_s` seconds. Returns instantly (served from a continuously-running history buffer, not a fresh listen window). |
137
+ | `decode_can_frame` | tool | Decode one frame's bytes into named signals. |
138
+ | `filter_frames` | tool | Like `read_can_frames`, filtered by arbitration ID and/or signal. |
139
+ | `monitor_signal` | tool | Timestamped samples of one decoded signal, from the same history buffer. |
140
+ | `get_vehicle_snapshot` | tool | Last known value of every signal seen so far, one entry per signal (not per frame) with an `age_s` freshness indicator: a single-call overview instead of decoding a frame stream yourself. |
141
+ | `send_obd_request` | tool | Standard OBD-II (SAE J1979) request; decodes known PIDs (coolant temp, speed, fuel level, fuel type). |
142
+ | `send_diagnostic_request` | tool | UDS-style diagnostic request (`vehicle.dbc`'s `DIAGNOSTIC_REQUEST`); collects every ECU's response. |
143
+ | `activate_fault_scenario` | tool | Activate (or clear) a named fault-injection preset (`overheat`, `abs_fault`, `low_fuel`) in the running simulator; see below. |
144
+ | `decode_j1939_frame` | tool | Decompose a 29-bit J1939 ID (priority / PGN / source + destination address) and decode known SPNs from the payload. |
145
+ | `list_j1939_pgns` | tool | The J1939 PGN/SPN catalog this server can decode and request (bit layout, scaling, units). |
146
+ | `request_j1939_pgn` | tool | Send a J1939 Request PGN (`0xEA00`) and return the decoded responses (needs a simulator on this process's bus). |
147
+ | `read_j1939_dtcs` | tool | Most recent J1939 DM1 broadcast: lamp status plus every active SPN/FMI. Served from the frame history buffer. |
148
+ | `dbc_info` | resource (`file://vehicle.dbc`) | Full DBC dump: nodes, messages, signals. |
149
+
150
+ `read_can_frames`/`filter_frames`/`monitor_signal`/`read_j1939_dtcs` are served from a single continuously-running history buffer (`server/live_state.py`) rather than each opening its own bus listener: they return immediately and won't miss frames sent between calls. `send_obd_request`/`send_diagnostic_request`/`request_j1939_pgn` are request/response and still wait live for a reply. In both cases, `duration_s`/`timeout_s` is capped by `MCP_CAN_MAX_DURATION_S` (default 30s; the history buffer retains at least that much, or 60s, whichever is larger). All tools return typed, structured content (see `server/schemas.py`) rather than ad-hoc JSON.
151
+
152
+ ### ๐Ÿฉบ About the diagnostic responder
153
+ `vehicle.dbc` defines a UDS-like diagnostic schema: `DIAGNOSTIC_REQUEST` (one shared request frame) and four `DIAGNOSTIC_RESPONSE_<ECU>` messages, one per ECU, but the request has no per-ECU target field. The simulator treats every request as functionally addressed to *all four* ECUs, so `send_diagnostic_request`/`diag-request` may return more than one response. Supported services: `START_DIAGNOSTIC_SESSION` (0x10) and `RESET_ECU` (0x11) are acknowledged OK; `READ_DATA_BY_ID` (0x22) returns a deterministic canned value derived from the parameter ID; `ROUTINE_CONTROL`/`READ_MEMORY`/`WRITE_MEMORY` and anything unrecognized return `SERVICE_NOT_SUPPORTED`; see `diagnostics.py::handle_service`.
154
+
155
+ ### โš ๏ธ Fault injection
156
+ Three named scenarios (`simulator/faults.py::PRESETS`) let you force the simulator into a specific fault state instead of waiting on random signal generation:
157
+ - `overheat` โ€“ `ENGINE_TEMP` pinned to its hottest reportable value, `SYSTEM_STATUS` set to `FAULT_PRESENT`, DTC `P0217` (Engine Overtemp Condition).
158
+ - `abs_fault` โ€“ all four `WHEEL_SPEED_*` signals stuck at zero, `SYSTEM_STATUS` set to `FAULT_PRESENT`, DTC `C0035` (Left Front Wheel Speed Sensor Circuit).
159
+ - `low_fuel` โ€“ `FUEL_LEVEL` pinned critically low; no DTC (a low-fuel light isn't a stored trouble code on a real vehicle either).
160
+
161
+ Activating a scenario sends a small control frame on the bus (like OBD/diagnostic requests, this is a round trip to whichever process is running the simulator, so it needs `mcp-can demo`/`simulate` already running) and overrides the named signals until cleared. Any DTCs the active scenario sets show up in `send_obd_request`/`obd-request`'s Mode 03 (service=3) response. Use `mcp-can fault list` or the `activate_fault_scenario` tool's docstring to see the current preset descriptions; pass `preset=None` (CLI: `clear`) to deactivate.
162
+
163
+ ### ๐Ÿš› SAE J1939 (heavy-duty)
164
+ Alongside the light-vehicle 11-bit bus, the simulator also speaks **SAE J1939** โ€” the protocol on trucks, buses and off-highway equipment. J1939 rides 29-bit *extended* CAN IDs whose arbitration field is itself structured data: a 3-bit priority, an 18-bit Parameter Group Number (PGN), and an 8-bit source address (plus, for peer-to-peer "PDU1" PGNs, a destination address). `src/mcp_can/j1939.py` is a self-contained implementation of that layer (not DBC-driven โ€” `vehicle.dbc` models an 11-bit light-vehicle bus).
165
+
166
+ What's simulated (`simulator/j1939_runner.py`), driven by the same correlated driving-dynamics state as the 11-bit signals:
167
+
168
+ | PGN | Acronym | Contents |
169
+ |---|---|---|
170
+ | `0xF004` | EEC1 | Engine speed (SPN 190), actual engine percent torque (SPN 513) |
171
+ | `0xF003` | EEC2 | Accelerator pedal position (SPN 91), percent load (SPN 92) |
172
+ | `0xFEEE` | ET1 | Engine coolant temperature (SPN 110), fuel temperature (SPN 174) |
173
+ | `0xFEF1` | CCVS1 | Wheel-based vehicle speed (SPN 84) |
174
+ | `0xFEF2` | LFE1 | Engine fuel rate (SPN 183), throttle valve position (SPN 51) |
175
+ | `0xFEFC` | DD1 | Fuel level (SPN 96) |
176
+ | `0xFECA` | DM1 | Active diagnostic trouble codes (SPN + FMI), broadcast at 1 Hz |
177
+
178
+ - **Request PGN (`0xEA00`):** `request_j1939_pgn` / `mcp-can j1939-request <pgn>` send a request; the simulator re-broadcasts the requested PGN once.
179
+ - **DM1 / DTCs:** `read_j1939_dtcs` / `mcp-can j1939-dtcs` read the latest DM1. The fault-injection presets map to J1939 DTCs too โ€” `overheat` โ†’ SPN 110 FMI 0, `abs_fault` โ†’ SPN 84 FMI 5, `low_fuel` โ†’ SPN 96 FMI 18 โ€” and the malfunction-indicator lamp turns on while a preset is active.
180
+ - J1939 signals also appear in `get_vehicle_snapshot` and the dashboard, grouped under `J1939:<acronym>`.
181
+ - Set `MCP_CAN_J1939_ENABLED=false` for an 11-bit-only bus.
182
+
183
+ ## ๐Ÿ” MCP Inspector (GUI for your tools)
184
+ Use the official Inspector to explore and call your MCP tools without writing a host:
185
+ ```bash
186
+ npx @modelcontextprotocol/inspector
187
+ ```
188
+ When prompted, connect to your server:
189
+ - URL: `http://localhost:6278/sse`
190
+
191
+ You can then list tools/resources and call one (e.g. monitor `ENGINE_SPEED` for 5 seconds) and view structured output live.
192
+
193
+ ## ๐Ÿค– Using with Ollama (local LLM)
194
+ 1) Ensure Ollama is running: `ollama serve` and pull a model: `ollama pull llama3`
195
+ 2) Run simulator + MCP server (see Quickstart).
196
+ 3) Point your MCP-capable host at `http://localhost:6278/sse` and configure its model endpoint to `http://localhost:11434` with your model name (e.g., `llama3`).
197
+ 4) Prompt the host: "Monitor ENGINE_SPEED for 5 seconds", "List all DBC messages", or "Send a READ_DATA_BY_ID diagnostic request for parameter 5."
198
+
199
+ If you need a minimal host, pair `@modelcontextprotocol/sdk` with Ollama (see SDK docs) or use Inspector for manual tool calls.
200
+
201
+ Example host config (OpenAI-compatible endpoint to local Ollama):
202
+ ```json
203
+ {
204
+ "model": {
205
+ "type": "openai-compatible",
206
+ "baseUrl": "http://localhost:11434/v1",
207
+ "model": "llama3"
208
+ },
209
+ "mcpServers": {
210
+ "can-mcp-server": {
211
+ "serverUrl": "http://localhost:6278/sse"
212
+ }
213
+ }
214
+ }
215
+ ```
216
+
217
+ ## โŒจ๏ธ CLI Reference
218
+ - `mcp-can simulate` โ€“ start ECU simulator using `vehicle.dbc`.
219
+ - `mcp-can server [--port 6278] [--transport sse|streamable-http|stdio]` โ€“ run the MCP server.
220
+ - `mcp-can demo [--port] [--transport]` โ€“ simulator + server in one process.
221
+ - `mcp-can frames --seconds 1.0` โ€“ capture raw frames as JSON.
222
+ - `mcp-can decode <id> <data> [--json]` โ€“ decode a single frame (table by default; `id` hex/decimal, `data` space/comma-separated bytes).
223
+ - `mcp-can snapshot --seconds 1.0 [--json]` โ€“ latest value of every signal seen while listening.
224
+ - `mcp-can dbc-info [message]` โ€“ table of every message/signal in the DBC, or just one message's.
225
+ - `mcp-can monitor <signal> --seconds 2.0 [--json]` โ€“ watch one signal (live output by default).
226
+ - `mcp-can obd-request --service <hex|int> [--pid <hex|int>]` โ€“ OBD-II request; response includes a decoded value for known PIDs.
227
+ - `mcp-can diag-request --service-id <hex|int> [--parameter-id] [--data-field]` โ€“ UDS-style diagnostic request; prints every ECU's response.
228
+ - `mcp-can fault <preset|clear|list>` โ€“ activate/clear a fault-injection scenario in a running simulator, or list available presets.
229
+ - `mcp-can j1939-decode <id> <data> [--json]` โ€“ decompose a 29-bit J1939 ID and decode known SPNs.
230
+ - `mcp-can j1939-pgns` โ€“ list the J1939 PGNs/SPNs this project can decode.
231
+ - `mcp-can j1939-request <pgn> [--timeout 2.0]` โ€“ send a J1939 Request PGN (`0xEA00`) and print decoded responses.
232
+ - `mcp-can j1939-dtcs [--seconds 3.0]` โ€“ listen for a J1939 DM1 broadcast and print its active trouble codes.
233
+
234
+ `server`/`demo`/`simulate` all print colorized logs (via `rich`) instead of raw text.
235
+
236
+ ## โš™๏ธ Configuration
237
+ Env vars (prefix `MCP_CAN_`):
238
+ - `CAN_INTERFACE` (default `virtual`)
239
+ - `CAN_CHANNEL` (default `bus0`)
240
+ - `DBC_PATH` (default `vehicle.dbc`)
241
+ - `MCP_PORT` (default `6278`)
242
+ - `MCP_TRANSPORT` (default `sse`; `streamable-http` requires a newer `mcp` SDK; the server logs a clear error and exits if the installed version doesn't support it, rather than crashing on an SDK traceback)
243
+ - `MAX_DURATION_S` (default `30.0`) โ€“ caps every tool's `duration_s`/`timeout_s`
244
+ - `J1939_ENABLED` (default `true`) โ€“ run the SAE J1939 side of the simulator alongside the 11-bit signals
245
+ - `LOG_LEVEL` (default `INFO`)
246
+ - `CORS_ALLOW_ORIGINS` (default `["*"]`, JSON array e.g. `["https://your-host.example"]`) โ€“ allowed browser origins for the SSE endpoint. Credentialed requests (`allow_credentials`) are only enabled once this is narrowed to specific origins; wildcard + credentials is a combination browsers reject outright, so it's never turned on for the default `"*"`. Override before any real deployment.
247
+
248
+ You can set these in a `.env` file at repo root.
249
+
250
+ ## ๐Ÿณ Docker
251
+ Build:
252
+ ```bash
253
+ docker build -t mcp-can .
254
+ ```
255
+ Run (combined server + simulator):
256
+ ```bash
257
+ docker run -d --name mcp-can -p 6278:6278 -p 5000:5000 -p 8080:8080 mcp-can
258
+ ```
259
+ Compose (from `docker/`):
260
+ ```bash
261
+ docker compose up -d --build
262
+ ```
263
+ > The compose file currently runs `server` and `simulator` as separate containers; like running them as two separate local processes, they won't share the virtual CAN bus unless the host provides a real shared `vcan0` interface. For a working combined setup today, use the single-container Dockerfile above (`mcp-can demo`).
264
+
265
+ ## ๐Ÿงช Development & Testing
266
+ See `CONTRIBUTING.md` for the full guide. Quick version:
267
+ ```bash
268
+ pip install -r requirements.txt
269
+ pip install -e .
270
+ pip install pytest ruff mypy
271
+
272
+ ruff check .
273
+ mypy src
274
+ pytest -q
275
+ ```
276
+
277
+ ## ๐Ÿ”ง Troubleshooting
278
+ - No frames? Ensure both simulator and server use the same interface/channel (`virtual`/`bus0` by default), and, on Windows, that they're the same process (`mcp-can demo`) rather than two separate ones.
279
+ - DBC missing? Set `MCP_CAN_DBC_PATH` or place `vehicle.dbc` in repo root.
280
+ - Docker networking: expose `6278` so your MCP host can reach it.
281
+ - `streamable-http` transport fails immediately? Your installed `mcp` package predates its support; the log line tells you. Switch to `sse` or `pip install -U mcp` (staying below `2.0.0`).
282
+
283
+ ## ๐Ÿ“„ License
284
+ MIT (see `LICENSE`). Educational/prototyping use only; use certified hardware for real automotive work.
@@ -0,0 +1,246 @@
1
+ # ๐Ÿš— MCP-CAN: Vehicle CAN Bus, OBD-II and J1939 Diagnostics for LLMs (Model Context Protocol)
2
+
3
+ ๐Ÿ”Œ Virtual CAN + MCP Server
4
+
5
+ **MCP-CAN** is a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that exposes automotive **CAN bus**, **OBD-II** (SAE J1979), **UDS**, and **SAE J1939** diagnostic data to LLMs and AI agents. It ships a built-in **virtual CAN bus** with an **ECU simulator**, decodes traffic via a **DBC** database (`cantools`), and serves MCP tools over SSE or streamable-HTTP. No CAN hardware, adapter, or vehicle is required by default; optional SocketCAN/vCAN on Linux.
6
+
7
+ Use it to let an LLM read live CAN frames, decode signals, run OBD-II PID and UDS diagnostic requests, inspect J1939 PGNs/SPNs and DM1 trouble codes, and drive fault-injection scenarios, all against a simulated vehicle.
8
+
9
+ **Keywords:** MCP server, Model Context Protocol, CAN bus, CANbus, OBD-II, OBD2, on-board diagnostics, SAE J1939, UDS, ECU simulator, vehicle diagnostics, automotive, DBC, python-can, cantools, SocketCAN, LLM tools, AI agents.
10
+
11
+ ---
12
+
13
+ ## โœจ Highlights
14
+ - MCP server for CAN/OBD/UDS-diagnostics/J1939 โ†’ LLM/SLM (tools + DBC metadata, SSE or streamable-HTTP).
15
+ - Virtual CAN backend (python-can) out of the box; optional SocketCAN/vCAN on Linux.
16
+ - DBC-driven encoding/decoding via `cantools`.
17
+ - ECU simulator that streams multiple messages, plus OBD-II, UDS-style, and SAE J1939 responders.
18
+ - SAE J1939 (heavy-duty, 29-bit extended IDs): ID decomposition (priority/PGN/source+destination address), a curated PGN/SPN catalog (EEC1, EEC2, ET1, CCVS1, LFE1, DD1), Request PGN (`0xEA00`) round trips, and DM1 active-DTC (SPN/FMI) broadcasts. Runs alongside the 11-bit bus; toggle with `MCP_CAN_J1939_ENABLED`.
19
+ - Correlated driving-dynamics signal generation, plus named fault-injection scenarios (`overheat`, `abs_fault`, `low_fuel`) with matching DTCs.
20
+ - Typer CLI: `mcp-can` (simulate, server, demo, frames, decode, monitor, dbc-info, obd-request, diag-request, fault, j1939-decode, j1939-pgns, j1939-request, j1939-dtcs).
21
+ - Structured tool output (typed Pydantic models), duration-capped tool calls, `/healthz`, colorized logging.
22
+ - Read-only live web dashboard (`/dashboard`): signal values and recent frames, updated over SSE.
23
+ - Dockerfile + docker compose for server + simulator.
24
+ - Unit tests, type hints, lint config (ruff, mypy); see `CONTRIBUTING.md`.
25
+
26
+ ## ๐Ÿ“ Repository Layout
27
+ - `src/mcp_can/`
28
+ - `cli.py` โ€“ Typer commands
29
+ - `bus.py` โ€“ python-can helpers
30
+ - `dbc.py` โ€“ DBC loading/decoding
31
+ - `obd.py` โ€“ OBD-II (SAE J1979) request/response helpers
32
+ - `diagnostics.py` โ€“ UDS-style diagnostic service/response-code logic
33
+ - `j1939.py` โ€“ SAE J1939: 29-bit ID decomposition, PGN/SPN catalog, DM1 DTCs, Request PGN
34
+ - `config.py` โ€“ env settings (`MCP_CAN_*`) + logging setup
35
+ - `models.py` โ€“ internal bus-layer dataclass (`Frame`)
36
+ - `simulator/runner.py` โ€“ ECU simulator + OBD/diagnostic responders
37
+ - `simulator/j1939_runner.py` โ€“ J1939 broadcasters (EEC1/ET1/CCVS1/โ€ฆ), Request PGN responder, DM1 emitter
38
+ - `simulator/state.py` โ€“ correlated driving-dynamics state (RPM/speed/throttle/etc.)
39
+ - `simulator/faults.py` โ€“ named fault-injection presets + activation protocol
40
+ - `server/fastmcp_server.py` โ€“ MCP tools/resources + dashboard routes
41
+ - `server/schemas.py` โ€“ Pydantic models for MCP tool structured output
42
+ - `server/live_state.py` โ€“ background bus listener backing the dashboard
43
+ - `server/templates/dashboard.html` โ€“ the dashboard page itself
44
+ - `vehicle.dbc` โ€“ sample CAN database (incl. a UDS-like diagnostic schema)
45
+ - `simulate-ecus.py`, `can-mcp.py` โ€“ standalone run-without-installing entrypoints
46
+ - `docker/compose.yml`, `Dockerfile`
47
+ - `tests/` โ€“ unit tests
48
+ - `CONTRIBUTING.md`, `CHANGELOG.md`
49
+
50
+ ## โœ… Prerequisites
51
+ - Python 3.10+
52
+ - (Optional) Docker / Docker Compose
53
+ - (Optional) Ollama if you want a local LLM backend
54
+
55
+ ## ๐Ÿ“ฆ Install (Python)
56
+ From repo root:
57
+ ```bash
58
+ pip install -r requirements.txt
59
+ pip install -e .
60
+ ```
61
+
62
+ ## ๐Ÿš€ Quickstart (Simulator + MCP Server)
63
+ Two terminals:
64
+ ```bash
65
+ # Terminal A: start ECU simulator on virtual bus0
66
+ mcp-can simulate
67
+
68
+ # Terminal B: start MCP server (SSE on 6278)
69
+ mcp-can server --port 6278
70
+ ```
71
+
72
+ Single-process (helps on Windows if virtual backend doesn't share across processes):
73
+ ```bash
74
+ mcp-can demo --port 6278
75
+ ```
76
+
77
+ Sample interactions:
78
+ ```bash
79
+ mcp-can frames --seconds 2
80
+ mcp-can decode 0x100 "01 02 03 04 05 06 07 08" # pretty table by default, --json for scripting
81
+ mcp-can dbc-info # table of every message/signal in the DBC
82
+ mcp-can monitor ENGINE_SPEED --seconds 3
83
+ mcp-can obd-request --service 0x01 --pid 0x0D
84
+ mcp-can diag-request --service-id 0x22 --parameter-id 0x05 # READ_DATA_BY_ID
85
+ mcp-can j1939-pgns # J1939 PGN/SPN catalog
86
+ mcp-can j1939-request 0xF004 # ask ECUs to send EEC1 (engine speed)
87
+ mcp-can j1939-dtcs # read the latest DM1 active-DTC broadcast
88
+ ```
89
+
90
+ ## ๐Ÿ“Š Live Dashboard
91
+ With `mcp-can demo` (or `server`) running, open `http://localhost:6278/dashboard` in a browser: live signal values grouped by ECU message, and a scrolling feed of recent frames, updating ~2x/second over Server-Sent Events. It's read-only (view only, no controls to send frames) and self-contained: no build step, no external assets, works offline. Like everything bus-related here, it only shows data when the simulator shares the *same process* as the server (`mcp-can demo`); pointed at a bare `mcp-can server` with no simulator, it just shows "waiting for CAN traffic."
92
+
93
+ ![MCP-CAN live dashboard showing grouped ECU signal values and a recent-frames feed](docs/images/dashboard.png)
94
+
95
+ ## ๐Ÿ› ๏ธ Available MCP Tools & Resources
96
+ | Name | Type | Description |
97
+ |---|---|---|
98
+ | `read_can_frames` | tool | Raw frames from the last `duration_s` seconds. Returns instantly (served from a continuously-running history buffer, not a fresh listen window). |
99
+ | `decode_can_frame` | tool | Decode one frame's bytes into named signals. |
100
+ | `filter_frames` | tool | Like `read_can_frames`, filtered by arbitration ID and/or signal. |
101
+ | `monitor_signal` | tool | Timestamped samples of one decoded signal, from the same history buffer. |
102
+ | `get_vehicle_snapshot` | tool | Last known value of every signal seen so far, one entry per signal (not per frame) with an `age_s` freshness indicator: a single-call overview instead of decoding a frame stream yourself. |
103
+ | `send_obd_request` | tool | Standard OBD-II (SAE J1979) request; decodes known PIDs (coolant temp, speed, fuel level, fuel type). |
104
+ | `send_diagnostic_request` | tool | UDS-style diagnostic request (`vehicle.dbc`'s `DIAGNOSTIC_REQUEST`); collects every ECU's response. |
105
+ | `activate_fault_scenario` | tool | Activate (or clear) a named fault-injection preset (`overheat`, `abs_fault`, `low_fuel`) in the running simulator; see below. |
106
+ | `decode_j1939_frame` | tool | Decompose a 29-bit J1939 ID (priority / PGN / source + destination address) and decode known SPNs from the payload. |
107
+ | `list_j1939_pgns` | tool | The J1939 PGN/SPN catalog this server can decode and request (bit layout, scaling, units). |
108
+ | `request_j1939_pgn` | tool | Send a J1939 Request PGN (`0xEA00`) and return the decoded responses (needs a simulator on this process's bus). |
109
+ | `read_j1939_dtcs` | tool | Most recent J1939 DM1 broadcast: lamp status plus every active SPN/FMI. Served from the frame history buffer. |
110
+ | `dbc_info` | resource (`file://vehicle.dbc`) | Full DBC dump: nodes, messages, signals. |
111
+
112
+ `read_can_frames`/`filter_frames`/`monitor_signal`/`read_j1939_dtcs` are served from a single continuously-running history buffer (`server/live_state.py`) rather than each opening its own bus listener: they return immediately and won't miss frames sent between calls. `send_obd_request`/`send_diagnostic_request`/`request_j1939_pgn` are request/response and still wait live for a reply. In both cases, `duration_s`/`timeout_s` is capped by `MCP_CAN_MAX_DURATION_S` (default 30s; the history buffer retains at least that much, or 60s, whichever is larger). All tools return typed, structured content (see `server/schemas.py`) rather than ad-hoc JSON.
113
+
114
+ ### ๐Ÿฉบ About the diagnostic responder
115
+ `vehicle.dbc` defines a UDS-like diagnostic schema: `DIAGNOSTIC_REQUEST` (one shared request frame) and four `DIAGNOSTIC_RESPONSE_<ECU>` messages, one per ECU, but the request has no per-ECU target field. The simulator treats every request as functionally addressed to *all four* ECUs, so `send_diagnostic_request`/`diag-request` may return more than one response. Supported services: `START_DIAGNOSTIC_SESSION` (0x10) and `RESET_ECU` (0x11) are acknowledged OK; `READ_DATA_BY_ID` (0x22) returns a deterministic canned value derived from the parameter ID; `ROUTINE_CONTROL`/`READ_MEMORY`/`WRITE_MEMORY` and anything unrecognized return `SERVICE_NOT_SUPPORTED`; see `diagnostics.py::handle_service`.
116
+
117
+ ### โš ๏ธ Fault injection
118
+ Three named scenarios (`simulator/faults.py::PRESETS`) let you force the simulator into a specific fault state instead of waiting on random signal generation:
119
+ - `overheat` โ€“ `ENGINE_TEMP` pinned to its hottest reportable value, `SYSTEM_STATUS` set to `FAULT_PRESENT`, DTC `P0217` (Engine Overtemp Condition).
120
+ - `abs_fault` โ€“ all four `WHEEL_SPEED_*` signals stuck at zero, `SYSTEM_STATUS` set to `FAULT_PRESENT`, DTC `C0035` (Left Front Wheel Speed Sensor Circuit).
121
+ - `low_fuel` โ€“ `FUEL_LEVEL` pinned critically low; no DTC (a low-fuel light isn't a stored trouble code on a real vehicle either).
122
+
123
+ Activating a scenario sends a small control frame on the bus (like OBD/diagnostic requests, this is a round trip to whichever process is running the simulator, so it needs `mcp-can demo`/`simulate` already running) and overrides the named signals until cleared. Any DTCs the active scenario sets show up in `send_obd_request`/`obd-request`'s Mode 03 (service=3) response. Use `mcp-can fault list` or the `activate_fault_scenario` tool's docstring to see the current preset descriptions; pass `preset=None` (CLI: `clear`) to deactivate.
124
+
125
+ ### ๐Ÿš› SAE J1939 (heavy-duty)
126
+ Alongside the light-vehicle 11-bit bus, the simulator also speaks **SAE J1939** โ€” the protocol on trucks, buses and off-highway equipment. J1939 rides 29-bit *extended* CAN IDs whose arbitration field is itself structured data: a 3-bit priority, an 18-bit Parameter Group Number (PGN), and an 8-bit source address (plus, for peer-to-peer "PDU1" PGNs, a destination address). `src/mcp_can/j1939.py` is a self-contained implementation of that layer (not DBC-driven โ€” `vehicle.dbc` models an 11-bit light-vehicle bus).
127
+
128
+ What's simulated (`simulator/j1939_runner.py`), driven by the same correlated driving-dynamics state as the 11-bit signals:
129
+
130
+ | PGN | Acronym | Contents |
131
+ |---|---|---|
132
+ | `0xF004` | EEC1 | Engine speed (SPN 190), actual engine percent torque (SPN 513) |
133
+ | `0xF003` | EEC2 | Accelerator pedal position (SPN 91), percent load (SPN 92) |
134
+ | `0xFEEE` | ET1 | Engine coolant temperature (SPN 110), fuel temperature (SPN 174) |
135
+ | `0xFEF1` | CCVS1 | Wheel-based vehicle speed (SPN 84) |
136
+ | `0xFEF2` | LFE1 | Engine fuel rate (SPN 183), throttle valve position (SPN 51) |
137
+ | `0xFEFC` | DD1 | Fuel level (SPN 96) |
138
+ | `0xFECA` | DM1 | Active diagnostic trouble codes (SPN + FMI), broadcast at 1 Hz |
139
+
140
+ - **Request PGN (`0xEA00`):** `request_j1939_pgn` / `mcp-can j1939-request <pgn>` send a request; the simulator re-broadcasts the requested PGN once.
141
+ - **DM1 / DTCs:** `read_j1939_dtcs` / `mcp-can j1939-dtcs` read the latest DM1. The fault-injection presets map to J1939 DTCs too โ€” `overheat` โ†’ SPN 110 FMI 0, `abs_fault` โ†’ SPN 84 FMI 5, `low_fuel` โ†’ SPN 96 FMI 18 โ€” and the malfunction-indicator lamp turns on while a preset is active.
142
+ - J1939 signals also appear in `get_vehicle_snapshot` and the dashboard, grouped under `J1939:<acronym>`.
143
+ - Set `MCP_CAN_J1939_ENABLED=false` for an 11-bit-only bus.
144
+
145
+ ## ๐Ÿ” MCP Inspector (GUI for your tools)
146
+ Use the official Inspector to explore and call your MCP tools without writing a host:
147
+ ```bash
148
+ npx @modelcontextprotocol/inspector
149
+ ```
150
+ When prompted, connect to your server:
151
+ - URL: `http://localhost:6278/sse`
152
+
153
+ You can then list tools/resources and call one (e.g. monitor `ENGINE_SPEED` for 5 seconds) and view structured output live.
154
+
155
+ ## ๐Ÿค– Using with Ollama (local LLM)
156
+ 1) Ensure Ollama is running: `ollama serve` and pull a model: `ollama pull llama3`
157
+ 2) Run simulator + MCP server (see Quickstart).
158
+ 3) Point your MCP-capable host at `http://localhost:6278/sse` and configure its model endpoint to `http://localhost:11434` with your model name (e.g., `llama3`).
159
+ 4) Prompt the host: "Monitor ENGINE_SPEED for 5 seconds", "List all DBC messages", or "Send a READ_DATA_BY_ID diagnostic request for parameter 5."
160
+
161
+ If you need a minimal host, pair `@modelcontextprotocol/sdk` with Ollama (see SDK docs) or use Inspector for manual tool calls.
162
+
163
+ Example host config (OpenAI-compatible endpoint to local Ollama):
164
+ ```json
165
+ {
166
+ "model": {
167
+ "type": "openai-compatible",
168
+ "baseUrl": "http://localhost:11434/v1",
169
+ "model": "llama3"
170
+ },
171
+ "mcpServers": {
172
+ "can-mcp-server": {
173
+ "serverUrl": "http://localhost:6278/sse"
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ ## โŒจ๏ธ CLI Reference
180
+ - `mcp-can simulate` โ€“ start ECU simulator using `vehicle.dbc`.
181
+ - `mcp-can server [--port 6278] [--transport sse|streamable-http|stdio]` โ€“ run the MCP server.
182
+ - `mcp-can demo [--port] [--transport]` โ€“ simulator + server in one process.
183
+ - `mcp-can frames --seconds 1.0` โ€“ capture raw frames as JSON.
184
+ - `mcp-can decode <id> <data> [--json]` โ€“ decode a single frame (table by default; `id` hex/decimal, `data` space/comma-separated bytes).
185
+ - `mcp-can snapshot --seconds 1.0 [--json]` โ€“ latest value of every signal seen while listening.
186
+ - `mcp-can dbc-info [message]` โ€“ table of every message/signal in the DBC, or just one message's.
187
+ - `mcp-can monitor <signal> --seconds 2.0 [--json]` โ€“ watch one signal (live output by default).
188
+ - `mcp-can obd-request --service <hex|int> [--pid <hex|int>]` โ€“ OBD-II request; response includes a decoded value for known PIDs.
189
+ - `mcp-can diag-request --service-id <hex|int> [--parameter-id] [--data-field]` โ€“ UDS-style diagnostic request; prints every ECU's response.
190
+ - `mcp-can fault <preset|clear|list>` โ€“ activate/clear a fault-injection scenario in a running simulator, or list available presets.
191
+ - `mcp-can j1939-decode <id> <data> [--json]` โ€“ decompose a 29-bit J1939 ID and decode known SPNs.
192
+ - `mcp-can j1939-pgns` โ€“ list the J1939 PGNs/SPNs this project can decode.
193
+ - `mcp-can j1939-request <pgn> [--timeout 2.0]` โ€“ send a J1939 Request PGN (`0xEA00`) and print decoded responses.
194
+ - `mcp-can j1939-dtcs [--seconds 3.0]` โ€“ listen for a J1939 DM1 broadcast and print its active trouble codes.
195
+
196
+ `server`/`demo`/`simulate` all print colorized logs (via `rich`) instead of raw text.
197
+
198
+ ## โš™๏ธ Configuration
199
+ Env vars (prefix `MCP_CAN_`):
200
+ - `CAN_INTERFACE` (default `virtual`)
201
+ - `CAN_CHANNEL` (default `bus0`)
202
+ - `DBC_PATH` (default `vehicle.dbc`)
203
+ - `MCP_PORT` (default `6278`)
204
+ - `MCP_TRANSPORT` (default `sse`; `streamable-http` requires a newer `mcp` SDK; the server logs a clear error and exits if the installed version doesn't support it, rather than crashing on an SDK traceback)
205
+ - `MAX_DURATION_S` (default `30.0`) โ€“ caps every tool's `duration_s`/`timeout_s`
206
+ - `J1939_ENABLED` (default `true`) โ€“ run the SAE J1939 side of the simulator alongside the 11-bit signals
207
+ - `LOG_LEVEL` (default `INFO`)
208
+ - `CORS_ALLOW_ORIGINS` (default `["*"]`, JSON array e.g. `["https://your-host.example"]`) โ€“ allowed browser origins for the SSE endpoint. Credentialed requests (`allow_credentials`) are only enabled once this is narrowed to specific origins; wildcard + credentials is a combination browsers reject outright, so it's never turned on for the default `"*"`. Override before any real deployment.
209
+
210
+ You can set these in a `.env` file at repo root.
211
+
212
+ ## ๐Ÿณ Docker
213
+ Build:
214
+ ```bash
215
+ docker build -t mcp-can .
216
+ ```
217
+ Run (combined server + simulator):
218
+ ```bash
219
+ docker run -d --name mcp-can -p 6278:6278 -p 5000:5000 -p 8080:8080 mcp-can
220
+ ```
221
+ Compose (from `docker/`):
222
+ ```bash
223
+ docker compose up -d --build
224
+ ```
225
+ > The compose file currently runs `server` and `simulator` as separate containers; like running them as two separate local processes, they won't share the virtual CAN bus unless the host provides a real shared `vcan0` interface. For a working combined setup today, use the single-container Dockerfile above (`mcp-can demo`).
226
+
227
+ ## ๐Ÿงช Development & Testing
228
+ See `CONTRIBUTING.md` for the full guide. Quick version:
229
+ ```bash
230
+ pip install -r requirements.txt
231
+ pip install -e .
232
+ pip install pytest ruff mypy
233
+
234
+ ruff check .
235
+ mypy src
236
+ pytest -q
237
+ ```
238
+
239
+ ## ๐Ÿ”ง Troubleshooting
240
+ - No frames? Ensure both simulator and server use the same interface/channel (`virtual`/`bus0` by default), and, on Windows, that they're the same process (`mcp-can demo`) rather than two separate ones.
241
+ - DBC missing? Set `MCP_CAN_DBC_PATH` or place `vehicle.dbc` in repo root.
242
+ - Docker networking: expose `6278` so your MCP host can reach it.
243
+ - `streamable-http` transport fails immediately? Your installed `mcp` package predates its support; the log line tells you. Switch to `sse` or `pip install -U mcp` (staying below `2.0.0`).
244
+
245
+ ## ๐Ÿ“„ License
246
+ MIT (see `LICENSE`). Educational/prototyping use only; use certified hardware for real automotive work.
@@ -0,0 +1,79 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mcp-can"
7
+ version = "0.1.0"
8
+ description = "MCP server exposing vehicle CAN bus, OBD-II and SAE J1939 diagnostics to LLMs, with a built-in virtual CAN simulator (no hardware required)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "farzad", email = "farzadnadiri7@gmail.com" }
14
+ ]
15
+ keywords = [
16
+ "mcp",
17
+ "model-context-protocol",
18
+ "can-bus",
19
+ "canbus",
20
+ "obd-ii",
21
+ "obd2",
22
+ "j1939",
23
+ "uds",
24
+ "automotive",
25
+ "vehicle-diagnostics",
26
+ "ecu",
27
+ "dbc",
28
+ "socketcan",
29
+ "python-can",
30
+ "cantools",
31
+ "llm",
32
+ "ai-agents",
33
+ "simulator",
34
+ ]
35
+ classifiers = [
36
+ "Development Status :: 4 - Beta",
37
+ "Intended Audience :: Developers",
38
+ "Intended Audience :: Science/Research",
39
+ "Programming Language :: Python :: 3",
40
+ "Programming Language :: Python :: 3.10",
41
+ "Programming Language :: Python :: 3.11",
42
+ "Programming Language :: Python :: 3.12",
43
+ "License :: OSI Approved :: MIT License",
44
+ "Operating System :: OS Independent",
45
+ "Topic :: Software Development :: Libraries",
46
+ "Topic :: Scientific/Engineering",
47
+ "Topic :: System :: Emulators",
48
+ "Topic :: System :: Hardware",
49
+ ]
50
+
51
+ dependencies = [
52
+ "python-can>=4.0",
53
+ "cantools>=40.0",
54
+ "mcp>=1.7.0,<2.0.0",
55
+ "httpx-sse>=0.4.0",
56
+ "typer>=0.9.0",
57
+ "pydantic>=2.0",
58
+ "pydantic-settings>=2.0",
59
+ "uvicorn>=0.22.0",
60
+ "rich>=13.0.0"
61
+ ]
62
+
63
+ [project.urls]
64
+ Homepage = "https://github.com/farzadnadiri/MCP-CAN"
65
+ Repository = "https://github.com/farzadnadiri/MCP-CAN"
66
+ Issues = "https://github.com/farzadnadiri/MCP-CAN/issues"
67
+ Changelog = "https://github.com/farzadnadiri/MCP-CAN/blob/main/CHANGELOG.md"
68
+
69
+ [project.scripts]
70
+ mcp-can = "mcp_can.cli:app"
71
+
72
+ [tool.setuptools.packages.find]
73
+ where = ["src"]
74
+
75
+ [tool.setuptools.package-data]
76
+ "mcp_can" = ["py.typed"]
77
+ "mcp_can.server" = ["templates/*.html"]
78
+
79
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,7 @@
1
+ __all__ = [
2
+ "__version__",
3
+ ]
4
+
5
+ __version__ = "0.1.0"
6
+
7
+