issue-scribe-mcp 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,6 +5,17 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
5
5
  import { Octokit } from "@octokit/rest";
6
6
  import { z } from "zod";
7
7
  const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
8
+ // Reaction type mapping: user-friendly names to GitHub API format
9
+ const REACTION_MAP = {
10
+ "thumbs_up": "+1",
11
+ "thumbs_down": "-1",
12
+ "laugh": "laugh",
13
+ "confused": "confused",
14
+ "heart": "heart",
15
+ "hooray": "hooray",
16
+ "rocket": "rocket",
17
+ "eyes": "eyes",
18
+ };
8
19
  if (!GITHUB_TOKEN) {
9
20
  console.error("Error: GITHUB_TOKEN environment variable is required");
10
21
  process.exit(1);
@@ -48,6 +59,76 @@ const CreatePRSchema = z.object({
48
59
  draft: z.boolean().optional(),
49
60
  maintainer_can_modify: z.boolean().optional(),
50
61
  });
62
+ const AddCommentSchema = z.object({
63
+ owner: z.string(),
64
+ repo: z.string(),
65
+ issue_number: z.number(), // works for both issues and PRs
66
+ body: z.string(),
67
+ });
68
+ const UpdateCommentSchema = z.object({
69
+ owner: z.string(),
70
+ repo: z.string(),
71
+ comment_id: z.number(),
72
+ body: z.string(),
73
+ });
74
+ const DeleteCommentSchema = z.object({
75
+ owner: z.string(),
76
+ repo: z.string(),
77
+ comment_id: z.number(),
78
+ });
79
+ const AddReactionSchema = z.object({
80
+ owner: z.string(),
81
+ repo: z.string(),
82
+ comment_id: z.number().optional(),
83
+ issue_number: z.number().optional(),
84
+ reaction: z.enum(["thumbs_up", "thumbs_down", "laugh", "confused", "heart", "hooray", "rocket", "eyes"]),
85
+ }).refine(data => data.comment_id || data.issue_number, {
86
+ message: "Either comment_id or issue_number must be provided",
87
+ });
88
+ const SearchIssuesSchema = z.object({
89
+ owner: z.string(),
90
+ repo: z.string(),
91
+ query: z.string().optional(),
92
+ state: z.enum(["open", "closed", "all"]).optional(),
93
+ labels: z.array(z.string()).optional(),
94
+ sort: z.enum(["created", "updated", "comments"]).optional(),
95
+ direction: z.enum(["asc", "desc"]).optional(),
96
+ per_page: z.number().max(100).optional(),
97
+ });
98
+ const SearchPRsSchema = z.object({
99
+ owner: z.string(),
100
+ repo: z.string(),
101
+ query: z.string().optional(),
102
+ state: z.enum(["open", "closed", "all"]).optional(),
103
+ sort: z.enum(["created", "updated", "popularity", "long-running"]).optional(),
104
+ direction: z.enum(["asc", "desc"]).optional(),
105
+ per_page: z.number().max(100).optional(),
106
+ });
107
+ const ListRecentIssuesSchema = z.object({
108
+ owner: z.string(),
109
+ repo: z.string(),
110
+ state: z.enum(["open", "closed", "all"]).optional(),
111
+ sort: z.enum(["created", "updated"]).optional(),
112
+ per_page: z.number().max(100).optional(),
113
+ });
114
+ const MergePRSchema = z.object({
115
+ owner: z.string(),
116
+ repo: z.string(),
117
+ pull_number: z.number(),
118
+ merge_method: z.enum(["merge", "squash", "rebase"]).optional(),
119
+ commit_title: z.string().optional(),
120
+ commit_message: z.string().optional(),
121
+ });
122
+ const GetPRDiffSchema = z.object({
123
+ owner: z.string(),
124
+ repo: z.string(),
125
+ pull_number: z.number(),
126
+ });
127
+ const GetPRFilesSchema = z.object({
128
+ owner: z.string(),
129
+ repo: z.string(),
130
+ pull_number: z.number(),
131
+ });
51
132
  const server = new Server({
52
133
  name: "issue-scribe-mcp",
53
134
  version: "1.0.0",
@@ -137,6 +218,158 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
137
218
  required: ["owner", "repo", "title", "head", "base"],
138
219
  },
139
220
  },
221
+ {
222
+ name: "github_add_comment",
223
+ description: "Add a comment to a GitHub Issue or Pull Request",
224
+ inputSchema: {
225
+ type: "object",
226
+ properties: {
227
+ owner: { type: "string", description: "Repository owner" },
228
+ repo: { type: "string", description: "Repository name" },
229
+ issue_number: { type: "number", description: "Issue or PR number" },
230
+ body: { type: "string", description: "Comment body text" },
231
+ },
232
+ required: ["owner", "repo", "issue_number", "body"],
233
+ },
234
+ },
235
+ {
236
+ name: "github_update_comment",
237
+ description: "Update an existing comment on a GitHub Issue or Pull Request",
238
+ inputSchema: {
239
+ type: "object",
240
+ properties: {
241
+ owner: { type: "string", description: "Repository owner" },
242
+ repo: { type: "string", description: "Repository name" },
243
+ comment_id: { type: "number", description: "Comment ID to update" },
244
+ body: { type: "string", description: "New comment body text" },
245
+ },
246
+ required: ["owner", "repo", "comment_id", "body"],
247
+ },
248
+ },
249
+ {
250
+ name: "github_delete_comment",
251
+ description: "Delete a comment from a GitHub Issue or Pull Request",
252
+ inputSchema: {
253
+ type: "object",
254
+ properties: {
255
+ owner: { type: "string", description: "Repository owner" },
256
+ repo: { type: "string", description: "Repository name" },
257
+ comment_id: { type: "number", description: "Comment ID to delete" },
258
+ },
259
+ required: ["owner", "repo", "comment_id"],
260
+ },
261
+ },
262
+ {
263
+ name: "github_add_reaction",
264
+ description: "Add a reaction (emoji) to a comment or an issue/PR directly. Provide either comment_id OR issue_number.",
265
+ inputSchema: {
266
+ type: "object",
267
+ properties: {
268
+ owner: { type: "string", description: "Repository owner" },
269
+ repo: { type: "string", description: "Repository name" },
270
+ comment_id: { type: "number", description: "Comment ID to react to (optional if issue_number is provided)" },
271
+ issue_number: { type: "number", description: "Issue/PR number to react to (optional if comment_id is provided)" },
272
+ reaction: {
273
+ type: "string",
274
+ enum: ["thumbs_up", "thumbs_down", "laugh", "confused", "heart", "hooray", "rocket", "eyes"],
275
+ description: "Reaction type: thumbs_up 👍, thumbs_down 👎, laugh 😄, confused 😕, heart ❤️, hooray 🎉, rocket 🚀, eyes 👀"
276
+ },
277
+ },
278
+ required: ["owner", "repo", "reaction"],
279
+ },
280
+ },
281
+ {
282
+ name: "github_search_issues",
283
+ description: "Search for issues in a repository with advanced filters",
284
+ inputSchema: {
285
+ type: "object",
286
+ properties: {
287
+ owner: { type: "string", description: "Repository owner" },
288
+ repo: { type: "string", description: "Repository name" },
289
+ query: { type: "string", description: "Search query (optional)" },
290
+ state: { type: "string", enum: ["open", "closed", "all"], description: "Issue state (optional)" },
291
+ labels: { type: "array", items: { type: "string" }, description: "Filter by labels (optional)" },
292
+ sort: { type: "string", enum: ["created", "updated", "comments"], description: "Sort by (optional)" },
293
+ direction: { type: "string", enum: ["asc", "desc"], description: "Sort direction (optional)" },
294
+ per_page: { type: "number", description: "Results per page, max 100 (optional)" },
295
+ },
296
+ required: ["owner", "repo"],
297
+ },
298
+ },
299
+ {
300
+ name: "github_search_prs",
301
+ description: "Search for pull requests in a repository with advanced filters",
302
+ inputSchema: {
303
+ type: "object",
304
+ properties: {
305
+ owner: { type: "string", description: "Repository owner" },
306
+ repo: { type: "string", description: "Repository name" },
307
+ query: { type: "string", description: "Search query (optional)" },
308
+ state: { type: "string", enum: ["open", "closed", "all"], description: "PR state (optional)" },
309
+ sort: { type: "string", enum: ["created", "updated", "popularity", "long-running"], description: "Sort by (optional)" },
310
+ direction: { type: "string", enum: ["asc", "desc"], description: "Sort direction (optional)" },
311
+ per_page: { type: "number", description: "Results per page, max 100 (optional)" },
312
+ },
313
+ required: ["owner", "repo"],
314
+ },
315
+ },
316
+ {
317
+ name: "github_list_recent_issues",
318
+ description: "List recent issues in a repository",
319
+ inputSchema: {
320
+ type: "object",
321
+ properties: {
322
+ owner: { type: "string", description: "Repository owner" },
323
+ repo: { type: "string", description: "Repository name" },
324
+ state: { type: "string", enum: ["open", "closed", "all"], description: "Issue state (optional, default: open)" },
325
+ sort: { type: "string", enum: ["created", "updated"], description: "Sort by (optional, default: created)" },
326
+ per_page: { type: "number", description: "Results per page, max 100 (optional, default: 30)" },
327
+ },
328
+ required: ["owner", "repo"],
329
+ },
330
+ },
331
+ {
332
+ name: "github_merge_pr",
333
+ description: "Merge a pull request",
334
+ inputSchema: {
335
+ type: "object",
336
+ properties: {
337
+ owner: { type: "string", description: "Repository owner" },
338
+ repo: { type: "string", description: "Repository name" },
339
+ pull_number: { type: "number", description: "PR number to merge" },
340
+ merge_method: { type: "string", enum: ["merge", "squash", "rebase"], description: "Merge method (optional, default: merge)" },
341
+ commit_title: { type: "string", description: "Custom commit title (optional)" },
342
+ commit_message: { type: "string", description: "Custom commit message (optional)" },
343
+ },
344
+ required: ["owner", "repo", "pull_number"],
345
+ },
346
+ },
347
+ {
348
+ name: "github_get_pr_diff",
349
+ description: "Get the full diff of a pull request",
350
+ inputSchema: {
351
+ type: "object",
352
+ properties: {
353
+ owner: { type: "string", description: "Repository owner" },
354
+ repo: { type: "string", description: "Repository name" },
355
+ pull_number: { type: "number", description: "PR number" },
356
+ },
357
+ required: ["owner", "repo", "pull_number"],
358
+ },
359
+ },
360
+ {
361
+ name: "github_get_pr_files",
362
+ description: "Get list of files changed in a pull request with details",
363
+ inputSchema: {
364
+ type: "object",
365
+ properties: {
366
+ owner: { type: "string", description: "Repository owner" },
367
+ repo: { type: "string", description: "Repository name" },
368
+ pull_number: { type: "number", description: "PR number" },
369
+ },
370
+ required: ["owner", "repo", "pull_number"],
371
+ },
372
+ },
140
373
  ],
141
374
  };
142
375
  });
@@ -165,9 +398,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
165
398
  labels: issue.data.labels.map((l) => typeof l === "string" ? l : l.name),
166
399
  },
167
400
  comments: comments.data.map((c) => ({
401
+ id: c.id,
168
402
  user: c.user?.login,
169
403
  body: c.body,
170
404
  created_at: c.created_at,
405
+ html_url: c.html_url,
171
406
  })),
172
407
  }, null, 2),
173
408
  },
@@ -220,9 +455,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
220
455
  labels: pr.data.labels.map((l) => typeof l === "string" ? l : l.name),
221
456
  },
222
457
  comments: comments.data.map((c) => ({
458
+ id: c.id,
223
459
  user: c.user?.login,
224
460
  body: c.body,
225
461
  created_at: c.created_at,
462
+ html_url: c.html_url,
226
463
  })),
227
464
  commits: commits.data.map((c) => ({
228
465
  sha: c.sha,
@@ -408,6 +645,507 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
408
645
  };
409
646
  }
410
647
  }
648
+ if (name === "github_add_comment") {
649
+ try {
650
+ const { owner, repo, issue_number, body } = AddCommentSchema.parse(args);
651
+ const comment = await octokit.rest.issues.createComment({
652
+ owner,
653
+ repo,
654
+ issue_number,
655
+ body,
656
+ });
657
+ return {
658
+ content: [
659
+ {
660
+ type: "text",
661
+ text: JSON.stringify({
662
+ success: true,
663
+ comment: {
664
+ id: comment.data.id,
665
+ body: comment.data.body,
666
+ user: comment.data.user?.login,
667
+ html_url: comment.data.html_url,
668
+ created_at: comment.data.created_at,
669
+ },
670
+ message: `Comment added successfully to issue/PR #${issue_number}`,
671
+ }, null, 2),
672
+ },
673
+ ],
674
+ };
675
+ }
676
+ catch (error) {
677
+ const issueNum = args && typeof args === 'object' && 'issue_number' in args ? args.issue_number : 'unknown';
678
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
679
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
680
+ return {
681
+ content: [
682
+ {
683
+ type: "text",
684
+ text: JSON.stringify({
685
+ error: error.message,
686
+ status: error.status,
687
+ detail: `Failed to add comment to issue/PR #${issueNum} in ${owner}/${repo}`,
688
+ }, null, 2),
689
+ },
690
+ ],
691
+ isError: true,
692
+ };
693
+ }
694
+ }
695
+ if (name === "github_update_comment") {
696
+ try {
697
+ const { owner, repo, comment_id, body } = UpdateCommentSchema.parse(args);
698
+ const comment = await octokit.rest.issues.updateComment({
699
+ owner,
700
+ repo,
701
+ comment_id,
702
+ body,
703
+ });
704
+ return {
705
+ content: [
706
+ {
707
+ type: "text",
708
+ text: JSON.stringify({
709
+ success: true,
710
+ comment: {
711
+ id: comment.data.id,
712
+ body: comment.data.body,
713
+ user: comment.data.user?.login,
714
+ html_url: comment.data.html_url,
715
+ updated_at: comment.data.updated_at,
716
+ },
717
+ message: `Comment #${comment_id} updated successfully`,
718
+ }, null, 2),
719
+ },
720
+ ],
721
+ };
722
+ }
723
+ catch (error) {
724
+ const commentId = args && typeof args === 'object' && 'comment_id' in args ? args.comment_id : 'unknown';
725
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
726
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
727
+ return {
728
+ content: [
729
+ {
730
+ type: "text",
731
+ text: JSON.stringify({
732
+ error: error.message,
733
+ status: error.status,
734
+ detail: `Failed to update comment #${commentId} in ${owner}/${repo}`,
735
+ }, null, 2),
736
+ },
737
+ ],
738
+ isError: true,
739
+ };
740
+ }
741
+ }
742
+ if (name === "github_delete_comment") {
743
+ try {
744
+ const { owner, repo, comment_id } = DeleteCommentSchema.parse(args);
745
+ await octokit.rest.issues.deleteComment({
746
+ owner,
747
+ repo,
748
+ comment_id,
749
+ });
750
+ return {
751
+ content: [
752
+ {
753
+ type: "text",
754
+ text: JSON.stringify({
755
+ success: true,
756
+ message: `Comment #${comment_id} deleted successfully`,
757
+ }, null, 2),
758
+ },
759
+ ],
760
+ };
761
+ }
762
+ catch (error) {
763
+ const commentId = args && typeof args === 'object' && 'comment_id' in args ? args.comment_id : 'unknown';
764
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
765
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
766
+ return {
767
+ content: [
768
+ {
769
+ type: "text",
770
+ text: JSON.stringify({
771
+ error: error.message,
772
+ status: error.status,
773
+ detail: `Failed to delete comment #${commentId} in ${owner}/${repo}`,
774
+ }, null, 2),
775
+ },
776
+ ],
777
+ isError: true,
778
+ };
779
+ }
780
+ }
781
+ if (name === "github_add_reaction") {
782
+ try {
783
+ const { owner, repo, comment_id, issue_number, reaction } = AddReactionSchema.parse(args);
784
+ let reactionResponse;
785
+ let target = "";
786
+ const githubReaction = REACTION_MAP[reaction] || reaction;
787
+ if (comment_id) {
788
+ // Add reaction to a comment
789
+ reactionResponse = await octokit.rest.reactions.createForIssueComment({
790
+ owner,
791
+ repo,
792
+ comment_id,
793
+ content: githubReaction,
794
+ });
795
+ target = `comment #${comment_id}`;
796
+ }
797
+ else if (issue_number) {
798
+ // Add reaction to an issue/PR
799
+ reactionResponse = await octokit.rest.reactions.createForIssue({
800
+ owner,
801
+ repo,
802
+ issue_number,
803
+ content: githubReaction,
804
+ });
805
+ target = `issue/PR #${issue_number}`;
806
+ }
807
+ return {
808
+ content: [
809
+ {
810
+ type: "text",
811
+ text: JSON.stringify({
812
+ success: true,
813
+ reaction: {
814
+ id: reactionResponse.data.id,
815
+ content: reactionResponse.data.content,
816
+ user: reactionResponse.data.user?.login,
817
+ created_at: reactionResponse.data.created_at,
818
+ },
819
+ message: `Reaction "${reaction}" added successfully to ${target}`,
820
+ }, null, 2),
821
+ },
822
+ ],
823
+ };
824
+ }
825
+ catch (error) {
826
+ const commentId = args && typeof args === 'object' && 'comment_id' in args ? args.comment_id : undefined;
827
+ const issueNum = args && typeof args === 'object' && 'issue_number' in args ? args.issue_number : undefined;
828
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
829
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
830
+ const reaction = args && typeof args === 'object' && 'reaction' in args ? args.reaction : 'unknown';
831
+ const target = commentId ? `comment #${commentId}` : issueNum ? `issue/PR #${issueNum}` : 'unknown';
832
+ return {
833
+ content: [
834
+ {
835
+ type: "text",
836
+ text: JSON.stringify({
837
+ error: error.message,
838
+ status: error.status,
839
+ detail: `Failed to add reaction "${reaction}" to ${target} in ${owner}/${repo}`,
840
+ }, null, 2),
841
+ },
842
+ ],
843
+ isError: true,
844
+ };
845
+ }
846
+ }
847
+ if (name === "github_search_issues") {
848
+ try {
849
+ const { owner, repo, query, state, labels, sort, direction, per_page } = SearchIssuesSchema.parse(args);
850
+ const issues = await octokit.rest.issues.listForRepo({
851
+ owner,
852
+ repo,
853
+ state: state || "open",
854
+ labels: labels?.join(","),
855
+ sort: sort,
856
+ direction: direction,
857
+ per_page: per_page || 30,
858
+ });
859
+ // Filter out pull requests first
860
+ const issuesOnly = issues.data.filter(issue => !issue.pull_request);
861
+ const filteredIssues = query
862
+ ? issues.data.filter(issue => !issue.pull_request).filter(issue => issue.title.toLowerCase().includes(query.toLowerCase()) ||
863
+ issue.body?.toLowerCase().includes(query.toLowerCase()))
864
+ : issues.data.filter(issue => !issue.pull_request);
865
+ return {
866
+ content: [
867
+ {
868
+ type: "text",
869
+ text: JSON.stringify({
870
+ total_count: filteredIssues.length,
871
+ issues: filteredIssues.map(issue => ({
872
+ number: issue.number,
873
+ title: issue.title,
874
+ state: issue.state,
875
+ user: issue.user?.login,
876
+ labels: issue.labels.map((l) => typeof l === "string" ? l : l.name),
877
+ created_at: issue.created_at,
878
+ updated_at: issue.updated_at,
879
+ comments: issue.comments,
880
+ html_url: issue.html_url,
881
+ })),
882
+ }, null, 2),
883
+ },
884
+ ],
885
+ };
886
+ }
887
+ catch (error) {
888
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
889
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
890
+ return {
891
+ content: [
892
+ {
893
+ type: "text",
894
+ text: JSON.stringify({
895
+ error: error.message,
896
+ status: error.status,
897
+ detail: `Failed to search issues in ${owner}/${repo}`,
898
+ }, null, 2),
899
+ },
900
+ ],
901
+ isError: true,
902
+ };
903
+ }
904
+ }
905
+ if (name === "github_search_prs") {
906
+ try {
907
+ const { owner, repo, query, state, sort, direction, per_page } = SearchPRsSchema.parse(args);
908
+ const prs = await octokit.rest.pulls.list({
909
+ owner,
910
+ repo,
911
+ state: state || "open",
912
+ sort: sort,
913
+ direction: direction,
914
+ per_page: per_page || 30,
915
+ });
916
+ const filteredPRs = query
917
+ ? prs.data.filter(pr => pr.title.toLowerCase().includes(query.toLowerCase()) ||
918
+ pr.body?.toLowerCase().includes(query.toLowerCase()))
919
+ : prs.data;
920
+ return {
921
+ content: [
922
+ {
923
+ type: "text",
924
+ text: JSON.stringify({
925
+ total_count: filteredPRs.length,
926
+ pull_requests: filteredPRs.map(pr => ({
927
+ number: pr.number,
928
+ title: pr.title,
929
+ state: pr.state,
930
+ user: pr.user?.login,
931
+ head: pr.head.ref,
932
+ base: pr.base.ref,
933
+ created_at: pr.created_at,
934
+ updated_at: pr.updated_at,
935
+ draft: pr.draft,
936
+ html_url: pr.html_url,
937
+ })),
938
+ }, null, 2),
939
+ },
940
+ ],
941
+ };
942
+ }
943
+ catch (error) {
944
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
945
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
946
+ return {
947
+ content: [
948
+ {
949
+ type: "text",
950
+ text: JSON.stringify({
951
+ error: error.message,
952
+ status: error.status,
953
+ detail: `Failed to search PRs in ${owner}/${repo}`,
954
+ }, null, 2),
955
+ },
956
+ ],
957
+ isError: true,
958
+ };
959
+ }
960
+ }
961
+ if (name === "github_list_recent_issues") {
962
+ try {
963
+ const { owner, repo, state, sort, per_page } = ListRecentIssuesSchema.parse(args);
964
+ const issues = await octokit.rest.issues.listForRepo({
965
+ owner,
966
+ repo,
967
+ state: state || "open",
968
+ sort: sort || "created",
969
+ direction: "desc",
970
+ per_page: per_page || 30,
971
+ });
972
+ // Filter out pull requests (GitHub API returns both issues and PRs)
973
+ const actualIssues = issues.data.filter(issue => !issue.pull_request);
974
+ return {
975
+ content: [
976
+ {
977
+ type: "text",
978
+ text: JSON.stringify({
979
+ count: actualIssues.length,
980
+ issues: actualIssues.map(issue => ({
981
+ number: issue.number,
982
+ title: issue.title,
983
+ state: issue.state,
984
+ user: issue.user?.login,
985
+ labels: issue.labels.map((l) => typeof l === "string" ? l : l.name),
986
+ created_at: issue.created_at,
987
+ updated_at: issue.updated_at,
988
+ comments: issue.comments,
989
+ html_url: issue.html_url,
990
+ })),
991
+ }, null, 2),
992
+ },
993
+ ],
994
+ };
995
+ }
996
+ catch (error) {
997
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
998
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
999
+ return {
1000
+ content: [
1001
+ {
1002
+ type: "text",
1003
+ text: JSON.stringify({
1004
+ error: error.message,
1005
+ status: error.status,
1006
+ detail: `Failed to list recent issues in ${owner}/${repo}`,
1007
+ }, null, 2),
1008
+ },
1009
+ ],
1010
+ isError: true,
1011
+ };
1012
+ }
1013
+ }
1014
+ if (name === "github_merge_pr") {
1015
+ try {
1016
+ const { owner, repo, pull_number, merge_method, commit_title, commit_message } = MergePRSchema.parse(args);
1017
+ const result = await octokit.rest.pulls.merge({
1018
+ owner,
1019
+ repo,
1020
+ pull_number,
1021
+ merge_method: merge_method,
1022
+ commit_title,
1023
+ commit_message,
1024
+ });
1025
+ return {
1026
+ content: [
1027
+ {
1028
+ type: "text",
1029
+ text: JSON.stringify({
1030
+ success: true,
1031
+ merged: result.data.merged,
1032
+ sha: result.data.sha,
1033
+ message: result.data.message,
1034
+ }, null, 2),
1035
+ },
1036
+ ],
1037
+ };
1038
+ }
1039
+ catch (error) {
1040
+ const pullNum = args && typeof args === 'object' && 'pull_number' in args ? args.pull_number : 'unknown';
1041
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
1042
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
1043
+ return {
1044
+ content: [
1045
+ {
1046
+ type: "text",
1047
+ text: JSON.stringify({
1048
+ error: error.message,
1049
+ status: error.status,
1050
+ detail: `Failed to merge PR #${pullNum} in ${owner}/${repo}`,
1051
+ }, null, 2),
1052
+ },
1053
+ ],
1054
+ isError: true,
1055
+ };
1056
+ }
1057
+ }
1058
+ if (name === "github_get_pr_diff") {
1059
+ try {
1060
+ const { owner, repo, pull_number } = GetPRDiffSchema.parse(args);
1061
+ const diff = await octokit.rest.pulls.get({
1062
+ owner,
1063
+ repo,
1064
+ pull_number,
1065
+ mediaType: {
1066
+ format: "diff",
1067
+ },
1068
+ });
1069
+ return {
1070
+ content: [
1071
+ {
1072
+ type: "text",
1073
+ text: JSON.stringify({
1074
+ pull_number,
1075
+ diff: diff.data,
1076
+ }, null, 2),
1077
+ },
1078
+ ],
1079
+ };
1080
+ }
1081
+ catch (error) {
1082
+ const pullNum = args && typeof args === 'object' && 'pull_number' in args ? args.pull_number : 'unknown';
1083
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
1084
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
1085
+ return {
1086
+ content: [
1087
+ {
1088
+ type: "text",
1089
+ text: JSON.stringify({
1090
+ error: error.message,
1091
+ status: error.status,
1092
+ detail: `Failed to get diff for PR #${pullNum} in ${owner}/${repo}`,
1093
+ }, null, 2),
1094
+ },
1095
+ ],
1096
+ isError: true,
1097
+ };
1098
+ }
1099
+ }
1100
+ if (name === "github_get_pr_files") {
1101
+ try {
1102
+ const { owner, repo, pull_number } = GetPRFilesSchema.parse(args);
1103
+ const files = await octokit.rest.pulls.listFiles({
1104
+ owner,
1105
+ repo,
1106
+ pull_number,
1107
+ });
1108
+ return {
1109
+ content: [
1110
+ {
1111
+ type: "text",
1112
+ text: JSON.stringify({
1113
+ pull_number,
1114
+ total_files: files.data.length,
1115
+ files: files.data.map(file => ({
1116
+ filename: file.filename,
1117
+ status: file.status,
1118
+ additions: file.additions,
1119
+ deletions: file.deletions,
1120
+ changes: file.changes,
1121
+ blob_url: file.blob_url,
1122
+ raw_url: file.raw_url,
1123
+ patch: file.patch,
1124
+ })),
1125
+ }, null, 2),
1126
+ },
1127
+ ],
1128
+ };
1129
+ }
1130
+ catch (error) {
1131
+ const pullNum = args && typeof args === 'object' && 'pull_number' in args ? args.pull_number : 'unknown';
1132
+ const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
1133
+ const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
1134
+ return {
1135
+ content: [
1136
+ {
1137
+ type: "text",
1138
+ text: JSON.stringify({
1139
+ error: error.message,
1140
+ status: error.status,
1141
+ detail: `Failed to get files for PR #${pullNum} in ${owner}/${repo}`,
1142
+ }, null, 2),
1143
+ },
1144
+ ],
1145
+ isError: true,
1146
+ };
1147
+ }
1148
+ }
411
1149
  throw new Error(`Unknown tool: ${name}`);
412
1150
  });
413
1151
  async function main() {