frigate-mcp
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
frigate-mcp
Read-only Frigate MCP tools with an optional pi-durable code-mode bridge.
Implemented: issues #1–#4 — the stdio get_system_status, list_cameras, get_capabilities, list_events, get_event, and search_events vertical slices, with managed login, camera scope, bounded retries, paged event investigation, and semantic search. Media, restart acceptance tests, and remote Streamable HTTP are tracked in issues #5–#9. This is not the complete v1 server.
Develop and verify
Use Node.js 24 LTS, version 24.15.0 or newer within 24.x.
npm ci
npm run typecheck
npm test
npm run smokeThe smoke command starts a controlled HTTP fixture and runs real QuickJS code mode inside a pi-durable harness. A scripted faux provider supplies the predetermined tool call; it does not contact an LLM. The script discovers the Frigate namespace and calls through the durable bridge → stdio MCP server → fixture HTTP API. Only the selected structured result reaches the simulated model. Tests also exercise ordinary MCP clients, login/session refresh, camera scope, auth failures, schema validation, cancellation, limits, retries, and transport failures.
No NVR or provider credentials are required. Tests never contact a real Frigate instance or modify footage. npm run build produces the executable and declarations; npm pack --dry-run checks the distributable.
Related MCP server: trocco-mcp-tools
Local stdio server
Configure an exact API root, including /api and any reverse-proxy path prefix. A trailing slash is optional. The server will not append another /api.
export FRIGATE_API_ROOT=https://frigate.example.internal/api
export FRIGATE_USERNAME='viewer'
export FRIGATE_PASSWORD='<password from your secret manager>'
node dist/cli.jsA stdio MCP client must launch this process; this is not an interactive shell command. After local installation the executable is frigate-mcp. Stdout is reserved for MCP messages. Startup failures produce a sanitized stderr diagnostic and nonzero exit status.
Example MCP client configuration (use an absolute executable path and your client's secret injection mechanism):
{
"mcpServers": {
"frigate": {
"command": "node",
"args": ["/absolute/path/to/frigate-mcp/dist/cli.js"],
"env": {
"FRIGATE_API_ROOT": "https://frigate.example.internal/api",
"FRIGATE_USERNAME": "${FRIGATE_USERNAME}",
"FRIGATE_PASSWORD": "${FRIGATE_PASSWORD}"
}
}
}
}Environment substitution in this JSON is client-specific. It is not a configuration loader supplied by pi-durable. Do not commit credentials or place them in tool arguments.
Environment variable | Default / meaning |
| Required absolute HTTP(S) API root, without URL credentials, query, or fragment |
| Optional Frigate username for managed |
| Optional Frigate password for managed |
| Optional externally supplied bearer JWT; forwarded verbatim and never refreshed |
|
|
| Optional comma-separated camera allowlist; discovery and statistics are restricted to it |
| Optional PEM file with a private CA to trust in addition to the system store (HTTPS only) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Prefer authenticated Frigate port 8971, directly or through a trusted TLS reverse proxy, and a least-privilege viewer account. Port 5000 bypasses Frigate role enforcement; use it only on a trusted private network, with explicit FRIGATE_TRUSTED_UNAUTHENTICATED=true and explicit HTTP opt-in if applicable. TLS verification stays enabled. FRIGATE_CA_CERT_FILE adds a private CA without disabling verification; Node's NODE_EXTRA_CA_CERTS also works at process startup. Never use NODE_TLS_REJECT_UNAUTHORIZED=0.
Never supply Frigate's JWT signing secret. The MCP server does not listen on an HTTP port yet.
Authentication
Three operator-selected modes:
Managed session (
FRIGATE_USERNAME+FRIGATE_PASSWORD): the server signs in withPOST /login, reads Frigate's configured session cookie (FRIGATE_AUTH_COOKIE_NAME, defaultfrigate_token), and sends it with later reads. The password is only ever sent in the login request body.External bearer token (
FRIGATE_BEARER_TOKEN): the token is forwarded verbatim. It is never refreshed, so an expired token produces an actionable authentication error instead of a retry loop.Unauthenticated: only for a trusted network or an instance with Frigate authentication disabled.
Session renewals are serialized, so concurrent reads share one login instead of stampeding the upstream. Each read performs at most one authentication retry. Rejected credentials are remembered: bad credentials fail once, actionably, and are not retried. A session that Frigate revokes is re-authenticated once per read and then reported.
Camera scope
FRIGATE_CAMERAS restricts what the server may observe:
list_camerasreturns only the allowlisted cameras, their zone names, and detection state.get_system_statusdrops statistics for cameras outside the scope and omits unscopable global aggregates (detector inference speed) instead of leaking counts or names.A request for a camera outside the scope is rejected with a
forbiddenerror rather than broadened.
Only allowlisted configuration fields are read from GET /config: camera identifiers, zone names, detection state, and tracked object labels. Raw configuration, RTSP URLs, provider credentials, filesystem paths, and environment variables never leave the server.
Tool contracts
get_system_status({})
Makes only GET /, /version, and /stats beneath the configured API root. Unknown input properties are rejected. It returns:
structuredContent.ok,data, andmeta; failures also include a stableerrorand MCPisError: true.Independent health, version, and statistics component outcomes, including partial results.
authorizedApiAccess:confirmedonly after a successful, valid protected/statsread;deniedfor 401/403; otherwiseunknown.Sanitized camera FPS/detection fields, detector inference speeds, service uptime and update time. No raw statistics, process command lines, RTSP URLs, storage paths, or configuration.
Observation time, contract release, and compatibility warnings. Retrieval time is not a camera frame timestamp.
list_cameras({ camera? })
Returns the permitted camera identifiers, their zone names, and detection state, plus whether the camera scope is restricted or unrestricted. An optional camera filter narrows the list; a camera outside the scope is rejected.
get_capabilities({})
Returns the tested Frigate version and compatibility, capability flags (supported or unknown), the tracked object labels, and the effective policy limits (scope, permitted camera count, deadline, response limit, concurrency).
Capability detection never searches events, downloads media, or claims a feature because public health succeeded. Uncertain capabilities — semantic search, image snapshots — are reported as unknown. Semantic search additionally cannot be confirmed without a billed upstream search.
list_events({ after, before, ... })
Lists object events (GET /events) inside a bounded interval. after and before are required and are UTC Unix seconds; millisecond values are rejected with an actionable units error, as are reversed intervals and windows longer than FRIGATE_EVENTS_MAX_WINDOW_SECONDS.
Optional typed filters are translated to the upstream query: cameras, labels, subLabels, zones, hasClip, hasSnapshot, inProgress, minScore, maxScore, limit, offset, and sort (date_desc by default). No arbitrary query parameters, paths, or headers are exposed.
The result reports page (limit, offset, returned, nextOffset when a page was filled, and the effective ordering), the requested interval, and completeness. A filled page is reported as truncated with complete: false plus a warning — a partial page is never presented as an exact total or as evidence that nothing else happened. Offset paging is the only pagination the pinned release supports; no cursor is invented. Duplicate event identifiers within a page are removed.
get_event({ eventId })
Reads one event (GET /events/{event_id}) and returns its identifier, camera, label, sub-label, zones, start/end times, evidence availability (hasClip, hasSnapshot), and optional score and speed. Event identifiers are validated and URL-encoded; path traversal is rejected before any upstream request.
Events outside the configured camera scope are rejected with forbidden and their metadata is never included in the error. Descriptions, OCR, and license-plate text are untrusted upstream data and are not returned. No thumbnail data is embedded.
search_events({ query, after, before, ... })
Searches object events with a natural-language query over GET /events/search, inside the same bounded UTC Unix-second interval and with the same camera scope, byte limits, deadlines, and completeness rules as list_events. searchType selects thumbnail, description, or both; unsupported modes are rejected rather than silently ignored.
The result states the observed capability: supported after a successful search, disabled when the upstream service reports that semantic search is not enabled, unsupported when the release does not provide the endpoint, and unknown when the state could not be determined. get_capabilities never triggers a search, so it reports semanticSearch as unknown until a search is actually performed.
Relevance is reported as the upstream searchDistance and searchSource. A distance is not a confidence score and is not presented as one. The search is read-only: it never regenerates descriptions or embeddings, manages models, invokes Frigate Chat, or starts background work.
Small structured results are mirrored as JSON text in content for ordinary clients. Output schemas bound camera/detector entries and identifier lengths; oversized or malformed upstream data fails rather than being silently truncated. Redirects are rejected, and upstream error bodies are never echoed. All components of a read share one deadline; reads are sequential, avoiding a fan-out beyond the configured concurrency bound.
Transient failures (network errors, 408, 429, 5xx) are retried at most twice within the deadline, with jitter and bounded Retry-After handling. Authorization, not-found, and validation failures are never retried.
Compatibility baseline
The fixture contract was checked against Frigate v0.18.2 tagged source:
HTTP handlers: plain-text health/version and authenticated statistics
Authentication:
/login, thefrigate_tokencookie, and role checks
0.18.2 and its hexadecimal commit-suffixed builds are classified supported for the status and discovery contracts only. Older pre-0.18 releases are classified unsupported; other versions/build formats are unknown. Probes may still succeed on untested versions, but the result explicitly warns about compatibility. This is source/fixture contract testing, not live-NVR certification, and does not establish support for future tools.
Exact tested dependency versions are locked: MCP SDK 1.32.2, Zod 4.6.7, pi-durable/pi-ai/pi-codemode/Chord 1.4.0. The server entry point never imports durable; the bridge's durable and pi-ai peers are optional for ordinary MCP users.
pi-durable integration
Install the optional durable peers at 1.4.0 in the host application, along with pi-codemode and Chord. Pi CLI's mcp.json alone does not register tools in a pi-durable harness.
import { connectFrigateStdio, frigateNamespace } from 'frigate-mcp/durable';
import { CodemodeSandbox } from '@earendil-works/pi-codemode';
import { createCodemodeExtension } from '@earendil-works/pi-durable/codemode';
const bridge = await connectFrigateStdio({
command: process.execPath,
args: ['/absolute/path/to/frigate-mcp/dist/cli.js'],
env: {
FRIGATE_API_ROOT: process.env.FRIGATE_API_ROOT!,
FRIGATE_USERNAME: process.env.FRIGATE_USERNAME!,
FRIGATE_PASSWORD: process.env.FRIGATE_PASSWORD!,
},
});
const sandbox = new CodemodeSandbox({ memoryLimitBytes: 64 * 1024 * 1024 });
registry.install(bridge.extension);
registry.install(createCodemodeExtension({
sandbox,
namespaces: [frigateNamespace],
}));
// Open your harness with this registry. Keep the bridge installed and its tools
// enabled. They use callers: ['tools'], so only code mode is offered to the model.
// On shutdown: close the harness first, then sandbox.close(), then bridge.close().The bridge initializes MCP, validates the exact known read-only manifest, and registers mcp__frigate__get_system_status, mcp__frigate__list_cameras, mcp__frigate__get_capabilities, mcp__frigate__list_events, mcp__frigate__get_event, and mcp__frigate__search_events as replay-safe nested tools. Connect again on host restart; never persist the live client, credentials, or transport. Tool-contract upgrades require a compatible bridge; an unknown manifest fails closed.
A code-mode investigation can use:
const namespace = await describeNamespace('frigate');
const result = await tools.mcp__frigate__get_system_status({});
if (result.isError || !result.structuredContent.ok) {
text({ error: result.structuredContent.error, partial: result.structuredContent.data });
} else {
text(result.structuredContent.data);
}The bridge preserves MCP content, structuredContent, and isError in durable structured output. Returned schema-valid tool errors resolve inside scripts and must be checked. Transport/harness failures can reject: use try/catch or Promise.allSettled() when independent calls should continue.
Durable code mode journals delivered nested results; interrupted safe reads may run again and observe changed state. This slice does not claim exactly-once external execution, a cross-call snapshot, or completed crash/reopen acceptance coverage (issue #8). The deterministic harness/fixture example is in examples/smoke.ts.
The full approved plan and dependency graph are in docs/spec.md.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read-only MCP access to a documented IT fleet: state, changes, posture. 15 tools.
Read-only MCP tools for AI agent discovery, structured resources, and NIULAI information.
Read-only MCP tools that check if a store and product feed are ready for AI shopping.
Read-only MCP access to sessions, funnels, campaigns, errors, live visitors, and anomalies.
Related MCP Servers
- AlicenseAqualityCmaintenanceA read-only MCP server for AppDynamics that exposes 12 task-oriented tools to query applications, topology, metrics, snapshots, health rules, anomalies, events, alerting config, analytics events, and dependency maps via OAuth API-client credentials.121MIT
- FlicenseBqualityBmaintenanceRead-only MCP tools for TROCCO API, enabling workflow and BigQuery datamart audit information retrieval.2-
- AlicenseCqualityDmaintenanceA local MCP server for the LimaCharlie security platform that provides investigation, administration, and content-review workflows via a broad read-only tool surface with explicit organization scoping and audit logging.100MIT
- AlicenseNot gradedqualityBmaintenanceExposes the LogicMonitor REST API as MCP tools with per-request LMv1 authentication, enabling querying of devices, alerts, reports, and more.Apache 2.0