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:
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool runs a command but doesn't explain what Shaka Packager is, what the command does (e.g., video packaging, transcoding), potential side effects (e.g., file modifications), or any requirements like permissions or rate limits. This leaves significant gaps in understanding the tool's behavior.

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?

The description is concise and well-structured, with a clear purpose statement followed by a bullet-point list of parameters. It avoids unnecessary words and is front-loaded with the main action. However, it could be slightly more efficient by integrating parameter details into a single sentence without losing clarity.

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

Completeness3/5

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

Given the tool's complexity (running a custom command with two parameters), no annotations, and an output schema (which reduces the need to describe return values), the description is minimally complete. It covers the basic purpose and parameters but lacks behavioral context and usage guidelines, making it adequate but with clear gaps for effective agent 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 adds basic semantics for both parameters ('file_path' and 'command_args'), explaining their purposes beyond the schema, which has 0% description coverage. However, it lacks details on formats (e.g., what 'command_args' should include) or constraints, providing only minimal value over the bare schema. With two parameters and low schema coverage, this is adequate but not comprehensive.

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: 'Run a custom Shaka Packager command.' This specifies the verb ('Run') and resource ('Shaka Packager command'), making it understandable. However, it doesn't differentiate from sibling tools like 'analyze_video' or 'get_shaka_options', which prevents a perfect score.

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 contexts where this is preferred, such as for custom packaging versus standard analysis or documentation retrieval. This lack of comparative usage information limits its helpfulness.

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

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