Road Viewer MCP
by Qualified4
README.md
# roadviewer-mcp
[Road Viewer](https://github.com/Qualified4/roadviewer-ha) Home Assistant 애드온을 Codex·Claude Code 같은 MCP 클라이언트에
도구로 노출하는 로컬 stdio 서버다. HA 장기 토큰으로 WebSocket `supervisor/api`에 접속해 Ingress 세션을 받고, 그 세션
쿠키로 기존 Road Viewer API를 호출한다. 애드온은 바꾸지 않고, 새 포트나 외부 공개도 없다. 로그 검색, 상세, 북마크,
저장 공간 조회와 리플레이 데이터·텔레메트리 구간 조회를 하고, 파일은 로컬 디렉터리에 저장한 뒤 경로만 돌려준다.
쓰기는 북마크 추가·수정과 태그 추가·제거만 한다.
요구 사항: Python 3.12 이상, Road Viewer 0.12.2 이상이 설치된 Home Assistant OS/Supervised, 관리자 사용자의
장기 액세스 토큰(Ingress 패널이 관리자 권한을 요구한다). Linux 또는 WSL에서 실행한다.
## 설치
```bash
# uv (권장)
uv tool install git+https://github.com/Qualified4/roadviewer-mcp
# 업데이트
uv tool install --force git+https://github.com/Qualified4/roadviewer-mcp
# pipx
pipx install git+https://github.com/Qualified4/roadviewer-mcp
```
실행 파일 이름은 `roadviewer-mcp`다(uv: `~/.local/share/uv/tools/roadviewer-mcp/bin/roadviewer-mcp`).
`ffmpeg`가 `PATH`에 있으면 `roadviewer_download_clip` 도구가 추가된다.
개발용 설치:
```bash
git clone https://github.com/Qualified4/roadviewer-mcp
uv pip install -e ./roadviewer-mcp --python <venv>/bin/python
```
## 환경 변수
`HAMCP_ENV_FILE`의 값을 먼저 읽고, 프로세스 환경 변수가 같은 이름이면 그 값을 쓴다. 토큰은 `.env` 파일에 두고
저장소나 채팅에 올리지 않는다.
| 이름 | 필수 | 기본값 | 설명 |
| --- | --- | --- | --- |
| `HAMCP_ENV_FILE` | 아니오 | | [ha-mcp](https://github.com/homeassistant-ai/ha-mcp)와 같은 형식의 `.env` 파일. `HOMEASSISTANT_URL`, `HOMEASSISTANT_TOKEN`을 여기서 읽는다 |
| `HOMEASSISTANT_URL` | 예 | | `http://` 또는 `https://` HA 주소 |
| `HOMEASSISTANT_TOKEN` | 예 | | HA 장기 액세스 토큰 (관리자 사용자) |
| `ROADVIEWER_DOWNLOAD_DIR` | 예 | | 다운로드 저장 디렉터리. 모든 파일은 이 아래에만 쓴다 |
| `ROADVIEWER_ADDON_SLUG` | 아니오 | `f21b33c3_roadviewer` | 애드온 slug. 저장소에서 설치한 Road Viewer의 기본값이며 `/api/hassio/addons`로 확인할 수 있다 |
| `ROADVIEWER_LIST_CACHE_SECONDS` | 아니오 | `30` | `/api/logs` 목록 캐시 시간(초) |
| `ROADVIEWER_MAX_FILE_BYTES` | 아니오 | `2147483648` (2 GiB) | 다운로드 파일 크기 상한 |
`.env` 예시:
```dotenv
HOMEASSISTANT_URL=https://your-ha.example.com:8123
HOMEASSISTANT_TOKEN=<long-lived access token>
```
## 클라이언트 등록
### Codex (`~/.codex/config.toml`)
```toml
[mcp_servers.roadviewer-mcp]
command = "/home/<user>/.local/share/uv/tools/roadviewer-mcp/bin/roadviewer-mcp"
[mcp_servers.roadviewer-mcp.env]
HAMCP_ENV_FILE = "/path/to/homeassistant/.env"
ROADVIEWER_DOWNLOAD_DIR = "/path/to/roadviewer-downloads"
```
### Claude Code
```bash
claude mcp add --scope user roadviewer-mcp \
-e HAMCP_ENV_FILE=/path/to/homeassistant/.env \
-e ROADVIEWER_DOWNLOAD_DIR=/path/to/roadviewer-downloads \
-- roadviewer-mcp
```
Windows에서 서버를 WSL에 설치했다면 명령을 `wsl -e env HAMCP_ENV_FILE=... ROADVIEWER_DOWNLOAD_DIR=... /home/<user>/.local/share/uv/tools/roadviewer-mcp/bin/roadviewer-mcp`로 등록한다.
## 도구
| 도구 | 종류 | 설명 |
| --- | --- | --- |
| `roadviewer_search_logs` | 읽기 | 이름·id·경로·구간·상태·영상·태그·고정·업로드 시각으로 검색 (최신 경로 먼저, limit 최대 200). 업로드 시각은 ISO 8601 문자열 또는 epoch 초(문자열이나 숫자) |
| `roadviewer_get_log` | 읽기 | 로그 1건 상세: 파일 이름, 코덱, 다운로드 옵션, 경고, 북마크 |
| `roadviewer_get_bookmarks` | 읽기 | 로그의 북마크 목록 |
| `roadviewer_storage_status` | 읽기 | 저장 공간 설정·사용량, 상태별 로그 수, 애드온 버전 |
| `roadviewer_get_frames` | 읽기 | 리플레이 프레임 구간 조회 (60초 이하) |
| `roadviewer_get_telemetry` | 읽기 | carState 등 텔레메트리 구간 조회 (60초 이하) |
| `roadviewer_download` | 로컬 파일 쓰기 (destructive) | rlog·카메라·data·telemetry 한 파일을 받아 로컬 경로를 반환, 중단 시 이어받기 |
| `roadviewer_download_clip` | 로컬 파일 쓰기 | 카메라 MP4에서 60초 이하 구간을 재인코딩 없이 잘라 저장 (ffmpeg가 있을 때만) |
| `roadviewer_add_bookmark` | 애드온 쓰기 | 북마크 추가 |
| `roadviewer_update_bookmark` | 애드온 쓰기 (destructive) | 북마크의 시간·제목·메모 수정 |
| `roadviewer_set_tags` | 애드온 쓰기 (destructive) | 태그 추가 또는 제거 |
읽기 도구는 `readOnlyHint`, 쓰기 도구는 `readOnlyHint=false`로 표시한다. 기존 값을 바꾸거나 지울 수 있는
도구(북마크 수정, 태그 제거, `overwrite`로 파일 덮어쓰기)는 `destructiveHint=true`, 추가만 하는
`roadviewer_add_bookmark`와 `roadviewer_download_clip`은 `destructiveHint=false`다. `add_bookmark`와
`set_tags`는 네트워크 오류 뒤 바로 다시 보내지 말고 `roadviewer_get_bookmarks`·`roadviewer_get_log`로 먼저
확인하라고 설명에 적혀 있다(서버가 이미 반영했을 수 있다).
`get_frames`와 `get_telemetry`는 gzip 원본을 `ROADVIEWER_DOWNLOAD_DIR/.cache`에 두고, 로그가 다시
올라오거나 재변환되면 새로 받는다. `download_clip`이 쓰는 카메라 MP4도 같은 기준(`<파일>.key`에 기록한
`uploaded`·`prepared_bytes`)으로, 바뀌었으면 다시 받는다.
## 안전 규칙
- HTTP 요청은 GET만 쓴다. 예외는 쓰기 3경로(`POST /api/logs/<id>/bookmarks`,
`PUT /api/logs/<id>/bookmarks/<bookmark_id>`, `POST /api/logs/<id>/tags`)뿐이고, 이 경로를 보내는
함수 하나에서만 `X-RoadViewer-Request: 1` 헤더를 붙인다. `DELETE`는 코드에 없다.
- 읽기 경로도 허용 목록(`/api/logs`, `/api/logs/<id>/...` 일부, `/api/settings/storage`) 밖은 요청 전에 거부한다.
id는 32자리 소문자 16진수만 받는다.
- HA 토큰, `ingress_session` 쿠키, `/api/hassio_ingress/<token>`의 토큰은 도구 결과·오류 메시지에서
`<token>`, `<ingress>`로 바뀐다. 리다이렉트는 따라가지 않는다. 환경의 프록시 설정(`HTTP(S)_PROXY`,
`ALL_PROXY` 등)은 HA WebSocket과 Ingress HTTP 두 경로 모두 쓰지 않는다.
- HA WebSocket 호출 하나(연결·인증·요청·응답)는 30초 안에 끝나지 않으면 실패한다. 도구 실행 중 Ingress
세션 갱신은 60초까지 기다리고, 넘으면 취소하고 오류를 돌려준다.
- HTTP 요청은 `client.py`의 한 함수에서만 보낸다(테스트가 다른 모듈의 직접 HTTP 호출을 막는다).
- 파일은 `ROADVIEWER_DOWNLOAD_DIR` 아래에만 저장한다. `dest_dir`는 그 안의 상대 경로만 받고 `..`·절대
경로는 거부한다. 기존 파일은 `overwrite`를 주지 않으면 덮어쓰지 않는다.
- 파일 하나는 2 GiB(`ROADVIEWER_MAX_FILE_BYTES`)까지, 디스크 여유가 파일 크기 + 100 MiB보다 적으면
시작하지 않는다. 다운로드는 한 번에 하나만 하며 64 KiB 단위로 스트리밍한다.
- 받은 파일은 길이를 확인하고 SHA-256을 반환한다. 이어받기는 ETag 또는 Last-Modified를 `If-Range`로
보내므로 서버 파일이 바뀌었으면 처음부터 다시 받는다.
보안 주의: 이 서버는 HA 관리자 토큰을 사용한다. 토큰이 유출되면 Home Assistant 전체가 위험하므로 `.env`의
권한을 제한하고(`chmod 600`), 신뢰하는 로컬 MCP 클라이언트에만 등록한다. 서버 자체는 네트워크 포트를 열지 않는다.
## 하지 않는 일
- 로그·북마크·태그 삭제, 태그 전체 교체, 변환·재변환, 고정, 설정 변경, 업로드
- 영상·로그 파일 내용을 MCP 응답으로 보내기 (경로만 반환)
- 새 포트 열기, 외부 공개, 별도 인증 서버, 애드온 코드 변경
- 토큰이나 세션 쿠키를 디스크·로그에 기록하기
## 테스트
로컬 가짜 Ingress 서버로만 테스트하며 실제 HA에는 접속하지 않는다.
```bash
python -m unittest discover -s tests -t . -v
```
## 라이선스
MIT. [LICENSE](LICENSE)를 참고한다.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues