github / github/github-mcp-server

Add file mode (executable bit) support to `push_files`

Abierto
#2,578 0 comentarios 2 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Go
Estrellas
33k
Forks
5k
Merge medio
2 d 1 h
PR fusionados (30 d)
52

Descripción

# Feature request: expose file mode (executable bit) on `push_files`

## Use case

Agentic coding harnesses use this MCP server to publish files into a repo
on behalf of an LLM (issue execution, autonomous PR workflows, etc.).
When the work the agent is asked to produce is a shell script, build
helper, or other executable, the resulting file needs the executable
bit set (`100755`) — otherwise reviewers either have to `chmod +x` after
merge or merge a broken artifact.

Today there is no way for an MCP client to land an executable file
through this server. Both file-write tools use APIs that surface a fixed
mode:

- `create_or_update_file` → Contents API (`PUT /repos/.../contents/{path}`),
which always writes mode `100644`. This is a GitHub API limitation, not
something this server can route around.
- `push_files` → Git Data API (`CreateTree` + `CreateCommit` +
`UpdateRef`). The underlying API **does** support per-entry mode, but
the tool's input schema doesn't expose it. Each entry is currently
`{content, path}` only.

We've observed agents react to this in two unhelpful ways:

1. **Quiet hallucination.** The agent reports in the PR body that it
pushed with mode `100755` when in fact the blob landed as `100644`.
2. **Workaround scope drift.** The agent invents a second file (e.g.
`scripts/permissions.sh` containing `find scripts/ -exec chmod +x`)
that wasn't part of the task, hoping a CI step or human will run it.

A first-class `mode` parameter on `push_files` would close both failure
modes cleanly.

## Proposed change

Extend the `push_files.files[]` input schema with an optional `mode`
field, validated against the set the Git Data API accepts:

| Mode | Meaning |
|----------|------------------------------------|
| `100644` | regular file (default if omitted) |
| `100755` | executable file |
| `120000` | symlink |
| `040000` | subdirectory (subtree) |
| `160000` | submodule |

For most agentic use cases only `100644` (default) and `100755` matter,
but echoing the full Git Data API set keeps parity with the underlying
endpoint.

Backwards compatible: existing callers that omit `mode` get `100644`,
which is what they get today.

## Implementation pointer

The relevant code is already calling `Git.CreateTree` with a
`[]*github.TreeEntry` (in `pkg/github/repositories.go`, around the
`PushFiles` tool definition). The go-github `TreeEntry` struct
already has a `Mode *string` field — it just isn't being set from the
tool input. Threading it through should be a small change:

```go
// in the loop that builds entries:
mode := "100644"
if f.Mode != nil {
mode = *f.Mode
}
entries = append(entries, &github.TreeEntry{
Path: github.String(f.Path),
Mode: github.String(mode),
Type: github.String("blob"),
Content: github.String(f.Content),
})
```

Plus input-schema additions for the optional field with the validated
set above.

## Single-file case

`create_or_update_file` can't be fixed the same way because the Contents
API doesn't support mode at all — it's a GitHub API constraint, not a
server constraint. For the single-file executable case, callers can use
`push_files` with a one-entry `files` array. Worth noting that in the
tool's description so clients pick the right call.

## Out of scope

- Changes to `create_or_update_file` (blocked by Contents API).
- Symlink/submodule write flows beyond just accepting their modes on the
schema.
- Validating the executable-bit semantics on the receiving end.

## Context

Filed against this server because we've been hitting the limitation
from a Claude-Code-plans / Goose-executes harness
([why-pengo/claude_and_goose](https://github.com/why-pengo/claude_and_goose)),
where local-model executors are asked to ship small bash helpers as
part of issue-driven PRs. Happy to send a PR if the proposed shape
looks right.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.