Skip to content
This repository was archived by the owner on Nov 20, 2025. It is now read-only.

Commit 7e348cd

Browse files
committed
github: add support to list, get commits
Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
1 parent a57e205 commit 7e348cd

2 files changed

Lines changed: 150 additions & 0 deletions

File tree

servlets/github/commits.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"net/url"
7+
8+
"github.com/extism/go-pdk"
9+
)
10+
11+
var (
12+
ListCommitsTool = ToolDescription{
13+
Name: "gh-list-commits",
14+
Description: "List commits in a GitHub repository",
15+
InputSchema: schema{
16+
"type": "object",
17+
"properties": props{
18+
"owner": prop("string", "The owner of the repository"),
19+
"repo": prop("string", "The repository name"),
20+
"sha": prop("string", "SHA or branch to start listing commits from. Default: the repository’s default branch (usually main)."),
21+
"path": prop("string", "Source branch (defaults to `main` if not provided)"),
22+
"author": prop("string", "GitHub username or email address to use to filter by commit author."),
23+
"committer": prop("string", "GitHub username or email address to use to filter by commit committer."),
24+
"since": prop("string", "Only show results that were last updated after the given time. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. Due to limitations of Git, timestamps must be between 1970-01-01 and 2099-12-31 (inclusive) or unexpected results may be returned."),
25+
"until": prop("string", "Only commits before this date will be returned. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. Due to limitations of Git, timestamps must be between 1970-01-01 and 2099-12-31 (inclusive) or unexpected results may be returned."),
26+
"per_page": prop("integer", "Results per page (max 100). Defaults to 30."),
27+
"page": prop("integer", "Page number of the results to fetch. Defaults to 1."),
28+
},
29+
"required": []string{"owner", "repo"},
30+
},
31+
}
32+
GetCommitTool = ToolDescription{
33+
Name: "gh-get-commit",
34+
Description: "Returns the contents of a single commit reference",
35+
InputSchema: schema{
36+
"type": "object",
37+
"properties": props{
38+
"owner": prop("string", "The owner of the repository"),
39+
"repo": prop("string", "The repository name"),
40+
"ref": prop("string", "The commit reference. Can be a commit SHA, branch name (heads/BRANCH_NAME), or tag name (tags/TAG_NAME). For more information, see 'Git References' in the Git documentation."),
41+
"per_page": prop("integer", "Results per page (max 100). Defaults to 30."),
42+
"page": prop("integer", "Page number of the results to fetch. Defaults to 1."),
43+
},
44+
"required": []string{"owner", "repo", "ref"},
45+
},
46+
}
47+
)
48+
49+
var CommitTools = []ToolDescription{
50+
ListCommitsTool,
51+
GetCommitTool,
52+
}
53+
54+
func commitList(apiKey, owner, repo string, args map[string]interface{}) CallToolResult {
55+
q := url.Values{}
56+
if sha, ok := args["sha"].(string); ok && sha != "" {
57+
q.Add("sha", sha)
58+
}
59+
if path, ok := args["path"].(string); ok && path != "" {
60+
q.Add("path", path)
61+
}
62+
if author, ok := args["author"].(string); ok && author != "" {
63+
q.Add("author", author)
64+
}
65+
if committer, ok := args["committer"].(string); ok && committer != "" {
66+
q.Add("committer", committer)
67+
}
68+
if since, ok := args["since"].(string); ok && since != "" {
69+
q.Add("since", since)
70+
}
71+
if until, ok := args["until"].(string); ok && until != "" {
72+
q.Add("until", until)
73+
}
74+
if perPage, ok := args["per_page"].(int); ok && perPage > 0 {
75+
q.Add("per_page", fmt.Sprintf("%d", perPage))
76+
}
77+
if page, ok := args["page"].(int); ok && page > 0 {
78+
q.Add("page", fmt.Sprintf("%d", page))
79+
}
80+
81+
u := fmt.Sprintf("https://api.github.com/repos/%s/%s/commits?%s", owner, repo, q.Encode())
82+
req := pdk.NewHTTPRequest(pdk.MethodGet, u)
83+
req.SetHeader("Authorization", fmt.Sprintf("token %s", apiKey))
84+
req.SetHeader("Accept", "application/vnd.github.v3+json")
85+
req.SetHeader("User-Agent", "github-mcpx-servlet")
86+
resp := req.Send()
87+
switch resp.Status() {
88+
case 200:
89+
return CallToolResult{
90+
Content: []Content{{
91+
Type: ContentTypeText,
92+
Text: some(string(resp.Body())),
93+
}},
94+
}
95+
default:
96+
return CallToolResult{
97+
IsError: some(true),
98+
Content: []Content{{
99+
Type: ContentTypeText,
100+
Text: some(fmt.Sprintf("Request failed with status %d: %s", resp.Status(), string(resp.Body()))),
101+
}},
102+
}
103+
}
104+
}
105+
106+
func commitGet(apiKey, owner, repo, ref string, args map[string]interface{}) CallToolResult {
107+
q := url.Values{}
108+
if perPage, ok := args["per_page"].(int); ok && perPage > 0 {
109+
q.Add("per_page", fmt.Sprintf("%d", perPage))
110+
}
111+
if page, ok := args["page"].(int); ok && page > 0 {
112+
q.Add("page", fmt.Sprintf("%d", page))
113+
}
114+
115+
u := fmt.Sprintf("https://api.github.com/repos/%s/%s/commits/%s?%s", owner, repo, ref, q.Encode())
116+
req := pdk.NewHTTPRequest(pdk.MethodGet, u)
117+
req.SetHeader("Authorization", fmt.Sprintf("tokexn %s", apiKey))
118+
req.SetHeader("Accept", "application/vnd.github.v3+json")
119+
req.SetHeader("User-Agent", "github-mcpx-servlet")
120+
121+
resp := req.Send()
122+
switch resp.Status() {
123+
case 200:
124+
return CallToolResult{
125+
Content: []Content{{
126+
Type: ContentTypeText,
127+
Text: some(string(resp.Body())),
128+
}},
129+
}
130+
default:
131+
return CallToolResult{
132+
IsError: some(true),
133+
Content: []Content{{
134+
Type: ContentTypeText,
135+
Text: some(fmt.Sprintf("Request failed with status %d: %s", resp.Status(), string(resp.Body()))),
136+
}},
137+
}
138+
}
139+
}

servlets/github/main.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ func Call(input CallToolRequest) (CallToolResult, error) {
7878
}
7979
return branchCreate(apiKey, owner, repo, from, maybeBranch), nil
8080

81+
case ListCommitsTool.Name:
82+
owner, _ := args["owner"].(string)
83+
repo, _ := args["repo"].(string)
84+
return commitList(apiKey, owner, repo, args), nil
85+
case GetCommitTool.Name:
86+
owner, _ := args["owner"].(string)
87+
repo, _ := args["repo"].(string)
88+
ref, _ := args["ref"].(string)
89+
return commitGet(apiKey, owner, repo, ref, args), nil
90+
8191
case ListPullRequestsTool.Name:
8292
owner, _ := args["owner"].(string)
8393
repo, _ := args["repo"].(string)
@@ -152,6 +162,7 @@ func Describe() (ListToolsResult, error) {
152162
IssueTools,
153163
FileTools,
154164
BranchTools,
165+
CommitTools,
155166
RepoTools,
156167
GistTools,
157168
}

0 commit comments

Comments
 (0)