@@ -1371,31 +1371,8 @@ func DeleteFile(t translations.TranslationHelperFunc) inventory.ServerTool {
13711371 return nil , nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
13721372 }
13731373
1374- // Get the reference for the branch
1375- ref , resp , err := client .Git .GetRef (ctx , owner , repo , "refs/heads/" + branch )
1376- if err != nil {
1377- return nil , nil , fmt .Errorf ("failed to get branch reference: %w" , err )
1378- }
1379- defer func () { _ = resp .Body .Close () }()
1380-
1381- // Get the commit object that the branch points to
1382- baseCommit , resp , err := client .Git .GetCommit (ctx , owner , repo , * ref .Object .SHA )
1383- if err != nil {
1384- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
1385- "failed to get base commit" ,
1386- resp ,
1387- err ,
1388- ), nil , nil
1389- }
1390- defer func () { _ = resp .Body .Close () }()
1391-
1392- if resp .StatusCode != http .StatusOK {
1393- body , err := io .ReadAll (resp .Body )
1394- if err != nil {
1395- return nil , nil , fmt .Errorf ("failed to read response body: %w" , err )
1396- }
1397- return ghErrors .NewGitHubAPIStatusErrorResponse (ctx , "failed to get commit" , resp , body ), nil , nil
1398- }
1374+ path = strings .TrimPrefix (path , "/" )
1375+ refName := "refs/heads/" + branch
13991376
14001377 // Create a tree entry for the file deletion by setting SHA to nil
14011378 treeEntries := []* github.TreeEntry {
@@ -1407,75 +1384,29 @@ func DeleteFile(t translations.TranslationHelperFunc) inventory.ServerTool {
14071384 },
14081385 }
14091386
1410- // Create a new tree with the deletion
1411- newTree , resp , err := client .Git .CreateTree (ctx , owner , repo , * baseCommit .Tree .SHA , treeEntries )
1412- if err != nil {
1413- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
1414- "failed to create tree" ,
1415- resp ,
1416- err ,
1417- ), nil , nil
1418- }
1419- defer func () { _ = resp .Body .Close () }()
1420-
1421- if resp .StatusCode != http .StatusCreated {
1422- body , err := io .ReadAll (resp .Body )
1423- if err != nil {
1424- return nil , nil , fmt .Errorf ("failed to read response body: %w" , err )
1425- }
1426- return ghErrors .NewGitHubAPIStatusErrorResponse (ctx , "failed to create tree" , resp , body ), nil , nil
1427- }
1428-
1429- // Create a new commit with the new tree
1430- commit := github.Commit {
1431- Message : github .Ptr (message ),
1432- Tree : newTree ,
1433- Parents : []* github.Commit {{SHA : baseCommit .SHA }},
1434- }
1435- newCommit , resp , err := client .Git .CreateCommit (ctx , owner , repo , commit , nil )
1387+ pushResult , resp , err := commitEntriesToRef (ctx , client , owner , repo , refName , message , treeEntries )
14361388 if err != nil {
1437- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
1438- "failed to create commit" ,
1439- resp ,
1440- err ,
1441- ), nil , nil
1442- }
1443- defer func () { _ = resp .Body .Close () }()
1444-
1445- if resp .StatusCode != http .StatusCreated {
1446- body , err := io .ReadAll (resp .Body )
1447- if err != nil {
1448- return nil , nil , fmt .Errorf ("failed to read response body: %w" , err )
1389+ errMsg := err .Error ()
1390+ if strings .Contains (errMsg , "get branch reference" ) {
1391+ return nil , nil , err
14491392 }
1450- return ghErrors .NewGitHubAPIStatusErrorResponse (ctx , "failed to create commit" , resp , body ), nil , nil
1451- }
1452-
1453- // Update the branch reference to point to the new commit
1454- ref .Object .SHA = newCommit .SHA
1455- _ , resp , err = client .Git .UpdateRef (ctx , owner , repo , * ref .Ref , github.UpdateRef {
1456- SHA : * newCommit .SHA ,
1457- Force : github .Ptr (false ),
1458- })
1459- if err != nil {
1460- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
1461- "failed to update reference" ,
1462- resp ,
1463- err ,
1464- ), nil , nil
1465- }
1466- defer func () { _ = resp .Body .Close () }()
1467-
1468- if resp .StatusCode != http .StatusOK {
1469- body , err := io .ReadAll (resp .Body )
1470- if err != nil {
1471- return nil , nil , fmt .Errorf ("failed to read response body: %w" , err )
1393+ stage := "failed to delete file"
1394+ switch {
1395+ case strings .Contains (errMsg , "get base commit" ):
1396+ stage = "failed to get base commit"
1397+ case strings .Contains (errMsg , "create tree" ):
1398+ stage = "failed to create tree"
1399+ case strings .Contains (errMsg , "create commit" ):
1400+ stage = "failed to create commit"
1401+ case strings .Contains (errMsg , "update reference" ):
1402+ stage = "failed to update reference"
14721403 }
1473- return ghErrors .NewGitHubAPIStatusErrorResponse (ctx , "failed to update reference" , resp , body ), nil , nil
1404+ return ghErrors .NewGitHubAPIErrorResponse (ctx , stage , resp , err ), nil , nil
14741405 }
14751406
14761407 // Create a response similar to what the DeleteFile API would return
14771408 response := map [string ]any {
1478- "commit" : newCommit ,
1409+ "commit" : pushResult . Commit ,
14791410 "content" : nil ,
14801411 }
14811412
@@ -1680,161 +1611,66 @@ func PushFiles(t translations.TranslationHelperFunc) inventory.ServerTool {
16801611 return utils .NewToolResultError (err .Error ()), nil , nil
16811612 }
16821613
1683- // Parse files parameter - this should be an array of objects with path and content
1684- filesObj , ok := args ["files" ].([] any )
1685- if ! ok {
1686- return utils .NewToolResultError ("files parameter must be an array of objects with path and content" ), nil , nil
1614+ // Parse files parameter - accept several encodings from different MCP hosts.
1615+ fileEntries , err := parsePushFilesEntries ( args ["files" ])
1616+ if err != nil {
1617+ return utils .NewToolResultError (err . Error () ), nil , nil
16871618 }
1688-
1689- entries := make ([]* github.TreeEntry , 0 , len (filesObj ))
1690- for _ , file := range filesObj {
1691- fileMap , ok := file .(map [string ]any )
1692- if ! ok {
1693- return utils .NewToolResultError ("each file must be an object with path and content" ), nil , nil
1694- }
1695-
1696- filePath , ok := fileMap ["path" ].(string )
1697- if ! ok || filePath == "" {
1698- return utils .NewToolResultError ("each file must have a path" ), nil , nil
1699- }
1700- filePath , err = validateRelativePath (filePath )
1619+ entries := pushFileEntriesToTreeEntries (fileEntries )
1620+ for _ , entry := range entries {
1621+ filePath , err := validateRelativePath (entry .GetPath ())
17011622 if err != nil {
17021623 return utils .NewToolResultError (fmt .Sprintf ("invalid file path: %s" , err )), nil , nil
17031624 }
1704-
1705- content , ok := fileMap ["content" ].(string )
1706- if ! ok {
1707- return utils .NewToolResultError ("each file must have content" ), nil , nil
1708- }
1709-
1710- entries = append (entries , & github.TreeEntry {
1711- Path : github .Ptr (filePath ),
1712- Mode : github .Ptr ("100644" ),
1713- Type : github .Ptr ("blob" ),
1714- Content : github .Ptr (content ),
1715- })
1625+ entry .Path = github .Ptr (filePath )
17161626 }
17171627
17181628 client , err := deps .GetClient (ctx )
17191629 if err != nil {
17201630 return nil , nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
17211631 }
17221632
1723- // Get the reference for the branch
1724- var repositoryIsEmpty bool
1725- var branchNotFound bool
1726- ref , resp , err := client .Git .GetRef (ctx , owner , repo , "refs/heads/" + branch )
1633+ refName , err := ensurePushFilesBranchRef (ctx , client , owner , repo , branch )
17271634 if err != nil {
1728- ghErr , isGhErr := err .(* github.ErrorResponse )
1729- if isGhErr {
1730- if ghErr .Response .StatusCode == http .StatusConflict && ghErr .Message == "Git Repository is empty." {
1731- repositoryIsEmpty = true
1732- } else if ghErr .Response .StatusCode == http .StatusNotFound {
1733- branchNotFound = true
1734- }
1735- }
1736-
1737- if ! repositoryIsEmpty && ! branchNotFound {
1738- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
1739- "failed to get branch reference" ,
1740- resp ,
1741- err ,
1742- ), nil , nil
1743- }
1744- }
1745- // Only close resp if it's not nil and not an error case where resp might be nil
1746- if resp != nil && resp .Body != nil {
1747- defer func () { _ = resp .Body .Close () }()
1748- }
1749-
1750- var baseCommit * github.Commit
1751- if ! repositoryIsEmpty {
1752- if branchNotFound {
1753- ref , err = createReferenceFromDefaultBranch (ctx , client , owner , repo , branch )
1754- if err != nil {
1755- return utils .NewToolResultError (fmt .Sprintf ("failed to create branch from default: %v" , err )), nil , nil
1756- }
1757- }
1758-
1759- // Get the commit object that the branch points to
1760- baseCommit , resp , err = client .Git .GetCommit (ctx , owner , repo , * ref .Object .SHA )
1761- if err != nil {
1762- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
1763- "failed to get base commit" ,
1764- resp ,
1765- err ,
1766- ), nil , nil
1767- }
1768- if resp != nil && resp .Body != nil {
1769- defer func () { _ = resp .Body .Close () }()
1770- }
1771- } else {
1772- var base * github.Commit
1773- // Repository is empty, need to initialize it first
1774- ref , base , err = initializeRepository (ctx , client , owner , repo )
1775- if err != nil {
1635+ errMsg := err .Error ()
1636+ switch {
1637+ case strings .Contains (errMsg , "initialize repository" ):
17761638 return utils .NewToolResultError (fmt .Sprintf ("failed to initialize repository: %v" , err )), nil , nil
1639+ case strings .Contains (errMsg , "create branch from default" ):
1640+ return utils .NewToolResultError (fmt .Sprintf ("failed to create branch from default: %v" , err )), nil , nil
1641+ default :
1642+ return ghErrors .NewGitHubAPIErrorResponse (ctx , "failed to get branch reference" , nil , err ), nil , nil
17771643 }
1778-
1779- defaultBranch := strings .TrimPrefix (* ref .Ref , "refs/heads/" )
1780- if branch != defaultBranch {
1781- // Create the requested branch from the default branch
1782- ref , err = createReferenceFromDefaultBranch (ctx , client , owner , repo , branch )
1783- if err != nil {
1784- return utils .NewToolResultError (fmt .Sprintf ("failed to create branch from default: %v" , err )), nil , nil
1785- }
1786- }
1787-
1788- baseCommit = base
17891644 }
17901645
1791- // Create a new tree with the file entries (baseCommit is now guaranteed to exist)
1792- newTree , resp , err := client .Git .CreateTree (ctx , owner , repo , * baseCommit .Tree .SHA , entries )
1793- if err != nil {
1794- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
1795- "failed to create tree" ,
1796- resp ,
1797- err ,
1798- ), nil , nil
1799- }
1800- if resp != nil && resp .Body != nil {
1801- defer func () { _ = resp .Body .Close () }()
1802- }
1803-
1804- // Create a new commit (baseCommit always has a value now)
1805- commit := github.Commit {
1806- Message : github .Ptr (message ),
1807- Tree : newTree ,
1808- Parents : []* github.Commit {{SHA : baseCommit .SHA }},
1809- }
1810- newCommit , resp , err := client .Git .CreateCommit (ctx , owner , repo , commit , nil )
1646+ pushResult , resp , err := commitEntriesToRef (
1647+ ctx ,
1648+ client ,
1649+ owner ,
1650+ repo ,
1651+ refName ,
1652+ message ,
1653+ entries ,
1654+ )
18111655 if err != nil {
1812- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
1813- "failed to create commit" ,
1814- resp ,
1815- err ,
1816- ), nil , nil
1817- }
1818- if resp != nil && resp .Body != nil {
1819- defer func () { _ = resp .Body .Close () }()
1820- }
1821-
1822- // Update the reference to point to the new commit
1823- ref .Object .SHA = newCommit .SHA
1824- updatedRef , resp , err := client .Git .UpdateRef (ctx , owner , repo , * ref .Ref , github.UpdateRef {
1825- SHA : * newCommit .SHA ,
1826- Force : github .Ptr (false ),
1827- })
1828- if err != nil {
1829- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
1830- "failed to update reference" ,
1831- resp ,
1832- err ,
1833- ), nil , nil
1656+ stage := "failed to push files"
1657+ errMsg := err .Error ()
1658+ switch {
1659+ case strings .Contains (errMsg , "get base commit" ):
1660+ stage = "failed to get base commit"
1661+ case strings .Contains (errMsg , "create tree" ):
1662+ stage = "failed to create tree"
1663+ case strings .Contains (errMsg , "create commit" ):
1664+ stage = "failed to create commit"
1665+ case strings .Contains (errMsg , "update reference" ):
1666+ stage = "failed to update reference"
1667+ case strings .Contains (errMsg , "get branch reference" ):
1668+ stage = "failed to get branch reference"
1669+ }
1670+ return ghErrors .NewGitHubAPIErrorResponse (ctx , stage , resp , err ), nil , nil
18341671 }
1835- defer func () { _ = resp .Body .Close () }()
18361672
1837- r , err := json .Marshal (updatedRef )
1673+ r , err := json .Marshal (pushResult . Ref )
18381674 if err != nil {
18391675 return nil , nil , fmt .Errorf ("failed to marshal response: %w" , err )
18401676 }
0 commit comments