axis-camera-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@axis-camera-mcpSet the camera clock to the current time."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
axis-camera-mcp
An MCP (Model Context Protocol) server that lets an LLM (Claude Code/Desktop) talk to an Axis network camera over VAPIX (Axis's HTTP/JSON camera API): view the live stream in VLC, control pan/tilt/zoom, adjust image sensor settings (brightness, exposure, gain, white balance), capture snapshots, add text/image overlays, get/set the clock, manage local camera user accounts, and control the light.
Built and live-verified against an AXIS M3057-PLVE Mk II on a local network.
See docs/live-verification-checklist.md for exactly what's been tested live vs.
not, and docs/open-questions.md for a confirmed limitation of this specific camera:
PTZ commands succeed and the reported position updates, but produce no real visual
pan/tilt/zoom effect on this fixed-camera model.
Architecture
axis_camera_mcp/
config.py Loads camera host/username/password from config.json
vapix/
session.py requests.Session + HTTP Digest auth
jsonrpc.py Client for VAPIX's modern JSON-RPC-style APIs
legacy.py Client for VAPIX's legacy query-param/plain-text CGIs
client.py VapixClient facade - the only thing tools/* imports
exceptions.py
vlc_launcher.py Starts/stops the local VLC install to view the RTSP stream
rag/ Local knowledge base: VAPIX docs + this project's own
live-verified findings, hybrid BM25+embedding search
(see "Knowledge base (RAG)" below)
app.py / context.py Shared FastMCP instance + lazy VapixClient/VlcLauncher/
HybridRetriever singletons
tools/ One module per capability, each registering @mcp.tool()s
server.py Entrypoint: import tools, preload_rag(), mcp.run()
tests/
test_*.py Unit tests, mocked HTTP, no live camera needed
manual/ Plain scripts (not pytest) exercised against the real camera
docs/
live-verification-checklist.md One row per tool - what's actually been tested live
open-questions.md Confirmed limitations and things not yet triedTwo VAPIX request styles exist side by side on this camera, and the client layer handles both without leaking the difference into tool code:
Modern JSON-RPC style (
api-discovery,basicdeviceinfo,streamprofile,lightcontrol,time.cgi,dynamicoverlay): POST JSON body{"apiVersion", "method", "params"}, response{"data"}or{"error"}.Legacy query-param/plain-text CGIs (
com/ptz.cgi,com/ptzconfig.cgi,pwdgrp.cgi,usergroup.cgi,jpg/image.cgi): plain GET with query params, responses are plain text (sometimeskey=valuelines) or, for the snapshot endpoint, raw JPEG bytes.
Setup
Credentials live in a config file, never in code or environment variables:
copy config.example.json config.json
notepad config.json # fill in host/username/passwordconfig.json is gitignored - it never gets committed.
py -m venv .venv
.venv\Scripts\python -m pip install -e ".[dev]"Running
.venv\Scripts\python -m axis_camera_mcp.serverKnowledge base (RAG)
A local, offline knowledge base lets the LLM search the real VAPIX API docs and this project's own hard-won, live-verified findings (undocumented required fields, real parameter values confirmed only through live trial-and-error, etc.) instead of guessing or re-deriving them each time.
All open-source, all local - no paid API, no vector-DB service:
Crawler (
rag/crawler.py): plainrequests+ BeautifulSoup againstdeveloper.axis.com(a server-rendered Docusaurus site - no headless browser needed), plus this project's own README/docs/vapix/*.pyas local sources.Chunking (
rag/chunker.py): paragraph-aware, ~700 chars with 100 overlap.Retrieval (
rag/retriever.py): hybrid - BM25 (rank-bm25, exact keyword matches likestreamProfileName) and dense embeddings (sentence-transformers,all-MiniLM-L6-v2, CPU-only, for paraphrased questions), combined via reciprocal rank fusion. A plain numpy.npzfile serves as the vector store (a few hundred chunks makes brute-force cosine search microseconds-scale - not worth a real vector-DB dependency).Tracking (
rag/tracking.py): a local SQLite file logs every ingestion run (per-source chunk/char counts, ok/error status) and every query (text, results, scores) - inspectable directly or via therag_statstool.
Setup: sentence-transformers pulls in torch, a genuinely heavy install. On a
CPU-only machine, install the CPU-only wheel explicitly first to keep it much
smaller than the default resolved build:
.venv\Scripts\python -m pip install torch --index-url https://download.pytorch.org/whl/cpu
.venv\Scripts\python -m pip install -e ".[dev]"Build/refresh the index (a separate, manually-run step - never runs automatically at server startup, so startup stays fast and network-call-free):
.venv\Scripts\python -m axis_camera_mcp.rag.ingestEdit axis_camera_mcp/rag/sources.json to add/remove doc pages or local files, then
re-run the command above. The embedding model (~80MB) downloads once from Hugging
Face Hub on first run and is cached afterward; everything else is fully offline.
Generated index files (rag/data/*.jsonl, *.npz, *.sqlite3) are gitignored.
Query it with the search_knowledge and rag_stats tools (see Tool reference below).
Register with Claude Code/Desktop
{
"mcpServers": {
"axis-camera": {
"command": "<path-to-repo>\\.venv\\Scripts\\python.exe",
"args": ["-m", "axis_camera_mcp.server"]
}
}
}Restart Claude Code/Desktop after adding this for the tools to appear.
Testing
.venv\Scripts\python -m pytest tests/ --ignore=tests/manual # unit tests, mocked HTTP
.venv\Scripts\python tests/manual/live_smoke_test.py # read-only checks against the real cameraThe tests/manual/ scripts talk to the real camera in config.json - review what
each one does before running it against a camera you care about. live_smoke_test.py
is read-only. The mcp_harness_*.py scripts exercise the full MCP tool-call path
(including mutations like PTZ moves, clock/timezone changes, and user add/remove) -
these were this project's own live-verification scripts; treat them as reference,
not something to re-run casually against a production camera.
Tool reference
Device/API info (read-only): get_device_info, get_supported_apis, get_camera_overview
Streaming (view the live stream in the local VLC install): start_stream,
stop_stream, get_stream_status, list_stream_profiles
Snapshot (single still frame, separate from the video stream): save_snapshot
(file only, the LLM never sees the image), get_snapshot (returned directly to the
LLM so it can describe what's in view)
PTZ (pan/tilt/zoom - see docs/open-questions.md re: no visual effect on
this specific camera): ptz_move_absolute, ptz_move_relative, ptz_get_position,
ptz_save_preset, ptz_goto_preset, ptz_list_presets
Image sensor (brightness/contrast/saturation/sharpness, exposure, gain,
shutter, white balance - the actual sensor/ISP settings, distinct from PTZ's
framing/crop and light's physical illuminator): get_image_settings,
set_image_appearance, set_exposure_mode, set_exposure_value,
set_manual_gain, set_manual_shutter, set_white_balance
Overlays (text/image burned into the video stream): add_text_overlay,
set_text_overlay, add_image_overlay, set_image_overlay, list_overlays,
remove_overlay, get_overlay_capabilities
Clock: get_clock_info, set_clock, set_timezone
Light (this specific camera has no light hardware - confirmed via
get_light_status, which reports that clearly): get_light_status,
activate_light/deactivate_light, enable_light/disable_light,
set_light_intensity, get_valid_intensity, set_light_auto_mode
Users - mutations require confirm: true, the one area with real security
consequence: list_users (returns every account with its privilege groups, for a
privileged caller), add_user, update_user, remove_user
Knowledge base (see "Knowledge base (RAG)" above): search_knowledge (hybrid
BM25+embedding search over VAPIX docs and this project's own live-verified findings,
with source citations and scores), rag_stats (what's indexed, from where, when,
and recent query history - states plainly if the index needs rag.ingest run)
Safety
User-account mutations (add_user, update_user, remove_user) require an explicit
confirm: true tool argument and raise a clear error without it - this is the one
area where a careless call has real security consequence (unauthorized access or an
accidental lockout). Everything else (PTZ, light, clock, streaming, overlays, snapshots)
is unconfirmed by design: it's freely reversible and this is a single camera the user
owns directly, not a shared production system.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server for Pentest-Tools.com: run scans, manage findings and reports via your preffered LLM.
MCP server for Clipkit — gives AI agents a video toolbox via the Clipkit schema.
MCP server for AI dialogue using various LLM models via AceDataCloud
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/cdp/GSX2026-axis-camera-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server