Skip to content

feat: add DeleteFile tool to delete files from GitHub repositories #356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 13, 2025

Conversation

ashwin-ant
Copy link
Contributor

This adds a tool that matches the behavior of this API endpoint. This is needed since the create_or_update_file tool doesn't allow deletion.

@ashwin-ant ashwin-ant requested a review from a team as a code owner April 28, 2025 18:17
@@ -528,6 +528,96 @@ func ForkRepository(getClient GetClientFn, t translations.TranslationHelperFunc)
}
}

// DeleteFile creates a tool to delete a file in a GitHub repository.
func DeleteFile(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("delete_file",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, can you look at adding annotations? You can see examples around the codebase?

For this particular one, as well as the tool title with translation I would add:

ReadOnlyHint: false
DestructiveHint: true

https://modelcontextprotocol.io/docs/concepts/tools#tool-definition-structure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SamMorrowDrums done! Thanks for the suggestion.

@ashwin-ant ashwin-ant force-pushed the ashwin/deletefile branch 2 times, most recently from abe06cd to e6201f4 Compare May 1, 2025 23:18
Copy link
Collaborator

@williammartin williammartin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ashwin-ant thanks for this PR. I'm a bit confused about the implementation though. It seems like you:

  • Get a github ref for the current branch
  • Get the commit for that ref
  • Create a new TreeEntry without the file
  • Create a new commit
  • Update the ref

Why not:

  • Get a github ref for the current branch
  • Get the commit for that ref
  • Get the SHA of the filepath in that commit
  • Call client.Repositories.DeleteFile which exactly calls the API endpoint you referenced.

Edit: Maybe the TreeEntry approach allows for deletion of a directory where the contents API doesn't?

@williammartin
Copy link
Collaborator

williammartin commented May 12, 2025

@ashwin-ant I just rebased this and added some e2e tests that we can consider refactoring behind (and perhaps, demonstrating that we can't do directory removal with the contents API). You can run them like:

 GOMAXPROCS=1 GITHUB_MCP_SERVER_E2E_TOKEN=<YOUR TOKEN> go test -v -count=1 -run 'Test.*Deletion' --tags e2e ./e2e

Make sure your token has delete_repo scope to avoid debris.

I'll probably collapse them to one test later, but for the moment while we figure this out it makes sense to keep them separate.

@ashwin-ant
Copy link
Contributor Author

@williammartin Thanks for reviewing! I actually was going with that approach in my first commit here, but ended up switching to this more complicated approach in 4d5f719.

The reason for this is that I discovered the REST file deletion endpoint (and client.Repositories.DeleteFile) don't add commit signing to the deletion commit, unlike how the endpoint backing the create_or_update_files tool does. This appears to be a quirk of the API. The approach here will get commit signing when used with either the github-actions user or as an app, both of which suit an LLM well.

@williammartin
Copy link
Collaborator

That's a great explanation and I'd love it if you put that as a comment in the code for all future reviewers!

williammartin
williammartin previously approved these changes May 13, 2025
Copy link
Collaborator

@williammartin williammartin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

➜  github-mcp-server git:(ashwin/deletefile) ✗ GOMAXPROCS=1 GITHUB_MCP_SERVER_E2E_TOKEN=$(gh auth token) go test -v -count=1 -run TestFileDeletion --tags e2e ./e2e

=== RUN   TestFileDeletion
=== PAUSE TestFileDeletion
=== CONT  TestFileDeletion
    e2e_test.go:50: Building Docker image for e2e tests...
    e2e_test.go:124: Starting Stdio MCP client...
    e2e_test.go:385: Getting current user...
    e2e_test.go:413: Creating repository williammartin/github-mcp-server-e2e-TestFileDeletion-1747137948412...
    e2e_test.go:437: Creating branch in williammartin/github-mcp-server-e2e-TestFileDeletion-1747137948412...
    e2e_test.go:454: Creating commit with new file in williammartin/github-mcp-server-e2e-TestFileDeletion-1747137948412...
    e2e_test.go:478: Getting file contents in williammartin/github-mcp-server-e2e-TestFileDeletion-1747137948412...
    e2e_test.go:506: Deleting file in williammartin/github-mcp-server-e2e-TestFileDeletion-1747137948412...
    e2e_test.go:520: Listing commits in williammartin/github-mcp-server-e2e-TestFileDeletion-1747137948412...
    e2e_test.go:554: Getting commit williammartin/github-mcp-server-e2e-TestFileDeletion-1747137948412:76f50ad460c472d7eb9df09bd1c94d5678a99b96...
    e2e_test.go:422: Deleting repository williammartin/github-mcp-server-e2e-TestFileDeletion-1747137948412...
--- PASS: TestFileDeletion (6.65s)
PASS
ok      github.com/github/github-mcp-server/e2e 6.894s
image image

LGTM, thanks.

@williammartin williammartin requested a review from Copilot May 13, 2025 12:07
@williammartin
Copy link
Collaborator

williammartin commented May 13, 2025

Bypassing the last pusher rule because I fixed minor breaking changes from mcp-go bump on main around annotations and some small error handling additions.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a new delete_file tool that leverages the Git Data API to delete files with commit signing, and adds accompanying tests.

  • Registers DeleteFile in the GitHub toolset initialization.
  • Implements DeleteFile in repositories.go, creating a deletion commit via trees and references.
  • Adds Test_DeleteFile in repositories_test.go for success and branch-not-found scenarios.

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
pkg/github/tools.go Register the new DeleteFile tool in server toolsets.
pkg/github/repositories.go Implement DeleteFile using Git Data API calls.
pkg/github/repositories_test.go Add unit tests for the DeleteFile tool.
Comments suppressed due to low confidence (1)

pkg/github/repositories_test.go:1532

  • The test covers success and branch-not-found paths; consider adding cases for failures in tree creation, commit creation, and reference updates to ensure robust error handling across all stages of the deletion flow.
func Test_DeleteFile(t *testing.T) {

@williammartin williammartin merged commit da2df71 into github:main May 13, 2025
9 checks passed
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.

3 participants