* feat(repos): add confirmed repository deletion
Add a destructive delete_repository tool that requires an exact owner/repo confirmation through multi-round-trip elicitation. Gate the tool to MCP protocol 2026-07-28 and newer across local and remote transports.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* refactor(inventory): generalize tool availability guards
Gate protocol-restricted tools on required elicitation capabilities and enforce direct calls inside the registered handler so SDK result finalization remains intact.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* feat(http): protect MRTR request state
Seal repository deletion targets for self-hosted HTTP with a stable AES-256-GCM key. Hide only delete_repository when no key is configured and expose an optional sealer interface for remote integrators.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* fix(repos): expire deletion confirmations
Bind sealed repository deletion state to the immutable repository ID and a ten-minute expiry. Re-check identity before deletion so replay cannot affect a recreated repository.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* fix(http): preserve tool and scope restrictions
Apply static allowlists before removing unavailable tools and fail closed on invalid configured tool names. Model independent OAuth requirements as conjunctive groups so repository deletion requires both delete_repo and repo.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* fix(repos): require protected confirmation state
Give stdio a process-local request-state sealer and make deletion fail closed without one. Preserve legacy any-of OAuth behavior globally while documenting and enforcing delete_repository's conjunctive delete_repo and repo requirements.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* fix(oauth): request repository deletion scope
Include delete_repo in the supported OAuth scope set used by stdio login, HTTP protected-resource metadata, and tool filtering.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* fix(oauth): require deletion scope opt-in
Keep delete_repo in protected-resource discovery for step-up authorization while excluding it from the default stdio OAuth grant.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* refactor(oauth): derive scope sets from catalog
Generate protected-resource supported scopes and the lower-risk default OAuth grant from one canonical scope definition list.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* refactor(scopes): own OAuth scope catalog
Move supported and default OAuth scope policy into pkg/scopes so protected-resource metadata and stdio grants derive from the scope domain package.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* fix(scopes): require workflow scope opt-in
Keep workflow and codespace in protected-resource discovery while excluding both from the default OAuth grant alongside delete_repo.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
---------
Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
* fix(http): reject unsupported subscription streams
Use the Mcp-Method header to reject subscriptions/listen with the spec-defined 404 Method Not Found response instead of opening an idle SSE stream. Preserve SDK validation for missing or mismatched headers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 06d5dda1-4086-4996-8d18-152e45e611b0
* refactor(http): clarify subscription rejection
Document why header validation precedes the unsupported-method rejection and use named SDK error constants in tests.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 06d5dda1-4086-4996-8d18-152e45e611b0
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 06d5dda1-4086-4996-8d18-152e45e611b0
Address review: the loopback exception accepted http://localhost:3000 and
http://[::1], but newGHESHost built URLs from u.Hostname(), which drops the
port (silently retargeting the dev server to port 80) and strips IPv6 brackets
(producing an unusable URL such as http://::1/api/v3/).
Derive the base-host REST/GraphQL/upload/raw/authorization URLs from u.Host so
the port and IPv6 brackets are preserved. Subdomain-isolation URLs keep using
the bare hostname, since a label cannot be prepended to a host:port or an IP
literal. Add tests for the ::1 case and for port preservation.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
GHES hosts accepted an http:// scheme, which was interpolated into every
REST/GraphQL/upload/raw/authorization URL. Authenticated requests would then
carry the bearer token/PAT over cleartext http, exposing it to network
interception and replay.
Add a central HTTPS check in parseAPIHost so no deployment can build
authenticated URLs over http, mirroring the existing GHEC behaviour. Permit
http only for loopback hosts (localhost, 127.0.0.1, ::1) so local development
against a dev server still works.
Closesgithub/copilot-mcp-core#1815
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* 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
* 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
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
* 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>
* 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
* 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
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.
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
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
* 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
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
- 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
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
* 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.
Move Octicon base64 encoding to the generation script and embed the generated lookup manifest for zero-allocation reads.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5557f448-5cc1-46ce-b5ad-36f9e2e224e0
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
* 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
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
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>
* 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>
* 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>