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.
Files changed (45) hide show
  1. universal_mcp/applications/application.py +6 -5
  2. universal_mcp/applications/calendly/README.md +78 -0
  3. universal_mcp/applications/calendly/app.py +954 -0
  4. universal_mcp/applications/e2b/app.py +18 -12
  5. universal_mcp/applications/firecrawl/app.py +28 -1
  6. universal_mcp/applications/github/app.py +150 -107
  7. universal_mcp/applications/google_calendar/app.py +72 -137
  8. universal_mcp/applications/google_docs/app.py +35 -15
  9. universal_mcp/applications/google_drive/app.py +84 -55
  10. universal_mcp/applications/google_mail/app.py +143 -53
  11. universal_mcp/applications/google_sheet/app.py +61 -38
  12. universal_mcp/applications/markitdown/app.py +12 -11
  13. universal_mcp/applications/notion/app.py +199 -89
  14. universal_mcp/applications/perplexity/app.py +17 -15
  15. universal_mcp/applications/reddit/app.py +110 -101
  16. universal_mcp/applications/resend/app.py +14 -7
  17. universal_mcp/applications/serpapi/app.py +13 -6
  18. universal_mcp/applications/tavily/app.py +13 -10
  19. universal_mcp/applications/wrike/README.md +71 -0
  20. universal_mcp/applications/wrike/__init__.py +0 -0
  21. universal_mcp/applications/wrike/app.py +1044 -0
  22. universal_mcp/applications/youtube/README.md +82 -0
  23. universal_mcp/applications/youtube/__init__.py +0 -0
  24. universal_mcp/applications/youtube/app.py +986 -0
  25. universal_mcp/applications/zenquotes/app.py +13 -3
  26. universal_mcp/exceptions.py +8 -2
  27. universal_mcp/integrations/__init__.py +15 -1
  28. universal_mcp/integrations/integration.py +132 -27
  29. universal_mcp/servers/__init__.py +6 -15
  30. universal_mcp/servers/server.py +209 -153
  31. universal_mcp/stores/__init__.py +7 -2
  32. universal_mcp/stores/store.py +103 -42
  33. universal_mcp/tools/__init__.py +3 -0
  34. universal_mcp/tools/adapters.py +40 -0
  35. universal_mcp/tools/func_metadata.py +214 -0
  36. universal_mcp/tools/tools.py +285 -0
  37. universal_mcp/utils/docgen.py +277 -123
  38. universal_mcp/utils/docstring_parser.py +156 -0
  39. universal_mcp/utils/openapi.py +149 -40
  40. {universal_mcp-0.1.8rc1.dist-info → universal_mcp-0.1.8rc2.dist-info}/METADATA +7 -3
  41. universal_mcp-0.1.8rc2.dist-info/RECORD +71 -0
  42. universal_mcp-0.1.8rc1.dist-info/RECORD +0 -58
  43. /universal_mcp/{utils/bridge.py → applications/calendly/__init__.py} +0 -0
  44. {universal_mcp-0.1.8rc1.dist-info → universal_mcp-0.1.8rc2.dist-info}/WHEEL +0 -0
  45. {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
- from typing import Annotated
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, code: Annotated[str, "The Python code to execute."]
62
- ) -> str:
63
+ self,
64
+ code: Annotated[str, "The Python code to execute."]
65
+ ) -> str:
63
66
  """
64
- Executes Python code within a secure E2B cloud sandbox.
65
-
67
+ Executes Python code in a sandbox environment and returns the formatted output
68
+
66
69
  Args:
67
- code: The string containing the Python code to execute.
68
-
70
+ code: String containing the Python code to be executed in the sandbox
71
+
69
72
  Returns:
70
- A string containing the formatted standard output (stdout) and standard error (stderr)
71
- from the execution. If an error occurs during setup or execution, an
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
- NotAuthorizedError: If the API key is not set.
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
- """Star a GitHub repository
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. 'owner/repo')
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
- A confirmation message
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
- """List recent commits for a GitHub repository
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 (e.g. 'owner/repo')
51
-
60
+ repo_full_name: The full name of the repository in 'owner/repo' format
61
+
52
62
  Returns:
53
- A formatted list of recent commits
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
- """List branches for a GitHub repository
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. 'owner/repo')
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
- """List pull requests for a GitHub repository
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. 'owner/repo')
104
- state: The state of the pull requests to filter by (open, closed, or all)
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
- """List issues for a GitHub repository
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 (e.g. 'owner/repo')
145
- state: State of issues to return (open, closed, all). Default: open
146
- assignee: Filter by assignee. Use 'none' for no assignee, '*' for any assignee
147
- labels: Comma-separated list of label names (e.g. "bug,ui,@high")
148
- per_page: The number of results per page (max 100)
149
- page: The page number of the results to fetch
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
- The complete GitHub API response
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
- """Get a specific pull request for a GitHub repository
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 of the repository (e.g. 'owner/repo')
173
- pull_number: The number of the pull request to retrieve
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 with pull request details
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
- """Create a new pull request for a GitHub repository
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
- The complete GitHub API response
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
- """Create a new issue in a GitHub repository
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 (e.g. 'owner/repo')
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 this issue. Enter as a comma-separated string
259
- (e.g. "bug,enhancement,documentation").
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 new issue details
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
- """List activities for a GitHub repository
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 (e.g. 'owner/repo')
300
- direction: The direction to sort the results by (asc or desc). Default: desc
301
- per_page: The number of results per page (max 100). Default: 30
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
- """Update an issue in a GitHub repository
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 (e.g. 'owner/repo')
347
- issue_number: The number that identifies the issue
348
- title: The title of the issue
349
- body: The contents of the issue
350
- assignee: Username to assign to this issue
351
- state: State of the issue (open or closed)
352
- state_reason: Reason for state change (completed, not_planned, reopened, null)
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
- The complete GitHub API response
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
+ ]