modelcontextprotocol/servers · error · BadName

Invalid contains value: '{contains}' - cannot start with '-'

Error message

Invalid contains value: '{contains}' - cannot start with '-'

What it means

git_branch (server.py:273-305) raises git.exc.BadName when the 'contains' argument starts with '-'. This is defense-in-depth against flag injection: the value is later passed verbatim to repo.git.branch('--contains', contains), and a leading dash would make GitPython parse it as a git flag.

Source

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

    try:
        resolved_repo = repo_path.resolve()
        resolved_allowed = allowed_repository.resolve()
    except (OSError, RuntimeError):
        raise ValueError(f"Invalid path: {repo_path}")

    # Check if repo_path is the same as or a subdirectory of allowed_repository
    try:
        resolved_repo.relative_to(resolved_allowed)
    except ValueError:
        raise ValueError(
            f"Repository path '{repo_path}' is outside the allowed repository '{allowed_repository}'"
        )


def git_branch(repo: git.Repo, branch_type: str, contains: str | None = None, not_contains: str | None = None) -> str:
    # Defense in depth: reject values starting with '-' to prevent flag injection
    if contains and contains.startswith("-"):
        raise BadName(f"Invalid contains value: '{contains}' - cannot start with '-'")
    if not_contains and not_contains.startswith("-"):
        raise BadName(f"Invalid not_contains value: '{not_contains}' - cannot start with '-'")

    match contains:
        case None:
            contains_sha = (None,)
        case _:
            contains_sha = ("--contains", contains)

    match not_contains:
        case None:
            not_contains_sha = (None,)
        case _:
            not_contains_sha = ("--no-contains", not_contains)

    match branch_type:
        case 'local':
            b_type = None

View on GitHub (pinned to 76d64c822f)

Solutions

  1. Pass the bare ref or commit (e.g. 'main', 'abc123') as 'contains', never the '--contains' prefix.
  2. Strip a leading '--contains=' from user input before forwarding it.
  3. Validate client-side that the value does not start with '-' and looks like a ref.
  4. Reuse the same ref-sanitization helper you use for branch_name/revision/not_contains.

Example fix

// before
//   arguments.get("contains") -> "--contains main"   -> BadName
// after
//   arguments.get("contains") -> "main"
Defensive patterns

Strategy: validation

Validate before calling

def clean_ref_arg(v: str | None) -> str | None:
    if v is None:
        return None
    v = v.strip()
    if v.startswith("--contains="):
        v = v[len("--contains="):]
    if v.startswith("-"):
        raise ValueError(f"ref argument must not start with '-': {v!r}")
    if not v:
        return None
    return v

contains = clean_ref_arg(arguments.get("contains"))

Type guard

def is_safe_ref_arg(v: object) -> bool:
    return v is None or (isinstance(v, str) and not v.startswith("-") and v.strip() != "")

Try / catch

from git.exc import BadName

try:
    result = git_branch(repo, branch_type, contains, not_contains)
except BadName as e:
    # reject the offending contains/not_contains value back to the user
    log.warning("rejected ref arg: %s", e)

Prevention

When it happens

Trigger: Calling the branch tool with a 'contains' value like '--all', '-v', '--contains=main', or any string beginning with '-'. Most often an LLM or user pasting the literal '--contains X' from a shell command instead of just the ref.

Common situations: Copy-pasting 'git branch --contains main' and passing '--contains main' as the value. Hallucinated flag prefixes from a model. Cross-contamination from the not_contains/--no-contains sibling argument.

Related errors


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