A Scrypted plugin that exposes a Streamable HTTP Model Context Protocol endpoint, with OAuth backed by Scrypted user accounts. AI assistants (Claude Desktop, Claude Code, etc.) connect to your running Scrypted server and call MCP tools to inspect logs, manage plugins, query devices, and snapshot the database.
Once installed and the plugin is running, the MCP endpoint is:
<your-scrypted-base-url>/endpoint/scrypted-mcp/public/mcp
Use whichever base you use for the Scrypted UI — https://host:10443 if you're on the secure port, or
http://host:11080 if you're on the insecure one. The plugin auto-detects the protocol from the inbound port (or trusts
x-forwarded-proto if you have a reverse proxy in front), so OAuth metadata documents come back with URLs your client
can actually reach.
You don't need to point clients at any other URL — the protected-resource metadata at
/endpoint/scrypted-mcp/public/.well-known/oauth-protected-resource advertises the authorization server, which
advertises /authorize, /token, /register, and the JWKS document.
The plugin is its own OAuth 2.1 Authorization Server. It supports:
- Dynamic Client Registration (RFC 7591) — your MCP client registers itself the first time it connects; no manual config needed.
- Authorization Code + PKCE (S256) — the only supported flow. Public clients only, no
client_secret. - ES256 JWT access tokens, valid 1 hour, signed with an EC P-256 key generated on the plugin's first boot and persisted in plugin storage.
- Rotating refresh tokens, valid 30 days. Every
/tokenresponse includes a freshrefresh_token; redeeming one invalidates the previous, so a stolen RT works at most once before the legitimate client's next refresh fails and forces re-auth (surfacing the breach). Refresh tokens persist in plugin storage; wipe it to hard-revoke every chain.
The login step is just your existing Scrypted session. When the MCP client opens /authorize in your browser,
Scrypted's auth pipeline checks the cookie and populates the username for us. Auto-approve happens immediately — no
consent screen.
First-time UX: if you aren't already logged into the Scrypted UI in the same browser, hitting /authorize shows a "
log into Scrypted first" hint page. Sign in to Scrypted in another tab, then retry the connection from your MCP client.
list_plugins— All installed plugins with id / pluginId / name / type.get_plugin_info— pid, pending RPC calls, object count for one plugin.reload_plugin— Restart a plugin host (picks up code changes).kill_plugin— Kill a plugin host without auto-reload.install_plugin— Install / upgrade a plugin from npm.update_plugins— Upgrade every installed plugin to its latest npm version.npm_info— Query npmjs.org via the Scrypted server. Use"scrypted-kasa"for package metadata, or"-/v1/search?text=keywords:scrypted-plugin"to discover plugins.rename_device_id— Rename a Scrypted device id (briefly kills the owning plugin).set_mixins— Replace the list of mixins on a device. Readget_devicefirst if you only want to add/remove one.get_storage/set_storage— Read / replace a plugin device's persistent KV storage.get_id_for_native_id— Reverse-lookup a Scrypted device id from a plugin's internalnativeId.disconnect_clients— Disconnect all websocket clients of a plugin (forces reconnection).clear_console— Clear a plugin device's console buffer.
list_devices— All devices. Filter by interface / type / name substring.get_device— Snapshot every state property on a device.call_device_method— Invoke a method on a device (turnOn,setBrightness,getSettings, …).
get_logs— Retained server logs (~48 h). Filter by component, level,sinceMs.clear_logs— Clear the server-side log buffer.list_alerts— Persisted alerts (plugin crashes, warnings).remove_alert— Remove a single alert by id.clear_alerts— Remove all alerts.
get_server_info— Scrypted version +SCRYPTED_*environment variables.restart_server— Restart the Scrypted server. The MCP connection drops.update_server— Trigger a server update (webhook or.updatefile + restart).get_dotenv— Read the contents of the Scrypted.envfile.set_dotenv— Overwrite the Scrypted.envfile (verbatim — include all keys).
list_users— All Scrypted user accounts (username + admin flag).add_user— Create a user. OmitaclIdto create an admin.remove_user— Delete a user by username.
get_local_addresses— Configured local addresses / interface names.set_local_addresses— Replace local addresses. Pass interface names (en0) or IPs.get_external_addresses— External (publicly reachable) addresses for a plugin endpoint.set_external_addresses— Replace external addresses for a plugin endpoint.get_cors— CORS origin allowlist for a plugin endpoint.set_cors— Replace the CORS origin allowlist for a plugin endpoint.
list_cluster_workers— Registered cluster workers (id, name, labels, mode, address, fork count).
create_backup— Snapshot the Scrypted database. Returns the ZIP inline as a base64-encodedEmbeddedResource(plus a JSON summary with byte count + sha256) for the agent to save locally. Nothing is written to the Scrypted host filesystem.restore_backup— Restore from a base64-encoded backup ZIP. The MCP host decodes it in memory and hands the bytes directly to Scrypted — no tmp file is staged on the host. Destructive — wipes the database and installed plugin files.
restore_backup is gated three ways before it runs:
- The
confirmargument must equal the exact stringRESTORE FROM BACKUP. - The MCP client must support elicitation and the
user must explicitly select
RESTOREin the confirmation dialog. Without elicitation support the tool refuses to run. - The tool is annotated
destructiveHint: trueso well-behaved clients require user approval before the call.
The MCP connection drops when the server restarts at the end of a successful restore.
The plugin's Settings tab in the Scrypted UI surfaces both configurable knobs and a live health snapshot. Open * Plugins → MCP Server → Settings* to see them.
Two read-only URLs the plugin computes from the live Scrypted listening address — paste whichever one matches how you're reaching Scrypted.
- MCP endpoint URL (HTTPS) — for production clients and anything behind a reverse proxy.
- MCP endpoint URL (HTTP) — for local-network clients and debugging where the self-signed HTTPS cert is awkward. Don't expose to the internet.
| Setting | Default | Range | What it does |
|---|---|---|---|
dcr_max_clients |
100 | ≥ 1 | Cap on persisted DCR client registrations. When a /register would push the total above this, the least-recently-used registration is evicted. |
access_token_ttl_sec |
3600 (1 h) | 60 – 86400 | Lifetime of issued JWT access tokens. Shorter = tighter security; longer = less re-auth churn. |
refresh_token_ttl_sec |
2592000 (30 d) | 3600 – 31536000 | Lifetime of issued refresh tokens. Each refresh rotates the token (single-use), so this is the upper bound on how long a stolen RT remains valid. |
max_restore_mb |
500 | 1 – 10240 | Maximum decoded backup size that restore_backup will accept. Bump if your Scrypted database exceeds the default. |
All four values are read at request time, so changes take effect on the next inbound request — no plugin reload required.
Read-only health fields, refreshed each time the Settings tab is opened.
- Active MCP sessions — In-memory sessions currently held open. Idle sessions are reaped after 1 h.
- Registered DCR clients — Persisted client registrations in plugin storage.
- Persisted refresh tokens — Refresh tokens currently in plugin storage.
- Revoke all tokens & disconnect clients — DESTRUCTIVE button. Drops every refresh token, deletes every DCR registration, rotates the JWT signing key (existing access tokens immediately stop verifying), and closes all active MCP sessions. Every client will need to re-register and re-authenticate. Use it if you suspect a token leak or want to force a clean slate.
Search for scrypted-mcp in your Scrypted plugin browser, or install via the npm registry:
# from the Scrypted UI: Plugins → Install Plugin → Search npm: scrypted-mcpThe plugin appears as MCP Server under your installed plugins. Click into it to see the endpoint URL in the console output on first boot.
claude mcp add scrypted --scope=user --transport http https://your-scrypted-host/endpoint/scrypted-mcp/public/mcpThe first call triggers the OAuth flow in your browser. Claude Code runs on your machine, so a LAN-only Scrypted host works fine.
Only works if your Scrypted server is reachable from the public internet. Claude Desktop's
streamable-httpMCP support — whether configured via Settings → Connectors or Settings → Developer → Edit Config — proxies through Anthropic's backend, which cannot reach LAN-only Scrypted hosts. If your server lives behind a Cloudflare Tunnel / Tailscale Funnel / equivalent with a public hostname and real TLS, follow the steps below; otherwise stick with Claude Code.
In Settings → Developer → Edit Config add:
{
"mcpServers": {
"scrypted": {
"type": "streamable-http",
"url": "https://your-public-scrypted-host/endpoint/scrypted-mcp/public/mcp"
}
}
}Open the Scrypted UI and sign in (in the same browser Claude Desktop will use for OAuth). Then start a Claude Desktop
session — it will redirect you through /authorize, register itself, and connect.
If your MCP client logs HTTP 404 ... Cannot POST /register (or any other path at the host root), the plugin's OAuth
metadata advertised URLs your client couldn't reach. Most often this means the protocol got mis-detected — check that
your client's URL matches the port (https → 10443, http → 11080) and that nothing in front of Scrypted is rewriting
Host without forwarding x-forwarded-proto.
After fixing the URL, re-run claude mcp remove scrypted and re-add it (or equivalent for your client) to clear any
cached OAuth discovery state.
If the connection drops and you clear the stored credentials in Claude Code (/mcp → Scrypted → Clear authentication),
click Reconnect before Authenticate. Otherwise the auth flow fails with a discovery 404.
Why: the MCP SDK only learns where this plugin's Authorization Server lives by reading the
WWW-Authenticate: Bearer resource_metadata="..." header that /mcp returns on a 401. Reconnect triggers a fresh POST
that produces that 401 and caches the metadata URL; Authenticate alone uses whatever discovery state was already
cached, and after a Clear authentication there is none — so it falls back to default URLs at the host root, which
Scrypted's root Express returns 404 for. Reconnect first → metadata cached → Authenticate succeeds.
npm install
npm run build # → out/main.nodejs.js + out/plugin.zip
npm run scrypted-deploy # push to a running Scrypted serverSee the Scrypted SDK docs for plugin development specifics.