modelcontextprotocol/servers · error · ValueError

Path '{f}' is outside the repository '{repo_root}'

Error message

Path '{f}' is outside the repository '{repo_root}'

What it means

In git_add(), after the path resolves successfully, resolved.relative_to(repo_root) raises ValueError when the path is not under the repository root (path traversal such as ../../etc/passwd, or an absolute path outside the tree). The code then stages with `git add --` to also prevent option injection. The exception type is ValueError and propagates raw to the client.

Source

Thrown at src/git/src/mcp_server_git/server.py:148

    return f"Changes committed successfully with hash {commit.hexsha}"

def git_add(repo: git.Repo, files: list[str]) -> str:
    if files == ["."]:
        repo.git.add(".")
    else:
        # Defense in depth: validate each path resolves within the repository
        # working tree to prevent path traversal (e.g. '../../etc/passwd' or an
        # absolute path) from staging files outside repository boundaries.
        repo_root = Path(repo.working_dir).resolve()
        for f in files:
            try:
                resolved = (repo_root / f).resolve()
            except (OSError, RuntimeError):
                raise ValueError(f"Invalid path: '{f}'")
            try:
                resolved.relative_to(repo_root)
            except ValueError:
                raise ValueError(
                    f"Path '{f}' is outside the repository '{repo_root}'"
                )
        # Use '--' to prevent files starting with '-' from being interpreted as options
        repo.git.add("--", *files)
    return "Files staged successfully"

def git_reset(repo: git.Repo) -> str:
    repo.index.reset()
    return "All staged changes reset"

def git_log(repo: git.Repo, max_count: int = 10, start_timestamp: Optional[str] = None, end_timestamp: Optional[str] = None) -> list[str]:
    if start_timestamp or end_timestamp:
        # Defense in depth: reject timestamps starting with '-' to prevent flag injection
        if start_timestamp and start_timestamp.startswith("-"):
            raise ValueError(f"Invalid start_timestamp: '{start_timestamp}' - cannot start with '-'")
        if end_timestamp and end_timestamp.startswith("-"):
            raise ValueError(f"Invalid end_timestamp: '{end_timestamp}' - cannot start with '-'")
        # Use git log command with date filtering

View on GitHub (pinned to 76d64c822f)

Solutions

  1. Use paths relative to the repository root and confined to it.
  2. Ensure the file physically resides under the working tree.
  3. Reject absolute paths and any resolved path whose common prefix is not repo_root.

Example fix

# before
git_add(repo, files=['../../etc/passwd'])  # -> ValueError outside repo

# after
def within_repo(repo, f):
    root = Path(repo.working_dir).resolve()
    return (root / f).resolve().is_relative_to(root)
git_add(repo, [f for f in files if within_repo(repo, f)])
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
def within_repo(repo, f: str) -> bool:
    root = Path(repo.working_dir).resolve()
    try:
        return (root / f).resolve().is_relative_to(root)
    except (OSError, RuntimeError):
        return False

Try / catch

try:
    git_add(repo, files)
except ValueError as e:
    if 'outside the repository' in e.args[0]:
        # filter out traversal paths and retry
    raise

Prevention

When it happens

Trigger: Passing a relative path that escapes the working tree (../); passing an absolute path outside the repo; symlinks that resolve outside the repo.

Common situations: Adversarial path input; UI permitting absolute paths; symlink chains pointing outside the repo.

Related errors


AI-assisted analysis of modelcontextprotocol/servers@76d64c822f (2026-08-12). Data as JSON: /api/errors/9666405fd8090e81. Report an issue: GitHub.