@cyanheads/seerr-mcp-server 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +19 -16
- package/CLAUDE.md +19 -16
- package/Dockerfile +11 -7
- package/LICENSE +1 -1
- package/README.md +7 -7
- package/changelog/0.1.x/0.1.1.md +30 -0
- package/changelog/0.1.x/0.1.2.md +22 -0
- package/changelog/template.md +5 -3
- package/dist/index.js +0 -0
- package/dist/mcp-server/resources/definitions/index.d.ts +1 -0
- package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/request.resource.d.ts +7 -3
- package/dist/mcp-server/resources/definitions/request.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/request.resource.js +17 -5
- package/dist/mcp-server/resources/definitions/request.resource.js.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +152 -142
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/list-requests.tool.d.ts +16 -9
- package/dist/mcp-server/tools/definitions/list-requests.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/list-requests.tool.js +74 -11
- package/dist/mcp-server/tools/definitions/list-requests.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/request-media.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/request-media.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/request-media.tool.js +18 -4
- package/dist/mcp-server/tools/definitions/request-media.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/request-status.tool.d.ts +4 -2
- package/dist/mcp-server/tools/definitions/request-status.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/request-status.tool.js +11 -4
- package/dist/mcp-server/tools/definitions/request-status.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-media.tool.d.ts +1 -1
- package/dist/services/seerr/normalizers.d.ts +5 -1
- package/dist/services/seerr/normalizers.d.ts.map +1 -1
- package/dist/services/seerr/normalizers.js +21 -7
- package/dist/services/seerr/normalizers.js.map +1 -1
- package/dist/services/seerr/seerr-service.d.ts +12 -2
- package/dist/services/seerr/seerr-service.d.ts.map +1 -1
- package/dist/services/seerr/seerr-service.js +42 -16
- package/dist/services/seerr/seerr-service.js.map +1 -1
- package/dist/services/seerr/titles.d.ts +47 -0
- package/dist/services/seerr/titles.d.ts.map +1 -0
- package/dist/services/seerr/titles.js +96 -0
- package/dist/services/seerr/titles.js.map +1 -0
- package/package.json +11 -9
- package/server.json +3 -3
package/AGENTS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** seerr-mcp-server
|
|
4
|
-
**Version:** 0.1.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.
|
|
4
|
+
**Version:** 0.1.2
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.11.0`
|
|
6
6
|
**Engines:** Bun ≥1.3.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
|
|
8
8
|
**Zod:** ^4.4.3
|
|
@@ -245,6 +245,7 @@ src/
|
|
|
245
245
|
seerr-service.ts # Jellyseerr REST client (init/accessor pattern)
|
|
246
246
|
status.ts # Pure status decoders → { raw, label }
|
|
247
247
|
normalizers.ts # PII/infra redaction choke point (the security boundary)
|
|
248
|
+
titles.ts # Request → title join (deduped, capped, best-effort)
|
|
248
249
|
errors.ts # Upstream-error classifier (500 → media_not_found, 404 → request_not_found)
|
|
249
250
|
types.ts # Raw upstream + domain types
|
|
250
251
|
mcp-server/
|
|
@@ -288,7 +289,6 @@ Available skills:
|
|
|
288
289
|
| `techniques` | Catalog of reusable response/data-shaping patterns — overflow handling, payload shaping, retrieval |
|
|
289
290
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
290
291
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
291
|
-
| `devcheck` | Lint, format, typecheck, audit |
|
|
292
292
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
293
293
|
| `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
|
|
294
294
|
| `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
@@ -321,20 +321,23 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
321
321
|
|
|
322
322
|
| Command | Purpose |
|
|
323
323
|
|:--------|:--------|
|
|
324
|
-
| `
|
|
325
|
-
| `
|
|
326
|
-
| `
|
|
327
|
-
| `
|
|
324
|
+
| `bun run build` | Compile TypeScript |
|
|
325
|
+
| `bun run rebuild` | Clean + build |
|
|
326
|
+
| `bun run clean` | Remove build artifacts |
|
|
327
|
+
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
328
328
|
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
|
|
329
|
-
| `
|
|
330
|
-
| `
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
329
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
330
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
331
|
+
| `bun run list-skills` | Print the skill registry |
|
|
332
|
+
| `bun run tree` | Generate directory structure doc |
|
|
333
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
334
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
335
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
336
|
+
| `bun run start:stdio` | Production mode (stdio) |
|
|
337
|
+
| `bun run start:http` | Production mode (HTTP) |
|
|
338
|
+
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
339
|
+
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
340
|
+
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
338
341
|
|
|
339
342
|
---
|
|
340
343
|
|
package/CLAUDE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** seerr-mcp-server
|
|
4
|
-
**Version:** 0.1.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.
|
|
4
|
+
**Version:** 0.1.2
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.11.0`
|
|
6
6
|
**Engines:** Bun ≥1.3.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
|
|
8
8
|
**Zod:** ^4.4.3
|
|
@@ -245,6 +245,7 @@ src/
|
|
|
245
245
|
seerr-service.ts # Jellyseerr REST client (init/accessor pattern)
|
|
246
246
|
status.ts # Pure status decoders → { raw, label }
|
|
247
247
|
normalizers.ts # PII/infra redaction choke point (the security boundary)
|
|
248
|
+
titles.ts # Request → title join (deduped, capped, best-effort)
|
|
248
249
|
errors.ts # Upstream-error classifier (500 → media_not_found, 404 → request_not_found)
|
|
249
250
|
types.ts # Raw upstream + domain types
|
|
250
251
|
mcp-server/
|
|
@@ -288,7 +289,6 @@ Available skills:
|
|
|
288
289
|
| `techniques` | Catalog of reusable response/data-shaping patterns — overflow handling, payload shaping, retrieval |
|
|
289
290
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
290
291
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
291
|
-
| `devcheck` | Lint, format, typecheck, audit |
|
|
292
292
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
293
293
|
| `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
|
|
294
294
|
| `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
@@ -321,20 +321,23 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
321
321
|
|
|
322
322
|
| Command | Purpose |
|
|
323
323
|
|:--------|:--------|
|
|
324
|
-
| `
|
|
325
|
-
| `
|
|
326
|
-
| `
|
|
327
|
-
| `
|
|
324
|
+
| `bun run build` | Compile TypeScript |
|
|
325
|
+
| `bun run rebuild` | Clean + build |
|
|
326
|
+
| `bun run clean` | Remove build artifacts |
|
|
327
|
+
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
328
328
|
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
|
|
329
|
-
| `
|
|
330
|
-
| `
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
329
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
330
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
331
|
+
| `bun run list-skills` | Print the skill registry |
|
|
332
|
+
| `bun run tree` | Generate directory structure doc |
|
|
333
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
334
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
335
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
336
|
+
| `bun run start:stdio` | Production mode (stdio) |
|
|
337
|
+
| `bun run start:http` | Production mode (HTTP) |
|
|
338
|
+
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
339
|
+
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
340
|
+
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
338
341
|
|
|
339
342
|
---
|
|
340
343
|
|
package/Dockerfile
CHANGED
|
@@ -4,15 +4,17 @@
|
|
|
4
4
|
# This stage installs all dependencies (including dev), builds the TypeScript
|
|
5
5
|
# source code into JavaScript, and prepares the production assets.
|
|
6
6
|
# ==============================================================================
|
|
7
|
-
FROM oven/bun:1.3 AS build
|
|
7
|
+
FROM oven/bun:1.3.14 AS build
|
|
8
8
|
|
|
9
9
|
WORKDIR /usr/src/app
|
|
10
10
|
|
|
11
11
|
# Copy dependency manifests for optimized layer caching
|
|
12
12
|
COPY package.json bun.lock ./
|
|
13
13
|
|
|
14
|
-
# Install all dependencies (including dev dependencies for building)
|
|
15
|
-
|
|
14
|
+
# Install all dependencies (including dev dependencies for building).
|
|
15
|
+
# The BuildKit cache mount persists Bun's global package cache across builds.
|
|
16
|
+
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
17
|
+
bun install --frozen-lockfile --ignore-scripts
|
|
16
18
|
|
|
17
19
|
# Copy the rest of the source code
|
|
18
20
|
COPY . .
|
|
@@ -28,7 +30,7 @@ RUN bun run build
|
|
|
28
30
|
# application. It uses a slim base image and only includes production
|
|
29
31
|
# dependencies and build artifacts.
|
|
30
32
|
# ==============================================================================
|
|
31
|
-
FROM oven/bun:1.3-slim AS production
|
|
33
|
+
FROM oven/bun:1.3.14-slim AS production
|
|
32
34
|
|
|
33
35
|
WORKDIR /usr/src/app
|
|
34
36
|
|
|
@@ -49,14 +51,16 @@ COPY package.json bun.lock ./
|
|
|
49
51
|
|
|
50
52
|
# Install only production dependencies, ignoring any lifecycle scripts (like 'prepare')
|
|
51
53
|
# that are not needed in the final production image.
|
|
52
|
-
RUN bun
|
|
54
|
+
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
55
|
+
bun install --production --frozen-lockfile --ignore-scripts
|
|
53
56
|
|
|
54
57
|
# Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
|
|
55
58
|
# These are not bundled by default to keep the base image lean. Enable at build time
|
|
56
59
|
# with: docker build --build-arg OTEL_ENABLED=true
|
|
57
60
|
ARG OTEL_ENABLED=true
|
|
58
|
-
RUN
|
|
59
|
-
|
|
61
|
+
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
62
|
+
if [ "$OTEL_ENABLED" = "true" ]; then \
|
|
63
|
+
bun add --omit=dev --ignore-scripts @hono/otel \
|
|
60
64
|
@opentelemetry/instrumentation-http \
|
|
61
65
|
@opentelemetry/exporter-metrics-otlp-http \
|
|
62
66
|
@opentelemetry/exporter-trace-otlp-http \
|
package/LICENSE
CHANGED
|
@@ -186,7 +186,7 @@ Apache License
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright
|
|
189
|
+
Copyright 2026 Casey Hand @cyanheads
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/seerr-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -26,7 +26,7 @@ A workflow MCP server over a self-hosted [Jellyseerr](https://github.com/Fallenb
|
|
|
26
26
|
Two properties make it safe to hand an agent:
|
|
27
27
|
|
|
28
28
|
- **Guarded writes.** The one mutating tool (`seerr_request_media`) defaults to `mode: preview` — it resolves the title and returns the exact payload that _would_ be submitted without writing anything. The real request fires only on `mode: request`, and asks for an explicit confirmation first when the client supports elicitation.
|
|
29
|
-
- **
|
|
29
|
+
- **Operator-safe output.** Every payload passes through one normalization layer ([`src/services/seerr/normalizers.ts`](./src/services/seerr/normalizers.ts)) that allow-lists the fields it emits, so operator email, Plex/Jellyfin tokens and media-server IDs, and internal `serviceUrl` hosts never reach the model — requesters are projected to `{ id, displayName }`. Redaction is always on; filesystem paths are the single opt-in, behind `includePaths`.
|
|
30
30
|
|
|
31
31
|
## Tools
|
|
32
32
|
|
|
@@ -36,9 +36,9 @@ Six tools covering the request workflow — discover (`search`) → confirm (`ge
|
|
|
36
36
|
|:---|:---|
|
|
37
37
|
| `seerr_search_media` | Search movies and TV by title; returns ranked matches with TMDB ID, year, overview, and decoded availability when Jellyseerr already tracks the title. The required first step before requesting. |
|
|
38
38
|
| `seerr_get_media` | Fetch exact movie/show details by TMDB ID + media type to confirm the title before a write; for TV, a per-season summary or one season's episode list. |
|
|
39
|
-
| `seerr_list_requests` | List recent requests with status/type/requester filters; echoes the applied filters and decodes every numeric status. |
|
|
39
|
+
| `seerr_list_requests` | List recent requests with status/type/requester filters; echoes the applied filters and decodes every numeric status. Titles are opt-in via `includeTitles`. |
|
|
40
40
|
| `seerr_request_media` | **Guarded write.** Previews the request payload by default (`mode: preview`); creates the request only on `mode: request` with an elicited confirmation. |
|
|
41
|
-
| `seerr_request_status` | Fetch one request by ID — decoded request + media availability (incl. 4K), requester, routing summary, and a state-tuned next-step hint. |
|
|
41
|
+
| `seerr_request_status` | Fetch one request by ID — title, decoded request + media availability (incl. 4K), requester, routing summary, and a state-tuned next-step hint. |
|
|
42
42
|
| `seerr_service_options` | Summarize configured Radarr/Sonarr services, default quality profiles, and instance capability flags (4K, partial requests, specials, media server). Filesystem paths redacted unless `includePaths`. |
|
|
43
43
|
|
|
44
44
|
Every status field is decoded to `{ raw, label }` — both the numeric code Jellyseerr returns and a human label — so an agent never has to hardcode the enum mapping.
|
|
@@ -72,7 +72,8 @@ Review recent requests and their lifecycle. Wraps `GET /request`.
|
|
|
72
72
|
- Lifecycle `filter` (pending, processing, available, failed, …), `mediaType`, and `requestedById` filters
|
|
73
73
|
- Sort by created (`added`) or last-changed (`modified`), ascending or descending
|
|
74
74
|
- `take` / `skip` pagination; the enrichment trailer echoes the filter set the server applied
|
|
75
|
-
- Requester is PII-redacted to `{ id, displayName }
|
|
75
|
+
- Requester is PII-redacted to `{ id, displayName }`
|
|
76
|
+
- Titles aren't on request objects. `includeTitles: true` joins them from the media records — one lookup per _distinct_ title on the page, so a 4K and a non-4K request for one film cost one call. Default is off, keeping the call to a single upstream read. Rows that can't be resolved keep every other field and are disclosed in the notice
|
|
76
77
|
|
|
77
78
|
---
|
|
78
79
|
|
|
@@ -103,7 +104,7 @@ Lets an agent reason about request capability and routing without a separate sta
|
|
|
103
104
|
|
|
104
105
|
| Type | Name | Description |
|
|
105
106
|
|:---|:---|:---|
|
|
106
|
-
| Resource | `seerr://request/{requestId}` | Read-once summary of one request — decoded status + media availability + routing. Mirrors `seerr_request_status`. |
|
|
107
|
+
| Resource | `seerr://request/{requestId}` | Read-once summary of one request — title, decoded status + media availability + routing. Mirrors `seerr_request_status`. |
|
|
107
108
|
|
|
108
109
|
All request data is also reachable via tools — request _enumeration_ is the job of `seerr_list_requests` (filterable, the tool-only access path), so the collection is intentionally not exposed as a resource. There are no prompts; the guarded-write workflow lives in the tool, not a prompt template.
|
|
109
110
|
|
|
@@ -127,7 +128,6 @@ Jellyseerr-specific:
|
|
|
127
128
|
|
|
128
129
|
Agent-friendly output:
|
|
129
130
|
|
|
130
|
-
- **Mandatory PII/infra redaction** — a single normalization choke point projects requester objects to `{ id, displayName }` and drops operator email, Plex/Jellyfin tokens, internal `serviceUrl`, and filesystem paths before any output reaches the model
|
|
131
131
|
- **Provenance and disclosure** — searches echo the effective query; capped lists disclose truncation; a degraded service leg surfaces a notice instead of silently dropping data
|
|
132
132
|
- **Typed, actionable errors** — `media_not_found`, `request_not_found`, `seasons_required`, `four_k_not_enabled`, `duplicate_request`, and more carry a recovery hint so callers can branch and retry without parsing prose
|
|
133
133
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Fixes unencoded search/list query strings, 4K request-status guidance, and season-0 (Specials) requests; consolidates README redaction prose; mcp-ts-core ^0.11.0 and TypeScript 7."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.1.1 — 2026-08-02
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **README** — consolidated the PII/infra redaction story to one statement in the intro; dropped the redundant "Agent-friendly output" restatement. ([#1](https://github.com/cyanheads/seerr-mcp-server/issues/1))
|
|
12
|
+
- **devcheck** — new "Dependency Specifiers" check rejects floating specifiers (`latest`, `*`, dist-tags) in `package.json` and `bun.lock`; "Packaging" now also validates plugin-manifest (`.claude-plugin`/`.codex-plugin`) env-var parity.
|
|
13
|
+
- **`bunfig.toml`** — Bun installs now go through `minimumReleaseAge` (3-day supply-chain hold, excluded for `@cyanheads/mcp-ts-core`) and Socket's install-time scanner.
|
|
14
|
+
|
|
15
|
+
## Fixed
|
|
16
|
+
|
|
17
|
+
- **`seerr_search_media` / `seerr_list_requests`** — query strings are now fully percent-encoded, so a title with a space or any of `! ' ( ) *` no longer 400s against Seerr's reserved-character check. ([#5](https://github.com/cyanheads/seerr-mcp-server/issues/5))
|
|
18
|
+
- **`seerr_request_status` / `seerr://request/{requestId}`** — `stateGuidance` now reads `mediaStatus4k` for `is4k: true` requests instead of the non-4K `mediaStatus`, which commonly sits at `unknown` while the 4K copy is already downloading; `seerr_list_requests` now surfaces `mediaStatus4k` per row too. ([#2](https://github.com/cyanheads/seerr-mcp-server/issues/2))
|
|
19
|
+
- **`seerr_request_media`** — `seasons` accepts `0` (Specials) at the schema; the handler rejects it only when the instance's `enableSpecialEpisodes` is not `true`, via a new `special_episodes_not_enabled` typed error. ([#3](https://github.com/cyanheads/seerr-mcp-server/issues/3))
|
|
20
|
+
|
|
21
|
+
## Dependencies
|
|
22
|
+
|
|
23
|
+
- `@cyanheads/mcp-ts-core` ^0.10.6 → ^0.11.0
|
|
24
|
+
- `typescript` ^6.0.3 → ^7.0.2
|
|
25
|
+
- `@biomejs/biome` 2.5.0 → 2.5.5
|
|
26
|
+
- `@types/node` 25.9.3 → 26.1.1
|
|
27
|
+
- `vitest` ^4.1.8 → ^4.1.10
|
|
28
|
+
- `tsc-alias` ^1.8.17 → ^1.9.1
|
|
29
|
+
- `ignore` ^7.0.5 → ^7.0.6
|
|
30
|
+
- `@socketsecurity/bun-security-scanner` ^1.1.2 (new)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Adds opt-in title hydration to seerr_list_requests and unconditional hydration to seerr_request_status/resource; narrows the retry budget for best-effort title lookups; corrects a stale docs/design.md claim about the service's HTTP layer."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.1.2 — 2026-08-02
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`seerr_list_requests`** — new opt-in `includeTitles` input (default `false`) resolves each row's title via a join against the media detail endpoint, deduplicated by `(mediaType, tmdbId)` and capped by a 6-wide worker pool (`src/services/seerr/titles.ts`); rows that can't be resolved keep every other field and are disclosed in `notice`. ([#4](https://github.com/cyanheads/seerr-mcp-server/issues/4))
|
|
12
|
+
|
|
13
|
+
## Changed
|
|
14
|
+
|
|
15
|
+
- **`seerr_request_status`** / **`seerr://request/{requestId}`** — now hydrate `title` unconditionally, one extra read per call, via the same join. ([#4](https://github.com/cyanheads/seerr-mcp-server/issues/4))
|
|
16
|
+
- **`SeerrService.getMovie` / `getTv`** — accept a per-call `options.maxRetries` override; the title join passes `maxRetries: 0` so a failed lookup degrades after a single attempt instead of retrying, since a title is cosmetic and a busy retry would only multiply load on the single-node instance for the same absent result. ([#4](https://github.com/cyanheads/seerr-mcp-server/issues/4))
|
|
17
|
+
- **`docs/design.md`** — corrected the service-layer description: `SeerrService` issues plain `fetch` + `AbortSignal.timeout`, not `fetchWithTimeout` — whose SSRF guard would block the private LAN/Tailscale address and whose thrown error hides the response body the not-found classifier reads.
|
|
18
|
+
|
|
19
|
+
## Dependencies
|
|
20
|
+
|
|
21
|
+
- `@biomejs/biome` 2.5.5 → 2.5.6
|
|
22
|
+
- `@types/node` 26.1.1 → 26.1.2
|
package/changelog/template.md
CHANGED
|
@@ -15,9 +15,11 @@ summary: ""
|
|
|
15
15
|
# usage. Flagged as `Breaking` in the rollup.
|
|
16
16
|
breaking: false
|
|
17
17
|
|
|
18
|
-
# Set `true`
|
|
19
|
-
#
|
|
20
|
-
#
|
|
18
|
+
# Set `true` ONLY for a security fix in THIS project's own source code — a
|
|
19
|
+
# vulnerability or hardening in code you ship. A dependency or transitive CVE
|
|
20
|
+
# bump is routine maintenance, NOT a security release: record it under
|
|
21
|
+
# `## Dependencies` (with the advisory ID) and leave this `false`. When true,
|
|
22
|
+
# pairs with the `## Security` section below and flags `Security` in the rollup.
|
|
21
23
|
security: false
|
|
22
24
|
|
|
23
25
|
# Optional free-form notes for maintenance agents processing this release.
|
package/dist/index.js
CHANGED
|
File without changes
|
|
@@ -11,6 +11,7 @@ export declare const allResourceDefinitions: import("@cyanheads/mcp-ts-core").Re
|
|
|
11
11
|
movie: "movie";
|
|
12
12
|
tv: "tv";
|
|
13
13
|
}>;
|
|
14
|
+
title: import("zod").ZodOptional<import("zod").ZodString>;
|
|
14
15
|
tmdbId: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
15
16
|
requestStatus: import("zod").ZodObject<{
|
|
16
17
|
raw: import("zod").ZodNumber;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,sBAAsB
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAAyB,CAAC"}
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* @fileoverview seerr://request/{requestId} — read-once request summary. Mirrors
|
|
3
3
|
* seerr_request_status for clients that support injectable resource context:
|
|
4
4
|
* decoded request + media status, requester (PII-redacted), routing summary, and a
|
|
5
|
-
* state-tuned hint. Reuses the same `projectRequestDetail` choke point
|
|
6
|
-
* so the
|
|
7
|
-
*
|
|
5
|
+
* state-tuned hint. Reuses the same `projectRequestDetail` choke point and title
|
|
6
|
+
* join as the tool, so the output is identical and equally redacted. A resource
|
|
7
|
+
* takes no per-read options, so the title is always joined — one request, one
|
|
8
|
+
* extra read — and degrades to absent when the request has no tmdbId or the
|
|
9
|
+
* lookup fails. No `list()` — request enumeration is the job of the filterable
|
|
10
|
+
* seerr_list_requests tool (the tool-only access path).
|
|
8
11
|
* @module mcp-server/resources/definitions/request.resource
|
|
9
12
|
*/
|
|
10
13
|
import { z } from '@cyanheads/mcp-ts-core';
|
|
@@ -17,6 +20,7 @@ export declare const seerrRequestResource: import("@cyanheads/mcp-ts-core").Reso
|
|
|
17
20
|
movie: "movie";
|
|
18
21
|
tv: "tv";
|
|
19
22
|
}>;
|
|
23
|
+
title: z.ZodOptional<z.ZodString>;
|
|
20
24
|
tmdbId: z.ZodOptional<z.ZodNumber>;
|
|
21
25
|
requestStatus: z.ZodObject<{
|
|
22
26
|
raw: z.ZodNumber;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/request.resource.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"request.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/request.resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAMjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoE/B,CAAC"}
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* @fileoverview seerr://request/{requestId} — read-once request summary. Mirrors
|
|
3
3
|
* seerr_request_status for clients that support injectable resource context:
|
|
4
4
|
* decoded request + media status, requester (PII-redacted), routing summary, and a
|
|
5
|
-
* state-tuned hint. Reuses the same `projectRequestDetail` choke point
|
|
6
|
-
* so the
|
|
7
|
-
*
|
|
5
|
+
* state-tuned hint. Reuses the same `projectRequestDetail` choke point and title
|
|
6
|
+
* join as the tool, so the output is identical and equally redacted. A resource
|
|
7
|
+
* takes no per-read options, so the title is always joined — one request, one
|
|
8
|
+
* extra read — and degrades to absent when the request has no tmdbId or the
|
|
9
|
+
* lookup fails. No `list()` — request enumeration is the job of the filterable
|
|
10
|
+
* seerr_list_requests tool (the tool-only access path).
|
|
8
11
|
* @module mcp-server/resources/definitions/request.resource
|
|
9
12
|
*/
|
|
10
13
|
import { resource, z } from '@cyanheads/mcp-ts-core';
|
|
@@ -12,6 +15,7 @@ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
|
12
15
|
import { projectRequestDetail } from '../../../services/seerr/normalizers.js';
|
|
13
16
|
import { getSeerrService } from '../../../services/seerr/seerr-service.js';
|
|
14
17
|
import { StatusRef } from '../../../services/seerr/status.js';
|
|
18
|
+
import { hydrateRequestTitle } from '../../../services/seerr/titles.js';
|
|
15
19
|
export const seerrRequestResource = resource('seerr://request/{requestId}', {
|
|
16
20
|
name: 'seerr-request',
|
|
17
21
|
title: 'seerr-mcp-server: request',
|
|
@@ -26,6 +30,10 @@ export const seerrRequestResource = resource('seerr://request/{requestId}', {
|
|
|
26
30
|
output: z.object({
|
|
27
31
|
requestId: z.number().describe('The request ID.'),
|
|
28
32
|
mediaType: z.enum(['movie', 'tv']).describe('Movie or TV.'),
|
|
33
|
+
title: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Title of the requested media, joined from the media record; absent when the request carries no tmdbId or the lookup failed.'),
|
|
29
37
|
tmdbId: z.number().optional().describe('TMDB ID of the media.'),
|
|
30
38
|
requestStatus: StatusRef.describe('Decoded request status {raw,label}.'),
|
|
31
39
|
mediaStatus: StatusRef.optional().describe('Decoded media availability {raw,label}.'),
|
|
@@ -63,8 +71,12 @@ export const seerrRequestResource = resource('seerr://request/{requestId}', {
|
|
|
63
71
|
async handler(params, ctx) {
|
|
64
72
|
const seerr = getSeerrService();
|
|
65
73
|
const raw = await seerr.getRequest(Number.parseInt(params.requestId, 10), ctx);
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
const detail = await hydrateRequestTitle(projectRequestDetail(raw), seerr, ctx);
|
|
75
|
+
ctx.log.debug('Seerr request resource read', {
|
|
76
|
+
requestId: params.requestId,
|
|
77
|
+
titleResolved: detail.title !== undefined,
|
|
78
|
+
});
|
|
79
|
+
return detail;
|
|
68
80
|
},
|
|
69
81
|
});
|
|
70
82
|
//# sourceMappingURL=request.resource.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/request.resource.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"request.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/request.resource.ts"],"names":[],"mappings":"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"}
|