Use GitHub MCP's 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 is based on what the author confirmed at the time of writing (July 2026)
Since GitHub MCP is an OSS that is actively updated, please check the
repository for the latest implementation
GitHub MCP Documentation
Checking the Server Configuration Guide, the following statement can be found
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.
Apparently this is a mode designed for security-conscious developers to prevent AI from performing writes on its own
It seems useful for cases where you want to use MCP but don't want to grant write permissions in a business 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 isn't the main topic, I'll skip the details, but GitHub MCP is no exception and is implemented in accordance with this standard
Running It
Since GitHub MCP is open source, I built and ran it locally to observe its behavior
After starting the MCP server and completing the initialization process, I send the following tool list retrieval request
// 1. Initialize
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "cli", "version": "0" }
}
}
// 2. Initialization complete notification
{
"jsonrpc":"2.0",
"method":"notifications/initialized"
}
// 3. Get tool list
{
"jsonrpc":"2.0",
"id":2,
"method":"tools/list"
}
At the start of a session, the client holds the content of the tools/list response as the list of tools the AI can execute
This is essentially what 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 the AI cannot perform writes — that is what it means to be Read-only
Sending a Write Request in Read-only Mode
I tested what would happen if I sent 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 the 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 no such tool exists
Looking at the error message, it says the tool doesn't exist rather than being denied
From the above, it is clear that tools that don't exist 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 the implementation uses this flag to determine 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 filtered out — 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 source above is that this is premised on being correctly implemented within GitHub MCP
My impression is that the concern that AI could use write tools if something were accidentally implemented incorrectly cannot be fully dismissed
For local use, running a verified version should allow safe usage
I'm not particularly knowledgeable about security, so this is just my personal opinion, but I feel that combining it with a PAT would be the safer approach
So Why Not Just Use GitHub CLI
If we're going to use it with PAT anyway, I thought it would be no different from letting AI use the gh command — but gh has a bug
For private repositories with CI configured under an organization, running gh pr view (default display) with a fine-grained PAT fails (issue#12597)
※Confirmed as of July 2026
The above bug is about attempting to retrieve CI status via a GraphQL query, which fails because the fine-grained PAT doesn't have the necessary permissions
As a workaround, specifying the JSON fields to retrieve and limiting to the necessary information, or running with the gh api command (REST API), should prevent the error from occurring
However, since GitHub MCP's pull request retrieval tool pull_request_read uses the REST API, it doesn't encounter GraphQL-related errors like gh does
Rather than writing instructions in CLAUDE.md to apply workarounds to prevent errors with the gh pr view command, GitHub MCP simply works without issue
Using pull_request_read in GitHub MCP automatically handles the workaround
Summary
- Looks safe when combined with PAT
- Can be used as a wrapper that works around GitHub CLI bugs
