@nutrient-sdk/dws-mcp-server
Version:
MCP server for Nutrient DWS Processor API
130 lines (129 loc) • 4.91 kB
JavaScript
import FormData from 'form-data';
import fs from 'fs';
import path from 'path';
import { handleApiError, handleFileResponse, handleJsonContentResponse } from './utils.js';
import { createErrorResponse } from '../responses.js';
import { resolveReadFilePath, resolveWriteFilePath } from '../fs/sandbox.js';
import { callNutrientApi } from './api.js';
/**
* Performs a build call to the Nutrient DWS Processor API
*/
export async function performBuildCall(instructions, outputFilePath) {
const { instructions: adjustedInstructions, fileReferences } = await processInstructions(instructions);
if (fileReferences.size === 0) {
return createErrorResponse('Error: No valid files or urls found in instructions');
}
try {
// We resolve the output path first to fail early
const resolvedOutputPath = await resolveWriteFilePath(outputFilePath);
const response = await makeApiBuildCall(adjustedInstructions, fileReferences);
if (adjustedInstructions.output?.type === 'json-content') {
return handleJsonContentResponse(response);
}
else {
return handleFileResponse(response, resolvedOutputPath, 'File processed successfully using build API');
}
}
catch (e) {
return handleApiError(e);
}
}
/**
* Process file references in instructions
*/
async function processInstructions(instructions) {
const adjustedInstructions = { ...instructions };
const fileReferences = new Map();
if (adjustedInstructions.parts) {
for (const part of adjustedInstructions.parts) {
if (part.file) {
const fileReference = await processFileReference(part.file);
part.file = fileReference.key;
fileReferences.set(fileReference.key, fileReference);
}
// Actions have been disabled in parts to reduce tool complexity. We can re-enable them if needed.
// if (part.actions) {
// for (const action of part.actions) {
// const fileReference = await processActionFileReferences(action)
// if (fileReference) {
// fileReferences.set(fileReference.key, fileReference)
// }
// }
// }
}
}
if (adjustedInstructions.actions) {
for (const action of adjustedInstructions.actions) {
const fileReference = await processActionFileReferences(action);
if (fileReference) {
fileReferences.set(fileReference.key, fileReference);
}
}
}
return { instructions: adjustedInstructions, fileReferences };
}
/**
* Process file references in actions
*/
async function processActionFileReferences(action) {
if (action.type === 'watermark' && 'image' in action && action.image) {
const fileReference = await processFileReference(action.image);
action.image = fileReference.key;
return fileReference;
}
else if (action.type === 'applyXfdf' && 'file' in action && typeof action.file === 'string') {
const fileReference = await processFileReference(action.file);
action.file = fileReference.key;
return fileReference;
}
// No need to parse files for the other actions
return undefined;
}
/**
* Process a single file reference
*/
async function processFileReference(reference) {
if (reference.startsWith('http://') || reference.startsWith('https://')) {
return {
key: reference,
url: reference,
name: reference,
};
}
try {
const resolvedPath = await resolveReadFilePath(reference);
const fileBuffer = await fs.promises.readFile(resolvedPath);
const fileName = path.basename(resolvedPath);
const fileKey = fileName.replace(/[^a-zA-Z0-9]/g, '_');
return {
key: fileKey,
name: fileName,
file: {
buffer: fileBuffer,
path: resolvedPath,
},
};
}
catch (error) {
throw new Error(`Error with referenced file ${reference}: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Make the API call to the build endpoint
*/
async function makeApiBuildCall(instructions, fileReferences) {
const allInputsAreUrls = Array.from(fileReferences.values()).every((fileRef) => fileRef.url);
if (allInputsAreUrls) {
return callNutrientApi('build', instructions);
}
else {
const formData = new FormData();
formData.append('instructions', JSON.stringify(instructions));
for (const [key, { file, name }] of fileReferences.entries()) {
if (file) {
formData.append(key, file.buffer, { filename: name });
}
}
return callNutrientApi('build', formData);
}
}