Use GitHub MCP's Read-only mode

Use GitHub MCP's Read-only mode

I investigated what kind of security feature GitHub MCP's Read-only mode is, including its implementation mechanism. I will introduce how it works as an option for those who want to utilize MCP without granting write permissions.
2026.07.27

This page has been translated by machine translation. View original

GitHub MCP has a feature called Read-only mode

https://github.com/github/github-mcp-server#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

Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

Share this article

AI白書