Skip to content

Commit 79b2a85

Browse files
committed
Added get_issue_events , get_issue_timeline and get_issue_event tools
1 parent 3134b09 commit 79b2a85

File tree

4 files changed

+601
-0
lines changed

4 files changed

+601
-0
lines changed

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,27 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
283283
- `repo`: Repository name (string, required)
284284
- `issue_number`: Issue number (number, required)
285285

286+
- **get_issue_events** - Get events for a GitHub issue
287+
- `owner`: Repository owner (string, required)
288+
- `repo`: Repository name (string, required)
289+
- `issue_number`: Issue number (number, required)
290+
- `page`: Page number (number, optional)
291+
- `perPage`: Results per page (number, optional)
292+
293+
- **get_issue_timeline** - Get the timeline of events for a GitHub issue
294+
295+
- `owner`: Repository owner (string, required)
296+
- `repo`: Repository name (string, required)
297+
- `issue_number`: Issue number (number, required)
298+
- `page`: Page number (number, optional)
299+
- `perPage`: Results per page (number, optional)
300+
301+
- **get_issue_event** - Get a specific event for a GitHub issue
302+
303+
- `owner`: Repository owner (string, required)
304+
- `repo`: Repository name (string, required)
305+
- `event_id`: Event ID (number, required)
306+
286307
- **create_issue** - Create a new issue in a GitHub repository
287308

288309
- `owner`: Repository owner (string, required)

pkg/github/issues.go

+212
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,218 @@ func GetIssueComments(getClient GetClientFn, t translations.TranslationHelperFun
711711
}
712712
}
713713

714+
// GetIssueTimeline creates a tool to get timeline for a GitHub issue.
715+
func GetIssueTimeline(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
716+
return mcp.NewTool("get_issue_timeline",
717+
mcp.WithDescription(t("TOOL_GET_ISSUE_TIMELINE_DESCRIPTION", "Get timeline for a GitHub issue")),
718+
mcp.WithString("owner",
719+
mcp.Required(),
720+
mcp.Description("Repository owner"),
721+
),
722+
mcp.WithString("repo",
723+
mcp.Required(),
724+
mcp.Description("Repository name"),
725+
),
726+
mcp.WithNumber("issue_number",
727+
mcp.Required(),
728+
mcp.Description("Issue number"),
729+
),
730+
mcp.WithNumber("page",
731+
mcp.Description("Page number"),
732+
),
733+
mcp.WithNumber("per_page",
734+
mcp.Description("Number of records per page"),
735+
),
736+
),
737+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
738+
owner, err := requiredParam[string](request, "owner")
739+
if err != nil {
740+
return mcp.NewToolResultError(err.Error()), nil
741+
}
742+
repo, err := requiredParam[string](request, "repo")
743+
if err != nil {
744+
return mcp.NewToolResultError(err.Error()), nil
745+
}
746+
issueNumber, err := RequiredInt(request, "issue_number")
747+
if err != nil {
748+
return mcp.NewToolResultError(err.Error()), nil
749+
}
750+
page, err := OptionalIntParamWithDefault(request, "page", 1)
751+
if err != nil {
752+
return mcp.NewToolResultError(err.Error()), nil
753+
}
754+
perPage, err := OptionalIntParamWithDefault(request, "per_page", 30)
755+
if err != nil {
756+
return mcp.NewToolResultError(err.Error()), nil
757+
}
758+
759+
opts := &github.ListOptions{
760+
Page: page,
761+
PerPage: perPage,
762+
}
763+
764+
client, err := getClient(ctx)
765+
if err != nil {
766+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
767+
}
768+
events, resp, err := client.Issues.ListIssueTimeline(ctx, owner, repo, issueNumber, opts)
769+
if err != nil {
770+
return nil, fmt.Errorf("failed to get issue timeline: %w", err)
771+
}
772+
defer func() { _ = resp.Body.Close() }()
773+
774+
if resp.StatusCode != http.StatusOK {
775+
body, err := io.ReadAll(resp.Body)
776+
if err != nil {
777+
return nil, fmt.Errorf("failed to read response body: %w", err)
778+
}
779+
return mcp.NewToolResultError(fmt.Sprintf("failed to get issue timeline: %s", string(body))), nil
780+
}
781+
782+
r, err := json.Marshal(events)
783+
if err != nil {
784+
return nil, fmt.Errorf("failed to marshal response: %w", err)
785+
}
786+
787+
return mcp.NewToolResultText(string(r)), nil
788+
}
789+
}
790+
791+
// GetIssueEvents creates a tool to get events for a GitHub issue.
792+
func GetIssueEvents(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
793+
return mcp.NewTool("get_issue_events",
794+
mcp.WithDescription(t("TOOL_GET_ISSUE_EVENTS_DESCRIPTION", "Get list of events for a GitHub issue")),
795+
mcp.WithString("owner",
796+
mcp.Required(),
797+
mcp.Description("Repository owner"),
798+
),
799+
mcp.WithString("repo",
800+
mcp.Required(),
801+
mcp.Description("Repository name"),
802+
),
803+
mcp.WithNumber("issue_number",
804+
mcp.Required(),
805+
mcp.Description("Issue number"),
806+
),
807+
mcp.WithNumber("page",
808+
mcp.Description("Page number"),
809+
),
810+
mcp.WithNumber("per_page",
811+
mcp.Description("Number of records per page"),
812+
),
813+
),
814+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
815+
owner, err := requiredParam[string](request, "owner")
816+
if err != nil {
817+
return mcp.NewToolResultError(err.Error()), nil
818+
}
819+
repo, err := requiredParam[string](request, "repo")
820+
if err != nil {
821+
return mcp.NewToolResultError(err.Error()), nil
822+
}
823+
issueNumber, err := RequiredInt(request, "issue_number")
824+
if err != nil {
825+
return mcp.NewToolResultError(err.Error()), nil
826+
}
827+
page, err := OptionalIntParamWithDefault(request, "page", 1)
828+
if err != nil {
829+
return mcp.NewToolResultError(err.Error()), nil
830+
}
831+
perPage, err := OptionalIntParamWithDefault(request, "per_page", 30)
832+
if err != nil {
833+
return mcp.NewToolResultError(err.Error()), nil
834+
}
835+
836+
opts := &github.ListOptions{
837+
Page: page,
838+
PerPage: perPage,
839+
}
840+
841+
client, err := getClient(ctx)
842+
if err != nil {
843+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
844+
}
845+
events, resp, err := client.Issues.ListIssueEvents(ctx, owner, repo, issueNumber, opts)
846+
if err != nil {
847+
return nil, fmt.Errorf("failed to get issue events: %w", err)
848+
}
849+
defer func() { _ = resp.Body.Close() }()
850+
851+
if resp.StatusCode != http.StatusOK {
852+
body, err := io.ReadAll(resp.Body)
853+
if err != nil {
854+
return nil, fmt.Errorf("failed to read response body: %w", err)
855+
}
856+
return mcp.NewToolResultError(fmt.Sprintf("failed to get issue events: %s", string(body))), nil
857+
}
858+
859+
r, err := json.Marshal(events)
860+
if err != nil {
861+
return nil, fmt.Errorf("failed to marshal response: %w", err)
862+
}
863+
864+
return mcp.NewToolResultText(string(r)), nil
865+
}
866+
}
867+
868+
// GetIssueEvent creates a tool to get an event for a GitHub issue.
869+
func GetIssueEvent(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
870+
return mcp.NewTool("get_issue_event",
871+
mcp.WithDescription(t("TOOL_GET_ISSUE_EVENT_DESCRIPTION", "Get single event for a GitHub issue")),
872+
mcp.WithString("owner",
873+
mcp.Required(),
874+
mcp.Description("Repository owner"),
875+
),
876+
mcp.WithString("repo",
877+
mcp.Required(),
878+
mcp.Description("Repository name"),
879+
),
880+
mcp.WithNumber("event_id",
881+
mcp.Required(),
882+
mcp.Description("Event ID"),
883+
),
884+
),
885+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
886+
owner, err := requiredParam[string](request, "owner")
887+
if err != nil {
888+
return mcp.NewToolResultError(err.Error()), nil
889+
}
890+
repo, err := requiredParam[string](request, "repo")
891+
if err != nil {
892+
return mcp.NewToolResultError(err.Error()), nil
893+
}
894+
eventID, err := RequiredInt64(request, "event_id")
895+
if err != nil {
896+
return mcp.NewToolResultError(err.Error()), nil
897+
}
898+
899+
client, err := getClient(ctx)
900+
if err != nil {
901+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
902+
}
903+
event, resp, err := client.Issues.GetEvent(ctx, owner, repo, eventID)
904+
if err != nil {
905+
return nil, fmt.Errorf("failed to get issue event: %w", err)
906+
}
907+
defer func() { _ = resp.Body.Close() }()
908+
909+
if resp.StatusCode != http.StatusOK {
910+
body, err := io.ReadAll(resp.Body)
911+
if err != nil {
912+
return nil, fmt.Errorf("failed to read response body: %w", err)
913+
}
914+
return mcp.NewToolResultError(fmt.Sprintf("failed to get issue event: %s", string(body))), nil
915+
}
916+
917+
r, err := json.Marshal(event)
918+
if err != nil {
919+
return nil, fmt.Errorf("failed to marshal response: %w", err)
920+
}
921+
922+
return mcp.NewToolResultText(string(r)), nil
923+
}
924+
}
925+
714926
// parseISOTimestamp parses an ISO 8601 timestamp string into a time.Time object.
715927
// Returns the parsed time or an error if parsing fails.
716928
// Example formats supported: "2023-01-15T14:30:00Z", "2023-01-15"

0 commit comments

Comments
 (0)