
movie-mcp-server
Movie Catalog Service
An asynchronous Python backend that fronts the TMDB catalog behind two independent transports: an MCP server for assistants and an HTTP/JSON API for the live web demo. Both surfaces call the same domain service, so behaviour, caching, retries, and failure handling are identical no matter which door a caller comes through.
Architecture
MCP adapter (server.py) HTTP adapter (webapp.py)
│ │
└────────────┬────────────────────┘
▼
catalog.CatalogService domain logic, orchestration,
│ degradation policy
▼
tmdb.TmdbGateway TMDB vocabulary → typed models
│
▼
transport.* pooling, retries, breaker, cache
│
▼
TMDB API
Dependencies point one way only. Nothing in catalog/ imports httpx; nothing in
transport/ knows what a movie is. That is what makes the layers independently
testable and independently replaceable — swapping TMDB for another catalog means
writing a second gateway with the same methods and changing nothing above it.
| Module | Responsibility |
|---|---|
movieservice/config.py | All environment reading, in one immutable Settings object |
movieservice/errors.py | Domain error taxonomy; upstream failures never leak past transport |
movieservice/models.py | Pydantic response models — the structured pipeline |
movieservice/transport/retry.py | Retry classification and jittered exponential backoff |
movieservice/transport/breaker.py | Circuit breaker (fault isolation per dependency) |
movieservice/transport/cache.py | TTL cache with single-flight de-duplication |
movieservice/transport/client.py | Pooled async HTTP client with all of the above applied |
movieservice/tmdb/gateway.py | The only module that knows TMDB URLs and payload shapes |
movieservice/catalog/query.py | Natural-language prompt parsing (pure functions) |
movieservice/catalog/service.py | Orchestration, ranking, degradation policy |
movieservice/inbound/ratelimit.py | Sliding-window inbound quota |
movieservice/observability.py | JSON logging with per-request correlation ids |
Resilience and latency behaviour
Retries. Timeouts, connection errors, and 408/425/429/5xx are retried with
exponential backoff plus jitter, capped at RETRY_MAX_DELAY. A Retry-After
header always overrides our own backoff guess. 401/403/404 are not
retried — a bad token will not fix itself on the second attempt.
Circuit breaking. After BREAKER_FAILURE_THRESHOLD consecutive failures the
breaker opens and calls fail fast for BREAKER_RESET_SECONDS, then a single probe
decides whether to close. This keeps a TMDB outage from turning into a queue of
slow timeouts holding this service's connection pool open. A 404 is a definitive
answer, not a fault, so it never trips the breaker.
Connection pooling. One httpx.AsyncClient is created lazily and held for the
life of the process (closed on shutdown via the ASGI lifespan), so requests reuse
warm TLS connections rather than paying a handshake each time. The MCP adapter
builds its service once for the same reason.
Caching and single-flight. Responses are cached by path plus normalised query string. Concurrent misses on the same key share one in-flight request instead of stampeding the upstream — five simultaneous identical searches produce one TMDB call.
Concurrent orchestration. get_movie_details fans out to /movie,
/credits, /recommendations, and /watch/providers together, so it costs
roughly one round trip instead of four (~150 ms rather than ~600 ms).
**Graceful d
Related servers

n8n
by n8n-io
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.

@modelcontextprotocol/server-everything
OfficialMCP server that exercises all the features of the MCP protocol

@modelcontextprotocol/server-filesystem
OfficialMCP server for filesystem access