Skip to content

fix(git): accept JSON-encoded string for git_add files argument#4276

Open
adityasingh2400 wants to merge 1 commit into
modelcontextprotocol:mainfrom
adityasingh2400:fix-git-add-json-string-files
Open

fix(git): accept JSON-encoded string for git_add files argument#4276
adityasingh2400 wants to merge 1 commit into
modelcontextprotocol:mainfrom
adityasingh2400:fix-git-add-json-string-files

Conversation

@adityasingh2400
Copy link
Copy Markdown
Contributor

Description

Fixes #4242. The git server's git_add tool rejects a perfectly reasonable call when a client sends the files argument as a JSON-encoded array string rather than a real array.

Server Details

  • Server: git
  • Changes to: tools (the git_add input schema and handler)

Motivation and Context

In #4242 a client called git_add with files set to the string '["inc/admin/functions.php", "inc/admin/menu.php", ...]' and got back:

Input validation error: '["inc/admin/functions.php", ...]' is not of type 'array'

There are two layers to this:

  1. GitAdd.files was typed list[str], so GitAdd.model_json_schema() published {"type": "array"}. The low-level MCP SDK validates raw arguments against the tool's inputSchema before the handler runs, so a string value is rejected with the exact "is not of type 'array'" message above.
  2. Even if that validation were bypassed, git_add passed the raw string straight to repo.git.add("--", *files), which iterates the string character by character. That produces git add -- [ " f i l e 1 ..., which fails with fatal: pathspec '[' did not match any files.

LLM clients fairly often serialize array arguments as JSON strings, so this is a recurring friction point worth handling leniently.

What changed

  • Widened GitAdd.files to list[str] | str with a description, so the published schema becomes anyOf: [array of strings, string] and the SDK no longer pre-rejects a string value.
  • Added a small normalize_file_list helper used by git_add: when files is a string it parses a JSON array of strings into a list, and otherwise treats the value as a single path. Real lists pass through unchanged, so the happy path and ["."] behavior are untouched.

How Has This Been Tested?

Ran the git server suite from src/git with uv run pytest: 44 passed, including four new cases:

  • test_git_add_json_encoded_string: stages two files passed as '["file1.txt", "file2.txt"]'.
  • test_git_add_single_path_string: a bare path string stages exactly that one file, not its characters.
  • test_normalize_file_list: lists pass through, JSON arrays parse, non-JSON and non-string-list JSON fall back to a single entry.
  • The existing test_git_add_all_files and test_git_add_specific_files still pass.

I confirmed the new behavioral tests fail on the unmodified code: the original git_add('["file1.txt", "file2.txt"]') raises GitCommandError (git add -- [ " f i l e 1 ...). I also validated end to end that GitAdd.model_json_schema() now accepts the array, the JSON-string, and the single-path forms through jsonschema.validate, matching how the SDK gates calls.

uv run pyright reports 0 errors and uv run ruff check . passes.

Breaking Changes

None. Existing array inputs behave exactly as before. This only adds acceptance of additional input shapes.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Protocol Documentation
  • My changes follows MCP security best practices
  • I have updated the server's README accordingly
  • I have tested this with an LLM client
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have documented all environment variables and configuration options

Additional context

The leniency is intentionally narrow: only a JSON array whose elements are all strings is expanded into multiple paths. Any other string, including malformed JSON or a JSON object, is treated as a single literal path, which keeps the existing strict pathspec semantics for genuine single-file calls.

Some clients send the files argument to git_add as a JSON-encoded array
string, for example '["a.py", "b.py"]', rather than a real array. The
published input schema declared files as an array only, so the SDK
rejected the call at the validation layer with "is not of type array".
Even past that layer, git_add passed the raw string through to git, which
split it into individual characters.

Widen the schema to accept a list of strings or a string, then normalize
a string into a list inside git_add: parse a JSON array of strings when
present, otherwise treat the value as a single path. Real lists are
unchanged.

Fixes modelcontextprotocol#4242
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[git] maybe more lenient with the input

1 participant