MCP-CAN

mcp
Guvenlik Denetimi
Gecti
Health Gecti
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Community trust — 13 GitHub stars
Code Gecti
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

Vehicle CAN Bus, OBD-II and J1939 Diagnostics for LLMs (Model Context Protocol)

README.md

🚗 MCP-CAN: Vehicle CAN Bus, OBD-II and J1939 Diagnostics for LLMs (Model Context Protocol)

🔌 Virtual CAN + MCP Server

MCP-CAN is a Model Context Protocol (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.

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.

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.


✨ Highlights

  • MCP server for CAN/OBD/UDS-diagnostics/J1939 → LLM/SLM (tools + DBC metadata, SSE or streamable-HTTP).
  • Virtual CAN backend (python-can) out of the box; optional SocketCAN/vCAN on Linux.
  • DBC-driven encoding/decoding via cantools.
  • ECU simulator that streams multiple messages, plus OBD-II, UDS-style, and SAE J1939 responders.
  • 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.
  • Correlated driving-dynamics signal generation, plus named fault-injection scenarios (overheat, abs_fault, low_fuel) with matching DTCs.
  • 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).
  • Structured tool output (typed Pydantic models), duration-capped tool calls, /healthz, colorized logging.
  • Read-only live web dashboard (/dashboard): signal values and recent frames, updated over SSE.
  • Dockerfile + docker compose for server + simulator.
  • Unit tests, type hints, lint config (ruff, mypy); see CONTRIBUTING.md.

📁 Repository Layout

  • src/mcp_can/
    • cli.py – Typer commands
    • bus.py – python-can helpers
    • dbc.py – DBC loading/decoding
    • obd.py – OBD-II (SAE J1979) request/response helpers
    • diagnostics.py – UDS-style diagnostic service/response-code logic
    • j1939.py – SAE J1939: 29-bit ID decomposition, PGN/SPN catalog, DM1 DTCs, Request PGN
    • config.py – env settings (MCP_CAN_*) + logging setup
    • models.py – internal bus-layer dataclass (Frame)
    • simulator/runner.py – ECU simulator + OBD/diagnostic responders
    • simulator/j1939_runner.py – J1939 broadcasters (EEC1/ET1/CCVS1/…), Request PGN responder, DM1 emitter
    • simulator/state.py – correlated driving-dynamics state (RPM/speed/throttle/etc.)
    • simulator/faults.py – named fault-injection presets + activation protocol
    • server/fastmcp_server.py – MCP tools/resources + dashboard routes
    • server/schemas.py – Pydantic models for MCP tool structured output
    • server/live_state.py – background bus listener backing the dashboard
    • server/templates/dashboard.html – the dashboard page itself
  • vehicle.dbc – sample CAN database (incl. a UDS-like diagnostic schema)
  • simulate-ecus.py, can-mcp.py – standalone run-without-installing entrypoints
  • docker/compose.yml, Dockerfile
  • tests/ – unit tests
  • CONTRIBUTING.md, CHANGELOG.md

✅ Prerequisites

  • Python 3.10+
  • (Optional) Docker / Docker Compose
  • (Optional) Ollama if you want a local LLM backend

📦 Install (Python)

From repo root:

pip install -r requirements.txt
pip install -e .

🚀 Quickstart (Simulator + MCP Server)

Two terminals:

# Terminal A: start ECU simulator on virtual bus0
mcp-can simulate

# Terminal B: start MCP server (SSE on 6278)
mcp-can server --port 6278

Single-process (helps on Windows if virtual backend doesn't share across processes):

mcp-can demo --port 6278

Sample interactions:

mcp-can frames --seconds 2
mcp-can decode 0x100 "01 02 03 04 05 06 07 08"       # pretty table by default, --json for scripting
mcp-can dbc-info                                      # table of every message/signal in the DBC
mcp-can monitor ENGINE_SPEED --seconds 3
mcp-can obd-request --service 0x01 --pid 0x0D
mcp-can diag-request --service-id 0x22 --parameter-id 0x05   # READ_DATA_BY_ID
mcp-can j1939-pgns                                            # J1939 PGN/SPN catalog
mcp-can j1939-request 0xF004                                  # ask ECUs to send EEC1 (engine speed)
mcp-can j1939-dtcs                                            # read the latest DM1 active-DTC broadcast

📊 Live Dashboard

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."

MCP-CAN live dashboard showing grouped ECU signal values and a recent-frames feed

🛠️ Available MCP Tools & Resources

Name Type Description
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).
decode_can_frame tool Decode one frame's bytes into named signals.
filter_frames tool Like read_can_frames, filtered by arbitration ID and/or signal.
monitor_signal tool Timestamped samples of one decoded signal, from the same history buffer.
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.
send_obd_request tool Standard OBD-II (SAE J1979) request; decodes known PIDs (coolant temp, speed, fuel level, fuel type).
send_diagnostic_request tool UDS-style diagnostic request (vehicle.dbc's DIAGNOSTIC_REQUEST); collects every ECU's response.
activate_fault_scenario tool Activate (or clear) a named fault-injection preset (overheat, abs_fault, low_fuel) in the running simulator; see below.
decode_j1939_frame tool Decompose a 29-bit J1939 ID (priority / PGN / source + destination address) and decode known SPNs from the payload.
list_j1939_pgns tool The J1939 PGN/SPN catalog this server can decode and request (bit layout, scaling, units).
request_j1939_pgn tool Send a J1939 Request PGN (0xEA00) and return the decoded responses (needs a simulator on this process's bus).
read_j1939_dtcs tool Most recent J1939 DM1 broadcast: lamp status plus every active SPN/FMI. Served from the frame history buffer.
dbc_info resource (file://vehicle.dbc) Full DBC dump: nodes, messages, signals.

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.

🩺 About the diagnostic responder

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.

⚠️ Fault injection

Three named scenarios (simulator/faults.py::PRESETS) let you force the simulator into a specific fault state instead of waiting on random signal generation:

  • overheatENGINE_TEMP pinned to its hottest reportable value, SYSTEM_STATUS set to FAULT_PRESENT, DTC P0217 (Engine Overtemp Condition).
  • abs_fault – all four WHEEL_SPEED_* signals stuck at zero, SYSTEM_STATUS set to FAULT_PRESENT, DTC C0035 (Left Front Wheel Speed Sensor Circuit).
  • low_fuelFUEL_LEVEL pinned critically low; no DTC (a low-fuel light isn't a stored trouble code on a real vehicle either).

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.

🚛 SAE J1939 (heavy-duty)

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).

What's simulated (simulator/j1939_runner.py), driven by the same correlated driving-dynamics state as the 11-bit signals:

PGN Acronym Contents
0xF004 EEC1 Engine speed (SPN 190), actual engine percent torque (SPN 513)
0xF003 EEC2 Accelerator pedal position (SPN 91), percent load (SPN 92)
0xFEEE ET1 Engine coolant temperature (SPN 110), fuel temperature (SPN 174)
0xFEF1 CCVS1 Wheel-based vehicle speed (SPN 84)
0xFEF2 LFE1 Engine fuel rate (SPN 183), throttle valve position (SPN 51)
0xFEFC DD1 Fuel level (SPN 96)
0xFECA DM1 Active diagnostic trouble codes (SPN + FMI), broadcast at 1 Hz
  • Request PGN (0xEA00): request_j1939_pgn / mcp-can j1939-request <pgn> send a request; the simulator re-broadcasts the requested PGN once.
  • 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.
  • J1939 signals also appear in get_vehicle_snapshot and the dashboard, grouped under J1939:<acronym>.
  • Set MCP_CAN_J1939_ENABLED=false for an 11-bit-only bus.

🔍 MCP Inspector (GUI for your tools)

Use the official Inspector to explore and call your MCP tools without writing a host:

npx @modelcontextprotocol/inspector

When prompted, connect to your server:

  • URL: http://localhost:6278/sse

You can then list tools/resources and call one (e.g. monitor ENGINE_SPEED for 5 seconds) and view structured output live.

🤖 Using with Ollama (local LLM)

  1. Ensure Ollama is running: ollama serve and pull a model: ollama pull llama3
  2. Run simulator + MCP server (see Quickstart).
  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).
  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."

If you need a minimal host, pair @modelcontextprotocol/sdk with Ollama (see SDK docs) or use Inspector for manual tool calls.

Example host config (OpenAI-compatible endpoint to local Ollama):

{
  "model": {
    "type": "openai-compatible",
    "baseUrl": "http://localhost:11434/v1",
    "model": "llama3"
  },
  "mcpServers": {
    "can-mcp-server": {
      "serverUrl": "http://localhost:6278/sse"
    }
  }
}

⌨️ CLI Reference

  • mcp-can simulate – start ECU simulator using vehicle.dbc.
  • mcp-can server [--port 6278] [--transport sse|streamable-http|stdio] – run the MCP server.
  • mcp-can demo [--port] [--transport] – simulator + server in one process.
  • mcp-can frames --seconds 1.0 – capture raw frames as JSON.
  • mcp-can decode <id> <data> [--json] – decode a single frame (table by default; id hex/decimal, data space/comma-separated bytes).
  • mcp-can snapshot --seconds 1.0 [--json] – latest value of every signal seen while listening.
  • mcp-can dbc-info [message] – table of every message/signal in the DBC, or just one message's.
  • mcp-can monitor <signal> --seconds 2.0 [--json] – watch one signal (live output by default).
  • mcp-can obd-request --service <hex|int> [--pid <hex|int>] – OBD-II request; response includes a decoded value for known PIDs.
  • mcp-can diag-request --service-id <hex|int> [--parameter-id] [--data-field] – UDS-style diagnostic request; prints every ECU's response.
  • mcp-can fault <preset|clear|list> – activate/clear a fault-injection scenario in a running simulator, or list available presets.
  • mcp-can j1939-decode <id> <data> [--json] – decompose a 29-bit J1939 ID and decode known SPNs.
  • mcp-can j1939-pgns – list the J1939 PGNs/SPNs this project can decode.
  • mcp-can j1939-request <pgn> [--timeout 2.0] – send a J1939 Request PGN (0xEA00) and print decoded responses.
  • mcp-can j1939-dtcs [--seconds 3.0] – listen for a J1939 DM1 broadcast and print its active trouble codes.

server/demo/simulate all print colorized logs (via rich) instead of raw text.

⚙️ Configuration

Env vars (prefix MCP_CAN_):

  • CAN_INTERFACE (default virtual)
  • CAN_CHANNEL (default bus0)
  • DBC_PATH (default vehicle.dbc)
  • MCP_PORT (default 6278)
  • 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)
  • MAX_DURATION_S (default 30.0) – caps every tool's duration_s/timeout_s
  • J1939_ENABLED (default true) – run the SAE J1939 side of the simulator alongside the 11-bit signals
  • LOG_LEVEL (default INFO)
  • 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.

You can set these in a .env file at repo root.

🐳 Docker

Build:

docker build -t mcp-can .

Run (combined server + simulator):

docker run -d --name mcp-can -p 6278:6278 -p 5000:5000 -p 8080:8080 mcp-can

Compose (from docker/):

docker compose up -d --build

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).

🧪 Development & Testing

See CONTRIBUTING.md for the full guide. Quick version:

pip install -r requirements.txt
pip install -e .
pip install pytest ruff mypy

ruff check .
mypy src
pytest -q

🔧 Troubleshooting

  • 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.
  • DBC missing? Set MCP_CAN_DBC_PATH or place vehicle.dbc in repo root.
  • Docker networking: expose 6278 so your MCP host can reach it.
  • 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).

📄 License

MIT (see LICENSE). Educational/prototyping use only; use certified hardware for real automotive work.

Yorumlar (0)

Sonuc bulunamadi