Skip to content

Commit 963262d

Browse files
author
The Red Thread
authored
Merge branch 'main' into aalmanasir-fix-draft-pr-conversion
2 parents 3486d19 + 0ea1f77 commit 963262d

40 files changed

Lines changed: 4865 additions & 429 deletions

README.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ To keep your GitHub PAT secure and reusable across different MCP hosts:
247247
The flag `--gh-host` and the environment variable `GITHUB_HOST` can be used to set
248248
the hostname for GitHub Enterprise Server or GitHub Enterprise Cloud with data residency.
249249

250-
- For GitHub Enterprise Server, prefix the hostname with the `https://` URI scheme, as it otherwise defaults to `http://`, which GitHub Enterprise Server does not support.
250+
- For GitHub Enterprise Server, prefix the hostname with the `https://` URI scheme. HTTPS is required and enforced: non-HTTPS hosts are refused so that credentials are never sent over cleartext (the only exception is a loopback host such as `http://localhost` for local development).
251251
- For GitHub Enterprise Cloud with data residency, use `https://YOURSUBDOMAIN.ghe.com` as the hostname.
252252

253253
``` json
@@ -926,7 +926,7 @@ The following sets of tools are available:
926926
- **Required OAuth Scopes**: `repo`
927927
- `assignees`: Usernames to assign to this issue (string[], optional)
928928
- `body`: Issue body content (string, optional)
929-
- `duplicate_of`: Issue number that this issue is a duplicate of. Only used when state_reason is 'duplicate'. (number, optional)
929+
- `duplicate_of`: Issue number that this issue is a duplicate of. Required when state_reason is 'duplicate'. (number, optional)
930930
- `issue_fields`: Issue field values to set or clear. Each item requires 'field_name' and exactly one of 'value', 'field_option_name', or 'delete: true'. (object[], optional)
931931
- `issue_number`: Issue number to update (number, optional)
932932
- `labels`: Labels to apply to this issue (string[], optional)
@@ -941,7 +941,7 @@ The following sets of tools are available:
941941
- `state`: New state (string, optional)
942942
- `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
943943
- `title`: Issue title (string, optional)
944-
- `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)
944+
- `type`: Type of this issue. For updates, pass null to remove the current type. Only use if issue types are enabled for this repository. Use list_issue_types to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string | null, optional)
945945

946946
- **list_issue_fields** - List issue fields
947947
- **Required OAuth Scopes (any of)**: `repo`, `read:org`
@@ -1099,6 +1099,7 @@ The following sets of tools are available:
10991099
- `owner_type`: Owner type (user or org). If not provided, will be automatically detected. (string, optional)
11001100
- `project_number`: The project's number. (number, optional)
11011101
- `status_update_id`: The node ID of the project status update. Required for 'get_project_status_update' method. (string, optional)
1102+
- `view_id`: The node ID of the project view. Required for 'get_project_view' method. (string, optional)
11021103

11031104
- **projects_list** - List GitHub Projects resources
11041105
- **Required OAuth Scopes**: `read:project`
@@ -1111,13 +1112,14 @@ The following sets of tools are available:
11111112
- `owner`: The owner (user or organization login). The name is not case sensitive. (string, required)
11121113
- `owner_type`: Owner type (user or org). If not provided, will automatically try both. (string, optional)
11131114
- `per_page`: Results per page (max 50) (number, optional)
1114-
- `project_number`: The project's number. Required for 'list_project_fields', 'list_project_items', and 'list_project_status_updates' methods. (number, optional)
1115+
- `project_number`: The project's number. Required for 'list_project_fields', 'list_project_items', 'list_project_views', and 'list_project_status_updates' methods. (number, optional)
11151116
- `query`: Filter/query string. For list_projects: filter by title text and state (e.g. "roadmap is:open"). For list_project_items: advanced filtering using GitHub's project filtering syntax. (string, optional)
11161117

11171118
- **projects_write** - Manage GitHub Projects
11181119
- **Required OAuth Scopes**: `project`
11191120
- `body`: The body of the status update (markdown). Used for 'create_project_status_update' method. (string, optional)
11201121
- `field_name`: The name of the iteration field (e.g. 'Sprint'). Required for 'create_iteration_field' method. (string, optional)
1122+
- `filter`: Saved view filter; omit on update to preserve it, or pass null to clear it. (string | null, optional)
11211123
- `issue_number`: The issue number. Required for 'add_project_item' when item_type is 'issue'. Also accepted by 'update_project_item' to resolve the item by issue number (combine with item_owner and item_repo). (number, optional)
11221124
- `item_id`: The project item ID. Required for 'delete_project_item'. For 'update_project_item', provide either item_id, or (item_owner + item_repo + issue_number) to resolve the item by issue. (number, optional)
11231125
- `item_owner`: The owner (user or organization) of the repository containing the issue or pull request. Required for 'add_project_item' method. Also accepted by 'update_project_item' when resolving the item by issue number. (string, optional)
@@ -1126,7 +1128,9 @@ The following sets of tools are available:
11261128
- `items`: The items to update with the top-level 'updated_field'. Required for 'update_project_items'; prefer it over calling 'update_project_item' in a loop. Each entry must match exactly one reference variant: 'node_id', numeric 'item_id', or 'item_owner' + 'item_repo' + 'issue_number'. Limit: 50 items per call. (object[], optional)
11271129
- `iteration_duration`: Duration in days for iterations of the field (e.g. 7 for weekly, 14 for bi-weekly). Required for 'create_iteration_field' method. (number, optional)
11281130
- `iterations`: Custom iterations for 'create_iteration_field' method. Only set this when you need iterations with varying durations, breaks between them, or specific titles. Otherwise omit it: GitHub auto-creates three iterations of 'iteration_duration' days starting on 'start_date', which is the right choice for most cases. (object[], optional)
1131+
- `layout`: View layout; required when creating a view. (string, optional)
11291132
- `method`: The method to execute (string, required)
1133+
- `name`: View name; required when creating a view. (string, optional)
11301134
- `owner`: The project owner (user or organization login). The name is not case sensitive. (string, required)
11311135
- `owner_type`: Owner type (user or org). Required for 'create_project' method. If not provided for other methods, will be automatically detected. (string, optional)
11321136
- `project_number`: The project's number. Required for all methods except 'create_project'. (number, optional)
@@ -1136,6 +1140,9 @@ The following sets of tools are available:
11361140
- `target_date`: The target date of the status update in YYYY-MM-DD format. Used for 'create_project_status_update' method. (string, optional)
11371141
- `title`: The project title. Required for 'create_project' method. (string, optional)
11381142
- `updated_field`: The field/value to apply, using {"id": 123, "value": ...} or {"name": "Status", "value": ...}; null clears the field. Required for 'update_project_item' and 'update_project_items', where one top-level field/value applies to every item in a batch. For 'update_project_item' SINGLE_SELECT fields, the name form accepts option names; the ID form expects an option ID. (object, optional)
1143+
- `view_id`: Project view node ID for update or delete; must belong to owner/project_number. (string, optional)
1144+
- `visible_field_names`: Ordered project field names to show on create or replace on update; omit on update to preserve, or pass [] to reset. Mutually exclusive with visible_fields. Roadmap accepts only []. (string[], optional)
1145+
- `visible_fields`: Ordered project field database IDs to show on create or replace on update; omit on update to preserve, or pass [] to reset. Mutually exclusive with visible_field_names. Roadmap accepts only []. (string[], optional)
11391146

11401147
</details>
11411148

cmd/github-mcp-server/generate_docs.go

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -273,19 +273,7 @@ func writeToolDoc(buf *strings.Builder, tool inventory.ServerTool) {
273273
requiredStr = "required"
274274
}
275275

276-
var typeStr string
277-
278-
// Get the type and description
279-
switch prop.Type {
280-
case "array":
281-
if prop.Items != nil {
282-
typeStr = prop.Items.Type + "[]"
283-
} else {
284-
typeStr = "array"
285-
}
286-
default:
287-
typeStr = prop.Type
288-
}
276+
typeStr := schemaTypeString(prop)
289277

290278
// Indent any continuation lines in the description to maintain markdown formatting
291279
description := indentMultilineDescription(prop.Description, " ")
@@ -300,6 +288,40 @@ func writeToolDoc(buf *strings.Builder, tool inventory.ServerTool) {
300288
}
301289
}
302290

291+
func schemaTypeString(schema *jsonschema.Schema) string {
292+
switch {
293+
case schema.Type == "array":
294+
if schema.Items != nil {
295+
return schema.Items.Type + "[]"
296+
}
297+
return "array"
298+
case schema.Type != "":
299+
return schema.Type
300+
case len(schema.Types) > 0:
301+
return strings.Join(schema.Types, " | ")
302+
}
303+
304+
var union []*jsonschema.Schema
305+
switch {
306+
case len(schema.AnyOf) > 0:
307+
union = schema.AnyOf
308+
case len(schema.OneOf) > 0:
309+
union = schema.OneOf
310+
default:
311+
// A schema without type constraints accepts any value.
312+
return "any"
313+
}
314+
315+
types := make([]string, 0, len(union))
316+
for _, member := range union {
317+
memberType := schemaTypeString(member)
318+
if !slices.Contains(types, memberType) {
319+
types = append(types, memberType)
320+
}
321+
}
322+
return strings.Join(types, " | ")
323+
}
324+
303325
// scopesEqual checks if two scope slices contain the same elements (order-independent)
304326
func scopesEqual(a, b []string) bool {
305327
if len(a) != len(b) {

cmd/github-mcp-server/main_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"path/filepath"
66
"testing"
77

8+
"github.com/google/jsonschema-go/jsonschema"
89
"github.com/stretchr/testify/assert"
910
"github.com/stretchr/testify/require"
1011
)
@@ -36,3 +37,29 @@ func TestGitHubAppFlagsAreStdioOnly(t *testing.T) {
3637
assert.NotNil(t, stdioCmd.Flags().Lookup("app-id"))
3738
assert.Nil(t, httpCmd.Flags().Lookup("app-id"))
3839
}
40+
41+
func TestSchemaTypeString(t *testing.T) {
42+
tests := []struct {
43+
name string
44+
schema *jsonschema.Schema
45+
want string
46+
}{
47+
{name: "type", schema: &jsonschema.Schema{Type: "string"}, want: "string"},
48+
{name: "types", schema: &jsonschema.Schema{Types: []string{"string", "number"}}, want: "string | number"},
49+
{name: "unconstrained", schema: &jsonschema.Schema{}, want: "any"},
50+
{name: "anyOf", schema: &jsonschema.Schema{AnyOf: []*jsonschema.Schema{{Type: "string"}, {Type: "null"}}}, want: "string | null"},
51+
{name: "oneOf", schema: &jsonschema.Schema{OneOf: []*jsonschema.Schema{{Type: "number"}, {Type: "string"}}}, want: "number | string"},
52+
{
53+
name: "array",
54+
schema: &jsonschema.Schema{Type: "array", Items: &jsonschema.Schema{Type: "string"}},
55+
want: "string[]",
56+
},
57+
{name: "untyped array", schema: &jsonschema.Schema{Type: "array"}, want: "array"},
58+
}
59+
60+
for _, tc := range tests {
61+
t.Run(tc.name, func(t *testing.T) {
62+
assert.Equal(t, tc.want, schemaTypeString(tc.schema))
63+
})
64+
}
65+
}

docs/feature-flags.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ runtime behavior (such as output formatting) won't appear here.
5656
- **MCP App UI**: `ui://github-mcp-server/issue-write`
5757
- `assignees`: Usernames to assign to this issue (string[], optional)
5858
- `body`: Issue body content (string, optional)
59-
- `duplicate_of`: Issue number that this issue is a duplicate of. Only used when state_reason is 'duplicate'. (number, optional)
59+
- `duplicate_of`: Issue number that this issue is a duplicate of. Required when state_reason is 'duplicate'. (number, optional)
6060
- `issue_fields`: Issue field values to set or clear. Each item requires 'field_name' and exactly one of 'value', 'field_option_name', or 'delete: true'. (object[], optional)
6161
- `issue_number`: Issue number to update (number, optional)
6262
- `labels`: Labels to apply to this issue (string[], optional)
@@ -71,7 +71,7 @@ runtime behavior (such as output formatting) won't appear here.
7171
- `state`: New state (string, optional)
7272
- `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
7373
- `title`: Issue title (string, optional)
74-
- `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)
74+
- `type`: Type of this issue. For updates, pass null to remove the current type. Only use if issue types are enabled for this repository. Use list_issue_types to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string | null, optional)
7575

7676
- **ui_get** - Get UI data
7777
- **Required OAuth Scopes (any of)**: `repo`, `read:org`
@@ -200,7 +200,7 @@ runtime behavior (such as output formatting) won't appear here.
200200
- `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)
201201
- `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)
202202
- `issue_number`: The issue number to update (number, required)
203-
- `issue_type`: The issue type to set (string, required)
203+
- `issue_type`: The issue type to set, or null to remove the current type (string | null, required)
204204
- `owner`: Repository owner (username or organization) (string, required)
205205
- `rationale`: One concise sentence explaining what specifically about the issue led you to choose this type. State the concrete signal (e.g. 'Reports a crash when saving' → bug, 'Asks for dark mode support' → feature). (string, optional)
206206
- `repo`: Repository name (string, required)
@@ -338,4 +338,15 @@ runtime behavior (such as output formatting) won't appear here.
338338
- 'blocked_by' - the subject issue is blocked by the related issue.
339339
- 'blocking' - the subject issue blocks the related issue. (string, required)
340340

341+
### `duplicate_detection`
342+
343+
- **find_duplicate** - Find duplicate issues
344+
- **Required OAuth Scopes**: `repo`
345+
- `confidence_threshold`: Minimum similarity threshold a candidate must meet to be returned; higher values are stricter. When omitted, the API's high-precision default is used. The scale is defined by the API, so no client-side bounds are enforced. (number, optional)
346+
- `issue_number`: The number of the existing issue to find duplicates for (number, required)
347+
- `owner`: The owner of the repository (string, required)
348+
- `page`: Page number for pagination (min 1) (number, optional)
349+
- `perPage`: Results per page for pagination (min 1, max 100) (number, optional)
350+
- `repo`: The name of the repository (string, required)
351+
341352
<!-- END AUTOMATED FEATURE FLAG TOOLS -->

docs/insiders-features.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ The list below is generated from the Go source. It covers tool **inventory and s
5050
- **MCP App UI**: `ui://github-mcp-server/issue-write`
5151
- `assignees`: Usernames to assign to this issue (string[], optional)
5252
- `body`: Issue body content (string, optional)
53-
- `duplicate_of`: Issue number that this issue is a duplicate of. Only used when state_reason is 'duplicate'. (number, optional)
53+
- `duplicate_of`: Issue number that this issue is a duplicate of. Required when state_reason is 'duplicate'. (number, optional)
5454
- `issue_fields`: Issue field values to set or clear. Each item requires 'field_name' and exactly one of 'value', 'field_option_name', or 'delete: true'. (object[], optional)
5555
- `issue_number`: Issue number to update (number, optional)
5656
- `labels`: Labels to apply to this issue (string[], optional)
@@ -65,7 +65,7 @@ The list below is generated from the Go source. It covers tool **inventory and s
6565
- `state`: New state (string, optional)
6666
- `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
6767
- `title`: Issue title (string, optional)
68-
- `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)
68+
- `type`: Type of this issue. For updates, pass null to remove the current type. Only use if issue types are enabled for this repository. Use list_issue_types to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string | null, optional)
6969

7070
- **ui_get** - Get UI data
7171
- **Required OAuth Scopes (any of)**: `repo`, `read:org`
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"annotations": {
3+
"idempotentHint": false,
4+
"readOnlyHint": true,
5+
"title": "Find duplicate issues"
6+
},
7+
"description": "Find likely duplicate issues for an existing issue in a GitHub repository. This is a read-only search scoped to the source issue's repository: it returns ranked candidate issues with a similarity score and confidence, and does not close, link, comment on, or otherwise modify any issue.",
8+
"inputSchema": {
9+
"properties": {
10+
"confidence_threshold": {
11+
"description": "Minimum similarity threshold a candidate must meet to be returned; higher values are stricter. When omitted, the API's high-precision default is used. The scale is defined by the API, so no client-side bounds are enforced.",
12+
"type": "number"
13+
},
14+
"issue_number": {
15+
"description": "The number of the existing issue to find duplicates for",
16+
"type": "number"
17+
},
18+
"owner": {
19+
"description": "The owner of the repository",
20+
"type": "string"
21+
},
22+
"page": {
23+
"description": "Page number for pagination (min 1)",
24+
"minimum": 1,
25+
"type": "number"
26+
},
27+
"perPage": {
28+
"description": "Results per page for pagination (min 1, max 100)",
29+
"maximum": 100,
30+
"minimum": 1,
31+
"type": "number"
32+
},
33+
"repo": {
34+
"description": "The name of the repository",
35+
"type": "string"
36+
}
37+
},
38+
"required": [
39+
"owner",
40+
"repo",
41+
"issue_number"
42+
],
43+
"type": "object"
44+
},
45+
"name": "find_duplicate"
46+
}

pkg/github/__toolsnaps__/issue_write.snap

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"type": "string"
2929
},
3030
"duplicate_of": {
31-
"description": "Issue number that this issue is a duplicate of. Only used when state_reason is 'duplicate'.",
31+
"description": "Issue number that this issue is a duplicate of. Required when state_reason is 'duplicate'.",
3232
"type": "number"
3333
},
3434
"issue_fields": {
@@ -120,8 +120,16 @@
120120
"type": "string"
121121
},
122122
"type": {
123-
"description": "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.",
124-
"type": "string"
123+
"anyOf": [
124+
{
125+
"minLength": 1,
126+
"type": "string"
127+
},
128+
{
129+
"type": "null"
130+
}
131+
],
132+
"description": "Type of this issue. For updates, pass null to remove the current type. Only use if issue types are enabled for this repository. Use list_issue_types to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter."
125133
}
126134
},
127135
"required": [

pkg/github/__toolsnaps__/list_label.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"readOnlyHint": true,
55
"title": "List labels from a repository"
66
},
7-
"description": "List labels from a repository",
7+
"description": "List labels from a repository, ordered by issue count (descending) so the most-used labels are returned first",
88
"inputSchema": {
99
"properties": {
1010
"owner": {

0 commit comments

Comments
 (0)