modelcontextprotocol/servers · error · BadName

Invalid not_contains value: '{not_contains}' - cannot start

Error message

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

What it means

Same defense-in-depth guard in git_branch (server.py:277-278) for the 'not_contains' argument, which would otherwise be forwarded to repo.git.branch('--no-contains', not_contains). A leading '-' is rejected with git.exc.BadName to prevent flag injection.

Source

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

        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
        case 'remote':
            b_type = "-r"

View on GitHub (pinned to 76d64c822f)

Solutions

  1. Pass the bare ref or commit as 'not_contains' (e.g. 'main'), not '--no-contains main'.
  2. Strip a leading '--no-contains=' prefix from user input if present.
  3. Reject any value starting with '-' before sending the call.
  4. Share one ref-sanitization helper across contains and not_contains.

Example fix

// before
//   not_contains: "--no-contains main"   -> BadName
// after
//   not_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("--no-contains="):
        v = v[len("--no-contains="):]
    if v.startswith("-"):
        raise ValueError(f"ref argument must not start with '-': {v!r}")
    if not v:
        return None
    return v

not_contains = clean_ref_arg(arguments.get("not_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:
    log.warning("rejected ref arg: %s", e)

Prevention

When it happens

Trigger: Calling the branch tool with a 'not_contains' value beginning with '-', e.g. '-r', '--merged', '--no-contains=main'. Typically a user/LLM copying the '--no-contains X' literal into the value slot.

Common situations: Translating 'git branch --no-contains main' into tool args naively. Reusing a flag string across contains/not_contains. Hallucinated prefixes.

Related errors


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