Skip to content

Commit a089b58

Browse files
committed
address feedback
1 parent 7463997 commit a089b58

13 files changed

Lines changed: 117 additions & 20 deletions

‎README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -620,9 +620,9 @@ Repository tools expose output schemas and typed `structuredContent` when the ne
620620

621621
For `get_file_contents`, directory results (including field projections) are structured arrays. File downloads and metadata-only responses use a `content` array of typed text, embedded-resource, or resource-link blocks, preserving status messages, text, base64 blobs, empty files, and download links. Text-only repository mutations such as starring and deletion also return a structured `message`; fork responses distinguish a repository reference from an in-progress message. Repository deletion still requires its existing confirmation flow before returning a completed result.
622622

623-
Structured repository results use compact DTOs rather than raw REST objects. Release lookups share the compact release shape with release listings; Git references retain the ref, object type, and SHA; annotated tags retain tag/message/tagger/target information; deletion commits retain SHA, message, authors, tree SHA, parent SHAs, and a human-facing `html_url` when available. Directory entries retain file metadata and useful HTML/download links, but omit API and hypermedia routing URLs even when those fields were requested in a projection. Legacy and unknown protocol versions retain the original text byte-for-byte; modern text-only JSON responses serialize the same compact DTO as `structuredContent`. Non-text resource blocks and tool errors retain their existing content behavior. Structured projections use the same sanitized and filtered source as the legacy formatter.
623+
Structured repository results use compact DTOs rather than raw REST objects. Release lookups share the compact release shape with release listings; Git references retain the ref, object type, and SHA; annotated tags retain tag/message/tagger/target information; deletion commits retain SHA, message, authors, tree SHA, parent SHAs, and a human-facing `html_url` when available. Directory entries retain file metadata and useful HTML/download links; API URLs are omitted by default but retained when explicitly selected. Legacy and unknown protocol versions retain the original text byte-for-byte; modern text-only JSON responses serialize the same compact DTO as `structuredContent`. Non-text resource blocks and tool errors retain their existing content behavior. Structured projections use the same sanitized and filtered source as the legacy formatter.
624624

625-
For `get_repository_tree`, `list_gists`, `get_gist`, `create_gist`, and `update_gist`, gist results retain file names, contents, sizes, language/type information, timestamps, visibility, and compact owner profiles, while omitting API/node and Git transport URLs. Tree entries omit their derivable API URLs. Create/update gist results contain the gist ID and HTML URL. Modern JSON text and structured content serialize the same compact result. Older or unknown protocol versions retain byte-for-byte legacy text without an output schema or structured content.
625+
For `get_repository_tree`, `list_gists`, `get_gist`, `create_gist`, and `update_gist`, gist results retain file names, contents, sizes, language/type information, timestamps, visibility, compact owner profiles, and `raw_url`/`git_pull_url` recovery handles, while omitting unrelated API/node/push URLs. Tree entries omit their derivable API URLs. Create/update gist results contain the gist ID and HTML URL. Modern JSON text and structured content serialize the same compact result. Older or unknown protocol versions retain byte-for-byte legacy text without an output schema or structured content.
626626

627627
The issue metadata (`list_issue_types`, `list_issue_fields`), comment (`add_issue_comment`, `update_issue_comment`), dependency (`issue_dependency_read`, `issue_dependency_write`), and duplicate-detection (`find_duplicate`) tools provide typed `outputSchema` and `structuredContent` for negotiated supported protocol versions `2026-07-28` or newer. Older or unknown protocol versions retain the same text responses without these fields. Tool errors do not return structured content. Dependency and duplicate-detection tools retain their existing feature gates.
628628

@@ -1424,7 +1424,7 @@ For negotiated supported protocol versions `2026-07-28` or newer, the consolidat
14241424

14251425
- **get_file_contents** - Get file or directory contents
14261426
- **OAuth Challenge Scopes**: `repo`
1427-
- `fields`: Subset of fields to return for each entry when the path is a directory. If omitted, all fields are returned. Ignored when the path is a single file. Use this to reduce response size when listing directories and you only need specific fields, e.g. just 'name' and 'type'. (string[], optional)
1427+
- `fields`: Subset of fields to return for each entry when the path is a directory. If omitted, modern clients receive compact entries without 'url' or 'git_url'; these API URLs require explicit selection. Legacy clients retain all GitHub API fields when omitted. Ignored when the path is a single file. Use this to reduce response size when listing directories and you only need specific fields, e.g. just 'name' and 'type'. (string[], optional)
14281428
- `owner`: Repository owner (username or organization) (string, required)
14291429
- `path`: Path to file/directory (string, optional)
14301430
- `ref`: Accepts optional git refs such as `refs/tags/{tag}`, `refs/heads/{branch}` or `refs/pull/{pr_number}/head` (string, optional)

‎pkg/github/__toolsnaps__/actions_get.snap‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@
109109
"not": {
110110
"description": "Any value."
111111
}
112+
},
113+
"workflow_run": {
114+
"not": {
115+
"type": "null"
116+
}
112117
}
113118
},
114119
"required": [
@@ -151,6 +156,11 @@
151156
},
152157
{
153158
"properties": {
159+
"artifact": {
160+
"not": {
161+
"type": "null"
162+
}
163+
},
154164
"logs": {
155165
"not": {
156166
"description": "Any value."
@@ -227,6 +237,11 @@
227237
"description": "Any value."
228238
}
229239
},
240+
"logs": {
241+
"not": {
242+
"type": "null"
243+
}
244+
},
230245
"method": {
231246
"enum": [
232247
"get_workflow_run_logs_url"

‎pkg/github/__toolsnaps__/actions_list.snap‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@
170170
"description": "Any value."
171171
}
172172
},
173+
"workflow_runs": {
174+
"not": {
175+
"type": "null"
176+
}
177+
},
173178
"workflows": {
174179
"not": {
175180
"description": "Any value."
@@ -192,6 +197,11 @@
192197
"list_workflow_jobs"
193198
]
194199
},
200+
"workflow_jobs": {
201+
"not": {
202+
"type": "null"
203+
}
204+
},
195205
"workflow_runs": {
196206
"not": {
197207
"description": "Any value."

‎pkg/github/__toolsnaps__/actions_run_trigger.snap‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@
9595
"description": "Any value."
9696
}
9797
},
98+
"dispatch": {
99+
"not": {
100+
"type": "null"
101+
}
102+
},
98103
"method": {
99104
"enum": [
100105
"run_workflow"
@@ -137,6 +142,11 @@
137142
"rerun_workflow_run"
138143
]
139144
},
145+
"rerun": {
146+
"not": {
147+
"type": "null"
148+
}
149+
},
140150
"rerun_failed": {
141151
"not": {
142152
"description": "Any value."
@@ -173,6 +183,11 @@
173183
"not": {
174184
"description": "Any value."
175185
}
186+
},
187+
"rerun_failed": {
188+
"not": {
189+
"type": "null"
190+
}
176191
}
177192
},
178193
"required": [
@@ -181,6 +196,11 @@
181196
},
182197
{
183198
"properties": {
199+
"cancel": {
200+
"not": {
201+
"type": "null"
202+
}
203+
},
184204
"delete_logs": {
185205
"not": {
186206
"description": "Any value."
@@ -218,6 +238,11 @@
218238
"description": "Any value."
219239
}
220240
},
241+
"delete_logs": {
242+
"not": {
243+
"type": "null"
244+
}
245+
},
221246
"dispatch": {
222247
"not": {
223248
"description": "Any value."

‎pkg/github/__toolsnaps__/get_file_contents.snap‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"inputSchema": {
2121
"properties": {
2222
"fields": {
23-
"description": "Subset of fields to return for each entry when the path is a directory. If omitted, all fields are returned. Ignored when the path is a single file. Use this to reduce response size when listing directories and you only need specific fields, e.g. just 'name' and 'type'.",
23+
"description": "Subset of fields to return for each entry when the path is a directory. If omitted, modern clients receive compact entries without 'url' or 'git_url'; these API URLs require explicit selection. Legacy clients retain all GitHub API fields when omitted. Ignored when the path is a single file. Use this to reduce response size when listing directories and you only need specific fields, e.g. just 'name' and 'type'.",
2424
"items": {
2525
"enum": [
2626
"type",

‎pkg/github/actions_types.go‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,10 @@ var actionsListOutputSchema = sync.OnceValue(func() *jsonschema.Schema {
234234
constrainMethodOutput(schema, map[string]string{
235235
actionsMethodListWorkflows: "workflows", actionsMethodListWorkflowRuns: "workflow_runs",
236236
actionsMethodListWorkflowJobs: "workflow_jobs", actionsMethodListWorkflowArtifacts: "artifacts",
237-
}, "workflows", "artifacts")
237+
}, methodOutputConstraints{
238+
optionalPayloads: []string{"workflows", "artifacts"},
239+
nonNullRequiredPayloads: true,
240+
})
238241
return schema
239242
})
240243

@@ -367,7 +370,10 @@ var actionsGetOutputSchema = sync.OnceValue(func() *jsonschema.Schema {
367370
actionsMethodGetWorkflow: "workflow", actionsMethodGetWorkflowRun: "workflow_run",
368371
actionsMethodGetWorkflowJob: "workflow_job", actionsMethodGetWorkflowRunUsage: "usage",
369372
actionsMethodDownloadWorkflowArtifact: "artifact", actionsMethodGetWorkflowRunLogsURL: "logs",
370-
}, "workflow", "workflow_job", "usage")
373+
}, methodOutputConstraints{
374+
optionalPayloads: []string{"workflow", "workflow_job", "usage"},
375+
nonNullRequiredPayloads: true,
376+
})
371377
return schema
372378
})
373379

@@ -414,7 +420,7 @@ var actionsRunTriggerOutputSchema = sync.OnceValue(func() *jsonschema.Schema {
414420
actionsMethodRunWorkflow: "dispatch", actionsMethodRerunWorkflowRun: "rerun",
415421
actionsMethodRerunFailedJobs: "rerun_failed", actionsMethodCancelWorkflowRun: "cancel",
416422
actionsMethodDeleteWorkflowRunLogs: "delete_logs",
417-
})
423+
}, methodOutputConstraints{nonNullRequiredPayloads: true})
418424
return schema
419425
})
420426

‎pkg/github/method_output_schema.go‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,23 @@ import (
66
"github.com/google/jsonschema-go/jsonschema"
77
)
88

9-
func constrainMethodOutput(schema *jsonschema.Schema, payloadByMethod map[string]string, optionalPayloads ...string) {
9+
type methodOutputConstraints struct {
10+
optionalPayloads []string
11+
nonNullRequiredPayloads bool
12+
}
13+
14+
func constrainMethodOutput(schema *jsonschema.Schema, payloadByMethod map[string]string, constraints methodOutputConstraints) {
1015
schema.Required = []string{"method"}
1116
for _, method := range schema.Properties["method"].Enum {
1217
payload := payloadByMethod[method.(string)]
1318
variant := &jsonschema.Schema{Properties: map[string]*jsonschema.Schema{
1419
"method": {Enum: []any{method}},
1520
}}
16-
if !slices.Contains(optionalPayloads, payload) {
21+
if !slices.Contains(constraints.optionalPayloads, payload) {
1722
variant.Required = []string{payload}
23+
if constraints.nonNullRequiredPayloads {
24+
variant.Properties[payload] = &jsonschema.Schema{Not: &jsonschema.Schema{Type: "null"}}
25+
}
1826
}
1927
for _, other := range payloadByMethod {
2028
if other != payload {

‎pkg/github/repositories.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,7 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
10111011
Required: []string{"owner", "repo"},
10121012
}
10131013
schema.Properties["fields"] = fieldsSchemaProperty(
1014-
"Subset of fields to return for each entry when the path is a directory. If omitted, all fields are returned. Ignored when the path is a single file. Use this to reduce response size when listing directories and you only need specific fields, e.g. just 'name' and 'type'.",
1014+
"Subset of fields to return for each entry when the path is a directory. If omitted, modern clients receive compact entries without 'url' or 'git_url'; these API URLs require explicit selection. Legacy clients retain all GitHub API fields when omitted. Ignored when the path is a single file. Use this to reduce response size when listing directories and you only need specific fields, e.g. just 'name' and 'type'.",
10151015
fileContentFieldEnum,
10161016
)
10171017

‎pkg/github/repositories_test.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ func Test_GetFileContents(t *testing.T) {
104104
assert.Contains(t, schema.Properties, "ref")
105105
assert.Contains(t, schema.Properties, "sha")
106106
assert.Contains(t, schema.Properties, "fields")
107+
assert.Contains(t, schema.Properties["fields"].Description, "modern clients receive compact entries without 'url' or 'git_url'")
108+
assert.Contains(t, schema.Properties["fields"].Description, "these API URLs require explicit selection")
109+
assert.Contains(t, schema.Properties["fields"].Description, "Legacy clients retain all GitHub API fields when omitted")
107110
assert.ElementsMatch(t, schema.Required, []string{"owner", "repo"})
108111

109112
// Mock response for raw content

‎pkg/github/security_outputs.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"fmt"
77
"math"
8+
"slices"
89
"strconv"
910
"time"
1011

@@ -448,7 +449,7 @@ func securityAdvisoryOutput(advisory *github.SecurityAdvisory) *SecurityAdvisory
448449
output := &SecurityAdvisoryOutput{
449450
GHSAID: advisory.GHSAID, CVEID: advisory.CVEID, Summary: advisory.Summary,
450451
Description: advisory.Description, Severity: advisory.Severity, State: advisory.State,
451-
HTMLURL: advisory.HTMLURL, CWEIDs: advisory.CWEIDs,
452+
HTMLURL: advisory.HTMLURL, CWEIDs: slices.Clone(advisory.CWEIDs),
452453
PublishedAt: githubTimestampTime(advisory.PublishedAt),
453454
UpdatedAt: githubTimestampTime(advisory.UpdatedAt),
454455
WithdrawnAt: githubTimestampTime(advisory.WithdrawnAt),

0 commit comments

Comments
 (0)