Skip to main content
Glama

generate-image-from-studio

Create customized images from Orshot Studio templates by mapping data to dynamic fields. Specify template IDs, modify content, and generate outputs in PNG, JPG, or PDF formats directly via API integration.

Instructions

Generate an image from an Orshot Studio template using specified data modifications. Automatically maps URLs to appropriate image fields in the template. You can use either the template ID (numeric) or template name.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
apiKeyNoOrshot API key for authentication (optional if set in environment)
dataNoObject containing data to populate the template (e.g., dynamic content, variable replacements, URLs for images)
formatNoOutput format for the generated imagepng
responseTypeNoResponse type: base64 data, download URL, or binary dataurl
templateIdYesThe ID or name of the Orshot Studio template to use
webhookNoOptional webhook URL to receive notifications when the rendering is complete

Implementation Reference

  • The main handler function that executes the tool logic: validates inputs, resolves template ID, auto-maps data, makes API request to Orshot Studio /v1/studio/render endpoint, and formats the response.
    async (args) => { const { apiKey, templateId, data, format, responseType, webhook } = args; const actualApiKey = apiKey || DEFAULT_API_KEY; if (!actualApiKey) { return { content: [ { type: "text", text: "No API key provided. Please provide an API key parameter or set ORSHOT_API_KEY environment variable.", }, ], }; } // Resolve template ID from name if needed const resolvedTemplateId = await resolveStudioTemplateId(templateId, actualApiKey); if (!resolvedTemplateId) { return { content: [ { type: "text", text: `❌ Studio template "${templateId}" not found. Please check the template ID or name.`, }, ], }; } // Auto-map modifications based on template structure const mappedData = await autoMapModifications(resolvedTemplateId, data, actualApiKey); const requestBody: any = { templateId: resolvedTemplateId, modifications: mappedData, response: { type: responseType, format: format }, source: "orshot-mcp-server" }; if (webhook) { requestBody.webhook = webhook; } const response = await makeOrShotRequest<OrShotStudioResponse>( `${ORSHOT_API_BASE}/v1/studio/render`, { method: "POST", headers: { Authorization: `Bearer ${actualApiKey}`, }, body: JSON.stringify(requestBody), } ); if (!response) { return { content: [ { type: "text", text: "Failed to generate image from studio template. Please check your API key and template ID.", }, ], }; } const { data: responseData } = response; // Create raw response display (truncate data for readability) const responseForDisplay = { ...response, data: response.data ? (response.data.length > 100 ? `${response.data.substring(0, 100)}... (truncated, total length: ${response.data.length})` : response.data) : response.data }; // Handle different response types if (responseType === "url" && response.url) { // Default case: Return clickable "View Generated Image" link for URL responses return { content: [ { type: "text", text: `Studio image generated successfully! 🖼️ **[View Generated Image](${response.url})** Task ID: ${response.task_id || 'Not available'} Status: ${response.status || 'Unknown'} ${webhook ? `Webhook notifications will be sent to: ${webhook}` : ""}`, }, ], }; } else if (responseType === "base64" && response.data && typeof response.data === 'string' && response.data.startsWith('data:image/')) { // Return the raw JSON for base64 responses (with truncated data) return { content: [ { type: "text", text: `Studio image generated successfully! **Raw API Response:** \`\`\`json ${JSON.stringify(responseForDisplay, null, 2)} \`\`\``, }, ], }; } else if (responseType === "binary") { // Return the raw JSON for binary responses return { content: [ { type: "text", text: `Studio image generated successfully! **Raw API Response:** \`\`\`json ${JSON.stringify(responseForDisplay, null, 2)} \`\`\``, }, ], }; } // Fallback to text response return { content: [ { type: "text", text: `Studio image generated successfully! **Raw API Response:** \`\`\`json ${JSON.stringify(responseForDisplay, null, 2)} \`\`\``, }, ], }; }
  • Zod schema defining input parameters for the tool, including API key, template ID/name, data modifications, format, response type, and optional webhook.
    { apiKey: z.string().optional().describe("Orshot API key for authentication (optional if set in environment)"), templateId: z.string().describe("The ID or name of the Orshot Studio template to use"), data: z.record(z.any()).default({}).describe("Object containing data to populate the template (e.g., dynamic content, variable replacements, URLs for images)"), format: z.enum(["png", "jpg", "pdf"]).default("png").describe("Output format for the generated image"), responseType: z.enum(["base64", "url", "binary"]).default("url").describe("Response type: base64 data, download URL, or binary data"), webhook: z.string().url().optional().describe("Optional webhook URL to receive notifications when the rendering is complete"), },
  • src/index.ts:643-797 (registration)
    MCP server tool registration call that defines the tool name, description, input schema, and handler function.
    server.tool( "generate-image-from-studio", "Generate an image from an Orshot Studio template using specified data modifications. Automatically maps URLs to appropriate image fields in the template. You can use either the template ID (numeric) or template name.", { apiKey: z.string().optional().describe("Orshot API key for authentication (optional if set in environment)"), templateId: z.string().describe("The ID or name of the Orshot Studio template to use"), data: z.record(z.any()).default({}).describe("Object containing data to populate the template (e.g., dynamic content, variable replacements, URLs for images)"), format: z.enum(["png", "jpg", "pdf"]).default("png").describe("Output format for the generated image"), responseType: z.enum(["base64", "url", "binary"]).default("url").describe("Response type: base64 data, download URL, or binary data"), webhook: z.string().url().optional().describe("Optional webhook URL to receive notifications when the rendering is complete"), }, async (args) => { const { apiKey, templateId, data, format, responseType, webhook } = args; const actualApiKey = apiKey || DEFAULT_API_KEY; if (!actualApiKey) { return { content: [ { type: "text", text: "No API key provided. Please provide an API key parameter or set ORSHOT_API_KEY environment variable.", }, ], }; } // Resolve template ID from name if needed const resolvedTemplateId = await resolveStudioTemplateId(templateId, actualApiKey); if (!resolvedTemplateId) { return { content: [ { type: "text", text: `❌ Studio template "${templateId}" not found. Please check the template ID or name.`, }, ], }; } // Auto-map modifications based on template structure const mappedData = await autoMapModifications(resolvedTemplateId, data, actualApiKey); const requestBody: any = { templateId: resolvedTemplateId, modifications: mappedData, response: { type: responseType, format: format }, source: "orshot-mcp-server" }; if (webhook) { requestBody.webhook = webhook; } const response = await makeOrShotRequest<OrShotStudioResponse>( `${ORSHOT_API_BASE}/v1/studio/render`, { method: "POST", headers: { Authorization: `Bearer ${actualApiKey}`, }, body: JSON.stringify(requestBody), } ); if (!response) { return { content: [ { type: "text", text: "Failed to generate image from studio template. Please check your API key and template ID.", }, ], }; } const { data: responseData } = response; // Create raw response display (truncate data for readability) const responseForDisplay = { ...response, data: response.data ? (response.data.length > 100 ? `${response.data.substring(0, 100)}... (truncated, total length: ${response.data.length})` : response.data) : response.data }; // Handle different response types if (responseType === "url" && response.url) { // Default case: Return clickable "View Generated Image" link for URL responses return { content: [ { type: "text", text: `Studio image generated successfully! 🖼️ **[View Generated Image](${response.url})** Task ID: ${response.task_id || 'Not available'} Status: ${response.status || 'Unknown'} ${webhook ? `Webhook notifications will be sent to: ${webhook}` : ""}`, }, ], }; } else if (responseType === "base64" && response.data && typeof response.data === 'string' && response.data.startsWith('data:image/')) { // Return the raw JSON for base64 responses (with truncated data) return { content: [ { type: "text", text: `Studio image generated successfully! **Raw API Response:** \`\`\`json ${JSON.stringify(responseForDisplay, null, 2)} \`\`\``, }, ], }; } else if (responseType === "binary") { // Return the raw JSON for binary responses return { content: [ { type: "text", text: `Studio image generated successfully! **Raw API Response:** \`\`\`json ${JSON.stringify(responseForDisplay, null, 2)} \`\`\``, }, ], }; } // Fallback to text response return { content: [ { type: "text", text: `Studio image generated successfully! **Raw API Response:** \`\`\`json ${JSON.stringify(responseForDisplay, null, 2)} \`\`\``, }, ], }; } );
  • Helper function to resolve studio template ID from name by querying the API.
    async function resolveStudioTemplateId(templateIdOrName: string, apiKey: string): Promise<string | null> { try { // If it's already a numeric ID, return as-is if (isLikelyStudioTemplate(templateIdOrName)) { return templateIdOrName; } // Fetch studio templates to find by name const studioResponse = await fetch(`${ORSHOT_API_BASE}/v1/studio/templates`, { headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json", }, }); if (studioResponse.ok) { const studioTemplates = await studioResponse.json(); const templatesArray = Array.isArray(studioTemplates) ? studioTemplates : []; // Find template by name (case-insensitive) or exact ID match const matchedTemplate = templatesArray.find((template: any) => template.name?.toLowerCase() === templateIdOrName.toLowerCase() || template.id === templateIdOrName ); if (matchedTemplate) { logger.debug(`Resolved template name "${templateIdOrName}" to ID: ${matchedTemplate.id}`); return String(matchedTemplate.id); } } return null; // Template not found } catch (error) { logger.error("Error resolving studio template ID", { templateIdOrName, error: error instanceof Error ? error.message : String(error) }); return null; } }
  • Helper function that automatically maps input modifications (especially URLs to image fields) based on template structure.
    async function autoMapModifications(templateId: string, inputModifications: Record<string, any>, apiKey: string): Promise<Record<string, any>> { if (!config.features.autoMapping) { logger.debug("Auto-mapping is disabled, returning input as-is"); return inputModifications; } try { logger.debug(`Starting auto-mapping for template ${templateId}`); // Get template modifications to understand the expected fields const response = await fetch(`${ORSHOT_API_BASE}/v1/studio/template/modifications?templateId=${templateId}`, { method: "GET", headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json", }, }); if (!response.ok) { logger.warn("Failed to fetch template modifications for auto-mapping", { templateId, status: response.status }); return inputModifications; } const modifications = await response.json(); const modArray = Array.isArray(modifications) ? modifications : []; if (modArray.length === 0) { logger.warn("No modifications found for template, using input as-is", { templateId }); return inputModifications; } logger.debug(`Found ${modArray.length} template modifications`, { templateId, modifications: modArray.map((m: any) => ({ key: m.key || m.id, description: m.description })) }); // Create a mapping object const mappedModifications = { ...inputModifications }; // Helper function to check if a string is a URL const isUrl = (str: string) => { try { const url = new URL(str); return url.protocol === 'https:' || url.protocol === 'http:'; } catch { return false; } }; // Look for URL patterns in input and map to appropriate fields for (const [key, value] of Object.entries(inputModifications)) { if (typeof value === 'string' && isUrl(value)) { // This is a URL - find the best matching modification field const urlMod = modArray.find((mod: any) => { const modKey = (mod.key || mod.id || '').toLowerCase(); const modDesc = (mod.description || '').toLowerCase(); return ( modKey.includes('image') || modKey.includes('url') || modKey.includes('photo') || modKey.includes('picture') || modKey.includes('media') || modKey.includes('src') || modDesc.includes('image') || modDesc.includes('url') || modDesc.includes('photo') || modDesc.includes('picture') || modDesc.includes('media') ); }); if (urlMod) { const urlKey = urlMod.key || urlMod.id; if (urlKey) { // Remove the original key if it was a generic name and add the proper field if (key !== urlKey) { delete mappedModifications[key]; } mappedModifications[urlKey] = value; logger.debug(`Auto-mapped URL to field`, { url: value, field: urlKey, templateId }); } } } } // Special handling: if we have a single URL value but no clear field mapping, // and there's only one image-like field, use that const urlValues = Object.values(inputModifications).filter(v => typeof v === 'string' && isUrl(v)); const imageFields = modArray.filter((mod: any) => { const modKey = (mod.key || mod.id || '').toLowerCase(); const modDesc = (mod.description || '').toLowerCase(); return modKey.includes('image') || modKey.includes('photo') || modKey.includes('picture') || modKey.includes('media') || modKey.includes('url') || modKey.includes('src') || modDesc.includes('image') || modDesc.includes('photo') || modDesc.includes('picture'); }); if (urlValues.length === 1 && imageFields.length === 1) { const urlValue = urlValues[0]; const imageField = imageFields[0]; const fieldKey = imageField.key || imageField.id; if (fieldKey && !mappedModifications[fieldKey]) { mappedModifications[fieldKey] = urlValue; logger.debug(`Auto-mapped single URL to single image field`, { url: urlValue, field: fieldKey, templateId }); } } const mappedFields = Object.keys(mappedModifications).filter(key => key !== Object.keys(inputModifications).find(k => mappedModifications[key] === inputModifications[k])); if (mappedFields.length > 0) { logger.autoMapping(templateId, mappedFields); } logger.debug("Auto-mapping completed", { templateId, mappedModifications }); return mappedModifications; } catch (error) { logger.error("Error in auto-mapping modifications", { templateId, error: error instanceof Error ? error.message : String(error) }); return inputModifications; } }

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rishimohan/orshot-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server