import { Injectable } from "@nestjs/common";
import { Tool, Context } from "@rekog/mcp-nest";
import { z } from "zod";
import { GammaApiService } from "./gamma-api.service";
@Injectable()
export class GenerateTool {
constructor(private readonly gammaApi: GammaApiService) {}
@Tool({
name: "generate_gamma",
description:
"Generate a Gamma presentation, document, or webpage. This tool will create the generation and poll until completion.",
parameters: z.object({
inputText: z
.string()
.describe("Content to generate from (max 400k chars)"),
textMode: z
.enum(["generate", "condense", "preserve"])
.describe("How to handle the input text"),
format: z
.enum(["presentation", "document", "social", "webpage"])
.optional()
.describe("Format of the output"),
numCards: z
.number()
.optional()
.describe("Number of cards/pages to generate"),
themeId: z
.string()
.optional()
.describe("Theme ID to use (from themes resource)"),
folderIds: z
.array(z.string())
.optional()
.describe("Folder IDs to save the gamma in"),
exportAs: z
.enum(["pptx", "pdf"])
.optional()
.describe("Export format (generates export URL)"),
}),
})
async generateGamma(params: any, context: Context) {
try {
// Report initial progress
await context.reportProgress({
progress: 10,
total: 100,
message: "Submitting generation request...",
});
// Create generation
const generation = await this.gammaApi.createGeneration(params);
await context.reportProgress({
progress: 20,
total: 100,
message: `Generation started (ID: ${generation.generationId}). Polling for completion...`,
});
// Poll until complete
let progress = 30;
const pollWithProgress = async () => {
let attempts = 0;
while (true) {
const status = await this.gammaApi.getGenerationStatus(
generation.generationId
);
if (status.status === "completed" || status.status === "failed") {
return status;
}
// Update progress periodically (cap at 90%)
attempts++;
if (attempts % 2 === 0 && progress < 90) {
progress += 5;
await context.reportProgress({
progress,
total: 100,
message: `Still generating... (${status.status})`,
});
}
// Wait 20 seconds before next poll
await new Promise((resolve) => setTimeout(resolve, 20000));
}
};
const finalStatus = await pollWithProgress();
await context.reportProgress({
progress: 100,
total: 100,
message:
finalStatus.status === "completed"
? "Generation complete!"
: "Generation failed",
});
// Return result
if (finalStatus.status === "completed") {
return {
success: true,
generationId: finalStatus.generationId,
gammaUrl: finalStatus.gammaUrl,
exportUrl: finalStatus.exportUrl,
credits: finalStatus.credits,
warnings: generation.warnings,
};
} else {
return {
success: false,
generationId: finalStatus.generationId,
error: finalStatus.error,
};
}
} catch (error: any) {
return {
success: false,
error: {
message: error.message || "Unknown error occurred",
details: error.response?.data || error.toString(),
},
};
}
}
}