@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,117 @@
1
+ /**
2
+ * @fileoverview seerr_request_media — the guarded write, the only mutation in the
3
+ * surface. It resolves and PREVIEWS a request payload by default (mode: preview,
4
+ * no write), and creates the request only on mode:request AND an explicit
5
+ * ctx.elicit confirmation. "Download X" means "create a Seerr request for X" —
6
+ * never a direct Radarr/Sonarr call.
7
+ *
8
+ * Three-layer guard:
9
+ * 1. mode:preview is the blast-radius-safe default (resolve + validate, no POST).
10
+ * 2. mode:request triggers a ctx.elicit confirmation when the client supports it.
11
+ * 3. destructiveHint:true is the fallback signal for non-interactive clients.
12
+ *
13
+ * Capability validation (4K enabled? seasons valid? partial allowed?) runs LOCALLY
14
+ * against the cached /settings/public BEFORE any POST, so a bad request fails with
15
+ * an actionable typed error rather than a failed write.
16
+ * @module mcp-server/tools/definitions/request-media.tool
17
+ */
18
+ import { z } from '@cyanheads/mcp-ts-core';
19
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
20
+ export declare const requestMediaTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
21
+ mediaType: z.ZodEnum<{
22
+ movie: "movie";
23
+ tv: "tv";
24
+ }>;
25
+ tmdbId: z.ZodNumber;
26
+ mode: z.ZodDefault<z.ZodEnum<{
27
+ request: "request";
28
+ preview: "preview";
29
+ }>>;
30
+ is4k: z.ZodDefault<z.ZodBoolean>;
31
+ seasons: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodArray<z.ZodNumber>]>>;
32
+ serverId: z.ZodOptional<z.ZodNumber>;
33
+ profileId: z.ZodOptional<z.ZodNumber>;
34
+ rootFolder: z.ZodOptional<z.ZodString>;
35
+ languageProfileId: z.ZodOptional<z.ZodNumber>;
36
+ }, z.core.$strip>, z.ZodObject<{
37
+ mode: z.ZodEnum<{
38
+ request: "request";
39
+ preview: "preview";
40
+ }>;
41
+ resolved: z.ZodObject<{
42
+ tmdbId: z.ZodNumber;
43
+ mediaType: z.ZodEnum<{
44
+ movie: "movie";
45
+ tv: "tv";
46
+ }>;
47
+ title: z.ZodString;
48
+ year: z.ZodOptional<z.ZodNumber>;
49
+ }, z.core.$strip>;
50
+ capability: z.ZodObject<{
51
+ is4kEnabled: z.ZodBoolean;
52
+ partialRequestsEnabled: z.ZodBoolean;
53
+ }, z.core.$strip>;
54
+ payload: z.ZodObject<{
55
+ mediaType: z.ZodEnum<{
56
+ movie: "movie";
57
+ tv: "tv";
58
+ }>;
59
+ mediaId: z.ZodNumber;
60
+ is4k: z.ZodBoolean;
61
+ seasons: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodArray<z.ZodNumber>]>>;
62
+ serverId: z.ZodOptional<z.ZodNumber>;
63
+ profileId: z.ZodOptional<z.ZodNumber>;
64
+ rootFolder: z.ZodOptional<z.ZodString>;
65
+ languageProfileId: z.ZodOptional<z.ZodNumber>;
66
+ }, z.core.$strip>;
67
+ existingRequest: z.ZodOptional<z.ZodObject<{
68
+ requestId: z.ZodNumber;
69
+ status: z.ZodObject<{
70
+ raw: z.ZodNumber;
71
+ label: z.ZodString;
72
+ }, z.core.$strip>;
73
+ is4k: z.ZodBoolean;
74
+ }, z.core.$strip>>;
75
+ created: z.ZodOptional<z.ZodObject<{
76
+ requestId: z.ZodNumber;
77
+ requestStatus: z.ZodObject<{
78
+ raw: z.ZodNumber;
79
+ label: z.ZodString;
80
+ }, z.core.$strip>;
81
+ mediaStatus: z.ZodOptional<z.ZodObject<{
82
+ raw: z.ZodNumber;
83
+ label: z.ZodString;
84
+ }, z.core.$strip>>;
85
+ }, z.core.$strip>>;
86
+ }, z.core.$strip>, readonly [{
87
+ readonly reason: "media_not_found";
88
+ readonly code: JsonRpcErrorCode.NotFound;
89
+ readonly when: "tmdbId does not resolve to a movie/show (Seerr 500 \"Unable to retrieve movie.\").";
90
+ readonly recovery: "Run seerr_search_media to get the correct tmdbId and retry with the matching mediaType.";
91
+ }, {
92
+ readonly reason: "seasons_required";
93
+ readonly code: JsonRpcErrorCode.InvalidParams;
94
+ readonly when: "mediaType is tv but no seasons were provided.";
95
+ readonly recovery: "Provide seasons: \"all\" for the whole series or an explicit list like [1,2].";
96
+ }, {
97
+ readonly reason: "four_k_not_enabled";
98
+ readonly code: JsonRpcErrorCode.InvalidParams;
99
+ readonly when: "is4k:true but the instance has no 4K configured for this media type.";
100
+ readonly recovery: "Resubmit with is4k:false, or check seerr_service_options for 4K availability first.";
101
+ }, {
102
+ readonly reason: "partial_requests_disabled";
103
+ readonly code: JsonRpcErrorCode.InvalidParams;
104
+ readonly when: "an explicit season list was given but the instance disallows partial requests.";
105
+ readonly recovery: "Use seasons:\"all\" to request the full series instead of specific seasons.";
106
+ }, {
107
+ readonly reason: "duplicate_request";
108
+ readonly code: JsonRpcErrorCode.InvalidParams;
109
+ readonly when: "Seerr rejects the POST because an identical request already exists.";
110
+ readonly recovery: "Check the existingRequest in this output; track it with seerr_request_status instead of re-requesting.";
111
+ }, {
112
+ readonly reason: "request_cancelled";
113
+ readonly code: JsonRpcErrorCode.InvalidParams;
114
+ readonly when: "the user declined the elicit confirmation.";
115
+ readonly recovery: "Re-run with mode:request and confirm the prompt if you intend to submit the request.";
116
+ }], undefined>;
117
+ //# sourceMappingURL=request-media.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-media.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/request-media.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAY,MAAM,+BAA+B,CAAC;AAW3E,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4V3B,CAAC"}
@@ -0,0 +1,347 @@
1
+ /**
2
+ * @fileoverview seerr_request_media — the guarded write, the only mutation in the
3
+ * surface. It resolves and PREVIEWS a request payload by default (mode: preview,
4
+ * no write), and creates the request only on mode:request AND an explicit
5
+ * ctx.elicit confirmation. "Download X" means "create a Seerr request for X" —
6
+ * never a direct Radarr/Sonarr call.
7
+ *
8
+ * Three-layer guard:
9
+ * 1. mode:preview is the blast-radius-safe default (resolve + validate, no POST).
10
+ * 2. mode:request triggers a ctx.elicit confirmation when the client supports it.
11
+ * 3. destructiveHint:true is the fallback signal for non-interactive clients.
12
+ *
13
+ * Capability validation (4K enabled? seasons valid? partial allowed?) runs LOCALLY
14
+ * against the cached /settings/public BEFORE any POST, so a bad request fails with
15
+ * an actionable typed error rather than a failed write.
16
+ * @module mcp-server/tools/definitions/request-media.tool
17
+ */
18
+ import { tool, z } from '@cyanheads/mcp-ts-core';
19
+ import { JsonRpcErrorCode, McpError } from '@cyanheads/mcp-ts-core/errors';
20
+ import { redactOpenRequest } from '../../../services/seerr/normalizers.js';
21
+ import { getSeerrService } from '../../../services/seerr/seerr-service.js';
22
+ import { decodeMediaStatus, decodeRequestStatus, StatusRef, statusText, } from '../../../services/seerr/status.js';
23
+ export const requestMediaTool = tool('seerr_request_media', {
24
+ title: 'seerr-mcp-server: request media',
25
+ description: 'Create a Seerr media request — the guarded write. Defaults to mode:preview, which resolves the exact title and returns the request payload that WOULD be submitted WITHOUT creating anything; pass mode:request to actually submit (a confirmation is requested first when the client supports it). Treat "download X" as "create a Seerr request for X". Always preview first unless the title is already confirmed.',
26
+ annotations: {
27
+ readOnlyHint: false,
28
+ destructiveHint: true,
29
+ openWorldHint: true,
30
+ idempotentHint: false,
31
+ },
32
+ input: z.object({
33
+ mediaType: z.enum(['movie', 'tv']).describe('Whether tmdbId is a movie or TV show.'),
34
+ tmdbId: z
35
+ .number()
36
+ .int()
37
+ .positive()
38
+ .describe('TMDB ID from seerr_search_media (results[].tmdbId). Must be the exact resolved ID — preview shows the title it resolves to before submitting.'),
39
+ mode: z
40
+ .enum(['preview', 'request'])
41
+ .default('preview')
42
+ .describe('"preview" (default) resolves and returns the exact payload that WOULD be submitted WITHOUT creating anything. "request" submits the request to Seerr. Always preview first unless the title is already confirmed.'),
43
+ is4k: z
44
+ .boolean()
45
+ .default(false)
46
+ .describe('Request the 4K version. Only valid when the instance has 4K enabled for this media type (preview reports capability); Seerr rejects 4K requests otherwise.'),
47
+ seasons: z
48
+ .union([
49
+ z.literal('all'),
50
+ z
51
+ .array(z.number().int().min(1))
52
+ .min(1)
53
+ .describe('Explicit list of season numbers to request, e.g. [1, 2].'),
54
+ ])
55
+ .optional()
56
+ .describe('TV only. "all" requests every season, or an explicit list of season numbers (e.g. [1,2]). Season 0 (Specials) is excluded unless the instance enables special episodes. Required for TV requests; ignored for movies.'),
57
+ serverId: z
58
+ .number()
59
+ .int()
60
+ .min(0)
61
+ .optional()
62
+ .describe('Override the Radarr/Sonarr server ID (from seerr_service_options). Omit to use the Seerr default — recommended.'),
63
+ profileId: z
64
+ .number()
65
+ .int()
66
+ .positive()
67
+ .optional()
68
+ .describe('Override the quality profile ID (from seerr_service_options). Omit to use the server default.'),
69
+ rootFolder: z
70
+ .string()
71
+ .optional()
72
+ .describe('Override the root folder path. Omit to use the server default — recommended. Rarely needed; Seerr routes by default.'),
73
+ languageProfileId: z
74
+ .number()
75
+ .int()
76
+ .positive()
77
+ .optional()
78
+ .describe('Sonarr language profile ID override (TV only). Omit to use the default. No-op on Sonarr v4.'),
79
+ }),
80
+ output: z.object({
81
+ mode: z.enum(['preview', 'request']).describe('Which arm ran.'),
82
+ resolved: z
83
+ .object({
84
+ tmdbId: z.number().describe('Resolved TMDB ID.'),
85
+ mediaType: z.enum(['movie', 'tv']).describe('Movie or TV.'),
86
+ title: z
87
+ .string()
88
+ .describe('Resolved title — confirm this matches intent before requesting.'),
89
+ year: z.number().optional().describe('Release/first-air year.'),
90
+ })
91
+ .describe('What the tmdbId resolved to — the confirmation surface.'),
92
+ capability: z
93
+ .object({
94
+ is4kEnabled: z
95
+ .boolean()
96
+ .describe('Whether 4K is enabled for this media type on the instance.'),
97
+ partialRequestsEnabled: z.boolean().describe('Whether per-season TV requests are allowed.'),
98
+ })
99
+ .describe('Instance constraints relevant to this request.'),
100
+ payload: z
101
+ .object({
102
+ mediaType: z.enum(['movie', 'tv']).describe('Payload media type.'),
103
+ mediaId: z.number().describe('TMDB ID Seerr will request.'),
104
+ is4k: z.boolean().describe('4K flag in the payload.'),
105
+ seasons: z
106
+ .union([
107
+ z.literal('all'),
108
+ z.array(z.number()).describe('Explicit season numbers in the payload.'),
109
+ ])
110
+ .optional()
111
+ .describe('Seasons in the payload (TV).'),
112
+ serverId: z.number().optional().describe('Server override, if any.'),
113
+ profileId: z.number().optional().describe('Profile override, if any.'),
114
+ rootFolder: z.string().optional().describe('Root folder override, if any.'),
115
+ languageProfileId: z.number().optional().describe('Language profile override, if any.'),
116
+ })
117
+ .describe('The exact request body that was (or would be) submitted.'),
118
+ existingRequest: z
119
+ .object({
120
+ requestId: z
121
+ .number()
122
+ .describe('Existing request ID for this title, if one already exists.'),
123
+ status: StatusRef.describe('Decoded status of the existing request.'),
124
+ is4k: z.boolean().describe('Whether the existing request is 4K.'),
125
+ })
126
+ .optional()
127
+ .describe('Surfaced when the title already has a request — preview warns; request still proceeds if the agent intends a distinct (e.g. 4K) request.'),
128
+ created: z
129
+ .object({
130
+ requestId: z.number().describe('New request ID — pass to seerr_request_status.'),
131
+ requestStatus: StatusRef.describe('Decoded status of the created request (often "pending" or auto-"approved").'),
132
+ mediaStatus: StatusRef.optional().describe('Decoded media availability after creation.'),
133
+ })
134
+ .optional()
135
+ .describe('Present only when mode=request and the POST succeeded.'),
136
+ }),
137
+ errors: [
138
+ {
139
+ reason: 'media_not_found',
140
+ code: JsonRpcErrorCode.NotFound,
141
+ when: 'tmdbId does not resolve to a movie/show (Seerr 500 "Unable to retrieve movie.").',
142
+ recovery: 'Run seerr_search_media to get the correct tmdbId and retry with the matching mediaType.',
143
+ },
144
+ {
145
+ reason: 'seasons_required',
146
+ code: JsonRpcErrorCode.InvalidParams,
147
+ when: 'mediaType is tv but no seasons were provided.',
148
+ recovery: 'Provide seasons: "all" for the whole series or an explicit list like [1,2].',
149
+ },
150
+ {
151
+ reason: 'four_k_not_enabled',
152
+ code: JsonRpcErrorCode.InvalidParams,
153
+ when: 'is4k:true but the instance has no 4K configured for this media type.',
154
+ recovery: 'Resubmit with is4k:false, or check seerr_service_options for 4K availability first.',
155
+ },
156
+ {
157
+ reason: 'partial_requests_disabled',
158
+ code: JsonRpcErrorCode.InvalidParams,
159
+ when: 'an explicit season list was given but the instance disallows partial requests.',
160
+ recovery: 'Use seasons:"all" to request the full series instead of specific seasons.',
161
+ },
162
+ {
163
+ reason: 'duplicate_request',
164
+ code: JsonRpcErrorCode.InvalidParams,
165
+ when: 'Seerr rejects the POST because an identical request already exists.',
166
+ recovery: 'Check the existingRequest in this output; track it with seerr_request_status instead of re-requesting.',
167
+ },
168
+ {
169
+ reason: 'request_cancelled',
170
+ code: JsonRpcErrorCode.InvalidParams,
171
+ when: 'the user declined the elicit confirmation.',
172
+ recovery: 'Re-run with mode:request and confirm the prompt if you intend to submit the request.',
173
+ },
174
+ ],
175
+ async handler(input, ctx) {
176
+ const seerr = getSeerrService();
177
+ // Step 1 — capability flags (cached). Step 2 — resolve title + existing requests.
178
+ const settings = await seerr.getPublicSettings(ctx);
179
+ const is4kEnabled = input.mediaType === 'movie'
180
+ ? settings.movie4kEnabled === true
181
+ : settings.series4kEnabled === true;
182
+ const partialRequestsEnabled = settings.partialRequestsEnabled === true;
183
+ let title = '';
184
+ let year;
185
+ let media;
186
+ if (input.mediaType === 'movie') {
187
+ const movie = await seerr.getMovie(input.tmdbId, ctx);
188
+ title = movie.title ?? '';
189
+ year = yearOf(movie.releaseDate);
190
+ media = movie.mediaInfo;
191
+ }
192
+ else {
193
+ const tv = await seerr.getTv(input.tmdbId, ctx);
194
+ title = tv.name ?? '';
195
+ year = yearOf(tv.firstAirDate);
196
+ media = tv.mediaInfo;
197
+ }
198
+ const existingRequest = redactOpenRequest(media);
199
+ // Step 3 — local validation BEFORE any write (actionable typed errors, no failed POST).
200
+ if (input.mediaType === 'tv' && input.seasons === undefined) {
201
+ throw ctx.fail('seasons_required', undefined, { ...ctx.recoveryFor('seasons_required') });
202
+ }
203
+ if (input.is4k && !is4kEnabled) {
204
+ throw ctx.fail('four_k_not_enabled', `4K is not enabled for ${input.mediaType} requests on this instance.`, {
205
+ ...ctx.recoveryFor('four_k_not_enabled'),
206
+ });
207
+ }
208
+ if (Array.isArray(input.seasons) && !partialRequestsEnabled) {
209
+ throw ctx.fail('partial_requests_disabled', undefined, {
210
+ ...ctx.recoveryFor('partial_requests_disabled'),
211
+ });
212
+ }
213
+ const resolved = {
214
+ tmdbId: input.tmdbId,
215
+ mediaType: input.mediaType,
216
+ title,
217
+ ...(year !== undefined ? { year } : {}),
218
+ };
219
+ const capability = { is4kEnabled, partialRequestsEnabled };
220
+ const payload = buildPayload(input);
221
+ const baseOutput = {
222
+ resolved,
223
+ capability,
224
+ payload,
225
+ ...(existingRequest ? { existingRequest } : {}),
226
+ };
227
+ // PREVIEW — resolve + validate only, no write.
228
+ if (input.mode === 'preview') {
229
+ ctx.log.info('Seerr request previewed', { tmdbId: input.tmdbId, mediaType: input.mediaType });
230
+ return { mode: 'preview', ...baseOutput };
231
+ }
232
+ // REQUEST — guarded write. Layer 2: elicit confirmation when the client supports it.
233
+ if (ctx.elicit) {
234
+ const seasonSummary = input.mediaType === 'tv'
235
+ ? input.seasons === 'all'
236
+ ? ' (all seasons)'
237
+ : Array.isArray(input.seasons)
238
+ ? ` (seasons ${input.seasons.join(', ')})`
239
+ : ''
240
+ : '';
241
+ const confirm = await ctx.elicit(`Create a Seerr request for "${title}"${input.is4k ? ' (4K)' : ''}${seasonSummary}? This adds it to your Radarr/Sonarr download queue.`, z.object({ confirmed: z.literal(true).describe('Set true to submit the request.') }));
242
+ if (confirm.action !== 'accept') {
243
+ throw ctx.fail('request_cancelled', 'Request cancelled before submission.', {
244
+ ...ctx.recoveryFor('request_cancelled'),
245
+ });
246
+ }
247
+ }
248
+ // Layer 1+3: preview default already passed; destructiveHint covers non-elicit clients.
249
+ let created;
250
+ try {
251
+ const result = await seerr.createRequest(payload, ctx);
252
+ const mediaStatus = typeof result.media?.status === 'number'
253
+ ? decodeMediaStatus(result.media.status)
254
+ : undefined;
255
+ created = {
256
+ requestId: result.id,
257
+ requestStatus: decodeRequestStatus(result.status ?? 1),
258
+ ...(mediaStatus ? { mediaStatus } : {}),
259
+ };
260
+ }
261
+ catch (err) {
262
+ // Cross-check a rejected POST against the resolved existing request → duplicate.
263
+ if (existingRequest && isDuplicateRejection(err)) {
264
+ throw ctx.fail('duplicate_request', 'Seerr rejected the request as a duplicate of an existing one.', {
265
+ existingRequestId: existingRequest.requestId,
266
+ ...ctx.recoveryFor('duplicate_request'),
267
+ });
268
+ }
269
+ throw err;
270
+ }
271
+ ctx.log.notice('Seerr request created', {
272
+ tmdbId: input.tmdbId,
273
+ requestId: created.requestId,
274
+ status: created.requestStatus.label,
275
+ });
276
+ return { mode: 'request', ...baseOutput, created };
277
+ },
278
+ format: (result) => {
279
+ const lines = [];
280
+ const r = result.resolved;
281
+ const yearPart = r.year !== undefined ? ` (${r.year})` : '';
282
+ const verb = result.mode === 'preview' ? 'Preview' : 'Request submitted';
283
+ lines.push(`# ${verb}: ${r.title}${yearPart} — ${r.mediaType}`);
284
+ lines.push(`**TMDB ID:** ${r.tmdbId}`);
285
+ lines.push(`**Capability:** 4K ${result.capability.is4kEnabled ? 'enabled' : 'disabled'} | partial requests ${result.capability.partialRequestsEnabled ? 'enabled' : 'disabled'}`);
286
+ const p = result.payload;
287
+ const payloadFacts = [`media=${p.mediaType}`, `tmdbId=${p.mediaId}`, `4K=${p.is4k}`];
288
+ if (p.seasons !== undefined)
289
+ payloadFacts.push(`seasons=${p.seasons === 'all' ? 'all' : p.seasons.join(',')}`);
290
+ if (p.serverId !== undefined)
291
+ payloadFacts.push(`serverId=${p.serverId}`);
292
+ if (p.profileId !== undefined)
293
+ payloadFacts.push(`profileId=${p.profileId}`);
294
+ if (p.rootFolder !== undefined)
295
+ payloadFacts.push(`rootFolder=${p.rootFolder}`);
296
+ if (p.languageProfileId !== undefined)
297
+ payloadFacts.push(`languageProfileId=${p.languageProfileId}`);
298
+ lines.push(`**Payload:** ${payloadFacts.join(', ')}`);
299
+ if (result.existingRequest) {
300
+ lines.push(`**Existing request:** #${result.existingRequest.requestId} — status ${statusText(result.existingRequest.status)} | 4K: ${result.existingRequest.is4k ? 'Yes' : 'No'} — a request for this title already exists.`);
301
+ }
302
+ if (result.mode === 'preview') {
303
+ lines.push('');
304
+ lines.push('> No request was created. Re-run with mode:request to submit.');
305
+ }
306
+ if (result.created) {
307
+ lines.push('');
308
+ const ms = result.created.mediaStatus
309
+ ? ` | media ${statusText(result.created.mediaStatus)}`
310
+ : '';
311
+ lines.push(`**Created request #${result.created.requestId}** — status ${statusText(result.created.requestStatus)}${ms}. Track with seerr_request_status.`);
312
+ }
313
+ return [{ type: 'text', text: lines.join('\n') }];
314
+ },
315
+ });
316
+ /** Parse a 4-digit year from an ISO-ish date string. */
317
+ function yearOf(date) {
318
+ if (!date)
319
+ return;
320
+ const year = Number.parseInt(date.slice(0, 4), 10);
321
+ return Number.isFinite(year) ? year : undefined;
322
+ }
323
+ /** Build the `POST /request` body from validated input. `userId` is intentionally never set. */
324
+ function buildPayload(input) {
325
+ return {
326
+ mediaType: input.mediaType,
327
+ mediaId: input.tmdbId,
328
+ is4k: input.is4k,
329
+ ...(input.mediaType === 'tv' && input.seasons !== undefined ? { seasons: input.seasons } : {}),
330
+ ...(typeof input.serverId === 'number' ? { serverId: input.serverId } : {}),
331
+ ...(typeof input.profileId === 'number' ? { profileId: input.profileId } : {}),
332
+ ...(input.rootFolder ? { rootFolder: input.rootFolder } : {}),
333
+ ...(typeof input.languageProfileId === 'number'
334
+ ? { languageProfileId: input.languageProfileId }
335
+ : {}),
336
+ };
337
+ }
338
+ /** A Seerr POST rejection that signals an already-existing request (409, or a 500 with a duplicate message). */
339
+ function isDuplicateRejection(err) {
340
+ if (!(err instanceof McpError))
341
+ return false;
342
+ const status = err.data?.httpStatus;
343
+ if (status === 409)
344
+ return true;
345
+ return /already exists|duplicate|existing request/i.test(err.message);
346
+ }
347
+ //# sourceMappingURL=request-media.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-media.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/request-media.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview seerr_request_status — track one request through its lifecycle.
3
+ * Wraps `GET /request/{id}`. Returns decoded request + media status, requester
4
+ * (PII-redacted), a routing summary (names/IDs only — no filesystem paths), and a
5
+ * status-tuned recovery hint. A missing request id surfaces as request_not_found
6
+ * (Seerr's raw HTTP 404 is classified in the service layer). `profileName` is null
7
+ * at this endpoint, so routing.profileName is optional; `title` is not on the
8
+ * request object, so it is left unpopulated.
9
+ * @module mcp-server/tools/definitions/request-status.tool
10
+ */
11
+ import { z } from '@cyanheads/mcp-ts-core';
12
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
13
+ export declare const requestStatusTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
14
+ requestId: z.ZodNumber;
15
+ }, z.core.$strip>, z.ZodObject<{
16
+ requestId: z.ZodNumber;
17
+ mediaType: z.ZodEnum<{
18
+ movie: "movie";
19
+ tv: "tv";
20
+ }>;
21
+ title: z.ZodOptional<z.ZodString>;
22
+ tmdbId: z.ZodOptional<z.ZodNumber>;
23
+ requestStatus: z.ZodObject<{
24
+ raw: z.ZodNumber;
25
+ label: z.ZodString;
26
+ }, z.core.$strip>;
27
+ mediaStatus: z.ZodOptional<z.ZodObject<{
28
+ raw: z.ZodNumber;
29
+ label: z.ZodString;
30
+ }, z.core.$strip>>;
31
+ mediaStatus4k: z.ZodOptional<z.ZodObject<{
32
+ raw: z.ZodNumber;
33
+ label: z.ZodString;
34
+ }, z.core.$strip>>;
35
+ is4k: z.ZodBoolean;
36
+ seasons: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
37
+ requestedBy: z.ZodObject<{
38
+ id: z.ZodNumber;
39
+ displayName: z.ZodString;
40
+ }, z.core.$strip>;
41
+ routing: z.ZodObject<{
42
+ serverId: z.ZodOptional<z.ZodNumber>;
43
+ profileName: z.ZodOptional<z.ZodString>;
44
+ is4k: z.ZodBoolean;
45
+ }, z.core.$strip>;
46
+ createdAt: z.ZodString;
47
+ updatedAt: z.ZodString;
48
+ stateGuidance: z.ZodOptional<z.ZodString>;
49
+ }, z.core.$strip>, readonly [{
50
+ readonly reason: "request_not_found";
51
+ readonly code: JsonRpcErrorCode.NotFound;
52
+ readonly when: "No request exists with the given ID (Seerr returns HTTP 404 \"Request not found.\").";
53
+ readonly recovery: "List requests with seerr_list_requests to find a valid requestId, then retry.";
54
+ }], undefined>;
55
+ //# sourceMappingURL=request-status.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-status.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/request-status.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAKjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAsG5B,CAAC"}