Skip to content

Commit 4e387dc

Browse files
committed
Added get_issue_events , get_issue_timeline and get_issue_event tools
1 parent 3ec8699 commit 4e387dc

File tree

4 files changed

+604
-0
lines changed

4 files changed

+604
-0
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,27 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
159159
- `repo`: Repository name (string, required)
160160
- `issue_number`: Issue number (number, required)
161161

162+
- **get_issue_events** - Get events for a GitHub issue
163+
- `owner`: Repository owner (string, required)
164+
- `repo`: Repository name (string, required)
165+
- `issue_number`: Issue number (number, required)
166+
- `page`: Page number (number, optional)
167+
- `perPage`: Results per page (number, optional)
168+
169+
- **get_issue_timeline** - Get the timeline of events for a GitHub issue
170+
171+
- `owner`: Repository owner (string, required)
172+
- `repo`: Repository name (string, required)
173+
- `issue_number`: Issue number (number, required)
174+
- `page`: Page number (number, optional)
175+
- `perPage`: Results per page (number, optional)
176+
177+
- **get_issue_event** - Get a specific event for a GitHub issue
178+
179+
- `owner`: Repository owner (string, required)
180+
- `repo`: Repository name (string, required)
181+
- `event_id`: Event ID (number, required)
182+
162183
- **create_issue** - Create a new issue in a GitHub repository
163184

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

pkg/github/issues.go

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,218 @@ func GetIssueComments(getClient GetClientFn, t translations.TranslationHelperFun
683683
}
684684
}
685685

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

0 commit comments

Comments
 (0)