132 Commits

Author SHA1 Message Date
Sam Morrow a7c3b494e6 test(http): cover authorization server override wiring
Verify the HTTP-only configuration surface, unchanged host-derived default, and explicit override propagation through OAuth metadata.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-20 16:44:51 +02:00
Anika Reiter 0ae533c163 feat(http): add --authorization-server flag to override OAuth AS URL
When deploying the MCP server behind an OAuth proxy (e.g. for GHES,
which does not natively support RFC 8414, RFC 7591, or PKCE), the
/.well-known/oauth-protected-resource endpoint currently always derives
the authorization_servers URL from GITHUB_HOST. There is no way to
point clients at a different authorization server without intercepting
that endpoint at the ingress/proxy layer.

The oauth.Config struct already has an AuthorizationServer field with
the conditional logic in place (pkg/http/oauth/oauth.go), but it was
never wired to any configuration surface.

This commit exposes it as:
- --authorization-server CLI flag on the http subcommand
- GITHUB_AUTHORIZATION_SERVER environment variable (via viper's
  existing GITHUB_ prefix + automatic env mapping)

When set, the value is passed through ServerConfig into oauth.Config,
and the protected resource metadata advertises it directly instead of
calling apiHost.AuthorizationServerURL().
2026-08-20 16:44:51 +02:00
Sam Morrow 3bad3bc651 fix(http): make server lockdown mode an upper bound over requests (#3112) 2026-08-19 15:20:19 +02:00
Sam Morrow 769340d6a1 fix(lockdown): harden pull_request_read get_commits handling
pull_request_read's get_commits method previously returned commit
messages without any lockdown check, unlike get_diff and get_files
which restrict the whole result when the PR author lacks push access.
Commit content is part of the same untrusted head branch as the diff
and file list, so GetPullRequestCommits now reuses
enforcePullRequestLockdown for consistent, fail-closed behavior
without adding a per-commit permission lookup.

Also updates the lockdown documentation in README.md and
docs/server-configuration.md to:
- list pull_request_read:get_diff, get_files, and get_commits among
  the tools that error when the PR author lacks push access (get_diff
  and get_files were already implemented this way but undocumented)
- clarify that lockdown mode is a best-effort content filter to
  reduce prompt-injection risk, not an authorization boundary
- document the existing intentional trusted-bot exception
  (github-actions[bot], copilot) accurately

Fixes #3105

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-19 14:41:45 +02:00
Sam Morrow 8ec62491c6 Add confirmed repository deletion tool (#3076)
* 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
2026-08-18 14:50:38 +02: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
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
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
Sam Morrow e05a38403f feat(auth): add GitHub App server-to-server authentication for stdio
Add non-interactive GitHub App installation authentication to the stdio
server, so headless deployments (CI, Kubernetes, background agents) can
authenticate without a browser, device code, or elicitation. This is the
outstanding follow-up tracked in #1333: OAuth login shipped the interactive
user-to-server flows, but PEM-based server-to-server auth was still needed to
remove the interactive requirement.

The new internal/githubapp package signs a short-lived RS256 JWT with the
app's private key, exchanges it for an installation access token, and refreshes
it transparently before expiry. It exposes a Provider whose AccessToken method
mirrors oauth.Manager so it plugs into the existing BearerAuthTransport token
provider. Only the standard library and golang.org/x/oauth2 are used.

The private key is injected safely: a file path (GITHUB_APP_PRIVATE_KEY_PATH,
preferred — mountable as a secret and kept off argv and out of the environment)
or an inline GITHUB_APP_PRIVATE_KEY env var. There is intentionally no flag for
the key contents, which would otherwise leak via the process command line.

App auth is mutually exclusive with a PAT and with OAuth login. A loud startup
warning and a dedicated docs page (docs/github-app-auth.md, with Docker and
Kubernetes examples) cover the security considerations: this injects a
high-privilege credential alongside the agent and is not recommended without an
independent security review.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-22 18:51:35 +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
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
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
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
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
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
Sam Morrow fab5fc5ff2 docs: lead local/stdio install with OAuth login (#2776)
* docs: lead local/stdio install with OAuth login

Surface OAuth 2.1 login as a first-class option above the Personal Access
Token in the README install section and in every local-capable installation
guide, each in that client's own config syntax, linking to the dedicated
docs/oauth-login.md guide.

The github.com OAuth example needs no token; in Docker it publishes a fixed
callback port to loopback (-p 127.0.0.1:8085:8085). The PAT block is retained
below it and noted as taking precedence over OAuth.

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

* docs(oauth): correct PKCE/client-secret explanation

The previous "How it works" text claimed PKCE removes the need for a client
secret. In practice GitHub requires the client secret at the token endpoint for
both OAuth Apps and GitHub Apps, and the implementation sends it alongside the
PKCE verifier (Manager.oauth2Config populates ClientSecret; flow.go exchanges
with VerifierOption). The secret is baked into the distributed binary and is not
truly confidential; PKCE is what actually secures the flow. Reword to reflect
this accurately.

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

* docs: don't frame OAuth login as github.com-only

The intro line said "On github.com you can skip the token entirely", which
implied OAuth login only works on github.com. OAuth works on GitHub Enterprise
too — you just supply your own app credentials there. Reword to lead with OAuth
as the mechanism and scope the zero-config claim correctly: on github.com the
official image already includes the app credentials, so the user provides none.
Enterprise / bring-your-own-app is covered by the linked OAuth guide.

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 14:45:06 +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 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
Renee Xu a37837027f Promote issue fields and deprecate legacy issue write tool (#2696)
* Promote issue fields FF and remove legacy issue write methods

* Remove legacy issue_write methods

* Remove feature flag

* Update tests

* Update tool snaps

* Update docs

* Remove legacy issue write from issues test

* Update tool snap

* Fix docs

* Include issue_fields in doc

* Remove issue fields FF check in ui tools

* Update UI tools test

* update docs

* Regenerate doc
2026-06-19 14:09:47 +02:00
Kelsey Myers 4f73cfd1db Add repo-scoped support to list_issue_types tool (#2692)
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Docker / build (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 repo-scoped support to list_issue_types tool

* Render multi-scope tools as "any of" in generated docs

* Clarify issue type field description for repo-scoped list_issue_types
2026-06-17 17:04:18 +01:00
Renee Xu 6830c4d394 Change confidence enum to use uppercase values to match GraphQL schema (#2715)
* Fix enum values to match those expected by gql

* Update test

* Update doc and snaps

* Normalize lower case confidence values

---------

Co-authored-by: Ross Tarrant <rosstarrant@github.com>
2026-06-17 14:50:16 +01:00
Koen Vlaswinkel f9a4dc5412 Add tool for getting code quality findings (#2604)
Co-authored-by: Sam Morrow <info@sam-morrow.com>
2026-06-17 12:19:11 +02:00
Matt Holloway 4e8eb81dac MCP Apps with extra functionality (#1974)
* PoC full flow (hello world example)

* add avatar resource domain

* add postmessage logic and richer UI

* add create issue ui

* update ui for issue creatioon

* fix

* ignore banner

* update docs after rebase

* update toolsnap for get_me

* new UI changes

* update docs

* update workflows that need ui build

* add UI diff

* fix build ui step for windows runners to use git bash

* fix UI diff

* refactor issue creation UI

* add AvatarWithFallback component and update UserCard to use it; enhance CreateIssueApp to manage existing issue data

* fix formatting of button labels

* add create pull request functionality with UI support and insiders

* update docs

* add test for insiders mode handling in ServerTool schema

* remove `show_ui` param for now

* make insiders mode metadata stripping generic

* remove ui diff

* fix CI

* remove redundant mention of old app name

* add node types to fix ide issues for ts code

* remove unused TriangleDownIcon import

* update @primer/behaviors and electron-to-chromium versions in package-lock.json

* add check to ensure base and head are not the same when creating a new PR

* remove old show_ui

* fix gitignore for dist so builds dont break

* add tests for insiders mode handling and metadata stripping in ServerTool

* remove unused state and components from CreatePRApp

* fix ui build

* update docker build to fix npm issue

* remove reference to show_ui

* allow insiders to work for non-ui features

* formalise insiders inventory support

* update docs

* fix overflow issues and replace pull request dropdown with matching UI from dotcom

* fix createpullrequest test

* consolidate fetching tools under `ui_get` tool to remove toolset deps

* fix issue data prefill in issue_write form

* fix link component when updating issue

* fix avatar URL

* fix broken issue update logic

* remove dbg

* fix for new GetFlags

* revert to original required fields for create_pull_request

* fix for UI form submission

* Simplify MCP App UIs for basic branch

Remove advanced features to be kept in mcp-ui-apps-advanced:
- Strip labels, assignees, milestones, issue types, repo picker from issue-write
- Strip repo picker, branch selectors from pr-write
- Delete ui_get tool (ui_tools.go, ui_tools_test.go, ui_get.snap)
- Remove UIGet registration from tools.go

Basic forms retain: title, body, submit with _ui_submitted,
draft/regular split button (PR), MarkdownEditor, and SuccessView.

* Fix header spacing in issue-write and pr-write UIs

Add proper spacing between icon, title text, and repo name in the
header bar for both issue-write and create-pull-request forms.

* fix UI spacing

* Revert "Simplify MCP App UIs for basic branch"

This reverts commit 24174b91e222e45b47913ff6b760db809f48660b.

* Undo dependency downgrades in ui/package-lock.json

* Update ui/src/apps/pr-write/App.tsx

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

* Update ui/src/apps/issue-write/App.tsx

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

* Implement pagination for uiGetBranches (#2012)

* Initial plan

* Implement pagination for uiGetBranches function

Co-authored-by: mattdholloway <918573+mattdholloway@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mattdholloway <918573+mattdholloway@users.noreply.github.com>

* update to new insiders feature flag func

* ensure transient state is reset on successive tool calls

* Mark ui_get as app-only visibility

ui_get backs only the MCP App views and has no business in the agent's
tool list. Per the MCP Apps 2026-01-26 spec, omitting _meta.ui.visibility
defaults to ["model","app"], which exposes the tool to the model. Declare
visibility ["app"] so the host hides it from tools/list while the views can
still invoke it via tools/call.

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

* Update ui_get toolsnap for app-only visibility

Regenerated via UPDATE_TOOLSNAPS to capture the new _meta.ui.visibility.

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

* Assert ui_get declares app-only visibility

Locks in the _meta.ui.visibility ["app"] contract so a future edit can't
silently re-expose the UI data tool to the model.

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

* Add ui_get to insiders feature docs

Regenerated docs/feature-flags.md and docs/insiders-features.md to include
the ui_get tool entry.

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

* Address ui_get review feedback

- Paginate the labels GraphQL query (cursor-based) so repos with more than
  100 labels return a complete list instead of silently truncating.
- Emit an empty due_on for milestones without a due date instead of
  formatting the zero time as "0001-01-01".
- Use NewGitHubAPIErrorResponse in uiGetIssueTypes to preserve GitHub
  response context, matching the other REST-backed methods.
- Extend tests to cover the labels (GraphQL), milestones (including the
  no-due-date case) and issue_types methods, plus the issue_types error path.

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

* Fix repo reset and stale base-branch in MCP App views

- Re-initialize selectedRepo from toolInput inside the reset-on-invocation
  effect instead of a separate effect. The two effects both depended on
  toolInput and ran in declaration order, so the reset wiped the just-
  initialized repo and the picker never reflected the invocation's owner/repo.
- Set the default base branch with a functional update in pr-write so a base
  prefilled from toolInput.base (or chosen by the user) isn't overwritten by
  a stale baseBranch value captured before the branches request resolved.

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>

* Fix issue-write repo owner mapping and clear stale UI state on reset

- issue-write: derive owner/name from full_name since search_repositories
  minimal output omits the owner object (mirrors pr-write)
- pr-write/issue-write: clear available branch/label/assignee/milestone/type
  lists and filters in the toolInput reset effect so prefill effects can't
  match against the previous repo's stale data

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

* Merge remote-tracking branch 'origin/main' into mcp-ui-apps-advanced

* feat: add pull request editing functionality with reviewers support

* feat: implement interactive form handling for issue and pull request creation and updates

* Close response body per page in ui_get pagination loops

Avoids leaking HTTP connections when paging through assignees,
milestones, branches, collaborators, and teams.

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

* Cache pr-edit.html in build-ui action

The build-ui cache only saved get-me/issue-write/pr-write HTML, so once a
cache entry was stored it restored an incomplete ui_dist on later runs and
skipped the rebuild, leaving pr-edit.html absent and panicking the tests.
Add pr-edit.html to the cached paths and bump the cache key to v2 to evict
the incomplete entries.

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

---------

Co-authored-by: tommaso-moro <tommaso-moro@github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-16 16:45:16 +01:00
Matt Holloway d27540ff67 Add explicit show_ui parameter to UI-enabled write tools (#2601)
* Add explicit show_ui parameter to UI-enabled write tools

Today the server decides whether to route issue_write and create_pull_request
through the MCP App form using two implicit signals: _ui_submitted (set by
the form on submit) and a heuristic that bypasses the form when the call
carries any parameter the form cannot represent (labels, assignees,
issue_fields, state, reviewers, etc.). The model had no first-class,
documented way to say "execute directly, do not show a form".

Add a show_ui boolean parameter to the input schema of IssueWrite,
LegacyIssueWrite, and CreatePullRequest. It defaults to true and is
visible only to clients that advertise MCP App UI support: the strip
happens per-request in inventory.ToolsForRegistration via a new
stripUIOnlySchemaProperties helper, gated by the same predicate that
already strips _meta.ui (shouldStripMCPAppsMetadata). The two strips share
one decision so the schema and metadata stay in lock-step.

Form-routing predicate becomes:

    MCPApps FF on && client supports UI &&
    !_ui_submitted && show_ui && !hasNonFormParams

show_ui=false is a new explicit way for the model to opt out. The existing
non-form-param auto-bypass stays as a safety net, and the React forms keep
sending _ui_submitted=true on submit unchanged. get_me is out of scope
because its UI is pure client-side card rendering with no server-side
gating to replace.

The current strip gate ("strip when FF is off OR capability explicitly
absent") mirrors today's _meta.ui behavior exactly, including the
"capability unknown" case. For stdio that means UI-capable schemas are
exposed to any FF-enabled client. The handler-side clientSupportsUI check
still gates form execution at call time, so it is functionally a no-op for
non-UI stdio clients. A separate follow-up will tighten the gate to
"strip on unknown too" and wire an InitializedHandler in stdio to
re-register the un-stripped surface only after a UI-capable client has
advertised; the two changes must ship together to avoid breaking stdio.

docs/feature-flags.md and docs/insiders-features.md include an unrelated
"reviewers" description update picked up by script/generate-docs from
commit 2bd162ac ("fix: support team pull request reviewers"), which
updated the source schema but did not regenerate docs.

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

* Clarify where show_ui appears in generated docs

The code comments next to the show_ui schema entries (and the
uiOnlySchemaProperties allowlist) said the property is documented in
"toolsnaps / README". README is generated from the stripped (non-UI)
schema, so show_ui is not actually in it — it only appears in toolsnaps
and the feature-flag / insiders docs. Reword the comments to match
reality.

Comment-only change; no behavior or test impact.

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

* Guard issue_write/create_pull_request schemas against UI-gating desync

The form-routing logic depends on a hand-maintained classification of
each schema property into form-resendable vs known-non-form. A new
property added without updating the classification would silently shift
UI gating behavior (e.g. a form-incompatible param wouldn't trigger the
safety-net bypass).

Add Test_issueWriteSchemaClassification and Test_createPullRequestSchemaClassification
that enumerate each tool's InputSchema.Properties and require every
property to be classified as exactly one of:
  - form-resendable (member of issueWriteFormParams / pullRequestWriteFormParams)
  - known-non-form (test-local allowlist)

A future schema addition without classification fails the test with a
message pointing at the exact set the contributor needs to update.

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

* Mark conditional schema parameters in generated docs

Previously `show_ui` was listed in docs/feature-flags.md and
docs/insiders-features.md alongside ordinary parameters with no
indication that it is hidden from clients without MCP App UI support.
A reader scanning the parameter list would assume it is always available.

Add a programmatic conditional-property mechanism:

- `inventory.ConditionalSchemaPropertyDescriptions()` exposes a
  map[propertyName]conditionDescription derived from the same
  uiOnlySchemaProperties allowlist that drives the per-request strip
  in ToolsForRegistration. Single source of truth.
- The doc generator (writeToolDoc) consults this map and appends
  "conditional — <description>" to the parameter's parenthesised
  type/required suffix.

Example rendered output:

  - `show_ui`: Whether to render the MCP App form... (boolean, optional,
    conditional — only visible to clients that advertise MCP App UI support)

A small test (TestConditionalSchemaPropertyDescriptions) ensures every
entry in uiOnlySchemaProperties has a description, so a future stripped
property addition can't silently lose its doc marker.

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>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sam Morrow <sammorrowdrums@github.com>
2026-06-15 16:31:28 +01:00
Mayowa Fajobi 1209da8eba feat: add get_file_blame tool for retrieving git blame information (#1538)
* feat: add get_file_blame tool

* feat: implement cursor-based pagination for get_file_blame tool

* resolve annotated tags to their target commit in get_file_blame

* Regenerate get_file_blame toolsnap and docs after merge with main

The cursor-pagination parameter description changed on main; regenerate the
toolsnap and README so docs-check and toolsnap tests pass.

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

* feat: gate get_file_blame behind file_blame feature flag

The git blame tool adds a new tool to the inventory, which carries a
context-footprint cost for every client. Gate it behind a new
file_blame feature flag (user opt-in via --features / X-MCP-Features)
that is also auto-enabled in insiders mode, so it is not advertised by
default.

Regenerated README, feature-flags.md and insiders-features.md docs.

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

---------

Co-authored-by: Sam Morrow <sammorrowdrums@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-11 15:51:09 +02:00
Mayowa Fajobi 918a42f05a feat: implement cursor pagination for dependabot alerts (#2651) 2026-06-10 23:09:34 +01:00
Tim Rogers 8bbd902b9e Update title annotations for issue_write and add_issue_comment tools to reflect that they also work with pull requesta (#2664)
* Clarify issue tool titles for PR context

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

* Fix issue_write_ff_remote_mcp_issue_fields snap title

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

* Re-generate docs after upstream merge

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-10 19:55:04 +02:00
Alon Dahari 457f59932a Add confidence parameter to issue mutation MCP tools (#2605)
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Docker / build (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 an optional confidence integer parameter (0–100) to update_issue_type,
update_issue_labels, and set_issue_fields MCP tools. The confidence score
is passed through to the REST/GraphQL API on mutation calls.

- Rename structs to WithIntent (labelWithIntent, issueTypeWithIntent)
- Add confidence schema property (integer, min 0, max 100) with prompt
  guidance describing what different confidence levels represent
- Update tool descriptions to encourage including confidence scores
- Pass confidence in the API request body alongside rationale/suggest

Closes github/plan-track-agentic-toolkit#219

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 15:04:22 +01:00
Copilot b0d9854388 docs: also advertise /x/all meta toolset
The default toolset row covers /mcp/ but /x/all is still a real, useful
meta toolset that enables every toolset at once. Render both as special
rows above the per-toolset list.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-30 11:10:36 +02:00
Dmitry Korobitsin 830ad2c613 docs: clarify that / uses default toolset in remote MCP server 2026-05-30 11:10:36 +02:00
Copilot d6b9dc9411 docs: address review feedback on env-var examples
- Linux/macOS: actually set GITHUB_PAT instead of inlining via subshell, matching the heading.
- PowerShell: use Select-Object -First 1, split with max 2 parts, and trim quotes/whitespace so common .env formats work.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-30 11:03:31 +02:00
Emily Chen 03da19109e docs: improve Claude installation guide with Windows PowerShell support
- Fix README.md: Remove non-existent 'Claude Web' from description
- Add Windows PowerShell environment variable example for loading PAT from .env file

The previous documentation only showed bash syntax for loading environment
variables from .env files, which doesn't work on Windows PowerShell. This
adds a PowerShell equivalent to help Windows users set up the GitHub MCP
Server correctly.
2026-05-30 11:03:31 +02:00
John CSA 29a86780bf docs: add installation guides for Zed and OpenCode
Adds two new installation guides under docs/installation-guides/ covering MCP host applications that are not yet documented:

- install-zed.md: covers Zed's 'context_servers' settings key (command + args shape), the official GitHub MCP extension as an easier alternative, remote/local setup, the 'mcp:<server>:<tool>' permission key format introduced in Zed v0.224.0, and OAuth-vs-PAT trade-offs.

- install-opencode.md: covers OpenCode's 'mcp' config block (type-discriminated local/remote, command-as-array, 'environment' instead of 'env'), the 'oauth: false' opt-out needed when using a PAT, the '{env:VAR}' interpolation pattern, and the per-agent tool-gating pattern recommended for token-heavy servers like GitHub.

Also adds both hosts to:

- docs/installation-guides/README.md installation-guides index and the support-by-host-application table.

- README.md 'Install in other MCP hosts' and 'Install in Other MCP Hosts' lists.

Closes #2531.
2026-05-30 11:01:51 +02:00
Sam Morrow 014fd17fa3 feat: gate issue_write and get_issue behind remote_mcp_issue_fields flag
Ports the gating from PR #2553 onto main (the original merge landed on a
stack base that did not make it to main).

Changes:
- pkg/inventory: FeatureFlagDisable becomes []string (any-listed-on → hide).
  FeatureFlagEnable stays as a single string. This avoids the AND-of-enable
  semantics from the earlier proposal, which encoded dependencies rather
  than rollout knobs and had no real call site. Disable-OR is the case
  that does need the slice (LegacyIssueWrite below).
- pkg/github/issues.go: split IssueWrite into IssueWrite (flag-enabled,
  exposes issue_fields) and LegacyIssueWrite (flag-disabled, omits it).
  Both register as 'issue_write'; mutually exclusive flag annotations
  pick exactly one at runtime. Refactored into a shared buildIssueWrite
  helper instead of duplicating the ~250-line tool definition.
- pkg/github/issues.go: GetIssue field_values enrichment now requires
  the flag at runtime. The verbose REST IssueFieldValues is always
  cleared from the response.
- Existing single-flag Disable call sites converted to slices.
- New toolsnap variant issue_write_ff_remote_mcp_issue_fields.snap; the
  canonical issue_write.snap is owned by LegacyIssueWrite.
- README + flag docs regenerated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 13:11:29 +02:00
Ross Tarrant 13e6b8bae1 Add consistency to code block language
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-28 12:22:20 +02:00
RossTarrant 001f5e3c31 docs: clarify auth in Codex CLI instructions 2026-05-28 12:22:20 +02:00
Boaz Reicher d661abf4ca Boazreicher/add suggest to labels and issue fields (#2557)
* adding suggestions for labels and fields

* fixing suggestions

* Potential fix for pull request finding

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

* updaing

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-28 02:29:34 -07:00
Iulia B 2b7807bcb7 add delete support and merge logic 2026-05-27 16:26:36 +02:00
Kelsey Myers 2f8c7e63d2 docs: regenerate after issue_fields schema update 2026-05-27 16:26:36 +02:00
Kelsey Myers dff18de98e docs update 2026-05-27 16:26:36 +02:00
Iulia B b91c3b5b3a feat(issue-fields): support issue_fields in issue_write using fullDatabaseId
- Expose fullDatabaseId (BigInt) in list_issue_fields
- Add issue_fields parameter to issue_write for setting field values
- Support single-select fields via field_option_name resolution
- Add REST API field value extraction in get_issue responses
- Update minimal types with IssueFieldValue for REST responses

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-27 16:26:36 +02:00
Sam Morrow b473a5afd1 feat(http): ignore proxy forwarding headers by default
X-Forwarded-Host and X-Forwarded-Proto were unconditionally honored when
constructing OAuth resource metadata URLs. In HTTP-mode deployments that
do not set --base-url and are not fronted by a proxy that strips these
headers, this lets an on-path client influence the URL advertised in
WWW-Authenticate and the /.well-known/oauth-protected-resource body.

This is a hardening change rather than a true vulnerability — exploiting
it requires HTTP without --base-url plus an attacker already positioned
to inject the header — but the unsafe default is worth closing.

Default behavior now derives host/scheme from r.Host and the TLS state.
Setups that rely on a trusted internal forwarder (e.g. an in-cluster
gateway that needs to preserve the originating hostname per request) can
opt back in with --trust-proxy-headers / GITHUB_TRUST_PROXY_HEADERS=1.
--base-url continues to take precedence in all cases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-27 07:29:04 +02:00
Sam Morrow 1add5fe231 docs: auto-generate per-flag tool lists for insiders and feature flags (#2521)
Adds two auto-generated documentation sections that describe how
feature flags shape the tool surface:

- docs/insiders-features.md gets a per-flag block under its existing
  hand-written prose. Each Insiders flag whose tools differ from the
  default surface is listed with the full tool schema rendered through
  the same writer used for README, so contributors can see exactly what
  Insiders Mode adds or changes.
- docs/feature-flags.md is new and gives the same treatment to every
  flag in AllowedFeatureFlags (user-controllable flags). It links back
  to the Insiders doc for the auto-enabled subset.

Both sections are produced by a single generator that diffs the
flag-on inventory against the default-flagged inventory and reports any
tool that is new or has a different InputSchema/Meta. No reason
classification - just tools and their schemas, kept intentionally
simple so contributors don't have to update the generator when adding
a new flag.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-22 00:31:39 +02:00
Ross Tarrant f929c58c6b feat: Add CSV output format for default list tools under insiders mode (#2450)
* Add CSV output for list tools under insiders mode

* fix: resolve rebase feature flag conflicts

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

* Simplify feature-flag handling: collapse CSV dual-variant + skip filtering when no checker (#2516)

* refactor: generic toolset+name sort, clarify feature flag intent

Address review feedback on #2450:

- Collapse the three near-identical sort helpers in pkg/inventory/filters.go
  into a generic sortByToolsetThenName so adding new inventory item types
  doesn't require copying the comparator.
- Expand the doc comments on the three *WithoutFeatureFiltering helpers to
  spell out why they exist: HTTP mode builds a static (process-wide)
  inventory as an upper bound, but per-request feature flags from headers
  (X-MCP-Features, X-MCP-Insiders) are evaluated later, so feature-flagged
  variants must be preserved here.
- Strengthen the doc comment on ResolveFeatureFlags to make the contract
  explicit: user-supplied flags are validated against AllowedFeatureFlags,
  but insiders expansion deliberately is not — InsidersFeatureFlags may
  include server-controlled flags that are not user-toggleable.

CORS comments are intentionally left for the PR author.

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

* docs(feature-flags): clarify allowed and insiders sets are independent

Also add tests covering:
- a user-toggleable flag (FeatureFlagIssuesGranular) that insiders does
  not turn on automatically
- insiders mode not turning on user-only allowed flags

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

* refactor(inventory): collapse three *WithoutFeatureFiltering helpers into StaticUpperBound

The three parallel methods (AvailableToolsWithoutFeatureFiltering,
AvailableResourceTemplatesWithoutFeatureFiltering,
AvailablePromptsWithoutFeatureFiltering) were always called as a triple
in exactly two places: HTTP buildStaticInventory and its test mirror.
They exist because the dual-variant pattern (sibling tools with mirrored
FeatureFlagEnable / FeatureFlagDisable on the same name, e.g. CSV output)
makes feature filtering at static-build time impossible — both variants
must be kept and resolved per-request.

Replace the three with one method, Inventory.StaticUpperBound(ctx), that
returns (tools, resources, prompts) and carries the rationale in its
doc comment. Reduces API surface, eliminates the triplication, and makes
the single "skip feature filtering" concept obvious to readers.

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

* refactor: simplify feature-flag handling

Two related simplifications, both about treating insiders as a meta flag
that expands once at startup and then stops mattering:

- Collapse CSV's dual-variant pattern into a single tool whose handler
  performs a runtime feature-flag check via deps.IsFeatureEnabled. CSV
  is a pure response-format toggle, not a schema change, so it does not
  need the dual-name pattern that genuine schema variants (granular
  issues/PRs) still use.

- When no feature checker is installed, skip feature-flag filtering and
  return the full upper bound. The static HTTP inventory now uses plain
  AvailableTools/Resources/Prompts; the per-request inventory always
  installs a checker, so MCP registration (which serves a tool name once)
  always sees a deduplicated set. The bespoke StaticUpperBound helper and
  the isToolEnabledWithFeatureFlags split go away.

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

* ci(mcp-diff): add insiders + per-feature configs

The mcp-diff matrix now includes:
  - --insiders (and --insiders --read-only)
  - one config per github.AllowedFeatureFlags entry, generated by
    script/print-mcp-diff-configs so new user-controllable flags get
    diffed automatically without editing the workflow

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

* docs(insiders): explain feature-flag resolution for contributors

Adds a 'How feature flags are resolved' section covering:
  - Insiders is a meta flag, like 'all'/'default' for toolsets
  - User input -> allowlist filter -> insiders expansion ->
    server-side fallback (remote only)
  - AllowedFeatureFlags vs InsidersFeatureFlags are independent
  - How to add a new feature flag, including the
    TestGitHubPackageDoesNotReadInsidersMode guard

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

* refactor(inventory): make feature-flag gating a regular ToolFilter

Move tool feature-flag evaluation out of isToolEnabled and into a
ToolFilter installed at the head of the pipeline by Build() when
WithFeatureChecker received a non-nil checker. The 'no checker = no
filtering' contract is now expressed structurally (the filter isn't
installed) instead of by a runtime nil check inside the helper.

Resources and prompts have no filter pipeline, so they call the now-pure
featureFlagAllowed helper behind an explicit r.featureChecker != nil
guard at the iteration site.

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

* perf(inventory): cache extracted toolset IDs in sort comparator

Avoid evaluating the extractor closures up to three times per comparison.

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

---------

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

* fix: correct MCP features header in cors

* docs: regenerate README for CSV output toolset

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

* fix: remove duplicate MCPFeaturesHeader from CORS headers

* ci(mcp-diff): add streamable-http job with header-based configs

Adds a sibling mcp-diff-http job that exercises the streamable-http
transport against a shared HTTP server, with per-config settings supplied
via X-MCP-* request headers — mirroring how the remote server is invoked
in production (server-side defaults + per-user header overrides).

The config generator gains a -transport flag:
- stdio (default, unchanged behaviour)
- http-headers (emits headers-only configs targeting a shared server)

Two new combined entries layer multiple headers together as a smoke test
for header-merging regressions.

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

* docs: regenerate after merging main

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 <info@sam-morrow.com>
Co-authored-by: sammorrowdrums <sammorrowdrums@github.com>
2026-05-21 16:50:55 +02:00