Conversation
- Route handler at app/api/video/render/route.ts - Zod validation for compositionId, inputProps, output settings - Trigger function that fires render-video task on Trigger.dev - Handler follows sandbox setup pattern: trigger → return runId - MCP tool (render_video) shares triggerRenderVideo logic with API - 18 tests covering validation, handler, and trigger function Polling for render status uses existing GET /api/tasks/runs endpoint.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR introduces a complete video rendering feature spanning API routes, request validation, authentication, and MCP tool integration. It adds a POST Changes
Sequence DiagramsequenceDiagram
participant Client
participant APIRoute as API Route<br>/api/video/render
participant Handler as renderVideoHandler
participant Validator as validateRenderVideoBody
participant Trigger as Trigger.dev
participant Response as Response
Client->>APIRoute: POST /api/video/render<br/>(with auth + body)
APIRoute->>Handler: POST(request)
Handler->>Handler: validateAuthContext<br/>(x-api-key or Bearer)
Handler->>Validator: validateRenderVideoBody(body)
Validator->>Validator: safeParseJson &<br/>Zod validation
Validator->>Handler: RenderVideoBody | Error
Handler->>Trigger: triggerRenderVideo(payload)<br/>with accountId
Trigger->>Trigger: Dispatch "render-video" task
Trigger->>Handler: {runId, ...metadata}
Handler->>Response: {status: "processing",<br/>runId} + CORS headers
Response->>Client: 200 OK
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@lib/mcp/tools/render/registerRenderVideoTool.ts`:
- Around line 70-76: The code manually extracts accountId from extra.authInfo;
replace that with the shared resolver: import resolveAccountId and call await
resolveAccountId({ authInfo }) using the existing authInfo variable, then check
the returned { accountId, error } and return getToolResultError(error ||
"Authentication required.") if error or no accountId; update the handler where
authInfo is used (the async handler in registerRenderVideoTool) to follow the
same pattern as registerUpdatePulseTool and registerCreateNewArtistTool.
🧹 Nitpick comments (4)
lib/trigger/triggerRenderVideo.ts (1)
1-27: Clean implementation — good SRP and DRY foundation.This function correctly serves as the single shared trigger point for both the REST endpoint and MCP tool. The type definition, JSDoc, and delegation are all solid.
One minor note: the
"render-video"task ID on Line 26 is a magic string. Per coding guidelines, shared constants should live inlib/const.ts. If this identifier is referenced elsewhere (e.g., in the tasks repo), extracting it would reduce drift risk.lib/render/validateRenderVideoBody.ts (1)
11-21: Schema defaults are duplicated in the MCP tool — consider reusing.The defaults defined here (720×1280, 30 fps, 240 frames,
"h264") are manually replicated inlib/mcp/tools/render/registerRenderVideoTool.ts(Lines 82–86) via?? fallbackexpressions. If these values ever change, they'll need updating in two places.Consider exporting the defaults (or the schema itself) so the MCP tool can reference the same source of truth, or at minimum add a comment linking the two.
lib/render/renderVideoHandler.ts (1)
45-48: Consider HTTP 202 Accepted for async task dispatch.The endpoint returns
200withstatus: "processing", but semantically202 Acceptedis the standard HTTP status for "request accepted, processing not yet complete." If this deviates from an established project convention, feel free to keep 200 — but 202 more clearly signals the async nature to API consumers.lib/mcp/tools/render/registerRenderVideoTool.ts (1)
11-46: Duplicated schema constraints withvalidateRenderVideoBody.ts.The validation constraints (e.g.,
.int().min(1).max(3840)for width/height,.min(1).max(60)for fps,.min(1).max(1800)for durationInFrames, the codec enum) are defined in both this file andlib/render/validateRenderVideoBody.ts. If constraints diverge over time, the MCP tool and REST endpoint would accept different inputs.Consider extracting the shared constraints or base schema into a common module that both consumers can reference, while each applies its own
.optional()vs.default()semantics on top.
| async (args, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => { | ||
| const authInfo = extra.authInfo as McpAuthInfo | undefined; | ||
| const accountId = authInfo?.extra?.accountId; | ||
|
|
||
| if (!accountId) { | ||
| return getToolResultError("Authentication required."); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
# Search for resolveAccountId definition and usage
rg -n "resolveAccountId" --type=ts -B2 -A5Repository: recoupable/api
Length of output: 3520
🏁 Script executed:
cat -n lib/mcp/tools/render/registerRenderVideoTool.ts | head -100Repository: recoupable/api
Length of output: 4366
Import and use resolveAccountId() to derive the account ID from authentication.
The codebase establishes a consistent pattern for MCP tools: use resolveAccountId() with authInfo from extra.authInfo rather than manual extraction. This centralizes auth resolution logic and aligns with established patterns in registerUpdatePulseTool.ts and registerCreateNewArtistTool.ts.
Replace lines 71–76 with:
const authInfo = extra.authInfo as McpAuthInfo | undefined;
const { accountId, error } = await resolveAccountId({ authInfo });
if (error || !accountId) {
return getToolResultError(error || "Authentication required.");
}Also add the import at the top:
import { resolveAccountId } from "@/lib/mcp/resolveAccountId";🤖 Prompt for AI Agents
In `@lib/mcp/tools/render/registerRenderVideoTool.ts` around lines 70 - 76, The
code manually extracts accountId from extra.authInfo; replace that with the
shared resolver: import resolveAccountId and call await resolveAccountId({
authInfo }) using the existing authInfo variable, then check the returned {
accountId, error } and return getToolResultError(error || "Authentication
required.") if error or no accountId; update the handler where authInfo is used
(the async handler in registerRenderVideoTool) to follow the same pattern as
registerUpdatePulseTool and registerCreateNewArtistTool.
Summary
Adds a
POST /api/video/renderendpoint that triggers a server-side video render as a Trigger.dev background task. Returns a run ID that can be polled via the existingGET /api/tasks/runsendpoint.What Changed
New Files (10 files, 686 lines)
app/api/video/render/route.tslib/render/renderVideoHandler.tslib/render/validateRenderVideoBody.tslib/trigger/triggerRenderVideo.tsrender-videotask on Trigger.devlib/mcp/tools/render/registerRenderVideoTool.tstriggerRenderVideolib/mcp/tools/render/index.tsModified Files
lib/mcp/tools/index.tsTests (18 passing)
lib/render/__tests__/validateRenderVideoBody.test.tslib/render/__tests__/renderVideoHandler.test.tslib/trigger/__tests__/triggerRenderVideo.test.tsArchitecture
Follows the exact same pattern as
POST /api/sandboxes/setup:DRY
The MCP tool (
render_video) and REST endpoint both call the sametriggerRenderVideofunction.Next Steps (PR 2)
renderVideoTaskimplementation (in tasks repo)SocialPostcomposition added to remotion submoduleRelated
POST /api/video/renderto OpenAPI spec + Mintlify docsSummary by CodeRabbit