Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
```

## Checks
- Run linting with `uv run ruff .`.
- Run linting with `uv run ruff check .`.
- Run the test suite with `uv run pytest` and ensure it passes before committing.

## Efficiency and Search
- Use `rg` (ripgrep) for recursive search.
- Avoid `ls -R` and `grep -R` as they generate excessive output.

## Git Commit Guidelines
- Commit messages must follow [Conventional Commit](https://www.conventionalcommits.org/) standards.
- Keep commits atomic and focused.
Expand Down
40 changes: 40 additions & 0 deletions mcp_plex/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,45 @@ async def recommend_media(
return [r.payload["data"] for r in recs]


@server.resource("resource://media-poster/{identifier}")
async def media_poster(
identifier: Annotated[
str,
Field(
description="Rating key, IMDb/TMDb ID, or media title",
examples=["49915", "tt8367814", "The Gentlemen"],
),
],
) -> str:
"""Return the poster image URL for the given media identifier."""
records = await _find_records(identifier, limit=1)
if not records:
raise ValueError("Media item not found")
thumb = records[0].payload["data"].get("plex", {}).get("thumb")
if not thumb:
raise ValueError("Poster not available")
return thumb


@server.resource("resource://media-background/{identifier}")
async def media_background(
identifier: Annotated[
str,
Field(
description="Rating key, IMDb/TMDb ID, or media title",
examples=["49915", "tt8367814", "The Gentlemen"],
),
],
) -> str:
"""Return the background art URL for the given media identifier."""
records = await _find_records(identifier, limit=1)
if not records:
raise ValueError("Media item not found")
art = records[0].payload["data"].get("plex", {}).get("art")
if not art:
raise ValueError("Background not available")
return art


if __name__ == "__main__": # pragma: no cover
server.run()
13 changes: 13 additions & 0 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pathlib import Path
import importlib
import types
import pytest

from mcp_plex import loader
from qdrant_client import models
Expand Down Expand Up @@ -151,3 +152,15 @@ def test_server_tools(tmp_path, monkeypatch):

# Exercise search path with an ID that doesn't exist
asyncio.run(server._find_records("12345", limit=1))

poster = asyncio.run(server.media_poster.fn(identifier=movie_id))
assert isinstance(poster, str) and "thumb" in poster

art = asyncio.run(server.media_background.fn(identifier=movie_id))
assert isinstance(art, str) and "art" in art

with pytest.raises(ValueError):
asyncio.run(server.media_poster.fn(identifier="0"))

with pytest.raises(ValueError):
asyncio.run(server.media_background.fn(identifier="0"))
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.