@cyanheads/seerr-mcp-server 0.1.0

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.
Files changed (73) hide show
  1. package/AGENTS.md +407 -0
  2. package/CLAUDE.md +407 -0
  3. package/Dockerfile +121 -0
  4. package/LICENSE +201 -0
  5. package/README.md +302 -0
  6. package/changelog/0.1.x/0.1.0.md +26 -0
  7. package/changelog/template.md +127 -0
  8. package/dist/config/server-config.d.ts +27 -0
  9. package/dist/config/server-config.d.ts.map +1 -0
  10. package/dist/config/server-config.js +45 -0
  11. package/dist/config/server-config.js.map +1 -0
  12. package/dist/index.d.ts +9 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +24 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/mcp-server/resources/definitions/index.d.ts +47 -0
  17. package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -0
  18. package/dist/mcp-server/resources/definitions/index.js +8 -0
  19. package/dist/mcp-server/resources/definitions/index.js.map +1 -0
  20. package/dist/mcp-server/resources/definitions/request.resource.d.ts +53 -0
  21. package/dist/mcp-server/resources/definitions/request.resource.d.ts.map +1 -0
  22. package/dist/mcp-server/resources/definitions/request.resource.js +70 -0
  23. package/dist/mcp-server/resources/definitions/request.resource.js.map +1 -0
  24. package/dist/mcp-server/tools/definitions/get-media.tool.d.ts +66 -0
  25. package/dist/mcp-server/tools/definitions/get-media.tool.d.ts.map +1 -0
  26. package/dist/mcp-server/tools/definitions/get-media.tool.js +197 -0
  27. package/dist/mcp-server/tools/definitions/get-media.tool.js.map +1 -0
  28. package/dist/mcp-server/tools/definitions/index.d.ts +341 -0
  29. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
  30. package/dist/mcp-server/tools/definitions/index.js +22 -0
  31. package/dist/mcp-server/tools/definitions/index.js.map +1 -0
  32. package/dist/mcp-server/tools/definitions/list-requests.tool.d.ts +76 -0
  33. package/dist/mcp-server/tools/definitions/list-requests.tool.d.ts.map +1 -0
  34. package/dist/mcp-server/tools/definitions/list-requests.tool.js +187 -0
  35. package/dist/mcp-server/tools/definitions/list-requests.tool.js.map +1 -0
  36. package/dist/mcp-server/tools/definitions/request-media.tool.d.ts +117 -0
  37. package/dist/mcp-server/tools/definitions/request-media.tool.d.ts.map +1 -0
  38. package/dist/mcp-server/tools/definitions/request-media.tool.js +347 -0
  39. package/dist/mcp-server/tools/definitions/request-media.tool.js.map +1 -0
  40. package/dist/mcp-server/tools/definitions/request-status.tool.d.ts +55 -0
  41. package/dist/mcp-server/tools/definitions/request-status.tool.d.ts.map +1 -0
  42. package/dist/mcp-server/tools/definitions/request-status.tool.js +111 -0
  43. package/dist/mcp-server/tools/definitions/request-status.tool.js.map +1 -0
  44. package/dist/mcp-server/tools/definitions/search-media.tool.d.ts +51 -0
  45. package/dist/mcp-server/tools/definitions/search-media.tool.d.ts.map +1 -0
  46. package/dist/mcp-server/tools/definitions/search-media.tool.js +167 -0
  47. package/dist/mcp-server/tools/definitions/search-media.tool.js.map +1 -0
  48. package/dist/mcp-server/tools/definitions/service-options.tool.d.ts +51 -0
  49. package/dist/mcp-server/tools/definitions/service-options.tool.d.ts.map +1 -0
  50. package/dist/mcp-server/tools/definitions/service-options.tool.js +195 -0
  51. package/dist/mcp-server/tools/definitions/service-options.tool.js.map +1 -0
  52. package/dist/services/seerr/errors.d.ts +33 -0
  53. package/dist/services/seerr/errors.d.ts.map +1 -0
  54. package/dist/services/seerr/errors.js +74 -0
  55. package/dist/services/seerr/errors.js.map +1 -0
  56. package/dist/services/seerr/normalizers.d.ts +111 -0
  57. package/dist/services/seerr/normalizers.d.ts.map +1 -0
  58. package/dist/services/seerr/normalizers.js +179 -0
  59. package/dist/services/seerr/normalizers.js.map +1 -0
  60. package/dist/services/seerr/seerr-service.d.ts +92 -0
  61. package/dist/services/seerr/seerr-service.d.ts.map +1 -0
  62. package/dist/services/seerr/seerr-service.js +187 -0
  63. package/dist/services/seerr/seerr-service.js.map +1 -0
  64. package/dist/services/seerr/status.d.ts +27 -0
  65. package/dist/services/seerr/status.d.ts.map +1 -0
  66. package/dist/services/seerr/status.js +70 -0
  67. package/dist/services/seerr/status.js.map +1 -0
  68. package/dist/services/seerr/types.d.ts +221 -0
  69. package/dist/services/seerr/types.d.ts.map +1 -0
  70. package/dist/services/seerr/types.js +11 -0
  71. package/dist/services/seerr/types.js.map +1 -0
  72. package/package.json +86 -0
  73. package/server.json +137 -0
@@ -0,0 +1,187 @@
1
+ /**
2
+ * @fileoverview SeerrService — the typed client for the Seerr REST API v1
3
+ * (`{SEERR_BASE_URL}/api/v1`, `X-Api-Key` auth). One upstream API, one service,
4
+ * init/accessor pattern. Every method wraps a single-attempt fetch + parse in
5
+ * `withRetry` (the retry boundary covers the full pipeline). Not-found-bearing GETs
6
+ * (movie/tv/request) route their non-OK responses through the `errors.ts` classifier
7
+ * so Seerr's idiosyncratic 500/404 not-found bodies become clean domain errors.
8
+ *
9
+ * The instance is a trusted local LAN / Tailscale host, so requests use plain
10
+ * `fetch` (the framework's `fetchWithTimeout` SSRF guard would block the private
11
+ * IP, and the classifier needs body access on non-OK). The base URL and API key
12
+ * never leave this file's request construction — no method returns them.
13
+ * @module services/seerr/seerr-service
14
+ */
15
+ import { serializationError } from '@cyanheads/mcp-ts-core/errors';
16
+ import { withRetry } from '@cyanheads/mcp-ts-core/utils';
17
+ import { getServerConfig } from '../../config/server-config.js';
18
+ import { throwClassifiedSeerrError } from './errors.js';
19
+ const SERVICE_NAME = 'Seerr';
20
+ const SETTINGS_CACHE_KEY = 'seerr:settings:public';
21
+ const SETTINGS_CACHE_TTL_SECONDS = 300;
22
+ export class SeerrService {
23
+ baseUrl;
24
+ apiKey;
25
+ timeoutMs;
26
+ constructor() {
27
+ const server = getServerConfig();
28
+ this.baseUrl = `${server.baseUrl.replace(/\/+$/, '')}/api/v1`;
29
+ this.apiKey = server.apiKey;
30
+ this.timeoutMs = server.requestTimeoutMs;
31
+ }
32
+ /* ------------------------------------------------------------------ *
33
+ * Search + media detail
34
+ * ------------------------------------------------------------------ */
35
+ /** `GET /search` — title disambiguation. */
36
+ search(params, ctx) {
37
+ const qs = new URLSearchParams({ query: params.query, page: String(params.page) });
38
+ if (params.language)
39
+ qs.set('language', params.language);
40
+ return this.request(`/search?${qs.toString()}`, ctx);
41
+ }
42
+ /** `GET /movie/{id}` — movie detail. 500 "Unable to retrieve movie." → media_not_found. */
43
+ getMovie(tmdbId, ctx) {
44
+ return this.request(`/movie/${tmdbId}`, ctx, { notFoundKind: 'media' });
45
+ }
46
+ /** `GET /tv/{id}` — TV detail. */
47
+ getTv(tmdbId, ctx) {
48
+ return this.request(`/tv/${tmdbId}`, ctx, { notFoundKind: 'media' });
49
+ }
50
+ /** `GET /tv/{id}/season/{n}` — episode list for one season. */
51
+ getSeason(tmdbId, seasonNumber, ctx) {
52
+ return this.request(`/tv/${tmdbId}/season/${seasonNumber}`, ctx, {
53
+ notFoundKind: 'media',
54
+ });
55
+ }
56
+ /* ------------------------------------------------------------------ *
57
+ * Requests
58
+ * ------------------------------------------------------------------ */
59
+ /** `GET /request` — list requests with filters. */
60
+ listRequests(params, ctx) {
61
+ const qs = new URLSearchParams({
62
+ take: String(params.take),
63
+ skip: String(params.skip),
64
+ filter: params.filter,
65
+ sort: params.sort,
66
+ sortDirection: params.sortDirection,
67
+ });
68
+ if (params.mediaType && params.mediaType !== 'all')
69
+ qs.set('mediaType', params.mediaType);
70
+ if (typeof params.requestedById === 'number')
71
+ qs.set('requestedBy', String(params.requestedById));
72
+ return this.request(`/request?${qs.toString()}`, ctx);
73
+ }
74
+ /** `GET /request/{id}` — single request. 404 "Request not found." → request_not_found. */
75
+ getRequest(requestId, ctx) {
76
+ return this.request(`/request/${requestId}`, ctx, {
77
+ notFoundKind: 'request',
78
+ });
79
+ }
80
+ /** `POST /request` — create a media request. The only write in the surface. */
81
+ createRequest(body, ctx) {
82
+ return this.request('/request', ctx, { method: 'POST', body });
83
+ }
84
+ /* ------------------------------------------------------------------ *
85
+ * Services + settings
86
+ * ------------------------------------------------------------------ */
87
+ /** `GET /service/radarr` — Radarr service list. */
88
+ getRadarrServices(ctx) {
89
+ return this.request('/service/radarr', ctx);
90
+ }
91
+ /** `GET /service/radarr/{id}` — Radarr profiles + root folders. */
92
+ getRadarrDetail(serverId, ctx) {
93
+ return this.request(`/service/radarr/${serverId}`, ctx);
94
+ }
95
+ /** `GET /service/sonarr` — Sonarr service list. */
96
+ getSonarrServices(ctx) {
97
+ return this.request('/service/sonarr', ctx);
98
+ }
99
+ /** `GET /service/sonarr/{id}` — Sonarr profiles + root folders. */
100
+ getSonarrDetail(serverId, ctx) {
101
+ return this.request(`/service/sonarr/${serverId}`, ctx);
102
+ }
103
+ /** `GET /status` — Seerr version. */
104
+ getStatus(ctx) {
105
+ return this.request('/status', ctx);
106
+ }
107
+ /**
108
+ * `GET /settings/public` — capability flags. Cached in `ctx.state` with a short
109
+ * TTL: capability checks read it on most calls (preview validation, search/get
110
+ * 4K decoding) but instance config changes rarely. Cache failures are non-fatal.
111
+ */
112
+ async getPublicSettings(ctx, options = {}) {
113
+ if (!options.forceRefresh) {
114
+ const cached = await ctx.state.get(SETTINGS_CACHE_KEY).catch(() => null);
115
+ if (cached)
116
+ return cached;
117
+ }
118
+ const settings = await this.request('/settings/public', ctx);
119
+ await ctx.state
120
+ .set(SETTINGS_CACHE_KEY, settings, { ttl: SETTINGS_CACHE_TTL_SECONDS })
121
+ .catch(() => { });
122
+ return settings;
123
+ }
124
+ /* ------------------------------------------------------------------ *
125
+ * Core request pipeline
126
+ * ------------------------------------------------------------------ */
127
+ /**
128
+ * Single Seerr request: fetch (single attempt) + status check + JSON parse, all
129
+ * inside `withRetry`. Non-OK responses route through the not-found classifier
130
+ * (when `notFoundKind` is set) or the framework's status mapping. The retry
131
+ * boundary wraps the whole pipeline so a transient parse failure also retries.
132
+ */
133
+ request(path, ctx, options = {}) {
134
+ const url = `${this.baseUrl}${path}`;
135
+ const method = options.method ?? 'GET';
136
+ return withRetry(async () => {
137
+ const headers = {
138
+ 'X-Api-Key': this.apiKey,
139
+ Accept: 'application/json',
140
+ };
141
+ if (options.body !== undefined)
142
+ headers['Content-Type'] = 'application/json';
143
+ const response = await fetch(url, {
144
+ method,
145
+ headers,
146
+ ...(options.body !== undefined ? { body: JSON.stringify(options.body) } : {}),
147
+ signal: AbortSignal.any([ctx.signal, AbortSignal.timeout(this.timeoutMs)]),
148
+ });
149
+ if (!response.ok) {
150
+ await throwClassifiedSeerrError(response, options.notFoundKind ?? 'media', SERVICE_NAME);
151
+ }
152
+ const text = await response.text();
153
+ if (text.length === 0)
154
+ return undefined;
155
+ try {
156
+ return JSON.parse(text);
157
+ }
158
+ catch (cause) {
159
+ throw serializationError(`${SERVICE_NAME} returned a non-JSON response.`, { path }, { cause });
160
+ }
161
+ }, {
162
+ operation: `SeerrService.request ${method} ${path}`,
163
+ context: { requestId: ctx.requestId, timestamp: ctx.timestamp },
164
+ baseDelayMs: options.baseDelayMs ?? 300,
165
+ signal: ctx.signal,
166
+ });
167
+ }
168
+ }
169
+ /* --- Init/accessor pattern --- */
170
+ let _service;
171
+ /**
172
+ * Initialize the singleton SeerrService — called from `createApp`'s `setup()`.
173
+ * Config and storage are accepted to match the framework's init signature; the
174
+ * service reads its own env config via `getServerConfig()` and uses tenant-scoped
175
+ * `ctx.state` (not the raw storage handle) for the settings cache.
176
+ */
177
+ export function initSeerrService(_config, _storage) {
178
+ _service = new SeerrService();
179
+ }
180
+ /** Accessor for the SeerrService singleton. Throws if `initSeerrService` was not called. */
181
+ export function getSeerrService() {
182
+ if (!_service) {
183
+ throw new Error('SeerrService not initialized — call initSeerrService() in setup()');
184
+ }
185
+ return _service;
186
+ }
187
+ //# sourceMappingURL=seerr-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seerr-service.js","sourceRoot":"","sources":["../../../src/services/seerr/seerr-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @fileoverview Pure status decoders for Seerr's numeric status enums. Every tool
3
+ * output that carries a request status, media availability, or media-server type
4
+ * emits a decoded `{ raw, label }` object via these helpers — never a bare number
5
+ * or bare string. Unknown codes degrade to `{ raw, label: 'unrecognized' }` rather
6
+ * than throwing, so a future Seerr status code still surfaces its raw value.
7
+ * @module services/seerr/status
8
+ */
9
+ import { z } from '@cyanheads/mcp-ts-core';
10
+ /**
11
+ * Shared Zod shape for a decoded status. Reused across every tool output and the
12
+ * resource so the `{ raw, label }` contract is identical everywhere.
13
+ */
14
+ export declare const StatusRef: z.ZodObject<{
15
+ raw: z.ZodNumber;
16
+ label: z.ZodString;
17
+ }, z.core.$strip>;
18
+ export type DecodedStatus = z.infer<typeof StatusRef>;
19
+ /** Decode a `MediaRequest.status` numeric code to `{ raw, label }`. */
20
+ export declare function decodeRequestStatus(raw: number): DecodedStatus;
21
+ /** Decode a `MediaInfo.status` / `status4k` numeric code to `{ raw, label }`. */
22
+ export declare function decodeMediaStatus(raw: number): DecodedStatus;
23
+ /** Decode a `mediaServerType` setting code to its label (plex | jellyfin | emby | unrecognized). */
24
+ export declare function decodeMediaServer(raw: number): string;
25
+ /** Render a decoded status for `format()` output as `label (raw)` — surfaces both halves so format-parity holds. */
26
+ export declare function statusText(status: DecodedStatus): string;
27
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/services/seerr/status.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C;;;GAGG;AACH,eAAO,MAAM,SAAS;;;iBAK4D,CAAC;AAEnF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAuCtD,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAE9D;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAE5D;AAED,oGAAoG;AACpG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,oHAAoH;AACpH,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAExD"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @fileoverview Pure status decoders for Seerr's numeric status enums. Every tool
3
+ * output that carries a request status, media availability, or media-server type
4
+ * emits a decoded `{ raw, label }` object via these helpers — never a bare number
5
+ * or bare string. Unknown codes degrade to `{ raw, label: 'unrecognized' }` rather
6
+ * than throwing, so a future Seerr status code still surfaces its raw value.
7
+ * @module services/seerr/status
8
+ */
9
+ import { z } from '@cyanheads/mcp-ts-core';
10
+ /**
11
+ * Shared Zod shape for a decoded status. Reused across every tool output and the
12
+ * resource so the `{ raw, label }` contract is identical everywhere.
13
+ */
14
+ export const StatusRef = z
15
+ .object({
16
+ raw: z.number().describe('Raw numeric status code from the Seerr API.'),
17
+ label: z.string().describe('Human-readable decoded label for the status code.'),
18
+ })
19
+ .describe('Decoded status: the raw numeric code plus its human-readable label.');
20
+ /**
21
+ * `MediaRequest.status` codes (request lifecycle). Matches Jellyseerr/Seerr's
22
+ * `MediaRequestStatus` enum. Live Seerr 3.3.0 emits 4 (failed) and 5 (completed)
23
+ * on real requests — over half the requests on a working instance are status 5 —
24
+ * so decoding only 1-3 mislabels every completed/failed request as `unrecognized`.
25
+ */
26
+ const REQUEST_STATUS_LABELS = {
27
+ 1: 'pending',
28
+ 2: 'approved',
29
+ 3: 'declined',
30
+ 4: 'failed',
31
+ 5: 'completed',
32
+ };
33
+ /**
34
+ * `MediaInfo.status` / `MediaInfo.status4k` codes (availability lifecycle). Matches
35
+ * Jellyseerr/Seerr's `MediaStatus` enum: 6 is `blocklisted` and 7 is `deleted`
36
+ * (BLOCKLISTED was inserted at 6, shifting DELETED to 7) — mapping 6 to `deleted`
37
+ * mislabels a blocklisted item and leaves an actually-deleted item `unrecognized`.
38
+ */
39
+ const MEDIA_STATUS_LABELS = {
40
+ 1: 'unknown',
41
+ 2: 'pending',
42
+ 3: 'processing',
43
+ 4: 'partially_available',
44
+ 5: 'available',
45
+ 6: 'blocklisted',
46
+ 7: 'deleted',
47
+ };
48
+ /** `mediaServerType` setting codes. */
49
+ const MEDIA_SERVER_LABELS = {
50
+ 1: 'plex',
51
+ 2: 'jellyfin',
52
+ 3: 'emby',
53
+ };
54
+ /** Decode a `MediaRequest.status` numeric code to `{ raw, label }`. */
55
+ export function decodeRequestStatus(raw) {
56
+ return { raw, label: REQUEST_STATUS_LABELS[raw] ?? 'unrecognized' };
57
+ }
58
+ /** Decode a `MediaInfo.status` / `status4k` numeric code to `{ raw, label }`. */
59
+ export function decodeMediaStatus(raw) {
60
+ return { raw, label: MEDIA_STATUS_LABELS[raw] ?? 'unrecognized' };
61
+ }
62
+ /** Decode a `mediaServerType` setting code to its label (plex | jellyfin | emby | unrecognized). */
63
+ export function decodeMediaServer(raw) {
64
+ return MEDIA_SERVER_LABELS[raw] ?? 'unrecognized';
65
+ }
66
+ /** Render a decoded status for `format()` output as `label (raw)` — surfaces both halves so format-parity holds. */
67
+ export function statusText(status) {
68
+ return `${status.label} (${status.raw})`;
69
+ }
70
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/services/seerr/status.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC;KACvB,MAAM,CAAC;IACN,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;IACvE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;CAChF,CAAC;KACD,QAAQ,CAAC,qEAAqE,CAAC,CAAC;AAInF;;;;;GAKG;AACH,MAAM,qBAAqB,GAA2B;IACpD,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,WAAW;CACf,CAAC;AAEF;;;;;GAKG;AACH,MAAM,mBAAmB,GAA2B;IAClD,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,YAAY;IACf,CAAC,EAAE,qBAAqB;IACxB,CAAC,EAAE,WAAW;IACd,CAAC,EAAE,aAAa;IAChB,CAAC,EAAE,SAAS;CACb,CAAC;AAEF,uCAAuC;AACvC,MAAM,mBAAmB,GAA2B;IAClD,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,MAAM;CACV,CAAC;AAEF,uEAAuE;AACvE,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,qBAAqB,CAAC,GAAG,CAAC,IAAI,cAAc,EAAE,CAAC;AACtE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,mBAAmB,CAAC,GAAG,CAAC,IAAI,cAAc,EAAE,CAAC;AACpE,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,mBAAmB,CAAC,GAAG,CAAC,IAAI,cAAc,CAAC;AACpD,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,UAAU,CAAC,MAAqB;IAC9C,OAAO,GAAG,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,GAAG,GAAG,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,221 @@
1
+ /**
2
+ * @fileoverview Raw upstream Seerr API types (hand-written from the OpenAPI spec +
3
+ * live read-only probes against Seerr 3.3.0) and the normalized domain types this
4
+ * server exposes. Raw types default to optional unless the live payload guarantees
5
+ * presence — third-party APIs omit fields rather than nulling them, and over-strict
6
+ * raw types either fail validation or invent facts. The domain types are the
7
+ * PII-redacted projections produced by `normalizers.ts`.
8
+ * @module services/seerr/types
9
+ */
10
+ /**
11
+ * Raw Seerr `User` object. Nested in `requestedBy` AND `modifiedBy` on requests.
12
+ * Carries heavy PII — email, Plex/Jellyfin tokens and IDs, quotas, avatar,
13
+ * permissions bitfield. `normalizers.ts` projects it to `{ id, displayName }`;
14
+ * nothing else here may reach tool output.
15
+ */
16
+ export interface RawUser {
17
+ avatar?: string;
18
+ displayName?: string;
19
+ email?: string;
20
+ id: number;
21
+ jellyfinUserId?: string;
22
+ jellyfinUsername?: string;
23
+ permissions?: number;
24
+ plexId?: number;
25
+ plexUsername?: string;
26
+ username?: string;
27
+ [key: string]: unknown;
28
+ }
29
+ /**
30
+ * Raw `MediaInfo` nested on search results, movie/TV detail, and request `media`.
31
+ * Leaks `serviceUrl`/`serviceUrl4k` (internal host:port), `jellyfinMediaId`,
32
+ * `ratingKey`, etc. — only `status`/`status4k`/`tmdbId`/`mediaType` are read.
33
+ * `requests[]` is present ONLY on movie/TV detail, never on search results.
34
+ */
35
+ export interface RawMediaInfo {
36
+ id?: number;
37
+ mediaType?: string;
38
+ requests?: RawMediaRequest[];
39
+ serviceUrl?: string;
40
+ serviceUrl4k?: string;
41
+ status?: number;
42
+ status4k?: number;
43
+ tmdbId?: number;
44
+ [key: string]: unknown;
45
+ }
46
+ /**
47
+ * Raw `MediaRequest`. The media-type discriminator is `type` (not `mediaType`).
48
+ * `profileName` is present at the list endpoint but ABSENT at `GET /request/{id}`.
49
+ * Both `requestedBy` and `modifiedBy` are full `RawUser` objects.
50
+ */
51
+ export interface RawMediaRequest {
52
+ createdAt?: string;
53
+ id: number;
54
+ is4k?: boolean;
55
+ isAutoRequest?: boolean;
56
+ languageProfileId?: number | null;
57
+ media?: RawMediaInfo;
58
+ modifiedBy?: RawUser;
59
+ profileId?: number;
60
+ profileName?: string | null;
61
+ requestedBy?: RawUser;
62
+ rootFolder?: string | null;
63
+ seasonCount?: number;
64
+ seasons?: Array<{
65
+ seasonNumber?: number;
66
+ } | number>;
67
+ serverId?: number;
68
+ status?: number;
69
+ type?: string;
70
+ updatedAt?: string;
71
+ [key: string]: unknown;
72
+ }
73
+ /** Raw search-result hit (`MovieResult` | `TvResult`); `person` is filtered out upstream of this. */
74
+ export interface RawSearchResult {
75
+ firstAirDate?: string;
76
+ id: number;
77
+ mediaInfo?: RawMediaInfo;
78
+ mediaType?: string;
79
+ name?: string;
80
+ overview?: string;
81
+ releaseDate?: string;
82
+ title?: string;
83
+ voteAverage?: number;
84
+ [key: string]: unknown;
85
+ }
86
+ /** Raw `/search` response envelope (flat — distinct from the request-list `pageInfo` envelope). */
87
+ export interface RawSearchResponse {
88
+ page?: number;
89
+ results?: RawSearchResult[];
90
+ totalPages?: number;
91
+ totalResults?: number;
92
+ }
93
+ /** Raw season summary on TV detail (`seasons[]`). */
94
+ export interface RawSeason {
95
+ airDate?: string | null;
96
+ episodeCount?: number;
97
+ id?: number;
98
+ name?: string;
99
+ overview?: string;
100
+ seasonNumber?: number;
101
+ [key: string]: unknown;
102
+ }
103
+ /** Raw episode on a season detail (`/tv/{id}/season/{n}` → `episodes[]`). */
104
+ export interface RawEpisode {
105
+ airDate?: string | null;
106
+ episodeNumber?: number;
107
+ id?: number;
108
+ name?: string;
109
+ overview?: string;
110
+ [key: string]: unknown;
111
+ }
112
+ /** Raw movie detail (`GET /movie/{id}`). */
113
+ export interface RawMovieDetail {
114
+ id: number;
115
+ mediaInfo?: RawMediaInfo;
116
+ overview?: string;
117
+ releaseDate?: string;
118
+ runtime?: number;
119
+ status?: string;
120
+ title?: string;
121
+ [key: string]: unknown;
122
+ }
123
+ /** Raw TV detail (`GET /tv/{id}`). Live API uses `numberOfSeasons` (plural). */
124
+ export interface RawTvDetail {
125
+ firstAirDate?: string;
126
+ id: number;
127
+ mediaInfo?: RawMediaInfo;
128
+ name?: string;
129
+ numberOfSeasons?: number;
130
+ overview?: string;
131
+ seasons?: RawSeason[];
132
+ status?: string;
133
+ [key: string]: unknown;
134
+ }
135
+ /** Raw season detail (`GET /tv/{id}/season/{n}`). */
136
+ export interface RawSeasonDetail {
137
+ episodes?: RawEpisode[];
138
+ id?: number;
139
+ name?: string;
140
+ seasonNumber?: number;
141
+ [key: string]: unknown;
142
+ }
143
+ /** Raw request-list envelope (`{ pageInfo, results, serviceErrors }`). */
144
+ export interface RawRequestListResponse {
145
+ pageInfo?: {
146
+ page?: number;
147
+ pages?: number;
148
+ pageSize?: number;
149
+ results?: number;
150
+ };
151
+ results?: RawMediaRequest[];
152
+ }
153
+ /** Raw `/settings/public`. Drives capability + validation; `vapidPublic`/`plexClientIdentifier` are stripped from output. */
154
+ export interface RawPublicSettings {
155
+ applicationTitle?: string;
156
+ enableSpecialEpisodes?: boolean;
157
+ mediaServerType?: number;
158
+ movie4kEnabled?: boolean;
159
+ partialRequestsEnabled?: boolean;
160
+ series4kEnabled?: boolean;
161
+ [key: string]: unknown;
162
+ }
163
+ /** Raw `/status`. */
164
+ export interface RawStatus {
165
+ version?: string;
166
+ [key: string]: unknown;
167
+ }
168
+ /** Raw Radarr/Sonarr list entry. Leaks `activeDirectory` (+ `activeAnimeDirectory` on Sonarr). */
169
+ export interface RawServiceListEntry {
170
+ activeAnimeDirectory?: string;
171
+ activeAnimeProfileId?: number;
172
+ activeDirectory?: string;
173
+ activeProfileId?: number;
174
+ id: number;
175
+ is4k?: boolean;
176
+ isDefault?: boolean;
177
+ name?: string;
178
+ [key: string]: unknown;
179
+ }
180
+ /** Raw quality profile (`{ id, name }`) from a service detail endpoint. */
181
+ export interface RawServiceProfile {
182
+ id: number;
183
+ name?: string;
184
+ [key: string]: unknown;
185
+ }
186
+ /** Raw root folder from a service detail endpoint (`freeSpace`, not `freeSpaceBytes`). */
187
+ export interface RawRootFolder {
188
+ freeSpace?: number;
189
+ id?: number;
190
+ path?: string;
191
+ [key: string]: unknown;
192
+ }
193
+ /** Raw service detail (`/service/radarr/{id}` | `/service/sonarr/{id}`). `profiles` is an array. */
194
+ export interface RawServiceDetail {
195
+ languageProfiles?: unknown;
196
+ profiles?: RawServiceProfile[];
197
+ rootFolders?: RawRootFolder[];
198
+ server?: RawServiceListEntry;
199
+ [key: string]: unknown;
200
+ }
201
+ /**
202
+ * Body submitted to `POST /request`. Also the `payload` surfaced in preview output,
203
+ * so `is4k` is always present (the tool sets it explicitly). `userId` is
204
+ * intentionally never set — the request is made as the API key's user.
205
+ */
206
+ export interface CreateRequestBody {
207
+ is4k: boolean;
208
+ languageProfileId?: number;
209
+ mediaId: number;
210
+ mediaType: 'movie' | 'tv';
211
+ profileId?: number;
212
+ rootFolder?: string;
213
+ seasons?: number[] | 'all';
214
+ serverId?: number;
215
+ }
216
+ /** PII-safe requester/modifier projection. The ONLY shape of a Seerr user that may reach output. */
217
+ export interface DomainUser {
218
+ displayName: string;
219
+ id: number;
220
+ }
221
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/seerr/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview Raw upstream Seerr API types (hand-written from the OpenAPI spec +
3
+ * live read-only probes against Seerr 3.3.0) and the normalized domain types this
4
+ * server exposes. Raw types default to optional unless the live payload guarantees
5
+ * presence — third-party APIs omit fields rather than nulling them, and over-strict
6
+ * raw types either fail validation or invent facts. The domain types are the
7
+ * PII-redacted projections produced by `normalizers.ts`.
8
+ * @module services/seerr/types
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/seerr/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "@cyanheads/seerr-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "Search Jellyseerr/Overseerr, check availability, and create guarded media requests via MCP. STDIO or Streamable HTTP.",
5
+ "mcpName": "io.github.cyanheads/seerr-mcp-server",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "seerr-mcp-server": "dist/index.js"
11
+ },
12
+ "files": [
13
+ "changelog/",
14
+ "dist/",
15
+ "README.md",
16
+ "LICENSE",
17
+ "CLAUDE.md",
18
+ "AGENTS.md",
19
+ "Dockerfile",
20
+ "server.json"
21
+ ],
22
+ "scripts": {
23
+ "build": "bun run scripts/build.ts",
24
+ "rebuild": "bun run scripts/clean.ts && bun run scripts/build.ts",
25
+ "clean": "bun run scripts/clean.ts",
26
+ "devcheck": "bun run scripts/devcheck.ts",
27
+ "audit:refresh": "rm -f bun.lock && bun install && bun audit",
28
+ "tree": "bun run scripts/tree.ts",
29
+ "list-skills": "bun run scripts/list-skills.ts",
30
+ "format": "biome check --write .",
31
+ "format:unsafe": "biome check --write --unsafe .",
32
+ "lint:mcp": "bun run scripts/lint-mcp.ts",
33
+ "lint:packaging": "bun run scripts/lint-packaging.ts",
34
+ "bundle": "bun run build && npx -y @anthropic-ai/mcpb pack . dist/seerr-mcp-server.mcpb && bun run scripts/clean-mcpb.ts dist/seerr-mcp-server.mcpb",
35
+ "changelog:build": "bun run scripts/build-changelog.ts",
36
+ "changelog:check": "bun run scripts/build-changelog.ts --check",
37
+ "release:github": "bun run scripts/release-github.ts",
38
+ "publish-mcp": "mcp-publisher login github -token \"$(security find-generic-password -a \"$USER\" -s mcp-publisher-github-pat -w)\" && mcp-publisher publish",
39
+ "test": "vitest run",
40
+ "start": "node dist/index.js",
41
+ "start:stdio": "MCP_TRANSPORT_TYPE=stdio node dist/index.js",
42
+ "start:http": "MCP_TRANSPORT_TYPE=http node dist/index.js"
43
+ },
44
+ "keywords": [
45
+ "mcp",
46
+ "mcp-server",
47
+ "model-context-protocol",
48
+ "jellyseerr",
49
+ "overseerr",
50
+ "seerr",
51
+ "radarr",
52
+ "sonarr",
53
+ "media-requests"
54
+ ],
55
+ "author": "cyanheads <casey@caseyjhand.com> (https://github.com/cyanheads/seerr-mcp-server#readme)",
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/cyanheads/seerr-mcp-server.git"
59
+ },
60
+ "homepage": "https://github.com/cyanheads/seerr-mcp-server#readme",
61
+ "bugs": {
62
+ "url": "https://github.com/cyanheads/seerr-mcp-server/issues"
63
+ },
64
+ "license": "Apache-2.0",
65
+ "engines": {
66
+ "bun": ">=1.3.0",
67
+ "node": ">=24.0.0"
68
+ },
69
+ "publishConfig": {
70
+ "access": "public"
71
+ },
72
+ "dependencies": {
73
+ "@cyanheads/mcp-ts-core": "^0.10.6",
74
+ "pino-pretty": "^13.1.3",
75
+ "zod": "^4.4.3"
76
+ },
77
+ "devDependencies": {
78
+ "@biomejs/biome": "2.5.0",
79
+ "@types/node": "25.9.3",
80
+ "depcheck": "^1.4.7",
81
+ "ignore": "^7.0.5",
82
+ "tsc-alias": "^1.8.17",
83
+ "typescript": "^6.0.3",
84
+ "vitest": "^4.1.8"
85
+ }
86
+ }