github-issue-pr-manager 0.1.0__py3-none-any.whl
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.
- github_issue_pr_manager/__init__.py +5 -0
- github_issue_pr_manager/server.py +299 -0
- github_issue_pr_manager-0.1.0.dist-info/METADATA +55 -0
- github_issue_pr_manager-0.1.0.dist-info/RECORD +6 -0
- github_issue_pr_manager-0.1.0.dist-info/WHEEL +4 -0
- github_issue_pr_manager-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GitHub Issue PR Manager
|
|
3
|
+
|
|
4
|
+
MCP server for managing GitHub issues and pull requests, providing AI agents with tools to gather context and close issues through PR workflows
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from fastmcp import FastMCP
|
|
8
|
+
|
|
9
|
+
# Initialize the MCP server
|
|
10
|
+
mcp = FastMCP("GitHub Issue PR Manager")
|
|
11
|
+
|
|
12
|
+
@mcp.tool()
|
|
13
|
+
def get_issue(owner: str, repo: str, issue_number: int) -> str:
|
|
14
|
+
"""Get detailed information about a GitHub issue including comments and linked PRs
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
owner: Repository owner (username or organization)
|
|
18
|
+
repo: Repository name
|
|
19
|
+
issue_number: Issue number
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Issue details including title, body, state, labels, assignees, and comments
|
|
23
|
+
"""
|
|
24
|
+
import requests
|
|
25
|
+
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
26
|
+
url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}'
|
|
27
|
+
response = requests.get(url, headers=headers)
|
|
28
|
+
if response.status_code == 200:
|
|
29
|
+
issue = response.json()
|
|
30
|
+
# Get comments
|
|
31
|
+
comments_url = issue['comments_url']
|
|
32
|
+
comments_response = requests.get(comments_url, headers=headers)
|
|
33
|
+
comments = comments_response.json() if comments_response.status_code == 200 else []
|
|
34
|
+
issue['all_comments'] = comments
|
|
35
|
+
return issue
|
|
36
|
+
else:
|
|
37
|
+
return {'error': f'Failed to get issue: {response.status_code}'}
|
|
38
|
+
|
|
39
|
+
@mcp.tool()
|
|
40
|
+
def list_issue_prs(owner: str, repo: str, issue_number: int) -> str:
|
|
41
|
+
"""List all pull requests that reference or are linked to a specific issue
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
owner: Repository owner (username or organization)
|
|
45
|
+
repo: Repository name
|
|
46
|
+
issue_number: Issue number to find related PRs for
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
List of pull requests that reference the issue
|
|
50
|
+
"""
|
|
51
|
+
import requests
|
|
52
|
+
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
53
|
+
# Search for PRs mentioning the issue
|
|
54
|
+
query = f'repo:{owner}/{repo} type:pr #{issue_number}'
|
|
55
|
+
search_url = f'https://api.github.com/search/issues?q={query}'
|
|
56
|
+
response = requests.get(search_url, headers=headers)
|
|
57
|
+
if response.status_code == 200:
|
|
58
|
+
return response.json()['items']
|
|
59
|
+
else:
|
|
60
|
+
return {'error': f'Failed to search PRs: {response.status_code}'}
|
|
61
|
+
|
|
62
|
+
@mcp.tool()
|
|
63
|
+
def get_pull_request(owner: str, repo: str, pr_number: int) -> str:
|
|
64
|
+
"""Get detailed information about a pull request including its status and mergability
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
owner: Repository owner (username or organization)
|
|
68
|
+
repo: Repository name
|
|
69
|
+
pr_number: Pull request number
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
PR details including state, mergeable status, checks, and review status
|
|
73
|
+
"""
|
|
74
|
+
import requests
|
|
75
|
+
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
76
|
+
url = f'https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}'
|
|
77
|
+
response = requests.get(url, headers=headers)
|
|
78
|
+
if response.status_code == 200:
|
|
79
|
+
pr = response.json()
|
|
80
|
+
# Get review status
|
|
81
|
+
reviews_url = f'https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}/reviews'
|
|
82
|
+
reviews_response = requests.get(reviews_url, headers=headers)
|
|
83
|
+
pr['reviews'] = reviews_response.json() if reviews_response.status_code == 200 else []
|
|
84
|
+
return pr
|
|
85
|
+
else:
|
|
86
|
+
return {'error': f'Failed to get PR: {response.status_code}'}
|
|
87
|
+
|
|
88
|
+
@mcp.tool()
|
|
89
|
+
def list_repository_prs(owner: str, repo: str, state: str) -> str:
|
|
90
|
+
"""List pull requests in a repository with optional filtering by state
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
owner: Repository owner (username or organization)
|
|
94
|
+
repo: Repository name
|
|
95
|
+
state: PR state filter (open, closed, all)
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
List of pull requests in the repository
|
|
99
|
+
"""
|
|
100
|
+
import requests
|
|
101
|
+
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
102
|
+
params = {}
|
|
103
|
+
if state:
|
|
104
|
+
params['state'] = state
|
|
105
|
+
url = f'https://api.github.com/repos/{owner}/{repo}/pulls'
|
|
106
|
+
response = requests.get(url, headers=headers, params=params)
|
|
107
|
+
if response.status_code == 200:
|
|
108
|
+
return response.json()
|
|
109
|
+
else:
|
|
110
|
+
return {'error': f'Failed to list PRs: {response.status_code}'}
|
|
111
|
+
|
|
112
|
+
@mcp.tool()
|
|
113
|
+
def create_pull_request(owner: str, repo: str, title: str, body: str, head: str, base: str) -> str:
|
|
114
|
+
"""Create a new pull request
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
owner: Repository owner (username or organization)
|
|
118
|
+
repo: Repository name
|
|
119
|
+
title: Pull request title
|
|
120
|
+
body: Pull request description
|
|
121
|
+
head: Branch name containing the changes
|
|
122
|
+
base: Branch name to merge into (default: main)
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Created pull request details
|
|
126
|
+
"""
|
|
127
|
+
import requests
|
|
128
|
+
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
129
|
+
data = {
|
|
130
|
+
'title': title,
|
|
131
|
+
'body': body,
|
|
132
|
+
'head': head,
|
|
133
|
+
'base': base or 'main'
|
|
134
|
+
}
|
|
135
|
+
url = f'https://api.github.com/repos/{owner}/{repo}/pulls'
|
|
136
|
+
response = requests.post(url, headers=headers, json=data)
|
|
137
|
+
if response.status_code == 201:
|
|
138
|
+
return response.json()
|
|
139
|
+
else:
|
|
140
|
+
return {'error': f'Failed to create PR: {response.status_code} - {response.text}'}
|
|
141
|
+
|
|
142
|
+
@mcp.tool()
|
|
143
|
+
def merge_pull_request(owner: str, repo: str, pr_number: int, commit_title: str, merge_method: str) -> str:
|
|
144
|
+
"""Merge a pull request if it's ready
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
owner: Repository owner (username or organization)
|
|
148
|
+
repo: Repository name
|
|
149
|
+
pr_number: Pull request number
|
|
150
|
+
commit_title: Merge commit title (optional)
|
|
151
|
+
merge_method: Merge method (merge, squash, rebase)
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Merge result information
|
|
155
|
+
"""
|
|
156
|
+
import requests
|
|
157
|
+
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
158
|
+
data = {}
|
|
159
|
+
if commit_title:
|
|
160
|
+
data['commit_title'] = commit_title
|
|
161
|
+
if merge_method:
|
|
162
|
+
data['merge_method'] = merge_method
|
|
163
|
+
url = f'https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}/merge'
|
|
164
|
+
response = requests.put(url, headers=headers, json=data)
|
|
165
|
+
if response.status_code == 200:
|
|
166
|
+
return response.json()
|
|
167
|
+
else:
|
|
168
|
+
return {'error': f'Failed to merge PR: {response.status_code} - {response.text}'}
|
|
169
|
+
|
|
170
|
+
@mcp.tool()
|
|
171
|
+
def close_issue(owner: str, repo: str, issue_number: int, comment: str) -> str:
|
|
172
|
+
"""Close an issue and optionally add a closing comment
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
owner: Repository owner (username or organization)
|
|
176
|
+
repo: Repository name
|
|
177
|
+
issue_number: Issue number
|
|
178
|
+
comment: Optional closing comment
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Updated issue information
|
|
182
|
+
"""
|
|
183
|
+
import requests
|
|
184
|
+
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
185
|
+
# Add comment if provided
|
|
186
|
+
if comment:
|
|
187
|
+
comment_url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}/comments'
|
|
188
|
+
requests.post(comment_url, headers=headers, json={'body': comment})
|
|
189
|
+
# Close the issue
|
|
190
|
+
data = {'state': 'closed'}
|
|
191
|
+
url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}'
|
|
192
|
+
response = requests.patch(url, headers=headers, json=data)
|
|
193
|
+
if response.status_code == 200:
|
|
194
|
+
return response.json()
|
|
195
|
+
else:
|
|
196
|
+
return {'error': f'Failed to close issue: {response.status_code}'}
|
|
197
|
+
|
|
198
|
+
@mcp.tool()
|
|
199
|
+
def add_issue_comment(owner: str, repo: str, issue_number: int, comment: str) -> str:
|
|
200
|
+
"""Add a comment to an issue
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
owner: Repository owner (username or organization)
|
|
204
|
+
repo: Repository name
|
|
205
|
+
issue_number: Issue number
|
|
206
|
+
comment: Comment text
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Created comment information
|
|
210
|
+
"""
|
|
211
|
+
import requests
|
|
212
|
+
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
213
|
+
data = {'body': comment}
|
|
214
|
+
url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}/comments'
|
|
215
|
+
response = requests.post(url, headers=headers, json=data)
|
|
216
|
+
if response.status_code == 201:
|
|
217
|
+
return response.json()
|
|
218
|
+
else:
|
|
219
|
+
return {'error': f'Failed to add comment: {response.status_code}'}
|
|
220
|
+
|
|
221
|
+
@mcp.resource("docs://github-api/{endpoint}")
|
|
222
|
+
def github_api_docs(endpoint: str) -> str:
|
|
223
|
+
"""GitHub API documentation reference for issues and pull requests"""
|
|
224
|
+
docs = {
|
|
225
|
+
'issues': 'GitHub Issues API: https://docs.github.com/en/rest/issues',
|
|
226
|
+
'pulls': 'GitHub Pull Requests API: https://docs.github.com/en/rest/pulls',
|
|
227
|
+
'authentication': 'Use personal access tokens in headers: {"Authorization": "token YOUR_TOKEN"}',
|
|
228
|
+
'rate_limits': 'GitHub API rate limit: 5000 requests per hour for authenticated users'
|
|
229
|
+
}
|
|
230
|
+
endpoint = endpoint if endpoint in docs else 'issues'
|
|
231
|
+
return docs[endpoint]
|
|
232
|
+
|
|
233
|
+
@mcp.resource("guide://workflow")
|
|
234
|
+
def workflow_guide() -> str:
|
|
235
|
+
"""Guide for using this MCP to close issues with PRs"""
|
|
236
|
+
return '''GitHub Issue-PR Workflow Guide:
|
|
237
|
+
|
|
238
|
+
1. Get issue details: Use get_issue() to understand the problem
|
|
239
|
+
2. Find related PRs: Use list_issue_prs() to see existing work
|
|
240
|
+
3. Check PR status: Use get_pull_request() to verify PR readiness
|
|
241
|
+
4. Create PR if needed: Use create_pull_request() for new solutions
|
|
242
|
+
5. Merge when ready: Use merge_pull_request() for approved PRs
|
|
243
|
+
6. Close issue: Use close_issue() with a summary comment
|
|
244
|
+
7. Update stakeholders: Use add_issue_comment() for status updates
|
|
245
|
+
|
|
246
|
+
Best Practices:
|
|
247
|
+
- Always check if PRs are mergeable and have passed checks
|
|
248
|
+
- Verify all required reviews are approved
|
|
249
|
+
- Add meaningful comments when closing issues
|
|
250
|
+
- Reference PR numbers when closing issues'''
|
|
251
|
+
|
|
252
|
+
@mcp.prompt()
|
|
253
|
+
def analyze_issue(owner: str, repo: str, issue_number: str) -> str:
|
|
254
|
+
"""Analyze an issue and determine what actions are needed to resolve it
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
owner: Repository owner
|
|
258
|
+
repo: Repository name
|
|
259
|
+
issue_number: Issue number to analyze
|
|
260
|
+
"""
|
|
261
|
+
return f"""Please analyze GitHub issue #{issue_number} in {owner}/{repo}.
|
|
262
|
+
|
|
263
|
+
1. First, get the issue details to understand the problem
|
|
264
|
+
2. Check for any existing pull requests that might address this issue
|
|
265
|
+
3. For each related PR, check its status and mergability
|
|
266
|
+
4. Provide a summary of:
|
|
267
|
+
- What the issue is about
|
|
268
|
+
- Current status of any related PRs
|
|
269
|
+
- What actions are needed to close this issue
|
|
270
|
+
- Whether the issue can be closed now or what's blocking it
|
|
271
|
+
|
|
272
|
+
Be thorough in your analysis and provide actionable recommendations."""
|
|
273
|
+
|
|
274
|
+
@mcp.prompt()
|
|
275
|
+
def close_issue_workflow(owner: str, repo: str, issue_number: str) -> str:
|
|
276
|
+
"""Guide through the complete workflow to close an issue via PR
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
owner: Repository owner
|
|
280
|
+
repo: Repository name
|
|
281
|
+
issue_number: Issue number to close
|
|
282
|
+
"""
|
|
283
|
+
return f"""Help me close GitHub issue #{issue_number} in {owner}/{repo} through the proper PR workflow:
|
|
284
|
+
|
|
285
|
+
1. Analyze the issue and gather context
|
|
286
|
+
2. Check for existing PRs that address this issue
|
|
287
|
+
3. Evaluate if any PRs are ready to merge
|
|
288
|
+
4. If PRs are ready and approved, merge them
|
|
289
|
+
5. Close the issue with an appropriate comment
|
|
290
|
+
6. Provide a summary of actions taken
|
|
291
|
+
|
|
292
|
+
Please be careful to verify that PRs have passed all checks and have proper approvals before merging. If anything is not ready, explain what needs to happen next."""
|
|
293
|
+
|
|
294
|
+
def main():
|
|
295
|
+
"""Entry point for the MCP server."""
|
|
296
|
+
mcp.run()
|
|
297
|
+
|
|
298
|
+
if __name__ == "__main__":
|
|
299
|
+
main()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: github-issue-pr-manager
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for managing GitHub issues and pull requests, providing AI agents with tools to gather context and close issues through PR workflows
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: fastmcp>=2.0.0
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# GitHub Issue PR Manager
|
|
10
|
+
|
|
11
|
+
MCP server for managing GitHub issues and pull requests, providing AI agents with tools to gather context and close issues through PR workflows
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uvx github-issue-pr-manager
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Tools
|
|
20
|
+
|
|
21
|
+
- `get_issue`: Get detailed information about a GitHub issue including comments and linked PRs
|
|
22
|
+
- `list_issue_prs`: List all pull requests that reference or are linked to a specific issue
|
|
23
|
+
- `get_pull_request`: Get detailed information about a pull request including its status and mergability
|
|
24
|
+
- `list_repository_prs`: List pull requests in a repository with optional filtering by state
|
|
25
|
+
- `create_pull_request`: Create a new pull request
|
|
26
|
+
- `merge_pull_request`: Merge a pull request if it's ready
|
|
27
|
+
- `close_issue`: Close an issue and optionally add a closing comment
|
|
28
|
+
- `add_issue_comment`: Add a comment to an issue
|
|
29
|
+
|
|
30
|
+
## Resources
|
|
31
|
+
|
|
32
|
+
- `docs://github-api/{endpoint}`: GitHub API documentation reference for issues and pull requests
|
|
33
|
+
- `guide://workflow`: Guide for using this MCP to close issues with PRs
|
|
34
|
+
|
|
35
|
+
## Prompts
|
|
36
|
+
|
|
37
|
+
- `analyze_issue`: Analyze an issue and determine what actions are needed to resolve it
|
|
38
|
+
- `close_issue_workflow`: Guide through the complete workflow to close an issue via PR
|
|
39
|
+
|
|
40
|
+
## Claude Desktop Configuration
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"github-issue-pr-manager": {
|
|
46
|
+
"command": "uvx",
|
|
47
|
+
"args": ["github-issue-pr-manager"]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
Generated with MCP Builder
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
github_issue_pr_manager/__init__.py,sha256=pYdESZlUIJYwcaQLSepearpHwYxEYTAvReJYgCTEpAc,234
|
|
2
|
+
github_issue_pr_manager/server.py,sha256=zLBLxdFcLxJr1SCLzCk65jHDI8bRZ1NwwNuE6nRlgc4,11102
|
|
3
|
+
github_issue_pr_manager-0.1.0.dist-info/METADATA,sha256=ovN9VftRCKyho2IwjF1OY7Dfx7NXr71tYqgg5xfGoSU,1751
|
|
4
|
+
github_issue_pr_manager-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
5
|
+
github_issue_pr_manager-0.1.0.dist-info/entry_points.txt,sha256=X7_hWYqtGIizRv6zdXU6pnG9lwrdo8PDmolzy38O-t4,73
|
|
6
|
+
github_issue_pr_manager-0.1.0.dist-info/RECORD,,
|