Skip to content

feat: add file mode (executable bit) support to push_files#2579

Open
why-pengo wants to merge 1 commit into
github:mainfrom
why-pengo:feature/push-files-mode
Open

feat: add file mode (executable bit) support to push_files#2579
why-pengo wants to merge 1 commit into
github:mainfrom
why-pengo:feature/push-files-mode

Conversation

@why-pengo

@why-pengo why-pengo commented May 30, 2026

Copy link
Copy Markdown

This is my first contribution to this repo. I used Claude Code with Opus 4.7. I reviewed the code and tried to make sure I followed contributing.md.

Summary

  • Adds an optional mode field to each entry in push_files, threaded into the Git Data API CreateTree call. Accepted values are the ones the API itself accepts: 100644 (regular file, the default when omitted), 100755 (executable), 120000 (symlink), 040000 (subtree), 160000 (submodule). Unknown values are rejected with a clear error.
  • Updates create_or_update_file's description to point callers at push_files (with a single-entry files array) when they need to land an executable file, since the Contents API can't set the mode.
  • Backwards compatible: existing callers that omit mode continue to write 100644.

Motivation

Agentic harnesses use this server to publish files into a repo on behalf of an LLM. When the produced artifact is a shell script, build helper, or other executable, the file needs the executable bit set — otherwise reviewers either have to chmod +x after merge or land a broken artifact. Today there is no in-band way to do this through the server.

In practice we observed two failure modes:

  1. Agents reporting in PR bodies that they pushed with mode 100755 when the blob in fact landed as 100644.
  2. Agents inventing a second "fix permissions" file outside the requested scope (e.g. a scripts/permissions.sh that runs chmod +x later).

Exposing mode as a first-class field closes both.

create_or_update_file is not changed in this PR because the Contents API has no mode parameter — that's a GitHub API constraint, not a server one. The description now points to push_files for that case.

Test plan

  • UPDATE_TOOLSNAPS=true go test ./... — all packages green; toolsnaps for push_files and create_or_update_file regenerated.
  • script/lint (golangci-lint v2) — 0 issues.
  • script/generate-docsREADME.md regenerated to reflect the new files[].mode field description.
  • New test cases in Test_PushFiles:
    • successful multi-file push where one entry is 100755 and another defaults to 100644 (asserts the request body sent to the Git Data API contains the correct per-entry mode);
    • rejects an unknown mode string (e.g. 0755) with a clear error;
    • rejects a non-string mode value.

Closes #2578

Adds an optional `mode` field to each entry of the push_files tool,
threaded into the Git Data API CreateTree call. Allowed values are
the modes the API itself accepts: 100644 (regular file, the default
when omitted), 100755 (executable), 120000 (symlink), 040000
(subtree), and 160000 (submodule). Unknown values are rejected with
a clear error so agentic callers can correct themselves instead of
silently writing the wrong mode.

Before this change there was no in-band way for an MCP client to
publish an executable file. Two failure modes were observed in
practice: agents hallucinating in PR bodies that they had pushed
mode 100755 when the blob actually landed as 100644, and agents
inventing a second "fix permissions" file outside the requested
scope. Exposing mode closes both gaps without breaking existing
callers, which continue to get 100644 when the field is omitted.

create_or_update_file still cannot set the executable bit because
the Contents API surfaces a fixed mode; its description now points
callers at push_files with a single-entry files array for that
case.

Closes github#2578

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@why-pengo why-pengo requested a review from a team as a code owner May 30, 2026 15:52
Copilot AI review requested due to automatic review settings May 30, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant