CanLab/docs

Integrations

Ways to drive CanLab from something else, and ways to extend it.

REST API

Start it from the REST API toolbar toggle. It binds to 127.0.0.1:8765 and prints a per-session token on start. Every request needs an X-API-Token header.

GET  /            # live web dashboard (HTML, open)
GET  /frames      # last N frames  (?n=N)
GET  /signals     # decoded DBC signals
GET  /status      # connection state and frame count
GET  /memory      # AI memory entries
POST /mark        # add an event mark: {"label":"brake","action":"toggle"}
                  # action is toggle (default), begin, end or point
POST /inject      # inject a frame: needs the token AND ARM TX
                  # {"id":"0x200","data":"01 02 03 04 05 06 07 08"}

Loopback only. It is meant for scripting the tool from the same machine, not for exposing a bus to a network. /mark is what a phone uses to say "this is the brake" while someone else drives; the mark lands on the INTELLIGENCE tab's list and the TIMELINE.

Injection is doubly gated

/inject needs both a valid token and ARM TX on. The token alone will not transmit.

MCP server: Claude, ChatGPT and Codex

CanLab is a Model Context Protocol server. An assistant connected to it can load a capture, list IDs, read byte statistics and raw frames, run every detector, draft a DBC, define and remove signals, decode frames, annotate the timeline and rank bytes against those annotations, list reassembled multi-frame messages, find repeated blocks, calibrate against a reference file and read the live watch's events: 29 tools in canlab/core/mcp_tools.py. No MCP tool transmits. Putting frames on a wire stays behind ARM TX in the window, where a person is watching.

The same tools are served two ways.

WhereHow
Inside the windowThe MCP toolbar toggle, or Settings → MCP, starts a Streamable HTTP server on 127.0.0.1:8766/mcp over the capture you have loaded or are recording right now. A signal the assistant adds appears in DBC BUILDER as one undoable step.
Headlesscanlab-mcp serves stdio and loads captures on request; canlab-mcp --http serves the same over HTTP with no window.

Settings → MCP writes the exact configuration for each client and copies it to the clipboard. In short:

# Claude Code, against the running window (or a headless --http server)
claude mcp add --transport http canlab http://127.0.0.1:8766/mcp

# Claude Desktop and Codex CLI launch stdio servers, so bridge to the window
canlab-mcp --attach http://127.0.0.1:8766/mcp

# Claude Desktop, headless, no window needed
{"command": "/path/to/.venv/bin/canlab-mcp"}

ChatGPT connects from OpenAI's servers, so it cannot reach your loopback address. Publish the server over HTTPS first, with cloudflared tunnel --url http://127.0.0.1:8766 or ngrok, tick allow connections from other machines, and add https://<tunnel-host>/mcp as a connector. The search and fetch tools exist for ChatGPT's connector contract; Developer mode exposes the rest.

A tunnel has no authentication

The in-window server takes an optional bearer token, but ChatGPT's connectors cannot send one. While a tunnel is up, anyone who learns the URL can read the capture and edit the signal list. Nothing can transmit, but close the tunnel when you are done.

Capture kit

A desktop captures well when someone is sitting at it. A day of driving needs a logger that starts at boot, writes to disk as it goes, and lets the driver say "this is the brake" without a screen. canlab-cli capture is that logger; see the command line page. It serves the same REST API as the window, with /mark and /frames but without /inject, so a phone on the same network can add marks while the kit records.

Plugins

Drop a .py file into ~/.canlab/plugins/. It needs three things:

PLUGIN_NAME    = "My Plugin"
PLUGIN_VERSION = "1.0"

def register(app):
    # called with the MainWindow instance when the plugin is activated
    ...

From app you reach the shared application state, the loaded frames as a pandas DataFrame, the signal definitions, the menu bar, and anything in canlab.core. Full API, the event list and two worked examples are in docs/PLUGINS.md.

Plugins run with full application privileges

Metadata is read statically, so listing plugins never executes anything. Code runs only after you approve it, and approval is trust-on-first-use keyed by the file's SHA-256: editing an approved plugin changes its hash and asks again, so approved code cannot be silently swapped. Only approve plugins you trust.

AI engine

Four providers: Anthropic, OpenAI, Groq and Ollama, the last running locally with no key and nothing leaving the machine. Configure in Settings → API KEYS; the model field is editable, so a model id newer than the built-in list can be typed in.

What is sent, when you click Analyze on an ID, is that message's statistics: byte roles, message type and period, the checksum guess, similar IDs, and a sample of frames. Not your whole capture, and nothing at all until you supply a key and ask.

The offline findings are included in the prompt deliberately, so the model reasons about measured facts rather than raw hex. Its answers are still suggestions; treat them the way you would treat the heuristics.

Hardware adapters

Settings → CAN ADAPTERS keeps a list of named adapters and the toolbar switches between them, so a bench with a PEAK on one port and a CANable on another is two clicks rather than two retypings. Each adapter is a backend, a channel, a bitrate, a CAN FD flag and whatever else that backend needs, such as an slcan stick's serial baud rate or a socketcand host.

Detect connected asks every python-can backend what it can see, reads CAN network devices from sysfs, and recognises common USB sticks by vendor and product id. Test opens the adapter and listens for one second; it never transmits, and when opening fails it names the fix, whether that is the ip link command, a missing pip package or the dialout group.

BackendNotes
socketcanLinux. The kernel owns the bitrate, so bring the device up first. PEAK, Kvaser, candleLight and 8devices adapters all appear here.
gvretSavvyCAN's own hardware: Macchina M2 and A0, EVTV CANDue, ESP32RET. python-can ships no backend for it, so CanLab supplies one in core/gvret.py and registers it as an interface. Give it a serial port, or <ip>:23 for a board on WiFi.
slcanCANable with slcan firmware, USBtin, Lawicel.
gs_usbcandleLight over libusb, where there is no kernel driver.
pcan, kvaser, vector, ixxatVendor drivers, which come from the vendor.
virtual, udp_multicastNo hardware at all, for trying the application.

pip install canlab[adapters] adds pyserial and gs_usb. A comma.ai Panda is supported separately through core/panda_backend.py, presented as a python-can compatible bus with the safety model selectable. The multi-bus configuration in Settings → MULTI-BUS captures from more than one interface at once, which is what you want when a vehicle has separate powertrain and body buses.

Trimming a capture

Tools → Trim capture cuts the loaded capture down to a time window, a frame or percentage range, a set of IDs or ID ranges, or one bus, with a live count as you type. The result either replaces what is loaded or is written to a file. Every analysis runs over whatever is loaded, so trimming first makes the detectors faster and their output shorter.