7.9 KiB
emby-mcp (Go)
Go rewrite of the Python Emby.MCP server — a Model Context Protocol (MCP) server that connects an Emby media server to an AI client such as Claude Desktop.
The original 20 tools match the Python version's parameter names and JSON output shapes; additional Go-only tools add library browsing, next-episode resolution, and subtitle/audio-track control.
Features
- Log on to / log out of an Emby media server (username/password, or API key)
- List libraries, select a library, list genres
- Search items by title/album, artist, genre, release years, lyrics, and
item type (
item_types, e.g.Series) — with chunked results for large libraries - Browse an item's children, a series' seasons, and a series' episodes
(
retrieve_item_children,retrieve_season_list,retrieve_episode_list), including per-user played state and resume positions - Resolve the next episode to play (
retrieve_next_episode, modesnext_unplayed/latest) without a library selection - Create/modify playlists, add/remove/reorder items, share playlists
- List player sessions, retrieve play queues, control playback (play/pause/seek/skip, PlayNow)
- Inspect the now-playing item's audio/subtitle tracks
(
retrieve_now_playing) and switch tracks (set_subtitle,set_audio_track) — mid-playback where the player supports it, else restart-at-position
Build
go build -o emby-mcp ./cmd/emby-mcp
Requires Go 1.27+.
Configuration
Create a .env file (in the working directory or next to the binary):
EMBY_SERVER_URL = "http://localhost:8096"
# Either username/password:
EMBY_USERNAME = "user"
EMBY_PASSWORD = "pass"
# ...or an API key plus the user ID it should act as:
# EMBY_API_KEY = "..."
# EMBY_USER_ID = "..."
# Set to False to skip SSL certificate verification (e.g. self-signed).
EMBY_VERIFY_SSL = True
# Max items per search-result chunk; 0 = no limit.
LLM_MAX_ITEMS = 100
# Transport: "stdio" (default) or "http" (streamable HTTP at /mcp).
MCP_TRANSPORT = stdio
# HTTP mode: bind address and idle-session timeout.
MCP_LISTEN_ADDR = "127.0.0.1:8080"
MCP_SESSION_TIMEOUT = 30m
In http mode the EMBY_USERNAME/EMBY_PASSWORD/EMBY_API_KEY variables are
not needed — every request authenticates against Emby (see below).
Usage
Startup checks only (login + list libraries, then exit):
./emby-mcp -check
Run the MCP server on stdio:
./emby-mcp
HTTP mode
MCP_TRANSPORT=http MCP_LISTEN_ADDR=0.0.0.0:8080 ./emby-mcp
Serves the MCP streamable HTTP
transport (SSE streaming) at POST http://<addr>/mcp. /healthz is an
unauthenticated liveness endpoint.
Every request must carry Emby credentials:
Authorization: Basic base64(user:pass)— exchanged for an Emby access token viaAuthenticateByName(cached ~15 min).Authorization: Bearer <token>— an Emby access token or API key used directly. The token's owning user is resolved automatically viaGET /Sessions; server-wide API keys and other ambiguous tokens require a disambiguation header:X-Emby-User-Id: <id>— the Emby user to act as (validated), orX-Emby-Username: <name>— looked up via the public user list.
Each MCP session is isolated: it gets its own library selection and search-chunking state, and requests to a session are rejected if they authenticate as a different user.
Plain HTTP only — put the server behind a reverse proxy for TLS.
Docker
docker run -e EMBY_SERVER_URL="http://emby:8096" -p 8080:8080 \
git.i3omb.com/gronod/emby-mcp:1.0.0
The image defaults to MCP_TRANSPORT=http on 0.0.0.0:8080.
Docker Compose / TrueNAS SCALE
The YAML below is a Compose v2 file suitable for a TrueNAS SCALE Custom App
(Apps → Discover → Custom App / Install via YAML) as well as plain
docker compose up -d.
Replace the placeholder values. On SCALE, map the same keys in the app
environment UI if you prefer not to bake them into the file. Use the NAS
host/IP (or an internal container DNS name) for EMBY_SERVER_URL so the
app can reach Emby; host networking is an alternative if Emby is also
on the same TrueNAS box.
In HTTP mode (MCP_TRANSPORT=http, the image default) per-request Emby
auth is used and EMBY_USERNAME / EMBY_PASSWORD / EMBY_API_KEY are
optional. They are included so the same file also works if you switch
the container to stdio or want a default identity.
# emby-mcp — TrueNAS SCALE Custom App / docker compose
# Placeholders: replace every CHANGE_ME_* value before starting.
services:
emby-mcp:
image: git.i3omb.com/gronod/emby-mcp:1.0.0
container_name: emby-mcp
restart: unless-stopped
ports:
- "8080:8080"
environment:
EMBY_SERVER_URL: "http://CHANGE_ME_EMBY_HOST:8096"
# Username/password login (leave empty if using an API key):
EMBY_USERNAME: "CHANGE_ME_EMBY_USERNAME"
EMBY_PASSWORD: "CHANGE_ME_EMBY_PASSWORD"
# API-key login (leave empty if using username/password).
# EMBY_USER_ID is required when EMBY_API_KEY is set:
EMBY_API_KEY: "CHANGE_ME_EMBY_API_KEY"
EMBY_USER_ID: "CHANGE_ME_EMBY_USER_ID"
EMBY_VERIFY_SSL: "true"
LLM_MAX_ITEMS: "100"
MCP_TRANSPORT: "http"
MCP_LISTEN_ADDR: "0.0.0.0:8080"
MCP_SESSION_TIMEOUT: "30m"
# Scratch image has no shell/curl — omit healthcheck, or front with a proxy.
# networks: [host] # uncomment instead of ports: if Emby is on the same NAS
MCP endpoint after start: POST http://<truenas-host>:8080/mcp
Liveness: GET http://<truenas-host>:8080/healthz
Claude Desktop example (stdio)
{
"mcpServers": {
"Emby": {
"command": "/path/to/emby-mcp",
"args": ["-env", "/path/to/.env"]
}
}
}
Streamable HTTP example (mcpServers)
Point an MCP client at the container's /mcp endpoint. Every request must
carry Emby credentials in headers (see HTTP mode above). Replace the
placeholders.
API key (add X-Emby-User-Id or X-Emby-Username if the key is server-wide):
{
"mcpServers": {
"Emby": {
"type": "http",
"url": "http://CHANGE_ME_MCP_HOST:8080/mcp",
"headers": {
"Authorization": "Bearer CHANGE_ME_EMBY_API_KEY",
"X-Emby-User-Id": "CHANGE_ME_EMBY_USER_ID"
}
}
}
}
Username and password (sent as HTTP Basic; the server exchanges them for an Emby access token):
{
"mcpServers": {
"Emby": {
"type": "http",
"url": "http://CHANGE_ME_MCP_HOST:8080/mcp",
"headers": {
"Authorization": "Basic CHANGE_ME_BASE64_USER_COLON_PASS"
}
}
}
}
CHANGE_ME_MCP_HOST is the host where emby-mcp is published (TrueNAS IP or
hostname), not the Emby server. CHANGE_ME_BASE64_USER_COLON_PASS is
base64("<username>:<password>"). Clients that do not understand "type": "http" may accept the same block with only "url" and "headers".
Development
go build ./...
go vet ./...
go test ./...
Layout:
cmd/emby-mcp— entry point, config loading, startup checksinternal/emby— Emby REST client (auth, libraries, items, playlists, sessions)internal/server— MCP tool handlers (official go-sdk)internal/mcphttp— streamable-HTTP transport with per-request Emby authinternal/state— shared session state (library selection, search chunking)internal/config—.envparsinginternal/textutil— Unicode→ASCII folding for lyrics searchdocs/api/emby_openapi.json— Emby REST API spec (reference)
Upstream endpoint conformance is checked by internal/emby/spec_conformance_test.go; see docs/api/endpoint_validation.md for the audit report. Planned features live in docs/ROADMAP.md.
License
GPL v3 — see LICENCE.md (same license as the upstream
angelltek/Emby.MCP project).