Commit Graph

480 Commits

Author SHA1 Message Date
Sam Morrow accc2e0970 fix(actions): avoid malformed response on log download failure
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9cdeefb9-91cd-4f65-8eb2-089c9e00a2b8
2026-08-14 10:53:39 +02:00
Bryan Zwicker 2198e8599b Add visible fields to project views (#2988)
* Add visible fields to project views

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 1421a5d5-fdce-4c0e-9528-56d555ec30d4

* Fail fast and surface orphaned views on project view writes

Reject roadmap layouts before enumerating project fields in both the
create and update paths, and verify view ownership before resolving
visible fields on update, so rejected requests no longer pay for a
paginated field listing.

Skip the follow-up filter mutation when the filter is explicitly null,
since a new view has no filter to clear, and include the created view ID
when cleanup after a failed filter mutation also fails so the caller can
recover the orphaned view.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1421a5d5-fdce-4c0e-9528-56d555ec30d4
2026-08-12 09:40:51 -04:00
Bryan Zwicker d6cab9757a Add basic project view management (#2961)
* Add basic project view management

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c6f8ede6-efee-4191-900d-59a1bb0af000

* Harden project view mutations

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c6f8ede6-efee-4191-900d-59a1bb0af000

* Resolve project view fields by name

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c6f8ede6-efee-4191-900d-59a1bb0af000

* Clear project view filters with explicit null

Align the filter parameter with the nullable-parameter convention: omit
to preserve, pass null to clear. Empty strings are now rejected rather
than treated as a clear sentinel. The GraphQL and REST wire format is
unchanged, since the API still clears a filter with an empty string.

Also replace the "<nil>" string comparison in deleteProjectView with a
direct nil check on the returned ID.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Use caller-specific project field hints

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c6f8ede6-efee-4191-900d-59a1bb0af000

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c6f8ede6-efee-4191-900d-59a1bb0af000
2026-08-12 08:41:33 -04:00
Tommaso Moro ff15f6825d Use minimal types for tool responses (#3055)
Return compact response shapes for pull request statuses, review comment replies, and individual workflow runs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d6786153-698a-4563-97ad-a8221c40e306
2026-08-12 12:33:51 +01:00
Tommaso Moro eff4c3c041 Minimize Actions workflow list responses (#3047)
Return compact response types for workflow run and workflow job lists while retaining diagnostic, step, and runner metadata.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0eecbca7-7271-4a04-8d28-d952c27ed9c1
2026-08-10 15:13:30 +01:00
Iulia Bejan cdfa34e0a9 Order list_label results by issue count (descending) (#2974)
* Order list_label results by issue count (descending)

Sends orderBy: {field: ISSUE_COUNT, direction: DESC} on the GraphQL
labels query so the most-used labels (by issue count) are returned
first. ISSUE_COUNT is accepted by the GitHub GraphQL API but is not
part of the public schema docs or the githubv4 client library's
LabelOrderField constants, so it is defined locally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* regen docs

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-10 10:35:52 +02:00
Bryan Zwicker eb4c099e05 Support singular Project Issue Field updates (#2941)
* Implement batch project write engine

Resolve and validate shared field updates and item references before executing ordered, chunked GraphQL writes with explicit ambiguous outcomes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0

* Expose update_project_items

Add the public projects_write contract, routing, handler coverage, and generated documentation for shared field updates across batches of up to 50 items.

Co-authored-by: Lizeth Vera <47796851+veralizeth@users.noreply.github.com>

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0

* Classify batch resolution failures

Use a neutral code for non-structured lookup failures while preserving structured resolution details.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0

* Resolve issue references concurrently

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0
Copilot-Session: 5709a470-df75-43ec-9a9c-98868e6065d2

* Add singular Issue Field project updates

Support name-based attached Issue Field updates for singular Project items while preserving existing read and standard field behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4c94f3ce-c04a-482f-830b-ab85abc3f6e4

* Preserve iteration project field updates

Bypass Issue Field metadata resolution for standard field data types and recognize exact missing fragment-type schema errors.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4c94f3ce-c04a-482f-830b-ab85abc3f6e4

* Adding GraphQL-Features: update_issue_suggestions

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Lizeth Vera <47796851+veralizeth@users.noreply.github.com>
Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0
Copilot-Session: 5709a470-df75-43ec-9a9c-98868e6065d2
Copilot-Session: 4c94f3ce-c04a-482f-830b-ab85abc3f6e4
2026-08-07 08:31:43 -04:00
Michael Jacholke 1b3f89a90a Add non-default find_duplicate tool gated by duplicate_detection flag (#3020)
* Add non-default find_duplicate tool gated by duplicate_detection flag

* Trim find_duplicate output to spec fields and relax confidence_threshold bounds

* Attach repo-visibility IFC label to find_duplicate results
2026-08-07 09:45:49 +01:00
Bryan Zwicker e7f7bb8b31 Support removing issue types (#2999)
* Render union types in generated docs

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: ea8faa5c-7f26-4e2d-bf9c-6f0b5f173e8c

* Support clearing issue types with issue_write

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: ea8faa5c-7f26-4e2d-bf9c-6f0b5f173e8c

* Support clearing issue types with granular tool

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: ea8faa5c-7f26-4e2d-bf9c-6f0b5f173e8c

* Validate duplicate closures before updates

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: ea8faa5c-7f26-4e2d-bf9c-6f0b5f173e8c

---------

Copilot-Session: ea8faa5c-7f26-4e2d-bf9c-6f0b5f173e8c
2026-08-06 20:25:58 -04:00
Kelsey Myers f3cb662c25 Make search_issues semantic by default (#2964)
* Make search_issues semantic by default

* initialize description depending on the host

---------

Co-authored-by: Iulia B <iulia-b@github.com>
Co-authored-by: Iulia Bejan <64602043+iulia-b@users.noreply.github.com>
2026-08-06 16:51:13 +02:00
Bryan Zwicker e6e3a4e841 Return closing pull requests from issue_read (#3006)
* Return closing pull requests from issue_read

Answering "is there a PR that closes this issue?" previously required
listing pull requests and grepping their bodies for closing keywords,
which is expensive and unreliable. GraphQL already exposes
Issue.closedByPullRequestsReferences.

Add it to the existing issue_read `get` enrichment query so the answer
comes back in the same round-trip as the hierarchy signals, as a compact
`closed_by_pull_requests` list. An enriched issue with no closing pull
requests serializes an explicit empty list so an agent can stop looking.

Lockdown mode filters references whose author cannot be verified as safe
content, mirroring the existing parent reference handling.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a0b58914-0d94-47a9-8229-c0ef7e32e69f

* Cap embedded closing pull requests and report the total

This enrichment runs on every issue_read get, so embedding up to 25
references costs more than the common case is worth. Embed at most 5,
keeping orderByState so open pull requests are the ones that survive.

Select totalCount alongside the nodes and return the summary as an
object of total_count plus references, so the rare issue with more than
five linked pull requests cannot be read as a complete list. The common
zero-to-two case stays compact and an empty result stays definitive.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a0b58914-0d94-47a9-8229-c0ef7e32e69f

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a0b58914-0d94-47a9-8229-c0ef7e32e69f
2026-08-06 09:04:10 +01:00
eric sciple 3778a41476 Clarify that create_or_update_file content is plain text (#2983)
The content parameter is passed to the API as plain text and the server
base64-encodes it, but the description said only "Content of the file".
The REST endpoint this wraps documents its own content field as base64,
so a model reading the tool description has a strong reason to encode the
content itself. When it does, the server encodes again and the file is
committed containing base64 text. Every layer reports success.

Describe the value by how it should end up on disk rather than by what
not to do, so a file whose contents are legitimately base64 is still
unambiguous, and name the encoding step so the conflict with the REST
API docs is resolved rather than merely overridden.
2026-07-31 08:49:14 +01:00
Sam Morrow ca8ab52dcc test: assert advertised capabilities omit list-changed
Documentation Check / docs-check (push) Has been cancelled
golangci-lint / lint (push) Has been cancelled
GoReleaser Release / release (push) Has been cancelled
Publish to MCP Registry / publish (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
MCP Server Diff / mcp-diff (push) Has been cancelled
MCP Server Diff / mcp-diff-http (push) Has been cancelled
Build and Test Go Project / build (macos-latest) (push) Has been cancelled
Build and Test Go Project / build (ubuntu-latest) (push) Has been cancelled
Build and Test Go Project / build (windows-latest) (push) Has been cancelled
Docker / build (push) Has been cancelled
Add a regression test locking in the capability contract set by NewMCPServer:
tools, prompts, and resources are advertised without list-changed
notifications, the deprecated logging capability is not advertised, and the
inferred completions capability is preserved. Covers both the stdio path (full
inventory, items present) and the HTTP path (inventory emptied for the
discovery request), which share the same NewMCPServer entry point.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 95b8432c-f280-472e-a242-d3ca6dc31f19
2026-07-30 15:22:06 +02:00
Sam Morrow ea8099d7b2 fix: don't advertise unsupported list-changed capabilities
The server exposes a static set of tools, prompts, and resources and never
mutates them at runtime, so it never emits list_changed notifications. When
capabilities are left unset, the go-sdk infers listChanged:true from the
presence of items and advertises tools/prompts/resources list-change support
we don't actually provide - and the 2026-07-28 spec (subscriptions/listen)
tightens expectations around this.

Declare empty tools/prompts/resources capabilities in NewMCPServer so both the
stdio and remote servers advertise honestly. The remote HTTP handler already
set these explicitly; that duplication is now removed in favour of the shared
default, leaving only the remote-specific schema cache.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 95b8432c-f280-472e-a242-d3ca6dc31f19
2026-07-30 15:22:06 +02:00
Lizeth Vera d080b23f59 Add batched update_project_items writes via GraphQL (#2903)
* Implement batch project write engine

Resolve and validate shared field updates and item references before executing ordered, chunked GraphQL writes with explicit ambiguous outcomes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0

* Expose update_project_items

Add the public projects_write contract, routing, handler coverage, and generated documentation for shared field updates across batches of up to 50 items.

Co-authored-by: Lizeth Vera <47796851+veralizeth@users.noreply.github.com>

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0

* Classify batch resolution failures

Use a neutral code for non-structured lookup failures while preserving structured resolution details.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0

* Resolve issue references concurrently

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0
Copilot-Session: 5709a470-df75-43ec-9a9c-98868e6065d2

---------

Co-authored-by: Bryan Zwicker <zwick@github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0
Copilot-Session: 5709a470-df75-43ec-9a9c-98868e6065d2
2026-07-29 15:57:26 -04:00
Tommaso Moro 456fae9d04 Make fields parameter available by default (#2952)
* Promote fields parameter beyond Insiders

Keep fields_param as an independently controlled feature flag while removing it from the Insiders expansion.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Make fields parameter available by default

Remove the fields_param feature flag and legacy tool variants so selected read tools always advertise and honor fields.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5c43cc70-27b5-47b4-bbd1-99d20f42d61b

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5c43cc70-27b5-47b4-bbd1-99d20f42d61b
2026-07-29 14:06:03 +01:00
Kelsey Myers 1348c479bc Bump go-github to pick up SearchType support (#2972)
* Bump go-github for search_type support

* chore: regenerate license files

Auto-generated by license-check workflow

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-29 12:01:20 +01:00
Connor Peet a217a7f43a Add MCP App form deferral opt-out
Allow clients to keep MCP App views enabled while making form-backed write tools execute directly when explicitly configured.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-22 19:02:50 +02:00
Sam Morrow ea4e3960b8 refactor(auth): isolate GitHub App auth to stdio startup
Keep PEM loading and installation-token provider construction at the CLI leaf, then pass a generic refreshing token provider through the existing HTTP transports. Rebase the feature onto current main and keep the HTTP command unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 646357dd-c89f-4973-9a5c-e6c5fc18818c
2026-07-22 18:51:35 +02:00
Bryan Zwicker 4ed4f816cc Extract aliased project mutation primitive (#2923)
* Extract aliased project mutation primitive

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d7dc302d-e6f2-41e9-a2c8-ed598de47067

* Test partial GraphQL mutation data

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4d3df48a-5aa0-4cf0-a067-4aa5618c2887

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-07-22 15:52:17 +01:00
Bryan Zwicker de310d4806 Address CCR feedback: assert resolved field NodeID for all variants; rename item resolver test
- Assert field.NodeID in Test_ResolveProjectFieldByName_Success.
- Add Test_ResolveProjectFieldByName_NodeIDsForAllVariants covering
  single-select, iteration, and generic fields (asserts NodeID + DataType).
- Rename Test_ResolveProjectItemIDByIssueNumber_Success to
  Test_ResolveProjectItemByIssueNumber_Success to match the resolver it calls.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 89f897b0-115f-4435-a071-46fb6c49be86
2026-07-22 14:01:32 +02:00
Bryan Zwicker 4c68b1b640 Add node IDs to project resolver results
Split out of #2903 as a small prerequisite refactor.

- Add a NodeID field to ResolvedField, populated for all three field
  variants in listAllProjectFields.
- Refactor resolveProjectItemIDByIssueNumber into a thin wrapper over a
  new resolveProjectItemByIssueNumber that also returns the item node ID,
  delegating to resolveProjectItemByIssueNumberWithProjectID for an
  already-resolved project ID. The projectItems query now selects the
  item node ID alongside its full database ID.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 89f897b0-115f-4435-a071-46fb6c49be86
2026-07-22 14:01:32 +02:00
Boaz Reicher 9d130049e9 Add opt-in intent-aware Copilot issue assignment tool (#2909)
* Add opt-in intent-aware Copilot issue assignment tool

Add `assign_copilot_to_issue_with_intent` in a new non-default toolset
`copilot_issue_intents`. The tool uses GraphQL's object-form
`assignees: [AssigneeUpdateInput!]` so intent metadata (rationale,
confidence, is_suggestion) is attached to the Copilot entry only, while
existing assignees are preserved.

- Reuses the existing Copilot actor lookup, target-repository
  resolution, base_ref, custom_instructions, GraphQL-Features header,
  and direct-assignment PR polling behavior.
- `is_suggestion: true` records a pending Copilot assignment intent,
  returns a suggestion-shaped result, and does not launch Copilot or
  poll for a linked PR.
- `rationale` is capped at 280 characters (schema + runtime);
  `confidence` is validated against `LOW`/`MEDIUM`/`HIGH`.
- Toolset is non-default so its inputs do not add schema bloat to the
  default tool surface; available via `copilot_issue_intents`,
  `all`, or explicit tool selection.

Includes unit tests for direct assignment (with existing assignees and
with base_ref/custom_instructions), the suggestion path, invalid
rationale length, invalid confidence, and Copilot-not-available; a
generated toolsnap; regenerated docs; and an e2e test for the
suggestion path.

Refs: github/plan-track-agentic-toolkit#683

* Address review: tighten description, require intent fields

Per review feedback on #2909:

- Trim the tool description to mirror assign_copilot_to_issue and add
  "Prefer this tool over assign_copilot_to_issue when available",
  removing the verbose is_suggestion narrative from the schema.
- Make rationale, confidence, and is_suggestion required inputs (schema
  and runtime). is_suggestion is now always sent explicitly on the
  Copilot AssigneeUpdateInput entry.
- Update unit tests to supply the newly-required fields and cover the
  missing-rationale and missing-confidence rejection paths.
- Regenerate toolsnap and README.

* Address review: dedupe copilot, require is_suggestion, update doc link

Per @RossTarrant review feedback on #2909:

- Reject requests where `is_suggestion` is omitted from the raw args
  before decoding. `mapstructure.WeakDecode` defaults missing bools
  to false, which would silently launch Copilot instead of recording a
  suggestion. Presence-check the raw map so callers make the choice
  explicit.
- Skip the copilot-swe-agent actor when copying existing assignees so
  we don't send its actorId twice (once without metadata and once with
  intent metadata) when Copilot is already assigned.
- Update the stale about-assigning-tasks-to-copilot reference to the
  redirect target (about-cloud-agent). Applied to the const message,
  both tool descriptions, and the e2e/unit-test literals that assert
  on that message.

New unit tests cover the missing-is_suggestion rejection and the
copilot-dedup behavior. Regenerated toolsnaps.
2026-07-21 02:27:09 -07:00
Bryan Zwicker 6a44cf24af Paginate project item lookup (#2914)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 1a67631b-dc18-448a-8be5-81110bfd543a

Co-authored-by: Ross Tarrant <rosstarrant@github.com>
2026-07-21 09:25:24 +01:00
石岳峰 870f3c710a fix(labels): add DestructiveHint to label_write tool (#2763) 2026-07-16 16:39:04 +02:00
kerobbi 334aac0d2b remove get_check_runs gate 2026-07-16 13:53:36 +02:00
kerobbi 8f6aa8e902 fix flaky test 2026-07-16 13:53:36 +02:00
kerobbi 0ad8cc67f3 enforce lockdown on pr diff/files/check_runs and fix reviews fail-open 2026-07-16 13:53:36 +02:00
kerobbi 5a0beacbcb test lockdown lookup-failure returns tool-result error 2026-07-16 13:53:36 +02:00
kerobbi b463b647ce centralise lockdown checks and default fail closed on empty author 2026-07-16 13:53:36 +02:00
Sam Morrow 05dc8a6e35 fix(oauth): harden multi-round-trip authorization
Move OAuth interception into tool-handler middleware so go-sdk finalizes
multi-round-trip results with resultType input_required. Correlate responses to
a per-flow ID, retire cancellations synchronously, and ignore late completions
from stale flows.

Also preserve actionable URLs for form-only clients and add wire-level,
concurrency, and real manager lifecycle coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 18e70efa-1b2d-4290-ba51-b82998db4ff8
2026-07-15 15:35:25 +02:00
tommaso-moro 7629b6e733 Enable fields param in Insiders mode
Docker / build (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Build and Test Go Project / build (macos-latest) (push) Has been cancelled
Build and Test Go Project / build (ubuntu-latest) (push) Has been cancelled
Build and Test Go Project / build (windows-latest) (push) Has been cancelled
GoReleaser Release / release (push) Has been cancelled
MCP Server Diff / mcp-diff (push) Has been cancelled
MCP Server Diff / mcp-diff-http (push) Has been cancelled
Publish to MCP Registry / publish (push) Has been cancelled
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-07-15 13:01:54 +02:00
Lizeth Vera c36e4e4493 MCP: name-based resolution for Projects fields (#2760)
* Let agents address Project fields, single-select options, and item field values by name through the GitHub MCP server

* Adding a method to resolve params resolveItemIDFromIssueArgs

* Adding the DatabaseID to be able to match the graphQL

* Changing the parsing to strconv.ParseInt

* Changing description.

* update readme

* Fixing copilot comments

* Adding Case-insensitive matching  and fields + field_names guard

* running snaps and readme
2026-07-10 10:25:32 -07:00
Tommaso Moro 439ec711fa Add fields param to six more list/search tools (#2810)
* Add fields param to six more list/search tools

Extend the optional `fields` response-filtering parameter (gated behind the
`fields_param` feature flag, with adoption/savings telemetry) to six more read
tools, following the dual-variant pattern already used by search_code and
get_file_contents:

- list_issues, list_pull_requests, list_commits, list_releases
- search_issues, search_pull_requests

For each tool, `X` is the flag-enabled variant that advertises `fields` and
filters each result item to the requested subset, while `LegacyX` exposes the
original schema and never filters, acting as a kill switch when the flag is off.
Exactly one variant survives inventory filtering for any flag state via mutually
exclusive FeatureFlagEnable / FeatureFlagDisable annotations. Wrapped responses
(list_issues, search_issues, search_pull_requests) preserve their count /
pagination envelope and only filter the item list; bare-array responses keep
their array shape.

Filtering reuses the shared filterEachField helper. A new fieldsSchemaProperty
helper builds the `fields` schema (search_code and get_file_contents now use it
too), and a shared recordFieldsUsageFor helper centralizes the telemetry
full-size computation. Each field enum lists only the JSON fields the specific
tool actually emits: list_commits omits stats/files (requested without per-file
detail) and list_issues lists only the fields its GraphQL fragment populates.

Adds per-tool field-filtering, telemetry, and Legacy definition tests, extends
the mutual-exclusivity gating test to all eight gated tools, and regenerates the
`_ff_fields_param` toolsnaps and feature-flag docs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Remove duplicate tool doc comments

Address review: drop the leftover one-line doc comment above each dual-variant
tool constructor (list_issues, list_pull_requests, list_commits, list_releases,
search_issues, search_pull_requests); the detailed variant comment remains.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-07-10 13:09:34 +01:00
Tommaso Moro 3b8ff504c0 Add fields param to search_code and get_file_contents (#2775)
* Add fields param to search_code and get_file_contents

Add an optional `fields` array parameter to the `search_code` and
`get_file_contents` tools so callers can request only the fields they
need, reducing tool response size and context usage.

- search_code: filters each result item to the selected fields while
  preserving the total_count / incomplete_results wrapper.
- get_file_contents: filters each directory entry when listing a
  directory; ignored for single-file responses.

Adds shared filterFields / filterEachField helpers and per-tool field
enums, plus unit tests and regenerated toolsnaps and docs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Gate fields param behind fields_param flag and add usage telemetry

Register search_code and get_file_contents as two mutually exclusive
variants gated by the new `fields_param` feature flag, following the
existing dual-variant flag pattern:

- The flag-enabled variant advertises the optional `fields` parameter and
  filters each result to the requested subset. It owns the
  `<tool>_ff_fields_param` toolsnap.
- The Legacy* variant exposes the original schema with no `fields`
  parameter and never filters, acting as a kill switch when the flag is
  off. It owns the canonical toolsnap.

Add best-effort, low-cardinality telemetry at each tool's filter point to
measure adoption and realized savings:

- `mcp.fields.tool_call` (increment) tagged by tool and whether the
  response was filtered.
- `mcp.fields.bytes_full` / `bytes_sent` / `bytes_saved` (counters) tagged
  by tool, emitted only when a response was filtered.

Tags are limited to `tool` and `filtered` to bound cardinality; repo,
owner, user, query, and the requested field list are never tagged. The
local server discards these via the noop metrics sink, while hosted
deployments inject a real sink. Metrics accessors now fall back to a noop
sink when no exporter is configured so emitting telemetry never panics.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Drop mcp.fields.bytes_saved metric

Remove the mcp.fields.bytes_saved counter. It is derivable on the
dashboard from the two remaining byte counters, since
sum(bytes_full) - sum(bytes_sent) equals the total saved at any rollup,
so emitting it separately is redundant. Keeping only bytes_full and
bytes_sent shrinks the emitted telemetry surface.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-10 09:50:47 +01:00
Tommaso Moro 6787f08569 Reimplement issue dependencies on go-github v89 REST API (#2839)
* Reimplement issue dependencies on go-github v89 REST API

Issue dependencies (blocked_by / blocking) were implemented on GraphQL via
githubv4. Because the pinned githubv4 library predates the dependency
mutations, the code hand-declared AddBlockedByInput / RemoveBlockedByInput
and resolved issue numbers to node IDs with a custom aliased query.

go-github v89 adds first-class REST methods (ListBlockedBy, ListBlocking,
AddBlockedBy, RemoveBlockedBy), so switch issue_dependency_read and
issue_dependency_write to those. This removes the workaround, aligns the
tools with the rest of the REST-based issue tooling, and simplifies tests.

- Rewrite issue_dependencies.go on REST: page-based pagination for reads,
  and a single Issues.Get to resolve the blocking issue's database ID for
  writes. Preserve the tool surface, self-dependency guard and cross-repo
  support.
- Rewrite the issue dependency tests on the REST mock helpers.
- Regenerate the read toolsnap (cursor -> page pagination) and docs.

Refs #950

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Normalize issue dependency ref state to match other tools

The REST issue-dependency endpoints return lower-case issue states
(open/closed), whereas the previous GraphQL implementation and the
sibling get_parent tool populate MinimalIssueRef.State from the GraphQL
IssueState enum (OPEN/CLOSED). Upper-case the state in
issueToDependencyRef so the field stays consistent across every tool that
emits a MinimalIssueRef, and guard against a nil issue while here.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-07-09 11:39:20 +01:00
Tommaso Moro 778f5bb6a3 chore(deps): bump go-github v87 → v89 and resolve breaking changes (#2840)
* chore(deps): bump go-github v87 -> v89 and resolve breaking changes

Bumps google/go-github from v87 to v89 across the module and fixes the
resulting breaking changes. No tool or behavior changes.

- Rewrite all import paths go-github/v87 -> go-github/v89.
- gists.go: Gists.Create now takes CreateGistRequest by value and
  Gists.Edit is renamed to Gists.Update taking UpdateGistRequest.
- repositories_test.go: adapt to RepositoryRelease fields that became
  value types in v89 (ID, TagName, Draft).
- Regenerate third-party license files for the new module path.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Don't clear gist description on update when omitted

update_gist always sent Description as a pointer to the OptionalParam
zero value (""), so omitting description would overwrite an existing gist
description with an empty string. Only set UpdateGistRequest.Description
when the caller actually provided the argument; an explicit empty string
still clears it. Adds a test asserting the description key is absent from
the PATCH body when omitted and present when set.

This addresses a pre-existing behavior surfaced while migrating to the
v89 gist request types.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-07-08 15:08:59 +01:00
Bryan Zwicker b7bc3dccee Enrich issue_read get with hierarchy relationship signals (#2764)
* Enrich issue_read get with hierarchy relationship signals

The default issue_read `get` payload surfaced no hierarchy data, forcing
agents to drop to raw REST (parent_issue_url) or scan sibling sub_issues
to discover relationships. Enrich `get` with a layered, zero-extra-round-trip
relationship signal derived from a single combined GraphQL query:

- has_parent / has_children: cheap, always-emitted routing booleans
  (addresses Sam Morrow's #2726 review note).
- parent: compact ref (number/title/state/url/repository) mirroring the
  existing get_parent payload keys; omitted when there is no parent.
- sub_issues_summary: native subIssuesSummary counts (total/completed/
  percent_completed); omitted when there are no sub-issues.

The single-issue field-values GraphQL call in GetIssue is replaced by one
combined query (fetchIssueReadEnrichment) returning field values + parent +
subIssuesSummary, so `get` adds no round-trips. Enrichment is best-effort:
a query failure still returns the base issue and never fails `get`. Parent
titles are sanitized (parent may be cross-repo) and redacted under lockdown
mode unless the parent content can be verified as safe; numeric/structural
fields and counts stay intact. get_parent / sub_issue_write behavior is
unchanged; tool descriptions clarify hierarchy is read here but written via
sub_issue_write (no writable parent field).

Refs github/planning-tracking#3306

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Tighten hierarchy tool wording

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Omit unverified parent under lockdown instead of redacting title

Align issue_read get parent enrichment with the codebase's existing
lockdown patterns: rather than introducing a third, redaction-with-sentinel
behavior, omit the whole parent reference when its (possibly cross-repo)
content cannot be verified safe. This mirrors how unsafe comments, sub-issues,
and PR reviews are filtered out. has_parent stays true so an agent can still
route to get_parent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Restore explicit issue read query matcher

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Omit hierarchy flags when enrichment fails

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address hierarchy review nits

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Roberto Nacu <kerobbi@github.com>
2026-07-07 12:18:09 +01:00
Bryan Zwicker 6592847357 Sanitize and lockdown-gate issue_read get_parent title (#2780)
* Sanitize and lockdown-gate issue_read get_parent title

GetIssueParent returned the parent issue title raw and ungated by
lockdown mode, so an agent could read an unverified, possibly cross-repo
parent title even with lockdown enabled. Always sanitize the parent
title and, under lockdown mode, only return the parent when its author
has push access to the parent repository, failing closed otherwise.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Prove sanitization in get_parent test; trim comments

Embed a U+202E BiDi control char in the mocked parent title so the
happy-path assertion fails if Sanitize is removed, and tighten the
GetIssueParent comments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Roberto Nacu <kerobbi@github.com>
2026-07-07 12:00:59 +01:00
Iulia Bejan 74c34cd71b Add rationale and confidence to closing an issue (#2802)
* Add rationale and confidence to closing an issue

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Fix review feedback for update_issue_state validation/docs

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Roberto Nacu <kerobbi@github.com>
2026-07-06 19:24:27 +01:00
Kelsey Myers 49abf1557c Add is_suggestion + rationale to update_issue_assignees (#2821)
* Add is_suggestion + rationale to update_issue_assignees

* Align assignee confidence to uppercase LOW/MEDIUM/HIGH; add non-string/object test
2026-07-06 17:52:06 +01:00
Sam Morrow 225c47cda1 chore(deps): upgrade go-sdk to v1.7.0-pre.1 (new MCP spec) (#2787)
* chore(deps): upgrade go-sdk to v1.7.0-pre.1 (new MCP spec)

Bumps github.com/modelcontextprotocol/go-sdk v1.6.1 -> v1.7.0-pre.1, the
pre-release that implements the new stateless MCP spec (SEP-2575 server/discover,
SEP-2567 sessionless, MRTR per SEP-2322).

The only source-visible change is tool annotation serialization: the new SDK
drops `omitempty` on ToolAnnotations.ReadOnlyHint and IdempotentHint, so false
values are now emitted explicitly. Regenerated the 113 affected toolsnaps to
match. No behavioural changes; build, vet, test and lint all pass.

Refs: github/copilot-mcp-core#1709

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: regenerate license files

Auto-generated by license-check workflow

* ci(mcp-diff): pin to cross-spec-aware mcp-server-diff (3c2d5ea)

Pins both the stdio and streamable-http MCP Server Diff jobs to the 3.0
branch of SamMorrowDrums/mcp-server-diff (commit 3c2d5ea), which normalizes
cross-spec-version churn: _meta protocol plumbing, CacheableResult cache
hints, the initialize envelope, and tool-annotation default hints. Without it
the go-sdk v1.6.1 -> v1.7.0-pre.1 bump would surface ~113 spurious
idempotentHint/readOnlyHint:false diffs from the SDK dropping omitempty.

Temporary commit pin; move to the v3.0.0 tag once it ships.

Refs: github/copilot-mcp-core#1709

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci(mcp-diff): bump to a2ba618 (stateless server/discover probe)

a2ba618 adds the SEP-2575 server/discover stateless probe path, so each
server is probed at its own newest supported spec (base v1.6.1 via
initialize/2025-11-25, this branch via server/discover/2026-07-28) rather
than negotiating both down to the legacy handshake. Produces an honest,
signal-only cross-spec diff. Still a temporary commit pin; moves to v3.0.0
once tagged.

Refs: github/copilot-mcp-core#1709

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci(mcp-diff): use full commit SHA for action pin

Actions rejects shortened SHAs ('not supported'); use the full
a2ba618c42293fb36e67be88e59c60d5608a302a so the action resolves.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci(mcp-diff): track 3.0 HEAD (8fc26d8, becomes v3.0.0)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(inventory): project owner/repo to Mcp-Param-* headers (SEP-2243)

Annotates owner/repo tool params with x-mcp-header so the SDK projects them
to Mcp-Param-owner/Mcp-Param-repo request headers. A remote proxy can route
and filter on owner/repo from headers instead of re-parsing the JSON-RPC body
(headers are SDK-validated against the body). No-op for tools without these
params; old-protocol traffic unaffected.

Refs: github/copilot-mcp-core#1709, #1828

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(inventory): preserve instructions in ForMCPRequest incl. server/discover

ForMCPRequest dropped the generated instructions when narrowing the per-request
inventory, so HTTP server/discover (and initialize) returned empty instructions
even though the full inventory had them. Preserve instructions on the copy and
treat server/discover like initialize. Fixes discover<->initialize parity flagged
on go-sdk#1034 (root cause was here, not the SDK).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci(mcp-diff): pin to mcp-server-diff v3.0.0

Release is out; move both jobs from the 3.0-branch SHA to v3.0.0
(3521651, full SHA since Actions rejects short SHAs).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(github): enforce owner/repo header projection across all tools

Export HeaderParams + AnnotateHeaderParams and add a coverage test over the
full all-toolsets inventory asserting every owner/repo param projects to its
Mcp-Param-* header. Guards the remote proxy's per-request header read so a new
tool can never silently ship without it (would fall back to body re-parsing).
Adding a future routing param is one entry in inventory.HeaderParams.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(inventory): clone schema before header annotation to avoid shared-map race

AnnotateHeaderParams mutated the *jsonschema.Schema (and per-property Extra
maps) shared with the original tool definition via the caller's shallow copy.
Under per-request registration (remote server), concurrent requests could race
on — and fatally panic from — the same Extra map. Now clone only what we touch
(schema value, Properties map, annotated property schemas + their Extra maps);
the original is never written. Adds a no-mutation test and a 64-goroutine
race regression (go test -race clean).

Addresses Copilot review on #2787.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-01 18:12:12 +02:00
Tim Rogers 8cd03c0185 Add reaction tools for issues and pull requests (#2732)
Docker / build (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Build and Test Go Project / build (macos-latest) (push) Has been cancelled
Build and Test Go Project / build (ubuntu-latest) (push) Has been cancelled
Build and Test Go Project / build (windows-latest) (push) Has been cancelled
GoReleaser Release / release (push) Has been cancelled
MCP Server Diff / mcp-diff (push) Has been cancelled
MCP Server Diff / mcp-diff-http (push) Has been cancelled
Publish to MCP Registry / publish (push) Has been cancelled
* Add reaction tools for issues and pull requests

Implement three granular-only tools for adding emoji reactions:
- add_issue_reaction: Add reaction to an issue
- add_issue_comment_reaction: Add reaction to an issue comment
- add_pull_request_review_comment_reaction: Add reaction to a PR review comment

All tools are feature-flagged with FeatureFlagEnable set to enable them
only when clients request granular toolsets. Tools use go-github's
Reactions service and return minimal ID response on success (HTTP 201).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Make reaction tools available in both granular and non-granular modes

Remove feature flag gates from reaction tools so they're available to all
clients regardless of granular toolset preference. Reaction tools are
naturally atomic operations and work equally well in both modes.

Updates test expectations to exclude reaction tools from granular-only
test assertions, since they're now always available.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update toolsnaps with aligned reaction tool descriptions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Align reaction tool descriptions with codebase style

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Improve reaction tool responses and wording

Return reaction URLs in minimal responses and clarify issue tools apply to pull requests where applicable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Expose reactions through default comment tools

Keep the standalone reaction tools behind granular feature flags to avoid expanding the default tool count. Add optional reaction support to the existing issue comment and pull request comment reply tools, requiring at least one of body or reaction.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Clarify PR review comment IDs for reactions

Document that PR review comment reaction inputs require the numeric review comment ID, not the GraphQL review thread node ID returned by review thread APIs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Support issue comment reactions in add_issue_comment

Add optional comment_id support so the default add_issue_comment tool can react to a specific issue or pull request comment without exposing a separate default reaction tool. Keep body creation tied to issue_number and require reaction targets to provide either issue_number or comment_id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Harden combined comment reaction tools

Apply reactions before creating comments or replies so retrying a failed combined call cannot duplicate the non-idempotent comment operation.

Also reject issue comment IDs without a reaction target to avoid silently ignoring the field.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Group reaction tools with granular registrations

Keep standalone reaction tool registrations next to the granular issue and pull request tools they belong with, so the default compound tool sections stay focused.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Align granular reaction tool constructors

Rename the standalone reaction tool constructors to match the granular tool naming convention and clarify issue comment reaction IDs for pull request comments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Require issue number for comment reactions

Make issue_number required on the consolidated add_issue_comment tool even when reacting to a specific issue comment, keeping the default tool input shape explicit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address issue comment feedback

Return structured GitHub API errors for issue comment creation failures and assert MCP error results in tests.

Also reject comment_id with body on the consolidated comment tool to avoid ambiguous comment-plus-comment-reaction requests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Validate issue comment reaction target

Use the required issue_number to verify issue comment reaction targets before creating the reaction, and remove overlapping add_issue_comment test coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Validate positive comment IDs

Reject non-positive issue and pull request comment IDs in handlers and add the missing schema minimum for pull request review comment IDs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Sam Morrow <sammorrowdrums@github.com>
Co-authored-by: Sam Morrow <info@sam-morrow.com>
2026-06-27 09:19:50 +02:00
Owen Niblock 29634dad00 Fix delete:true on issue fields by calling deleteIssueFieldValue mutation (#2755)
* Fall back to REST DELETE when the PATCH can't carry an issue field clear

The dotcom REST update endpoint uses set semantics for issue_field_values:
sending {"issue_field_values": [...]} overwrites the whole list, and
anything not in the new list is treated as a deletion. UpdateIssue
already exploits this via merge-and-filter — delete:true on a field
removes it from the kept list and the server clears it as a side effect.

That breaks for one specific case: when the kept list ends up empty
(e.g. you're deleting the only remaining field value, or every field in
one call), go-github's omitempty tag on
`IssueRequest.IssueFieldValues` strips the empty slice from the JSON
body. The dotcom REST handler's top-level
`if data.include?(ISSUE_FIELD_VALUES)` guard then short-circuits — the
key isn't in the payload, so the whole block is skipped and the field
keeps its old value. The MCP tool returns success regardless, so a
coding agent would happily report the field as cleared.

Detect this case in UpdateIssue and fall back to the dedicated REST
DELETE endpoint per field: DELETE /repos/{owner}/{repo}/issues/{number}/
issue-field-values/{field_id}. The endpoint is idempotent, takes the
integer field ID (no GraphQL node ID needed), and is the same one the
public OpenAPI documents.

Empirical confirmation on github/github-mcp-server#2756:

- Set Priority=P1 via REST
- Before the fix: MCP issue_write delete:true returns success, PATCH body
  on the wire is literally {}, Priority remains P1 (silent no-op).
- After the fix: MCP issue_write delete:true returns success, PATCH body
  is still {}, but the follow-up DELETE clears the field. Priority is
  cleared as expected.

Three new tests in issues_delete_test.go cover:

- The omitempty contract (so we know if go-github ever drops the tag)
- The 1-of-1 fallback path (PATCH body has no issue_field_values, DELETE
  fires)
- The N-1 set-semantics path (kept list is non-empty, PATCH alone
  handles it, no DELETE call)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review: handle delete-absent-field and DELETE partial failures

Two issues surfaced in code review of the original delete fix:

1. Asking to delete a field that isn't set on the issue used to be a
   silent no-op (PATCH body is {} after omitempty stripping, server
   skips the issue_field_values block, tool returns success). The fix
   turned it into a hard error because the fallback DELETE loop fires
   unconditionally for every field ID in fieldIDsToDelete, and the
   dotcom DELETE endpoint returns 404 when the field has no value to
   delete. This breaks idempotent 'ensure field X is cleared' callers
   that may re-run the same delete on a field that's already been
   cleared.

   Fix: before queueing the fallback DELETEs, filter
   fallbackDeleteFieldIDs down to IDs that actually appeared in the
   existing field values. This preserves the pre-fix silent-no-op
   behaviour and avoids a guaranteed 404.

2. The DELETE loop returned on the first error. If a caller asks to
   clear three fields and the second fails (transient 5xx, rate limit,
   etc.), the first is gone, the third is never attempted, and the
   user gets a generic error with no indication of which field failed
   or which had already succeeded. This is unrecoverable from the
   caller side.

   Fix: continue on per-field errors, accumulate the failed and
   succeeded IDs, and return a single aggregated error naming both
   sets so callers can retry the right fields.

Tests:

- Test_UpdateIssue_DeleteAbsentFieldIsNoOp: existing field values is
  empty, fieldIDsToDelete=[101]. Asserts no DELETE call fires (the
  mock returns 404 if it does, which would fail the test) and the
  tool returns success.

- Test_UpdateIssue_DeleteFallbackContinuesOnPartialFailure: three
  fields exist, all three are deleted. Mock returns 500 for the
  middle DELETE, 204 for the others. Asserts all three DELETEs
  fired (the middle failure didn't short-circuit the third) and the
  error result names failed=[202] and cleared=[101 303].

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Slim down code comments in delete-fix region

Trim the doc comments on UpdateIssue's omitempty-trap region + test
file to the bare minimum needed to understand the non-obvious behaviour
(why the DELETE fallback exists, why we filter to existing IDs, why
errors are aggregated). Drop restated context and step-by-step
explanations.

No behaviour change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 16:10:40 +02:00
Bryan Zwicker 067756a00a Add issue dependency read/write MCP tools (#2751)
* Add issue dependency read/write MCP tools

Add two feature-flagged tools for issue blocked-by / blocking
relationships, gated behind the issue_dependencies flag so they stay off
the default tool surface (auto-enabled for insiders only):

- issue_dependency_read: get_blocked_by / get_blocking via the
  Issue.blockedBy / Issue.blocking GraphQL connections, cursor-paginated.
- issue_dependency_write: add / remove x blocked_by / blocking via the
  addBlockedBy / removeBlockedBy mutations. Accepts issue numbers and
  resolves them to node IDs in a single aliased query; "blocking" is the
  inverse of "blocked_by" with the subject/related roles swapped.

Closes the MCP gap behind the gh CLI dependency verbs (cli/cli#13057).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fail fast on self-dependency in issue_dependency_write

Reject a write where the subject and related issue are identical before
resolving node IDs or issuing a mutation, avoiding two API round-trips.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert addition of `any of` for required ui_get scopes

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 16:03:57 +02:00
Sam Morrow 0503f2f2c5 fix(mcp-apps): reconcile the show/defer contract — render results, remove show_ui (#2774)
* fix(ui): render success view when an MCP App tool executed up-front

The create_pull_request / issue_write / update_pull_request Views decided
form-vs-success from in-app submit state only, ignoring the tool-result the
host delivers on render. Per the MCP Apps 2026-01-26 spec the host renders a
View whenever the tool carries _meta.ui.resourceUri — independent of whether
the server deferred or executed. So when the server executed up-front (e.g.
show_ui=false, or parameters the form can't represent) the View still showed
its "Create pull request" input form over an already-created PR, which reads
as a bug (it even shows a PR number).

Drive the Views off the result instead: a new shared completedToolResult()
helper returns parsed data only for a genuine completed success, and returns
null for the awaiting_user_submission deferral sentinel, errors, or no result.
Each write View now shows its success card when that completed result is
present, so the form is only ever shown while the action is genuinely deferred.

Reconciles the show/defer state machine at the View (decision layer that the
host result feeds). See github/copilot-mcp-core#1864.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ui): scope tool-result to the current invocation

Address review feedback: the write Views derive their success card from
`toolResult`, but it wasn't cleared when a new invocation arrived (only the
in-app `successPR`/`successIssue` was reset on `toolInput` change). A completed
result from a previous invocation could briefly render a stale success card
over the next, still-deferred form.

Clear `toolResult` whenever a new `tool-input` notification arrives. The spec
guarantees `tool-input` precedes that invocation's `tool-result`, so this scopes
the result to the current invocation centrally in the hook — fixing all three
Views without per-app invocation keys.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(mcp-apps): remove show_ui — it can't suppress app rendering

show_ui promised "skip the form and execute directly", but it can't deliver:
the host renders an MCP App for any tool that carries _meta.ui.resourceUri, and
the 2026-01-26 MCP Apps spec has no per-call/per-result way to opt out of
rendering. show_ui only flipped the server's defer decision, so show_ui=false
created the PR/issue up-front yet the host still rendered the app — exactly the
contradiction this work set out to fix. And show_ui is only ever exposed to
clients that support UI, i.e. precisely the clients that always render the app.

Remove it entirely:
- Drop the show_ui schema property, the form-param allowlist entry, and the
  showUI term from the defer predicate in create_pull_request and issue_write.
  The gate is now FF && clientSupportsUI && !_ui_submitted && !hasNonFormParams.
- Delete the now-unused UI-only schema-property strip machinery in
  pkg/inventory (uiOnlySchemaProperties, stripUIOnlySchemaProperties,
  stripSchemaProperties) and the exported ConditionalSchemaPropertyDescriptions,
  which existed solely to surface show_ui to UI-capable clients. _meta.ui
  stripping is untouched.
- Drop the conditional-property annotation from the docs generator.
- Update toolsnaps, generated docs, and tests.

With the up-front-execution Views now rendering the result (success card), the
remaining contract is simple: when MCP Apps are enabled the form is the path,
and the form is only shown while the action is genuinely deferred.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(mcp-apps): centralize the show/defer decision (single source of truth)

The defer-to-form predicate was triplicated across create_pull_request,
update_pull_request, and issue_write, each with its own near-identical
*HasNonFormParams function. As more MCP App tools are added this duplication
would grow and the copies could silently drift.

Extract one shared gate in ui_capability.go:
- shouldDeferToForm(ctx, deps, req, args, formParams) — the single show/defer
  decision (MCP Apps enabled, client supports UI, not a form submission, and no
  non-form params).
- hasNonFormParams(args, formParams) — one generic helper replacing the three
  per-tool functions.
- uiSubmitted(args) — small shared predicate.

Each handler is now a one-line `if shouldDeferToForm(...) { return awaiting }`.
The per-tool form-parameter allowlists and the user-facing messages stay
per-tool (that is the genuine per-tool config). Pure refactor — behavior
unchanged; existing tests now exercise the generic helper.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 15:28:29 +02:00
石岳峰 5aa8ed36df fix(pull_requests): validate required params in add_comment_to_pending_review (#2770)
Replace mapstructure.WeakDecode with RequiredParam/RequiredInt checks so
missing arguments like owner return a clear validation error instead of
running the GraphQL query with zero values.

Fixes #2718

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-06-26 15:21:43 +02:00
Sam Morrow 8ce77e3c16 feat(oauth): add stdio OAuth 2.1 login core library (1/4) (#2704)
* feat(oauth): add stdio OAuth 2.1 stdio login
Introduce internal/oauth, a self-contained library that performs the
user-facing GitHub OAuth login the stdio server uses to obtain a token
without a pre-provisioned PAT. It is independent of MCP: client concerns
(elicitation) sit behind the Prompter interface so the flows are testable
without a live session.

What it provides:
- Authorization-code + PKCE flow with a local loopback callback server,
  state/CSRF validation, and XSS-safe result pages.
- Device-authorization flow as a fallback (headless, containers).
- A Manager that selects the most secure available channel
  (browser auto-open -> URL elicitation -> last-resort user action),
  runs a single flow at a time, and exposes a refreshing token source.

Both GitHub OAuth Apps and GitHub Apps are supported without special
casing: the token is modeled as an x/oauth2 refreshing TokenSource, so
expiring GitHub App user tokens are renewed transparently (the gap that
made a stored-token approach silently die after ~8h).

When a client lacks secure URL elicitation and the flow falls back to a
tool-response message, the message advises the user that their agent/CLI/
IDE does not appear to support URL elicitation and suggests requesting it
for improved security.

Tests exercise real protocol behavior against an httptest GitHub stand-in:
PKCE challenge/verifier, GitHub App refresh-on-expiry, device polling,
URL elicitation, declined prompts, the last-resort action with advisory,
and single-flight concurrency.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(oauth): reap browser launcher and keep native callback on loopback

Address code review:
- openBrowser: reap the launcher process asynchronously so it does not
  linger as a zombie for the lifetime of the server.
- listenCallback: take an explicit bindAll flag and bind to all interfaces
  only inside a container (where the published port arrives via eth0).
  A native run, even with a fixed callback port, now stays on 127.0.0.1
  instead of 0.0.0.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(oauth): fail fast when a fixed callback port is unavailable

A fixed --oauth-callback-port is registered with the OAuth app and chosen
deliberately, so a bind failure means another process holds the port and
could intercept the authorization redirect. Treat that as fatal instead of
silently downgrading to the device flow, which would mask the conflict.

Also warn, when binding the callback inside a container, that the listener
is on all interfaces and should be published to loopback only so the
authorization code is not exposed on the container network.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(oauth): surface refresh failures, bound refresh, prefer device flow when headless

Addresses pre-merge review of the OAuth stdio core:

- Log a one-time warning when token refresh fails instead of silently
  returning an empty access token, so a forced re-login isn't a surprise.
- Bound each background token refresh with a 30s HTTP client timeout so a
  stalled GitHub token endpoint can't block tool calls indefinitely.
- On a headless host (no display server) with a random callback port, fall
  back to the device-code flow — the only channel reachable from a browser
  on another machine — instead of dead-ending on an unreachable localhost
  redirect. A generic browser-open failure still offers the manual URL.
- Mark the callback bind failure with a sentinel so the fixed-port-busy
  fatal path can't misreport an unrelated error as a port conflict.
- Export NormalizeHost so callers can recognize the default github.com host
  (consumed by the build-time baked-in credential guard).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(oauth): wire stdio OAuth 2.1 login into the server (2/4) (#2710)

* feat(oauth): wire stdio OAuth 2.1 login into the server

Connect the internal/oauth core library to the stdio MCP server so users
can authenticate with an OAuth App or GitHub App client ID instead of a
static personal access token.

- BearerAuthTransport gains a TokenProvider that is consulted per request,
  letting the lazily-acquired, auto-refreshing OAuth token take effect
  without rebuilding the client.
- createGitHubClients uses BearerAuthTransport (and skips go-github's
  WithAuthToken, which would pin a static token) when a TokenProvider is set.
- RunStdioServer starts without a token and installs receiving middleware
  that runs the authorization flow on the first tool call, surfacing the
  auth URL or device code via elicitation (or a tool result as a fallback).
- Tool filtering uses the requested OAuth scopes; the default supported set
  hides nothing, while a narrower --oauth-scopes both narrows the grant and
  filters tools accordingly.
- A sessionPrompter adapts the MCP server session to oauth.Prompter, keeping
  the authorization URL off the model's context.
- New stdio flags: --oauth-client-id/-client-secret/-scopes/-callback-port.

This is stdio-only and deliberately does not touch MCP-HTTP auth.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(oauth): address review — omit empty bearer header, guard token/oauth

- BearerAuthTransport omits the Authorization header entirely when the token
  is empty (pre-authorization) rather than sending an empty "Bearer " value.
- RunStdioServer rejects the ambiguous combination of a static Token and an
  OAuthManager up front, enforcing the documented mutual exclusivity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(oauth): clarify SupportedScopes is the stdio default and tool filter

Document that stdio OAuth login requests these scopes by default and then
filters the exposed tools to the scopes actually granted, so a tool whose
required scope is absent from this list is hidden under default OAuth even
though a PAT carrying that scope would expose it. Keep the list in sync with
tool scope requirements when scopes change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Distinguish undeliverable auth prompts from user declines

An elicitation prompt that the client cannot deliver (a transport or
protocol failure) was treated the same as a user actively declining: any
display error cancelled the flow. That conflated a system failure with a
deliberate "no", so a client that advertised URL elicitation but failed
to deliver it would hard-fail the login instead of degrading.

Add an ErrPromptUnavailable sentinel alongside ErrPromptDeclined and have
the MCP adapter return it when Elicit fails at the transport level. The
manager now falls back to the manual user-action channel on an
undeliverable prompt (keeping the background flow alive so the user can
still authorize out of band), while a genuine decline still aborts. A
context-cancelled prompt is checked first so an ending flow is never
misread as a transport failure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* build(oauth): bake in default OAuth credentials for official releases (3/4) (#2711)

* build(oauth): bake in default OAuth credentials via build-time ldflags

Inject the public OAuth client credentials (stored as the OAUTH_CLIENT_ID
and OAUTH_CLIENT_SECRET repo secrets) at build time via -ldflags so
official binaries and images ship a working default app for zero-config
login. Security relies on PKCE, not on the secret. Local/dev builds leave
the values empty and continue to require an explicit token or
--oauth-client-id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(oauth): recognize github.com host aliases for the baked-in client

Match the default host via oauth.NormalizeHost instead of only an empty
host string, so an explicit GITHUB_HOST=github.com (or api.github.com)
still counts as the default and keeps zero-config baked-in login working.
GHES and ghe.com users continue to bring their own --oauth-client-id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(oauth): document stdio OAuth login; make PAT optional in install config (#2717)

Add a dedicated Local Server OAuth Login guide (docs/oauth-login.md) covering
the PKCE/device flows, display channels and the URL-elicitation security
advisory, scope-based tool filtering, the fixed-port Docker recipe and its
loopback/port-safety behavior, bringing your own OAuth or GitHub App, and the
GitHub Enterprise Server / ghe.com requirement to register an app on that host
(custom --gh-host directs login at that instance's authorization server).

Reflect that the local server now logs in with OAuth by default on github.com:
- README: make the stdio Docker install badges OAuth-first (fixed callback port
  8085 published to loopback), drop the PAT prompt, and reframe the PAT as an
  optional alternative with a pointer to the new guide.
- server.json: make GITHUB_PERSONAL_ACCESS_TOKEN optional and publish the OAuth
  callback port so the registry default works without a token.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 11:59:35 +02:00
Matt Holloway 63d313a973 fix(ui_get): bound pagination to cap latency (#2766)
ui_get backs a synchronous UI picker (label/assignee/etc. dropdowns in the
MCP App issue/PR write surfaces). Each handler paginated GitHub API results
in an unbounded loop (PerPage 100, looping until NextPage==0 /
HasNextPage==false). On very large repos/orgs this fans out into dozens of
sequential round-trips, and a single slow page inflates the whole call —
production telemetry showed a tail spiking to ~20 minutes.

Bound every ui_get pagination loop to uiGetMaxPages (10 pages, ~1000 items)
and add an additive "has_more" flag indicating results were truncated.
Truncation is acceptable here because the picker pairs it with typeahead, so
responsiveness matters more than completeness.

Affected methods: labels, assignees, milestones, branches, reviewers
(both the collaborators and teams loops). uiGetIssueTypes and issue_fields
are single-request and left unchanged.

has_more is response-only and backward-compatible: no existing keys are
removed or renamed, and the tool input schema is unchanged. HTTP-client
timeouts are intentionally a separate follow-up.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Sam Morrow <info@sam-morrow.com>
2026-06-25 14:26:13 +01:00
Matt Holloway 3c4749d234 Show issue_write MCP App form for labels/assignees/milestone/type (#2767)
The issue_write form-gating logic uses issueWriteFormParams to decide whether
to render the MCP App form or execute directly: a call carrying any parameter
outside that allowlist bypasses the form. The form prefills and re-submits
labels, assignees, milestone and type, but those four were absent from the
allowlist, so passing any of them skipped the confirmation form even though the
form fully supports them.

Add labels, assignees, milestone and type to issueWriteFormParams so the form
is shown when they are present. The allowlist now covers every input-schema
property, leaving issueWriteHasNonFormParams as a forward-compatibility safety
net for properties added without form support.

Also correct the now-inaccurate show_ui guidance on issue_write and
create_pull_request: both descriptions claimed the form "does not collect"
fields it actually collects (labels/assignees/milestone/type/issue_fields, and
reviewers respectively).

Update unit tests and regenerate toolsnaps and docs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-25 14:07:31 +01:00