Describe the bug
Running github-mcp-server locally in Docker and consuming it from the Claude desktop app, the exposed toolset is write-only. Every repository read tool is absent, so an agent can commit, push, open PRs and merge, but cannot read a single file, list a branch, or look up an issue it is about to modify.
18 tools are exposed, all of them mutations:
add_comment_to_pending_review create_repository push_files
add_issue_comment delete_file request_copilot_review
add_reply_to_pull_request_comment fork_repository sub_issue_write
assign_copilot_to_issue issue_write update_pull_request
create_branch merge_pull_request update_pull_request_branch
create_or_update_file pull_request_review_write
create_pull_request
Missing entirely: get_me, get_file_contents, search_code, search_repositories, search_issues, search_pull_requests, get_issue, list_issues, list_issue_types, get_pull_request, list_pull_requests, get_pull_request_files, get_pull_request_diff, list_commits, get_commit, list_branches, list_tags, list_releases.
Two things make this look like a filtering defect rather than an intended configuration:
- The server's own instruction block references tools that are not exposed. The instructions delivered by the server to the client say, verbatim: "Use 'list_' tools for broad, simple retrieval", "Use 'search_' tools for targeted queries", "Always call 'get_me' first to understand current user permissions and context", "Check 'list_issue_types' first for organizations", and "Use 'search_issues' before creating new issues to avoid duplicates." None of those tools exist in the exposed set. The server is instructing the client to call tools it did not provide.
create_or_update_file documents a shell workaround instead of an MCP path. Its own description reads: "In order to obtain the SHA of original file version before updating, use the following git command: git rev-parse <branch>:<path to file>." Updating an existing file requires a SHA that no exposed tool can return, so the documented escape hatch is to leave MCP entirely.
The practical effect is that the safest operations are blocked while the destructive ones are available. delete_file and merge_pull_request work; reading the file you are about to delete does not. Any edit to an existing file becomes a blind overwrite, because the agent cannot diff against, or even confirm the existence of, the current content.
Whatever the root cause turns out to be, point 1 is a defect on its own: the instruction block should not tell a client to call get_me, list_* and search_* when the active toolset configuration has excluded them. If a restricted toolset is a supported mode, the instructions ought to reflect the tools actually shipped in that mode.
Caveat for triage: I cannot fully rule out that the tool list is filtered by the MCP host rather than by the server. There is a --read-only flag but no inverse "write-only" mode, which is why I suspect an inverted or misapplied toolset filter somewhere in the chain. If the host is the culprit, please close and redirect.
Affected version
GitHub MCP Server
Version: v1.8.0
Commit: ca8ab52dcc45b86fae190398178fd22edb7b1362
Build Date: 2026-07-30T13:28:24Z
Deployment: ghcr.io/github/github-mcp-server:latest running locally in Docker, consumed from the Claude desktop app (Cowork mode).
Steps to reproduce the behavior
- Run
ghcr.io/github/github-mcp-server:latest locally in Docker with a token that has repository read and write scope, and connect it to the Claude desktop app.
- Ask the assistant to read an existing file from a repository you own, for example
read path/to/file.md from <owner>/<repo>.
- Observe that the assistant reports no read tool is available. Inspecting the exposed toolset shows only the 18 mutation tools listed above.
- Ask it to update that same file.
create_or_update_file requires the current blob SHA, which no exposed tool can produce, so the only route is push_files, which overwrites without any comparison against the current content.
Expected vs actual behavior
Expected: a connection granted repository read and write scope exposes both. At minimum get_file_contents, list_commits, list_branches and get_me should be present, so that content can be read before it is modified and so that the tools the server's own instructions tell the client to call actually exist.
Actual: only mutation tools are exposed. Reading any repository content through MCP is impossible. Editing an existing file is a blind write, and the workaround is to keep a local clone, read from disk, and push the full file contents back, which defeats the point of the connector and silently loses any commits made elsewhere since the clone was last pulled.
Logs
No server-side logs were captured; the client surfaces only the tool list and tool results. Evidence is therefore the exposed tool inventory itself, reproduced in full above.
No error is thrown at any point. The failure is silent by nature: the tools are simply not present, so a client that reasons from its available tools concludes that reading is unsupported rather than that something is misconfigured. The one signal that something is wrong is the contradiction between the server's instruction block and the tools it ships, quoted above.
Describe the bug
Running
github-mcp-serverlocally in Docker and consuming it from the Claude desktop app, the exposed toolset is write-only. Every repository read tool is absent, so an agent can commit, push, open PRs and merge, but cannot read a single file, list a branch, or look up an issue it is about to modify.18 tools are exposed, all of them mutations:
Missing entirely:
get_me,get_file_contents,search_code,search_repositories,search_issues,search_pull_requests,get_issue,list_issues,list_issue_types,get_pull_request,list_pull_requests,get_pull_request_files,get_pull_request_diff,list_commits,get_commit,list_branches,list_tags,list_releases.Two things make this look like a filtering defect rather than an intended configuration:
create_or_update_filedocuments a shell workaround instead of an MCP path. Its own description reads: "In order to obtain the SHA of original file version before updating, use the following git command:git rev-parse <branch>:<path to file>." Updating an existing file requires a SHA that no exposed tool can return, so the documented escape hatch is to leave MCP entirely.The practical effect is that the safest operations are blocked while the destructive ones are available.
delete_fileandmerge_pull_requestwork; reading the file you are about to delete does not. Any edit to an existing file becomes a blind overwrite, because the agent cannot diff against, or even confirm the existence of, the current content.Whatever the root cause turns out to be, point 1 is a defect on its own: the instruction block should not tell a client to call
get_me,list_*andsearch_*when the active toolset configuration has excluded them. If a restricted toolset is a supported mode, the instructions ought to reflect the tools actually shipped in that mode.Caveat for triage: I cannot fully rule out that the tool list is filtered by the MCP host rather than by the server. There is a
--read-onlyflag but no inverse "write-only" mode, which is why I suspect an inverted or misapplied toolset filter somewhere in the chain. If the host is the culprit, please close and redirect.Affected version
Deployment:
ghcr.io/github/github-mcp-server:latestrunning locally in Docker, consumed from the Claude desktop app (Cowork mode).Steps to reproduce the behavior
ghcr.io/github/github-mcp-server:latestlocally in Docker with a token that has repository read and write scope, and connect it to the Claude desktop app.read path/to/file.md from <owner>/<repo>.create_or_update_filerequires the current blob SHA, which no exposed tool can produce, so the only route ispush_files, which overwrites without any comparison against the current content.Expected vs actual behavior
Expected: a connection granted repository read and write scope exposes both. At minimum
get_file_contents,list_commits,list_branchesandget_meshould be present, so that content can be read before it is modified and so that the tools the server's own instructions tell the client to call actually exist.Actual: only mutation tools are exposed. Reading any repository content through MCP is impossible. Editing an existing file is a blind write, and the workaround is to keep a local clone, read from disk, and push the full file contents back, which defeats the point of the connector and silently loses any commits made elsewhere since the clone was last pulled.
Logs
No server-side logs were captured; the client surfaces only the tool list and tool results. Evidence is therefore the exposed tool inventory itself, reproduced in full above.
No error is thrown at any point. The failure is silent by nature: the tools are simply not present, so a client that reasons from its available tools concludes that reading is unsupported rather than that something is misconfigured. The one signal that something is wrong is the contradiction between the server's instruction block and the tools it ships, quoted above.