Skip to content

fix(github-mcp): block api_read for a single issue - #20

Merged
Martin Bens (SpiGAndromeda) merged 5 commits into
mainfrom
fix/api-read-issue-and-code-search
Oct 3, 2026
Merged

Martin Bens (SpiGAndromeda) merged 5 commits into
mainfrom
fix/api-read-issue-and-code-search

Conversation

@SpiGAndromeda

@SpiGAndromeda Martin Bens (SpiGAndromeda) commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #14.

With block_api_tool_read: true, check-api-tools.sh let a GET of repos/OWNER/REPO/issues/N through, although issue_view covers it. Its GET patterns covered only issues/N/issue-field-values.

Changes

  • Single issue: a GET of repos/[^/]+/[^/]+/issues/[0-9]+ now blocks and points to issue_view, and to pr_view for a pull request number, since the issues endpoint also answers for pull requests. The same rule applies to the write server's api tool under block_api_tool_write: true.
  • Path-only match: the rule matches the endpoint without its query string and fragment, along whole path segments. A query string, a fragment (which gh drops before sending), or a trailing slash does not get past it, and an issue path inside another endpoint's query, such as search/issues?q=…, does not trigger it. Sub-resources without a dedicated tool, such as issues/N/timeline, stay allowed.
  • owner and the default repo in search_code and search_commits: both used the configured default repo whenever repo was empty, and passed owner to gh only when no default repo was set, so an explicit owner was dropped. They also failed the owner + bare repo split form that the SessionStart directive promises for the search tools. _gh_resolve_search_scope now resolves both: an owner/repo value in repo, then owner joined with a bare repo, then owner alone, then the default repo.
  • owner checks: owner takes the characters _gh_validate_repo allows in an owner, so a user login with an underscore (Enterprise Managed Users, such as octocat_acme) works, while a value such as acme,other, which gh's --owner would read as two owners, fails before gh runs. An owner passed with an owner/repo value must name the same owner, compared without regard to case; before, it was ignored without a message.
  • Docs: the owner and repo descriptions in tools-read.json and REFERENCE.md state the order and the matching rule, and the README says when the default repo applies to these two tools.

Not blocked

  • search/code: search_code sends its search to gh as one exact phrase, so separate keywords, OR/NOT, and path: have no other route. Blocking it would leave those queries without a working tool.

Known limits

  • issue_view does not return closed_by, author_association, or the lock state. With the block on, those fields are not available, and the block message says so.

With block_api_tool_read enabled, check-api-tools.sh let a GET of repos/OWNER/REPO/issues/N and of search/code through, although issue_view and search_code cover both. Its GET patterns covered only issues/N/issue-field-values and no search endpoint.

The issue pattern is anchored at the issue number, so sub-resources without a dedicated tool, such as issues/N/timeline, stay allowed. search/issues, search/repositories, and search/commits stay allowed as well; REFERENCE.md uses search/issues through api_read as an example. The issue_view hint names pr_view for a pull request number, since the issues endpoint also answers for pull requests.

Co-Authored-By: Claude <noreply@anthropic.com>
search_code and search_commits used GH_DEFAULT_REPO whenever repo was empty, and passed owner to gh only when no default repo was configured. With a default repo set, an explicit owner was dropped, and neither tool could search outside that repository. With api_read blocked for search/code, no other tool could either.

The scope now resolves an explicit repo first, then an explicit owner, then the default repo. The owner descriptions in tools-read.json and REFERENCE.md state the precedence.

Co-Authored-By: Claude <noreply@anthropic.com>
The search/code block left queries without a working tool: search_code sends its search to gh as one exact phrase, so separate keywords, OR/NOT, and path: could only go through api_read. check-api-tools.sh no longer blocks search/code.

The issues/N rule matched the whole endpoint, so a fragment, which gh drops before sending, got past it, and an issue path inside another endpoint's query string, such as search/issues?q=..., triggered it. It now matches the path without query and fragment, along whole segments, and also covers a trailing slash. The block message names the fields issue_view cannot return: closed_by, author_association, and the lock state. The CHANGELOG entry now also names block_api_tool_write, under which the write server's api tool applies the same rule.

Tests cover a leading slash, a query string, a fragment, a trailing slash, gh's {owner}/{repo} placeholders, an issue path inside a search query, search/code, and the write tool.

Co-Authored-By: Claude <noreply@anthropic.com>
The SessionStart directive says the search tools accept owner with a bare repo name, but search_code and search_commits sent the bare name to gh as the repo and failed it as not owner/repo. _gh_resolve_search_scope now resolves both tools' scope in one place: an owner/repo value in repo first, then owner joined with a bare repo, then owner alone, then GH_DEFAULT_REPO. owner goes through _gh_validate_org, so a value such as "acme,other", which gh's --owner would read as two owners, or a whitespace-only owner fails before gh runs.

The owner and repo descriptions in tools-read.json and REFERENCE.md describe the split form, and the README says when the default repo applies to these two tools. The tests now check the exact gh arguments for each order of precedence in both tools.

Co-Authored-By: Claude <noreply@anthropic.com>
@SpiGAndromeda Martin Bens (SpiGAndromeda) changed the title fix(github-mcp): block api_read for a single issue and for code search fix(github-mcp): block api_read for a single issue Oct 3, 2026
_gh_resolve_search_scope checked a lone owner with _gh_validate_org, whose pattern allows only letters, digits, and hyphens. That is right for organizations, but a search owner can be a user, and an Enterprise Managed User login such as octocat_acme contains an underscore, so search_code and search_commits rejected a valid owner that gh had accepted before. The owner check now uses the characters _gh_validate_repo allows in an owner, so the lone owner and the owner in the split form follow the same rule, and the error names a user or organization login.

An owner passed with an owner/repo value in repo was ignored, so repo acme/app with owner other searched acme/app without saying so. Such a call now fails before gh runs unless the two owners match, compared without regard to case. REFERENCE.md and the CHANGELOG state the rule.

Co-Authored-By: Claude <noreply@anthropic.com>
@SpiGAndromeda
Martin Bens (SpiGAndromeda) merged commit a9e541b into main Oct 3, 2026
1 check passed
@SpiGAndromeda
Martin Bens (SpiGAndromeda) deleted the fix/api-read-issue-and-code-search branch October 3, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-api-tools.sh: block api_read for issues/N and search/code

1 participant