CarlaMCP
# CarlaMCP
Natural language → executable autonomous-vehicle scenarios in the
[CARLA](https://carla.org/) simulator, exposed to any LLM through the
[Model Context Protocol](https://modelcontextprotocol.io/).
Built on the tool-based architecture of [IROSA](https://arxiv.org/abs/2603.03897),
validated against [CARLOS](https://github.com/ika-rwth-aachen/carlos) for
containerized deployment.
A prompt like *"an aggressive cut-in from the right at highway speed in heavy
rain at night"* becomes a validated, reproducible scenario that runs in CARLA —
and can be saved as a spec for replay.
---
## How it works
```
NL prompt ─▶ LLM picks tools ─▶ validated ScenarioManifest ─┬─▶ execute in CARLA ─▶ ScenarioResult
(Pydantic primitives) │
└─▶ save .json spec ─▶ replay / reuse
```
Three layers, each independently testable:
| Layer | Package | Responsibility |
|---|---|---|
| **Primitives** | `carlamcp/primitives/` | Pydantic models with LLM-facing descriptions, range constraints, and cross-field validators. The LLM never writes raw simulator code. |
| **Executors** | `carlamcp/executors/` | Translate validated primitives into `carla` API calls. The only layer that touches the live simulator. |
| **Server** | `carlamcp/server.py` | The MCP server exposing tools to the model. |
## Project structure
```
CarlaMCP/
├── carlamcp/
│ ├── config.py # connection + simulation/spawn constants
│ ├── enums.py # CARLAMap, Side, WeatherCondition
│ ├── results.py # ScenarioResult
│ ├── carla_client.py # CARLA binding import + get_client()
│ ├── world.py # map / actor / sync-mode / find_ego helpers
│ ├── storage.py # save / load scenario specs (export & replay)
│ ├── primitives/ # one file per primitive + the manifest
│ │ ├── cut_in.py pedestrian.py weather.py manifest.py
│ ├── executors/ # one executor per primitive + the orchestrator
│ │ ├── cut_in.py pedestrian.py weather.py manifest.py
│ └── server.py # FastMCP server + tools + main()
├── tests/ # pytest suite, runs WITHOUT a simulator
│ ├── fakes.py conftest.py test_executor.py test_primitives.py
├── pyproject.toml requirements.txt .gitignore LICENSE README.md
```
---
## Setup
### 1. Start CARLA (separate terminal)
```bash
~/carla/CarlaUE4.sh -RenderOffScreen -quality-level=Low &
```
### 2. Install CarlaMCP
```bash
cd CarlaMCP
pip install -e ".[dev]" # package + test deps (no simulator needed)
pip install -e ".[carla,dev]" # also installs the carla==0.9.15 binding
```
### 3. Verify the CARLA connection
```bash
python -c "from carlamcp.carla_client import get_client; print(get_client().get_server_version())"
```
### 4. Run the MCP server
```bash
python -m carlamcp.server # or just: carlamcp
```
### 5. Wire into Claude Desktop
Add to your `claude_desktop_config.json` (use the absolute path to *your* clone):
```json
{
"mcpServers": {
"carlamcp": {
"command": "python",
"args": ["-m", "carlamcp.server"],
"cwd": "<path-to-your-CarlaMCP-clone>"
}
}
}
```
Restart Claude Desktop; the CarlaMCP tools will appear.
---
## Usage
Once connected, talk to the model:
- *"Check if CARLA is running"* → `get_status`
- *"Run an aggressive cut-in from the right at 120 km/h with 1.2s TTC"* → `run_scenario`
- *"Pedestrian crosses 25 m ahead, occluded by a parked car, in heavy fog"*
- *"Set the weather to rain_night at intensity 0.8"* → `set_weather`
- *"Validate this manifest before running it: {...}"* → `validate_scenario`
### Tools
| Tool | Purpose |
|---|---|
| `get_status` | Check the simulator is reachable. |
| `list_maps` | List supported towns and their characteristics. |
| `run_scenario` | Build + validate + execute a scenario (optional `save_as`). |
| `set_weather` | Apply a weather preset only. |
| `validate_scenario` | Validate a manifest JSON without running it. |
| `save_scenario` | Validate and persist a manifest for later. |
| `replay_scenario` | Load a saved spec and execute it. |
### Scenario primitives
| Primitive | Key parameters | Default map |
|---|---|---|
| `CutInManeuver` | `speed_kmh`, `ttc_s`, `side`, `ego_speed_kmh` | Town04 (motorway) |
| `PedestrianCross` | `gap_acceptance_s`, `crossing_distance_m`, `occluded` | Town03 (urban) |
| `WeatherOverlay` | `condition`, `intensity` | Any |
### Export & replay
Any scenario can be persisted as a validated JSON spec and re-run later — useful
for regression sets, sharing, and scenario inpainting / augmentation pipelines:
```text
run_scenario("...", cut_in_speed_kmh=120, cut_in_ttc_s=1.2, cut_in_side="right",
save_as="rainy_cutin") # writes scenarios/rainy_cutin.json
replay_scenario("rainy_cutin") # re-executes the exact same spec
```
The export layer (`carlamcp/storage.py`) is deliberately decoupled from
execution, so generated specs can be consumed by downstream tooling without a
running simulator. See **Roadmap → OpenSCENARIO export**.
---
## Testing
The suite runs entirely offline — `tests/conftest.py` injects a fake `carla`
module so the executor logic (spawn geometry, velocities, ordering, error paths,
collision metric) is verified without a GPU or a running simulator. This makes
it CI-friendly.
```bash
pip install -e ".[dev]"
pytest -q
```
`tests/test_executor.py` doubles as the canonical example of the project's
Google-style (Sphinx [Napoleon](https://sphinxcontrib-napoleon.readthedocs.io/))
docstring convention used across every module.
---
## FAQ — why Python and not C++?
CARLA runs on Unreal Engine (C++), so it's a fair question. For CarlaMCP, C++
buys nothing: the `carla` PyPI package is a thin binding over the C++
`LibCarla` client, and everything done here (spawning actors, weather, the
traffic manager) is fully exposed in Python with negligible overhead at these
tick rates. C++ only becomes worthwhile for work that lives *inside the CarlaUE4
server build* — custom Unreal sensors/actors/map assets, per-frame
high-bandwidth sensor processing where the GIL is the bottleneck, or C++
ScenarioRunner extensions. None of those are on the roadmap, so CarlaMCP stays
pure Python.
---
## Roadmap
- [x] Phase 1 — Validated primitives + MCP server + CARLA execution
- [x] Phase 2 — Modular package, offline test suite, scenario export/replay
- [ ] Phase 3 — CARLOS Docker integration (`docker-compose.yml`)
- [ ] Phase 4 — CSR/ISR/TCR evaluation harness
- [ ] Phase 5 — OpenSCENARIO export for the CARLOS replay pipeline
- [ ] Paper — IEEE IV / ITSC submission
## Related work
- [IROSA](https://arxiv.org/abs/2603.03897) — tool-based LLM architecture for robot skill adaptation (DLR/TUM)
- [CARLOS](https://github.com/ika-rwth-aachen/carlos) — containerized CARLA simulation framework (ika RWTH Aachen)
- [SUMO-MCP](https://arxiv.org/abs/2506.03548) — MCP for traffic simulation (adjacent domain)
## License
[MIT](LICENSE).
TDQS
Scored across 7 tools
Each tool has a clearly defined purpose with no overlap: server status, map listing, scenario generation from NL, replay, save, weather control, and validation. The pair run_scenario and replay_scenario are distinguished by input source (NL vs. saved spec).
All tools follow the verb_noun snake_case convention (get_status, list_maps, run_scenario, etc.), making the API predictable and easy to navigate.
Seven tools cover the essential operations for a CARLA simulation server: status check, map exploration, scenario lifecycle (create, save, replay, validate), and weather control. The count is well-scoped without redundancy.
The tool surface covers the core workflow (scenario generation, execution, replay, weather). Minor gaps include no tool to list or delete saved scenarios, but agents can work around by using the file system or naming conventions.