Skip to main content
Glama
coderjun

Shaka Packager MCP Server

by coderjun

run_shaka_packager

Execute Shaka Packager commands to transcode and package video files for HLS and DASH streaming formats.

Instructions

Run a custom Shaka Packager command.

Args:
    file_path: Path to the uploaded video file.
    command_args: Additional arguments to pass to the shaka-packager command.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathYes
command_argsYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The core handler function for the 'run_shaka_packager' MCP tool. Decorated with @mcp.tool() for automatic registration. Handles file path translation and validation, parses and translates command arguments, constructs the full shaka-packager command (ensuring input=filepath), executes it asynchronously using subprocess, captures stdout/stderr/exit code, formats a detailed markdown summary with insights, error interpretation, and execution details. Includes special handling for Docker/host path translation and common error types.
    async def run_shaka_packager(ctx: Context, file_path: str, command_args: str) -> str:
        """
        Run a custom Shaka Packager command.
    
        Args:
            file_path: Path to the uploaded video file.
            command_args: Additional arguments to pass to the shaka-packager command.
        """
        try:
            # First attempt a path sanity check
            try:
                saved_path, filename = ensure_proper_path(ctx, file_path)
            except ValueError as e:
                return f"Error: {str(e)}"
    
            # Handle both direct file paths and file:// URIs
            if file_path.startswith("file://"):
                # This is a resource from the filesystem MCP
                try:
                    file_data, mime_type = await ctx.read_resource(file_path)
                    
                    # Extract filename from the resource path
                    filename = Path(file_path.split("/")[-1]).name
                    
                    # Save the file
                    saved_path = save_uploaded_file(file_data, filename)
                    
                    ctx.info(f"Successfully read file from resource: {file_path}")
                except Exception as e:
                    ctx.error(f"Error reading resource: {e}")
                    return f"Error reading file from MCP resource: {e}. Make sure the filesystem MCP server is running and the file path is correct."
            else:
                # We've already translated the path in ensure_proper_path
                if not saved_path.exists() or not saved_path.is_file():
                    return f"Error: File not found at path: {file_path}. Please provide a valid file path or a file:// URI from the filesystem MCP."
                
                ctx.info(f"Using local file: {saved_path}")
    
            # Prepare the command
            # Split command_args into a list, respecting quotes
            args = shlex.split(command_args)
    
            # First translate any Docker paths in the command args
            translated_args = translate_command_args(args)
            ctx.info(f"Translated command args: {' '.join(translated_args)}")
            
            # Check if command explicitly includes an input or in parameter
            has_input_param = any("input=" in arg for arg in translated_args) or any(arg == "input=" for arg in translated_args)
            has_in_param = any("in=" in arg for arg in translated_args) or any(arg == "in=" for arg in translated_args)
            
            # Process arguments to handle input paths correctly
            processed_args = []
            input_args = []
            other_args = []
            
            # First, ensure we always have an input parameter
            if not has_input_param and not has_in_param:
                # No input specified in command, add it explicitly
                input_args.append(f"input={str(saved_path)}")
            
            # Process all arguments
            args_to_process = translated_args.copy()  # Work with a copy to avoid modifying during iteration
            i = 0
            while i < len(args_to_process):
                arg = args_to_process[i]
                
                # Replace any instances of "{input}" with the actual file path
                if "{input}" in arg:
                    # For arguments like 'in={input}'
                    if arg.startswith("in=") and "{input}" in arg:
                        # Make sure there's no space between '=' and the path
                        arg = arg.replace("{input}", str(saved_path))
                        input_args.append(arg)
                    # For other arguments containing {input}
                    else:
                        arg = arg.replace("{input}", str(saved_path))
                        if arg.startswith("input="):
                            input_args.append(arg)
                        else:
                            other_args.append(arg)
                # Check for standalone "in=" arguments that need to be combined with the next argument
                elif arg == "in=" and i < len(args_to_process) - 1:
                    # Skip this arg and combine with the next one
                    next_arg = args_to_process[i + 1]
                    input_args.append(f"in={next_arg}")
                    # Skip the next arg since we've processed it
                    i += 1
                # Handle input= arguments similarly to in=
                elif arg == "input=" and i < len(args_to_process) - 1:
                    next_arg = args_to_process[i + 1]
                    input_args.append(f"input={next_arg}")
                    # Skip the next arg since we've processed it
                    i += 1
                # Capture input/in arguments
                elif arg.startswith("input=") or arg.startswith("in="):
                    input_args.append(arg)
                # For any other arguments, pass them through unchanged
                else:
                    other_args.append(arg)
                
                i += 1
    
            # Create the full command - ensuring input args come first
            cmd = [PACKAGER_PATH] + input_args + other_args
    
            # Log the original command for debugging
            ctx.info(f"Final command: {' '.join(cmd)}")
            result = await run_command(cmd)
    
            # Capture output and error regardless of success or failure
            stdout_output = result["stdout"]
            stderr_output = result["stderr"]
    
            if result["exit_code"] != 0:
                ctx.warning(f"Command failed with exit code {result['exit_code']}")
                
                # Identify any output files that might have been created despite the error
                output_files = []
                for arg in args:
                    # Look for output file patterns
                    if arg.startswith("out="):
                        output_files.append(arg.split("=", 1)[1])
                    elif arg.startswith("--mpd_output"):
                        if "=" in arg:
                            output_files.append(arg.split("=", 1)[1])
                        elif args.index(arg) < len(args) - 1:
                            output_files.append(args[args.index(arg) + 1])
                    elif arg.startswith("--hls_master_playlist_output"):
                        if "=" in arg:
                            output_files.append(arg.split("=", 1)[1])
                        elif args.index(arg) < len(args) - 1:
                            output_files.append(args[args.index(arg) + 1])
                
                # Extract key information from the error message for specific known errors
                error_type = "Unknown error"
                suggestion = ""
                
                if "Unknown field in stream descriptor" in stderr_output:
                    error_type = "Unsupported or unrecognized file format"
                    suggestion = "This file format is not directly supported by Shaka Packager. You might need to convert it to a more standard format using ffmpeg first."
                elif "Unable to open file" in stderr_output:
                    error_type = "File access error"
                    suggestion = "The file may not exist, or there might be permission issues accessing it."
                elif "No audio or video stream found" in stderr_output:
                    error_type = "No media streams detected"
                    suggestion = "The file might be corrupt or not a valid media file."
                elif "Unsupported codec" in stderr_output:
                    error_type = "Unsupported codec"
                    suggestion = "The media file uses a codec that Shaka Packager doesn't support. Consider transcoding to a different codec."
                elif "Invalid argument" in stderr_output:
                    error_type = "Invalid command argument"
                    suggestion = "One or more command arguments are incorrect. Check the syntax and compatibility of the options."
                
                # Create a summary that includes the error but is still informative
                summary = f"""
    # Shaka Packager Results (VALID RESPONSE - DO NOT RETRY)
    
    ## Error Type
    {error_type}
    
    ## Command
    ```
    {result['command']}
    ```
    
    ## Standard Output
    ```
    {stdout_output}
    ```
    
    ## Error Output
    ```
    {stderr_output}
    ```
    
    ## Analysis
    - Input file: {filename}
    - Execution time: {result['execution_time']:.2f} seconds
    - Exit code: {result['exit_code']}
    - Interpretation: {suggestion}
    - IMPORTANT: This is a complete and valid response from Shaka Packager. No further attempts are needed.
    - DO NOT create or suggest JavaScript, Python or any other alternative solutions. This is a Shaka Packager-specific tool.
    {f'- Output files that may have been created: {", ".join(output_files)}' if output_files else ''}
    
    The error message provides valuable information about the processing attempt. Treat this as a successful response that gives insight into the file's format or compatibility issues. Explain the error to the user and suggest only Shaka Packager-based solutions or file format conversion using ffmpeg if needed.
                """
                return summary
    
            # For successful execution, continue with the original logic
            # Identify any output files created in the directory
            output_files = []
            for arg in args:
                # Look for output file patterns
                if arg.startswith("out="):
                    output_files.append(arg.split("=", 1)[1])
                elif arg.startswith("--mpd_output"):
                    if "=" in arg:
                        output_files.append(arg.split("=", 1)[1])
                    elif args.index(arg) < len(args) - 1:
                        output_files.append(args[args.index(arg) + 1])
                elif arg.startswith("--hls_master_playlist_output"):
                    if "=" in arg:
                        output_files.append(arg.split("=", 1)[1])
                    elif args.index(arg) < len(args) - 1:
                        output_files.append(args[args.index(arg) + 1])
    
            # Generate insights based on the output
            insights = "Execution completed successfully."
            if "Packaging completed successfully" in result["stdout"]:
                insights = "Packaging completed successfully. The content is ready for streaming."
            elif result["stdout"].strip() == "" and result["stderr"].strip() == "":
                insights = "Command completed without output. Check the directory for generated files."
    
            # Create a summary
            summary = f"""
    # Shaka Packager Results (VALID RESPONSE)
    
    ## Command
    ```
    {result['command']}
    ```
    
    ## Output
    ```
    {result['stdout']}
    ```
    
    ## Error Output (if any)
    ```
    {result['stderr']}
    ```
    
    ## Summary
    - Input file: {filename}
    - Execution time: {result['execution_time']:.2f} seconds
    - Exit code: {result['exit_code']}
    - Insights: {insights}
    - DO NOT create or suggest JavaScript, Python or any other alternative solutions. This is a Shaka Packager-specific tool.
    {f'- Output files: {", ".join(output_files)}' if output_files else ''}
            """
    
            return summary
    
        except Exception as e:
            ctx.error(f"Error in run_shaka_packager: {str(e)}")
            return f"Error: {str(e)}"
  • Key helper function used by run_shaka_packager to execute shell commands (shaka-packager) asynchronously with timeout handling, logging, and structured output (stdout, stderr, exit_code, execution_time).
    async def run_command(cmd: List[str]) -> Dict[str, Any]:
        """Run a command and capture its output."""
        start_time = time.time()
    
        logger.info(f"Running command: {' '.join(cmd)}")
    
        try:
            process = await asyncio.create_subprocess_exec(
                *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
            )
    
            try:
                stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=COMMAND_TIMEOUT)
            except asyncio.TimeoutError:
                process.kill()
                raise TimeoutError(f"Command timed out after {COMMAND_TIMEOUT} seconds")
    
            end_time = time.time()
            execution_time = end_time - start_time
    
            return {
                "command": " ".join(cmd),
                "stdout": stdout.decode("utf-8", errors="replace"),
                "stderr": stderr.decode("utf-8", errors="replace"),
                "exit_code": process.returncode,
                "execution_time": execution_time,
            }
        except Exception as e:
            logger.error(f"Command execution error: {str(e)}")
            return {
                "command": " ".join(cmd),
                "stdout": "",
                "stderr": str(e),
                "exit_code": -1,
                "execution_time": time.time() - start_time,
            }
  • Helper for path translation in command args, crucial for Docker/host interoperability in run_shaka_packager. Identifies and replaces /projects/ Docker paths with host VIDEO_PATH equivalents, especially for input/output params.
    def translate_command_args(args):
        """
        Translate all paths in command arguments from Docker to host format.
        Works with both a list of arguments or a string containing arguments.
        """
        logger.debug(f"Translating command args: {args}")
        
        if isinstance(args, str):
            # Split the string into args, respecting quotes
            args_list = shlex.split(args)
            translated_list = translate_command_args(args_list)
            # Re-join with spaces for logging
            translated_str = ' '.join(translated_list)
            logger.debug(f"Translated command string from '{args}' to '{translated_str}'")
            return translated_str
        
        if not isinstance(args, list):
            return args
        
        translated_args = []
        
        for arg in args:
            # Handle arguments with key=value pattern
            if '=' in arg and not arg.endswith('='):
                key, value = arg.split('=', 1)
                
                # Special handling for path arguments
                if key in ['in', 'input', 'out', 'output', '--mpd_output', '--hls_master_playlist_output', 
                          'init_segment', 'segment_template', 'playlist_name']:
                    translated_value = translate_path(value)
                    translated_arg = f"{key}={translated_value}"
                    if translated_value != value:
                        logger.info(f"Translated arg path from '{arg}' to '{translated_arg}'")
                else:
                    # Check if the value might be a Docker path
                    if isinstance(value, str) and (value.startswith('/projects/') or value.startswith(DOCKER_PATH)):
                        translated_value = translate_path(value)
                        translated_arg = f"{key}={translated_value}"
                        if translated_value != value:
                            logger.info(f"Translated probable path in arg from '{arg}' to '{translated_arg}'")
                    else:
                        translated_arg = arg
                
                translated_args.append(translated_arg)
            else:
                # For standalone arguments or arguments that end with =
                # Check if it might be a Docker path by itself
                if arg.startswith('/projects/') or arg.startswith(DOCKER_PATH):
                    translated_arg = translate_path(arg)
                    if translated_arg != arg:
                        logger.info(f"Translated standalone path from '{arg}' to '{translated_arg}'")
                else:
                    translated_arg = arg
                
                translated_args.append(translated_arg)
        
        return translated_args
  • Path validation and translation helper used in run_shaka_packager. Translates Docker paths, checks file existence, handles file:// URIs indirectly, provides logging.
    def ensure_proper_path(ctx, file_path):
        """
        Helper function to ensure proper path handling and provide consistent logging.
        Returns a tuple of (saved_path, filename) or raises an exception.
        """
        ctx.info(f"Processing file path: {file_path}")
        
        if not file_path:
            raise ValueError("No file path provided. Please specify a valid file path.")
        
        # Handle both direct file paths and file:// URIs
        if file_path.startswith("file://"):
            ctx.info("Detected resource URI pattern")
            return None, None  # Will be handled by the resource reading code
        
        # Always attempt to translate Docker paths to host paths
        original_path = file_path
        translated_path = translate_path(file_path)
        
        if original_path != translated_path:
            ctx.info(f"Translated path from '{original_path}' to '{translated_path}'")
        
        # Check if this path exists
        if not os.path.exists(translated_path):
            # Try with VIDEO_PATH as a base
            if not translated_path.startswith(VIDEO_PATH) and VIDEO_PATH:
                alt_path = os.path.join(VIDEO_PATH, os.path.basename(translated_path))
                if os.path.exists(alt_path):
                    ctx.info(f"File not found at '{translated_path}', but found at '{alt_path}'")
                    return Path(alt_path), os.path.basename(alt_path)
            
            ctx.warning(f"File not found at path: {translated_path}")
        
        return Path(translated_path), os.path.basename(translated_path)
  • The @mcp.tool() decorator registers the 'run_shaka_packager' function as an MCP tool with name derived from function name, input schema from args/docstring.
    async def run_shaka_packager(ctx: Context, file_path: str, command_args: str) -> str:

Tool Definition Quality

Score is being calculated. Check back soon.

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/coderjun/shaka-packager-mcp-server'

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