Skip to main content
Glama
README.md
<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

A3.7/5.0

Scored across 5 tools

Disambiguation5/5

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.

Naming Consistency3/5

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.

Tool Count5/5

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.

Completeness4/5

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.

Maintenance

ActivityNo data
ResponsivenessNo issues