uc-remote-mcp
Enables natural language interaction with Unfolded Circle Remote 3/Remote Two for configuration, button mapping, UI editing, and backup/restore.
README
UC Remote MCP
MCP server for the Unfolded Circle Remote 3 / Remote Two. Exposes the remote's configuration as conversational tools for Claude — ask questions about your setup, remap buttons, redesign UI pages, and restore from backups, all in plain language.
Requirements
- An Unfolded Circle Remote 3 or Remote Two on the same LAN as your computer
uv(manages Python and dependencies — Python 3.11+ is fetched automatically)- An MCP client — the examples below use Claude Desktop
Quick start (no clone needed)
1. Add the server to Claude Desktop
Add to your Claude Desktop config file
(Windows: %APPDATA%\Claude\claude_desktop_config.json,
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"uc-remote": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/b2dmx/uc-remote-mcp@v0.1.0",
"uc-remote-mcp"
]
}
}
}
That's the whole install — uvx fetches, builds, and caches the server on
first launch. Restart Claude Desktop after saving. (Drop the @v0.1.0 to
track the latest code instead of a pinned release.)
<details> <summary><b>Alternative: clone for development</b></summary>
git clone https://github.com/b2dmx/uc-remote-mcp.git
cd uc-remote-mcp
uv sync
Then point the config at your working copy instead:
{
"mcpServers": {
"uc-remote": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/uc-remote-mcp",
"run",
"uc-remote-mcp"
]
}
}
}
(Windows paths need doubled backslashes: "C:\\path\\to\\uc-remote-mcp".)
Run the tests with uv run pytest.
</details>
2. Pair with your remote (one time)
You need the remote's Web Configurator PIN: on the remote, go to Settings → Web Configurator and toggle it on — the PIN is shown on the remote's screen.
Then just tell Claude:
Discover my Unfolded Circle remote, then set it up with PIN 1234.
discover_remotes finds the remote via mDNS; setup_remote exchanges the PIN
for a long-lived API key stored in %APPDATA%\uc-remote-mcp\config.json
(Windows) or ~/.config/uc-remote-mcp/config.json (macOS/Linux, chmod 600).
The PIN itself is never stored. After that, every tool works without further auth.
Things to ask once it's running
- "What's the battery level on my remote?"
- "List my activities and what's on their UI pages."
- "What does the volume button do in each activity?"
- "Map the PLAY button in the TV activity to the Apple TV's play/pause."
- "Back up my remote's config." / "What changed since that backup?"
Troubleshooting
ConnectTimeout/ tools suddenly fail — the remote parks its HTTP server in standby. Wake it (lift it or press a button) or keep it docked. This is by far the most common failure mode.- Remote not found / timeouts after it worked before — DHCP may have moved
its IP. Ask Claude to run
discover_remotesagain and re-runsetup_remote(or give the remote a DHCP reservation in your router). discover_remotesreturns nothing — mDNS only works on the same subnet/VLAN, and some firewalls block it. Find the IP on the remote (Settings → About → Network) and callsetup_remotewith it directly.- Wrong PIN — the PIN changes each time the Web Configurator is toggled; read it off the remote's screen, not from memory.
Compatibility
Developed and battle-tested against a Remote 3 (firmware/core 0.69.x, API 0.16). The Remote Two exposes the same Core API and should work identically, but hasn't been tested by the author.
Tools (Phase 1 — read-only + backup)
| Tool | Description |
|---|---|
setup_remote |
First-run PIN auth, creates API key, saves config |
discover_remotes |
mDNS scan — finds UC remotes on the LAN |
get_remote_info |
Name, model, firmware, battery, active activities |
list_devices |
All entities; filter by entity_type (media_player, remote, activity, macro) |
get_device |
Full entity config: features, simple_commands, attributes |
list_device_commands |
Commands an entity exposes (features + simple_commands) |
list_activities |
All activities with included entities |
get_activity |
Included entities (+their commands), button overrides, UI pages |
get_button_mapping |
Button→command bindings; scope = activity / remote / device |
list_ui_pages |
UI pages for an activity or remote (needs scope_id) |
get_ui_page |
Items on one page: grid, positions, bound commands |
backup_config |
Full config snapshot to one JSON file (keeps last 50) |
Tools (Phase 2 — mutations)
All mutations default to dry_run=True (preview only) and take an automatic
config backup before any real write. Set dry_run=false to apply.
| Tool | Endpoint | Notes |
|---|---|---|
send_command |
PUT /entities/{id}/command |
One-off command; {cmd_id, params?} |
set_button_mapping |
PATCH /{activities|remotes}/{id}/buttons/{BUTTON} |
entity_id required for activities, ignored for remotes |
bulk_set_button_mapping |
same, fanned across activities | Filter by activity_ids/name_contains; invalid activities skipped with reason; one backup per batch |
update_ui_page |
PATCH /{…}/{id}/ui/pages/{pageId} |
{name?, grid?, items?}; omitted = unchanged, empty items clears; items validated |
delete_ui_page |
DELETE /{…}/{id}/ui/pages/{pageId} |
Irrevocable on device; dry-run shows full content being lost |
set_default_ui_page |
PATCH /{…}/{id}/ui/pages {page_order} |
No explicit default-page property — first page wins, so this reorders |
update_activity_sequence |
PATCH /activities/{id} {options.sequences} |
Steps: {type:"command",command:{…}} / {type:"delay",delay:ms} |
diff_config |
read-only | Live config vs backup file: restore ops + unrestorable differences |
restore_config |
granular PATCH/DELETE/POST per difference | sha256 token flow: 1st call returns token+plan, 2nd call with token + dry_run=false applies |
Command names are validated client-side before a write: for an activity the
target entity_id must be one of its included entities and the cmd_id must be
in that entity's entity_commands/simple_commands; for a remote the cmd_id
must be one of the remote's simple_commands (and remote UI/button commands may
NOT carry an entity_id — a remote operates on itself). Invalid names raise
before any network call.
restore_config restores the customization layer (names, button mappings, UI
pages, sequences of activities/remotes). Entities and integration instances come
from integrations and can't be recreated through these endpoints — those
differences are reported as not_restorable and left untouched.
Notes on the UC data model (verified against firmware 0.69.x)
- Auth:
POST /api/pub/loginwith usernameweb-configurator+ PIN → session cookie →POST /api/auth/api_keys. All other calls useAuthorization: Bearer. - Devices are entities. One physical device often spans several entities
(the LG TV = a
media_player"LG WebOS Apps" + aremote"LG TV"). - Button mappings & UI pages are embedded, not separate endpoints. They live
under
options.button_mappingandoptions.user_interface.pageson each activity (/api/activities/{id}) and remote-entity (/api/remotes/{id}). There is no global profile and no/api/profiles/pagesendpoint on this firmware. - Button targets use
cmd_id. IR remotes (options.kind == "IR") bind to local IR codes with noentity_id; device bindings carry the targetentity_id. - Battery level isn't a JSON field — it's parsed from
/api/system/logs.
Layout
src/uc_remote_mcp/
server.py FastMCP app; wraps every tool with @mcp.tool()
config.py config load/save (%APPDATA%\uc-remote-mcp\config.json)
client/rest.py async httpx REST client (PIN→key auth, Bearer)
tools/_common.py get_client(), localized(), button/page normalizers
tools/*.py discovery, setup, devices, activities, buttons, ui, backup
safety/backup.py collect_config()/create_backup() (reused by Phase 2)
Safety model
- Mutating tools default to
dry_run=True— they show exactly what would change and write nothing until called again withdry_run=false. - Every real write is preceded by an automatic full-config backup
(
%APPDATA%\uc-remote-mcp\backups\, last 50 kept). - Command names and target entities are validated client-side before any network write, so typos fail fast instead of half-applying.
Credits
- Built with Claude — this server was designed, implemented, and live-tested conversationally using Claude Code against a real Remote 3.
- The conversational-tool design was informed by the ha-mcp Home Assistant MCP server.
- API reference: the official Unfolded Circle Core API spec.
This project is not affiliated with or endorsed by Unfolded Circle.
License
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.