issue-scribe-mcp 1.0.0 → 1.2.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/LICENSE +21 -0
- package/README.md +354 -42
- package/README_EN.md +411 -0
- package/dist/index.js +1293 -0
- package/package.json +1 -1
- package/src/index.ts +1428 -0
- package/test-local.sh +63 -0
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,124 @@ 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
|
+
});
|
|
132
|
+
const CreateLabelSchema = z.object({
|
|
133
|
+
owner: z.string(),
|
|
134
|
+
repo: z.string(),
|
|
135
|
+
name: z.string(),
|
|
136
|
+
color: z.string(), // hex color without '#'
|
|
137
|
+
description: z.string().optional(),
|
|
138
|
+
});
|
|
139
|
+
const UpdateLabelSchema = z.object({
|
|
140
|
+
owner: z.string(),
|
|
141
|
+
repo: z.string(),
|
|
142
|
+
name: z.string(), // current label name
|
|
143
|
+
new_name: z.string().optional(),
|
|
144
|
+
color: z.string().optional(), // hex color without '#'
|
|
145
|
+
description: z.string().optional(),
|
|
146
|
+
});
|
|
147
|
+
const DeleteLabelSchema = z.object({
|
|
148
|
+
owner: z.string(),
|
|
149
|
+
repo: z.string(),
|
|
150
|
+
name: z.string(),
|
|
151
|
+
});
|
|
152
|
+
const ListLabelsSchema = z.object({
|
|
153
|
+
owner: z.string(),
|
|
154
|
+
repo: z.string(),
|
|
155
|
+
per_page: z.number().max(100).optional(),
|
|
156
|
+
});
|
|
157
|
+
const ListBranchesSchema = z.object({
|
|
158
|
+
owner: z.string(),
|
|
159
|
+
repo: z.string(),
|
|
160
|
+
protected: z.boolean().optional(),
|
|
161
|
+
per_page: z.number().max(100).optional(),
|
|
162
|
+
});
|
|
163
|
+
const CreateBranchSchema = z.object({
|
|
164
|
+
owner: z.string(),
|
|
165
|
+
repo: z.string(),
|
|
166
|
+
branch: z.string(), // new branch name
|
|
167
|
+
ref: z.string(), // source branch or commit SHA (e.g., "main" or full ref "refs/heads/main")
|
|
168
|
+
});
|
|
169
|
+
const DeleteBranchSchema = z.object({
|
|
170
|
+
owner: z.string(),
|
|
171
|
+
repo: z.string(),
|
|
172
|
+
branch: z.string(), // branch name to delete
|
|
173
|
+
});
|
|
174
|
+
const CompareBranchesSchema = z.object({
|
|
175
|
+
owner: z.string(),
|
|
176
|
+
repo: z.string(),
|
|
177
|
+
base: z.string(), // base branch
|
|
178
|
+
head: z.string(), // head branch to compare
|
|
179
|
+
});
|
|
51
180
|
const server = new Server({
|
|
52
181
|
name: "issue-scribe-mcp",
|
|
53
182
|
version: "1.0.0",
|
|
@@ -137,6 +266,270 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
137
266
|
required: ["owner", "repo", "title", "head", "base"],
|
|
138
267
|
},
|
|
139
268
|
},
|
|
269
|
+
{
|
|
270
|
+
name: "github_add_comment",
|
|
271
|
+
description: "Add a comment to a GitHub Issue or Pull Request",
|
|
272
|
+
inputSchema: {
|
|
273
|
+
type: "object",
|
|
274
|
+
properties: {
|
|
275
|
+
owner: { type: "string", description: "Repository owner" },
|
|
276
|
+
repo: { type: "string", description: "Repository name" },
|
|
277
|
+
issue_number: { type: "number", description: "Issue or PR number" },
|
|
278
|
+
body: { type: "string", description: "Comment body text" },
|
|
279
|
+
},
|
|
280
|
+
required: ["owner", "repo", "issue_number", "body"],
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: "github_update_comment",
|
|
285
|
+
description: "Update an existing comment on a GitHub Issue or Pull Request",
|
|
286
|
+
inputSchema: {
|
|
287
|
+
type: "object",
|
|
288
|
+
properties: {
|
|
289
|
+
owner: { type: "string", description: "Repository owner" },
|
|
290
|
+
repo: { type: "string", description: "Repository name" },
|
|
291
|
+
comment_id: { type: "number", description: "Comment ID to update" },
|
|
292
|
+
body: { type: "string", description: "New comment body text" },
|
|
293
|
+
},
|
|
294
|
+
required: ["owner", "repo", "comment_id", "body"],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: "github_delete_comment",
|
|
299
|
+
description: "Delete a comment from a GitHub Issue or Pull Request",
|
|
300
|
+
inputSchema: {
|
|
301
|
+
type: "object",
|
|
302
|
+
properties: {
|
|
303
|
+
owner: { type: "string", description: "Repository owner" },
|
|
304
|
+
repo: { type: "string", description: "Repository name" },
|
|
305
|
+
comment_id: { type: "number", description: "Comment ID to delete" },
|
|
306
|
+
},
|
|
307
|
+
required: ["owner", "repo", "comment_id"],
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: "github_add_reaction",
|
|
312
|
+
description: "Add a reaction (emoji) to a comment or an issue/PR directly. Provide either comment_id OR issue_number.",
|
|
313
|
+
inputSchema: {
|
|
314
|
+
type: "object",
|
|
315
|
+
properties: {
|
|
316
|
+
owner: { type: "string", description: "Repository owner" },
|
|
317
|
+
repo: { type: "string", description: "Repository name" },
|
|
318
|
+
comment_id: { type: "number", description: "Comment ID to react to (optional if issue_number is provided)" },
|
|
319
|
+
issue_number: { type: "number", description: "Issue/PR number to react to (optional if comment_id is provided)" },
|
|
320
|
+
reaction: {
|
|
321
|
+
type: "string",
|
|
322
|
+
enum: ["thumbs_up", "thumbs_down", "laugh", "confused", "heart", "hooray", "rocket", "eyes"],
|
|
323
|
+
description: "Reaction type: thumbs_up 👍, thumbs_down 👎, laugh 😄, confused 😕, heart ❤️, hooray 🎉, rocket 🚀, eyes 👀"
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
required: ["owner", "repo", "reaction"],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: "github_search_issues",
|
|
331
|
+
description: "Search for issues in a repository with advanced filters",
|
|
332
|
+
inputSchema: {
|
|
333
|
+
type: "object",
|
|
334
|
+
properties: {
|
|
335
|
+
owner: { type: "string", description: "Repository owner" },
|
|
336
|
+
repo: { type: "string", description: "Repository name" },
|
|
337
|
+
query: { type: "string", description: "Search query (optional)" },
|
|
338
|
+
state: { type: "string", enum: ["open", "closed", "all"], description: "Issue state (optional)" },
|
|
339
|
+
labels: { type: "array", items: { type: "string" }, description: "Filter by labels (optional)" },
|
|
340
|
+
sort: { type: "string", enum: ["created", "updated", "comments"], description: "Sort by (optional)" },
|
|
341
|
+
direction: { type: "string", enum: ["asc", "desc"], description: "Sort direction (optional)" },
|
|
342
|
+
per_page: { type: "number", description: "Results per page, max 100 (optional)" },
|
|
343
|
+
},
|
|
344
|
+
required: ["owner", "repo"],
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: "github_search_prs",
|
|
349
|
+
description: "Search for pull requests in a repository with advanced filters",
|
|
350
|
+
inputSchema: {
|
|
351
|
+
type: "object",
|
|
352
|
+
properties: {
|
|
353
|
+
owner: { type: "string", description: "Repository owner" },
|
|
354
|
+
repo: { type: "string", description: "Repository name" },
|
|
355
|
+
query: { type: "string", description: "Search query (optional)" },
|
|
356
|
+
state: { type: "string", enum: ["open", "closed", "all"], description: "PR state (optional)" },
|
|
357
|
+
sort: { type: "string", enum: ["created", "updated", "popularity", "long-running"], description: "Sort by (optional)" },
|
|
358
|
+
direction: { type: "string", enum: ["asc", "desc"], description: "Sort direction (optional)" },
|
|
359
|
+
per_page: { type: "number", description: "Results per page, max 100 (optional)" },
|
|
360
|
+
},
|
|
361
|
+
required: ["owner", "repo"],
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
name: "github_list_recent_issues",
|
|
366
|
+
description: "List recent issues in a repository",
|
|
367
|
+
inputSchema: {
|
|
368
|
+
type: "object",
|
|
369
|
+
properties: {
|
|
370
|
+
owner: { type: "string", description: "Repository owner" },
|
|
371
|
+
repo: { type: "string", description: "Repository name" },
|
|
372
|
+
state: { type: "string", enum: ["open", "closed", "all"], description: "Issue state (optional, default: open)" },
|
|
373
|
+
sort: { type: "string", enum: ["created", "updated"], description: "Sort by (optional, default: created)" },
|
|
374
|
+
per_page: { type: "number", description: "Results per page, max 100 (optional, default: 30)" },
|
|
375
|
+
},
|
|
376
|
+
required: ["owner", "repo"],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: "github_merge_pr",
|
|
381
|
+
description: "Merge a pull request",
|
|
382
|
+
inputSchema: {
|
|
383
|
+
type: "object",
|
|
384
|
+
properties: {
|
|
385
|
+
owner: { type: "string", description: "Repository owner" },
|
|
386
|
+
repo: { type: "string", description: "Repository name" },
|
|
387
|
+
pull_number: { type: "number", description: "PR number to merge" },
|
|
388
|
+
merge_method: { type: "string", enum: ["merge", "squash", "rebase"], description: "Merge method (optional, default: merge)" },
|
|
389
|
+
commit_title: { type: "string", description: "Custom commit title (optional)" },
|
|
390
|
+
commit_message: { type: "string", description: "Custom commit message (optional)" },
|
|
391
|
+
},
|
|
392
|
+
required: ["owner", "repo", "pull_number"],
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: "github_get_pr_diff",
|
|
397
|
+
description: "Get the full diff of a pull request",
|
|
398
|
+
inputSchema: {
|
|
399
|
+
type: "object",
|
|
400
|
+
properties: {
|
|
401
|
+
owner: { type: "string", description: "Repository owner" },
|
|
402
|
+
repo: { type: "string", description: "Repository name" },
|
|
403
|
+
pull_number: { type: "number", description: "PR number" },
|
|
404
|
+
},
|
|
405
|
+
required: ["owner", "repo", "pull_number"],
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: "github_get_pr_files",
|
|
410
|
+
description: "Get list of files changed in a pull request with details",
|
|
411
|
+
inputSchema: {
|
|
412
|
+
type: "object",
|
|
413
|
+
properties: {
|
|
414
|
+
owner: { type: "string", description: "Repository owner" },
|
|
415
|
+
repo: { type: "string", description: "Repository name" },
|
|
416
|
+
pull_number: { type: "number", description: "PR number" },
|
|
417
|
+
},
|
|
418
|
+
required: ["owner", "repo", "pull_number"],
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
name: "github_create_label",
|
|
423
|
+
description: "Create a new label in the repository",
|
|
424
|
+
inputSchema: {
|
|
425
|
+
type: "object",
|
|
426
|
+
properties: {
|
|
427
|
+
owner: { type: "string", description: "Repository owner" },
|
|
428
|
+
repo: { type: "string", description: "Repository name" },
|
|
429
|
+
name: { type: "string", description: "Label name" },
|
|
430
|
+
color: { type: "string", description: "Hex color code without '#' (e.g., 'FF0000' for red)" },
|
|
431
|
+
description: { type: "string", description: "Label description (optional)" },
|
|
432
|
+
},
|
|
433
|
+
required: ["owner", "repo", "name", "color"],
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
name: "github_update_label",
|
|
438
|
+
description: "Update an existing label (name, color, or description)",
|
|
439
|
+
inputSchema: {
|
|
440
|
+
type: "object",
|
|
441
|
+
properties: {
|
|
442
|
+
owner: { type: "string", description: "Repository owner" },
|
|
443
|
+
repo: { type: "string", description: "Repository name" },
|
|
444
|
+
name: { type: "string", description: "Current label name to update" },
|
|
445
|
+
new_name: { type: "string", description: "New label name (optional)" },
|
|
446
|
+
color: { type: "string", description: "New hex color code without '#' (optional)" },
|
|
447
|
+
description: { type: "string", description: "New description (optional)" },
|
|
448
|
+
},
|
|
449
|
+
required: ["owner", "repo", "name"],
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: "github_delete_label",
|
|
454
|
+
description: "Delete a label from the repository",
|
|
455
|
+
inputSchema: {
|
|
456
|
+
type: "object",
|
|
457
|
+
properties: {
|
|
458
|
+
owner: { type: "string", description: "Repository owner" },
|
|
459
|
+
repo: { type: "string", description: "Repository name" },
|
|
460
|
+
name: { type: "string", description: "Label name to delete" },
|
|
461
|
+
},
|
|
462
|
+
required: ["owner", "repo", "name"],
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
name: "github_list_labels",
|
|
467
|
+
description: "List all labels in the repository",
|
|
468
|
+
inputSchema: {
|
|
469
|
+
type: "object",
|
|
470
|
+
properties: {
|
|
471
|
+
owner: { type: "string", description: "Repository owner" },
|
|
472
|
+
repo: { type: "string", description: "Repository name" },
|
|
473
|
+
per_page: { type: "number", description: "Results per page, max 100 (optional, default: 30)" },
|
|
474
|
+
},
|
|
475
|
+
required: ["owner", "repo"],
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
name: "github_list_branches",
|
|
480
|
+
description: "List all branches in the repository",
|
|
481
|
+
inputSchema: {
|
|
482
|
+
type: "object",
|
|
483
|
+
properties: {
|
|
484
|
+
owner: { type: "string", description: "Repository owner" },
|
|
485
|
+
repo: { type: "string", description: "Repository name" },
|
|
486
|
+
protected: { type: "boolean", description: "Filter by protected status (optional)" },
|
|
487
|
+
per_page: { type: "number", description: "Results per page, max 100 (optional, default: 30)" },
|
|
488
|
+
},
|
|
489
|
+
required: ["owner", "repo"],
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
name: "github_create_branch",
|
|
494
|
+
description: "Create a new branch from an existing branch or commit",
|
|
495
|
+
inputSchema: {
|
|
496
|
+
type: "object",
|
|
497
|
+
properties: {
|
|
498
|
+
owner: { type: "string", description: "Repository owner" },
|
|
499
|
+
repo: { type: "string", description: "Repository name" },
|
|
500
|
+
branch: { type: "string", description: "New branch name" },
|
|
501
|
+
ref: { type: "string", description: "Source branch name or commit SHA (e.g., 'main' or 'abc123')" },
|
|
502
|
+
},
|
|
503
|
+
required: ["owner", "repo", "branch", "ref"],
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: "github_delete_branch",
|
|
508
|
+
description: "Delete a branch from the repository",
|
|
509
|
+
inputSchema: {
|
|
510
|
+
type: "object",
|
|
511
|
+
properties: {
|
|
512
|
+
owner: { type: "string", description: "Repository owner" },
|
|
513
|
+
repo: { type: "string", description: "Repository name" },
|
|
514
|
+
branch: { type: "string", description: "Branch name to delete" },
|
|
515
|
+
},
|
|
516
|
+
required: ["owner", "repo", "branch"],
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
name: "github_compare_branches",
|
|
521
|
+
description: "Compare two branches and show the differences",
|
|
522
|
+
inputSchema: {
|
|
523
|
+
type: "object",
|
|
524
|
+
properties: {
|
|
525
|
+
owner: { type: "string", description: "Repository owner" },
|
|
526
|
+
repo: { type: "string", description: "Repository name" },
|
|
527
|
+
base: { type: "string", description: "Base branch name" },
|
|
528
|
+
head: { type: "string", description: "Head branch name to compare" },
|
|
529
|
+
},
|
|
530
|
+
required: ["owner", "repo", "base", "head"],
|
|
531
|
+
},
|
|
532
|
+
},
|
|
140
533
|
],
|
|
141
534
|
};
|
|
142
535
|
});
|
|
@@ -165,9 +558,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
165
558
|
labels: issue.data.labels.map((l) => typeof l === "string" ? l : l.name),
|
|
166
559
|
},
|
|
167
560
|
comments: comments.data.map((c) => ({
|
|
561
|
+
id: c.id,
|
|
168
562
|
user: c.user?.login,
|
|
169
563
|
body: c.body,
|
|
170
564
|
created_at: c.created_at,
|
|
565
|
+
html_url: c.html_url,
|
|
171
566
|
})),
|
|
172
567
|
}, null, 2),
|
|
173
568
|
},
|
|
@@ -220,9 +615,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
220
615
|
labels: pr.data.labels.map((l) => typeof l === "string" ? l : l.name),
|
|
221
616
|
},
|
|
222
617
|
comments: comments.data.map((c) => ({
|
|
618
|
+
id: c.id,
|
|
223
619
|
user: c.user?.login,
|
|
224
620
|
body: c.body,
|
|
225
621
|
created_at: c.created_at,
|
|
622
|
+
html_url: c.html_url,
|
|
226
623
|
})),
|
|
227
624
|
commits: commits.data.map((c) => ({
|
|
228
625
|
sha: c.sha,
|
|
@@ -408,6 +805,902 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
408
805
|
};
|
|
409
806
|
}
|
|
410
807
|
}
|
|
808
|
+
if (name === "github_add_comment") {
|
|
809
|
+
try {
|
|
810
|
+
const { owner, repo, issue_number, body } = AddCommentSchema.parse(args);
|
|
811
|
+
const comment = await octokit.rest.issues.createComment({
|
|
812
|
+
owner,
|
|
813
|
+
repo,
|
|
814
|
+
issue_number,
|
|
815
|
+
body,
|
|
816
|
+
});
|
|
817
|
+
return {
|
|
818
|
+
content: [
|
|
819
|
+
{
|
|
820
|
+
type: "text",
|
|
821
|
+
text: JSON.stringify({
|
|
822
|
+
success: true,
|
|
823
|
+
comment: {
|
|
824
|
+
id: comment.data.id,
|
|
825
|
+
body: comment.data.body,
|
|
826
|
+
user: comment.data.user?.login,
|
|
827
|
+
html_url: comment.data.html_url,
|
|
828
|
+
created_at: comment.data.created_at,
|
|
829
|
+
},
|
|
830
|
+
message: `Comment added successfully to issue/PR #${issue_number}`,
|
|
831
|
+
}, null, 2),
|
|
832
|
+
},
|
|
833
|
+
],
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
catch (error) {
|
|
837
|
+
const issueNum = args && typeof args === 'object' && 'issue_number' in args ? args.issue_number : 'unknown';
|
|
838
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
839
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
840
|
+
return {
|
|
841
|
+
content: [
|
|
842
|
+
{
|
|
843
|
+
type: "text",
|
|
844
|
+
text: JSON.stringify({
|
|
845
|
+
error: error.message,
|
|
846
|
+
status: error.status,
|
|
847
|
+
detail: `Failed to add comment to issue/PR #${issueNum} in ${owner}/${repo}`,
|
|
848
|
+
}, null, 2),
|
|
849
|
+
},
|
|
850
|
+
],
|
|
851
|
+
isError: true,
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (name === "github_update_comment") {
|
|
856
|
+
try {
|
|
857
|
+
const { owner, repo, comment_id, body } = UpdateCommentSchema.parse(args);
|
|
858
|
+
const comment = await octokit.rest.issues.updateComment({
|
|
859
|
+
owner,
|
|
860
|
+
repo,
|
|
861
|
+
comment_id,
|
|
862
|
+
body,
|
|
863
|
+
});
|
|
864
|
+
return {
|
|
865
|
+
content: [
|
|
866
|
+
{
|
|
867
|
+
type: "text",
|
|
868
|
+
text: JSON.stringify({
|
|
869
|
+
success: true,
|
|
870
|
+
comment: {
|
|
871
|
+
id: comment.data.id,
|
|
872
|
+
body: comment.data.body,
|
|
873
|
+
user: comment.data.user?.login,
|
|
874
|
+
html_url: comment.data.html_url,
|
|
875
|
+
updated_at: comment.data.updated_at,
|
|
876
|
+
},
|
|
877
|
+
message: `Comment #${comment_id} updated successfully`,
|
|
878
|
+
}, null, 2),
|
|
879
|
+
},
|
|
880
|
+
],
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
catch (error) {
|
|
884
|
+
const commentId = args && typeof args === 'object' && 'comment_id' in args ? args.comment_id : 'unknown';
|
|
885
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
886
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
887
|
+
return {
|
|
888
|
+
content: [
|
|
889
|
+
{
|
|
890
|
+
type: "text",
|
|
891
|
+
text: JSON.stringify({
|
|
892
|
+
error: error.message,
|
|
893
|
+
status: error.status,
|
|
894
|
+
detail: `Failed to update comment #${commentId} in ${owner}/${repo}`,
|
|
895
|
+
}, null, 2),
|
|
896
|
+
},
|
|
897
|
+
],
|
|
898
|
+
isError: true,
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
if (name === "github_delete_comment") {
|
|
903
|
+
try {
|
|
904
|
+
const { owner, repo, comment_id } = DeleteCommentSchema.parse(args);
|
|
905
|
+
await octokit.rest.issues.deleteComment({
|
|
906
|
+
owner,
|
|
907
|
+
repo,
|
|
908
|
+
comment_id,
|
|
909
|
+
});
|
|
910
|
+
return {
|
|
911
|
+
content: [
|
|
912
|
+
{
|
|
913
|
+
type: "text",
|
|
914
|
+
text: JSON.stringify({
|
|
915
|
+
success: true,
|
|
916
|
+
message: `Comment #${comment_id} deleted successfully`,
|
|
917
|
+
}, null, 2),
|
|
918
|
+
},
|
|
919
|
+
],
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
catch (error) {
|
|
923
|
+
const commentId = args && typeof args === 'object' && 'comment_id' in args ? args.comment_id : 'unknown';
|
|
924
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
925
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
926
|
+
return {
|
|
927
|
+
content: [
|
|
928
|
+
{
|
|
929
|
+
type: "text",
|
|
930
|
+
text: JSON.stringify({
|
|
931
|
+
error: error.message,
|
|
932
|
+
status: error.status,
|
|
933
|
+
detail: `Failed to delete comment #${commentId} in ${owner}/${repo}`,
|
|
934
|
+
}, null, 2),
|
|
935
|
+
},
|
|
936
|
+
],
|
|
937
|
+
isError: true,
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (name === "github_add_reaction") {
|
|
942
|
+
try {
|
|
943
|
+
const { owner, repo, comment_id, issue_number, reaction } = AddReactionSchema.parse(args);
|
|
944
|
+
let reactionResponse;
|
|
945
|
+
let target = "";
|
|
946
|
+
const githubReaction = REACTION_MAP[reaction] || reaction;
|
|
947
|
+
if (comment_id) {
|
|
948
|
+
// Add reaction to a comment
|
|
949
|
+
reactionResponse = await octokit.rest.reactions.createForIssueComment({
|
|
950
|
+
owner,
|
|
951
|
+
repo,
|
|
952
|
+
comment_id,
|
|
953
|
+
content: githubReaction,
|
|
954
|
+
});
|
|
955
|
+
target = `comment #${comment_id}`;
|
|
956
|
+
}
|
|
957
|
+
else if (issue_number) {
|
|
958
|
+
// Add reaction to an issue/PR
|
|
959
|
+
reactionResponse = await octokit.rest.reactions.createForIssue({
|
|
960
|
+
owner,
|
|
961
|
+
repo,
|
|
962
|
+
issue_number,
|
|
963
|
+
content: githubReaction,
|
|
964
|
+
});
|
|
965
|
+
target = `issue/PR #${issue_number}`;
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
content: [
|
|
969
|
+
{
|
|
970
|
+
type: "text",
|
|
971
|
+
text: JSON.stringify({
|
|
972
|
+
success: true,
|
|
973
|
+
reaction: {
|
|
974
|
+
id: reactionResponse.data.id,
|
|
975
|
+
content: reactionResponse.data.content,
|
|
976
|
+
user: reactionResponse.data.user?.login,
|
|
977
|
+
created_at: reactionResponse.data.created_at,
|
|
978
|
+
},
|
|
979
|
+
message: `Reaction "${reaction}" added successfully to ${target}`,
|
|
980
|
+
}, null, 2),
|
|
981
|
+
},
|
|
982
|
+
],
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
catch (error) {
|
|
986
|
+
const commentId = args && typeof args === 'object' && 'comment_id' in args ? args.comment_id : undefined;
|
|
987
|
+
const issueNum = args && typeof args === 'object' && 'issue_number' in args ? args.issue_number : undefined;
|
|
988
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
989
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
990
|
+
const reaction = args && typeof args === 'object' && 'reaction' in args ? args.reaction : 'unknown';
|
|
991
|
+
const target = commentId ? `comment #${commentId}` : issueNum ? `issue/PR #${issueNum}` : 'unknown';
|
|
992
|
+
return {
|
|
993
|
+
content: [
|
|
994
|
+
{
|
|
995
|
+
type: "text",
|
|
996
|
+
text: JSON.stringify({
|
|
997
|
+
error: error.message,
|
|
998
|
+
status: error.status,
|
|
999
|
+
detail: `Failed to add reaction "${reaction}" to ${target} in ${owner}/${repo}`,
|
|
1000
|
+
}, null, 2),
|
|
1001
|
+
},
|
|
1002
|
+
],
|
|
1003
|
+
isError: true,
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (name === "github_search_issues") {
|
|
1008
|
+
try {
|
|
1009
|
+
const { owner, repo, query, state, labels, sort, direction, per_page } = SearchIssuesSchema.parse(args);
|
|
1010
|
+
const issues = await octokit.rest.issues.listForRepo({
|
|
1011
|
+
owner,
|
|
1012
|
+
repo,
|
|
1013
|
+
state: state || "open",
|
|
1014
|
+
labels: labels?.join(","),
|
|
1015
|
+
sort: sort,
|
|
1016
|
+
direction: direction,
|
|
1017
|
+
per_page: per_page || 30,
|
|
1018
|
+
});
|
|
1019
|
+
// Filter out pull requests first
|
|
1020
|
+
const issuesOnly = issues.data.filter(issue => !issue.pull_request);
|
|
1021
|
+
const filteredIssues = query
|
|
1022
|
+
? issues.data.filter(issue => !issue.pull_request).filter(issue => issue.title.toLowerCase().includes(query.toLowerCase()) ||
|
|
1023
|
+
issue.body?.toLowerCase().includes(query.toLowerCase()))
|
|
1024
|
+
: issues.data.filter(issue => !issue.pull_request);
|
|
1025
|
+
return {
|
|
1026
|
+
content: [
|
|
1027
|
+
{
|
|
1028
|
+
type: "text",
|
|
1029
|
+
text: JSON.stringify({
|
|
1030
|
+
total_count: filteredIssues.length,
|
|
1031
|
+
issues: filteredIssues.map(issue => ({
|
|
1032
|
+
number: issue.number,
|
|
1033
|
+
title: issue.title,
|
|
1034
|
+
state: issue.state,
|
|
1035
|
+
user: issue.user?.login,
|
|
1036
|
+
labels: issue.labels.map((l) => typeof l === "string" ? l : l.name),
|
|
1037
|
+
created_at: issue.created_at,
|
|
1038
|
+
updated_at: issue.updated_at,
|
|
1039
|
+
comments: issue.comments,
|
|
1040
|
+
html_url: issue.html_url,
|
|
1041
|
+
})),
|
|
1042
|
+
}, null, 2),
|
|
1043
|
+
},
|
|
1044
|
+
],
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
catch (error) {
|
|
1048
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1049
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1050
|
+
return {
|
|
1051
|
+
content: [
|
|
1052
|
+
{
|
|
1053
|
+
type: "text",
|
|
1054
|
+
text: JSON.stringify({
|
|
1055
|
+
error: error.message,
|
|
1056
|
+
status: error.status,
|
|
1057
|
+
detail: `Failed to search issues in ${owner}/${repo}`,
|
|
1058
|
+
}, null, 2),
|
|
1059
|
+
},
|
|
1060
|
+
],
|
|
1061
|
+
isError: true,
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
if (name === "github_search_prs") {
|
|
1066
|
+
try {
|
|
1067
|
+
const { owner, repo, query, state, sort, direction, per_page } = SearchPRsSchema.parse(args);
|
|
1068
|
+
const prs = await octokit.rest.pulls.list({
|
|
1069
|
+
owner,
|
|
1070
|
+
repo,
|
|
1071
|
+
state: state || "open",
|
|
1072
|
+
sort: sort,
|
|
1073
|
+
direction: direction,
|
|
1074
|
+
per_page: per_page || 30,
|
|
1075
|
+
});
|
|
1076
|
+
const filteredPRs = query
|
|
1077
|
+
? prs.data.filter(pr => pr.title.toLowerCase().includes(query.toLowerCase()) ||
|
|
1078
|
+
pr.body?.toLowerCase().includes(query.toLowerCase()))
|
|
1079
|
+
: prs.data;
|
|
1080
|
+
return {
|
|
1081
|
+
content: [
|
|
1082
|
+
{
|
|
1083
|
+
type: "text",
|
|
1084
|
+
text: JSON.stringify({
|
|
1085
|
+
total_count: filteredPRs.length,
|
|
1086
|
+
pull_requests: filteredPRs.map(pr => ({
|
|
1087
|
+
number: pr.number,
|
|
1088
|
+
title: pr.title,
|
|
1089
|
+
state: pr.state,
|
|
1090
|
+
user: pr.user?.login,
|
|
1091
|
+
head: pr.head.ref,
|
|
1092
|
+
base: pr.base.ref,
|
|
1093
|
+
created_at: pr.created_at,
|
|
1094
|
+
updated_at: pr.updated_at,
|
|
1095
|
+
draft: pr.draft,
|
|
1096
|
+
html_url: pr.html_url,
|
|
1097
|
+
})),
|
|
1098
|
+
}, null, 2),
|
|
1099
|
+
},
|
|
1100
|
+
],
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
catch (error) {
|
|
1104
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1105
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1106
|
+
return {
|
|
1107
|
+
content: [
|
|
1108
|
+
{
|
|
1109
|
+
type: "text",
|
|
1110
|
+
text: JSON.stringify({
|
|
1111
|
+
error: error.message,
|
|
1112
|
+
status: error.status,
|
|
1113
|
+
detail: `Failed to search PRs in ${owner}/${repo}`,
|
|
1114
|
+
}, null, 2),
|
|
1115
|
+
},
|
|
1116
|
+
],
|
|
1117
|
+
isError: true,
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
if (name === "github_list_recent_issues") {
|
|
1122
|
+
try {
|
|
1123
|
+
const { owner, repo, state, sort, per_page } = ListRecentIssuesSchema.parse(args);
|
|
1124
|
+
const issues = await octokit.rest.issues.listForRepo({
|
|
1125
|
+
owner,
|
|
1126
|
+
repo,
|
|
1127
|
+
state: state || "open",
|
|
1128
|
+
sort: sort || "created",
|
|
1129
|
+
direction: "desc",
|
|
1130
|
+
per_page: per_page || 30,
|
|
1131
|
+
});
|
|
1132
|
+
// Filter out pull requests (GitHub API returns both issues and PRs)
|
|
1133
|
+
const actualIssues = issues.data.filter(issue => !issue.pull_request);
|
|
1134
|
+
return {
|
|
1135
|
+
content: [
|
|
1136
|
+
{
|
|
1137
|
+
type: "text",
|
|
1138
|
+
text: JSON.stringify({
|
|
1139
|
+
count: actualIssues.length,
|
|
1140
|
+
issues: actualIssues.map(issue => ({
|
|
1141
|
+
number: issue.number,
|
|
1142
|
+
title: issue.title,
|
|
1143
|
+
state: issue.state,
|
|
1144
|
+
user: issue.user?.login,
|
|
1145
|
+
labels: issue.labels.map((l) => typeof l === "string" ? l : l.name),
|
|
1146
|
+
created_at: issue.created_at,
|
|
1147
|
+
updated_at: issue.updated_at,
|
|
1148
|
+
comments: issue.comments,
|
|
1149
|
+
html_url: issue.html_url,
|
|
1150
|
+
})),
|
|
1151
|
+
}, null, 2),
|
|
1152
|
+
},
|
|
1153
|
+
],
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
catch (error) {
|
|
1157
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1158
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1159
|
+
return {
|
|
1160
|
+
content: [
|
|
1161
|
+
{
|
|
1162
|
+
type: "text",
|
|
1163
|
+
text: JSON.stringify({
|
|
1164
|
+
error: error.message,
|
|
1165
|
+
status: error.status,
|
|
1166
|
+
detail: `Failed to list recent issues in ${owner}/${repo}`,
|
|
1167
|
+
}, null, 2),
|
|
1168
|
+
},
|
|
1169
|
+
],
|
|
1170
|
+
isError: true,
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
if (name === "github_merge_pr") {
|
|
1175
|
+
try {
|
|
1176
|
+
const { owner, repo, pull_number, merge_method, commit_title, commit_message } = MergePRSchema.parse(args);
|
|
1177
|
+
const result = await octokit.rest.pulls.merge({
|
|
1178
|
+
owner,
|
|
1179
|
+
repo,
|
|
1180
|
+
pull_number,
|
|
1181
|
+
merge_method: merge_method,
|
|
1182
|
+
commit_title,
|
|
1183
|
+
commit_message,
|
|
1184
|
+
});
|
|
1185
|
+
return {
|
|
1186
|
+
content: [
|
|
1187
|
+
{
|
|
1188
|
+
type: "text",
|
|
1189
|
+
text: JSON.stringify({
|
|
1190
|
+
success: true,
|
|
1191
|
+
merged: result.data.merged,
|
|
1192
|
+
sha: result.data.sha,
|
|
1193
|
+
message: result.data.message,
|
|
1194
|
+
}, null, 2),
|
|
1195
|
+
},
|
|
1196
|
+
],
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
catch (error) {
|
|
1200
|
+
const pullNum = args && typeof args === 'object' && 'pull_number' in args ? args.pull_number : 'unknown';
|
|
1201
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1202
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1203
|
+
return {
|
|
1204
|
+
content: [
|
|
1205
|
+
{
|
|
1206
|
+
type: "text",
|
|
1207
|
+
text: JSON.stringify({
|
|
1208
|
+
error: error.message,
|
|
1209
|
+
status: error.status,
|
|
1210
|
+
detail: `Failed to merge PR #${pullNum} in ${owner}/${repo}`,
|
|
1211
|
+
}, null, 2),
|
|
1212
|
+
},
|
|
1213
|
+
],
|
|
1214
|
+
isError: true,
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (name === "github_get_pr_diff") {
|
|
1219
|
+
try {
|
|
1220
|
+
const { owner, repo, pull_number } = GetPRDiffSchema.parse(args);
|
|
1221
|
+
const diff = await octokit.rest.pulls.get({
|
|
1222
|
+
owner,
|
|
1223
|
+
repo,
|
|
1224
|
+
pull_number,
|
|
1225
|
+
mediaType: {
|
|
1226
|
+
format: "diff",
|
|
1227
|
+
},
|
|
1228
|
+
});
|
|
1229
|
+
return {
|
|
1230
|
+
content: [
|
|
1231
|
+
{
|
|
1232
|
+
type: "text",
|
|
1233
|
+
text: JSON.stringify({
|
|
1234
|
+
pull_number,
|
|
1235
|
+
diff: diff.data,
|
|
1236
|
+
}, null, 2),
|
|
1237
|
+
},
|
|
1238
|
+
],
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
catch (error) {
|
|
1242
|
+
const pullNum = args && typeof args === 'object' && 'pull_number' in args ? args.pull_number : 'unknown';
|
|
1243
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1244
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1245
|
+
return {
|
|
1246
|
+
content: [
|
|
1247
|
+
{
|
|
1248
|
+
type: "text",
|
|
1249
|
+
text: JSON.stringify({
|
|
1250
|
+
error: error.message,
|
|
1251
|
+
status: error.status,
|
|
1252
|
+
detail: `Failed to get diff for PR #${pullNum} in ${owner}/${repo}`,
|
|
1253
|
+
}, null, 2),
|
|
1254
|
+
},
|
|
1255
|
+
],
|
|
1256
|
+
isError: true,
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
if (name === "github_get_pr_files") {
|
|
1261
|
+
try {
|
|
1262
|
+
const { owner, repo, pull_number } = GetPRFilesSchema.parse(args);
|
|
1263
|
+
const files = await octokit.rest.pulls.listFiles({
|
|
1264
|
+
owner,
|
|
1265
|
+
repo,
|
|
1266
|
+
pull_number,
|
|
1267
|
+
});
|
|
1268
|
+
return {
|
|
1269
|
+
content: [
|
|
1270
|
+
{
|
|
1271
|
+
type: "text",
|
|
1272
|
+
text: JSON.stringify({
|
|
1273
|
+
pull_number,
|
|
1274
|
+
total_files: files.data.length,
|
|
1275
|
+
files: files.data.map(file => ({
|
|
1276
|
+
filename: file.filename,
|
|
1277
|
+
status: file.status,
|
|
1278
|
+
additions: file.additions,
|
|
1279
|
+
deletions: file.deletions,
|
|
1280
|
+
changes: file.changes,
|
|
1281
|
+
blob_url: file.blob_url,
|
|
1282
|
+
raw_url: file.raw_url,
|
|
1283
|
+
patch: file.patch,
|
|
1284
|
+
})),
|
|
1285
|
+
}, null, 2),
|
|
1286
|
+
},
|
|
1287
|
+
],
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
catch (error) {
|
|
1291
|
+
const pullNum = args && typeof args === 'object' && 'pull_number' in args ? args.pull_number : 'unknown';
|
|
1292
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1293
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1294
|
+
return {
|
|
1295
|
+
content: [
|
|
1296
|
+
{
|
|
1297
|
+
type: "text",
|
|
1298
|
+
text: JSON.stringify({
|
|
1299
|
+
error: error.message,
|
|
1300
|
+
status: error.status,
|
|
1301
|
+
detail: `Failed to get files for PR #${pullNum} in ${owner}/${repo}`,
|
|
1302
|
+
}, null, 2),
|
|
1303
|
+
},
|
|
1304
|
+
],
|
|
1305
|
+
isError: true,
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
if (name === "github_create_label") {
|
|
1310
|
+
try {
|
|
1311
|
+
const { owner, repo, name: labelName, color, description } = CreateLabelSchema.parse(args);
|
|
1312
|
+
const label = await octokit.rest.issues.createLabel({
|
|
1313
|
+
owner,
|
|
1314
|
+
repo,
|
|
1315
|
+
name: labelName,
|
|
1316
|
+
color,
|
|
1317
|
+
description,
|
|
1318
|
+
});
|
|
1319
|
+
return {
|
|
1320
|
+
content: [
|
|
1321
|
+
{
|
|
1322
|
+
type: "text",
|
|
1323
|
+
text: JSON.stringify({
|
|
1324
|
+
success: true,
|
|
1325
|
+
label: {
|
|
1326
|
+
name: label.data.name,
|
|
1327
|
+
color: label.data.color,
|
|
1328
|
+
description: label.data.description,
|
|
1329
|
+
url: label.data.url,
|
|
1330
|
+
},
|
|
1331
|
+
message: `Label "${labelName}" created successfully`,
|
|
1332
|
+
}, null, 2),
|
|
1333
|
+
},
|
|
1334
|
+
],
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
catch (error) {
|
|
1338
|
+
const labelName = args && typeof args === 'object' && 'name' in args ? args.name : 'unknown';
|
|
1339
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1340
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1341
|
+
return {
|
|
1342
|
+
content: [
|
|
1343
|
+
{
|
|
1344
|
+
type: "text",
|
|
1345
|
+
text: JSON.stringify({
|
|
1346
|
+
error: error.message,
|
|
1347
|
+
status: error.status,
|
|
1348
|
+
detail: `Failed to create label "${labelName}" in ${owner}/${repo}`,
|
|
1349
|
+
}, null, 2),
|
|
1350
|
+
},
|
|
1351
|
+
],
|
|
1352
|
+
isError: true,
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
if (name === "github_update_label") {
|
|
1357
|
+
try {
|
|
1358
|
+
const { owner, repo, name: currentName, new_name, color, description } = UpdateLabelSchema.parse(args);
|
|
1359
|
+
const label = await octokit.rest.issues.updateLabel({
|
|
1360
|
+
owner,
|
|
1361
|
+
repo,
|
|
1362
|
+
name: currentName,
|
|
1363
|
+
new_name,
|
|
1364
|
+
color,
|
|
1365
|
+
description,
|
|
1366
|
+
});
|
|
1367
|
+
return {
|
|
1368
|
+
content: [
|
|
1369
|
+
{
|
|
1370
|
+
type: "text",
|
|
1371
|
+
text: JSON.stringify({
|
|
1372
|
+
success: true,
|
|
1373
|
+
label: {
|
|
1374
|
+
name: label.data.name,
|
|
1375
|
+
color: label.data.color,
|
|
1376
|
+
description: label.data.description,
|
|
1377
|
+
url: label.data.url,
|
|
1378
|
+
},
|
|
1379
|
+
message: `Label "${currentName}" updated successfully`,
|
|
1380
|
+
}, null, 2),
|
|
1381
|
+
},
|
|
1382
|
+
],
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
catch (error) {
|
|
1386
|
+
const labelName = args && typeof args === 'object' && 'name' in args ? args.name : 'unknown';
|
|
1387
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1388
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1389
|
+
return {
|
|
1390
|
+
content: [
|
|
1391
|
+
{
|
|
1392
|
+
type: "text",
|
|
1393
|
+
text: JSON.stringify({
|
|
1394
|
+
error: error.message,
|
|
1395
|
+
status: error.status,
|
|
1396
|
+
detail: `Failed to update label "${labelName}" in ${owner}/${repo}`,
|
|
1397
|
+
}, null, 2),
|
|
1398
|
+
},
|
|
1399
|
+
],
|
|
1400
|
+
isError: true,
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
if (name === "github_delete_label") {
|
|
1405
|
+
try {
|
|
1406
|
+
const { owner, repo, name: labelName } = DeleteLabelSchema.parse(args);
|
|
1407
|
+
await octokit.rest.issues.deleteLabel({
|
|
1408
|
+
owner,
|
|
1409
|
+
repo,
|
|
1410
|
+
name: labelName,
|
|
1411
|
+
});
|
|
1412
|
+
return {
|
|
1413
|
+
content: [
|
|
1414
|
+
{
|
|
1415
|
+
type: "text",
|
|
1416
|
+
text: JSON.stringify({
|
|
1417
|
+
success: true,
|
|
1418
|
+
message: `Label "${labelName}" deleted successfully from ${owner}/${repo}`,
|
|
1419
|
+
}, null, 2),
|
|
1420
|
+
},
|
|
1421
|
+
],
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
catch (error) {
|
|
1425
|
+
const labelName = args && typeof args === 'object' && 'name' in args ? args.name : 'unknown';
|
|
1426
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1427
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1428
|
+
return {
|
|
1429
|
+
content: [
|
|
1430
|
+
{
|
|
1431
|
+
type: "text",
|
|
1432
|
+
text: JSON.stringify({
|
|
1433
|
+
error: error.message,
|
|
1434
|
+
status: error.status,
|
|
1435
|
+
detail: `Failed to delete label "${labelName}" from ${owner}/${repo}`,
|
|
1436
|
+
}, null, 2),
|
|
1437
|
+
},
|
|
1438
|
+
],
|
|
1439
|
+
isError: true,
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
if (name === "github_list_labels") {
|
|
1444
|
+
try {
|
|
1445
|
+
const { owner, repo, per_page } = ListLabelsSchema.parse(args);
|
|
1446
|
+
const labels = await octokit.rest.issues.listLabelsForRepo({
|
|
1447
|
+
owner,
|
|
1448
|
+
repo,
|
|
1449
|
+
per_page,
|
|
1450
|
+
});
|
|
1451
|
+
return {
|
|
1452
|
+
content: [
|
|
1453
|
+
{
|
|
1454
|
+
type: "text",
|
|
1455
|
+
text: JSON.stringify({
|
|
1456
|
+
success: true,
|
|
1457
|
+
count: labels.data.length,
|
|
1458
|
+
labels: labels.data.map((label) => ({
|
|
1459
|
+
name: label.name,
|
|
1460
|
+
color: label.color,
|
|
1461
|
+
description: label.description,
|
|
1462
|
+
url: label.url,
|
|
1463
|
+
})),
|
|
1464
|
+
}, null, 2),
|
|
1465
|
+
},
|
|
1466
|
+
],
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
catch (error) {
|
|
1470
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1471
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1472
|
+
return {
|
|
1473
|
+
content: [
|
|
1474
|
+
{
|
|
1475
|
+
type: "text",
|
|
1476
|
+
text: JSON.stringify({
|
|
1477
|
+
error: error.message,
|
|
1478
|
+
status: error.status,
|
|
1479
|
+
detail: `Failed to list labels for ${owner}/${repo}`,
|
|
1480
|
+
}, null, 2),
|
|
1481
|
+
},
|
|
1482
|
+
],
|
|
1483
|
+
isError: true,
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
if (name === "github_list_branches") {
|
|
1488
|
+
try {
|
|
1489
|
+
const { owner, repo, protected: isProtected, per_page } = ListBranchesSchema.parse(args);
|
|
1490
|
+
const branches = await octokit.rest.repos.listBranches({
|
|
1491
|
+
owner,
|
|
1492
|
+
repo,
|
|
1493
|
+
protected: isProtected,
|
|
1494
|
+
per_page,
|
|
1495
|
+
});
|
|
1496
|
+
return {
|
|
1497
|
+
content: [
|
|
1498
|
+
{
|
|
1499
|
+
type: "text",
|
|
1500
|
+
text: JSON.stringify({
|
|
1501
|
+
success: true,
|
|
1502
|
+
count: branches.data.length,
|
|
1503
|
+
branches: branches.data.map((branch) => ({
|
|
1504
|
+
name: branch.name,
|
|
1505
|
+
commit: {
|
|
1506
|
+
sha: branch.commit.sha,
|
|
1507
|
+
url: branch.commit.url,
|
|
1508
|
+
},
|
|
1509
|
+
protected: branch.protected,
|
|
1510
|
+
})),
|
|
1511
|
+
}, null, 2),
|
|
1512
|
+
},
|
|
1513
|
+
],
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
catch (error) {
|
|
1517
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1518
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1519
|
+
return {
|
|
1520
|
+
content: [
|
|
1521
|
+
{
|
|
1522
|
+
type: "text",
|
|
1523
|
+
text: JSON.stringify({
|
|
1524
|
+
error: error.message,
|
|
1525
|
+
status: error.status,
|
|
1526
|
+
detail: `Failed to list branches for ${owner}/${repo}`,
|
|
1527
|
+
}, null, 2),
|
|
1528
|
+
},
|
|
1529
|
+
],
|
|
1530
|
+
isError: true,
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
if (name === "github_create_branch") {
|
|
1535
|
+
try {
|
|
1536
|
+
const { owner, repo, branch, ref } = CreateBranchSchema.parse(args);
|
|
1537
|
+
// Get the SHA of the source ref
|
|
1538
|
+
let sha;
|
|
1539
|
+
try {
|
|
1540
|
+
// Try to get ref as a branch first
|
|
1541
|
+
const refData = await octokit.rest.git.getRef({
|
|
1542
|
+
owner,
|
|
1543
|
+
repo,
|
|
1544
|
+
ref: `heads/${ref}`,
|
|
1545
|
+
});
|
|
1546
|
+
sha = refData.data.object.sha;
|
|
1547
|
+
}
|
|
1548
|
+
catch {
|
|
1549
|
+
// If not a branch, try as a commit SHA
|
|
1550
|
+
const commit = await octokit.rest.git.getCommit({
|
|
1551
|
+
owner,
|
|
1552
|
+
repo,
|
|
1553
|
+
commit_sha: ref,
|
|
1554
|
+
});
|
|
1555
|
+
sha = commit.data.sha;
|
|
1556
|
+
}
|
|
1557
|
+
// Create the new branch
|
|
1558
|
+
const newBranch = await octokit.rest.git.createRef({
|
|
1559
|
+
owner,
|
|
1560
|
+
repo,
|
|
1561
|
+
ref: `refs/heads/${branch}`,
|
|
1562
|
+
sha,
|
|
1563
|
+
});
|
|
1564
|
+
return {
|
|
1565
|
+
content: [
|
|
1566
|
+
{
|
|
1567
|
+
type: "text",
|
|
1568
|
+
text: JSON.stringify({
|
|
1569
|
+
success: true,
|
|
1570
|
+
branch: {
|
|
1571
|
+
name: branch,
|
|
1572
|
+
ref: newBranch.data.ref,
|
|
1573
|
+
sha: newBranch.data.object.sha,
|
|
1574
|
+
url: newBranch.data.url,
|
|
1575
|
+
},
|
|
1576
|
+
message: `Branch "${branch}" created successfully from "${ref}"`,
|
|
1577
|
+
}, null, 2),
|
|
1578
|
+
},
|
|
1579
|
+
],
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
catch (error) {
|
|
1583
|
+
const branchName = args && typeof args === 'object' && 'branch' in args ? args.branch : 'unknown';
|
|
1584
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1585
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1586
|
+
return {
|
|
1587
|
+
content: [
|
|
1588
|
+
{
|
|
1589
|
+
type: "text",
|
|
1590
|
+
text: JSON.stringify({
|
|
1591
|
+
error: error.message,
|
|
1592
|
+
status: error.status,
|
|
1593
|
+
detail: `Failed to create branch "${branchName}" in ${owner}/${repo}`,
|
|
1594
|
+
}, null, 2),
|
|
1595
|
+
},
|
|
1596
|
+
],
|
|
1597
|
+
isError: true,
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
if (name === "github_delete_branch") {
|
|
1602
|
+
try {
|
|
1603
|
+
const { owner, repo, branch } = DeleteBranchSchema.parse(args);
|
|
1604
|
+
await octokit.rest.git.deleteRef({
|
|
1605
|
+
owner,
|
|
1606
|
+
repo,
|
|
1607
|
+
ref: `heads/${branch}`,
|
|
1608
|
+
});
|
|
1609
|
+
return {
|
|
1610
|
+
content: [
|
|
1611
|
+
{
|
|
1612
|
+
type: "text",
|
|
1613
|
+
text: JSON.stringify({
|
|
1614
|
+
success: true,
|
|
1615
|
+
message: `Branch "${branch}" deleted successfully from ${owner}/${repo}`,
|
|
1616
|
+
}, null, 2),
|
|
1617
|
+
},
|
|
1618
|
+
],
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
catch (error) {
|
|
1622
|
+
const branchName = args && typeof args === 'object' && 'branch' in args ? args.branch : 'unknown';
|
|
1623
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1624
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1625
|
+
return {
|
|
1626
|
+
content: [
|
|
1627
|
+
{
|
|
1628
|
+
type: "text",
|
|
1629
|
+
text: JSON.stringify({
|
|
1630
|
+
error: error.message,
|
|
1631
|
+
status: error.status,
|
|
1632
|
+
detail: `Failed to delete branch "${branchName}" from ${owner}/${repo}`,
|
|
1633
|
+
}, null, 2),
|
|
1634
|
+
},
|
|
1635
|
+
],
|
|
1636
|
+
isError: true,
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
if (name === "github_compare_branches") {
|
|
1641
|
+
try {
|
|
1642
|
+
const { owner, repo, base, head } = CompareBranchesSchema.parse(args);
|
|
1643
|
+
const comparison = await octokit.rest.repos.compareCommits({
|
|
1644
|
+
owner,
|
|
1645
|
+
repo,
|
|
1646
|
+
base,
|
|
1647
|
+
head,
|
|
1648
|
+
});
|
|
1649
|
+
return {
|
|
1650
|
+
content: [
|
|
1651
|
+
{
|
|
1652
|
+
type: "text",
|
|
1653
|
+
text: JSON.stringify({
|
|
1654
|
+
success: true,
|
|
1655
|
+
comparison: {
|
|
1656
|
+
status: comparison.data.status,
|
|
1657
|
+
ahead_by: comparison.data.ahead_by,
|
|
1658
|
+
behind_by: comparison.data.behind_by,
|
|
1659
|
+
total_commits: comparison.data.total_commits,
|
|
1660
|
+
base_commit: {
|
|
1661
|
+
sha: comparison.data.base_commit.sha,
|
|
1662
|
+
message: comparison.data.base_commit.commit.message,
|
|
1663
|
+
},
|
|
1664
|
+
commits: comparison.data.commits.map((commit) => ({
|
|
1665
|
+
sha: commit.sha,
|
|
1666
|
+
message: commit.commit.message,
|
|
1667
|
+
author: commit.commit.author?.name,
|
|
1668
|
+
date: commit.commit.author?.date,
|
|
1669
|
+
})),
|
|
1670
|
+
files: comparison.data.files?.map((file) => ({
|
|
1671
|
+
filename: file.filename,
|
|
1672
|
+
status: file.status,
|
|
1673
|
+
additions: file.additions,
|
|
1674
|
+
deletions: file.deletions,
|
|
1675
|
+
changes: file.changes,
|
|
1676
|
+
})),
|
|
1677
|
+
},
|
|
1678
|
+
message: `Comparing ${base}...${head}: ${comparison.data.ahead_by} commits ahead, ${comparison.data.behind_by} commits behind`,
|
|
1679
|
+
}, null, 2),
|
|
1680
|
+
},
|
|
1681
|
+
],
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
catch (error) {
|
|
1685
|
+
const base = args && typeof args === 'object' && 'base' in args ? args.base : 'unknown';
|
|
1686
|
+
const head = args && typeof args === 'object' && 'head' in args ? args.head : 'unknown';
|
|
1687
|
+
const owner = args && typeof args === 'object' && 'owner' in args ? args.owner : 'unknown';
|
|
1688
|
+
const repo = args && typeof args === 'object' && 'repo' in args ? args.repo : 'unknown';
|
|
1689
|
+
return {
|
|
1690
|
+
content: [
|
|
1691
|
+
{
|
|
1692
|
+
type: "text",
|
|
1693
|
+
text: JSON.stringify({
|
|
1694
|
+
error: error.message,
|
|
1695
|
+
status: error.status,
|
|
1696
|
+
detail: `Failed to compare branches ${base}...${head} in ${owner}/${repo}`,
|
|
1697
|
+
}, null, 2),
|
|
1698
|
+
},
|
|
1699
|
+
],
|
|
1700
|
+
isError: true,
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
411
1704
|
throw new Error(`Unknown tool: ${name}`);
|
|
412
1705
|
});
|
|
413
1706
|
async function main() {
|