IP摄像头管理MCP服务

该MCP服务通过统一接口管理Dahua/Amcrest IP摄像头,支持多设备配置、系统信息查询、参数调整、运动检测、抓拍、录像检索与日志搜索,适用于安防监控和智能运维场景。

  • by brianegge
  • Python
  • 混合部署
  • 6 星标
  • 更新于
在 GitHub 查看 brianegge/dahua-mcp

使用说明

Dahua MCP Server

MCP server for managing Dahua/Amcrest IP cameras via their CGI HTTP API. Supports multiple cameras from a single instance, with both stdio and HTTP transports.

Quick Start

1. Create a cameras config file

Create ~/.config/dahua-mcp/cameras.yaml (or cameras.json) with your camera credentials. This is the default config location — no env var needed.

JSON (cameras.json):

{
  "cameras": [
    {
      "name": "front-door",
      "host": "192.168.1.108",
      "port": 80,
      "username": "admin",
      "password": "secret"
    },
    {
      "name": "backyard",
      "host": "192.168.1.109",
      "port": 80,
      "username": "admin",
      "password": "secret"
    }
  ]
}

YAML (cameras.yaml):

cameras:
  - name: front-door
    host: 192.168.1.108
    port: 80
    username: admin
    password: secret
  - name: backyard
    host: 192.168.1.109
    port: 80
    username: admin
    password: secret

Each camera entry supports:

Field Required Default Description
name Yes — Unique camera name used in all tool calls
host Yes — Camera hostname or IP address
port No 80 HTTP port (443 auto-enables HTTPS)
username Yes — Camera username
password Yes — Camera password
verify_ssl No false Verify SSL certificates

2. Add the MCP server

Option A: stdio (Claude Code / Claude Desktop)

Using the Claude Code CLI (cameras config auto-discovered from ~/.config/dahua-mcp/):

# From PyPI
claude mcp add dahua-mcp -- uvx dahua-mcp

# From a local clone
claude mcp add dahua-mcp -- uv run --directory /path/to/dahua-mcp dahua-mcp

# With a custom config path
claude mcp add dahua-mcp -e DAHUA_CAMERAS_CONFIG=/path/to/cameras.json -- uvx dahua-mcp

Or manually add to your Claude Code settings (~/.claude.json) or Claude Desktop config:

{
  "mcpServers": {
    "dahua-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": ["dahua-mcp"]
    }
  }
}

Or run from a local clone:

{
  "mcpServers": {
    "dahua-mcp": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "--directory", "/path/to/dahua-mcp", "dahua-mcp"]
    }
  }
}

To override the config location, add an env block:

{
  "env": {
    "DAHUA_CAMERAS_CONFIG": "/custom/path/cameras.yaml"
  }
}

Option B: Docker container (HTTP transport)

Run the container with your config file volume-mounted:

docker run -d \
  -v /path/to/cameras.json:/config/cameras.json:ro \
  -p 8000:8000 \
  ghcr.io/brianegge/dahua-mcp:latest

Then add the HTTP server to your MCP client config:

{
  "mcpServers": {
    "dahua-mcp": {
      "type": "http",
      "url": "http://localhost:8000/mcp"
    }
  }
}

To use a YAML config with Docker:

docker run -d \
  -v /path/to/cameras.yaml:/config/cameras.yaml:ro \
  -e DAHUA_CAMERAS_CONFIG=/config/cameras.yaml \
  -p 8000:8000 \
  ghcr.io/brianegge/dahua-mcp:latest

To add bearer token authentication:

docker run -d \
  -v /path/to/cameras.json:/config/cameras.json:ro \
  -e MCP_HTTP_BEARER_TOKEN=your-secret-token \
  -p 8000:8000 \
  ghcr.io/brianegge/dahua-mcp:latest
{
  "mcpServers": {
    "dahua-mcp": {
      "type": "http",
      "url": "http://localhost:8000/mcp",
      "headers": {
        "Authorization": "Bearer your-secret-token"
      }
    }
  }
}

Environment Variables

All settings beyond camera credentials are configured via environment variables:

Variable Default Description
DAHUA_CAMERAS_CONFIG ~/.config/dahua-mcp/cameras.yaml Path to cameras config file (JSON or YAML). Auto-discovers from ~/.config/dahua-mcp/ if not set.
DAHUA_TIMEOUT 20 HTTP request timeout in seconds
DAHUA_SNAPSHOT_DIR system temp dir Directory take_snapshot writes JPEGs to
READ_ONLY_MODE false Disable all write tools (reboot, set_config, etc.)
DISABLED_TAGS — Comma-separated tags to disable (e.g. destructive,write)
LOG_LEVEL INFO Logging level
RATE_LIMIT_ENABLED false Enable rate limiting
RATE_LIMIT_MAX_REQUESTS 60 Max requests per window
RATE_LIMIT_WINDOW_MINUTES 1 Rate limit window in minutes
MCP_TRANSPORT stdio Transport type: stdio, sse, or http
MCP_HTTP_HOST 0.0.0.0 HTTP bind host
MCP_HTTP_PORT 8000 HTTP bind port
MCP_HTTP_BEARER_TOKEN — Optional bearer token for HTTP auth

Available Tools

Discovery

Tool Description Read-Only
list_cameras List all configured cameras (name, host, port) Yes

System Info

Tool Description Read-Only
get_system_info Get full system info (device type, serial, firmware, etc.) Yes
get_device_type Get the device type/model Yes
get_software_version Get firmware version Yes
get_machine_name Get the camera's machine name Yes
get_serial_number Get the serial number Yes
get_hardware_version Get the hardware version Yes
get_vendor Get the vendor/manufacturer Yes

Config Read

Tool Description Read-Only
get_config Get any config section by name Yes
get_motion_detection Get motion detection config Yes
get_video_in_mode Get video input mode (day/night profile) Yes
get_exposure Get exposure mode, shutter range and gain per day/night profile Yes
get_encoding_config Get encoding/streaming config Yes
get_network_config Get network config (IP, gateway, DNS) Yes
get_ntp_config Get NTP time sync config Yes

Config Write

Tool Description Destructive
set_config Set arbitrary config key-value pairs Yes
enable_motion_detection Enable/disable motion detection No
set_shutter_range Cap the slowest shutter (e.g. 1/60) to cut motion blur No
set_exposure_auto Hand exposure back to the camera No
set_record_mode Set recording mode (auto/manual/off) No

System Control

Tool Description Destructive
reboot Reboot the camera Yes
take_snapshot Take a JPEG snapshot, saved to disk (returns the path) No

Storage / Recordings

Tool Description Read-Only
get_storage_info Hard drive status, capacity and health Yes
find_recordings List recorded files, to confirm a recorder is recording Yes

Logs

Tool Description Read-Only
search_logs Search camera logs by time range and type Yes

Firmware quirks

The device protocol lives in aiodahua, which this server is a thin MCP layer over. That is where the digest/basic auth negotiation, the CGI parsing and the firmware quirks are implemented and tested, so the Home Assistant integration and this server share one implementation instead of two.

Behaviours confirmed against real hardware, handled for you:

  • Clearing a config field. Sending Key= with an empty value returns OK and changes nothing. Pass "" to set_config and it is sent as a single space, which the device trims back to "".
  • & cannot appear in a config value. The firmware percent-decodes the whole query string before splitting it on &, so an encoded & still terminates the value — the request either returns HTTP 400 or the value is silently truncated. set_config raises a clear error instead. #, +, %, =, / and ' are all fine and round-trip correctly.
  • used == total on a healthy disk. A recorder pre-allocates the whole drive into fixed-size blocks when it formats, so even a brand new disk reports 100% used. get_storage_info flags this so it is not mistaken for a full disk; use find_recordings to confirm writes are landing.
  • find_recordings channels are 1-based. Channel 0 is rejected.
  • A missing endpoint is an error body, not a 404. Older firmware answers Bad Request!, 2024 builds answer Not Implemented! with HTTP 501, and some failures arrive as HTTP 200 with an error body that would otherwise parse into a plausible-looking dict.
  • Some devices only accept basic auth. Digest is tried first and the fallback is automatic.
  • A Lorex E891AB reboots if asked for audio.cgi, dropping HTTP and RTSP for ~105 seconds. The library refuses that request on the brands known to do it.

Development

git clone https://github.com/brianegge/dahua-mcp.git
cd dahua-mcp
uv sync
uv run pre-commit install  # Install ruff format/lint hook
uv run pytest              # Run tests
uv run ruff check .        # Lint

License

MIT License - see LICENSE file for details.