Skip to content

Commit 5207576

Browse files
owenniblockCopilot
andcommitted
Merge main + regression test for IssueRequest omitempty drop
Resolve merge conflicts in feature-flags.md and insiders-features.md by regenerating with `generate-docs`. Delete the FF-snap variants that were removed on main. Add Test_IssueRequest_EmptyFieldValues_OmittedByJSON to pin the exact behaviour that motivates the GraphQL deleteIssueFieldValue mutation in this PR: an empty []*IssueRequestFieldValue{} is dropped from the JSON body by go-github's `omitempty` tag, so a REST PATCH alone can never clear a field's last value. If go-github ever drops the tag we'll know straight away. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2 parents 24d015f + a378370 commit 5207576

92 files changed

Lines changed: 9434 additions & 2284 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/build-ui/action.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ runs:
1212
pkg/github/ui_dist/get-me.html
1313
pkg/github/ui_dist/issue-write.html
1414
pkg/github/ui_dist/pr-write.html
15-
key: ui-dist-v1-${{ hashFiles('ui/package-lock.json', 'ui/package.json', 'ui/index.html', 'ui/tsconfig*.json', 'ui/vite.config.ts', 'ui/src/**', 'ui/scripts/**') }}
15+
pkg/github/ui_dist/pr-edit.html
16+
key: ui-dist-v2-${{ hashFiles('ui/package-lock.json', 'ui/package.json', 'ui/index.html', 'ui/tsconfig*.json', 'ui/vite.config.ts', 'ui/src/**', 'ui/scripts/**') }}
1617
enableCrossOsArchive: true
1718

1819
- name: Set up Node.js

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:26-alpine@sha256:7c6af15abe4e3de859690e7db171d0d711bf37d27528eddfe625b2fe89e097f8 AS ui-build
1+
FROM node:26-alpine@sha256:3ad34ca6292aec4a91d8ddeb9229e29d9c2f689efd0dd242860889ac71842eba AS ui-build
22
WORKDIR /app
33
COPY ui/package*.json ./ui/
44
RUN cd ui && npm ci
@@ -7,7 +7,7 @@ COPY ui/ ./ui/
77
RUN mkdir -p ./pkg/github/ui_dist && \
88
cd ui && npm run build
99

10-
FROM golang:1.25.10-alpine@sha256:8d22e29d960bc50cd025d93d5b7c7d220b1ee9aa7a239b3c8f55a57e987e8d45 AS build
10+
FROM golang:1.25.11-alpine@sha256:8d95af53d0d58e1759ddb4028285d9b1239067e4fbf4f544618cad0f60fbc354 AS build
1111
ARG VERSION="dev"
1212

1313
# Set the working directory
@@ -30,7 +30,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \
3030
-o /bin/github-mcp-server ./cmd/github-mcp-server
3131

3232
# Make a stage to run the app
33-
FROM gcr.io/distroless/base-debian12@sha256:58695f439f772a00009c8f6be4c183f824c1f556d74b313c30900f167e4772f8
33+
FROM gcr.io/distroless/base-debian12@sha256:e7e678c88c59e70e105a46549bb3fbfb3d732ee3b4afd3a19fdab2e15afaa6b3
3434

3535
# Add required MCP server annotation
3636
LABEL io.modelcontextprotocol.server.name="io.github.github/github-mcp-server"

README.md

Lines changed: 41 additions & 17 deletions
Large diffs are not rendered by default.

cmd/github-mcp-server/generate_docs.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,15 @@ func writeToolDoc(buf *strings.Builder, tool inventory.ServerTool) {
221221

222222
// OAuth scopes if present
223223
if len(tool.RequiredScopes) > 0 {
224-
fmt.Fprintf(buf, " - **Required OAuth Scopes**: `%s`\n", strings.Join(tool.RequiredScopes, "`, `"))
224+
// Scope filtering uses "any of" semantics (see scopes.HasRequiredScopes),
225+
// so when multiple required scopes are listed, render them as alternatives
226+
// rather than implying all are required.
227+
scopeList := "`" + strings.Join(tool.RequiredScopes, "`, `") + "`"
228+
if len(tool.RequiredScopes) > 1 {
229+
fmt.Fprintf(buf, " - **Required OAuth Scopes (any of)**: %s\n", scopeList)
230+
} else {
231+
fmt.Fprintf(buf, " - **Required OAuth Scopes**: %s\n", scopeList)
232+
}
225233

226234
// Only show accepted scopes if they differ from required scopes
227235
if len(tool.AcceptedScopes) > 0 && !scopesEqual(tool.RequiredScopes, tool.AcceptedScopes) {
@@ -257,6 +265,8 @@ func writeToolDoc(buf *strings.Builder, tool inventory.ServerTool) {
257265
}
258266
sort.Strings(paramNames)
259267

268+
conditional := inventory.ConditionalSchemaPropertyDescriptions()
269+
260270
for i, propName := range paramNames {
261271
prop := schema.Properties[propName]
262272
required := slices.Contains(schema.Required, propName)
@@ -282,7 +292,11 @@ func writeToolDoc(buf *strings.Builder, tool inventory.ServerTool) {
282292
// Indent any continuation lines in the description to maintain markdown formatting
283293
description := indentMultilineDescription(prop.Description, " ")
284294

285-
fmt.Fprintf(buf, " - `%s`: %s (%s, %s)", propName, description, typeStr, requiredStr)
295+
if cond, isConditional := conditional[propName]; isConditional {
296+
fmt.Fprintf(buf, " - `%s`: %s (%s, %s, conditional — %s)", propName, description, typeStr, requiredStr, cond)
297+
} else {
298+
fmt.Fprintf(buf, " - `%s`: %s (%s, %s)", propName, description, typeStr, requiredStr)
299+
}
286300
if i < len(paramNames)-1 {
287301
buf.WriteString("\n")
288302
}

cmd/github-mcp-server/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ var (
138138
Version: version,
139139
Host: viper.GetString("host"),
140140
Port: viper.GetInt("port"),
141+
ListenHost: viper.GetString("listen-host"),
141142
BaseURL: viper.GetString("base-url"),
142143
ResourcePath: viper.GetString("base-path"),
143144
ExportTranslations: viper.GetBool("export-translations"),
@@ -184,6 +185,7 @@ func init() {
184185

185186
// HTTP-specific flags
186187
httpCmd.Flags().Int("port", 8082, "HTTP server port")
188+
httpCmd.Flags().String("listen-host", "", "Host the HTTP server binds to (e.g. 127.0.0.1). Empty binds to all interfaces.")
187189
httpCmd.Flags().String("base-url", "", "Base URL where this server is publicly accessible (for OAuth resource metadata)")
188190
httpCmd.Flags().String("base-path", "", "Externally visible base path for the HTTP server (for OAuth resource metadata)")
189191
httpCmd.Flags().Bool("scope-challenge", false, "Enable OAuth scope challenge responses")
@@ -204,6 +206,7 @@ func init() {
204206
_ = viper.BindPFlag("insiders", rootCmd.PersistentFlags().Lookup("insiders"))
205207
_ = viper.BindPFlag("repo-access-cache-ttl", rootCmd.PersistentFlags().Lookup("repo-access-cache-ttl"))
206208
_ = viper.BindPFlag("port", httpCmd.Flags().Lookup("port"))
209+
_ = viper.BindPFlag("listen-host", httpCmd.Flags().Lookup("listen-host"))
207210
_ = viper.BindPFlag("base-url", httpCmd.Flags().Lookup("base-url"))
208211
_ = viper.BindPFlag("base-path", httpCmd.Flags().Lookup("base-path"))
209212
_ = viper.BindPFlag("scope-challenge", httpCmd.Flags().Lookup("scope-challenge"))

docs/feature-flags.md

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -44,40 +44,20 @@ runtime behavior (such as output formatting) won't appear here.
4444
- `maintainer_can_modify`: Allow maintainer edits (boolean, optional)
4545
- `owner`: Repository owner (string, required)
4646
- `repo`: Repository name (string, required)
47+
- `reviewers`: GitHub usernames or ORG/team-slug team reviewers to request reviews from (string[], optional)
48+
- `show_ui`: Whether to render the MCP App form instead of executing the request immediately. Defaults to true. Set to false to skip the form and execute directly — useful when you have all required values (especially ones the form does not collect, like reviewers) and the user has already confirmed the action. (boolean, optional, conditional — visible when remote_mcp_ui_apps is enabled unless the client explicitly indicates it does not support io.modelcontextprotocol/ui)
4749
- `title`: PR title (string, required)
4850

4951
- **get_me** - Get my user profile
5052
- **MCP App UI**: `ui://github-mcp-server/get-me`
5153
- No parameters required
5254

53-
- **issue_write** - Create or update issue
55+
- **issue_write** - Create or update issue/pull request
5456
- **Required OAuth Scopes**: `repo`
5557
- **MCP App UI**: `ui://github-mcp-server/issue-write`
5658
- `assignees`: Usernames to assign to this issue (string[], optional)
5759
- `body`: Issue body content (string, optional)
5860
- `duplicate_of`: Issue number that this issue is a duplicate of. Only used when state_reason is 'duplicate'. (number, optional)
59-
- `issue_number`: Issue number to update (number, optional)
60-
- `labels`: Labels to apply to this issue (string[], optional)
61-
- `method`: Write operation to perform on a single issue.
62-
Options are:
63-
- 'create' - creates a new issue.
64-
- 'update' - updates an existing issue.
65-
(string, required)
66-
- `milestone`: Milestone number (number, optional)
67-
- `owner`: Repository owner (string, required)
68-
- `repo`: Repository name (string, required)
69-
- `state`: New state (string, optional)
70-
- `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
71-
- `title`: Issue title (string, optional)
72-
- `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
73-
74-
### `remote_mcp_issue_fields`
75-
76-
- **issue_write** - Create or update issue
77-
- **Required OAuth Scopes**: `repo`
78-
- `assignees`: Usernames to assign to this issue (string[], optional)
79-
- `body`: Issue body content (string, optional)
80-
- `duplicate_of`: Issue number that this issue is a duplicate of. Only used when state_reason is 'duplicate'. (number, optional)
8161
- `issue_fields`: Issue field values to set or clear. Each item requires 'field_name' and exactly one of 'value', 'field_option_name', 'field_option_names', or 'delete: true'. (object[], optional)
8262
- `issue_number`: Issue number to update (number, optional)
8363
- `labels`: Labels to apply to this issue (string[], optional)
@@ -89,29 +69,32 @@ runtime behavior (such as output formatting) won't appear here.
8969
- `milestone`: Milestone number (number, optional)
9070
- `owner`: Repository owner (string, required)
9171
- `repo`: Repository name (string, required)
72+
- `show_ui`: Whether to render the MCP App form instead of executing the request immediately. Defaults to true. Set to false to skip the form and execute directly — useful when you have all required values (especially ones the form does not collect, like labels, assignees, milestone, type, issue_fields, or state changes) and the user has already confirmed the action. (boolean, optional, conditional — visible when remote_mcp_ui_apps is enabled unless the client explicitly indicates it does not support io.modelcontextprotocol/ui)
9273
- `state`: New state (string, optional)
9374
- `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
9475
- `title`: Issue title (string, optional)
95-
- `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
76+
- `type`: Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
9677

97-
- **list_issue_fields** - List issue fields
98-
- **Required OAuth Scopes**: `repo`, `read:org`
78+
- **ui_get** - Get UI data
79+
- **Required OAuth Scopes (any of)**: `repo`, `read:org`
9980
- **Accepted OAuth Scopes**: `admin:org`, `read:org`, `repo`, `write:org`
100-
- `owner`: The account owner of the repository or organization. The name is not case sensitive. (string, required)
101-
- `repo`: The name of the repository. When provided, returns fields for this specific repository (inherited from its organization). When omitted, returns org-level fields directly. (string, optional)
81+
- `method`: The type of data to fetch (string, required)
82+
- `owner`: Repository owner (required for all methods) (string, required)
83+
- `repo`: Repository name (required for labels, assignees, milestones, branches, issue fields, reviewers) (string, optional)
10284

103-
- **list_issues** - List issues
85+
- **update_pull_request** - Edit pull request
10486
- **Required OAuth Scopes**: `repo`
105-
- `after`: Cursor for pagination. Use the endCursor from the previous page's PageInfo for GraphQL APIs. (string, optional)
106-
- `direction`: Order direction. If provided, the 'orderBy' also needs to be provided. (string, optional)
107-
- `field_filters`: Filter by custom issue field values. Each entry takes a field_name and either 'value' (text, number, YYYY-MM-DD date, or single-select option name) or 'values' (multi-select option names). For multi-select fields, all listed values must be set on an issue for it to match (AND semantics) — to match any-of, make multiple list_issues calls and union the results. (object[], optional)
108-
- `labels`: Filter by labels (string[], optional)
109-
- `orderBy`: Order issues by field. If provided, the 'direction' also needs to be provided. (string, optional)
87+
- **MCP App UI**: `ui://github-mcp-server/pr-edit`
88+
- `base`: New base branch name (string, optional)
89+
- `body`: New description (string, optional)
90+
- `draft`: Mark pull request as draft (true) or ready for review (false) (boolean, optional)
91+
- `maintainer_can_modify`: Allow maintainer edits (boolean, optional)
11092
- `owner`: Repository owner (string, required)
111-
- `perPage`: Results per page for pagination (min 1, max 100) (number, optional)
93+
- `pullNumber`: Pull request number to update (number, required)
11294
- `repo`: Repository name (string, required)
113-
- `since`: Filter by date (ISO 8601 timestamp) (string, optional)
114-
- `state`: Filter by state, by default both open and closed issues are returned when not provided (string, optional)
95+
- `reviewers`: GitHub usernames or ORG/team-slug team reviewers to request reviews from (string[], optional)
96+
- `state`: New state (string, optional)
97+
- `title`: New title (string, optional)
11598

11699
### `issues_granular`
117100

@@ -148,7 +131,7 @@ runtime behavior (such as output formatting) won't appear here.
148131

149132
- **set_issue_fields** - Set Issue Fields
150133
- **Required OAuth Scopes**: `repo`
151-
- `fields`: Array of issue field values to set. Each element must have a 'field_id' (string, the GraphQL node ID of the field) and exactly one value field: 'text_value' for text fields, 'number_value' for number fields, 'date_value' (ISO 8601 date string) for date fields, or 'single_select_option_id' (the GraphQL node ID of the option) for single select fields. Set 'delete' to true to remove a field value. (object[], required)
134+
- `fields`: Array of issue field values to set. Each element must have a 'field_id' (string, the GraphQL node ID of the field) and exactly one value field: 'text_value' for text fields, 'number_value' for number fields, 'date_value' (ISO 8601 date string) for date fields, 'single_select_option_id' (the GraphQL node ID of the option) for single select fields, or 'multi_select_option_ids' (an array of GraphQL node IDs) for multi select fields. Set 'delete' to true to remove a field value. (object[], required)
152135
- `issue_number`: The issue number to update (number, required)
153136
- `owner`: Repository owner (username or organization) (string, required)
154137
- `repo`: Repository name (string, required)
@@ -198,7 +181,7 @@ runtime behavior (such as output formatting) won't appear here.
198181

199182
- **update_issue_type** - Update Issue Type
200183
- **Required OAuth Scopes**: `repo`
201-
- `confidence`: How confident you are in this choice. Use 'high' for clear signal or explicit user request, 'medium' for reasonable inference with some ambiguity, 'low' for best guess with limited signal. (string, optional)
184+
- `confidence`: How confident you are in this choice. Use 'HIGH' for clear signal or explicit user request, 'MEDIUM' for reasonable inference with some ambiguity, 'LOW' for best guess with limited signal. (string, optional)
202185
- `is_suggestion`: If true, this issue type change is sent to the API as a suggestion (suggest:true) rather than an applied value. Whether the type is applied or recorded as a proposal is determined by the API. (boolean, optional)
203186
- `issue_number`: The issue number to update (number, required)
204187
- `issue_type`: The issue type to set (string, required)
@@ -287,4 +270,17 @@ runtime behavior (such as output formatting) won't appear here.
287270
- `repo`: Repository name (string, required)
288271
- `title`: The new title for the pull request (string, required)
289272

273+
### `file_blame`
274+
275+
- **get_file_blame** - Get file blame information
276+
- **Required OAuth Scopes**: `repo`
277+
- `after`: Cursor for pagination. Use the cursor from the previous response. (string, optional)
278+
- `end_line`: Optional 1-based ending line of the window of interest. Must be >= start_line when both are provided. (number, optional)
279+
- `owner`: Repository owner (username or organization) (string, required)
280+
- `path`: Path to the file in the repository, relative to the repository root (string, required)
281+
- `perPage`: Results per page for pagination (min 1, max 100) (number, optional)
282+
- `ref`: Git reference (branch, tag, or commit SHA). Defaults to the repository's default branch (HEAD). (string, optional)
283+
- `repo`: Repository name (string, required)
284+
- `start_line`: Optional 1-based starting line of the window of interest. Only ranges overlapping [start_line, end_line] are returned, clamped to the window. (number, optional)
285+
290286
<!-- END AUTOMATED FEATURE FLAG TOOLS -->

0 commit comments

Comments
 (0)