Skip to content

Commit 1d83838

Browse files
committed
Add Global Security Advisories Toolset
1 parent c2d5b43 commit 1d83838

File tree

4 files changed

+468
-0
lines changed

4 files changed

+468
-0
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,29 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
666666
- `repo`: Repository name (string, required)
667667
- `prNumber`: Pull request number (string, required)
668668
- `path`: File or directory path (string, optional)
669+
670+
## Security Advisories
671+
672+
- **`list_global_security_advisories`**
673+
List global security advisories
674+
675+
- **Parameters**:
676+
- * `ghsaId`: Filter by GitHub Security Advisory ID (string, optional – format: `GHSA-xxxx-xxxx-xxxx`)
677+
- * `type`: Advisory type (string, optional – one of `reviewed`, `malware`, `unreviewed`)
678+
- * `cveId`: Filter by CVE ID (string, optional)
679+
- * `ecosystem`: Filter by package ecosystem (string, optional – one of `actions`, `composer`, `erlang`, `go`, `maven`, `npm`, `nuget`, `other`, `pip`, `pub`, `rubygems`, `rust`)
680+
- * `severity`: Filter by severity (string, optional – one of `unknown`, `low`, `medium`, `high`, `critical`)
681+
- * `cwes`: Filter by Common Weakness Enumeration IDs (array of strings, optional – e.g. `["79", "284", "22"]`)
682+
- * `isWithdrawn`: Whether to only return withdrawn advisories (boolean, optional)
683+
- * `affects`: Filter advisories by affected package or version (string, optional – e.g. `"package1,package2@1.0.0"`)
684+
- * `published`: Filter by publish date or date range (string, optional – ISO 8601 date or range)
685+
- * `updated`: Filter by update date or date range (string, optional – ISO 8601 date or range)
686+
- * `modified`: Filter by publish or update date or date range (string, optional – ISO 8601 date or range)
687+
688+
- **`get_global_security_advisory`**
689+
Get a global security advisory
690+
691+
- **Template**: `advisories/{ghsaId}`
669692

670693
## Library Usage
671694

pkg/github/security_advisories.go

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
10+
"github.com/github/github-mcp-server/pkg/translations"
11+
"github.com/google/go-github/v72/github"
12+
"github.com/mark3labs/mcp-go/mcp"
13+
"github.com/mark3labs/mcp-go/server"
14+
)
15+
16+
func ListGlobalSecurityAdvisories(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
17+
return mcp.NewTool("list_global_security_advisories",
18+
mcp.WithDescription(t("TOOL_LIST_GLOBAL_SECURITY_ADVISORIES_DESCRIPTION", "List global security advisories from GitHub.")),
19+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
20+
Title: t("TOOL_LIST_GLOBAL_SECURITY_ADVISORIES_USER_TITLE", "List global security advisories"),
21+
ReadOnlyHint: toBoolPtr(true),
22+
}),
23+
mcp.WithString("ghsaId",
24+
mcp.Description("Filter by GitHub Security Advisory ID (format: GHSA-xxxx-xxxx-xxxx)."),
25+
),
26+
mcp.WithString("type",
27+
mcp.Description("Advisory type."),
28+
mcp.Enum("reviewed", "malware", "unreviewed"),
29+
),
30+
mcp.WithString("cveId",
31+
mcp.Description("Filter by CVE ID."),
32+
),
33+
mcp.WithString("ecosystem",
34+
mcp.Description("Filter by package ecosystem."),
35+
mcp.Enum("actions", "composer", "erlang", "go", "maven", "npm", "nuget", "other", "pip", "pub", "rubygems", "rust"),
36+
),
37+
mcp.WithString("severity",
38+
mcp.Description("Filter by severity."),
39+
mcp.Enum("unknown", "low", "medium", "high", "critical"),
40+
),
41+
mcp.WithArray("cwes",
42+
mcp.Description("Filter by Common Weakness Enumeration IDs (e.g. [\"79\", \"284\", \"22\"])."),
43+
),
44+
mcp.WithBoolean("isWithdrawn",
45+
mcp.Description("Whether to only return withdrawn advisories."),
46+
),
47+
mcp.WithString("affects",
48+
mcp.Description("Filter advisories by affected package or version (e.g. \"package1,package2@1.0.0\")."),
49+
),
50+
mcp.WithString("published",
51+
mcp.Description("Filter by publish date or date range (ISO 8601 date or range)."),
52+
),
53+
mcp.WithString("updated",
54+
mcp.Description("Filter by update date or date range (ISO 8601 date or range)."),
55+
),
56+
mcp.WithString("modified",
57+
mcp.Description("Filter by publish or update date or date range (ISO 8601 date or range)."),
58+
),
59+
), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
60+
client, err := getClient(ctx)
61+
if err != nil {
62+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
63+
}
64+
65+
ghsaID, err := OptionalParam[string](request, "ghsaId")
66+
if err != nil {
67+
return mcp.NewToolResultError(fmt.Sprintf("invalid ghsaId: %v", err)), nil
68+
}
69+
70+
typ, err := OptionalParam[string](request, "type")
71+
if err != nil {
72+
return mcp.NewToolResultError(fmt.Sprintf("invalid type: %v", err)), nil
73+
}
74+
75+
cveID, err := OptionalParam[string](request, "cveId")
76+
if err != nil {
77+
return mcp.NewToolResultError(fmt.Sprintf("invalid cveId: %v", err)), nil
78+
}
79+
80+
eco, err := OptionalParam[string](request, "ecosystem")
81+
if err != nil {
82+
return mcp.NewToolResultError(fmt.Sprintf("invalid ecosystem: %v", err)), nil
83+
}
84+
85+
sev, err := OptionalParam[string](request, "severity")
86+
if err != nil {
87+
return mcp.NewToolResultError(fmt.Sprintf("invalid severity: %v", err)), nil
88+
}
89+
90+
cwes, err := OptionalParam[[]string](request, "cwes")
91+
if err != nil {
92+
return mcp.NewToolResultError(fmt.Sprintf("invalid cwes: %v", err)), nil
93+
}
94+
95+
isWithdrawn, err := OptionalParam[bool](request, "isWithdrawn")
96+
if err != nil {
97+
return mcp.NewToolResultError(fmt.Sprintf("invalid isWithdrawn: %v", err)), nil
98+
}
99+
100+
affects, err := OptionalParam[string](request, "affects")
101+
if err != nil {
102+
return mcp.NewToolResultError(fmt.Sprintf("invalid affects: %v", err)), nil
103+
}
104+
105+
published, err := OptionalParam[string](request, "published")
106+
if err != nil {
107+
return mcp.NewToolResultError(fmt.Sprintf("invalid published: %v", err)), nil
108+
}
109+
110+
updated, err := OptionalParam[string](request, "updated")
111+
if err != nil {
112+
return mcp.NewToolResultError(fmt.Sprintf("invalid updated: %v", err)), nil
113+
}
114+
115+
modified, err := OptionalParam[string](request, "modified")
116+
if err != nil {
117+
return mcp.NewToolResultError(fmt.Sprintf("invalid modified: %v", err)), nil
118+
}
119+
120+
advisories, resp, err := client.SecurityAdvisories.ListGlobalSecurityAdvisories(ctx, &github.ListGlobalSecurityAdvisoriesOptions{
121+
GHSAID: &ghsaID,
122+
Type: &typ,
123+
CVEID: &cveID,
124+
Ecosystem: &eco,
125+
Severity: &sev,
126+
CWEs: cwes,
127+
IsWithdrawn: &isWithdrawn,
128+
Affects: &affects,
129+
Published: &published,
130+
Updated: &updated,
131+
Modified: &modified,
132+
})
133+
if err != nil {
134+
return nil, fmt.Errorf("failed to list global security advisories: %w", err)
135+
}
136+
defer func() { _ = resp.Body.Close() }()
137+
138+
if resp.StatusCode != http.StatusOK {
139+
body, err := io.ReadAll(resp.Body)
140+
if err != nil {
141+
return nil, fmt.Errorf("failed to read response body: %w", err)
142+
}
143+
return mcp.NewToolResultError(fmt.Sprintf("failed to list advisories: %s", string(body))), nil
144+
}
145+
146+
r, err := json.Marshal(advisories)
147+
if err != nil {
148+
return nil, fmt.Errorf("failed to marshal advisories: %w", err)
149+
}
150+
151+
return mcp.NewToolResultText(string(r)), nil
152+
}
153+
}
154+
155+
func GetGlobalSecurityAdvisory(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
156+
return mcp.NewTool("get_global_security_advisory",
157+
mcp.WithDescription(t("TOOL_GET_GLOBAL_SECURITY_ADVISORY_DESCRIPTION", "Get a global security advisory")),
158+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
159+
Title: t("TOOL_GET_GLOBAL_SECURITY_ADVISORY_USER_TITLE", "Get a global security advisory"),
160+
ReadOnlyHint: toBoolPtr(true),
161+
}),
162+
mcp.WithString("ghsaId",
163+
mcp.Description("GitHub Security Advisory ID (format: GHSA-xxxx-xxxx-xxxx)."),
164+
mcp.Required(),
165+
),
166+
), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
167+
client, err := getClient(ctx)
168+
if err != nil {
169+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
170+
}
171+
172+
ghsaID, err := requiredParam[string](request, "ghsaId")
173+
if err != nil {
174+
return mcp.NewToolResultError(fmt.Sprintf("invalid ghsaId: %v", err)), nil
175+
}
176+
177+
advisory, resp, err := client.SecurityAdvisories.GetGlobalSecurityAdvisories(ctx, ghsaID)
178+
if err != nil {
179+
return nil, fmt.Errorf("failed to get advisory: %w", err)
180+
}
181+
defer func() { _ = resp.Body.Close() }()
182+
183+
if resp.StatusCode != http.StatusOK {
184+
body, err := io.ReadAll(resp.Body)
185+
if err != nil {
186+
return nil, fmt.Errorf("failed to read response body: %w", err)
187+
}
188+
return mcp.NewToolResultError(fmt.Sprintf("failed to get advisory: %s", string(body))), nil
189+
}
190+
191+
r, err := json.Marshal(advisory)
192+
if err != nil {
193+
return nil, fmt.Errorf("failed to marshal advisory: %w", err)
194+
}
195+
196+
return mcp.NewToolResultText(string(r)), nil
197+
}
198+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy