-
Notifications
You must be signed in to change notification settings - Fork 1
Add render item tool #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
32c1fcf
c8e6ee0
ba08a65
a7ebae0
83e91cd
5c9e355
636250c
4bdbc98
4fa629d
3dbc423
ae9d653
9bc027d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| export * from "./listRenderableItems"; | ||
| export * from "./getRenderableItemDetails"; | ||
| export * from "./renderItem"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import { AxiosResponse } from "axios"; | ||
| import { createAxiosInstance } from "../../axiosConfig"; | ||
| import { DesignRenderDto, ProjectRenderDto, Render } from "../types"; | ||
|
|
||
| const api = createAxiosInstance(); | ||
|
|
||
| type RenderParams = { | ||
| isDesign: boolean; | ||
| projectDesignId: string; | ||
| templateVariantId: string; | ||
| parameters?: { [key: string]: any }; | ||
| }; | ||
|
|
||
| export const renderItem = async (params: RenderParams): Promise<Render> => { | ||
| if (params.isDesign) { | ||
| return await renderDesign(params); | ||
| } else { | ||
| return await renderProject(params); | ||
| } | ||
| }; | ||
|
|
||
| const renderDesign = async (params: RenderParams): Promise<Render> => { | ||
| const response = await api.post< | ||
| Render, | ||
| AxiosResponse<Render>, | ||
| DesignRenderDto | ||
| >("/api/v2/designs", { | ||
| designId: params.projectDesignId, | ||
| variantId: params.templateVariantId, | ||
| parameters: params.parameters, | ||
| }); | ||
|
|
||
| return response.data; | ||
| }; | ||
|
|
||
| const renderProject = async (params: RenderParams): Promise<Render> => { | ||
| const response = await api.post< | ||
| Render, | ||
| AxiosResponse<Render>, | ||
| ProjectRenderDto | ||
| >("/api/v2/renders", { | ||
| projectId: params.projectDesignId, | ||
| templateId: params.templateVariantId, | ||
| parameters: params.parameters, | ||
|
danixeee marked this conversation as resolved.
|
||
| }); | ||
|
|
||
| return response.data; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| export * from "./getRenderableItemDetails"; | ||
| export * from "./listRenderableItems"; | ||
| export * from "./renderItem"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,204 @@ | ||
| import { z } from "zod"; | ||
| import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; | ||
| import { getRenderableItemsDetails, renderItem } from "../sdk"; | ||
|
|
||
| export function registerRenderItem(server: McpServer) { | ||
| const Input = { | ||
| isDesign: z | ||
| .boolean() | ||
| .describe( | ||
| "True when the parent is a Design; false when it is a Project." | ||
| ), | ||
| projectDesignId: z | ||
| .string() | ||
| .describe("Parent identifier (projectId or designId)."), | ||
| templateVariantId: z | ||
| .string() | ||
|
danixeee marked this conversation as resolved.
|
||
| .describe( | ||
| "Template/variant identifier (the renderable leaf under the parent)." | ||
| ), | ||
| parameters: z | ||
| .record(z.any()) | ||
| .default({}) | ||
| .describe( | ||
| "Key-value parameters required by the chosen template/variant to customize the render. Mandatory parameters must be provided. Parameter type must be respected." | ||
| ), | ||
| }; | ||
|
|
||
| const Output = { | ||
|
danixeee marked this conversation as resolved.
|
||
| id: z.string().optional().describe("Server-assigned render job ID."), | ||
| state: z | ||
| .enum([ | ||
|
danixeee marked this conversation as resolved.
Outdated
|
||
| "PENDING", | ||
| "THROTTLED", | ||
| "QUEUED", | ||
| "IN_PROGRESS", | ||
| "DONE", | ||
| "FAILED", | ||
| "INVALID", | ||
| "CANCELLED", | ||
| ]) | ||
| .optional() | ||
| .describe("Current state of the render job."), | ||
| output: z | ||
| .string() | ||
| .nullable() | ||
| .optional() | ||
| .describe("URL to the rendered video, if state is DONE."), | ||
| error: z | ||
| .record(z.any()) | ||
| .nullable() | ||
| .optional() | ||
| .describe("Error details, if state is FAILED or INVALID."), | ||
| }; | ||
|
|
||
| server.registerTool( | ||
| "render_item", | ||
| { | ||
| title: "Render Item", | ||
| description: ` | ||
| Create a render for a selected Project template or Design variant with specified parameters. | ||
|
|
||
| How to use: | ||
| - Call this after the user selects a candidate from \`get_renderable_items_details\`. | ||
| - Call this only once the user approved all parameters for the chosen template/variant. | ||
|
|
||
| Guidance: | ||
| - Use parameters to customize the render. | ||
| - All mandatory parameters must be provided. | ||
| - Provide values for optional parameters if it makes sense. | ||
| - Parameter types must be respected: | ||
| - STRING: text string relevant to the parameter context. | ||
| - MEDIA: URL to a media file (image, audio, or video). Ensure the URL is publicly accessible and points directly to the media file. | ||
| - MEDIA (image): URL to an image file (jpg, png, etc.). | ||
| - MEDIA (audio): URL to an audio file (mp3, wav, etc.). | ||
| - MEDIA (video): URL to a video file (mp4, mov, etc.). | ||
| - COLOR: hex color code (e.g. #FF5733). | ||
| - If a parameter has a default value and the user does not provide a value, the default will be used. | ||
| - If the user is unsure about a parameter, ask for clarification rather than guessing. | ||
| - When referencing parameters in conversation, use their \`label\` or \`description\` for clarity. | ||
|
|
||
| Use when: | ||
| - The user wants to create a video from a specific template/variant with defined parameters. | ||
| `, | ||
| inputSchema: Input, | ||
| outputSchema: Output, | ||
| }, | ||
| async ({ isDesign, projectDesignId, templateVariantId, parameters }) => { | ||
| // TODO: Handle object parameters "my.parameter.x" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i guess not needed, if explained how "keyed" params should be represented in the parameters object (just add instruction point).. |
||
|
|
||
| try { | ||
| // Validate that the chosen project / design exists | ||
| const projectDesignItems = await getRenderableItemsDetails( | ||
| projectDesignId, | ||
| isDesign | ||
| ); | ||
|
|
||
| if (projectDesignItems.length === 0) { | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `Could not find a ${ | ||
| isDesign ? "design" : "project" | ||
| } with id ${projectDesignId} .`, | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
|
|
||
| // Validate that the chosen template / variant exists | ||
| const renderableItem = projectDesignItems.find( | ||
| (item) => item.templateVariantId === templateVariantId | ||
| ); | ||
|
|
||
| if (!renderableItem) { | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `Could not find a ${ | ||
| isDesign ? "variant" : "template" | ||
| } with id ${templateVariantId} under the specified ${ | ||
| isDesign ? "design" : "project" | ||
| } (${projectDesignId}).`, | ||
| }, | ||
| ], | ||
| structuredContent: {}, | ||
| isError: true, | ||
| }; | ||
| } | ||
|
|
||
| // Validate parameters | ||
| const mandatoryParams = renderableItem.parameters.filter( | ||
| (p) => p.mandatory | ||
| ); | ||
| const providedParams = Object.keys(parameters); | ||
| const missingParams = mandatoryParams.filter( | ||
| (p) => !providedParams.includes(p.key) | ||
| ); | ||
|
|
||
| if (missingParams.length > 0) { | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `Missing required parameters: ${missingParams | ||
| .map((p) => p.key) | ||
| .join(", ")}.`, | ||
| }, | ||
| ], | ||
| structuredContent: {}, | ||
| isError: true, | ||
| }; | ||
| } | ||
|
|
||
| // If everything looks good, submit the render | ||
| const render = await renderItem({ | ||
| isDesign, | ||
| projectDesignId, | ||
| templateVariantId, | ||
| parameters, | ||
| }); | ||
|
|
||
| return { | ||
| content: [ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does it make sence that we send also a https://modelcontextprotocol.io/specification/2025-06-18/server/tools#resource-links for this render html page, so link to content is array and you can send multiple things
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| { | ||
|
danixeee marked this conversation as resolved.
|
||
| type: "text", | ||
| text: `🚀 Render submitted successfully! | ||
|
|
||
| **Render ID:** ${render.id} | ||
| **Parameters Used:** | ||
| ${Object.entries(parameters) | ||
| .map(([key, value]) => { | ||
| const param = renderableItem.parameters.find((p) => p.key === key); | ||
| return `• ${param?.label || key}: ${value}`; | ||
| }) | ||
| .join("\n")} | ||
|
|
||
| The render is being processed. Use the render ID to check status and retrieve the final video when complete.`, | ||
| }, | ||
| ], | ||
| structuredContent: { | ||
| id: render.id, | ||
| state: render.state ?? "QUEUED", | ||
| output: render.output ?? null, | ||
| error: null, | ||
| }, | ||
| }; | ||
| } catch (err: any) { | ||
|
danixeee marked this conversation as resolved.
|
||
| // Handle API errors gracefully | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `Failed to create render: ${err}`, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| }, | ||
| ], | ||
| structuredContent: {}, | ||
|
danixeee marked this conversation as resolved.
Outdated
|
||
| isError: true, | ||
| }; | ||
| } | ||
| } | ||
| ); | ||
| } | ||




Uh oh!
There was an error while loading. Please reload this page.