Fix logical path when file or directory not found
This commit is contained in:
+106
-113
@@ -623,17 +623,113 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t
|
||||
if respContents != nil {
|
||||
defer func() { _ = respContents.Body.Close() }()
|
||||
}
|
||||
if err != nil {
|
||||
return ghErrors.NewGitHubAPIErrorResponse(ctx,
|
||||
"failed to get file SHA",
|
||||
respContents,
|
||||
err,
|
||||
), nil, nil
|
||||
|
||||
// The path does not point to a file or directory.
|
||||
// Instead let's try to find it in the Git Tree by matching the end of the path.
|
||||
if err != nil || (fileContent == nil && dirContent == nil) {
|
||||
// Step 1: Get Git Tree recursively
|
||||
tree, response, err := client.Git.GetTree(ctx, owner, repo, ref, true)
|
||||
if err != nil {
|
||||
return ghErrors.NewGitHubAPIErrorResponse(ctx,
|
||||
"failed to get git tree",
|
||||
response,
|
||||
err,
|
||||
), nil, nil
|
||||
}
|
||||
defer func() { _ = response.Body.Close() }()
|
||||
|
||||
// Step 2: Filter tree for matching paths
|
||||
const maxMatchingFiles = 3
|
||||
matchingFiles := filterPaths(tree.Entries, path, maxMatchingFiles)
|
||||
if len(matchingFiles) > 0 {
|
||||
matchingFilesJSON, err := json.Marshal(matchingFiles)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError(fmt.Sprintf("failed to marshal matching files: %s", err)), nil, nil
|
||||
}
|
||||
resolvedRefs, err := json.Marshal(rawOpts)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError(fmt.Sprintf("failed to marshal resolved refs: %s", err)), nil, nil
|
||||
}
|
||||
return utils.NewToolResultError(fmt.Sprintf("Resolved potential matches in the repository tree (resolved refs: %s, matching files: %s), but the raw content API returned an unexpected status code %d.", string(resolvedRefs), string(matchingFilesJSON), rawAPIResponseCode)), nil, nil
|
||||
}
|
||||
return utils.NewToolResultError("Failed to get file contents. The path does not point to a file or directory, or the file does not exist in the repository."), nil, nil
|
||||
}
|
||||
|
||||
// file content or file SHA is nil which means it's a directory
|
||||
if fileContent == nil || fileContent.SHA == nil {
|
||||
defer func() { _ = respContents.Body.Close() }()
|
||||
if fileContent != nil && fileContent.SHA != nil {
|
||||
fileSHA = *fileContent.SHA
|
||||
|
||||
rawClient, err := getRawClient(ctx)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError("failed to get GitHub raw content client"), nil, nil
|
||||
}
|
||||
resp, err := rawClient.GetRawContent(ctx, owner, repo, path, rawOpts)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError("failed to get raw repository content"), nil, nil
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
// If the raw content is found, return it directly
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError("failed to read response body"), nil, nil
|
||||
}
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
|
||||
var resourceURI string
|
||||
switch {
|
||||
case sha != "":
|
||||
resourceURI, err = url.JoinPath("repo://", owner, repo, "sha", sha, "contents", path)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
|
||||
}
|
||||
case ref != "":
|
||||
resourceURI, err = url.JoinPath("repo://", owner, repo, ref, "contents", path)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
|
||||
}
|
||||
default:
|
||||
resourceURI, err = url.JoinPath("repo://", owner, repo, "contents", path)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if content is text or binary
|
||||
isTextContent := strings.HasPrefix(contentType, "text/") ||
|
||||
contentType == "application/json" ||
|
||||
contentType == "application/xml" ||
|
||||
strings.HasSuffix(contentType, "+json") ||
|
||||
strings.HasSuffix(contentType, "+xml")
|
||||
|
||||
if isTextContent {
|
||||
result := &mcp.ResourceContents{
|
||||
URI: resourceURI,
|
||||
Text: string(body),
|
||||
MIMEType: contentType,
|
||||
}
|
||||
// Include SHA in the result metadata
|
||||
if fileSHA != "" {
|
||||
return utils.NewToolResultResource(fmt.Sprintf("successfully downloaded text file (SHA: %s)", fileSHA), result), nil, nil
|
||||
}
|
||||
return utils.NewToolResultResource("successfully downloaded text file", result), nil, nil
|
||||
}
|
||||
|
||||
result := &mcp.ResourceContents{
|
||||
URI: resourceURI,
|
||||
Blob: body,
|
||||
MIMEType: contentType,
|
||||
}
|
||||
// Include SHA in the result metadata
|
||||
if fileSHA != "" {
|
||||
return utils.NewToolResultResource(fmt.Sprintf("successfully downloaded binary file (SHA: %s)", fileSHA), result), nil, nil
|
||||
}
|
||||
return utils.NewToolResultResource("successfully downloaded binary file", result), nil, nil
|
||||
}
|
||||
} else if dirContent != nil {
|
||||
// file content or file SHA is nil which means it's a directory
|
||||
r, err := json.Marshal(dirContent)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError("failed to marshal response"), nil, nil
|
||||
@@ -641,110 +737,7 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t
|
||||
return utils.NewToolResultText(string(r)), nil, nil
|
||||
}
|
||||
|
||||
fileSHA = *fileContent.SHA
|
||||
|
||||
rawClient, err := getRawClient(ctx)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError("failed to get GitHub raw content client"), nil, nil
|
||||
}
|
||||
resp, err := rawClient.GetRawContent(ctx, owner, repo, path, rawOpts)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError("failed to get raw repository content"), nil, nil
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
// If the raw content is found, return it directly
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError("failed to read response body"), nil, nil
|
||||
}
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
|
||||
var resourceURI string
|
||||
switch {
|
||||
case sha != "":
|
||||
resourceURI, err = url.JoinPath("repo://", owner, repo, "sha", sha, "contents", path)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
|
||||
}
|
||||
case ref != "":
|
||||
resourceURI, err = url.JoinPath("repo://", owner, repo, ref, "contents", path)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
|
||||
}
|
||||
default:
|
||||
resourceURI, err = url.JoinPath("repo://", owner, repo, "contents", path)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create resource URI: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if content is text or binary
|
||||
isTextContent := strings.HasPrefix(contentType, "text/") ||
|
||||
contentType == "application/json" ||
|
||||
contentType == "application/xml" ||
|
||||
strings.HasSuffix(contentType, "+json") ||
|
||||
strings.HasSuffix(contentType, "+xml")
|
||||
|
||||
if isTextContent {
|
||||
result := &mcp.ResourceContents{
|
||||
URI: resourceURI,
|
||||
Text: string(body),
|
||||
MIMEType: contentType,
|
||||
}
|
||||
// Include SHA in the result metadata
|
||||
if fileSHA != "" {
|
||||
return utils.NewToolResultResource(fmt.Sprintf("successfully downloaded text file (SHA: %s)", fileSHA), result), nil, nil
|
||||
}
|
||||
return utils.NewToolResultResource("successfully downloaded text file", result), nil, nil
|
||||
}
|
||||
|
||||
result := &mcp.ResourceContents{
|
||||
URI: resourceURI,
|
||||
Blob: body,
|
||||
MIMEType: contentType,
|
||||
}
|
||||
// Include SHA in the result metadata
|
||||
if fileSHA != "" {
|
||||
return utils.NewToolResultResource(fmt.Sprintf("successfully downloaded binary file (SHA: %s)", fileSHA), result), nil, nil
|
||||
}
|
||||
return utils.NewToolResultResource("successfully downloaded binary file", result), nil, nil
|
||||
}
|
||||
rawAPIResponseCode = resp.StatusCode
|
||||
|
||||
// The path does not point to a file or directory.
|
||||
// Instead let's try to find it in the Git Tree by matching the end of the path.
|
||||
|
||||
// Step 1: Get Git Tree recursively
|
||||
tree, response, err := client.Git.GetTree(ctx, owner, repo, ref, true)
|
||||
if err != nil {
|
||||
return ghErrors.NewGitHubAPIErrorResponse(ctx,
|
||||
"failed to get git tree",
|
||||
response,
|
||||
err,
|
||||
), nil, nil
|
||||
}
|
||||
defer func() { _ = response.Body.Close() }()
|
||||
|
||||
// Step 2: Filter tree for matching paths
|
||||
const maxMatchingFiles = 3
|
||||
matchingFiles := filterPaths(tree.Entries, path, maxMatchingFiles)
|
||||
if len(matchingFiles) > 0 {
|
||||
matchingFilesJSON, err := json.Marshal(matchingFiles)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError(fmt.Sprintf("failed to marshal matching files: %s", err)), nil, nil
|
||||
}
|
||||
resolvedRefs, err := json.Marshal(rawOpts)
|
||||
if err != nil {
|
||||
return utils.NewToolResultError(fmt.Sprintf("failed to marshal resolved refs: %s", err)), nil, nil
|
||||
}
|
||||
return utils.NewToolResultError(fmt.Sprintf("Resolved potential matches in the repository tree (resolved refs: %s, matching files: %s), but the raw content API returned an unexpected status code %d.", string(resolvedRefs), string(matchingFilesJSON), rawAPIResponseCode)), nil, nil
|
||||
}
|
||||
|
||||
return utils.NewToolResultError("Failed to get file contents. The path does not point to a file or directory, or the file does not exist in the repository."), nil, nil
|
||||
return utils.NewToolResultError("failed to get file contents"), nil, nil
|
||||
})
|
||||
|
||||
return tool, handler
|
||||
|
||||
Reference in New Issue
Block a user