Skip to main content
Glama
Noosbai
by Noosbai

Estimer le coût d'impression

estimate_cost

Calculate 3D printing costs for filament, electricity, and time. Compare draft, standard, and quality profiles side by side.

Instructions

Estime le coût d'impression d'un modèle 3D : filament, électricité, temps. Peut aussi comparer plusieurs profils (draft/standard/quality) côte à côte.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathYesChemin absolu vers le fichier STL ou 3MF
materialNoMatériau (PLA, PETG, ABS...)PLA
nozzleNoDiamètre de buse en mm
filament_price_per_kgNoPrix du filament en EUR/kg
electricity_priceNoPrix électricité en EUR/kWh
compare_profilesNoComparer draft/standard/quality côte à côte

Implementation Reference

  • Main tool registration and handler for estimate_cost. Defines the tool schema (file_path, material, nozzle, filament_price_per_kg, electricity_price, compare_profiles) and contains the complete handler logic that parses mesh files, analyzes them, and calls estimateCostFromMesh to compute costs. Supports both single profile and multi-profile comparison modes.
    export function registerEstimateCost(server: McpServer) {
      server.registerTool(
        "estimate_cost",
        {
          title: "Estimer le coût d'impression",
          description:
            "Estime le coût d'impression d'un modèle 3D : filament, électricité, temps. " +
            "Peut aussi comparer plusieurs profils (draft/standard/quality) côte à côte.",
          inputSchema: {
            file_path: z.string().describe("Chemin absolu vers le fichier STL ou 3MF"),
            material: z.string().default("PLA").describe("Matériau (PLA, PETG, ABS...)"),
            nozzle: z.number().default(0.4).describe("Diamètre de buse en mm"),
            filament_price_per_kg: z.number().default(22).describe("Prix du filament en EUR/kg"),
            electricity_price: z.number().default(0.25).describe("Prix électricité en EUR/kWh"),
            compare_profiles: z
              .boolean()
              .default(true)
              .describe("Comparer draft/standard/quality côte à côte"),
          },
        },
        async ({ file_path, material, nozzle, filament_price_per_kg, electricity_price, compare_profiles }) => {
          try {
            if (!existsSync(file_path)) {
              return {
                isError: true,
                content: [{ type: "text" as const, text: `Fichier non trouvé : ${file_path}` }],
              };
            }
    
            const mesh = await parseModel(file_path);
            const analysis = analyzeMesh(mesh);
    
            const params = {
              filamentPricePerKg: filament_price_per_kg,
              electricityPricePerKwh: electricity_price,
              printerWattage: 200,
            };
    
            const lines: string[] = [];
    
            if (compare_profiles) {
              // Compare draft / standard / quality
              const goals = ["draft", "standard", "quality", "strong"] as const;
              const comparisons: Array<{
                name: string;
                time: string;
                cost: string;
                filament: string;
                layerH: number;
              }> = [];
    
              lines.push("## Comparaison des profils");
              lines.push(`**Modèle** : ${mesh.name} | **Matériau** : ${material} | **Buse** : ${nozzle}mm`);
              lines.push(`**Prix filament** : ${filament_price_per_kg}€/kg | **Électricité** : ${electricity_price}€/kWh`);
              lines.push("");
    
              for (const goal of goals) {
                const profile = recommendProfile("Generic", nozzle, goal, material);
                const lh = profile.settings.layer_height.value as number;
                const infill = profile.settings.infill_density.value as number;
                const perimeters = profile.settings.perimeters.value as number;
                const speed = profile.settings.print_speed.value as number;
    
                const estimate = estimateCostFromMesh(
                  analysis, lh, infill, perimeters, speed, material, params,
                );
    
                comparisons.push({
                  name: goal,
                  time: estimate.printTimeFormatted,
                  cost: `${estimate.totalCostEur}€`,
                  filament: `${estimate.filamentWeightG}g`,
                  layerH: lh,
                });
              }
    
              // Table
              lines.push("| Profil | Layer | Temps | Filament | Coût total |");
              lines.push("|--------|-------|-------|----------|------------|");
              for (const c of comparisons) {
                lines.push(`| **${c.name}** | ${c.layerH}mm | ${c.time} | ${c.filament} | ${c.cost} |`);
              }
    
              // Savings
              if (comparisons.length >= 2) {
                const fastest = comparisons[0];
                const slowest = comparisons[comparisons.length - 1];
                lines.push("");
                lines.push(`**Économie draft vs strong** : ${slowest.cost} → ${fastest.cost} en passant de ${slowest.time} à ${fastest.time}`);
              }
            } else {
              // Single profile estimate
              const profile = recommendProfile("Generic", nozzle, "standard", material);
              const lh = profile.settings.layer_height.value as number;
              const infill = profile.settings.infill_density.value as number;
              const perimeters = profile.settings.perimeters.value as number;
              const speed = profile.settings.print_speed.value as number;
    
              const estimate = estimateCostFromMesh(
                analysis, lh, infill, perimeters, speed, material, params,
              );
    
              lines.push("## Estimation de coût");
              lines.push(`**Temps** : ${estimate.printTimeFormatted}`);
              lines.push(`**Filament** : ${estimate.filamentWeightG}g (${estimate.filamentLengthMm.toFixed(0)}mm)`);
              lines.push(`**Coût filament** : ${estimate.filamentCostEur}€`);
              lines.push(`**Coût électricité** : ${estimate.electricityCostEur}€`);
              lines.push(`**Coût total** : ${estimate.totalCostEur}€`);
            }
    
            return {
              content: [{ type: "text" as const, text: lines.join("\n") }],
            };
          } catch (error) {
            return {
              isError: true,
              content: [{
                type: "text" as const,
                text: `Erreur : ${error instanceof Error ? error.message : String(error)}`,
              }],
            };
          }
        },
      );
    }
  • Core implementation of estimateCostFromMesh function that performs the actual cost calculation. Takes mesh analysis data, printing parameters (layer height, infill, perimeters, speed), material type, and pricing parameters. Calculates filament volume (shell, infill, top/bottom solid layers), weight, length, print time, and costs (filament + electricity).
    export function estimateCostFromMesh(
      analysis: MeshAnalysis,
      layerHeight: number,
      infillPercent: number,
      perimeters: number,
      printSpeed: number,
      material: string = "PLA",
      params: Partial<CostParams> = {},
      filamentDiameter: number = 1.75,
    ): CostEstimate {
      const density = params.filamentDensity ?? MATERIAL_DENSITY[material.toUpperCase()] ?? 1.24;
      const pricePerKg = params.filamentPricePerKg ?? DEFAULT_FILAMENT_PRICE;
      const electricityPrice = params.electricityPricePerKwh ?? 0.25;
      const wattage = params.printerWattage ?? 200;
    
      // Estimate filament volume:
      // Shell volume = surface area × perimeters × line width (≈ nozzle diameter)
      const lineWidth = 0.45; // approximate
      const shellVolume = analysis.surfaceArea * perimeters * lineWidth; // mm³
    
      // Infill volume = (total volume - shell volume) × infill%
      const innerVolume = Math.max(0, analysis.volume - shellVolume);
      const infillVolume = innerVolume * (infillPercent / 100);
    
      // Top/bottom solid layers (assume 4 each at layer_height)
      const topBottomArea = analysis.boundingBox.size.x * analysis.boundingBox.size.y;
      const solidVolume = topBottomArea * layerHeight * 8; // 4 top + 4 bottom
    
      const totalFilamentVolume = shellVolume + infillVolume + solidVolume; // mm³
      const weightG = (totalFilamentVolume / 1000) * density; // mm³ to cm³ then × g/cm³
    
      // Filament length (1.75mm or 2.85mm diameter)
      const filamentCrossSectionMm2 = Math.PI * (filamentDiameter / 2) ** 2;
      const lengthMm = totalFilamentVolume / filamentCrossSectionMm2;
    
      // Time estimate: total volume / (line width × layer height × speed)
      const extrusionRate = lineWidth * layerHeight * printSpeed; // mm³/s
      const printTimeSeconds = extrusionRate > 0
        ? (totalFilamentVolume / extrusionRate) * 1.3  // 1.3× factor for travel moves
        : 0;
    
      const filamentCost = (weightG / 1000) * pricePerKg;
      const printTimeHours = printTimeSeconds / 3600;
      const electricityCost = (wattage / 1000) * printTimeHours * electricityPrice;
    
      return {
        filamentWeightG: round2(weightG),
        filamentLengthMm: round2(lengthMm),
        filamentCostEur: round2(filamentCost),
        electricityCostEur: round2(electricityCost),
        totalCostEur: round2(filamentCost + electricityCost),
        printTimeSeconds: Math.round(printTimeSeconds),
        printTimeFormatted: formatTime(printTimeSeconds),
      };
    }
  • Type definitions for cost estimation. Defines CostEstimate interface (output structure with filamentWeightG, filamentLengthMm, filamentCostEur, electricityCostEur, totalCostEur, printTimeSeconds, printTimeFormatted) and CostParams interface (input parameters including filamentPricePerKg, filamentDensity, electricityPricePerKwh, printerWattage).
    export interface CostEstimate {
      filamentWeightG: number;
      filamentLengthMm: number;
      filamentCostEur: number;
      electricityCostEur: number;
      totalCostEur: number;
      printTimeSeconds: number;
      printTimeFormatted: string;
    }
    
    export interface CostParams {
      filamentPricePerKg: number;   // EUR/kg
      filamentDensity: number;       // g/cm³ (PLA ≈ 1.24, PETG ≈ 1.27, ABS ≈ 1.04)
      electricityPricePerKwh: number; // EUR/kWh
      printerWattage: number;        // watts during printing
    }
  • src/index.ts:16-47 (registration)
    Tool registration in the main server file. Imports registerEstimateCost from tools/estimate-cost.js and calls registerEstimateCost(server) at line 47 to register the estimate_cost tool with the MCP server.
    import { registerEstimateCost } from "./tools/estimate-cost.js";
    import { registerPostprocessGcode } from "./tools/postprocess-gcode.js";
    import { registerUploadPrint } from "./tools/upload-print.js";
    import { registerSearchFilament } from "./tools/search-filament.js";
    import { registerPrintWizard } from "./tools/print-wizard.js";
    import { registerSubmitFeedback, registerFeedbackStats, registerExportFeedback } from "./tools/feedback.js";
    import { registerDiagnosePrint } from "./tools/diagnose-print.js";
    
    async function main() {
      console.error("PrusaMCP v2.1.0 — MCP Server intelligent pour PrusaSlicer");
    
      const config = loadConfig();
    
      if (config.executablePath) {
        console.error(`PrusaSlicer : ${config.executablePath}`);
      }
      if (config.profilesDir) {
        console.error(`Profils : ${config.profilesDir}`);
      }
    
      const server = new McpServer({
        name: "prusa-mcp",
        version: "2.1.0",
      });
    
      // Analyse & recommandation (sans PrusaSlicer)
      registerAnalyzeMesh(server);
      registerCheckPrintability(server);
      registerSuggestOrientation(server);
      registerRecommendProfile(server);
      registerGenerateConfig(server);
      registerEstimateCost(server);
  • Helper utility functions for cost estimation. Includes round2() for rounding values to 2 decimal places and formatTime() for converting seconds to human-readable time format (e.g., '2h 30min', '45min 20s').
    function round2(v: number): number {
      return Math.round(v * 100) / 100;
    }
    
    function formatTime(seconds: number): string {
      if (seconds <= 0) return "N/A";
      const h = Math.floor(seconds / 3600);
      const m = Math.floor((seconds % 3600) / 60);
      const s = Math.round(seconds % 60);
      if (h > 0) return `${h}h ${m}min`;
      if (m > 0) return `${m}min ${s}s`;
      return `${s}s`;
    }
Behavior2/5

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

With no annotations provided, the description carries full burden for behavioral disclosure. While it mentions the tool can compare profiles, it doesn't describe what the output looks like, whether it's a read-only operation, if it modifies files, or any rate limits/authentication requirements. For a tool with 6 parameters and no annotation coverage, this is insufficient.

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

Conciseness5/5

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

Two concise sentences that efficiently convey the core functionality. First sentence establishes primary purpose, second adds the comparison capability. No wasted words or redundant information.

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?

For a tool with 6 parameters, no annotations, and no output schema, the description is incomplete. It doesn't explain what the estimation returns (cost breakdown? total? comparison table?), nor does it address behavioral aspects like whether it's a read operation or has side effects. The description should do more given the complexity and lack of structured metadata.

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 description coverage is 100%, so the schema already documents all parameters thoroughly. The description mentions 'filament, electricity, time' which aligns with some parameters, but adds no meaningful semantic context beyond what's in the schema descriptions. Baseline 3 is appropriate when schema does the heavy lifting.

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

Purpose5/5

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

The description clearly states the tool's purpose with specific verbs ('estimate', 'compare') and resources ('3D model printing cost', 'multiple profiles'). It distinguishes from siblings by focusing on cost estimation rather than mesh analysis, printability checks, or slicing operations mentioned in the sibling list.

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

Usage Guidelines3/5

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

The description implies usage for cost estimation and profile comparison, but doesn't explicitly state when to use this tool versus alternatives like 'analyze_mesh' or 'check_printability'. No guidance on prerequisites or exclusions is provided, leaving usage context somewhat ambiguous.

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/Noosbai/PrusaMCP'

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