Skip to content

Commit 6fba3ac

Browse files
committed
refactor
1 parent d5675c5 commit 6fba3ac

File tree

9 files changed

+550
-142
lines changed

9 files changed

+550
-142
lines changed

README.md

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ The following sets of tools are available:
406406
| `orgs` | GitHub Organization related tools |
407407
| `projects` | GitHub Projects related tools |
408408
| `pull_requests` | GitHub Pull Request related tools |
409+
| `releases` | GitHub Releases related tools |
409410
| `repos` | GitHub Repository related tools |
410411
| `secret_protection` | Secret protection related tools, such as GitHub Secret Scanning |
411412
| `security_advisories` | Security advisories related tools |
@@ -962,6 +963,20 @@ Possible options:
962963

963964
<details>
964965

966+
<summary>Releases</summary>
967+
968+
- **release_read** - Read operations for releases
969+
- `method`: The read operation to perform on releases. (string, required)
970+
- `owner`: Repository owner (string, required)
971+
- `page`: Page number for pagination (min 1) (number, optional)
972+
- `perPage`: Results per page for pagination (min 1, max 100) (number, optional)
973+
- `repo`: Repository name (string, required)
974+
- `tag`: Tag name (required for get_by_tag method) (string, optional)
975+
976+
</details>
977+
978+
<details>
979+
965980
<summary>Repositories</summary>
966981

967982
- **commit_read** - Read commits
@@ -1016,14 +1031,6 @@ Possible options:
10161031
- `perPage`: Results per page for pagination (min 1, max 100) (number, optional)
10171032
- `repo`: Repository name (string, required)
10181033

1019-
- **release_read** - Read operations for releases and tags
1020-
- `method`: The read operation to perform on releases/tags. (string, required)
1021-
- `owner`: Repository owner (string, required)
1022-
- `page`: Page number for pagination (min 1) (for list_tags and list_releases methods) (number, optional)
1023-
- `perPage`: Results per page for pagination (min 1, max 100) (for list_tags and list_releases methods) (number, optional)
1024-
- `repo`: Repository name (string, required)
1025-
- `tag`: Tag name (required for get_tag and get_release_by_tag methods) (string, optional)
1026-
10271034
- **search_code** - Search code
10281035
- `order`: Sort order for results (string, optional)
10291036
- `page`: Page number for pagination (min 1) (number, optional)
@@ -1039,6 +1046,14 @@ Possible options:
10391046
- `query`: Repository search query. Examples: 'machine learning in:name stars:>1000 language:python', 'topic:react', 'user:facebook'. Supports advanced search syntax for precise filtering. (string, required)
10401047
- `sort`: Sort repositories by field, defaults to best match (string, optional)
10411048

1049+
- **tag_read** - Read operations for git tags
1050+
- `method`: The read operation to perform on tags. (string, required)
1051+
- `owner`: Repository owner (string, required)
1052+
- `page`: Page number for pagination (min 1) (number, optional)
1053+
- `perPage`: Results per page for pagination (min 1, max 100) (number, optional)
1054+
- `repo`: Repository name (string, required)
1055+
- `tag`: Tag name (required for get method) (string, optional)
1056+
10421057
</details>
10431058

10441059
<details>

docs/remote-server.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Below is a table of available toolsets for the remote GitHub MCP Server. Each to
3232
| Organizations | GitHub Organization related tools | https://api.githubcopilot.com/mcp/x/orgs | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-orgs&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Forgs%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/orgs/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-orgs&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Forgs%2Freadonly%22%7D) |
3333
| Projects | GitHub Projects related tools | https://api.githubcopilot.com/mcp/x/projects | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-projects&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fprojects%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/projects/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-projects&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fprojects%2Freadonly%22%7D) |
3434
| Pull Requests | GitHub Pull Request related tools | https://api.githubcopilot.com/mcp/x/pull_requests | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-pull_requests&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fpull_requests%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/pull_requests/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-pull_requests&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fpull_requests%2Freadonly%22%7D) |
35+
| Releases | GitHub Releases related tools | https://api.githubcopilot.com/mcp/x/releases | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-releases&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Freleases%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/releases/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-releases&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Freleases%2Freadonly%22%7D) |
3536
| Repositories | GitHub Repository related tools | https://api.githubcopilot.com/mcp/x/repos | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-repos&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Frepos%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/repos/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-repos&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Frepos%2Freadonly%22%7D) |
3637
| Secret Protection | Secret protection related tools, such as GitHub Secret Scanning | https://api.githubcopilot.com/mcp/x/secret_protection | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-secret_protection&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecret_protection%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/secret_protection/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-secret_protection&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecret_protection%2Freadonly%22%7D) |
3738
| Security Advisories | Security advisories related tools | https://api.githubcopilot.com/mcp/x/security_advisories | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-security_advisories&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecurity_advisories%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/security_advisories/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-security_advisories&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecurity_advisories%2Freadonly%22%7D) |

pkg/github/__toolsnaps__/release_read.snap

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
{
22
"annotations": {
3-
"title": "Read operations for releases and tags",
3+
"title": "Read operations for releases",
44
"readOnlyHint": true
55
},
6-
"description": "Read operations for releases and tags in a GitHub repository.\n\nAvailable methods:\n- list_tags: List all git tags in a repository.\n- get_tag: Get details about a specific git tag.\n- list_releases: List all releases in a repository.\n- get_latest_release: Get the latest release in a repository.\n- get_release_by_tag: Get a specific release by its tag name.\n",
6+
"description": "Read operations for GitHub releases in a repository.\n\nAvailable methods:\n- list: List all releases in a repository.\n- get_latest: Get the latest release in a repository.\n- get_by_tag: Get a specific release by its tag name.\n",
77
"inputSchema": {
88
"properties": {
99
"method": {
10-
"description": "The read operation to perform on releases/tags.",
10+
"description": "The read operation to perform on releases.",
1111
"enum": [
12-
"list_tags",
13-
"get_tag",
14-
"list_releases",
15-
"get_latest_release",
16-
"get_release_by_tag"
12+
"list",
13+
"get_latest",
14+
"get_by_tag"
1715
],
1816
"type": "string"
1917
},
@@ -22,19 +20,22 @@
2220
"type": "string"
2321
},
2422
"page": {
25-
"description": "Page number for pagination (min 1) (for list_tags and list_releases methods)",
23+
"description": "Page number for pagination (min 1)",
24+
"minimum": 1,
2625
"type": "number"
2726
},
2827
"perPage": {
29-
"description": "Results per page for pagination (min 1, max 100) (for list_tags and list_releases methods)",
28+
"description": "Results per page for pagination (min 1, max 100)",
29+
"maximum": 100,
30+
"minimum": 1,
3031
"type": "number"
3132
},
3233
"repo": {
3334
"description": "Repository name",
3435
"type": "string"
3536
},
3637
"tag": {
37-
"description": "Tag name (required for get_tag and get_release_by_tag methods)",
38+
"description": "Tag name (required for get_by_tag method)",
3839
"type": "string"
3940
}
4041
},
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"annotations": {
3+
"title": "Read operations for git tags",
4+
"readOnlyHint": true
5+
},
6+
"description": "Read operations for Git tags in a repository.\n\nAvailable methods:\n- list: List all git tags in a repository.\n- get: Get details about a specific git tag.\n",
7+
"inputSchema": {
8+
"properties": {
9+
"method": {
10+
"description": "The read operation to perform on tags.",
11+
"enum": [
12+
"list",
13+
"get"
14+
],
15+
"type": "string"
16+
},
17+
"owner": {
18+
"description": "Repository owner",
19+
"type": "string"
20+
},
21+
"page": {
22+
"description": "Page number for pagination (min 1)",
23+
"minimum": 1,
24+
"type": "number"
25+
},
26+
"perPage": {
27+
"description": "Results per page for pagination (min 1, max 100)",
28+
"maximum": 100,
29+
"minimum": 1,
30+
"type": "number"
31+
},
32+
"repo": {
33+
"description": "Repository name",
34+
"type": "string"
35+
},
36+
"tag": {
37+
"description": "Tag name (required for get method)",
38+
"type": "string"
39+
}
40+
},
41+
"required": [
42+
"method",
43+
"owner",
44+
"repo"
45+
],
46+
"type": "object"
47+
},
48+
"name": "tag_read"
49+
}

pkg/github/releases.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
10+
ghErrors "github.com/github/github-mcp-server/pkg/errors"
11+
"github.com/github/github-mcp-server/pkg/translations"
12+
"github.com/google/go-github/v74/github"
13+
"github.com/mark3labs/mcp-go/mcp"
14+
"github.com/mark3labs/mcp-go/server"
15+
)
16+
17+
// ReleaseRead creates a tool for reading GitHub releases in a repository.
18+
// Supports multiple methods: list, get_latest, and get_by_tag.
19+
func ReleaseRead(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
20+
return mcp.NewTool("release_read",
21+
mcp.WithDescription(t("TOOL_RELEASE_READ_DESCRIPTION", `Read operations for GitHub releases in a repository.
22+
23+
Available methods:
24+
- list: List all releases in a repository.
25+
- get_latest: Get the latest release in a repository.
26+
- get_by_tag: Get a specific release by its tag name.
27+
`)),
28+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
29+
Title: t("TOOL_RELEASE_READ_USER_TITLE", "Read operations for releases"),
30+
ReadOnlyHint: ToBoolPtr(true),
31+
}),
32+
mcp.WithString("method",
33+
mcp.Required(),
34+
mcp.Enum("list", "get_latest", "get_by_tag"),
35+
mcp.Description("The read operation to perform on releases."),
36+
),
37+
mcp.WithString("owner",
38+
mcp.Required(),
39+
mcp.Description("Repository owner"),
40+
),
41+
mcp.WithString("repo",
42+
mcp.Required(),
43+
mcp.Description("Repository name"),
44+
),
45+
mcp.WithString("tag",
46+
mcp.Description("Tag name (required for get_by_tag method)"),
47+
),
48+
WithPagination(),
49+
),
50+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
51+
method, err := RequiredParam[string](request, "method")
52+
if err != nil {
53+
return mcp.NewToolResultError(err.Error()), nil
54+
}
55+
56+
owner, err := RequiredParam[string](request, "owner")
57+
if err != nil {
58+
return mcp.NewToolResultError(err.Error()), nil
59+
}
60+
repo, err := RequiredParam[string](request, "repo")
61+
if err != nil {
62+
return mcp.NewToolResultError(err.Error()), nil
63+
}
64+
65+
client, err := getClient(ctx)
66+
if err != nil {
67+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
68+
}
69+
70+
switch method {
71+
case "list":
72+
return ListReleasesMethod(ctx, client, owner, repo, request)
73+
case "get_latest":
74+
return GetLatestReleaseMethod(ctx, client, owner, repo)
75+
case "get_by_tag":
76+
return GetReleaseByTagMethod(ctx, client, owner, repo, request)
77+
default:
78+
return mcp.NewToolResultError(fmt.Sprintf("unknown method: %s", method)), nil
79+
}
80+
}
81+
}
82+
83+
// ListReleasesMethod handles the "list" method for ReleaseRead
84+
func ListReleasesMethod(ctx context.Context, client *github.Client, owner, repo string, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
85+
pagination, err := OptionalPaginationParams(request)
86+
if err != nil {
87+
return mcp.NewToolResultError(err.Error()), nil
88+
}
89+
90+
opts := &github.ListOptions{
91+
Page: pagination.Page,
92+
PerPage: pagination.PerPage,
93+
}
94+
95+
releases, resp, err := client.Repositories.ListReleases(ctx, owner, repo, opts)
96+
if err != nil {
97+
return ghErrors.NewGitHubAPIErrorResponse(ctx,
98+
"failed to list releases",
99+
resp,
100+
err,
101+
), nil
102+
}
103+
defer func() { _ = resp.Body.Close() }()
104+
105+
if resp.StatusCode != http.StatusOK {
106+
body, err := io.ReadAll(resp.Body)
107+
if err != nil {
108+
return nil, fmt.Errorf("failed to read response body: %w", err)
109+
}
110+
return mcp.NewToolResultError(fmt.Sprintf("failed to list releases: %s", string(body))), nil
111+
}
112+
113+
r, err := json.Marshal(releases)
114+
if err != nil {
115+
return nil, fmt.Errorf("failed to marshal response: %w", err)
116+
}
117+
118+
return mcp.NewToolResultText(string(r)), nil
119+
}
120+
121+
// GetLatestReleaseMethod handles the "get_latest" method for ReleaseRead
122+
func GetLatestReleaseMethod(ctx context.Context, client *github.Client, owner, repo string) (*mcp.CallToolResult, error) {
123+
release, resp, err := client.Repositories.GetLatestRelease(ctx, owner, repo)
124+
if err != nil {
125+
return ghErrors.NewGitHubAPIErrorResponse(ctx,
126+
"failed to get latest release",
127+
resp,
128+
err,
129+
), nil
130+
}
131+
defer func() { _ = resp.Body.Close() }()
132+
133+
if resp.StatusCode != http.StatusOK {
134+
body, err := io.ReadAll(resp.Body)
135+
if err != nil {
136+
return nil, fmt.Errorf("failed to read response body: %w", err)
137+
}
138+
return mcp.NewToolResultError(fmt.Sprintf("failed to get latest release: %s", string(body))), nil
139+
}
140+
141+
r, err := json.Marshal(release)
142+
if err != nil {
143+
return nil, fmt.Errorf("failed to marshal response: %w", err)
144+
}
145+
146+
return mcp.NewToolResultText(string(r)), nil
147+
}
148+
149+
// GetReleaseByTagMethod handles the "get_by_tag" method for ReleaseRead
150+
func GetReleaseByTagMethod(ctx context.Context, client *github.Client, owner, repo string, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
151+
tag, err := RequiredParam[string](request, "tag")
152+
if err != nil {
153+
return mcp.NewToolResultError(err.Error()), nil
154+
}
155+
156+
release, resp, err := client.Repositories.GetReleaseByTag(ctx, owner, repo, tag)
157+
if err != nil {
158+
return ghErrors.NewGitHubAPIErrorResponse(ctx,
159+
fmt.Sprintf("failed to get release by tag: %s", tag),
160+
resp,
161+
err,
162+
), nil
163+
}
164+
defer func() { _ = resp.Body.Close() }()
165+
166+
if resp.StatusCode != http.StatusOK {
167+
body, err := io.ReadAll(resp.Body)
168+
if err != nil {
169+
return nil, fmt.Errorf("failed to read response body: %w", err)
170+
}
171+
return mcp.NewToolResultError(fmt.Sprintf("failed to get release by tag: %s", string(body))), nil
172+
}
173+
174+
r, err := json.Marshal(release)
175+
if err != nil {
176+
return nil, fmt.Errorf("failed to marshal response: %w", err)
177+
}
178+
179+
return mcp.NewToolResultText(string(r)), nil
180+
}

0 commit comments

Comments
 (0)