@rbm897/github-mcp 0.7.1

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 ADDED
@@ -0,0 +1,1610 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { Octokit } from '@octokit/rest';
6
+ import * as dotenv from 'dotenv';
7
+ dotenv.config();
8
+ class GitHubMCPServer {
9
+ server;
10
+ octokit;
11
+ defaultOwner;
12
+ constructor() {
13
+ this.server = new Server({
14
+ name: 'github-mcp',
15
+ version: '0.2.0',
16
+ }, {
17
+ capabilities: {
18
+ tools: {},
19
+ },
20
+ });
21
+ const githubToken = process.env.GITHUB_TOKEN;
22
+ if (!githubToken) {
23
+ throw new Error('GITHUB_TOKEN environment variable is required');
24
+ }
25
+ this.defaultOwner = process.env.OWNER;
26
+ this.octokit = new Octokit({
27
+ auth: githubToken,
28
+ authStrategy: undefined,
29
+ });
30
+ this.setupToolHandlers();
31
+ }
32
+ resolveOwner(providedOwner) {
33
+ if (providedOwner) {
34
+ return providedOwner;
35
+ }
36
+ if (this.defaultOwner) {
37
+ return this.defaultOwner;
38
+ }
39
+ throw new Error('Owner parameter is required. Either provide owner in the request or set OWNER environment variable.');
40
+ }
41
+ setupToolHandlers() {
42
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
43
+ return {
44
+ tools: [
45
+ {
46
+ name: 'list_pull_requests',
47
+ description: 'List pull requests for a repository',
48
+ inputSchema: {
49
+ type: 'object',
50
+ properties: {
51
+ owner: {
52
+ type: 'string',
53
+ description: 'Repository owner (optional if OWNER env var is set)',
54
+ },
55
+ repo: {
56
+ type: 'string',
57
+ description: 'Repository name',
58
+ },
59
+ state: {
60
+ type: 'string',
61
+ enum: ['open', 'closed', 'all'],
62
+ description: 'PR state filter',
63
+ default: 'open',
64
+ },
65
+ per_page: {
66
+ type: 'number',
67
+ description: 'Number of results per page (max 100)',
68
+ default: 30,
69
+ },
70
+ },
71
+ required: ['repo'],
72
+ },
73
+ },
74
+ {
75
+ name: 'get_pull_request',
76
+ description: 'Get detailed information about a pull request',
77
+ inputSchema: {
78
+ type: 'object',
79
+ properties: {
80
+ owner: {
81
+ type: 'string',
82
+ description: 'Repository owner (optional if OWNER env var is set)',
83
+ },
84
+ repo: {
85
+ type: 'string',
86
+ description: 'Repository name',
87
+ },
88
+ pull_number: {
89
+ type: 'number',
90
+ description: 'Pull request number',
91
+ },
92
+ },
93
+ required: ['repo', 'pull_number'],
94
+ },
95
+ },
96
+ {
97
+ name: 'get_pull_request_files',
98
+ description: 'Get the files changed in a pull request',
99
+ inputSchema: {
100
+ type: 'object',
101
+ properties: {
102
+ owner: {
103
+ type: 'string',
104
+ description: 'Repository owner (optional if OWNER env var is set)',
105
+ },
106
+ repo: {
107
+ type: 'string',
108
+ description: 'Repository name',
109
+ },
110
+ pull_number: {
111
+ type: 'number',
112
+ description: 'Pull request number',
113
+ },
114
+ },
115
+ required: ['repo', 'pull_number'],
116
+ },
117
+ },
118
+ {
119
+ name: 'list_pull_requests_for_review',
120
+ description: 'List pull requests that are assigned for review to the authenticated user',
121
+ inputSchema: {
122
+ type: 'object',
123
+ properties: {
124
+ state: {
125
+ type: 'string',
126
+ enum: ['open', 'closed', 'all'],
127
+ description: 'PR state filter',
128
+ default: 'open',
129
+ },
130
+ per_page: {
131
+ type: 'number',
132
+ description: 'Number of results per page (max 100)',
133
+ default: 30,
134
+ },
135
+ },
136
+ required: [],
137
+ },
138
+ },
139
+ {
140
+ name: 'create_pull_request',
141
+ description: 'Create a new pull request',
142
+ inputSchema: {
143
+ type: 'object',
144
+ properties: {
145
+ owner: {
146
+ type: 'string',
147
+ description: 'Repository owner (optional if OWNER env var is set)',
148
+ },
149
+ repo: {
150
+ type: 'string',
151
+ description: 'Repository name',
152
+ },
153
+ title: {
154
+ type: 'string',
155
+ description: 'Pull request title',
156
+ },
157
+ head: {
158
+ type: 'string',
159
+ description: 'Branch containing changes',
160
+ },
161
+ base: {
162
+ type: 'string',
163
+ description: 'Target branch',
164
+ },
165
+ body: {
166
+ type: 'string',
167
+ description: 'Pull request description',
168
+ },
169
+ draft: {
170
+ type: 'boolean',
171
+ description: 'Create as draft PR',
172
+ default: false,
173
+ },
174
+ },
175
+ required: ['repo', 'title', 'head', 'base'],
176
+ },
177
+ },
178
+ {
179
+ name: 'add_review_comment',
180
+ description: 'Add a review comment to a pull request',
181
+ inputSchema: {
182
+ type: 'object',
183
+ properties: {
184
+ owner: {
185
+ type: 'string',
186
+ description: 'Repository owner (optional if OWNER env var is set)',
187
+ },
188
+ repo: {
189
+ type: 'string',
190
+ description: 'Repository name',
191
+ },
192
+ pull_number: {
193
+ type: 'number',
194
+ description: 'Pull request number',
195
+ },
196
+ body: {
197
+ type: 'string',
198
+ description: 'Review comment body',
199
+ },
200
+ commit_id: {
201
+ type: 'string',
202
+ description: 'SHA of commit to comment on',
203
+ },
204
+ path: {
205
+ type: 'string',
206
+ description: 'File path for line comment',
207
+ },
208
+ line: {
209
+ type: 'number',
210
+ description: 'Line number for comment (1-based)',
211
+ },
212
+ },
213
+ required: ['repo', 'pull_number', 'body', 'commit_id', 'path', 'line'],
214
+ },
215
+ },
216
+ {
217
+ name: 'approve_pull_request',
218
+ description: 'Approve a pull request',
219
+ inputSchema: {
220
+ type: 'object',
221
+ properties: {
222
+ owner: {
223
+ type: 'string',
224
+ description: 'Repository owner (optional if OWNER env var is set)',
225
+ },
226
+ repo: {
227
+ type: 'string',
228
+ description: 'Repository name',
229
+ },
230
+ pull_number: {
231
+ type: 'number',
232
+ description: 'Pull request number',
233
+ },
234
+ body: {
235
+ type: 'string',
236
+ description: 'Optional review message',
237
+ },
238
+ },
239
+ required: ['repo', 'pull_number'],
240
+ },
241
+ },
242
+ {
243
+ name: 'create_issue',
244
+ description: 'Create a new issue in a repository',
245
+ inputSchema: {
246
+ type: 'object',
247
+ properties: {
248
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
249
+ repo: { type: 'string', description: 'Repository name' },
250
+ title: { type: 'string', description: 'Issue title' },
251
+ body: { type: 'string', description: 'Issue description' },
252
+ assignees: { type: 'array', items: { type: 'string' }, description: 'Usernames to assign' },
253
+ milestone: { type: 'number', description: 'Milestone number' },
254
+ labels: { type: 'array', items: { type: 'string' }, description: 'Label names' },
255
+ },
256
+ required: ['repo', 'title'],
257
+ },
258
+ },
259
+ {
260
+ name: 'list_issues',
261
+ description: 'List issues in a repository',
262
+ inputSchema: {
263
+ type: 'object',
264
+ properties: {
265
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
266
+ repo: { type: 'string', description: 'Repository name' },
267
+ state: { type: 'string', enum: ['open', 'closed', 'all'], description: 'Issue state', default: 'open' },
268
+ labels: { type: 'string', description: 'Comma-separated label names' },
269
+ assignee: { type: 'string', description: 'Username assigned to issues' },
270
+ milestone: { type: 'string', description: 'Milestone title or number' },
271
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
272
+ },
273
+ required: ['repo'],
274
+ },
275
+ },
276
+ {
277
+ name: 'get_issue',
278
+ description: 'Get details of a specific issue',
279
+ inputSchema: {
280
+ type: 'object',
281
+ properties: {
282
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
283
+ repo: { type: 'string', description: 'Repository name' },
284
+ issue_number: { type: 'number', description: 'Issue number' },
285
+ },
286
+ required: ['repo', 'issue_number'],
287
+ },
288
+ },
289
+ {
290
+ name: 'update_issue',
291
+ description: 'Update an existing issue',
292
+ inputSchema: {
293
+ type: 'object',
294
+ properties: {
295
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
296
+ repo: { type: 'string', description: 'Repository name' },
297
+ issue_number: { type: 'number', description: 'Issue number' },
298
+ title: { type: 'string', description: 'Issue title' },
299
+ body: { type: 'string', description: 'Issue description' },
300
+ state: { type: 'string', enum: ['open', 'closed'], description: 'Issue state' },
301
+ assignees: { type: 'array', items: { type: 'string' }, description: 'Usernames to assign' },
302
+ labels: { type: 'array', items: { type: 'string' }, description: 'Label names' },
303
+ },
304
+ required: ['repo', 'issue_number'],
305
+ },
306
+ },
307
+ {
308
+ name: 'close_issue',
309
+ description: 'Close an issue',
310
+ inputSchema: {
311
+ type: 'object',
312
+ properties: {
313
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
314
+ repo: { type: 'string', description: 'Repository name' },
315
+ issue_number: { type: 'number', description: 'Issue number' },
316
+ reason: { type: 'string', enum: ['completed', 'not_planned'], description: 'Reason for closing' },
317
+ },
318
+ required: ['repo', 'issue_number'],
319
+ },
320
+ },
321
+ {
322
+ name: 'add_issue_comment',
323
+ description: 'Add a comment to an issue',
324
+ inputSchema: {
325
+ type: 'object',
326
+ properties: {
327
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
328
+ repo: { type: 'string', description: 'Repository name' },
329
+ issue_number: { type: 'number', description: 'Issue number' },
330
+ body: { type: 'string', description: 'Comment text' },
331
+ },
332
+ required: ['repo', 'issue_number', 'body'],
333
+ },
334
+ },
335
+ {
336
+ name: 'get_repository',
337
+ description: 'Get repository information and statistics',
338
+ inputSchema: {
339
+ type: 'object',
340
+ properties: {
341
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
342
+ repo: { type: 'string', description: 'Repository name' },
343
+ },
344
+ required: ['repo'],
345
+ },
346
+ },
347
+ {
348
+ name: 'list_commits',
349
+ description: 'List commits in a repository',
350
+ inputSchema: {
351
+ type: 'object',
352
+ properties: {
353
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
354
+ repo: { type: 'string', description: 'Repository name' },
355
+ sha: { type: 'string', description: 'Branch, tag, or commit SHA' },
356
+ path: { type: 'string', description: 'File path to filter commits' },
357
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
358
+ },
359
+ required: ['repo'],
360
+ },
361
+ },
362
+ {
363
+ name: 'get_commit',
364
+ description: 'Get details of a specific commit',
365
+ inputSchema: {
366
+ type: 'object',
367
+ properties: {
368
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
369
+ repo: { type: 'string', description: 'Repository name' },
370
+ ref: { type: 'string', description: 'Commit SHA' },
371
+ },
372
+ required: ['repo', 'ref'],
373
+ },
374
+ },
375
+ {
376
+ name: 'list_branches',
377
+ description: 'List repository branches',
378
+ inputSchema: {
379
+ type: 'object',
380
+ properties: {
381
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
382
+ repo: { type: 'string', description: 'Repository name' },
383
+ protected: { type: 'boolean', description: 'Filter by protection status' },
384
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
385
+ },
386
+ required: ['repo'],
387
+ },
388
+ },
389
+ {
390
+ name: 'create_branch',
391
+ description: 'Create a new branch',
392
+ inputSchema: {
393
+ type: 'object',
394
+ properties: {
395
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
396
+ repo: { type: 'string', description: 'Repository name' },
397
+ ref: { type: 'string', description: 'New branch name (refs/heads/branch-name)' },
398
+ sha: { type: 'string', description: 'SHA to create branch from' },
399
+ },
400
+ required: ['repo', 'ref', 'sha'],
401
+ },
402
+ },
403
+ {
404
+ name: 'get_file_content',
405
+ description: 'Get file contents from repository',
406
+ inputSchema: {
407
+ type: 'object',
408
+ properties: {
409
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
410
+ repo: { type: 'string', description: 'Repository name' },
411
+ path: { type: 'string', description: 'File path' },
412
+ ref: { type: 'string', description: 'Branch, tag, or commit SHA', default: 'main' },
413
+ },
414
+ required: ['repo', 'path'],
415
+ },
416
+ },
417
+ {
418
+ name: 'update_file',
419
+ description: 'Create or update a file in repository',
420
+ inputSchema: {
421
+ type: 'object',
422
+ properties: {
423
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
424
+ repo: { type: 'string', description: 'Repository name' },
425
+ path: { type: 'string', description: 'File path' },
426
+ message: { type: 'string', description: 'Commit message' },
427
+ content: { type: 'string', description: 'File content (base64 encoded)' },
428
+ sha: { type: 'string', description: 'SHA of file being replaced (for updates)' },
429
+ branch: { type: 'string', description: 'Branch name', default: 'main' },
430
+ },
431
+ required: ['repo', 'path', 'message', 'content'],
432
+ },
433
+ },
434
+ {
435
+ name: 'request_review',
436
+ description: 'Request reviews for a pull request',
437
+ inputSchema: {
438
+ type: 'object',
439
+ properties: {
440
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
441
+ repo: { type: 'string', description: 'Repository name' },
442
+ pull_number: { type: 'number', description: 'Pull request number' },
443
+ reviewers: { type: 'array', items: { type: 'string' }, description: 'Usernames to request reviews from' },
444
+ team_reviewers: { type: 'array', items: { type: 'string' }, description: 'Team names to request reviews from' },
445
+ },
446
+ required: ['repo', 'pull_number'],
447
+ },
448
+ },
449
+ {
450
+ name: 'submit_review',
451
+ description: 'Submit a pull request review',
452
+ inputSchema: {
453
+ type: 'object',
454
+ properties: {
455
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
456
+ repo: { type: 'string', description: 'Repository name' },
457
+ pull_number: { type: 'number', description: 'Pull request number' },
458
+ event: { type: 'string', enum: ['APPROVE', 'REQUEST_CHANGES', 'COMMENT'], description: 'Review action' },
459
+ body: { type: 'string', description: 'Review summary comment' },
460
+ },
461
+ required: ['repo', 'pull_number', 'event'],
462
+ },
463
+ },
464
+ {
465
+ name: 'list_review_comments',
466
+ description: 'List review comments for a pull request',
467
+ inputSchema: {
468
+ type: 'object',
469
+ properties: {
470
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
471
+ repo: { type: 'string', description: 'Repository name' },
472
+ pull_number: { type: 'number', description: 'Pull request number' },
473
+ },
474
+ required: ['repo', 'pull_number'],
475
+ },
476
+ },
477
+ {
478
+ name: 'list_labels',
479
+ description: 'List repository labels',
480
+ inputSchema: {
481
+ type: 'object',
482
+ properties: {
483
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
484
+ repo: { type: 'string', description: 'Repository name' },
485
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
486
+ },
487
+ required: ['repo'],
488
+ },
489
+ },
490
+ {
491
+ name: 'create_label',
492
+ description: 'Create a new label',
493
+ inputSchema: {
494
+ type: 'object',
495
+ properties: {
496
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
497
+ repo: { type: 'string', description: 'Repository name' },
498
+ name: { type: 'string', description: 'Label name' },
499
+ color: { type: 'string', description: 'Hex color code without #' },
500
+ description: { type: 'string', description: 'Label description' },
501
+ },
502
+ required: ['repo', 'name', 'color'],
503
+ },
504
+ },
505
+ {
506
+ name: 'list_milestones',
507
+ description: 'List repository milestones',
508
+ inputSchema: {
509
+ type: 'object',
510
+ properties: {
511
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
512
+ repo: { type: 'string', description: 'Repository name' },
513
+ state: { type: 'string', enum: ['open', 'closed', 'all'], description: 'Milestone state', default: 'open' },
514
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
515
+ },
516
+ required: ['repo'],
517
+ },
518
+ },
519
+ {
520
+ name: 'create_milestone',
521
+ description: 'Create a new milestone',
522
+ inputSchema: {
523
+ type: 'object',
524
+ properties: {
525
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
526
+ repo: { type: 'string', description: 'Repository name' },
527
+ title: { type: 'string', description: 'Milestone title' },
528
+ description: { type: 'string', description: 'Milestone description' },
529
+ due_on: { type: 'string', description: 'Due date (ISO 8601 format)' },
530
+ },
531
+ required: ['repo', 'title'],
532
+ },
533
+ },
534
+ {
535
+ name: 'list_collaborators',
536
+ description: 'List repository collaborators',
537
+ inputSchema: {
538
+ type: 'object',
539
+ properties: {
540
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
541
+ repo: { type: 'string', description: 'Repository name' },
542
+ affiliation: { type: 'string', enum: ['outside', 'direct', 'all'], description: 'Filter by affiliation', default: 'all' },
543
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
544
+ },
545
+ required: ['repo'],
546
+ },
547
+ },
548
+ {
549
+ name: 'check_user_permissions',
550
+ description: 'Check user permissions for repository',
551
+ inputSchema: {
552
+ type: 'object',
553
+ properties: {
554
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
555
+ repo: { type: 'string', description: 'Repository name' },
556
+ username: { type: 'string', description: 'Username to check' },
557
+ },
558
+ required: ['repo', 'username'],
559
+ },
560
+ },
561
+ {
562
+ name: 'assign_issue',
563
+ description: 'Assign users to an issue',
564
+ inputSchema: {
565
+ type: 'object',
566
+ properties: {
567
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
568
+ repo: { type: 'string', description: 'Repository name' },
569
+ issue_number: { type: 'number', description: 'Issue number' },
570
+ assignees: { type: 'array', items: { type: 'string' }, description: 'Usernames to assign' },
571
+ },
572
+ required: ['repo', 'issue_number', 'assignees'],
573
+ },
574
+ },
575
+ {
576
+ name: 'search_repositories',
577
+ description: 'Search for repositories',
578
+ inputSchema: {
579
+ type: 'object',
580
+ properties: {
581
+ q: { type: 'string', description: 'Search query' },
582
+ sort: { type: 'string', enum: ['stars', 'forks', 'updated'], description: 'Sort results by' },
583
+ order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order', default: 'desc' },
584
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
585
+ },
586
+ required: ['q'],
587
+ },
588
+ },
589
+ {
590
+ name: 'search_issues',
591
+ description: 'Search for issues and pull requests',
592
+ inputSchema: {
593
+ type: 'object',
594
+ properties: {
595
+ q: { type: 'string', description: 'Search query' },
596
+ sort: { type: 'string', enum: ['comments', 'reactions', 'created', 'updated'], description: 'Sort results by' },
597
+ order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order', default: 'desc' },
598
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
599
+ },
600
+ required: ['q'],
601
+ },
602
+ },
603
+ {
604
+ name: 'search_code',
605
+ description: 'Search for code within repositories',
606
+ inputSchema: {
607
+ type: 'object',
608
+ properties: {
609
+ q: { type: 'string', description: 'Search query' },
610
+ sort: { type: 'string', enum: ['indexed'], description: 'Sort results by' },
611
+ order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order', default: 'desc' },
612
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
613
+ },
614
+ required: ['q'],
615
+ },
616
+ },
617
+ {
618
+ name: 'list_workflows',
619
+ description: 'List repository workflows',
620
+ inputSchema: {
621
+ type: 'object',
622
+ properties: {
623
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
624
+ repo: { type: 'string', description: 'Repository name' },
625
+ per_page: { type: 'number', description: 'Results per page (max 100)', default: 30 },
626
+ },
627
+ required: ['repo'],
628
+ },
629
+ },
630
+ {
631
+ name: 'trigger_workflow',
632
+ description: 'Manually trigger a workflow',
633
+ inputSchema: {
634
+ type: 'object',
635
+ properties: {
636
+ owner: { type: 'string', description: 'Repository owner (optional if OWNER env var is set)' },
637
+ repo: { type: 'string', description: 'Repository name' },
638
+ workflow_id: { type: 'string', description: 'Workflow ID or filename' },
639
+ ref: { type: 'string', description: 'Git reference (branch or tag)' },
640
+ inputs: { type: 'object', description: 'Input parameters for workflow', additionalProperties: true },
641
+ },
642
+ required: ['repo', 'workflow_id', 'ref'],
643
+ },
644
+ },
645
+ ],
646
+ };
647
+ });
648
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
649
+ const { name, arguments: args } = request.params;
650
+ try {
651
+ switch (name) {
652
+ case 'list_pull_requests':
653
+ return await this.listPullRequests(args);
654
+ case 'list_pull_requests_for_review':
655
+ return await this.listPullRequestsForReview(args);
656
+ case 'get_pull_request':
657
+ return await this.getPullRequest(args);
658
+ case 'get_pull_request_files':
659
+ return await this.getPullRequestFiles(args);
660
+ case 'create_pull_request':
661
+ return await this.createPullRequest(args);
662
+ case 'add_review_comment':
663
+ return await this.addReviewComment(args);
664
+ case 'approve_pull_request':
665
+ return await this.approvePullRequest(args);
666
+ case 'create_issue':
667
+ return await this.createIssue(args);
668
+ case 'list_issues':
669
+ return await this.listIssues(args);
670
+ case 'get_issue':
671
+ return await this.getIssue(args);
672
+ case 'update_issue':
673
+ return await this.updateIssue(args);
674
+ case 'close_issue':
675
+ return await this.closeIssue(args);
676
+ case 'add_issue_comment':
677
+ return await this.addIssueComment(args);
678
+ case 'get_repository':
679
+ return await this.getRepository(args);
680
+ case 'list_commits':
681
+ return await this.listCommits(args);
682
+ case 'get_commit':
683
+ return await this.getCommit(args);
684
+ case 'list_branches':
685
+ return await this.listBranches(args);
686
+ case 'create_branch':
687
+ return await this.createBranch(args);
688
+ case 'get_file_content':
689
+ return await this.getFileContent(args);
690
+ case 'update_file':
691
+ return await this.updateFile(args);
692
+ case 'request_review':
693
+ return await this.requestReview(args);
694
+ case 'submit_review':
695
+ return await this.submitReview(args);
696
+ case 'list_review_comments':
697
+ return await this.listReviewComments(args);
698
+ case 'list_labels':
699
+ return await this.listLabels(args);
700
+ case 'create_label':
701
+ return await this.createLabel(args);
702
+ case 'list_milestones':
703
+ return await this.listMilestones(args);
704
+ case 'create_milestone':
705
+ return await this.createMilestone(args);
706
+ case 'list_collaborators':
707
+ return await this.listCollaborators(args);
708
+ case 'check_user_permissions':
709
+ return await this.checkUserPermissions(args);
710
+ case 'assign_issue':
711
+ return await this.assignIssue(args);
712
+ case 'search_repositories':
713
+ return await this.searchRepositories(args);
714
+ case 'search_issues':
715
+ return await this.searchIssues(args);
716
+ case 'search_code':
717
+ return await this.searchCode(args);
718
+ case 'list_workflows':
719
+ return await this.listWorkflows(args);
720
+ case 'trigger_workflow':
721
+ return await this.triggerWorkflow(args);
722
+ default:
723
+ throw new Error(`Unknown tool: ${name}`);
724
+ }
725
+ }
726
+ catch (error) {
727
+ return {
728
+ content: [
729
+ {
730
+ type: 'text',
731
+ text: `Error executing ${name}: ${error instanceof Error ? error.message : 'Unknown error'}`,
732
+ },
733
+ ],
734
+ isError: true,
735
+ };
736
+ }
737
+ });
738
+ }
739
+ async listPullRequests(args) {
740
+ const owner = this.resolveOwner(args.owner);
741
+ const { data: pullRequests } = await this.octokit.rest.pulls.list({
742
+ owner,
743
+ repo: args.repo,
744
+ state: args.state || 'open',
745
+ per_page: args.per_page || 30,
746
+ });
747
+ const summary = pullRequests.map(pr => ({
748
+ number: pr.number,
749
+ title: pr.title,
750
+ author: pr.user?.login,
751
+ state: pr.state,
752
+ created_at: pr.created_at,
753
+ updated_at: pr.updated_at,
754
+ head_branch: pr.head.ref,
755
+ base_branch: pr.base.ref,
756
+ url: pr.html_url,
757
+ }));
758
+ return {
759
+ content: [
760
+ {
761
+ type: 'text',
762
+ text: JSON.stringify(summary, null, 2),
763
+ },
764
+ ],
765
+ };
766
+ }
767
+ async listPullRequestsForReview(args) {
768
+ const { data: result } = await this.octokit.rest.search.issuesAndPullRequests({
769
+ q: `type:pr state:${args.state || 'open'} review-requested:@me`,
770
+ per_page: args.per_page || 30,
771
+ });
772
+ const summary = result.items.map(pr => ({
773
+ number: pr.number,
774
+ title: pr.title,
775
+ author: pr.user?.login,
776
+ state: pr.state,
777
+ created_at: pr.created_at,
778
+ updated_at: pr.updated_at,
779
+ url: pr.html_url,
780
+ repository: pr.repository_url ? pr.repository_url.split('/').slice(-2).join('/') : 'unknown',
781
+ }));
782
+ return {
783
+ content: [
784
+ {
785
+ type: 'text',
786
+ text: JSON.stringify({
787
+ total_count: result.total_count,
788
+ pull_requests: summary
789
+ }, null, 2),
790
+ },
791
+ ],
792
+ };
793
+ }
794
+ async getPullRequest(args) {
795
+ const owner = this.resolveOwner(args.owner);
796
+ const { data: pr } = await this.octokit.rest.pulls.get({
797
+ owner,
798
+ repo: args.repo,
799
+ pull_number: args.pull_number,
800
+ });
801
+ const summary = {
802
+ number: pr.number,
803
+ title: pr.title,
804
+ body: pr.body,
805
+ author: pr.user?.login,
806
+ state: pr.state,
807
+ created_at: pr.created_at,
808
+ updated_at: pr.updated_at,
809
+ head_branch: pr.head.ref,
810
+ base_branch: pr.base.ref,
811
+ commits: pr.commits,
812
+ additions: pr.additions,
813
+ deletions: pr.deletions,
814
+ changed_files: pr.changed_files,
815
+ url: pr.html_url,
816
+ mergeable: pr.mergeable,
817
+ merged: pr.merged,
818
+ };
819
+ return {
820
+ content: [
821
+ {
822
+ type: 'text',
823
+ text: JSON.stringify(summary, null, 2),
824
+ },
825
+ ],
826
+ };
827
+ }
828
+ async getPullRequestFiles(args) {
829
+ const owner = this.resolveOwner(args.owner);
830
+ const { data: files } = await this.octokit.rest.pulls.listFiles({
831
+ owner,
832
+ repo: args.repo,
833
+ pull_number: args.pull_number,
834
+ });
835
+ const summary = files.map(file => ({
836
+ filename: file.filename,
837
+ status: file.status,
838
+ additions: file.additions,
839
+ deletions: file.deletions,
840
+ changes: file.changes,
841
+ patch: file.patch,
842
+ }));
843
+ return {
844
+ content: [
845
+ {
846
+ type: 'text',
847
+ text: JSON.stringify(summary, null, 2),
848
+ },
849
+ ],
850
+ };
851
+ }
852
+ async createPullRequest(args) {
853
+ const owner = this.resolveOwner(args.owner);
854
+ const { data: pr } = await this.octokit.rest.pulls.create({
855
+ owner,
856
+ repo: args.repo,
857
+ title: args.title,
858
+ head: args.head,
859
+ base: args.base,
860
+ body: args.body,
861
+ draft: args.draft || false,
862
+ });
863
+ return {
864
+ content: [
865
+ {
866
+ type: 'text',
867
+ text: `Created pull request #${pr.number}: ${pr.title}\nURL: ${pr.html_url}`,
868
+ },
869
+ ],
870
+ };
871
+ }
872
+ async addReviewComment(args) {
873
+ const owner = this.resolveOwner(args.owner);
874
+ const { data: comment } = await this.octokit.rest.pulls.createReviewComment({
875
+ owner,
876
+ repo: args.repo,
877
+ pull_number: args.pull_number,
878
+ body: args.body,
879
+ commit_id: args.commit_id,
880
+ path: args.path,
881
+ line: args.line,
882
+ });
883
+ return {
884
+ content: [
885
+ {
886
+ type: 'text',
887
+ text: `Added review comment on ${args.path}:${args.line}\nComment ID: ${comment.id}\nURL: ${comment.html_url}`,
888
+ },
889
+ ],
890
+ };
891
+ }
892
+ async approvePullRequest(args) {
893
+ const owner = this.resolveOwner(args.owner);
894
+ const { data: review } = await this.octokit.rest.pulls.createReview({
895
+ owner,
896
+ repo: args.repo,
897
+ pull_number: args.pull_number,
898
+ event: 'APPROVE',
899
+ body: args.body,
900
+ });
901
+ return {
902
+ content: [
903
+ {
904
+ type: 'text',
905
+ text: `Approved pull request #${args.pull_number}\nReview ID: ${review.id}${args.body ? `\nMessage: ${args.body}` : ''}`,
906
+ },
907
+ ],
908
+ };
909
+ }
910
+ async createIssue(args) {
911
+ const owner = this.resolveOwner(args.owner);
912
+ const { data: issue } = await this.octokit.rest.issues.create({
913
+ owner,
914
+ repo: args.repo,
915
+ title: args.title,
916
+ body: args.body,
917
+ assignees: args.assignees,
918
+ milestone: args.milestone,
919
+ labels: args.labels,
920
+ });
921
+ return {
922
+ content: [
923
+ {
924
+ type: 'text',
925
+ text: `Created issue #${issue.number}: ${issue.title}\nURL: ${issue.html_url}`,
926
+ },
927
+ ],
928
+ };
929
+ }
930
+ async listIssues(args) {
931
+ const owner = this.resolveOwner(args.owner);
932
+ const { data: issues } = await this.octokit.rest.issues.listForRepo({
933
+ owner,
934
+ repo: args.repo,
935
+ state: args.state || 'open',
936
+ labels: args.labels,
937
+ assignee: args.assignee,
938
+ milestone: args.milestone,
939
+ per_page: args.per_page || 30,
940
+ });
941
+ const summary = issues.map(issue => ({
942
+ number: issue.number,
943
+ title: issue.title,
944
+ state: issue.state,
945
+ user: issue.user?.login,
946
+ labels: issue.labels.map(label => typeof label === 'string' ? label : label.name),
947
+ assignees: issue.assignees?.map(assignee => assignee?.login),
948
+ created_at: issue.created_at,
949
+ updated_at: issue.updated_at,
950
+ url: issue.html_url,
951
+ }));
952
+ return {
953
+ content: [
954
+ {
955
+ type: 'text',
956
+ text: JSON.stringify(summary, null, 2),
957
+ },
958
+ ],
959
+ };
960
+ }
961
+ async getIssue(args) {
962
+ const owner = this.resolveOwner(args.owner);
963
+ const { data: issue } = await this.octokit.rest.issues.get({
964
+ owner,
965
+ repo: args.repo,
966
+ issue_number: args.issue_number,
967
+ });
968
+ const summary = {
969
+ number: issue.number,
970
+ title: issue.title,
971
+ body: issue.body,
972
+ state: issue.state,
973
+ user: issue.user?.login,
974
+ labels: issue.labels.map(label => typeof label === 'string' ? label : label.name),
975
+ assignees: issue.assignees?.map(assignee => assignee?.login),
976
+ milestone: issue.milestone?.title,
977
+ created_at: issue.created_at,
978
+ updated_at: issue.updated_at,
979
+ closed_at: issue.closed_at,
980
+ url: issue.html_url,
981
+ };
982
+ return {
983
+ content: [
984
+ {
985
+ type: 'text',
986
+ text: JSON.stringify(summary, null, 2),
987
+ },
988
+ ],
989
+ };
990
+ }
991
+ async updateIssue(args) {
992
+ const owner = this.resolveOwner(args.owner);
993
+ const { data: issue } = await this.octokit.rest.issues.update({
994
+ owner,
995
+ repo: args.repo,
996
+ issue_number: args.issue_number,
997
+ title: args.title,
998
+ body: args.body,
999
+ state: args.state,
1000
+ assignees: args.assignees,
1001
+ labels: args.labels,
1002
+ });
1003
+ return {
1004
+ content: [
1005
+ {
1006
+ type: 'text',
1007
+ text: `Updated issue #${issue.number}: ${issue.title}\nState: ${issue.state}\nURL: ${issue.html_url}`,
1008
+ },
1009
+ ],
1010
+ };
1011
+ }
1012
+ async closeIssue(args) {
1013
+ const owner = this.resolveOwner(args.owner);
1014
+ const { data: issue } = await this.octokit.rest.issues.update({
1015
+ owner,
1016
+ repo: args.repo,
1017
+ issue_number: args.issue_number,
1018
+ state: 'closed',
1019
+ state_reason: args.reason,
1020
+ });
1021
+ return {
1022
+ content: [
1023
+ {
1024
+ type: 'text',
1025
+ text: `Closed issue #${issue.number}: ${issue.title}\nReason: ${args.reason || 'not specified'}\nURL: ${issue.html_url}`,
1026
+ },
1027
+ ],
1028
+ };
1029
+ }
1030
+ async addIssueComment(args) {
1031
+ const owner = this.resolveOwner(args.owner);
1032
+ const { data: comment } = await this.octokit.rest.issues.createComment({
1033
+ owner,
1034
+ repo: args.repo,
1035
+ issue_number: args.issue_number,
1036
+ body: args.body,
1037
+ });
1038
+ return {
1039
+ content: [
1040
+ {
1041
+ type: 'text',
1042
+ text: `Added comment to issue #${args.issue_number}\nComment ID: ${comment.id}\nURL: ${comment.html_url}`,
1043
+ },
1044
+ ],
1045
+ };
1046
+ }
1047
+ async getRepository(args) {
1048
+ const owner = this.resolveOwner(args.owner);
1049
+ const { data: repo } = await this.octokit.rest.repos.get({
1050
+ owner,
1051
+ repo: args.repo,
1052
+ });
1053
+ const summary = {
1054
+ name: repo.name,
1055
+ full_name: repo.full_name,
1056
+ description: repo.description,
1057
+ private: repo.private,
1058
+ fork: repo.fork,
1059
+ stargazers_count: repo.stargazers_count,
1060
+ watchers_count: repo.watchers_count,
1061
+ forks_count: repo.forks_count,
1062
+ open_issues_count: repo.open_issues_count,
1063
+ default_branch: repo.default_branch,
1064
+ language: repo.language,
1065
+ size: repo.size,
1066
+ created_at: repo.created_at,
1067
+ updated_at: repo.updated_at,
1068
+ pushed_at: repo.pushed_at,
1069
+ clone_url: repo.clone_url,
1070
+ html_url: repo.html_url,
1071
+ };
1072
+ return {
1073
+ content: [
1074
+ {
1075
+ type: 'text',
1076
+ text: JSON.stringify(summary, null, 2),
1077
+ },
1078
+ ],
1079
+ };
1080
+ }
1081
+ async listCommits(args) {
1082
+ const owner = this.resolveOwner(args.owner);
1083
+ const { data: commits } = await this.octokit.rest.repos.listCommits({
1084
+ owner,
1085
+ repo: args.repo,
1086
+ sha: args.sha,
1087
+ path: args.path,
1088
+ per_page: args.per_page || 30,
1089
+ });
1090
+ const summary = commits.map(commit => ({
1091
+ sha: commit.sha,
1092
+ message: commit.commit.message,
1093
+ author: {
1094
+ name: commit.commit.author?.name,
1095
+ email: commit.commit.author?.email,
1096
+ date: commit.commit.author?.date,
1097
+ },
1098
+ committer: {
1099
+ name: commit.commit.committer?.name,
1100
+ email: commit.commit.committer?.email,
1101
+ date: commit.commit.committer?.date,
1102
+ },
1103
+ url: commit.html_url,
1104
+ }));
1105
+ return {
1106
+ content: [
1107
+ {
1108
+ type: 'text',
1109
+ text: JSON.stringify(summary, null, 2),
1110
+ },
1111
+ ],
1112
+ };
1113
+ }
1114
+ async getCommit(args) {
1115
+ const owner = this.resolveOwner(args.owner);
1116
+ const { data: commit } = await this.octokit.rest.repos.getCommit({
1117
+ owner,
1118
+ repo: args.repo,
1119
+ ref: args.ref,
1120
+ });
1121
+ const summary = {
1122
+ sha: commit.sha,
1123
+ message: commit.commit.message,
1124
+ author: {
1125
+ name: commit.commit.author?.name,
1126
+ email: commit.commit.author?.email,
1127
+ date: commit.commit.author?.date,
1128
+ },
1129
+ stats: commit.stats,
1130
+ files: commit.files?.map(file => ({
1131
+ filename: file.filename,
1132
+ status: file.status,
1133
+ additions: file.additions,
1134
+ deletions: file.deletions,
1135
+ changes: file.changes,
1136
+ patch: file.patch,
1137
+ })),
1138
+ url: commit.html_url,
1139
+ };
1140
+ return {
1141
+ content: [
1142
+ {
1143
+ type: 'text',
1144
+ text: JSON.stringify(summary, null, 2),
1145
+ },
1146
+ ],
1147
+ };
1148
+ }
1149
+ async listBranches(args) {
1150
+ const owner = this.resolveOwner(args.owner);
1151
+ const { data: branches } = await this.octokit.rest.repos.listBranches({
1152
+ owner,
1153
+ repo: args.repo,
1154
+ protected: args.protected,
1155
+ per_page: args.per_page || 30,
1156
+ });
1157
+ const summary = branches.map(branch => ({
1158
+ name: branch.name,
1159
+ commit: {
1160
+ sha: branch.commit.sha,
1161
+ url: branch.commit.url,
1162
+ },
1163
+ protected: branch.protected,
1164
+ }));
1165
+ return {
1166
+ content: [
1167
+ {
1168
+ type: 'text',
1169
+ text: JSON.stringify(summary, null, 2),
1170
+ },
1171
+ ],
1172
+ };
1173
+ }
1174
+ async createBranch(args) {
1175
+ const owner = this.resolveOwner(args.owner);
1176
+ const { data: ref } = await this.octokit.rest.git.createRef({
1177
+ owner,
1178
+ repo: args.repo,
1179
+ ref: args.ref,
1180
+ sha: args.sha,
1181
+ });
1182
+ return {
1183
+ content: [
1184
+ {
1185
+ type: 'text',
1186
+ text: `Created branch: ${ref.ref}\nSHA: ${ref.object.sha}\nURL: ${ref.url}`,
1187
+ },
1188
+ ],
1189
+ };
1190
+ }
1191
+ async getFileContent(args) {
1192
+ const owner = this.resolveOwner(args.owner);
1193
+ const { data: file } = await this.octokit.rest.repos.getContent({
1194
+ owner,
1195
+ repo: args.repo,
1196
+ path: args.path,
1197
+ ref: args.ref,
1198
+ });
1199
+ if (Array.isArray(file)) {
1200
+ return {
1201
+ content: [
1202
+ {
1203
+ type: 'text',
1204
+ text: `Path is a directory with ${file.length} items:\n${file.map(item => `${item.type}: ${item.name}`).join('\n')}`,
1205
+ },
1206
+ ],
1207
+ };
1208
+ }
1209
+ const content = file.type === 'file' ?
1210
+ Buffer.from(file.content, 'base64').toString('utf-8') :
1211
+ 'File is not a regular file';
1212
+ return {
1213
+ content: [
1214
+ {
1215
+ type: 'text',
1216
+ text: `File: ${file.name}\nSize: ${file.size} bytes\nSHA: ${file.sha}\n\nContent:\n${content}`,
1217
+ },
1218
+ ],
1219
+ };
1220
+ }
1221
+ async updateFile(args) {
1222
+ const owner = this.resolveOwner(args.owner);
1223
+ const { data: result } = await this.octokit.rest.repos.createOrUpdateFileContents({
1224
+ owner,
1225
+ repo: args.repo,
1226
+ path: args.path,
1227
+ message: args.message,
1228
+ content: args.content,
1229
+ sha: args.sha,
1230
+ branch: args.branch,
1231
+ });
1232
+ return {
1233
+ content: [
1234
+ {
1235
+ type: 'text',
1236
+ text: `${args.sha ? 'Updated' : 'Created'} file: ${args.path}\nCommit SHA: ${result.commit.sha}\nURL: ${result.commit.html_url}`,
1237
+ },
1238
+ ],
1239
+ };
1240
+ }
1241
+ async requestReview(args) {
1242
+ const owner = this.resolveOwner(args.owner);
1243
+ const { data: result } = await this.octokit.rest.pulls.requestReviewers({
1244
+ owner,
1245
+ repo: args.repo,
1246
+ pull_number: args.pull_number,
1247
+ reviewers: args.reviewers,
1248
+ team_reviewers: args.team_reviewers,
1249
+ });
1250
+ return {
1251
+ content: [
1252
+ {
1253
+ type: 'text',
1254
+ text: `Requested reviews for PR #${args.pull_number}\nReviewers: ${args.reviewers?.join(', ') || 'none'}\nTeam reviewers: ${args.team_reviewers?.join(', ') || 'none'}`,
1255
+ },
1256
+ ],
1257
+ };
1258
+ }
1259
+ async submitReview(args) {
1260
+ const owner = this.resolveOwner(args.owner);
1261
+ const { data: review } = await this.octokit.rest.pulls.createReview({
1262
+ owner,
1263
+ repo: args.repo,
1264
+ pull_number: args.pull_number,
1265
+ event: args.event,
1266
+ body: args.body,
1267
+ });
1268
+ return {
1269
+ content: [
1270
+ {
1271
+ type: 'text',
1272
+ text: `Submitted ${args.event} review for PR #${args.pull_number}\nReview ID: ${review.id}\nURL: ${review.html_url}`,
1273
+ },
1274
+ ],
1275
+ };
1276
+ }
1277
+ async listReviewComments(args) {
1278
+ const owner = this.resolveOwner(args.owner);
1279
+ const { data: comments } = await this.octokit.rest.pulls.listReviewComments({
1280
+ owner,
1281
+ repo: args.repo,
1282
+ pull_number: args.pull_number,
1283
+ });
1284
+ const summary = comments.map(comment => ({
1285
+ id: comment.id,
1286
+ user: comment.user?.login,
1287
+ body: comment.body,
1288
+ path: comment.path,
1289
+ line: comment.line,
1290
+ created_at: comment.created_at,
1291
+ updated_at: comment.updated_at,
1292
+ url: comment.html_url,
1293
+ }));
1294
+ return {
1295
+ content: [
1296
+ {
1297
+ type: 'text',
1298
+ text: JSON.stringify(summary, null, 2),
1299
+ },
1300
+ ],
1301
+ };
1302
+ }
1303
+ async listLabels(args) {
1304
+ const owner = this.resolveOwner(args.owner);
1305
+ const { data: labels } = await this.octokit.rest.issues.listLabelsForRepo({
1306
+ owner,
1307
+ repo: args.repo,
1308
+ per_page: args.per_page || 30,
1309
+ });
1310
+ const summary = labels.map(label => ({
1311
+ id: label.id,
1312
+ name: label.name,
1313
+ color: label.color,
1314
+ description: label.description,
1315
+ }));
1316
+ return {
1317
+ content: [
1318
+ {
1319
+ type: 'text',
1320
+ text: JSON.stringify(summary, null, 2),
1321
+ },
1322
+ ],
1323
+ };
1324
+ }
1325
+ async createLabel(args) {
1326
+ const owner = this.resolveOwner(args.owner);
1327
+ const { data: label } = await this.octokit.rest.issues.createLabel({
1328
+ owner,
1329
+ repo: args.repo,
1330
+ name: args.name,
1331
+ color: args.color,
1332
+ description: args.description,
1333
+ });
1334
+ return {
1335
+ content: [
1336
+ {
1337
+ type: 'text',
1338
+ text: `Created label: ${label.name}\nColor: #${label.color}\nDescription: ${label.description || 'none'}`,
1339
+ },
1340
+ ],
1341
+ };
1342
+ }
1343
+ async listMilestones(args) {
1344
+ const owner = this.resolveOwner(args.owner);
1345
+ const { data: milestones } = await this.octokit.rest.issues.listMilestones({
1346
+ owner,
1347
+ repo: args.repo,
1348
+ state: args.state || 'open',
1349
+ per_page: args.per_page || 30,
1350
+ });
1351
+ const summary = milestones.map(milestone => ({
1352
+ number: milestone.number,
1353
+ title: milestone.title,
1354
+ description: milestone.description,
1355
+ state: milestone.state,
1356
+ open_issues: milestone.open_issues,
1357
+ closed_issues: milestone.closed_issues,
1358
+ due_on: milestone.due_on,
1359
+ created_at: milestone.created_at,
1360
+ updated_at: milestone.updated_at,
1361
+ url: milestone.html_url,
1362
+ }));
1363
+ return {
1364
+ content: [
1365
+ {
1366
+ type: 'text',
1367
+ text: JSON.stringify(summary, null, 2),
1368
+ },
1369
+ ],
1370
+ };
1371
+ }
1372
+ async createMilestone(args) {
1373
+ const owner = this.resolveOwner(args.owner);
1374
+ const { data: milestone } = await this.octokit.rest.issues.createMilestone({
1375
+ owner,
1376
+ repo: args.repo,
1377
+ title: args.title,
1378
+ description: args.description,
1379
+ due_on: args.due_on,
1380
+ });
1381
+ return {
1382
+ content: [
1383
+ {
1384
+ type: 'text',
1385
+ text: `Created milestone: ${milestone.title}\nNumber: ${milestone.number}\nDue: ${milestone.due_on || 'not set'}\nURL: ${milestone.html_url}`,
1386
+ },
1387
+ ],
1388
+ };
1389
+ }
1390
+ async listCollaborators(args) {
1391
+ const owner = this.resolveOwner(args.owner);
1392
+ const { data: collaborators } = await this.octokit.rest.repos.listCollaborators({
1393
+ owner,
1394
+ repo: args.repo,
1395
+ affiliation: args.affiliation || 'all',
1396
+ per_page: args.per_page || 30,
1397
+ });
1398
+ const summary = collaborators.map(collaborator => ({
1399
+ login: collaborator.login,
1400
+ id: collaborator.id,
1401
+ type: collaborator.type,
1402
+ site_admin: collaborator.site_admin,
1403
+ permissions: collaborator.permissions,
1404
+ url: collaborator.html_url,
1405
+ }));
1406
+ return {
1407
+ content: [
1408
+ {
1409
+ type: 'text',
1410
+ text: JSON.stringify(summary, null, 2),
1411
+ },
1412
+ ],
1413
+ };
1414
+ }
1415
+ async checkUserPermissions(args) {
1416
+ const owner = this.resolveOwner(args.owner);
1417
+ try {
1418
+ const { data: permission } = await this.octokit.rest.repos.getCollaboratorPermissionLevel({
1419
+ owner,
1420
+ repo: args.repo,
1421
+ username: args.username,
1422
+ });
1423
+ return {
1424
+ content: [
1425
+ {
1426
+ type: 'text',
1427
+ text: `User ${args.username} permissions:\nLevel: ${permission.permission}\nUser: ${permission.user?.login}\nType: ${permission.user?.type}`,
1428
+ },
1429
+ ],
1430
+ };
1431
+ }
1432
+ catch (error) {
1433
+ if (error.status === 404) {
1434
+ return {
1435
+ content: [
1436
+ {
1437
+ type: 'text',
1438
+ text: `User ${args.username} is not a collaborator on ${args.owner}/${args.repo}`,
1439
+ },
1440
+ ],
1441
+ };
1442
+ }
1443
+ throw error;
1444
+ }
1445
+ }
1446
+ async assignIssue(args) {
1447
+ const owner = this.resolveOwner(args.owner);
1448
+ const { data: issue } = await this.octokit.rest.issues.addAssignees({
1449
+ owner,
1450
+ repo: args.repo,
1451
+ issue_number: args.issue_number,
1452
+ assignees: args.assignees,
1453
+ });
1454
+ return {
1455
+ content: [
1456
+ {
1457
+ type: 'text',
1458
+ text: `Assigned issue #${issue.number} to: ${args.assignees.join(', ')}\nURL: ${issue.html_url}`,
1459
+ },
1460
+ ],
1461
+ };
1462
+ }
1463
+ async searchRepositories(args) {
1464
+ const { data: result } = await this.octokit.rest.search.repos({
1465
+ q: args.q,
1466
+ sort: args.sort,
1467
+ order: args.order || 'desc',
1468
+ per_page: args.per_page || 30,
1469
+ });
1470
+ const summary = {
1471
+ total_count: result.total_count,
1472
+ incomplete_results: result.incomplete_results,
1473
+ items: result.items.map(repo => ({
1474
+ name: repo.name,
1475
+ full_name: repo.full_name,
1476
+ description: repo.description,
1477
+ stars: repo.stargazers_count,
1478
+ forks: repo.forks_count,
1479
+ language: repo.language,
1480
+ updated_at: repo.updated_at,
1481
+ url: repo.html_url,
1482
+ })),
1483
+ };
1484
+ return {
1485
+ content: [
1486
+ {
1487
+ type: 'text',
1488
+ text: JSON.stringify(summary, null, 2),
1489
+ },
1490
+ ],
1491
+ };
1492
+ }
1493
+ async searchIssues(args) {
1494
+ const { data: result } = await this.octokit.rest.search.issuesAndPullRequests({
1495
+ q: args.q,
1496
+ sort: args.sort,
1497
+ order: args.order || 'desc',
1498
+ per_page: args.per_page || 30,
1499
+ });
1500
+ const summary = {
1501
+ total_count: result.total_count,
1502
+ incomplete_results: result.incomplete_results,
1503
+ items: result.items.map(item => ({
1504
+ number: item.number,
1505
+ title: item.title,
1506
+ state: item.state,
1507
+ user: item.user?.login,
1508
+ labels: item.labels?.map(label => typeof label === 'string' ? label : label.name),
1509
+ created_at: item.created_at,
1510
+ updated_at: item.updated_at,
1511
+ url: item.html_url,
1512
+ repository_url: item.repository_url,
1513
+ })),
1514
+ };
1515
+ return {
1516
+ content: [
1517
+ {
1518
+ type: 'text',
1519
+ text: JSON.stringify(summary, null, 2),
1520
+ },
1521
+ ],
1522
+ };
1523
+ }
1524
+ async searchCode(args) {
1525
+ const { data: result } = await this.octokit.rest.search.code({
1526
+ q: args.q,
1527
+ sort: args.sort,
1528
+ order: args.order || 'desc',
1529
+ per_page: args.per_page || 30,
1530
+ });
1531
+ const summary = {
1532
+ total_count: result.total_count,
1533
+ incomplete_results: result.incomplete_results,
1534
+ items: result.items.map(item => ({
1535
+ name: item.name,
1536
+ path: item.path,
1537
+ sha: item.sha,
1538
+ url: item.html_url,
1539
+ git_url: item.git_url,
1540
+ repository: {
1541
+ name: item.repository.name,
1542
+ full_name: item.repository.full_name,
1543
+ url: item.repository.html_url,
1544
+ },
1545
+ })),
1546
+ };
1547
+ return {
1548
+ content: [
1549
+ {
1550
+ type: 'text',
1551
+ text: JSON.stringify(summary, null, 2),
1552
+ },
1553
+ ],
1554
+ };
1555
+ }
1556
+ async listWorkflows(args) {
1557
+ const owner = this.resolveOwner(args.owner);
1558
+ const { data: workflows } = await this.octokit.rest.actions.listRepoWorkflows({
1559
+ owner,
1560
+ repo: args.repo,
1561
+ per_page: args.per_page || 30,
1562
+ });
1563
+ const summary = workflows.workflows.map(workflow => ({
1564
+ id: workflow.id,
1565
+ name: workflow.name,
1566
+ path: workflow.path,
1567
+ state: workflow.state,
1568
+ created_at: workflow.created_at,
1569
+ updated_at: workflow.updated_at,
1570
+ url: workflow.html_url,
1571
+ }));
1572
+ return {
1573
+ content: [
1574
+ {
1575
+ type: 'text',
1576
+ text: JSON.stringify(summary, null, 2),
1577
+ },
1578
+ ],
1579
+ };
1580
+ }
1581
+ async triggerWorkflow(args) {
1582
+ const owner = this.resolveOwner(args.owner);
1583
+ await this.octokit.rest.actions.createWorkflowDispatch({
1584
+ owner,
1585
+ repo: args.repo,
1586
+ workflow_id: args.workflow_id,
1587
+ ref: args.ref,
1588
+ inputs: args.inputs,
1589
+ });
1590
+ return {
1591
+ content: [
1592
+ {
1593
+ type: 'text',
1594
+ text: `Triggered workflow: ${args.workflow_id}\nRef: ${args.ref}\nInputs: ${JSON.stringify(args.inputs || {}, null, 2)}`,
1595
+ },
1596
+ ],
1597
+ };
1598
+ }
1599
+ async run() {
1600
+ const transport = new StdioServerTransport();
1601
+ await this.server.connect(transport);
1602
+ console.error('GitHub MCP server running on stdio');
1603
+ }
1604
+ }
1605
+ const server = new GitHubMCPServer();
1606
+ server.run().catch((error) => {
1607
+ console.error('Fatal error in main():', error);
1608
+ process.exit(1);
1609
+ });
1610
+ //# sourceMappingURL=index.js.map