Skip to main content
Glama
kmexnx
by kmexnx

create_invoice_pdf

Generate professional PDF invoices from structured data with customizable templates, tax rates, and line items for business documentation.

Instructions

Generate a PDF invoice file

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
invoice_dataYesComplete invoice data including company info, bill-to info, items, etc.
filenameNoOptional custom filename for the PDF (without .pdf extension)

Implementation Reference

  • The `generateInvoicePDF` function, which handles the actual conversion of invoice data into a PDF file using puppeteer.
    export async function generateInvoicePDF(
      invoiceData: InvoiceData,
      customFilename?: string
    ): Promise<string> {
      // Calculate totals
      const subtotal = invoiceData.items.reduce((sum, item) => sum + item.amount, 0);
      const taxAmount = subtotal * (invoiceData.taxRate || 0) / 100;
      const total = subtotal + taxAmount + (invoiceData.other || 0);
    
      // Generate HTML
      const html = generateInvoiceHTML({
        ...invoiceData,
        subtotal,
        taxAmount,
        total,
      });
    
      // Determine output directory and ensure it exists
      const outputDir = getOutputDirectory();
      console.log(`Using output directory: ${outputDir}`);
      
      try {
        await ensureDirectoryExists(outputDir);
      } catch (error) {
        console.error('Directory creation failed, trying alternative locations...');
        
        // Try alternative directories
        const fallbackDirs = [
          path.join(process.cwd(), 'temp'),
          path.join(__dirname, 'temp'),
          process.env.TMPDIR || process.env.TMP || '/tmp',
        ];
    
        let successDir: string | null = null;
        for (const dir of fallbackDirs) {
          try {
            await ensureDirectoryExists(dir);
            successDir = dir;
            console.log(`Successfully using fallback directory: ${dir}`);
            break;
          } catch (fallbackError) {
            console.log(`Failed to use fallback directory ${dir}:`, fallbackError);
            continue;
          }
        }
    
        if (!successDir) {
          throw new Error('Unable to create any output directory. Please check file system permissions.');
        }
      }
    
      // Generate filename
      const safeInvoiceNumber = invoiceData.invoiceNumber.replace(/[^a-zA-Z0-9]/g, '_');
      const filename = customFilename || `invoice-${safeInvoiceNumber}-${Date.now()}`;
      const pdfPath = path.join(outputDir, `${filename}.pdf`);
    
      console.log(`Generating PDF at: ${pdfPath}`);
    
      // Launch browser and generate PDF
      let browser;
      try {
        browser = await puppeteer.launch({
          headless: true,
          args: [
            '--no-sandbox',
            '--disable-setuid-sandbox',
            '--disable-dev-shm-usage',
            '--disable-gpu',
            '--disable-web-security',
            '--disable-extensions',
            '--no-first-run',
            '--disable-default-apps',
          ],
        });
    
        const page = await browser.newPage();
        await page.setContent(html, { waitUntil: 'networkidle0' });
        
        // Test if we can write to the output path
        try {
          await page.pdf({
            path: pdfPath,
            format: 'A4',
            printBackground: true,
            margin: {
              top: '20px',
              right: '20px',
              bottom: '20px',
              left: '20px',
            },
          });
    
          // Verify the file was created
          await fs.access(pdfPath);
          console.log(`PDF successfully created at: ${pdfPath}`);
          
        } catch (pdfError) {
          console.error('Failed to create PDF at specified path:', pdfError);
          
          // Try creating in temp directory as absolute fallback
          const tempPath = path.join(process.env.TMPDIR || '/tmp', `${filename}.pdf`);
          console.log(`Trying fallback path: ${tempPath}`);
          
          await page.pdf({
            path: tempPath,
            format: 'A4',
            printBackground: true,
            margin: {
              top: '20px',
              right: '20px',
              bottom: '20px',
              left: '20px',
            },
          });
          
          return tempPath;
        }
    
        return pdfPath;
        
      } catch (error) {
        console.error('Error in PDF generation:', error);
        throw new Error(`PDF generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
      } finally {
        if (browser) {
          await browser.close();
        }
      }
    }
  • src/index.ts:143-157 (registration)
    The case handler for 'create_invoice_pdf' in the `index.ts` file, where the tool is called within the MCP server's request handler.
    case 'create_invoice_pdf': {
      const invoiceData = InvoiceDataSchema.parse(args.invoice_data);
      const filename = args.filename as string | undefined;
      
      const pdfPath = await generateInvoicePDF(invoiceData, filename);
      
      return {
        content: [
          {
            type: 'text',
            text: `Invoice PDF generated successfully at: ${pdfPath}`,
          },
        ],
      };
    }
  • The `InvoiceDataSchema` definition used for validating the input arguments of the tool.
    const InvoiceDataSchema = z.object({
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full disclosure burden but only states the basic action. It fails to specify where the generated file is stored (returned as binary, saved to disk, etc.), whether it overwrites existing files, or any idempotency characteristics.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Extremely brief (5 words) and front-loaded with the action verb. No wasted words or redundancy, though the extreme brevity contributes to the lack of behavioral and contextual detail given the absence of annotations and output schemas.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Inadequate for a file-generation tool with nested parameters, no annotations, and no output schema. The description fails to hint at return values (file path vs. binary data) or explain relationships to sibling tools, leaving significant gaps in the agent's understanding.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 100% description coverage, establishing a baseline of 3. The description adds no additional parameter context beyond what the schema already provides (e.g., no details on invoice_data structure or formatting constraints).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States specific action (Generate) and resource (PDF invoice file). Clear and direct, though it does not explicitly differentiate from sibling tools like get_invoice_template or send_invoice_email.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides no guidance on when to use this tool versus alternatives (e.g., when to generate a PDF vs. sending an email or retrieving a template). No prerequisites or workflow context is mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other 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/kmexnx/invoice-mcp-server'

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