Use GitHub MCP in Read-only mode
This page has been translated by machine translation. View original
GitHub MCP has a feature called Read-only mode
I was curious about what it is, so I looked into it
※The content of this article reflects what the author verified at the time of writing (July 2026)
Since GitHub MCP is an OSS that is actively updated, please check the latest implementation at the
repository
GitHub MCP Documentation
Looking at the Server Configuration Guide, it contains the following statement:
Note: read-only mode acts as a strict security filter that takes precedence over any other configuration, by disabling write tools even when explicitly requested.
It appears to be a mode designed for security-conscious developers that prevents AI from performing write operations on its own.
This seems useful when you want to use MCP but do not want to grant write permissions in a work context.
Background: How MCP (Model Context Protocol) Works
Basically, when AI communicates with an MCP server, it uses JSON-RPC 2.0.
This specification was apparently proposed by Anthropic as a common standard for connecting AI with external data and tools.
Since this is not the main topic, I will skip the details, but GitHub MCP is no exception and is implemented in accordance with this specification.
Running It
Since GitHub MCP is open source, I built and ran it locally to see how it behaves.
After starting the MCP server and performing the initialization process, I sent the following tool list retrieval request:
// Retrieve tool list
{
"jsonrpc":"2.0",
"id":2,
"method":"tools/list"
}
At session start, the client holds the contents of the tools/list response as the list of tools available for AI execution.
This is essentially the tool list that appears when you go to /mcp -> the relevant MCP -> View tools in Claude Code CLI.
In Read-only mode, write-capable tools are not provided, so AI cannot perform writes — that is what Read-only means.
Sending a Write Request in Read-only Mode
I tested what happens when you send a request using a write tool to an MCP server started in Read-only mode.
There was a write tool called create_repository, so I sent a request to it (※ it does not appear in the tool list in Read-only mode).
Sending a request using tools/call:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "create_repository",
"arguments": { "name": "test-repo" }
}
}
Response:
{
"jsonrpc": "2.0",
"id": 3,
"error": { "code": -32602, "message": "unknown tool \"create_repository\"" }
}
An error was returned saying that such a tool does not exist.
Looking at the error message, it says the tool does not exist rather than being rejected.
From this, we can see that tools not present in the tool list cannot be called.
Looking at the Implementation
I checked the source code to see how read-only tools are determined.
It turns out that each defined tool has a boolean field called ReadOnlyHint, and this flag is what determines whether a tool is read-only.
※The following is an excerpt based on the main branch of github/github-mcp-server (as of July 2026)
// Each tool declares ReadOnlyHint — pkg/github/repositories.go
func CreateRepository(t translations.TranslationHelperFunc) inventory.ServerTool {
return NewTool(
ToolsetMetadataRepos,
mcp.Tool{
Name: "create_repository",
Annotations: &mcp.ToolAnnotations{
Title: t("TOOL_CREATE_REPOSITORY_USER_TITLE", "Create repository"),
ReadOnlyHint: false, // ← write tool (not read-only)
},
// ...
},
// ...
)
}
// Reading that flag — pkg/inventory/server_tool.go
func (st *ServerTool) IsReadOnly() bool {
return st.Tool.Annotations != nil && st.Tool.Annotations.ReadOnlyHint
}
// In read-only mode, tools that are not read-only are excluded by a filter — pkg/inventory/filters.go
func (r *Inventory) isToolEnabled(ctx context.Context, tool *ServerTool) bool {
// ...
if r.readOnly && !tool.IsReadOnly() { // ← tool availability is determined and filtered here
return false
}
// ...
}
Source: github/github-mcp-server (MIT License)
Copyright (c) GitHub, Inc.
What can be understood from the above source is that this is based on the premise that GitHub MCP itself is correctly implemented.
My impression is that the concern that AI could use write tools if there were an implementation mistake cannot be fully dismissed.
(So I recommend verifying safety for the version you use before using it.)
For local use, it seems safe as long as you operate with a version whose safety has been confirmed.
I am not particularly knowledgeable about security, so this is just my personal opinion, but I feel that Read-only mode is a restriction at the tool call level, and it would be safer to use it in combination with permission management through fine-grained PATs.
Then Why Not Just Use GitHub CLI
If we are going to use PATs together anyway, one might think it is no different from letting AI use the gh command — but gh has a bug.
In private repositories under an organization where CI is configured, running gh pr view (default display) with a fine-grained PAT fails (issue#12597)
※Confirmed as of July 2026
The above bug involves a GraphQL query attempting to retrieve CI status, which fails because the fine-grained PAT does not have the necessary permissions.
As a workaround, you can either specify the JSON fields to retrieve and limit to the necessary information, or use the gh api command (REST API) to avoid the error.
However, GitHub MCP's pull request retrieval tool pull_request_read uses the REST API, so GraphQL-related errors like those in gh do not occur.
Without writing instructions to Claude to avoid errors with gh pr view, GitHub MCP simply works as expected.
Using pull_request_read in GitHub MCP automatically handles the workaround for you.
Summary
- Seems safe when used in combination with PATs
- Can be used as a wrapper that works around GitHub CLI bugs
