The Access server provides event monitoring through a combination of websocket buffering, MCP resources, and query tools.
UniFi Access Controller
|
v WebSocket (event listener)
|
EventManager (processes + serializes events)
|
v Ring buffer (in-memory)
|
EventBuffer (deque, configurable size + TTL)
|
+---> MCP Resources (access://events/stream, access://events/stream/summary)
+---> access_recent_events tool (filtered buffer queries)
+---> access_subscribe_events tool (subscription instructions)
-
WebSocket connection -- The server connects to the Access controller's websocket endpoint when
ACCESS_WEBSOCKET_ENABLED=true. This streams events in real time: door opens, access grants, denials, alarms, etc. -
Event processing -- The
EventManagerreceives raw websocket messages, filters for relevant event types, and serializes them to plain dicts. -
Ring buffer -- Processed events are stored in an
EventBuffer(adequewith configurable max size and TTL). When the buffer is full, the oldest event is silently dropped. -
Client access -- MCP clients read buffered events via resources or tools. No events are lost between polls as long as the buffer does not overflow.
The websocket event listener depends on the authentication path:
- API key auth (port 12445) -- WebSocket event streaming is supported when an API key is configured. This is the primary path for real-time events.
- Proxy session only (port 443) -- WebSocket may not be available in proxy-only mode. REST-based event tools (
access_list_events,access_get_activity_summary) will still work.
If you need real-time events, ensure UNIFI_ACCESS_API_KEY is configured.
Returns the full contents of the event buffer as a JSON array (newest first). Each event includes:
type-- Event type (door_open, door_close, access_granted, access_denied, door_alarm, etc.)door_id-- Source door ID (if applicable)user_id-- User who triggered the event (if applicable)timestamp-- Event timestampresult-- Outcome of the access attempt_buffered_at-- Unix timestamp when the event entered the buffer
A lightweight summary of the buffer: total event count, breakdown by event type, and breakdown by door. Useful for deciding whether to fetch the full stream.
{
"total_events": 42,
"by_type": {"access_granted": 30, "access_denied": 8, "door_open": 4},
"by_door": {"door-id-1": 25, "door-id-2": 17},
"buffer_size": 100
}| Tool | Source | Best for |
|---|---|---|
access_recent_events |
Websocket buffer | Real-time monitoring with filters |
access_list_events |
REST API | Historical queries with time ranges |
access_get_activity_summary |
REST API | Aggregated trends and counts |
access_subscribe_events |
-- | Getting subscription instructions |
-
access_recent_eventsreads from the in-memory buffer. It is fast (no API call) but limited to events received since the server started, up to the buffer size. Supports filtering by event_type, door_id, and limit. -
access_list_eventsqueries the Access controller's REST API. It can access the full event history. Supports filtering by time range, door, user, and result limit.
Use access_recent_events for near-real-time monitoring. Use access_list_events for historical analysis.
MCP push notifications are not yet available from background websocket callbacks due to a FastMCP limitation. The recommended approach is polling:
- Call
access_subscribe_eventsto get resource URIs and instructions - Poll
access://events/stream/summaryevery 5-10 seconds to check for new events - When the summary shows new events, read
access://events/streamfor full details - Or use
access_recent_eventstool with filters for targeted queries
| Variable | Default | Description |
|---|---|---|
ACCESS_EVENT_BUFFER_SIZE |
100 |
Maximum events in the ring buffer |
ACCESS_EVENT_BUFFER_TTL |
300 |
Seconds before buffered events are considered expired |
ACCESS_WEBSOCKET_ENABLED |
true |
Enable/disable the websocket listener |
- High-traffic environments (many doors, frequent access events): increase
ACCESS_EVENT_BUFFER_SIZEto 500+ and decrease poll interval to 2-3 seconds - Low-traffic environments: default values work well; poll every 10-30 seconds
- Memory-constrained: reduce
ACCESS_EVENT_BUFFER_SIZE; each event is ~1-2 KB
Set ACCESS_WEBSOCKET_ENABLED=false to disable real-time streaming. REST-based tools (access_list_events, access_get_activity_summary) will still work. The buffer-based tools and resources will return empty results.