universal-mcp 0.1.8rc1__py3-none-any.whl → 0.1.8rc2__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.
- universal_mcp/applications/application.py +6 -5
- universal_mcp/applications/calendly/README.md +78 -0
- universal_mcp/applications/calendly/app.py +954 -0
- universal_mcp/applications/e2b/app.py +18 -12
- universal_mcp/applications/firecrawl/app.py +28 -1
- universal_mcp/applications/github/app.py +150 -107
- universal_mcp/applications/google_calendar/app.py +72 -137
- universal_mcp/applications/google_docs/app.py +35 -15
- universal_mcp/applications/google_drive/app.py +84 -55
- universal_mcp/applications/google_mail/app.py +143 -53
- universal_mcp/applications/google_sheet/app.py +61 -38
- universal_mcp/applications/markitdown/app.py +12 -11
- universal_mcp/applications/notion/app.py +199 -89
- universal_mcp/applications/perplexity/app.py +17 -15
- universal_mcp/applications/reddit/app.py +110 -101
- universal_mcp/applications/resend/app.py +14 -7
- universal_mcp/applications/serpapi/app.py +13 -6
- universal_mcp/applications/tavily/app.py +13 -10
- universal_mcp/applications/wrike/README.md +71 -0
- universal_mcp/applications/wrike/__init__.py +0 -0
- universal_mcp/applications/wrike/app.py +1044 -0
- universal_mcp/applications/youtube/README.md +82 -0
- universal_mcp/applications/youtube/__init__.py +0 -0
- universal_mcp/applications/youtube/app.py +986 -0
- universal_mcp/applications/zenquotes/app.py +13 -3
- universal_mcp/exceptions.py +8 -2
- universal_mcp/integrations/__init__.py +15 -1
- universal_mcp/integrations/integration.py +132 -27
- universal_mcp/servers/__init__.py +6 -15
- universal_mcp/servers/server.py +209 -153
- universal_mcp/stores/__init__.py +7 -2
- universal_mcp/stores/store.py +103 -42
- universal_mcp/tools/__init__.py +3 -0
- universal_mcp/tools/adapters.py +40 -0
- universal_mcp/tools/func_metadata.py +214 -0
- universal_mcp/tools/tools.py +285 -0
- universal_mcp/utils/docgen.py +277 -123
- universal_mcp/utils/docstring_parser.py +156 -0
- universal_mcp/utils/openapi.py +149 -40
- {universal_mcp-0.1.8rc1.dist-info → universal_mcp-0.1.8rc2.dist-info}/METADATA +7 -3
- universal_mcp-0.1.8rc2.dist-info/RECORD +71 -0
- universal_mcp-0.1.8rc1.dist-info/RECORD +0 -58
- /universal_mcp/{utils/bridge.py → applications/calendly/__init__.py} +0 -0
- {universal_mcp-0.1.8rc1.dist-info → universal_mcp-0.1.8rc2.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.8rc1.dist-info → universal_mcp-0.1.8rc2.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,8 @@
|
|
1
|
+
from typing import Annotated
|
2
|
+
|
1
3
|
from e2b_code_interpreter import Sandbox
|
2
4
|
from loguru import logger
|
3
|
-
|
5
|
+
|
4
6
|
from universal_mcp.applications.application import APIApplication
|
5
7
|
from universal_mcp.integrations import Integration
|
6
8
|
|
@@ -58,21 +60,25 @@ class E2BApp(APIApplication):
|
|
58
60
|
return "\n\n".join(output_parts)
|
59
61
|
|
60
62
|
def execute_python_code(
|
61
|
-
self,
|
62
|
-
|
63
|
+
self,
|
64
|
+
code: Annotated[str, "The Python code to execute."]
|
65
|
+
) -> str:
|
63
66
|
"""
|
64
|
-
Executes Python code
|
65
|
-
|
67
|
+
Executes Python code in a sandbox environment and returns the formatted output
|
68
|
+
|
66
69
|
Args:
|
67
|
-
code:
|
68
|
-
|
70
|
+
code: String containing the Python code to be executed in the sandbox
|
71
|
+
|
69
72
|
Returns:
|
70
|
-
A string containing the formatted
|
71
|
-
|
72
|
-
error message string is returned.
|
73
|
-
|
73
|
+
A string containing the formatted execution output/logs from running the code
|
74
|
+
|
74
75
|
Raises:
|
75
|
-
|
76
|
+
SandboxError: When there are issues with sandbox initialization or code execution
|
77
|
+
AuthenticationError: When API key authentication fails during sandbox setup
|
78
|
+
ValueError: When provided code string is empty or invalid
|
79
|
+
|
80
|
+
Tags:
|
81
|
+
execute, sandbox, code-execution, security, important
|
76
82
|
"""
|
77
83
|
self._set_api_key()
|
78
84
|
with Sandbox(api_key=self.api_key) as sandbox:
|
@@ -64,6 +64,9 @@ class FirecrawlApp(APIApplication):
|
|
64
64
|
Returns:
|
65
65
|
A dictionary containing the scraped data on success,
|
66
66
|
or a string containing an error message on failure.
|
67
|
+
|
68
|
+
Tags:
|
69
|
+
scrape, important
|
67
70
|
"""
|
68
71
|
try:
|
69
72
|
client = self._get_client()
|
@@ -86,6 +89,9 @@ class FirecrawlApp(APIApplication):
|
|
86
89
|
Returns:
|
87
90
|
A dictionary containing the search results on success,
|
88
91
|
or a string containing an error message on failure.
|
92
|
+
|
93
|
+
Tags:
|
94
|
+
search, important
|
89
95
|
"""
|
90
96
|
try:
|
91
97
|
client = self._get_client()
|
@@ -111,6 +117,9 @@ class FirecrawlApp(APIApplication):
|
|
111
117
|
Returns:
|
112
118
|
A dictionary containing the job initiation response on success,
|
113
119
|
or a string containing an error message on failure.
|
120
|
+
|
121
|
+
Tags:
|
122
|
+
crawl, async_job, start
|
114
123
|
"""
|
115
124
|
try:
|
116
125
|
client = self._get_client()
|
@@ -132,6 +141,9 @@ class FirecrawlApp(APIApplication):
|
|
132
141
|
Returns:
|
133
142
|
A dictionary containing the job status details on success,
|
134
143
|
or a string containing an error message on failure.
|
144
|
+
|
145
|
+
Tags:
|
146
|
+
crawl, async_job, status
|
135
147
|
"""
|
136
148
|
try:
|
137
149
|
client = self._get_client()
|
@@ -151,6 +163,9 @@ class FirecrawlApp(APIApplication):
|
|
151
163
|
Returns:
|
152
164
|
A dictionary confirming the cancellation status on success,
|
153
165
|
or a string containing an error message on failure.
|
166
|
+
|
167
|
+
Tags:
|
168
|
+
crawl, async_job, management, cancel
|
154
169
|
"""
|
155
170
|
try:
|
156
171
|
client = self._get_client()
|
@@ -178,6 +193,9 @@ class FirecrawlApp(APIApplication):
|
|
178
193
|
Returns:
|
179
194
|
A dictionary containing the job initiation response on success,
|
180
195
|
or a string containing an error message on failure.
|
196
|
+
|
197
|
+
Tags:
|
198
|
+
scrape, batch, async_job, start
|
181
199
|
"""
|
182
200
|
try:
|
183
201
|
client = self._get_client()
|
@@ -199,6 +217,9 @@ class FirecrawlApp(APIApplication):
|
|
199
217
|
Returns:
|
200
218
|
A dictionary containing the job status details on success,
|
201
219
|
or a string containing an error message on failure.
|
220
|
+
|
221
|
+
Tags:
|
222
|
+
scrape, batch, async_job, status
|
202
223
|
"""
|
203
224
|
try:
|
204
225
|
client = self._get_client()
|
@@ -225,6 +246,9 @@ class FirecrawlApp(APIApplication):
|
|
225
246
|
Returns:
|
226
247
|
A dictionary containing the job initiation response on success,
|
227
248
|
or a string containing an error message on failure.
|
249
|
+
|
250
|
+
Tags:
|
251
|
+
extract, ai, async_job, start
|
228
252
|
"""
|
229
253
|
|
230
254
|
try:
|
@@ -247,6 +271,9 @@ class FirecrawlApp(APIApplication):
|
|
247
271
|
Returns:
|
248
272
|
A dictionary containing the job status details on success,
|
249
273
|
or a string containing an error message on failure.
|
274
|
+
|
275
|
+
Tags:
|
276
|
+
extract, ai, async_job, status
|
250
277
|
"""
|
251
278
|
try:
|
252
279
|
client = self._get_client()
|
@@ -268,4 +295,4 @@ class FirecrawlApp(APIApplication):
|
|
268
295
|
self.check_batch_scrape_status,
|
269
296
|
self.start_extract,
|
270
297
|
self.check_extract_status,
|
271
|
-
]
|
298
|
+
]
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Any
|
1
|
+
from typing import Any, ClassVar
|
2
2
|
|
3
3
|
from loguru import logger
|
4
4
|
|
@@ -7,6 +7,9 @@ from universal_mcp.integrations import Integration
|
|
7
7
|
|
8
8
|
|
9
9
|
class GithubApp(APIApplication):
|
10
|
+
|
11
|
+
APP_TAGS: ClassVar[list[str]] = ["developers-tools"]
|
12
|
+
|
10
13
|
def __init__(self, integration: Integration) -> None:
|
11
14
|
super().__init__(name="github", integration=integration)
|
12
15
|
self.base_api_url = "https://api.github.com/repos"
|
@@ -23,18 +26,24 @@ class GithubApp(APIApplication):
|
|
23
26
|
}
|
24
27
|
|
25
28
|
def star_repository(self, repo_full_name: str) -> str:
|
26
|
-
"""
|
27
|
-
|
29
|
+
"""
|
30
|
+
Stars a GitHub repository using the GitHub API and returns a status message.
|
31
|
+
|
28
32
|
Args:
|
29
|
-
repo_full_name: The full name of the repository (e.g
|
30
|
-
|
33
|
+
repo_full_name: The full name of the repository in 'owner/repo' format (e.g., 'octocat/Hello-World')
|
34
|
+
|
31
35
|
Returns:
|
32
|
-
|
33
|
-
|
36
|
+
A string message indicating whether the starring operation was successful, the repository was not found, or an error occurred
|
37
|
+
|
38
|
+
Raises:
|
39
|
+
RequestException: If there are network connectivity issues or API request failures
|
40
|
+
ValueError: If the repository name format is invalid
|
41
|
+
|
42
|
+
Tags:
|
43
|
+
star, github, api, action, social, repository, important
|
34
44
|
"""
|
35
45
|
url = f"https://api.github.com/user/starred/{repo_full_name}"
|
36
46
|
response = self._put(url, data={})
|
37
|
-
|
38
47
|
if response.status_code == 204:
|
39
48
|
return f"Successfully starred repository {repo_full_name}"
|
40
49
|
elif response.status_code == 404:
|
@@ -44,23 +53,29 @@ class GithubApp(APIApplication):
|
|
44
53
|
return f"Error starring repository: {response.text}"
|
45
54
|
|
46
55
|
def list_commits(self, repo_full_name: str) -> str:
|
47
|
-
"""
|
48
|
-
|
56
|
+
"""
|
57
|
+
Retrieves and formats a list of recent commits from a GitHub repository
|
58
|
+
|
49
59
|
Args:
|
50
|
-
repo_full_name: The full name of the repository
|
51
|
-
|
60
|
+
repo_full_name: The full name of the repository in 'owner/repo' format
|
61
|
+
|
52
62
|
Returns:
|
53
|
-
A formatted
|
63
|
+
A formatted string containing the most recent 12 commits, including commit hash, message, and author
|
64
|
+
|
65
|
+
Raises:
|
66
|
+
requests.exceptions.HTTPError: When the GitHub API request fails (e.g., repository not found, rate limit exceeded)
|
67
|
+
requests.exceptions.RequestException: When network issues or other request-related problems occur
|
68
|
+
|
69
|
+
Tags:
|
70
|
+
list, read, commits, github, history, api, important
|
54
71
|
"""
|
55
72
|
repo_full_name = repo_full_name.strip()
|
56
73
|
url = f"{self.base_api_url}/{repo_full_name}/commits"
|
57
74
|
response = self._get(url)
|
58
75
|
response.raise_for_status()
|
59
|
-
|
60
76
|
commits = response.json()
|
61
77
|
if not commits:
|
62
78
|
return f"No commits found for repository {repo_full_name}"
|
63
|
-
|
64
79
|
result = f"Recent commits for {repo_full_name}:\n\n"
|
65
80
|
for commit in commits[:12]: # Limit to 12 commits
|
66
81
|
sha = commit.get("sha", "")[:7]
|
@@ -68,54 +83,63 @@ class GithubApp(APIApplication):
|
|
68
83
|
author = commit.get("commit", {}).get("author", {}).get("name", "Unknown")
|
69
84
|
|
70
85
|
result += f"- {sha}: {message} (by {author})\n"
|
71
|
-
|
72
86
|
return result
|
73
87
|
|
74
88
|
def list_branches(self, repo_full_name: str) -> str:
|
75
|
-
"""
|
76
|
-
|
89
|
+
"""
|
90
|
+
Lists all branches for a specified GitHub repository and returns them in a formatted string representation.
|
91
|
+
|
77
92
|
Args:
|
78
|
-
repo_full_name: The full name of the repository (e.g
|
79
|
-
|
93
|
+
repo_full_name: The full name of the repository in 'owner/repo' format (e.g., 'octocat/Hello-World')
|
94
|
+
|
80
95
|
Returns:
|
81
|
-
A formatted list of branches
|
96
|
+
A formatted string containing the list of branches, or a message indicating no branches were found
|
97
|
+
|
98
|
+
Raises:
|
99
|
+
HTTPError: When the GitHub API request fails (e.g., repository not found, authentication error)
|
100
|
+
RequestException: When there are network connectivity issues or API communication problems
|
101
|
+
|
102
|
+
Tags:
|
103
|
+
list, branches, github, read, api, repository, important
|
82
104
|
"""
|
83
105
|
repo_full_name = repo_full_name.strip()
|
84
106
|
url = f"{self.base_api_url}/{repo_full_name}/branches"
|
85
107
|
response = self._get(url)
|
86
108
|
response.raise_for_status()
|
87
|
-
|
88
109
|
branches = response.json()
|
89
110
|
if not branches:
|
90
111
|
return f"No branches found for repository {repo_full_name}"
|
91
|
-
|
92
112
|
result = f"Branches for {repo_full_name}:\n\n"
|
93
113
|
for branch in branches:
|
94
114
|
branch_name = branch.get("name", "Unknown")
|
95
115
|
result += f"- {branch_name}\n"
|
96
|
-
|
97
116
|
return result
|
98
117
|
|
99
118
|
def list_pull_requests(self, repo_full_name: str, state: str = "open") -> str:
|
100
|
-
"""
|
101
|
-
|
119
|
+
"""
|
120
|
+
Retrieves and formats a list of pull requests for a specified GitHub repository.
|
121
|
+
|
102
122
|
Args:
|
103
|
-
repo_full_name: The full name of the repository (e.g
|
104
|
-
state:
|
105
|
-
|
123
|
+
repo_full_name: The full name of the repository in the format 'owner/repo' (e.g., 'tensorflow/tensorflow')
|
124
|
+
state: Filter for pull request state. Can be 'open', 'closed', or 'all'. Defaults to 'open'
|
125
|
+
|
106
126
|
Returns:
|
107
|
-
A formatted list of pull requests
|
127
|
+
A formatted string containing the list of pull requests, including PR number, title, author, and status. Returns a message if no pull requests are found.
|
128
|
+
|
129
|
+
Raises:
|
130
|
+
HTTPError: Raised when the GitHub API request fails (e.g., invalid repository name, rate limiting, or authentication issues)
|
131
|
+
|
132
|
+
Tags:
|
133
|
+
list, pull-request, github, api, read, important, fetch, query
|
108
134
|
"""
|
109
135
|
repo_full_name = repo_full_name.strip()
|
110
136
|
url = f"{self.base_api_url}/{repo_full_name}/pulls"
|
111
137
|
params = {"state": state}
|
112
138
|
response = self._get(url, params=params)
|
113
139
|
response.raise_for_status()
|
114
|
-
|
115
140
|
pull_requests = response.json()
|
116
141
|
if not pull_requests:
|
117
142
|
return f"No pull requests found for repository {repo_full_name} with state '{state}'"
|
118
|
-
|
119
143
|
result = f"Pull requests for {repo_full_name} (State: {state}):\n\n"
|
120
144
|
for pr in pull_requests:
|
121
145
|
pr_title = pr.get("title", "No Title")
|
@@ -126,7 +150,6 @@ class GithubApp(APIApplication):
|
|
126
150
|
result += (
|
127
151
|
f"- PR #{pr_number}: {pr_title} (by {pr_user}, Status: {pr_state})\n"
|
128
152
|
)
|
129
|
-
|
130
153
|
return result
|
131
154
|
|
132
155
|
def list_issues(
|
@@ -138,62 +161,72 @@ class GithubApp(APIApplication):
|
|
138
161
|
per_page: int = 30,
|
139
162
|
page: int = 1,
|
140
163
|
) -> list[dict[str, Any]]:
|
141
|
-
"""
|
142
|
-
|
164
|
+
"""
|
165
|
+
Retrieves a list of issues from a specified GitHub repository with optional filtering parameters.
|
166
|
+
|
143
167
|
Args:
|
144
|
-
repo_full_name: The full name of the repository
|
145
|
-
state:
|
146
|
-
assignee: Filter by assignee. Use 'none' for
|
147
|
-
labels: Comma-separated
|
148
|
-
per_page:
|
149
|
-
page:
|
150
|
-
|
168
|
+
repo_full_name: The full name of the repository in 'owner/repo' format
|
169
|
+
state: Filter issues by state ('open', 'closed', 'all'). Defaults to 'open'
|
170
|
+
assignee: Filter by assignee username. Use 'none' for unassigned issues, '*' for assigned issues
|
171
|
+
labels: Comma-separated string of label names to filter by (e.g., 'bug,ui,@high')
|
172
|
+
per_page: Number of results per page (max 100). Defaults to 30
|
173
|
+
page: Page number for pagination. Defaults to 1
|
174
|
+
|
151
175
|
Returns:
|
152
|
-
|
176
|
+
List of dictionaries containing issue data from the GitHub API response
|
177
|
+
|
178
|
+
Raises:
|
179
|
+
HTTPError: When the GitHub API request fails (e.g., invalid repository name, authentication failure)
|
180
|
+
RequestException: When there are network connectivity issues or other request-related problems
|
181
|
+
|
182
|
+
Tags:
|
183
|
+
list, issues, github, api, read, filter, pagination, important, project-management
|
153
184
|
"""
|
154
185
|
repo_full_name = repo_full_name.strip()
|
155
186
|
url = f"{self.base_api_url}/{repo_full_name}/issues"
|
156
|
-
|
157
187
|
params = {"state": state, "per_page": per_page, "page": page}
|
158
|
-
|
159
188
|
if assignee:
|
160
189
|
params["assignee"] = assignee
|
161
190
|
if labels:
|
162
191
|
params["labels"] = labels
|
163
|
-
|
164
192
|
response = self._get(url, params=params)
|
165
193
|
response.raise_for_status()
|
166
194
|
return response.json()
|
167
195
|
|
168
196
|
def get_pull_request(self, repo_full_name: str, pull_number: int) -> str:
|
169
|
-
"""
|
170
|
-
|
197
|
+
"""
|
198
|
+
Retrieves and formats detailed information about a specific GitHub pull request from a repository
|
199
|
+
|
171
200
|
Args:
|
172
|
-
repo_full_name: The full name
|
173
|
-
pull_number: The
|
174
|
-
|
201
|
+
repo_full_name: The full repository name in 'owner/repo' format (e.g., 'octocat/Hello-World')
|
202
|
+
pull_number: The numeric identifier of the pull request to retrieve
|
203
|
+
|
175
204
|
Returns:
|
176
|
-
A formatted string
|
205
|
+
A formatted string containing pull request details including title, creator, status, and description
|
206
|
+
|
207
|
+
Raises:
|
208
|
+
HTTPError: Raised when the GitHub API request fails (e.g., invalid repository name, non-existent PR number, or authentication issues)
|
209
|
+
RequestException: Raised when there are network connectivity issues or other request-related problems
|
210
|
+
|
211
|
+
Tags:
|
212
|
+
get, read, github, pull-request, api, fetch, format, important
|
177
213
|
"""
|
178
214
|
repo_full_name = repo_full_name.strip()
|
179
215
|
url = f"{self.base_api_url}/{repo_full_name}/pulls/{pull_number}"
|
180
216
|
response = self._get(url)
|
181
217
|
response.raise_for_status()
|
182
|
-
|
183
218
|
pr = response.json()
|
184
219
|
pr_title = pr.get("title", "No Title")
|
185
220
|
pr_number = pr.get("number", "Unknown")
|
186
221
|
pr_state = pr.get("state", "Unknown")
|
187
222
|
pr_user = pr.get("user", {}).get("login", "Unknown")
|
188
223
|
pr_body = pr.get("body", "No description provided.")
|
189
|
-
|
190
224
|
result = (
|
191
225
|
f"Pull Request #{pr_number}: {pr_title}\n"
|
192
226
|
f"Created by: {pr_user}\n"
|
193
227
|
f"Status: {pr_state}\n"
|
194
228
|
f"Description: {pr_body}\n"
|
195
229
|
)
|
196
|
-
|
197
230
|
return result
|
198
231
|
|
199
232
|
def create_pull_request(
|
@@ -207,32 +240,37 @@ class GithubApp(APIApplication):
|
|
207
240
|
maintainer_can_modify: bool = True,
|
208
241
|
draft: bool = False,
|
209
242
|
) -> dict[str, Any]:
|
210
|
-
"""
|
211
|
-
|
243
|
+
"""
|
244
|
+
Creates a new pull request in a GitHub repository, optionally converting an existing issue into a pull request.
|
245
|
+
|
212
246
|
Args:
|
213
247
|
repo_full_name: The full name of the repository (e.g. 'owner/repo')
|
214
248
|
head: The name of the branch where your changes are implemented
|
215
249
|
base: The name of the branch you want the changes pulled into
|
216
250
|
title: The title of the new pull request (required if issue is not specified)
|
217
251
|
body: The contents of the pull request
|
218
|
-
issue: An issue number to convert to a pull request. If specified, the issue's
|
219
|
-
title, body, and comments will be used for the pull request
|
252
|
+
issue: An issue number to convert to a pull request. If specified, the issue's title, body, and comments will be used
|
220
253
|
maintainer_can_modify: Indicates whether maintainers can modify the pull request
|
221
254
|
draft: Indicates whether the pull request is a draft
|
222
|
-
|
255
|
+
|
223
256
|
Returns:
|
224
|
-
|
257
|
+
A dictionary containing the complete GitHub API response
|
258
|
+
|
259
|
+
Raises:
|
260
|
+
ValueError: Raised when neither 'title' nor 'issue' parameter is specified
|
261
|
+
HTTPError: Raised when the GitHub API request fails
|
262
|
+
|
263
|
+
Tags:
|
264
|
+
create, pull-request, github, api, write, important
|
225
265
|
"""
|
226
266
|
repo_full_name = repo_full_name.strip()
|
227
267
|
url = f"{self.base_api_url}/{repo_full_name}/pulls"
|
228
|
-
|
229
268
|
pull_request_data = {
|
230
269
|
"head": head,
|
231
270
|
"base": base,
|
232
271
|
"maintainer_can_modify": maintainer_can_modify,
|
233
272
|
"draft": draft,
|
234
273
|
}
|
235
|
-
|
236
274
|
if issue is not None:
|
237
275
|
pull_request_data["issue"] = issue
|
238
276
|
else:
|
@@ -241,7 +279,6 @@ class GithubApp(APIApplication):
|
|
241
279
|
pull_request_data["title"] = title
|
242
280
|
if body is not None:
|
243
281
|
pull_request_data["body"] = body
|
244
|
-
|
245
282
|
response = self._post(url, pull_request_data)
|
246
283
|
response.raise_for_status()
|
247
284
|
return response.json()
|
@@ -249,25 +286,27 @@ class GithubApp(APIApplication):
|
|
249
286
|
def create_issue(
|
250
287
|
self, repo_full_name: str, title: str, body: str = "", labels=None
|
251
288
|
) -> str:
|
252
|
-
"""
|
253
|
-
|
289
|
+
"""
|
290
|
+
Creates a new issue in a specified GitHub repository with a title, body content, and optional labels.
|
291
|
+
|
254
292
|
Args:
|
255
|
-
repo_full_name: The full name of the repository
|
293
|
+
repo_full_name: The full name of the repository in 'owner/repo' format
|
256
294
|
title: The title of the issue
|
257
|
-
body: The contents of the issue
|
258
|
-
labels: Labels to associate with
|
259
|
-
|
260
|
-
NOTE: Only users with push access can set labels for new issues.
|
261
|
-
Labels are silently dropped otherwise.
|
262
|
-
|
295
|
+
body: The contents/description of the issue (defaults to empty string)
|
296
|
+
labels: Labels to associate with the issue, as a comma-separated string or list. Only users with push access can set labels
|
297
|
+
|
263
298
|
Returns:
|
264
|
-
A confirmation message with the
|
299
|
+
A string containing a confirmation message with the issue number, title, and URL
|
300
|
+
|
301
|
+
Raises:
|
302
|
+
HTTPError: When the GitHub API request fails (e.g., invalid repository name, authentication issues, or insufficient permissions)
|
303
|
+
|
304
|
+
Tags:
|
305
|
+
create, issues, github, api, project-management, write, important
|
265
306
|
"""
|
266
307
|
repo_full_name = repo_full_name.strip()
|
267
308
|
url = f"{self.base_api_url}/{repo_full_name}/issues"
|
268
|
-
|
269
309
|
issue_data = {"title": title, "body": body}
|
270
|
-
|
271
310
|
if labels:
|
272
311
|
if isinstance(labels, str):
|
273
312
|
labels_list = [
|
@@ -276,14 +315,11 @@ class GithubApp(APIApplication):
|
|
276
315
|
issue_data["labels"] = labels_list
|
277
316
|
else:
|
278
317
|
issue_data["labels"] = labels
|
279
|
-
|
280
318
|
response = self._post(url, issue_data)
|
281
319
|
response.raise_for_status()
|
282
|
-
|
283
320
|
issue = response.json()
|
284
321
|
issue_number = issue.get("number", "Unknown")
|
285
322
|
issue_url = issue.get("html_url", "")
|
286
|
-
|
287
323
|
return (
|
288
324
|
f"Successfully created issue #{issue_number}:\n"
|
289
325
|
f"Title: {title}\n"
|
@@ -293,31 +329,33 @@ class GithubApp(APIApplication):
|
|
293
329
|
def list_repo_activities(
|
294
330
|
self, repo_full_name: str, direction: str = "desc", per_page: int = 30
|
295
331
|
) -> str:
|
296
|
-
"""
|
297
|
-
|
332
|
+
"""
|
333
|
+
Retrieves and formats a list of activities for a specified GitHub repository.
|
334
|
+
|
298
335
|
Args:
|
299
|
-
repo_full_name: The full name of the repository
|
300
|
-
direction: The direction
|
301
|
-
per_page:
|
302
|
-
|
336
|
+
repo_full_name: The full name of the repository in 'owner/repo' format
|
337
|
+
direction: The sort direction for results ('asc' or 'desc'). Defaults to 'desc'
|
338
|
+
per_page: Number of activities to return per page (1-100). Defaults to 30
|
339
|
+
|
303
340
|
Returns:
|
304
|
-
A formatted list of repository activities
|
341
|
+
A formatted string containing a list of repository activities, including timestamps and actor names. Returns a 'No activities' message if no activities are found.
|
342
|
+
|
343
|
+
Raises:
|
344
|
+
HTTPError: Raised when the GitHub API request fails
|
345
|
+
ValueError: May be raised if repo_full_name is invalid or empty after stripping
|
346
|
+
|
347
|
+
Tags:
|
348
|
+
list, activity, github, read, events, api, query, format
|
305
349
|
"""
|
306
350
|
repo_full_name = repo_full_name.strip()
|
307
351
|
url = f"{self.base_api_url}/{repo_full_name}/activity"
|
308
|
-
|
309
|
-
# Build query parameters
|
310
352
|
params = {"direction": direction, "per_page": per_page}
|
311
|
-
|
312
353
|
response = self._get(url, params=params)
|
313
354
|
response.raise_for_status()
|
314
|
-
|
315
355
|
activities = response.json()
|
316
356
|
if not activities:
|
317
357
|
return f"No activities found for repository {repo_full_name}"
|
318
|
-
|
319
358
|
result = f"Repository activities for {repo_full_name}:\n\n"
|
320
|
-
|
321
359
|
for activity in activities:
|
322
360
|
# Extract common fields
|
323
361
|
timestamp = activity.get("timestamp", "Unknown time")
|
@@ -327,7 +365,6 @@ class GithubApp(APIApplication):
|
|
327
365
|
|
328
366
|
# Create a simple description of the activity
|
329
367
|
result += f"- {actor_name} performed an activity at {timestamp}\n"
|
330
|
-
|
331
368
|
return result
|
332
369
|
|
333
370
|
def update_issue(
|
@@ -340,22 +377,29 @@ class GithubApp(APIApplication):
|
|
340
377
|
state: str = None,
|
341
378
|
state_reason: str = None,
|
342
379
|
) -> dict[str, Any]:
|
343
|
-
"""
|
344
|
-
|
380
|
+
"""
|
381
|
+
Updates an existing GitHub issue with specified parameters including title, body, assignee, state, and state reason.
|
382
|
+
|
345
383
|
Args:
|
346
|
-
repo_full_name: The full name of the repository
|
347
|
-
issue_number: The number
|
348
|
-
title: The title of the issue
|
349
|
-
body: The
|
350
|
-
assignee:
|
351
|
-
state:
|
352
|
-
state_reason:
|
353
|
-
|
384
|
+
repo_full_name: The full name of the repository in 'owner/repo' format
|
385
|
+
issue_number: The unique identifier number of the issue to update
|
386
|
+
title: The new title of the issue (optional)
|
387
|
+
body: The new content/description of the issue (optional)
|
388
|
+
assignee: GitHub username to assign to the issue (optional)
|
389
|
+
state: The desired state of the issue ('open' or 'closed') (optional)
|
390
|
+
state_reason: The reason for state change ('completed', 'not_planned', 'reopened', or null) (optional)
|
391
|
+
|
354
392
|
Returns:
|
355
|
-
|
393
|
+
A dictionary containing the complete GitHub API response with updated issue details
|
394
|
+
|
395
|
+
Raises:
|
396
|
+
HTTPError: Raised when the GitHub API request fails (e.g., invalid repository, non-existent issue, insufficient permissions)
|
397
|
+
RequestException: Raised when there's a network error or API connectivity issue
|
398
|
+
|
399
|
+
Tags:
|
400
|
+
github, issues, update, api, project-management, write, important
|
356
401
|
"""
|
357
402
|
url = f"{self.base_api_url}/{repo_full_name}/issues/{issue_number}"
|
358
|
-
|
359
403
|
update_data = {}
|
360
404
|
if title is not None:
|
361
405
|
update_data["title"] = title
|
@@ -367,7 +411,6 @@ class GithubApp(APIApplication):
|
|
367
411
|
update_data["state"] = state
|
368
412
|
if state_reason is not None:
|
369
413
|
update_data["state_reason"] = state_reason
|
370
|
-
|
371
414
|
response = self._patch(url, update_data)
|
372
415
|
response.raise_for_status()
|
373
416
|
return response.json()
|
@@ -384,4 +427,4 @@ class GithubApp(APIApplication):
|
|
384
427
|
self.create_issue,
|
385
428
|
self.update_issue,
|
386
429
|
self.list_repo_activities,
|
387
|
-
]
|
430
|
+
]
|