Metadata-Version: 2.5
Name: cinelens-mcp
Version: 0.1.0
Summary: MCP server for movie and TV discovery, powered by TMDB, TVmaze, and Watchmode, with programmatic prerequisite enforcement.
Author: Gayathri Ramakrishnan
License: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: fastmcp>=0.4
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.0
Requires-Dist: starlette>=0.37
Description-Content-Type: text/markdown

# cinelens-mcp

An MCP server for movie and TV discovery, powered by TMDB, TVmaze, and Watchmode.

A standalone [FastMCP](https://github.com/jlowin/fastmcp) server, extracted
from the CineLens conversational movie agent. Bring your own API keys (BYOK)
— nothing is bundled or hardcoded.

## Features

**Movies**

- `search_movie_tool` — search TMDB by title
- `discover_recent_movies_tool` — browse by real release date ("recent"/"latest"/"new")
- `discover_by_genre_tool` — browse by genre, optionally within a recent window
- `search_person_tool` — find an actor or director by name
- `get_person_credits_tool` — list a person's movie credits, newest first
- `select_movie_tool` — switch the active movie to a specific candidate
- `get_movie_details_tool` — details for the active movie
- `get_cast_tool` — cast for the active movie
- `get_recommendations_tool` — recommendations similar to the active movie
- `get_watch_providers_tool` — where to stream/rent/buy the active movie

**TV Shows**

- `search_tv_show_tool` — search TVmaze by title (free, no API key required)
- `get_show_details_tool` — name, status, genres, rating, network, schedule, summary
- `get_show_streaming_tool` — streaming sources grouped by free/subscription/rent/buy

**Theatres**

- `get_now_playing_tool` — up to 10 movies currently playing in theatres
- `get_trending_tool` — top 10 trending movies for `"day"` or `"week"`

## Quickstart (Claude Desktop)

Add this to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "cinelens": {
      "command": "uvx",
      "args": ["cinelens-mcp"],
      "env": {
        "TMDB_API_KEY": "your-tmdb-bearer-token",
        "DEFAULT_REGION": "US",
        "WATCHMODE_API_KEY": "your-watchmode-api-key"
      }
    }
  }
}
```

Restart Claude Desktop, then ask it to search for a movie — it will call
`search_movie_tool` first automatically, then any other tools.

## Environment variables

| Variable | Required | Where to get it | Default |
|---|---|---|---|
| `TMDB_API_KEY` | Yes, at call time for all movie tools | [themoviedb.org/settings/api](https://www.themoviedb.org/settings/api) (v4 Bearer token) | — |
| `DEFAULT_REGION` | No | ISO 3166-1 country code | `US` |
| `WATCHMODE_API_KEY` | Yes, at call time for `get_show_streaming_tool` | [api.watchmode.com](https://api.watchmode.com/) | — |

TVmaze tools (`search_tv_show_tool`, `get_show_details_tool`) need no key.

## Bring Your Own Key (BYOK)

cinelens-mcp ships with no embedded credentials. Every tool that calls a
paid or rate-limited API reads its key from an environment variable at
**call time**, not at import time — so the server starts and lists its
tools fine even with no keys configured, and only fails the specific tool
call that needed a missing key. This keeps the package free to distribute,
puts API usage and billing under each user's own account, and means a key
never has to be committed, bundled, or shared to use the server. Keys are
read only by the module that owns the corresponding backend (see
`backends/`) and are never logged or included in a tool response or error
message.

## Tool reference

| Tool | Description | Inputs | Prerequisite |
|---|---|---|---|
| `search_movie_tool` | Search for a movie by title | `query: str` | none (entry point) |
| `discover_recent_movies_tool` | Browse movies by real release date | `within_days=90`, `genre_keyword`, `sort_by`, `region` | none (entry point) |
| `discover_by_genre_tool` | Browse movies by genre | `genre: str`, `within_days`, `region`, `limit=10` | none (entry point) |
| `search_person_tool` | Find an actor/director by name | `query: str` | none |
| `get_person_credits_tool` | List a person's movie credits | `person_id: int`, `role="all"`, `limit=10` | none |
| `select_movie_tool` | Set active movie from a candidate list | `movie_id: int` | a prior search/discover/credits call |
| `get_movie_details_tool` | Details for the active movie | — | active movie |
| `get_cast_tool` | Cast for the active movie | `limit=10` | active movie |
| `get_recommendations_tool` | Recommendations for the active movie | `limit=10` | active movie |
| `get_watch_providers_tool` | Watch providers for the active movie | `region` | active movie |
| `get_now_playing_tool` | Movies currently in theatres | — | none |
| `get_trending_tool` | Top trending movies | `time_window="week"` | none |
| `search_tv_show_tool` | Search for a TV show by title | `query: str` | none (entry point) |
| `get_show_details_tool` | Details for a TV show | `show_id: int` | active TV show |
| `get_show_streaming_tool` | Streaming sources for a TV show | `show_id: int` | active TV show |

## How it works: prerequisite middleware

Tools that depend on an active movie or TV show (e.g. `get_cast_tool`,
`get_show_details_tool`) require an entry-point tool to have run first in
the same session. This is **not** enforced by instructing the model in a
system prompt — LLMs occasionally skip or misorder steps when the rule
lives only in text. Instead, `middleware/prerequisites.py` checks session
state programmatically before every gated tool executes, keyed off fields
on `MovieSessionState` (`movie_id`, `show_id`, `last_candidates`) that are
only ever set by a successful upstream call.

If the prerequisite isn't met, the tool call is short-circuited and returns
a structured error instead of hitting the backend:

```json
{
  "error": "prerequisite_not_met",
  "message": "Please search for a movie first.",
  "required_tool": "search_movie"
}
```

The movie chain and TV chain gate independently — an active movie doesn't
satisfy a TV prerequisite and vice versa. This makes the dependency graph a
property of the code, not a hope about model behavior, so it holds under
any client and any prompt.

## Reliability

TMDB requests automatically retry on 429/5xx with exponential backoff (3
retries, respecting `Retry-After`); persistent rate limiting raises
`TMDBRateLimitError` rather than failing silently.

## Install & run

```bash
uvx cinelens-mcp
```

Or from source:

```bash
uv sync
uv run cinelens-mcp
```

## Development

```bash
uv sync
uv run pytest
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for how to add a new tool or data
source.

## License

MIT — see [LICENSE](LICENSE).
