Skip to content

Commit d03072f

Browse files
authored
Repository security advisories (#925)
* Add support for listing repo level security advisories * Add support for listing repo security advisories at the org level
1 parent 47040f4 commit d03072f

File tree

4 files changed

+467
-0
lines changed

4 files changed

+467
-0
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,19 @@ The following sets of tools are available (all are on by default):
942942
- `type`: Advisory type. (string, optional)
943943
- `updated`: Filter by update date or date range (ISO 8601 date or range). (string, optional)
944944

945+
- **list_org_repository_security_advisories** - List org repository security advisories
946+
- `direction`: Sort direction. (string, optional)
947+
- `org`: The organization login. (string, required)
948+
- `sort`: Sort field. (string, optional)
949+
- `state`: Filter by advisory state. (string, optional)
950+
951+
- **list_repository_security_advisories** - List repository security advisories
952+
- `direction`: Sort direction. (string, optional)
953+
- `owner`: The owner of the repository. (string, required)
954+
- `repo`: The name of the repository. (string, required)
955+
- `sort`: Sort field. (string, optional)
956+
- `state`: Filter by advisory state. (string, optional)
957+
945958
</details>
946959

947960
<details>

pkg/github/security_advisories.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,95 @@ func ListGlobalSecurityAdvisories(getClient GetClientFn, t translations.Translat
182182
}
183183
}
184184

185+
func ListRepositorySecurityAdvisories(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
186+
return mcp.NewTool("list_repository_security_advisories",
187+
mcp.WithDescription(t("TOOL_LIST_REPOSITORY_SECURITY_ADVISORIES_DESCRIPTION", "List repository security advisories for a GitHub repository.")),
188+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
189+
Title: t("TOOL_LIST_REPOSITORY_SECURITY_ADVISORIES_USER_TITLE", "List repository security advisories"),
190+
ReadOnlyHint: ToBoolPtr(true),
191+
}),
192+
mcp.WithString("owner",
193+
mcp.Required(),
194+
mcp.Description("The owner of the repository."),
195+
),
196+
mcp.WithString("repo",
197+
mcp.Required(),
198+
mcp.Description("The name of the repository."),
199+
),
200+
mcp.WithString("direction",
201+
mcp.Description("Sort direction."),
202+
mcp.Enum("asc", "desc"),
203+
),
204+
mcp.WithString("sort",
205+
mcp.Description("Sort field."),
206+
mcp.Enum("created", "updated", "published"),
207+
),
208+
mcp.WithString("state",
209+
mcp.Description("Filter by advisory state."),
210+
mcp.Enum("triage", "draft", "published", "closed"),
211+
),
212+
), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
213+
owner, err := RequiredParam[string](request, "owner")
214+
if err != nil {
215+
return mcp.NewToolResultError(err.Error()), nil
216+
}
217+
repo, err := RequiredParam[string](request, "repo")
218+
if err != nil {
219+
return mcp.NewToolResultError(err.Error()), nil
220+
}
221+
222+
direction, err := OptionalParam[string](request, "direction")
223+
if err != nil {
224+
return mcp.NewToolResultError(err.Error()), nil
225+
}
226+
sortField, err := OptionalParam[string](request, "sort")
227+
if err != nil {
228+
return mcp.NewToolResultError(err.Error()), nil
229+
}
230+
state, err := OptionalParam[string](request, "state")
231+
if err != nil {
232+
return mcp.NewToolResultError(err.Error()), nil
233+
}
234+
235+
client, err := getClient(ctx)
236+
if err != nil {
237+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
238+
}
239+
240+
opts := &github.ListRepositorySecurityAdvisoriesOptions{}
241+
if direction != "" {
242+
opts.Direction = direction
243+
}
244+
if sortField != "" {
245+
opts.Sort = sortField
246+
}
247+
if state != "" {
248+
opts.State = state
249+
}
250+
251+
advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, owner, repo, opts)
252+
if err != nil {
253+
return nil, fmt.Errorf("failed to list repository security advisories: %w", err)
254+
}
255+
defer func() { _ = resp.Body.Close() }()
256+
257+
if resp.StatusCode != http.StatusOK {
258+
body, err := io.ReadAll(resp.Body)
259+
if err != nil {
260+
return nil, fmt.Errorf("failed to read response body: %w", err)
261+
}
262+
return mcp.NewToolResultError(fmt.Sprintf("failed to list repository advisories: %s", string(body))), nil
263+
}
264+
265+
r, err := json.Marshal(advisories)
266+
if err != nil {
267+
return nil, fmt.Errorf("failed to marshal advisories: %w", err)
268+
}
269+
270+
return mcp.NewToolResultText(string(r)), nil
271+
}
272+
}
273+
185274
func GetGlobalSecurityAdvisory(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
186275
return mcp.NewTool("get_global_security_advisory",
187276
mcp.WithDescription(t("TOOL_GET_GLOBAL_SECURITY_ADVISORY_DESCRIPTION", "Get a global security advisory")),
@@ -226,3 +315,83 @@ func GetGlobalSecurityAdvisory(getClient GetClientFn, t translations.Translation
226315
return mcp.NewToolResultText(string(r)), nil
227316
}
228317
}
318+
319+
func ListOrgRepositorySecurityAdvisories(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
320+
return mcp.NewTool("list_org_repository_security_advisories",
321+
mcp.WithDescription(t("TOOL_LIST_ORG_REPOSITORY_SECURITY_ADVISORIES_DESCRIPTION", "List repository security advisories for a GitHub organization.")),
322+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
323+
Title: t("TOOL_LIST_ORG_REPOSITORY_SECURITY_ADVISORIES_USER_TITLE", "List org repository security advisories"),
324+
ReadOnlyHint: ToBoolPtr(true),
325+
}),
326+
mcp.WithString("org",
327+
mcp.Required(),
328+
mcp.Description("The organization login."),
329+
),
330+
mcp.WithString("direction",
331+
mcp.Description("Sort direction."),
332+
mcp.Enum("asc", "desc"),
333+
),
334+
mcp.WithString("sort",
335+
mcp.Description("Sort field."),
336+
mcp.Enum("created", "updated", "published"),
337+
),
338+
mcp.WithString("state",
339+
mcp.Description("Filter by advisory state."),
340+
mcp.Enum("triage", "draft", "published", "closed"),
341+
),
342+
), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
343+
org, err := RequiredParam[string](request, "org")
344+
if err != nil {
345+
return mcp.NewToolResultError(err.Error()), nil
346+
}
347+
direction, err := OptionalParam[string](request, "direction")
348+
if err != nil {
349+
return mcp.NewToolResultError(err.Error()), nil
350+
}
351+
sortField, err := OptionalParam[string](request, "sort")
352+
if err != nil {
353+
return mcp.NewToolResultError(err.Error()), nil
354+
}
355+
state, err := OptionalParam[string](request, "state")
356+
if err != nil {
357+
return mcp.NewToolResultError(err.Error()), nil
358+
}
359+
360+
client, err := getClient(ctx)
361+
if err != nil {
362+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
363+
}
364+
365+
opts := &github.ListRepositorySecurityAdvisoriesOptions{}
366+
if direction != "" {
367+
opts.Direction = direction
368+
}
369+
if sortField != "" {
370+
opts.Sort = sortField
371+
}
372+
if state != "" {
373+
opts.State = state
374+
}
375+
376+
advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, org, opts)
377+
if err != nil {
378+
return nil, fmt.Errorf("failed to list organization repository security advisories: %w", err)
379+
}
380+
defer func() { _ = resp.Body.Close() }()
381+
382+
if resp.StatusCode != http.StatusOK {
383+
body, err := io.ReadAll(resp.Body)
384+
if err != nil {
385+
return nil, fmt.Errorf("failed to read response body: %w", err)
386+
}
387+
return mcp.NewToolResultError(fmt.Sprintf("failed to list organization repository advisories: %s", string(body))), nil
388+
}
389+
390+
r, err := json.Marshal(advisories)
391+
if err != nil {
392+
return nil, fmt.Errorf("failed to marshal advisories: %w", err)
393+
}
394+
395+
return mcp.NewToolResultText(string(r)), nil
396+
}
397+
}

0 commit comments

Comments
 (0)