diff --git a/packages/api/src/mcp-tools.ts b/packages/api/src/mcp-tools.ts index b8ae56deb..d9ade36a0 100644 --- a/packages/api/src/mcp-tools.ts +++ b/packages/api/src/mcp-tools.ts @@ -24,6 +24,17 @@ const isProcedure = (value: AnyProcedure | RouterRecord): value is AnyProcedure const mergeInputs = (inputs: z.ZodObject[]) => inputs.reduce((merged, input) => merged.extend(input.shape), emptyInput); +const getObjectInput = (input: z.ZodType) => { + if (input instanceof z.ZodObject) return input; + + if (input instanceof z.ZodOptional) { + const unwrappedInput = input.unwrap(); + if (unwrappedInput instanceof z.ZodObject) return unwrappedInput; + } + + return null; +}; + const getMcpMetadata = (meta: unknown): McpMeta["mcp"] => { if (!meta || typeof meta !== "object" || !("mcp" in meta)) return undefined; @@ -41,8 +52,13 @@ const getMcpMetadata = (meta: unknown): McpMeta["mcp"] => { const getProcedureInput = (inputs: z.ZodType[]) => { if (inputs.length === 0) return emptyInput; - if (inputs.length === 1) return inputs[0] ?? emptyInput; - if (inputs.every((input): input is z.ZodObject => input instanceof z.ZodObject)) return mergeInputs(inputs); + if (inputs.length === 1) { + const input = inputs[0] ?? emptyInput; + return getObjectInput(input) ?? input; + } + + const objectInputs = inputs.map(getObjectInput); + if (objectInputs.every((input): input is z.ZodObject => input !== null)) return mergeInputs(objectInputs); return null; }; diff --git a/packages/api/src/router/integration/integration-router.ts b/packages/api/src/router/integration/integration-router.ts index 0add5f517..a36ff5c6e 100644 --- a/packages/api/src/router/integration/integration-router.ts +++ b/packages/api/src/router/integration/integration-router.ts @@ -715,8 +715,15 @@ export const integrationRouter = createTRPCRouter({ .mutation(async ({ ctx, input }) => { const integration = await createIntegrationAsync(ctx.integration); const result = await integration.requestMediaAsync(input.mediaType, input.mediaId, input.seasons); - mediaRequestListRequestHandler.invalidateCache(); - await mediaRequestStatsRequestHandler.invalidateCacheAsync([ctx.integration.id]); + try { + mediaRequestListRequestHandler.invalidateCache(); + await mediaRequestStatsRequestHandler.invalidateCacheAsync([ctx.integration.id]); + } catch (error) { + logger.warn("Failed to invalidate media request caches after a successful request", { + integrationId: ctx.integration.id, + error: String(error), + }); + } return result; }), }); diff --git a/packages/redis/src/lib/channel.ts b/packages/redis/src/lib/channel.ts index 1cc9d6829..467ea14a7 100644 --- a/packages/redis/src/lib/channel.ts +++ b/packages/redis/src/lib/channel.ts @@ -140,7 +140,11 @@ export const createGetSetChannel = (name: string, options: RedisChannelOp await requireRedisConnection(client).set(name, superjson.stringify(data), "PX", options.ttlMs); return; } - if (options?.ttlSeconds) { + if (options?.ttlSeconds !== undefined) { + if (options.ttlSeconds <= 0) { + await requireRedisConnection(client).del(name); + return; + } await requireRedisConnection(client).set(name, superjson.stringify(data), "EX", options.ttlSeconds); return; }