-
Notifications
You must be signed in to change notification settings - Fork 713
feat: add line range support to get_file_contents tool #351
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
base: main
Are you sure you want to change the base?
Changes from all commits
275d315
f966e30
ba78e02
57c8c75
2e797d8
0c0ce8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,66 @@ automation and interaction capabilities for developers and tools. | |
## Prerequisites | ||
|
||
1. To run the server in a container, you will need to have [Docker](https://www.docker.com/) installed. | ||
|
||
## Required Token Permissions | ||
|
||
Each tool requires specific GitHub Personal Access Token permissions to function. Below are the required permissions for each tool category: | ||
|
||
### Users | ||
- **get_me** | ||
- Required permissions: | ||
- `read:user` - Read access to profile info | ||
|
||
### Issues | ||
- **get_issue**, **get_issue_comments**, **list_issues** | ||
- Required permissions: | ||
- `repo` - Full control of private repositories (for private repos) | ||
- `public_repo` - Access public repositories (for public repos) | ||
|
||
- **create_issue**, **add_issue_comment**, **update_issue** | ||
- Required permissions: | ||
- `repo` - Full control of private repositories (for private repos) | ||
- `public_repo` - Access public repositories (for public repos) | ||
- `write:discussion` - Write access to repository discussions (if using discussions) | ||
|
||
### Pull Requests | ||
- **get_pull_request**, **list_pull_requests**, **get_pull_request_files**, **get_pull_request_status** | ||
- Required permissions: | ||
- `repo` - Full control of private repositories (for private repos) | ||
- `public_repo` - Access public repositories (for public repos) | ||
|
||
- **merge_pull_request**, **update_pull_request_branch**, **create_pull_request**, **update_pull_request** | ||
- Required permissions: | ||
- `repo` - Full control of private repositories (for private repos) | ||
- `public_repo` - Access public repositories (for public repos) | ||
- `write:discussion` - Write access to repository discussions (if using discussions) | ||
|
||
### Repositories | ||
- **get_file_contents**, **search_repositories**, **list_commits** | ||
- Required permissions: | ||
- `repo` - Full control of private repositories (for private repos) | ||
- `public_repo` - Access public repositories (for public repos) | ||
|
||
- **create_or_update_file**, **push_files**, **create_repository**, **fork_repository**, **create_branch** | ||
- Required permissions: | ||
- `repo` - Full control of private repositories (for private repos) | ||
- `public_repo` - Access public repositories (for public repos) | ||
- `delete_repo` - Delete repositories (if needed) | ||
|
||
### Search | ||
- **search_code**, **search_users** | ||
- Required permissions: | ||
- No special permissions required for public data | ||
- `repo` - Required for searching private repositories | ||
|
||
### Code Scanning | ||
- **get_code_scanning_alert**, **list_code_scanning_alerts** | ||
- Required permissions: | ||
- `security_events` - Read and write security events | ||
- `repo` - Full control of private repositories (for private repos) | ||
|
||
Note: For organization repositories, additional organization-specific permissions may be required. | ||
|
||
2. Once Docker is installed, you will also need to ensure Docker is running. The image is public; if you get errors on pull, you may have an expired token and need to `docker logout ghcr.io`. | ||
3. Lastly you will need to [Create a GitHub Personal Access Token](https://github.com/settings/personal-access-tokens/new). | ||
The MCP server can use many of the GitHub APIs, so enable the permissions that you feel comfortable granting your AI tools (to learn more about access tokens, please check out the [documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)). | ||
|
@@ -491,14 +551,16 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description | |
- `branch`: New branch name (string, required) | ||
- `sha`: SHA to create branch from (string, required) | ||
|
||
- **list_commits** - Get a list of commits of a branch in a repository | ||
- **list_commits** - Gets commits of a branch in a repository | ||
- `owner`: Repository owner (string, required) | ||
- `repo`: Repository name (string, required) | ||
- `sha`: Branch name, tag, or commit SHA (string, optional) | ||
- `path`: Only commits containing this file path (string, optional) | ||
- `page`: Page number (number, optional) | ||
- `perPage`: Results per page (number, optional) | ||
|
||
### Search | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure why this is moved. |
||
|
||
- **get_commit** - Get details for a commit from a repository | ||
- `owner`: Repository owner (string, required) | ||
- `repo`: Repository name (string, required) | ||
|
@@ -613,3 +675,4 @@ The exported Go API of this module should currently be considered unstable, and | |
## License | ||
|
||
This project is licensed under the terms of the MIT open source license. Please refer to [MIT](./LICENSE) for the full terms. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,9 @@ import ( | |
"fmt" | ||
"io" | ||
"net/http" | ||
"strings" | ||
|
||
"encoding/base64" | ||
|
||
"github.com/github/github-mcp-server/pkg/translations" | ||
"github.com/google/go-github/v69/github" | ||
|
@@ -431,6 +434,12 @@ func GetFileContents(getClient GetClientFn, t translations.TranslationHelperFunc | |
mcp.WithString("branch", | ||
mcp.Description("Branch to get contents from"), | ||
), | ||
mcp.WithNumber("begin", | ||
mcp.Description("Begin line number (1-indexed, optional)"), | ||
), | ||
mcp.WithNumber("end", | ||
mcp.Description("End line number (1-indexed, optional)"), | ||
), | ||
), | ||
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { | ||
owner, err := requiredParam[string](request, "owner") | ||
|
@@ -449,6 +458,14 @@ func GetFileContents(getClient GetClientFn, t translations.TranslationHelperFunc | |
if err != nil { | ||
return mcp.NewToolResultError(err.Error()), nil | ||
} | ||
begin, err := OptionalIntParam(request, "begin") | ||
if err != nil { | ||
return mcp.NewToolResultError(err.Error()), nil | ||
} | ||
end, err := OptionalIntParam(request, "end") | ||
if err != nil { | ||
return mcp.NewToolResultError(err.Error()), nil | ||
} | ||
|
||
client, err := getClient(ctx) | ||
if err != nil { | ||
|
@@ -471,7 +488,39 @@ func GetFileContents(getClient GetClientFn, t translations.TranslationHelperFunc | |
|
||
var result interface{} | ||
if fileContent != nil { | ||
result = fileContent | ||
if fileContent.Content != nil && (begin > 0 || end > 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure about this implementation. Should it be possible for the LLM to only provide the beginning or the end? Whatever we choose should be documented so the LLM understands what is acceptable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All of this needs to be unit tested. |
||
decoded, err := fileContent.GetContent() | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to decode file content: %w", err) | ||
} | ||
lines := strings.Split(decoded, "\n") | ||
totalLines := len(lines) | ||
// Adjust indices for 1-based input | ||
startIdx := begin - 1 | ||
if startIdx < 0 { | ||
startIdx = 0 | ||
} | ||
endIdx := end | ||
if endIdx <= 0 || endIdx > totalLines { | ||
endIdx = totalLines | ||
} | ||
if startIdx >= totalLines { | ||
startIdx = totalLines - 1 | ||
} | ||
if startIdx < 0 { | ||
startIdx = 0 | ||
} | ||
if endIdx < startIdx { | ||
endIdx = startIdx | ||
} | ||
ranged := lines[startIdx:endIdx] | ||
joined := strings.Join(ranged, "\n") | ||
result = &github.RepositoryContent{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like we should check the |
||
Content: github.Ptr(base64.StdEncoding.EncodeToString([]byte(joined))), | ||
} | ||
} else { | ||
result = fileContent | ||
} | ||
} else { | ||
result = dirContent | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What should happen if the LLM provides begin and end but the contents returned are |
||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this section. I think it's interesting to have, but should have its own issue for discussion.