flick
<p align="center">
<img src="assets/logo.svg" alt="flick — your media server, agent-controlled" width="520">
</p>
<p align="center">
<strong>Your media server, agent-controlled.</strong>
</p>
<p align="center">
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT license"></a>
<img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python 3.10+">
<img src="https://img.shields.io/badge/MCP-server-000000.svg" alt="MCP server">
</p>
---
Flick is an [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that
connects AI agents to your [Jellyfin](https://jellyfin.org) media server. Once it's
registered with an MCP-capable client, your agent can search the library, look up item
details, see what's playing right now, and plan the next movie night — no browser, no
hand-rolled API scripts.
## What is this?
A small Python package with two parts:
- **`flick/server.py`** — a [FastMCP](https://github.com/modelcontextprotocol/python-sdk)
server exposing five tools: `search`, `info`, `sessions`, `libraries`, `next_up`.
- **`flick/jellyfin.py`** — a thin, synchronous HTTP client for the Jellyfin REST API,
built on the Python standard library only (`urllib`). No `httpx`, no `requests`.
It talks to your existing Jellyfin server over its normal HTTP API; nothing is installed
on the server and no files are modified there.
## Why?
Jellyfin's REST API is powerful but raw: endpoints, query params, auth headers, paging.
Agents are bad at guessing those and good at using small, well-described tools. Flick is
that small surface:
- one dependency (`mcp`), one auth header (`X-Emby-Token`), five tools;
- boring, readable, stdlib-only HTTP code you can audit in minutes;
- agents get structured JSON back, so they can reason about titles, types, and IDs.
## Tools
| Tool | Description | Args |
| --- | --- | --- |
| `search` | Search the whole library by title (movies, shows, episodes, …) | `query` (str, required) · `limit` (int, default 20) |
| `info` | Full metadata for a single library item | `item_id` (str, required) |
| `sessions` | Active playback sessions: who is watching what, on which device | — |
| `libraries` | Top-level media libraries (Movies, TV Shows, …) | — |
| `next_up` | Next unwatched episode of shows you're following | `limit` (int, default 20) |
## Quickstart
Requires Python 3.10+ and [uv](https://docs.astral.sh/uv/) (or any venv + pip).
```bash
git clone <this-repo> flick
cd flick
uv venv # create .venv
uv pip install -e . # installs mcp + the flick package
# Point at your Jellyfin server
export JELLYFIN_URL=http://localhost:8096
export JELLYFIN_API_KEY=your-key-here
.venv/bin/flick # starts the MCP server over stdio
```
### Getting an API key
1. Open the Jellyfin web UI → **Dashboard** (hamburger menu → Dashboard).
2. Go to **Advanced → API Keys**.
3. Click **New API Key**, give it a name (e.g. `flick`), and click **OK**.
4. Copy the generated key — it is shown only once.
The key authenticates every request via the `X-Emby-Token` header. Keep it out of git
(see `.gitignore`) and pass it through the environment or your client's `env` block.
> `JELLYFIN_URL` defaults to `http://localhost:8096` if unset. `JELLYFIN_API_KEY` has no
> default: the server refuses to start without it.
## Client setup examples
### Claude Desktop
Edit `claude_desktop_config.json` (Claude → Settings → Developer → Edit Config):
```json
{
"mcpServers": {
"flick": {
"command": "/absolute/path/to/flick/.venv/bin/flick",
"env": {
"JELLYFIN_URL": "http://localhost:8096",
"JELLYFIN_API_KEY": "your-key-here"
}
}
}
}
```
### Cursor
Create/merge `.cursor/mcp.json` in your project:
```json
{
"mcpServers": {
"flick": {
"command": "/absolute/path/to/flick/.venv/bin/flick",
"env": {
"JELLYFIN_URL": "http://localhost:8096",
"JELLYFIN_API_KEY": "your-key-here"
}
}
}
}
```
### Hermes agent
Add a `mcp_servers` entry to your Hermes config (e.g. `~/.hermes/config.yaml`):
```yaml
mcp_servers:
flick:
command: /home/lappy/repos/flick/.venv/bin/flick
env:
JELLYFIN_URL: http://localhost:8096
JELLYFIN_API_KEY: your-key-here
```
Then restart the agent and ask: *"search my Jellyfin library for 'matrix'"* or *"what's
playing on Jellyfin right now?"*
## API
Each tool maps to one Jellyfin REST endpoint:
| Tool | Endpoint |
| --- | --- |
| `search(query, limit)` | `GET /Search/Hints?searchTerm={query}&limit={limit}` |
| `info(item_id)` | `GET /Items/{item_id}` |
| `sessions()` | `GET /Sessions` |
| `libraries()` | `GET /Library/MediaFolders` |
| `next_up(limit)` | `GET /Shows/NextUp?limit={limit}` |
- Auth: every request sends `X-Emby-Token: <api key>`.
- Errors: non-2xx responses and network failures raise `flick.jellyfin.JellyfinError`
with the HTTP status and reason in the message.
- The client (`flick/jellyfin.py`) is standalone: use it from scripts with
`JellyfinClient(base_url, api_key)` — no MCP required.
## Roadmap
- [ ] Playback control: play / pause / stop on a session
- [ ] Queue & playlist management (add to playlist, reorder, clear queue)
- [ ] Library browsing with filters (type, genre, year, sort)
- [ ] User-scoped queries (`userId` passthrough for `next_up` and friends)
- [ ] Streamable HTTP transport for remote servers
## Contributing
PRs are welcome. Keep it boring:
- no new runtime dependencies (stdlib `urllib` for HTTP, `mcp` for the server);
- tests use stdlib `unittest` with the HTTP layer mocked — run them with
`.venv/bin/python -m unittest discover -s tests -v`;
- never commit secrets or real API keys.
## License
MIT — see [LICENSE](LICENSE). Copyright (c) 2026 Manny7717.
TDQS
Scored across 5 tools
Each tool has a clearly distinct responsibility: searching media, fetching metadata by ID, listing active sessions, listing top-level libraries, and showing next-up episodes. Search and info are related but role-separated by ID lookup, so an agent is unlikely to confuse them.
The names are readable and all lowercase, but they are not pattern-consistent: search is a verb while info, sessions, and libraries are nouns, and next_up uses an underscore while the others do not. A uniform list_/verb_noun convention would make the set more predictable.
Five tools is a well-scoped size for a focused Jellyfin browsing server. Each tool covers a meaningful high-level capability without overwhelming the agent with redundant operations.
The core read-only workflows for a Jellyfin assistant are covered: search, details, library listing, active sessions, and next episodes. Obvious minor gaps include browsing seasons/episodes within a show and playback controls, but these are not severe for the apparent purpose.