Skip to main content
Glama
bitscorp-mcp

MCP FFmpeg Video Processor

by bitscorp-mcp

extract-audio

Extract audio from video files in MP3, AAC, WAV, or OGG format using the MCP FFmpeg Video Processor. Convert video to audio by specifying input path and desired format.

Instructions

Extract audio from a video file

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
videoPathYesPath to the video file to extract audio from
formatNoAudio format to extractmp3
outputDirNoOptional directory to save the output file (defaults to a temporary directory)

Implementation Reference

  • The handler function for the 'extract-audio' tool. It resolves paths, checks file existence and permissions, requests user permission, determines audio codec based on format, executes FFmpeg command to extract audio without video (-vn -acodec), and returns success or error response.
    async ({ videoPath, format, outputDir }) => {
      try {
        // Resolve the absolute path
        const absVideoPath = path.resolve(videoPath);
    
        // Check if file exists
        try {
          await fs.access(absVideoPath);
        } catch (error) {
          return {
            isError: true,
            content: [{
              type: "text" as const,
              text: `Error: Video file not found at ${absVideoPath}`
            }]
          };
        }
    
        // Determine output directory
        let outputDirectory = outputDir ? path.resolve(outputDir) : await ensureDirectoriesExist();
    
        // Check if output directory exists and is writable
        try {
          await fs.access(outputDirectory, fs.constants.W_OK);
        } catch (error) {
          return {
            isError: true,
            content: [{
              type: "text" as const,
              text: `Error: Output directory ${outputDirectory} does not exist or is not writable`
            }]
          };
        }
    
        // Format command for permission request
        const permissionMessage = `Extract ${format} audio from video ${path.basename(absVideoPath)}`;
    
        // Ask for permission
        const permitted = await askPermission(permissionMessage);
    
        if (!permitted) {
          return {
            isError: true,
            content: [{
              type: "text" as const,
              text: "Permission denied by user"
            }]
          };
        }
    
        // Get video filename without extension
        const videoFilename = path.basename(absVideoPath, path.extname(absVideoPath));
        const outputFilename = `${videoFilename}.${format}`;
        const outputPath = path.join(outputDirectory, outputFilename);
    
        // Determine audio codec based on format
        let audioCodec;
        switch (format) {
          case 'mp3':
            audioCodec = 'libmp3lame';
            break;
          case 'aac':
            audioCodec = 'aac';
            break;
          case 'wav':
            audioCodec = 'pcm_s16le';
            break;
          case 'ogg':
            audioCodec = 'libvorbis';
            break;
          default:
            audioCodec = 'libmp3lame';
        }
    
        // Build FFmpeg command
        const command = `ffmpeg -i "${absVideoPath}" -vn -acodec ${audioCodec} "${outputPath}"`;
    
        try {
          // Execute FFmpeg command
          const { stdout, stderr } = await execAsync(command);
    
          return {
            content: [{
              type: "text" as const,
              text: `Successfully extracted audio to: ${outputPath}`
            }]
          };
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          return {
            isError: true,
            content: [{
              type: "text" as const,
              text: `Error extracting audio: ${errorMessage}`
            }]
          };
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        return {
          isError: true,
          content: [{
            type: "text" as const,
            text: `Error extracting audio: ${errorMessage}`
          }]
        };
      }
    }
  • Input schema using Zod for the 'extract-audio' tool, defining videoPath (required string), format (enum mp3|aac|wav|ogg, default mp3), outputDir (optional string).
    {
      videoPath: z.string().describe("Path to the video file to extract audio from"),
      format: z.enum(["mp3", "aac", "wav", "ogg"]).default("mp3").describe("Audio format to extract"),
      outputDir: z.string().optional().describe("Optional directory to save the output file (defaults to a temporary directory)")
    },
  • Registration of the 'extract-audio' tool on the MCP server using server.tool(), specifying name, description, input schema, and handler function.
    server.tool(
      "extract-audio",
      "Extract audio from a video file",
      {
        videoPath: z.string().describe("Path to the video file to extract audio from"),
        format: z.enum(["mp3", "aac", "wav", "ogg"]).default("mp3").describe("Audio format to extract"),
        outputDir: z.string().optional().describe("Optional directory to save the output file (defaults to a temporary directory)")
      },
      async ({ videoPath, format, outputDir }) => {
        try {
          // Resolve the absolute path
          const absVideoPath = path.resolve(videoPath);
    
          // Check if file exists
          try {
            await fs.access(absVideoPath);
          } catch (error) {
            return {
              isError: true,
              content: [{
                type: "text" as const,
                text: `Error: Video file not found at ${absVideoPath}`
              }]
            };
          }
    
          // Determine output directory
          let outputDirectory = outputDir ? path.resolve(outputDir) : await ensureDirectoriesExist();
    
          // Check if output directory exists and is writable
          try {
            await fs.access(outputDirectory, fs.constants.W_OK);
          } catch (error) {
            return {
              isError: true,
              content: [{
                type: "text" as const,
                text: `Error: Output directory ${outputDirectory} does not exist or is not writable`
              }]
            };
          }
    
          // Format command for permission request
          const permissionMessage = `Extract ${format} audio from video ${path.basename(absVideoPath)}`;
    
          // Ask for permission
          const permitted = await askPermission(permissionMessage);
    
          if (!permitted) {
            return {
              isError: true,
              content: [{
                type: "text" as const,
                text: "Permission denied by user"
              }]
            };
          }
    
          // Get video filename without extension
          const videoFilename = path.basename(absVideoPath, path.extname(absVideoPath));
          const outputFilename = `${videoFilename}.${format}`;
          const outputPath = path.join(outputDirectory, outputFilename);
    
          // Determine audio codec based on format
          let audioCodec;
          switch (format) {
            case 'mp3':
              audioCodec = 'libmp3lame';
              break;
            case 'aac':
              audioCodec = 'aac';
              break;
            case 'wav':
              audioCodec = 'pcm_s16le';
              break;
            case 'ogg':
              audioCodec = 'libvorbis';
              break;
            default:
              audioCodec = 'libmp3lame';
          }
    
          // Build FFmpeg command
          const command = `ffmpeg -i "${absVideoPath}" -vn -acodec ${audioCodec} "${outputPath}"`;
    
          try {
            // Execute FFmpeg command
            const { stdout, stderr } = await execAsync(command);
    
            return {
              content: [{
                type: "text" as const,
                text: `Successfully extracted audio to: ${outputPath}`
              }]
            };
          } catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            return {
              isError: true,
              content: [{
                type: "text" as const,
                text: `Error extracting audio: ${errorMessage}`
              }]
            };
          }
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          return {
            isError: true,
            content: [{
              type: "text" as const,
              text: `Error extracting audio: ${errorMessage}`
            }]
          };
        }
      }
    );
  • Helper function to request user permission via desktop notification before executing FFmpeg operations, used by extract-audio handler.
    async function askPermission(action: string): Promise<boolean> {
      // Skip notification if DISABLE_NOTIFICATIONS is set
      if (process.env.DISABLE_NOTIFICATIONS === 'true') {
        console.log(`Auto-allowing action (notifications disabled): ${action}`);
        return true;
      }
    
      return new Promise((resolve) => {
        notifier.notify({
          title: 'FFmpeg Processor Permission Request',
          message: `${action}`,
          wait: true,
          timeout: 60,
          actions: 'Allow',
          closeLabel: 'Deny'
        }, (err, response, metadata) => {
          if (err) {
            console.error('Error showing notification:', err);
            resolve(false);
            return;
          }
    
          const buttonPressed = metadata?.activationValue || response;
          resolve(buttonPressed !== 'Deny');
        });
      });
    }
  • Helper function to ensure output directory exists in temp folder, used by extract-audio to determine output path.
    async function ensureDirectoriesExist() {
      const outputDir = path.join(os.tmpdir(), 'ffmpeg-output');
      try {
        await fs.mkdir(outputDir, { recursive: true });
        return outputDir;
      } catch (error) {
        console.error('Error creating output directory:', error);
        return os.tmpdir();
      }
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It states what the tool does but doesn't cover important behavioral aspects like whether it modifies the original video file, what permissions are needed, error handling, or performance characteristics. The description is minimal and lacks operational context.

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?

The description is extremely concise with just one sentence that directly states the tool's purpose. There's no wasted language or unnecessary elaboration, making it front-loaded and efficient. Every word earns its place in conveying the core functionality.

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?

Given the lack of annotations and output schema, the description is insufficiently complete. It doesn't explain what the tool returns (e.g., path to extracted audio file, success/failure indicators) or important behavioral details. For a tool that performs file operations with multiple parameters, more context is needed for effective use.

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?

The description doesn't add any parameter information beyond what's already in the schema, which has 100% coverage with clear descriptions for all parameters. The baseline score of 3 reflects that the schema adequately documents parameters, so the description doesn't need to compensate but also doesn't provide additional value.

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?

The description clearly states the tool's purpose with a specific verb ('extract') and resource ('audio from a video file'), making it immediately understandable. However, it doesn't explicitly differentiate from sibling tools like 'get-video-info' or 'resize-video', which might also involve video processing but serve different functions.

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?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention sibling tools or any context-specific usage scenarios, leaving the agent to infer based on tool names alone. There's no explicit when/when-not or alternative recommendations.

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/bitscorp-mcp/mcp-ffmpeg'

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