Skip to main content
Glama

Letterboxd MCP

A local-first, read-only MCP server for public Letterboxd profile, diary, and watched-film data. It uses FastMCP over stdio, fetches public pages with requests, parses them with BeautifulSoup, and keeps a local SQLite cache.

Requirements

  • Python 3.12 or newer

  • uv

Related MCP server: tg-mcp-spy

Install and run

Install the project and its development dependencies:

uv sync --dev

Start the stdio MCP server from the repository root:

uv run letterboxd-mcp

An MCP client should launch that command as a stdio server. For example, from outside the repository, configure the equivalent of:

{
  "mcpServers": {
    "letterboxd": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/letterboxd_mcp",
        "run",
        "letterboxd-mcp"
      ]
    }
  }
}

Replace the example path with this repository's absolute path.

Tools

The server exposes exactly three read-only tools:

  • get_profile(username: str, refresh: bool = false) returns normalized public profile details.

  • get_diary(username: str, limit: int = 50, refresh: bool = false) returns up to limit normalized diary entries, newest first.

  • get_films(username: str, limit: int = 10, offset: int = 0, refresh: bool = false) returns a page of unique watched titles ordered by newest added and enriched with directors, genres, top-ten cast, runtime, synopsis, aggregate rating, nested diary viewings, and the profile's latest public review.

Example tool arguments:

{"username": "dave"}
{"username": "dave", "limit": 25, "refresh": true}
{"username": "dave", "limit": 10, "offset": 20}

Usernames may contain letters, numbers, underscores, and hyphens. Diary limits must be positive. Watched-film limits must be between 1 and 20, and offsets must be non-negative.

Cache and configuration

By default, data is stored in server.db in the server's working directory. Profiles remain fresh for 30 minutes, profile collections/reviews for 10 minutes, and generic film details for 24 hours. Fresh cached data is returned without an HTTP request; refresh: true always requests current public data. get_films fully synchronizes the public watched collection and diary when stale, but only enriches its requested 10–20 title window.

Configuration is optional and uses LETTERBOXD_MCP_ environment variables. The most useful settings are:

  • LETTERBOXD_MCP_DATABASE_PATH

  • LETTERBOXD_MCP_PROFILE_TTL_SECONDS

  • LETTERBOXD_MCP_DIARY_TTL_SECONDS

  • LETTERBOXD_MCP_CONNECT_TIMEOUT_SECONDS

  • LETTERBOXD_MCP_READ_TIMEOUT_SECONDS

  • LETTERBOXD_MCP_MAX_RETRIES

  • LETTERBOXD_MCP_USER_AGENT

All TTL and timeout values must be positive. MAX_RETRIES may be zero.

Development

Run the offline test suite and build the distributions:

uv run pytest
uv build

The implementation keeps MCP tools, service/cache decisions, HTTP behavior, parsers, models, and SQLite repositories in separate layers.

Limitations

This MVP only reads public profile, diary, watched-film, film-detail, and profile-review pages. It does not return community reviews, log in, access private data, write to Letterboxd, bypass challenge pages or CAPTCHAs, or use browser automation. Large profiles can require many public page requests during the first full collection/diary synchronization. Letterboxd markup changes or anti-bot challenges can surface as structured fetch or parse errors.

See LETTERBOXD_MCP_SPEC.md for the product specification and .codex/plan/implementation.md for the implementation ledger.

Available Tools

3 tools
get_diaryGet DiaryB
Read-onlyIdempotent

Return normalized public diary entries for a Letterboxd user.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
refreshNo
usernameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only, idempotent, non-destructive behavior, so the description need not repeat that. It adds the 'normalized' detail, but no further behavioral traits like ordering, pagination, or error handling are disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single, clear sentence with no filler or redundant information. Well-structured and directly to the point.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only tool, the core purpose is present, but the lack of parameter explanations and usage context leaves some gaps. Output schema exists, so return values need not be described.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the description makes no mention of 'limit' or 'refresh' parameters. Even though names are somewhat self-explanatory, the description fails to explain their semantics or the username pattern.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the action 'Return' and the specific resource 'normalized public diary entries for a Letterboxd user,' which distinguishes it from sibling tools like get_profile and get_films.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides no guidance on when to choose this tool over alternatives or any prerequisites. It simply describes what it returns without contextual direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_filmsGet FilmsB
Read-onlyIdempotent

Return enriched public films watched by a Letterboxd user.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
offsetNo
refreshNo
usernameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
itemsYes
limitYes
totalYes
offsetYes
has_moreYes
usernameYes
fetched_atYes

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only/idempotent; description does not add additional behavioral details like caching or rate limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence, no unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

While output schema exists, the description omits parameter roles (pagination, refresh) and the meaning of 'enriched'; for a tool with four parameters, this is insufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

None of the four parameters (username, limit, offset, refresh) are described in the schema or description; description only references user implicitly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States it returns enriched public films for a Letterboxd user, using a specific verb and resource, and distinguishes from siblings like get_profile and get_diary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this over get_profile or get_diary; no conditions or alternatives mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_profileGet ProfileA
Read-onlyIdempotent

Return normalized public profile information for a Letterboxd user.

ParametersJSON Schema
NameRequiredDescriptionDefault
refreshNo
usernameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
bioNo
usernameYes
avatar_urlNo
fetched_atYes
films_countNo
display_nameYes
followers_countNo
following_countNo

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false, which already provide strong transparency about side effects. The description's word 'Return' aligns with these hints. No contradictions. The description does not add extra context about caching or network behavior, but the annotations cover the essential safety profile.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, clear sentence. No fluff or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has an output schema, so return values are documented elsewhere. The description is sufficient to understand the tool's purpose, though it lacks parameter explanations. Given the simplicity and existing schema, it's adequately complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage for parameters, and the description does not explain the meaning of 'username' or 'refresh' beyond their obvious names. 'refresh' is ambiguous (e.g., force refresh vs. poll for updates). Since schema coverage is low, the description should have compensated, but it didn't.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (Return) and the resource (normalized public profile information) and specifies the target (a Letterboxd user). This distinguishes it from sibling tools like get_diary and get_films, which focus on diary entries and film data respectively.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not explicitly mention when to use this tool instead of others. It implies that it is for profile data, but there is no direct comparison to get_diary or get_films, nor any condition like 'use this when you need the user's profile.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.1.0
    • First observedget_diary
    • First observedget_films
    • First observedget_profile

TDQS

A3.7/5.0

Scored across 3 tools

Disambiguation5/5

Each tool targets a distinct aspect of a user's Letterboxd data: profile, diary, and films watched. There is no ambiguity about which tool to use for which purpose.

Naming Consistency5/5

All tool names follow the exact same get_<noun> pattern, making the naming scheme highly predictable and consistent.

Tool Count5/5

Three tools is well-scoped for a server focused on read-only Letterboxd user data. Each tool covers a clear and essential information need without redundancy.

Completeness3/5

The set covers profile, diary, and watched films, but omits common Letterboxd data such as watchlist, ratings, reviews, or lists. It is a usable core but leaves notable gaps for a full user data surface.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Read-only MCP server for accessing local Kindle library data, exposing tools to query profile, health, and book metadata.
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    A local MCP server for managing saved Substack posts. Enables offline reading, searching, bookmarking, and unbookmarking of Substack content via CLI or MCP clients.
    17
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Local-first MCP server that binds to a project opened by the Seojeom desktop app and serves its local wiki and graph data over stdio, enabling project-aware reading, searching, and writing of wiki and graph content.
    6 npm
    MIT