Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbdb59e0

Browse files
feat: add GitHub notifications tools for managing user notifications
1 parente6b73f7 commitbdb59e0

File tree

2 files changed

+258
-0
lines changed

2 files changed

+258
-0
lines changed

‎pkg/github/notifications.go

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"time"
10+
11+
"github.com/github/github-mcp-server/pkg/translations"
12+
"github.com/google/go-github/v69/github"
13+
"github.com/mark3labs/mcp-go/mcp"
14+
"github.com/mark3labs/mcp-go/server"
15+
)
16+
17+
// getNotifications creates a tool to list notifications for the current user.
18+
funcgetNotifications(client*github.Client,t translations.TranslationHelperFunc) (tool mcp.Tool,handler server.ToolHandlerFunc) {
19+
returnmcp.NewTool("get_notifications",
20+
mcp.WithDescription(t("TOOL_GET_NOTIFICATIONS_DESCRIPTION","Get notifications for the authenticated GitHub user")),
21+
mcp.WithBoolean("all",
22+
mcp.Description("If true, show notifications marked as read. Default: false"),
23+
),
24+
mcp.WithBoolean("participating",
25+
mcp.Description("If true, only shows notifications in which the user is directly participating or mentioned. Default: false"),
26+
),
27+
mcp.WithString("since",
28+
mcp.Description("Only show notifications updated after the given time (ISO 8601 format)"),
29+
),
30+
mcp.WithString("before",
31+
mcp.Description("Only show notifications updated before the given time (ISO 8601 format)"),
32+
),
33+
mcp.WithNumber("per_page",
34+
mcp.Description("Results per page (max 100). Default: 30"),
35+
),
36+
mcp.WithNumber("page",
37+
mcp.Description("Page number of the results to fetch. Default: 1"),
38+
),
39+
),
40+
func(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
41+
// Extract optional parameters with defaults
42+
all,err:=optionalParamWithDefault[bool](request,"all",false)
43+
iferr!=nil {
44+
returnmcp.NewToolResultError(err.Error()),nil
45+
}
46+
47+
participating,err:=optionalParamWithDefault[bool](request,"participating",false)
48+
iferr!=nil {
49+
returnmcp.NewToolResultError(err.Error()),nil
50+
}
51+
52+
since,err:=optionalParam[string](request,"since")
53+
iferr!=nil {
54+
returnmcp.NewToolResultError(err.Error()),nil
55+
}
56+
57+
before,err:=optionalParam[string](request,"before")
58+
iferr!=nil {
59+
returnmcp.NewToolResultError(err.Error()),nil
60+
}
61+
62+
perPage,err:=optionalIntParamWithDefault(request,"per_page",30)
63+
iferr!=nil {
64+
returnmcp.NewToolResultError(err.Error()),nil
65+
}
66+
67+
page,err:=optionalIntParamWithDefault(request,"page",1)
68+
iferr!=nil {
69+
returnmcp.NewToolResultError(err.Error()),nil
70+
}
71+
72+
// Build options
73+
opts:=&github.NotificationListOptions{
74+
All:all,
75+
Participating:participating,
76+
ListOptions: github.ListOptions{
77+
Page:page,
78+
PerPage:perPage,
79+
},
80+
}
81+
82+
// Parse time parameters if provided
83+
ifsince!="" {
84+
sinceTime,err:=time.Parse(time.RFC3339,since)
85+
iferr!=nil {
86+
returnmcp.NewToolResultError(fmt.Sprintf("invalid since time format, should be RFC3339/ISO8601: %v",err)),nil
87+
}
88+
opts.Since=sinceTime
89+
}
90+
91+
ifbefore!="" {
92+
beforeTime,err:=time.Parse(time.RFC3339,before)
93+
iferr!=nil {
94+
returnmcp.NewToolResultError(fmt.Sprintf("invalid before time format, should be RFC3339/ISO8601: %v",err)),nil
95+
}
96+
opts.Before=beforeTime
97+
}
98+
99+
// Call GitHub API
100+
notifications,resp,err:=client.Activity.ListNotifications(ctx,opts)
101+
iferr!=nil {
102+
returnnil,fmt.Errorf("failed to get notifications: %w",err)
103+
}
104+
deferfunc() {_=resp.Body.Close() }()
105+
106+
ifresp.StatusCode!=http.StatusOK {
107+
body,err:=io.ReadAll(resp.Body)
108+
iferr!=nil {
109+
returnnil,fmt.Errorf("failed to read response body: %w",err)
110+
}
111+
returnmcp.NewToolResultError(fmt.Sprintf("failed to get notifications: %s",string(body))),nil
112+
}
113+
114+
// Marshal response to JSON
115+
r,err:=json.Marshal(notifications)
116+
iferr!=nil {
117+
returnnil,fmt.Errorf("failed to marshal response: %w",err)
118+
}
119+
120+
returnmcp.NewToolResultText(string(r)),nil
121+
}
122+
}
123+
124+
// markNotificationRead creates a tool to mark a notification as read.
125+
funcmarkNotificationRead(client*github.Client,t translations.TranslationHelperFunc) (tool mcp.Tool,handler server.ToolHandlerFunc) {
126+
returnmcp.NewTool("mark_notification_read",
127+
mcp.WithDescription(t("TOOL_MARK_NOTIFICATION_READ_DESCRIPTION","Mark a notification as read")),
128+
mcp.WithString("threadID",
129+
mcp.Required(),
130+
mcp.Description("The ID of the notification thread"),
131+
),
132+
),
133+
func(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
134+
threadID,err:=requiredParam[string](request,"threadID")
135+
iferr!=nil {
136+
returnmcp.NewToolResultError(err.Error()),nil
137+
}
138+
139+
resp,err:=client.Activity.MarkThreadRead(ctx,threadID)
140+
iferr!=nil {
141+
returnnil,fmt.Errorf("failed to mark notification as read: %w",err)
142+
}
143+
deferfunc() {_=resp.Body.Close() }()
144+
145+
ifresp.StatusCode!=http.StatusResetContent&&resp.StatusCode!=http.StatusOK {
146+
body,err:=io.ReadAll(resp.Body)
147+
iferr!=nil {
148+
returnnil,fmt.Errorf("failed to read response body: %w",err)
149+
}
150+
returnmcp.NewToolResultError(fmt.Sprintf("failed to mark notification as read: %s",string(body))),nil
151+
}
152+
153+
returnmcp.NewToolResultText("Notification marked as read"),nil
154+
}
155+
}
156+
157+
// markAllNotificationsRead creates a tool to mark all notifications as read.
158+
funcmarkAllNotificationsRead(client*github.Client,t translations.TranslationHelperFunc) (tool mcp.Tool,handler server.ToolHandlerFunc) {
159+
returnmcp.NewTool("mark_all_notifications_read",
160+
mcp.WithDescription(t("TOOL_MARK_ALL_NOTIFICATIONS_READ_DESCRIPTION","Mark all notifications as read")),
161+
mcp.WithString("lastReadAt",
162+
mcp.Description("Describes the last point that notifications were checked (optional). Default: Now"),
163+
),
164+
),
165+
func(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
166+
lastReadAt,err:=optionalParam[string](request,"lastReadAt")
167+
iferr!=nil {
168+
returnmcp.NewToolResultError(err.Error()),nil
169+
}
170+
171+
varmarkReadOptions github.Timestamp
172+
iflastReadAt!="" {
173+
lastReadTime,err:=time.Parse(time.RFC3339,lastReadAt)
174+
iferr!=nil {
175+
returnmcp.NewToolResultError(fmt.Sprintf("invalid lastReadAt time format, should be RFC3339/ISO8601: %v",err)),nil
176+
}
177+
markReadOptions= github.Timestamp{
178+
Time:lastReadTime,
179+
}
180+
}
181+
182+
resp,err:=client.Activity.MarkNotificationsRead(ctx,markReadOptions)
183+
iferr!=nil {
184+
returnnil,fmt.Errorf("failed to mark all notifications as read: %w",err)
185+
}
186+
deferfunc() {_=resp.Body.Close() }()
187+
188+
ifresp.StatusCode!=http.StatusResetContent&&resp.StatusCode!=http.StatusOK {
189+
body,err:=io.ReadAll(resp.Body)
190+
iferr!=nil {
191+
returnnil,fmt.Errorf("failed to read response body: %w",err)
192+
}
193+
returnmcp.NewToolResultError(fmt.Sprintf("failed to mark all notifications as read: %s",string(body))),nil
194+
}
195+
196+
returnmcp.NewToolResultText("All notifications marked as read"),nil
197+
}
198+
}
199+
200+
// getNotificationThread creates a tool to get a specific notification thread.
201+
funcgetNotificationThread(client*github.Client,t translations.TranslationHelperFunc) (tool mcp.Tool,handler server.ToolHandlerFunc) {
202+
returnmcp.NewTool("get_notification_thread",
203+
mcp.WithDescription(t("TOOL_GET_NOTIFICATION_THREAD_DESCRIPTION","Get a specific notification thread")),
204+
mcp.WithString("threadID",
205+
mcp.Required(),
206+
mcp.Description("The ID of the notification thread"),
207+
),
208+
),
209+
func(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
210+
threadID,err:=requiredParam[string](request,"threadID")
211+
iferr!=nil {
212+
returnmcp.NewToolResultError(err.Error()),nil
213+
}
214+
215+
thread,resp,err:=client.Activity.GetThread(ctx,threadID)
216+
iferr!=nil {
217+
returnnil,fmt.Errorf("failed to get notification thread: %w",err)
218+
}
219+
deferfunc() {_=resp.Body.Close() }()
220+
221+
ifresp.StatusCode!=http.StatusOK {
222+
body,err:=io.ReadAll(resp.Body)
223+
iferr!=nil {
224+
returnnil,fmt.Errorf("failed to read response body: %w",err)
225+
}
226+
returnmcp.NewToolResultError(fmt.Sprintf("failed to get notification thread: %s",string(body))),nil
227+
}
228+
229+
r,err:=json.Marshal(thread)
230+
iferr!=nil {
231+
returnnil,fmt.Errorf("failed to marshal response: %w",err)
232+
}
233+
234+
returnmcp.NewToolResultText(string(r)),nil
235+
}
236+
}

‎pkg/github/server.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ func NewServer(client *github.Client, readOnly bool, t translations.TranslationH
7777
// Add GitHub tools - Code Scanning
7878
s.AddTool(getCodeScanningAlert(client,t))
7979
s.AddTool(listCodeScanningAlerts(client,t))
80+
81+
// Add GitHub tools - Notifications
82+
s.AddTool(getNotifications(client,t))
83+
s.AddTool(getNotificationThread(client,t))
84+
if!readOnly {
85+
s.AddTool(markNotificationRead(client,t))
86+
s.AddTool(markAllNotificationsRead(client,t))
87+
}
8088
returns
8189
}
8290

@@ -189,6 +197,20 @@ func optionalIntParam(r mcp.CallToolRequest, p string) (int, error) {
189197
returnint(v),nil
190198
}
191199

200+
// optionalParamWithDefault is a generic helper function that can be used to fetch a requested parameter from the request
201+
// with a default value if the parameter is not provided or is zero value.
202+
funcoptionalParamWithDefault[Tcomparable](r mcp.CallToolRequest,pstring,dT) (T,error) {
203+
varzeroT
204+
v,err:=optionalParam[T](r,p)
205+
iferr!=nil {
206+
returnzero,err
207+
}
208+
ifv==zero {
209+
returnd,nil
210+
}
211+
returnv,nil
212+
}
213+
192214
// optionalIntParamWithDefault is a helper function that can be used to fetch a requested parameter from the request
193215
// similar to optionalIntParam, but it also takes a default value.
194216
funcoptionalIntParamWithDefault(r mcp.CallToolRequest,pstring,dint) (int,error) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp