mcp-kodi
The mcp-kodi server lets an AI assistant control Kodi media players via an MCP interface, enabling comprehensive media management and playback across one or more Kodi instances.
Playback Transport
Play, pause, stop, skip to next/previous items
Play any specific file by path (in-library or not)
Volume & Audio
Adjust volume by relative step, mute/unmute, read current volume/mute state
Now Playing & Notifications
Query current playback state and media info (
nowplaying)Display toast notifications on the Kodi screen (title, message, icon, duration)
Media Search & Discovery
Search music, TV shows, and movies by name, actor, director, season, episode, etc. with paging
List/search contributors (artists, bands, actors, directors) and see where they appear
Inspect audio tracks and subtitles of movies/episodes before playback
Queue Management
Append or insert items into the playback queue
Read current or named playlists without modifying them
Clear all active queues (audio, video, picture) while keeping current item playing
Audio/Subtitle Switching
Switch the active audio track and/or subtitle stream during video playback
Playback History
Query a local playback log with filters (time window, media type, artist, free text, specific IDs); works even when Kodi is offline
Instance Configuration Management
Read, add, update, or remove Kodi instance configurations; passwords are never exposed
Raw JSON-RPC Passthrough (Escape Hatch)
Send any raw JSON-RPC method directly to Kodi; disabled by default and must be manually enabled per instance via
allow_rpc: truein the config — the AI cannot enable this itself
Controls a Kodi media player via its JSON-RPC API, enabling transport control (play, pause, stop), volume adjustment, search/discovery of media, queue management, playback history, and an escape hatch for arbitrary Kodi JSON-RPC methods.
mcp-kodi
An MCP server that lets an AI assistant control a Kodi media player over Kodi's JSON-RPC API. Written in C on the GLib stack.
There is no user-facing CLI: you talk to the AI, the AI calls this server's tools, and the server speaks JSON-RPC to Kodi. The only interface is MCP over stdio.
Where this is going. The plan is a complete, full-featured package that lets any MCP-compatible AI transform the entire media-player experience — not just press buttons, but reinvent how you discover, queue, and enjoy your media. Tell the assistant what you're in the mood for and let it run the room: build the evening's lineup, pick up where you left off last night, adapt on the fly, and surface things you'd never have found yourself. We're aiming for something genuinely new — a mind-blowingly different way to live with your media player.
Status — early, but already remarkable (v0.2.0dev). A growing set of dedicated, purpose-built tools covers transport, volume, search, queue management, and playback history. What makes the build capable beyond that list is the
rpcescape hatch (see below): an opt-in passthrough to any Kodi JSON-RPC method. Between the first-class tools and that escape hatch, an assistant can already drive nearly everything Kodi exposes — remarkable enough that we decided to publish early rather than wait for the full vision to land. Expect frequent releases.
What it can do
Each tool targets a configured Kodi box by key (instance); omit it and the
configured default box is used (with one exception: history spans all
boxes when instance is omitted).
Transport & audio
Tool | What it does |
| Press Play on the remote — resume or begin playback on the target box. |
| Press Pause — pause the active player. |
| Press Stop — stop the active player and clear the playlist it was consuming, so no queued items linger. |
| Press Skip Next — advance to the next track/chapter in the active playlist. |
| Press Skip Previous — go to the previous track/chapter (or restart the current item). |
| Adjust the volume by a relative signed step (percentage points); step |
| Mute the box's audio output. |
| Unmute the box's audio output. |
| Report what is playing without changing anything — also a reachability + state probe. |
| Show a notification toast on the box's screen — title + message, with an optional icon ( |
Search & discovery
Tool | What it does |
| Find playable leaf files by name across |
| Find or list people — bands, solo artists, composers, actors, directors — by optional name substring and/or |
| Inspect a movie or episode's audio tracks and subtitles before playback, from Kodi's cached |
| Switch the audio track and/or subtitle of the video now playing, by the |
Playback & queue
Tool | What it does |
| Play one file by path (typically a |
| Append an item behind the one now playing (a |
| Read a queue without changing it — the active player's playlist, or a named one ( |
| Empty all queues (audio, video, picture) in one call. The current item keeps playing — only the items queued behind it are removed. Not undoable; inspect with |
Bookkeeping & escape hatch
Tool | What it does |
| List recently played items from the local playback log, written as a side effect of every playback-affecting call. Optional ISO-8601 |
| Read or modify the server's own instance config ( |
| Escape hatch — send a raw JSON-RPC method to Kodi and return its reply unchanged. Off by default; opt-in per instance (see below). |
Most action tools return a small player-state snapshot — { "state": "playing"|"paused"|"stopped", "type", "time", "totaltime", "progress", "nowplaying": { "media", "id", "file", "label", "title", … } } — keeping the
player status (state/type/time/totaltime plus a legible progress clause
like "15:38 / 46:40 (33%)") at the top and nesting the loaded media under
nowplaying, with its per-media fields where they apply (artist, album,
track, showtitle, season, episode) — so the assistant always sees the
effect of its action: this covers
play/pause/stop, playfile, queue, dropplaylists, and nowplaying. The
audio tools volume/mute/unmute return { "muted", "volume" } (volume
also adds the min/max bounds). The read tools (searchmedia,
contributors, getplaylist, history) return their own paged result
envelopes.
Every tool except rpc declares these shapes as an MCP outputSchema
(spec revision 2025-06-18) and mirrors each successful result as
structuredContent alongside the JSON text block, so schema-aware clients
can validate and consume results without parsing. Each description also
states the result shape inline — the channel every client shows the model.
rpc declares none: it returns Kodi's raw reply verbatim, which has no
fixed shape.
The rpc escape hatch
rpc POSTs any JSON-RPC method you name to the target box and hands back Kodi's
raw result, unshaped:
// e.g. raise the GUI volume, activate a window, send an input action…
rpc { "instance": "hall", "method": "GUI.ActivateWindow", "params": { "window": "home" } }This is powerful and unconstrained, so it is disabled by default and gated
per instance. A box permits rpc only when its config object carries
"allow_rpc": true. That flag is granted only by hand-editing config.json —
it is intentionally not one of the fields the instances tool can write, so
the assistant can never enable its own escape hatch. Opting a box in is an
explicit, out-of-band human decision; calling rpc on a box that hasn't opted in
returns a clean error and makes no Kodi request.
See the full Kodi method surface in docs/kodi-jsonrpc-catalog.md.
Related MCP server: mcp-mpv-player
Configuration
Set up Kodi for remote control
Before mcp-kodi can reach a box, that Kodi has to allow remote control. In Kodi,
open Settings → Services → Control and turn on Allow remote control via
HTTP, then set a username and password right there. Those same credentials
go into this server's config as auth, in user:pass form.
mcp-kodi speaks HTTPS, so for normal use you put a small reverse proxy in
front of Kodi's plain-HTTP control port. Caddy with
tls internal is the easy choice: it terminates HTTPS and forwards to Kodi on
localhost. Point the instance's host at the proxy and set insecure: true
to accept its self-signed certificate. The full step-by-step walkthrough — the
exact Kodi menus, installing Caddy, and the Caddyfile — is in
docs/kodi-server-setup.md.
Most Kodi players already sit on a home LAN behind a router/firewall and are not exposed to the internet, so turning on the HTTP control interface there is generally safe. The login/password and the HTTPS proxy add defence in depth — just keep the proxy on a trusted network and don't forward its port to the open internet.
Registering a Kodi instance
There are two ways to add a box. The simplest is to let the AI do it: once
the MCP server is loaded, just tell the assistant about your Kodi — its address
and login — and it registers the instance for you through the instances tool
(set), writing the entry into the config file.
You can also do it by hand, by editing config.json directly. Two things
are deliberately off-limits to the AI, so hand-editing is the only way to set
them:
The password.
authis write-only — theinstancestool never returns a stored password, so the assistant can register a box but can never read back the credentials. If you'd rather the AI never see the password at all, type it into the file yourself.The escape hatch.
allow_rpcis not a field theinstancestool can write, so the assistant can never grant itself the unrestrictedrpcpassthrough. Enabling it is always an explicit, out-of-band human edit.
The config file
The server reads ${XDG_CONFIG_HOME:-~/.config}/mcp-kodi/config.json. It holds a
map of named Kodi instances and names one default:
{
"version": 2,
"default": "hall",
"instances": {
"hall": { "name": "Living Room TV", "host": "hall.example.local:8443", "auth": "kodi:<password>", "scheme": "https", "insecure": true, "allow_rpc": true },
"bedroom": { "name": "Bedroom", "host": "bedroom.example.local:8443", "auth": "kodi:<password>", "scheme": "https", "insecure": true }
}
}Field | Meaning |
instance key | The short id every tool references ( |
| The key used when a tool omits |
| Optional human-readable label, surfaced in the tool schema. |
|
|
| HTTP Basic credentials as |
|
|
|
|
|
|
The file is created 0600 in a 0700 directory (it holds passwords) and is
written back atomically when the instances tool changes it.
No config file? A single box can be defined entirely from the environment,
applied to an implicit default instance: KODI_HOST, KODI_AUTH,
KODI_SCHEME, and -k in KODI_CURL_OPTS (→ insecure). With neither a file
nor env, the server exits with a clear error telling you to configure.
A legacy version: 1 flat file (single top-level host/auth/…) is read as one
instance named default and rewritten in the version: 2 shape on the next save.
Note: config is read once at startup. After hand-editing
config.json(e.g. to setallow_rpc), restart the server so it re-reads the file.
Registering with an MCP client
Claude Code
# user scope makes the tools available in every project
claude mcp add --scope user kodi -- /path/to/mcp-kodi/src/mcp-kodiClaude Desktop / other MCP clients
Add a stdio server entry pointing at the binary:
{
"mcpServers": {
"kodi": {
"command": "/path/to/mcp-kodi/src/mcp-kodi"
}
}
}Once registered, ask your assistant things like "what's the player doing in the
living room?" (nowplaying), "find Pink Floyd's Animals and play it" (searchmedia →
playfile), or "mute the bedroom" (mute).
Security notes
config.jsonstores Kodi passwords in plaintext, mode0600. Protect it like any other credential file.insecure: truedisables TLS certificate verification for that box. It is the right choice behind Caddy'stls internalself-signed cert, but means the connection is encrypted, not authenticated against a CA — keep the proxy on a trusted network.The
rpcescape hatch can invoke any JSON-RPC method (including destructive ones likeSystem.Shutdown). It stays off until you deliberately setallow_rpc: truefor a specific box. Enable it only where you trust the assistant to operate unconstrained.
License
Copyright © 2026 Laszlo Pere <laszlopere@gmail.com>. An independent, clean-room implementation.
Free software, licensed under the GNU General Public License v3.0. You may redistribute and/or modify it under those terms; it comes with no warranty. See LICENSE for the full text.
Support this project
If this project is useful to you, please consider supporting it through GitHub Sponsors. Every bit of support keeps it alive — funding the time to ship the frequent releases this is built on, and to chase the full vision.
And if you're still on the fence: ask your Claude whether this project is good for you — and if it recommends it (as most Claude do), support it by becoming a sponsor.
Available Tools
22 toolsaudio_and_subtitle_infoA
Inspect a movie or episode's audio tracks and subtitles BEFORE playback, from Kodi's cached streamdetails (no playback). Name the item by type+id (a searchmedia movie/episode row) or by file. Returns { "file", "audio":[{ "index", "codec", "channels", "language" }], "subtitle":[{ "index", "language" }], "video":[…] }. Embedded streams only: external .srt sidecars and files Kodi hasn't scanned come back as empty arrays; "index" maps to the audio/subtitle setters for embedded streams.
Inputs: id, file, type, instance. Returns: file, audio, video, subtitle
contributorsA
Find or list contributors (bands, solo artists, composers, actors, directors): optional name substring, optional type (band/composer/actor/director) — e.g. {type: "band"} lists all bands. Rows {name, in: [albums|songs|movies|tvshows]} say where each name yields hits — feed the exact name back into searchmedia to drill. Returns { "total", "returned", "offset", "truncated", "rows" }.
Inputs: name, type, count, limit, +2 more. Returns: rows, total, offset, returned, +1 more
dropplaylistsA
Empty all queues: clear the audio, video and picture playlists in one call. The current item keeps playing — only the queued items behind it are removed — so playback is never interrupted. Not undoable; inspect with getplaylist first if the content matters. Returns the player-state snapshot — whatever was playing still is, with nothing queued behind it.
Inputs: instance. Returns: time, type, audio, state, +4 more
getplaylistA
Read the queue without changing it: the items of the active player's playlist — or of a named one (audio/video/picture), which always wins — plus the position of the now-playing item. An empty queue is an empty list. Returns { "type"?, "total", "position"?, "items": [ { "id", "file", "label", "type" } ] }.
Inputs: type, instance. Returns: type, items, total, position
historyA
List recently played items from the local playback log. Filter with an ISO-8601 window (since/until), media/kind/artist, a free-text match or an exact id; page with limit/offset/order, or ask for a count only. An omitted instance returns all boxes. Reads only the local log — no Kodi call, so it works even when no box is reachable. Returns { "total", "returned", "offset", "truncated", "entries": [ { "at", "instance", "kind", "media", "title", "artist", … } ] }.
Inputs: id, kind, count, limit, +8 more. Returns: since, total, until, offset, +4 more
instancesA
Read or modify the configured Kodi instances (action: get/set/remove). Manages the MCP server's own config, not a Kodi device. Returns { "default", "instances": [ { "key", "host", "scheme", "insecure", "has_auth", "allow_rpc", … } ] }.
Inputs: action (required), key, auth, host, +4 more. Returns: default, instances
muteA
Mute the target instance's audio output. Returns { "muted", "volume" }.
Inputs: instance. Returns: muted, volume
notifyA
Show a notification toast on the Kodi box's screen: a title and message, with an optional icon ("info"/"warning"/"error" or any image path/URL) and display time in milliseconds. Doesn't touch playback. Returns { "shown", "title", "message", "displaytime", "image"? }.
Inputs: title (required), message (required), image, instance, +1 more. Returns: image, shown, title, message, +1 more
nowplayingA
Report what is playing on the target instance without changing anything — also a reachability and state probe. Returns the player-state snapshot { "state", "type", "time", "totaltime", "progress", "audio", "subtitle" (audio/subtitle for video: count + current track), "nowplaying": { "media", "id", "title", "artist", … } }; { "state": "stopped" } when idle.
Inputs: instance. Returns: time, type, audio, state, +4 more
pauseB
Press Pause on the Kodi remote: pause the active player on the target instance. Returns the player-state snapshot { "state", "type", "time", "totaltime", "progress", "nowplaying": { "media", "id", "title", "artist", … } }.
Inputs: instance. Returns: time, type, audio, state, +4 more
playA
Press Play on the Kodi remote: resumes paused playback only — it cannot start new content (with nothing loaded it is a no-op); use playfile to start something. Returns the player-state snapshot { "state", "type", "time", "totaltime", "progress", "nowplaying": { "media", "id", "title", "artist", … } }.
Inputs: instance. Returns: time, type, audio, state, +4 more
playfileA
Play one file by path (e.g. a searchmedia result's file): Player.Open auto-selects the audio/video player. Works for any reachable path, in-library or not. A file missing from disk (stale library entry) is reported as an error. Returns the player-state snapshot { "state", "type", "time", "totaltime", "progress", "nowplaying": { "media", "id", "title", "artist", … } }.
Inputs: file (required), instance, fullscreenaudio. Returns: time, type, audio, state, +4 more
playfile_with_audio_and_subtitleA
Play a video file by path AND select its audio track and/or subtitle once it starts — the "when starting playback" partner of set_audio_and_subtitle. "file" is required; "audio" is a stream index and "subtitle" is a stream index or "off", using the "index" values from audio_and_subtitle_info. Give at least one of audio/subtitle (else use playfile). Video only. Returns the player-state snapshot.
Inputs: file (required), audio, instance, subtitle. Returns: time, type, audio, state, +4 more
queueA
Queue an item behind the one now playing, for continuous playback: a searchmedia row's library id (type+id) or a file path; next:true plays it right after the current item. Something must already be playing (start with play/playfile). An item whose file is missing from disk (stale library entry) is refused. Returns the player-state snapshot, unchanged by the add — the current item keeps playing.
Inputs: id, file, next, type, +1 more. Returns: time, type, audio, state, +4 more
rpcA
Escape hatch: send a raw JSON-RPC method to Kodi and return its reply unchanged. Disabled unless the target instance has opted in (allow_rpc in the server config, set by hand only).
Inputs: method (required), params, instance.
searchmediaA
Find playable files by name: music/tv-show/movie, drilled to leaf files with paging (limit/offset) and a total count. For music, title means the ALBUM name; match a song by its own title with the song arg ("play Hey Jude" needs no artist). Find a tv episode by its own name with the episodetitle arg. Movie and tv-show queries can also filter by actor/director. Finds media items only — for people lookups (bands, artists, who is in the library) use contributors. Returns { "type", "total", "returned", "offset", "truncated", "rows": [ { "file", "id", "label", "title", … } ] }.
Inputs: type (required), song, actor, count, +9 more. Returns: rows, type, total, offset, +4 more
set_audio_and_subtitleA
Switch the audio track and/or subtitle of the video now playing on the target instance. "audio" is a stream index; "subtitle" is a stream index or "off" to hide subtitles — use the "index" values from audio_and_subtitle_info. Omit an argument to leave that stream unchanged; give at least one. Returns the player-state snapshot.
Inputs: audio, instance, subtitle. Returns: time, type, audio, state, +4 more
skipnextA
Press Skip Next (Next track / chapter) on the Kodi remote: advance the active player to the next item in its playlist (or next chapter). A remote keypress, not a player command; with nothing loaded it is a no-op. Returns the player-state snapshot { "state", "type", "time", "totaltime", "progress", "nowplaying": { "media", "id", "title", "artist", … } }.
Inputs: instance. Returns: time, type, audio, state, +4 more
skippreviousA
Press Skip Previous (Previous track / chapter) on the Kodi remote: go to the previous item in the playlist (or restart the current item, matching Kodi's own button). A remote keypress, not a player command; with nothing loaded it is a no-op. Returns the player-state snapshot { "state", "type", "time", "totaltime", "progress", "nowplaying": { "media", "id", "title", "artist", … } }.
Inputs: instance. Returns: time, type, audio, state, +4 more
stopA
Press Stop on the Kodi remote: stop the active player on the target instance and clear the playlist it was playing, so no queued items linger. Returns the player-state snapshot — { "state": "stopped" } after a successful stop.
Inputs: instance. Returns: time, type, audio, state, +4 more
unmuteA
Unmute the target instance's audio output. Returns { "muted", "volume" }.
Inputs: instance. Returns: muted, volume
volumeA
Adjust the target instance's volume by a relative step (in percentage points); step 0 or omitted just reports the current volume. Returns { "muted", "volume", "min", "max" }.
Inputs: step, instance. Returns: max, min, muted, volume
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 22 tools
Most tools target a distinct resource+action (play/pause/stop, skipnext/skipprevious, mute/unmute/volume) and are clearly separable. The only real overlap is the audio/subtitle cluster: playfile vs playfile_with_audio_and_subtitle, and set_audio_and_subtitle vs the others, but the descriptions explicitly state when to use each, so ambiguity is low.
Conventions are mixed: bare verbs (play, pause, stop, queue, notify), concatenated lowercase (nowplaying, skipnext, searchmedia, getplaylist, dropplaylists), and snake_case (audio_and_subtitle_info, set_audio_and_subtitle, playfile_with_audio_and_subtitle). All lowercase keeps it readable, but there is no single predictable pattern.
22 tools is on the heavier side for a media-center controller, but the domain (playback, search, queueing, audio/subtitle, volume, history, config, raw RPC) is broad and each tool is distinct. Mute/unmute could be a single toggle, but overall the set is justifiable.
Coverage is strong across playback control, media/people search, queue read/drop, audio-subtitle selection, volume/mute, notifications, history, instance config, and a raw JSON-RPC escape hatch. Minor gaps exist (no seek/position control, no individual queue-item removal, no library scan/update), but core workflows are complete.
Maintenance
Related MCP Connectors
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
MCP server for Argo RPG Platform — connects AI assistants to campaign data via OAuth2
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to control Unreal E…
Related MCP Servers
- FlicenseAqualityDmaintenanceAn MCP server that enables AI assistants to control TVs on a local network through natural language commands. It currently supports Roku devices, allowing users to launch apps, manage playback, and navigate menus.51-
- AlicenseAqualityDmaintenanceControl mpv media player through AI conversation. Play music and video, manage playlists — all via natural language. Works with opencode and any MCP-compatible AI tool.169 npmMIT
- AlicenseAqualityDmaintenanceMCP server that enables natural language control of internet radio from Claude Code, with access to 30,000+ global stations, auto-playback via mpv, and a real-time status line with audio spectrum visualization.7MIT
- AlicenseAqualityDmaintenanceMCP server for controlling the mpv media player, enabling playback control, music library browsing, YouTube streaming and downloading, and metadata editing from within an MCP client.1766 npm1MIT