Skip to content

Split granular issue and pull request tools so each only acts on its own kind - #3352

Open
timrogers wants to merge 2 commits into
mainfrom
granular-issue-pr-split
Open

timrogers wants to merge 2 commits into
mainfrom
granular-issue-pr-split

Conversation

@timrogers

@timrogers timrogers commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR updates the issues_granular and pull_requests_granular feature flag behavior so that:

  • The granular issues tools only operate on issues, not on PRs (e.g. update_issue_assignees only works with PRs)
  • We have companion granular tools for PRs (e.g. update_pull_request_assignees, rather than relying on update_issue_assignees)

This does not affect the default base behavior used by normal customers - only opt in behavior used by Copilot Automations behind the issues_granular and pull_requests_granular feature flags.

Why

The issues_granular and pull_requests_granular tools are used in Copilot Automations, which are fully autonomous.

We want to allow users to have full and maximally granular control of what their automations can do with an allowlist of tools. That promise only works if each tool does what its name says.

Today it doesn't. GitHub's issues REST API treats every pull request as an issue, so tools like update_issue_labels, update_issue_state, update_issue_title and add_issue_comment also work on PRs when given a PR number.

Allowlisting a bunch of tools for triaging issues therefore also lets an automation relabel, retitle, close or comment on pull requests, and we couldn't grant one without the other.

After this change, issue tools refuse PRs and PR tools refuse issues. Each capability can be allowed on its own.

What changed

  • Issue tools only accept issues. update_issue_{title,body,assignees,labels,milestone,type,state} and {add,remove}_issue_reaction look up the number first and return an error, without changing anything, if it's a pull request. The error points to the matching PR tool. {add,remove}_issue_comment_reaction do the same for comments, by checking which issue or PR the comment belongs to.
  • New PR tools (behind pull_requests_granular). Each one checks that the target really is a PR (or a PR comment):
    • update_pull_request_assignees, update_pull_request_labels, update_pull_request_milestone
    • add_pull_request_reaction, remove_pull_request_reaction
    • add_pull_request_comment, update_pull_request_comment
    • add_pull_request_comment_reaction, remove_pull_request_comment_reaction (for conversation comments; the existing *_review_comment_reaction tools still handle review comments)
  • Comment writing. add_issue_comment and update_issue_comment are shared by the default and granular tool sets. They become issue-only only when both issues_granular and pull_requests_granular are enabled, so enabling one flag never takes away PR commenting. The version used by default is unchanged.
  • New exported helpers EnsureIssue, EnsurePullRequest, EnsureIssueComment and EnsurePullRequestComment, so the remote server can use the same checks.

MCP impact

  • No tool or API changes
  • Tool schema or behavior changed: the issue granular tools listed above now reject pull requests, and their descriptions and titles say so. add_issue_reaction, remove_issue_reaction and the issue comment-reaction tools previously accepted both issues and PRs.
  • New tool added: the 9 pull request tools listed above.

Prompts tested (tool changes only)

Covered by unit tests only; I haven't run these prompts against a live server.

Security / limits

  • No security or limits impact
  • Auth / permissions considered: this tightens what each tool can do, which is what makes tool allowlists reliable. Required scopes are unchanged.
  • Data exposure, filtering, or token/size limits considered

The checks add one extra GET per call (two for comment-ID tools). Checking before writing means a rejected call never changes anything.

Tool renaming

  • I am renaming tools as part of this PR (e.g. a part of a consolidation effort)
    • I have added the new tool aliases in deprecated_tool_aliases.go
  • I am not renaming tools as part of this PR

Lint & tests

  • Linted locally with ./script/lint: ran gofmt -s and go vet. golangci-lint v2.9.0 couldn't type-check under the local Go 1.27, so CI's lint job covers it.
  • Tested locally with ./script/test

New tests check that each restricted tool rejects the wrong kind without sending any write, that each new PR tool works on a PR, and which tool versions are active under all four flag combinations.

Docs

  • Not needed
  • Updated (README / docs / examples): regenerated docs/feature-flags.md and added a hand-written note on the behavior when both flags are on.

…own kind

GitHub's issues API accepts pull request numbers, so issue tools such as
update_issue_labels could also modify pull requests. That makes it
impossible to allowlist issue-only capabilities for autonomous agents.

- Issue granular tools now verify the target is an issue (or an issue
  comment) and reject pull requests before making any change.
- Add pull request counterparts: update_pull_request_{assignees,labels,
  milestone}, {add,remove}_pull_request_reaction, add/update
  pull_request_comment, and {add,remove}_pull_request_comment_reaction.
- When both granular flags are enabled, add_issue_comment and
  update_issue_comment are served as issue-only variants.
- Export EnsureIssue, EnsurePullRequest, EnsureIssueComment and
  EnsurePullRequestComment helpers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@timrogers
timrogers marked this pull request as ready for review September 30, 2026 05:54
@timrogers
timrogers requested a review from a team as a code owner September 30, 2026 05:54
Copilot AI balanced review requested due to automatic review settings September 30, 2026 05:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Error-response bodies leak, required arrays can be omitted to clear data, and variant snapshot coverage is incomplete.

Review effort: Balanced
Findings: 2 High severity · 3 Medium severity · 1 Low severity

Open (6)
What changed in this PR

Splits granular issue and pull-request operations to enforce kind-specific automation allowlists.

Changes:

  • Adds issue/PR validation helpers and conditional comment-tool variants.
  • Adds nine granular pull-request companion tools.
  • Updates tests, schema snapshots, and feature-flag documentation.
File Description
pkg/​github/​tools.go Registers new tool variants.
pkg/​github/​pullrequests_granular.go Implements granular PR tools.
pkg/​github/​issues.go Adds issue-only comment variants.
pkg/​github/​issues_granular.go Enforces issue/PR kind checks.
pkg/​github/​granular_tools_test.go Tests kind-specific behavior.
pkg/​github/​feature_flags.go Defines combined-flag rules.
pkg/​github/​feature_flags_test.go Tests flag combinations.
pkg/​github/​__toolsnaps__/​update_pull_request_milestone.snap Snapshots PR milestone tool.
pkg/​github/​__toolsnaps__/​update_pull_request_labels.snap Snapshots PR labels tool.
pkg/​github/​__toolsnaps__/​update_pull_request_comment.snap Snapshots PR comment update.
pkg/​github/​__toolsnaps__/​update_pull_request_assignees.snap Snapshots PR assignees tool.
pkg/​github/​__toolsnaps__/​update_issue_type.snap Updates issue-type description.
pkg/​github/​__toolsnaps__/​update_issue_title.snap Updates issue-title description.
pkg/​github/​__toolsnaps__/​update_issue_state.snap Updates issue-state description.
pkg/​github/​__toolsnaps__/​update_issue_milestone.snap Updates issue-milestone description.
pkg/​github/​__toolsnaps__/​update_issue_labels.snap Updates issue-label description.
pkg/​github/​__toolsnaps__/​update_issue_body.snap Updates issue-body description.
pkg/​github/​__toolsnaps__/​update_issue_assignees.snap Updates issue-assignees description.
pkg/​github/​__toolsnaps__/​remove_pull_request_reaction.snap Snapshots PR reaction removal.
pkg/​github/​__toolsnaps__/​remove_pull_request_comment_reaction.snap Snapshots PR comment-reaction removal.
pkg/​github/​__toolsnaps__/​remove_issue_reaction.snap Narrows issue-reaction removal schema.
pkg/​github/​__toolsnaps__/​remove_issue_comment_reaction.snap Narrows issue comment-reaction schema.
pkg/​github/​__toolsnaps__/​add_pull_request_reaction.snap Snapshots PR reaction addition.
pkg/​github/​__toolsnaps__/​add_pull_request_comment.snap Snapshots PR commenting.
pkg/​github/​__toolsnaps__/​add_pull_request_comment_reaction.snap Snapshots PR comment reactions.
pkg/​github/​__toolsnaps__/​add_issue_reaction.snap Narrows issue-reaction schema.
pkg/​github/​__toolsnaps__/​add_issue_comment_reaction.snap Narrows issue comment-reaction schema.
docs/​feature-flags.md Documents split tool behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1087 to +1093
func(args map[string]any) (gogithub.UpdateIssueRequest, error) {
assignees, err := OptionalStringArrayParam(args, "assignees")
if err != nil {
return gogithub.UpdateIssueRequest{}, err
}
return gogithub.UpdateIssueRequest{Assignees: assignees}, nil
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+998888688333 @VorDvoRr пиши звони

Comment on lines +1111 to +1117
func(args map[string]any) (gogithub.UpdateIssueRequest, error) {
labels, err := OptionalStringArrayParam(args, "labels")
if err != nil {
return gogithub.UpdateIssueRequest{}, err
}
return gogithub.UpdateIssueRequest{Labels: labels}, nil
},
Comment thread pkg/github/issues_granular.go
Comment thread pkg/github/issues_granular.go Outdated
Comment thread pkg/github/pullrequests_granular.go Outdated
Comment thread pkg/github/tools.go
@timrogers

This comment was marked as outdated.

Co-authored-by: timrogers <116134+timrogers@users.noreply.github.com>

This comment was marked as outdated.

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.

4 participants