github-agent 0.2.55__tar.gz → 0.3.0__tar.gz

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 (35) hide show
  1. {github_agent-0.2.55 → github_agent-0.3.0}/PKG-INFO +6 -3
  2. {github_agent-0.2.55 → github_agent-0.3.0}/README.md +1 -1
  3. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent/agent_server.py +2 -3
  4. github_agent-0.2.55/github_agent/api_wrapper.py → github_agent-0.3.0/github_agent/api_client.py +46 -44
  5. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent/auth.py +8 -8
  6. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent/github_input_models.py +33 -33
  7. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent/github_response_models.py +45 -44
  8. github_agent-0.3.0/github_agent/mcp_config.json +3 -0
  9. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent/mcp_server.py +35 -27
  10. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent.egg-info/PKG-INFO +6 -3
  11. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent.egg-info/SOURCES.txt +5 -12
  12. github_agent-0.3.0/github_agent.egg-info/requires.txt +5 -0
  13. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent.egg-info/top_level.txt +1 -0
  14. github_agent-0.3.0/pyproject.toml +47 -0
  15. github_agent-0.3.0/tests/test_github_agent_api_brute_force_coverage.py +107 -0
  16. github_agent-0.3.0/tests/test_github_agent_brute_force_coverage.py +155 -0
  17. github_agent-0.2.55/github_agent/agent_data/CRON.md +0 -12
  18. github_agent-0.2.55/github_agent/agent_data/CRON_LOG.md +0 -4
  19. github_agent-0.2.55/github_agent/agent_data/HEARTBEAT.md +0 -31
  20. github_agent-0.2.55/github_agent/agent_data/IDENTITY.md +0 -48
  21. github_agent-0.2.55/github_agent/agent_data/MEMORY.md +0 -8
  22. github_agent-0.2.55/github_agent/agent_data/NODE_AGENTS.md +0 -55
  23. github_agent-0.2.55/github_agent/agent_data/USER.md +0 -7
  24. github_agent-0.2.55/github_agent/agent_data/chats +0 -3
  25. github_agent-0.2.55/github_agent/agent_data/icon.png +0 -3
  26. github_agent-0.2.55/github_agent/agent_data/mcp_config.json +0 -18
  27. github_agent-0.2.55/github_agent.egg-info/requires.txt +0 -1
  28. github_agent-0.2.55/pyproject.toml +0 -33
  29. {github_agent-0.2.55 → github_agent-0.3.0}/LICENSE +0 -0
  30. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent/__init__.py +0 -0
  31. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent/__main__.py +0 -0
  32. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent.egg-info/dependency_links.txt +0 -0
  33. {github_agent-0.2.55 → github_agent-0.3.0}/github_agent.egg-info/entry_points.txt +0 -0
  34. {github_agent-0.2.55 → github_agent-0.3.0}/scripts/validate_a2a_agent.py +2 -2
  35. {github_agent-0.2.55 → github_agent-0.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-agent
3
- Version: 0.2.55
3
+ Version: 0.3.0
4
4
  Summary: GitHub Agent for MCP
5
5
  Author-email: Audel Rouhi <knucklessg1@gmail.com>
6
6
  License: MIT
@@ -12,7 +12,10 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: agent-utilities[agent,logfire]>=0.2.31
15
+ Requires-Dist: agent-utilities[agent,logfire]>=0.2.42
16
+ Provides-Extra: test
17
+ Requires-Dist: pytest; extra == "test"
18
+ Requires-Dist: pytest-asyncio; extra == "test"
16
19
  Dynamic: license-file
17
20
 
18
21
  # GitHub Agent - A2A | AG-UI | MCP
@@ -38,7 +41,7 @@ Dynamic: license-file
38
41
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/github-agent)
39
42
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/github-agent)
40
43
 
41
- *Version: 0.2.55*
44
+ *Version: 0.3.0*
42
45
 
43
46
  ## Overview
44
47
 
@@ -21,7 +21,7 @@
21
21
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/github-agent)
22
22
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/github-agent)
23
23
 
24
- *Version: 0.2.55*
24
+ *Version: 0.3.0*
25
25
 
26
26
  ## Overview
27
27
 
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/python
2
- # coding: utf-8
2
+ import logging
3
3
  import os
4
4
  import sys
5
- import logging
6
5
  import warnings
7
6
 
8
7
  from agent_utilities import (
@@ -13,7 +12,7 @@ from agent_utilities import (
13
12
  load_identity,
14
13
  )
15
14
 
16
- __version__ = "0.2.55"
15
+ __version__ = "0.3.0"
17
16
 
18
17
  logging.basicConfig(
19
18
  level=logging.INFO,
@@ -1,51 +1,52 @@
1
1
  #!/usr/bin/python
2
2
 
3
+ import logging
3
4
  import re
5
+ from concurrent.futures import ThreadPoolExecutor, as_completed
6
+ from typing import TypeVar
7
+
4
8
  import requests
5
9
  import urllib3
6
- import logging
7
- from typing import List, TypeVar, Tuple
8
- from pydantic import ValidationError
9
- from concurrent.futures import ThreadPoolExecutor, as_completed
10
10
  from agent_utilities.base_utilities import get_logger
11
+ from pydantic import BaseModel, ValidationError
11
12
 
12
13
  logger = get_logger(__name__)
13
14
 
15
+ from agent_utilities.core.decorators import require_auth
16
+ from agent_utilities.core.exceptions import (
17
+ AuthError,
18
+ MissingParameterError,
19
+ ParameterError,
20
+ UnauthorizedError,
21
+ )
22
+
14
23
  from github_agent.github_input_models import (
15
- RepoModel,
16
- IssueModel,
17
- PullRequestModel,
18
- ContentModel,
19
24
  BranchModel,
20
25
  CommitModel,
26
+ ContentModel,
27
+ IssueModel,
28
+ PullRequestModel,
29
+ RepoModel,
21
30
  )
22
31
  from github_agent.github_response_models import (
23
- Repository,
24
- Issue,
25
- PullRequest,
26
- Content,
27
32
  Branch,
28
33
  Commit,
34
+ Content,
35
+ Issue,
36
+ PullRequest,
37
+ Repository,
29
38
  Response,
30
39
  )
31
- from agent_utilities.decorators import require_auth
32
- from agent_utilities.exceptions import (
33
- AuthError,
34
- UnauthorizedError,
35
- ParameterError,
36
- MissingParameterError,
37
- )
38
-
39
- T = TypeVar("T")
40
40
 
41
+ T = TypeVar("T", bound=BaseModel)
41
42
 
42
- class Api(object):
43
43
 
44
+ class Api:
44
45
  def __init__(
45
46
  self,
46
47
  url: str = "https://api.github.com",
47
- token: str = None,
48
- proxies: dict = None,
48
+ token: str | None = None,
49
+ proxies: dict | None = None,
49
50
  verify: bool = True,
50
51
  debug: bool = False,
51
52
  ):
@@ -73,7 +74,6 @@ class Api(object):
73
74
  if token:
74
75
  self.headers["Authorization"] = f"Bearer {token}"
75
76
  else:
76
-
77
77
  logger.warning("No token provided for GitHub API")
78
78
 
79
79
  try:
@@ -84,20 +84,20 @@ class Api(object):
84
84
  proxies=self.proxies,
85
85
  )
86
86
  if response.status_code in (401, 403):
87
- logger.error(f"Authentication Error: {response.content}")
87
+ logger.error(f"Authentication Error: {response.text}")
88
88
  raise AuthError if response.status_code == 401 else UnauthorizedError
89
89
  except requests.exceptions.RequestException as e:
90
90
  logger.error(f"Connection Error: {str(e)}")
91
91
 
92
92
  def _fetch_next_page(
93
93
  self, endpoint: str, model: T, header: dict, page: int
94
- ) -> List[dict]:
94
+ ) -> list[dict]:
95
95
  """Fetch a single page of data from the specified endpoint"""
96
- model.page = page
96
+ model.page = page # type: ignore[attr-defined]
97
97
  model.model_post_init(None)
98
98
  response = self._session.get(
99
99
  url=f"{self.url}{endpoint}" if endpoint.startswith("/") else endpoint,
100
- params=model.api_parameters,
100
+ params=model.api_parameters, # type: ignore[attr-defined]
101
101
  headers=header,
102
102
  verify=self.verify,
103
103
  proxies=self.proxies,
@@ -119,7 +119,7 @@ class Api(object):
119
119
 
120
120
  def _fetch_all_pages(
121
121
  self, endpoint: str, model: T
122
- ) -> Tuple[requests.Response, List[dict]]:
122
+ ) -> tuple[requests.Response, list[dict]]:
123
123
  """Generic method to fetch all pages with parallelization if possible"""
124
124
  all_data = []
125
125
 
@@ -127,7 +127,7 @@ class Api(object):
127
127
 
128
128
  response = self._session.get(
129
129
  url=initial_url,
130
- params=model.api_parameters,
130
+ params=model.api_parameters, # type: ignore[attr-defined]
131
131
  headers=self.headers,
132
132
  verify=self.verify,
133
133
  proxies=self.proxies,
@@ -142,14 +142,15 @@ class Api(object):
142
142
 
143
143
  total_pages = self._get_total_pages(response)
144
144
 
145
- if not model.max_pages or model.max_pages == 0 or model.max_pages > total_pages:
146
- model.max_pages = total_pages
147
-
148
- if model.max_pages > 1:
145
+ max_pages = getattr(model, "max_pages", total_pages)
146
+ if not max_pages or max_pages == 0 or max_pages > total_pages:
147
+ max_pages = total_pages
148
+ model.max_pages = total_pages # type: ignore[attr-defined]
149
149
 
150
+ if max_pages > 1:
150
151
  with ThreadPoolExecutor(max_workers=5) as executor:
151
152
  futures = []
152
- for page in range(2, model.max_pages + 1):
153
+ for page in range(2, max_pages + 1):
153
154
  futures.append(
154
155
  executor.submit(
155
156
  self._fetch_next_page,
@@ -177,7 +178,7 @@ class Api(object):
177
178
  parsed_data = [Repository(**item) for item in data]
178
179
  return Response(response=response, data=parsed_data)
179
180
  except ValidationError as e:
180
- raise ParameterError(f"Invalid parameters: {e.errors()}")
181
+ raise ParameterError(f"Invalid parameters: {e.errors()}") from e
181
182
 
182
183
  @require_auth
183
184
  def get_repository(self, owner: str, repo: str) -> Response:
@@ -194,8 +195,8 @@ class Api(object):
194
195
  return Response(response=response, data=parsed_data)
195
196
  except requests.exceptions.HTTPError as e:
196
197
  if e.response.status_code == 404:
197
- raise ParameterError(f"Repository {owner}/{repo} not found")
198
- raise e
198
+ raise ParameterError(f"Repository {owner}/{repo} not found") from e
199
+ raise
199
200
 
200
201
  @require_auth
201
202
  def get_issues(self, **kwargs) -> Response:
@@ -210,7 +211,7 @@ class Api(object):
210
211
  parsed_data = [Issue(**item) for item in data]
211
212
  return Response(response=response, data=parsed_data)
212
213
  except ValidationError as e:
213
- raise ParameterError(f"Invalid parameters: {e.errors()}")
214
+ raise ParameterError(f"Invalid parameters: {e.errors()}") from e
214
215
 
215
216
  @require_auth
216
217
  def get_pull_requests(self, **kwargs) -> Response:
@@ -225,7 +226,7 @@ class Api(object):
225
226
  parsed_data = [PullRequest(**item) for item in data]
226
227
  return Response(response=response, data=parsed_data)
227
228
  except ValidationError as e:
228
- raise ParameterError(f"Invalid parameters: {e.errors()}")
229
+ raise ParameterError(f"Invalid parameters: {e.errors()}") from e
229
230
 
230
231
  @require_auth
231
232
  def get_contents(self, **kwargs) -> Response:
@@ -241,13 +242,14 @@ class Api(object):
241
242
  )
242
243
  response.raise_for_status()
243
244
  data = response.json()
245
+ parsed_data: list[Content] | Content
244
246
  if isinstance(data, list):
245
247
  parsed_data = [Content(**item) for item in data]
246
248
  else:
247
249
  parsed_data = Content(**data)
248
250
  return Response(response=response, data=parsed_data)
249
251
  except ValidationError as e:
250
- raise ParameterError(f"Invalid parameters: {e.errors()}")
252
+ raise ParameterError(f"Invalid parameters: {e.errors()}") from e
251
253
 
252
254
  @require_auth
253
255
  def get_branches(self, **kwargs) -> Response:
@@ -260,7 +262,7 @@ class Api(object):
260
262
  parsed_data = [Branch(**item) for item in data]
261
263
  return Response(response=response, data=parsed_data)
262
264
  except ValidationError as e:
263
- raise ParameterError(f"Invalid parameters: {e.errors()}")
265
+ raise ParameterError(f"Invalid parameters: {e.errors()}") from e
264
266
 
265
267
  @require_auth
266
268
  def get_commits(self, **kwargs) -> Response:
@@ -273,4 +275,4 @@ class Api(object):
273
275
  parsed_data = [Commit(**item) for item in data]
274
276
  return Response(response=response, data=parsed_data)
275
277
  except ValidationError as e:
276
- raise ParameterError(f"Invalid parameters: {e.errors()}")
278
+ raise ParameterError(f"Invalid parameters: {e.errors()}") from e
@@ -2,12 +2,12 @@
2
2
 
3
3
  import os
4
4
  import threading
5
- from typing import Optional
6
5
 
7
6
  import requests
8
- from agent_utilities.base_utilities import to_boolean, get_logger
9
- from github_agent.api_wrapper import Api
10
- from agent_utilities.exceptions import AuthError, UnauthorizedError
7
+ from agent_utilities.base_utilities import get_logger, to_boolean
8
+ from agent_utilities.core.exceptions import AuthError, UnauthorizedError
9
+
10
+ from github_agent.api_client import Api
11
11
 
12
12
  local = threading.local()
13
13
  logger = get_logger(__name__)
@@ -15,9 +15,9 @@ logger = get_logger(__name__)
15
15
 
16
16
  def get_client(
17
17
  instance: str = os.getenv("GITHUB_URL", "https://api.github.com"),
18
- token: Optional[str] = os.getenv("GITHUB_TOKEN", None),
18
+ token: str | None = os.getenv("GITHUB_TOKEN", None),
19
19
  verify: bool = to_boolean(string=os.getenv("GITHUB_VERIFY", "True")),
20
- config: Optional[dict] = None,
20
+ config: dict | None = None,
21
21
  ) -> Api:
22
22
  """
23
23
  Factory function to create the GitHub Api client.
@@ -53,14 +53,14 @@ def get_client(
53
53
  auth = (config["oidc_client_id"], config["oidc_client_secret"])
54
54
  try:
55
55
  response = requests.post(
56
- config["token_endpoint"], data=exchange_data, auth=auth
56
+ config["token_endpoint"], data=exchange_data, auth=auth, timeout=30
57
57
  )
58
58
  response.raise_for_status()
59
59
  new_token = response.json()["access_token"]
60
60
  logger.info("Token exchange successful")
61
61
  except Exception as e:
62
62
  logger.error(f"Token exchange failed: {str(e)}")
63
- raise RuntimeError(f"Token exchange failed: {str(e)}")
63
+ raise RuntimeError(f"Token exchange failed: {str(e)}") from e
64
64
 
65
65
  try:
66
66
  return Api(
@@ -1,6 +1,6 @@
1
- #!/usr/bin/python
1
+ from typing import Any
2
2
 
3
- from typing import Union, List, Dict, Optional
3
+ #!/usr/bin/python
4
4
  from pydantic import (
5
5
  BaseModel,
6
6
  Field,
@@ -12,12 +12,14 @@ class BaseModelWrapper(BaseModel):
12
12
  Base Model wrapping common functionalities.
13
13
  """
14
14
 
15
- max_pages: Optional[int] = Field(
15
+ max_pages: int | None = Field(
16
16
  description="Max amount of pages to retrieve", default=None
17
17
  )
18
- page: Optional[int] = Field(description="Pagination page", default=1)
19
- per_page: Optional[int] = Field(description="Results per page", default=100)
20
- api_parameters: Optional[Dict] = Field(description="API Parameters", default=None)
18
+ page: int | None = Field(description="Pagination page", default=1)
19
+ per_page: int | None = Field(description="Results per page", default=100)
20
+ api_parameters: dict[str, Any] = Field(
21
+ description="API Parameters", default_factory=dict
22
+ )
21
23
 
22
24
  def model_post_init(self, __context):
23
25
  self.api_parameters = {}
@@ -32,15 +34,13 @@ class RepoModel(BaseModelWrapper):
32
34
  Pydantic model for Repository requests.
33
35
  """
34
36
 
35
- owner: Optional[str] = Field(
36
- None, description="The account owner of the repository."
37
- )
38
- repo: Optional[str] = Field(None, description="The name of the repository.")
39
- visibility: Optional[str] = Field(
37
+ owner: str | None = Field(None, description="The account owner of the repository.")
38
+ repo: str | None = Field(None, description="The name of the repository.")
39
+ visibility: str | None = Field(
40
40
  None, description="Can be one of all, public, or private."
41
41
  )
42
- affiliation: Optional[str] = Field(None, description="Affiliation filter.")
43
- type: Optional[str] = Field(
42
+ affiliation: str | None = Field(None, description="Affiliation filter.")
43
+ type: str | None = Field(
44
44
  None, description="Can be one of all, owner, public, private, member."
45
45
  )
46
46
 
@@ -61,25 +61,25 @@ class IssueModel(BaseModelWrapper):
61
61
 
62
62
  owner: str = Field(..., description="The account owner of the repository.")
63
63
  repo: str = Field(..., description="The name of the repository.")
64
- issue_number: Optional[int] = Field(
64
+ issue_number: int | None = Field(
65
65
  None, description="The number that identifies the issue."
66
66
  )
67
- state: Optional[str] = Field(
67
+ state: str | None = Field(
68
68
  None,
69
69
  description="Indicates the state of the issues to return. Can be either open, closed, or all.",
70
70
  )
71
- labels: Optional[Union[str, List[str]]] = Field(
71
+ labels: str | list[str] | None = Field(
72
72
  None, description="A list of comma separated label names."
73
73
  )
74
- assignee: Optional[str] = Field(
74
+ assignee: str | None = Field(
75
75
  None,
76
76
  description="Can be the name of a user. Use none for issues with no assigned user, and * for assigned issues to any user.",
77
77
  )
78
- creator: Optional[str] = Field(None, description="The user that created the issue.")
79
- mentioned: Optional[str] = Field(
78
+ creator: str | None = Field(None, description="The user that created the issue.")
79
+ mentioned: str | None = Field(
80
80
  None, description="A user that is mentioned in the issue."
81
81
  )
82
- since: Optional[str] = Field(
82
+ since: str | None = Field(
83
83
  None, description="Only show notifications updated after the given time."
84
84
  )
85
85
 
@@ -105,22 +105,22 @@ class PullRequestModel(BaseModelWrapper):
105
105
 
106
106
  owner: str = Field(..., description="The account owner of the repository.")
107
107
  repo: str = Field(..., description="The name of the repository.")
108
- pull_number: Optional[int] = Field(
108
+ pull_number: int | None = Field(
109
109
  None, description="The number that identifies the pull request."
110
110
  )
111
- state: Optional[str] = Field(
111
+ state: str | None = Field(
112
112
  None, description="State of the PR. (open, closed, or all)"
113
113
  )
114
- head: Optional[str] = Field(
114
+ head: str | None = Field(
115
115
  None,
116
116
  description="Filter pulls by head user or head organization and branch name in the format of user:ref-name or organization:ref-name.",
117
117
  )
118
- base: Optional[str] = Field(None, description="Filter pulls by base branch name.")
119
- sort: Optional[str] = Field(
118
+ base: str | None = Field(None, description="Filter pulls by base branch name.")
119
+ sort: str | None = Field(
120
120
  None,
121
121
  description="What to sort results by. Can be created, updated, popularity, long-running.",
122
122
  )
123
- direction: Optional[str] = Field(
123
+ direction: str | None = Field(
124
124
  None, description="The direction of the sort. Can be asc or desc."
125
125
  )
126
126
 
@@ -146,7 +146,7 @@ class ContentModel(BaseModelWrapper):
146
146
  owner: str = Field(..., description="The account owner of the repository.")
147
147
  repo: str = Field(..., description="The name of the repository.")
148
148
  path: str = Field(..., description="The content path.")
149
- ref: Optional[str] = Field(
149
+ ref: str | None = Field(
150
150
  None,
151
151
  description="The name of the commit/branch/tag. Default: the repository's default branch.",
152
152
  )
@@ -164,7 +164,7 @@ class BranchModel(BaseModelWrapper):
164
164
 
165
165
  owner: str = Field(..., description="The account owner of the repository.")
166
166
  repo: str = Field(..., description="The name of the repository.")
167
- branch: Optional[str] = Field(None, description="The name of the branch.")
167
+ branch: str | None = Field(None, description="The name of the branch.")
168
168
 
169
169
  def model_post_init(self, __context):
170
170
  super().model_post_init(__context)
@@ -177,19 +177,19 @@ class CommitModel(BaseModelWrapper):
177
177
 
178
178
  owner: str = Field(..., description="The account owner of the repository.")
179
179
  repo: str = Field(..., description="The name of the repository.")
180
- sha: Optional[str] = Field(
180
+ sha: str | None = Field(
181
181
  None, description="SHA or branch to start listing commits from."
182
182
  )
183
- path: Optional[str] = Field(
183
+ path: str | None = Field(
184
184
  None, description="Only commits containing this file path will be returned."
185
185
  )
186
- author: Optional[str] = Field(
186
+ author: str | None = Field(
187
187
  None, description="GitHub username or email address to filter by commit author."
188
188
  )
189
- since: Optional[str] = Field(
189
+ since: str | None = Field(
190
190
  None, description="Only show notifications updated after the given time."
191
191
  )
192
- until: Optional[str] = Field(
192
+ until: str | None = Field(
193
193
  None, description="Only show notifications updated before the given time."
194
194
  )
195
195
 
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/python
2
2
 
3
- from typing import List, Dict, Optional, Any
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ import requests
4
7
  from pydantic import (
5
8
  BaseModel,
6
9
  ConfigDict,
7
10
  HttpUrl,
8
11
  )
9
- from datetime import datetime
10
- import requests
11
12
 
12
13
 
13
14
  class Response(BaseModel):
@@ -17,7 +18,7 @@ class Response(BaseModel):
17
18
 
18
19
  model_config = ConfigDict(arbitrary_types_allowed=True)
19
20
  response: requests.Response
20
- data: Any = None
21
+ data: Any | None = None
21
22
 
22
23
 
23
24
  class User(BaseModel):
@@ -41,7 +42,7 @@ class Repository(BaseModel):
41
42
  private: bool
42
43
  owner: User
43
44
  html_url: HttpUrl
44
- description: Optional[str] = None
45
+ description: str | None = None
45
46
  fork: bool
46
47
  url: HttpUrl
47
48
  created_at: datetime
@@ -51,25 +52,25 @@ class Repository(BaseModel):
51
52
  ssh_url: str
52
53
  clone_url: HttpUrl
53
54
  svn_url: HttpUrl
54
- homepage: Optional[str] = None
55
+ homepage: str | None = None
55
56
  size: int
56
57
  stargazers_count: int
57
58
  watchers_count: int
58
- language: Optional[str] = None
59
+ language: str | None = None
59
60
  has_issues: bool
60
61
  has_projects: bool
61
62
  has_downloads: bool
62
63
  has_wiki: bool
63
64
  has_pages: bool
64
65
  forks_count: int
65
- mirror_url: Optional[HttpUrl] = None
66
+ mirror_url: HttpUrl | None = None
66
67
  archived: bool
67
68
  disabled: bool
68
69
  open_issues_count: int
69
- license: Optional[Dict] = None
70
+ license: dict | None = None
70
71
  allow_forking: bool
71
72
  is_template: bool
72
- topics: List[str]
73
+ topics: list[str]
73
74
  visibility: str
74
75
  forks: int
75
76
  open_issues: int
@@ -90,23 +91,23 @@ class Issue(BaseModel):
90
91
  number: int
91
92
  title: str
92
93
  user: User
93
- labels: List[Dict]
94
+ labels: list[dict]
94
95
  state: str
95
96
  locked: bool
96
- assignee: Optional[User] = None
97
- assignees: List[User]
98
- milestone: Optional[Dict] = None
97
+ assignee: User | None = None
98
+ assignees: list[User]
99
+ milestone: dict | None = None
99
100
  comments: int
100
101
  created_at: datetime
101
102
  updated_at: datetime
102
- closed_at: Optional[datetime] = None
103
+ closed_at: datetime | None = None
103
104
  author_association: str
104
- active_lock_reason: Optional[str] = None
105
- body: Optional[str] = None
106
- reactions: Optional[Dict] = None
105
+ active_lock_reason: str | None = None
106
+ body: str | None = None
107
+ reactions: dict | None = None
107
108
  timeline_url: HttpUrl
108
- performed_via_github_app: Optional[Dict] = None
109
- state_reason: Optional[str] = None
109
+ performed_via_github_app: dict | None = None
110
+ state_reason: str | None = None
110
111
 
111
112
 
112
113
  class PullRequest(BaseModel):
@@ -123,30 +124,30 @@ class PullRequest(BaseModel):
123
124
  locked: bool
124
125
  title: str
125
126
  user: User
126
- body: Optional[str] = None
127
+ body: str | None = None
127
128
  created_at: datetime
128
129
  updated_at: datetime
129
- closed_at: Optional[datetime] = None
130
- merged_at: Optional[datetime] = None
131
- merge_commit_sha: Optional[str] = None
132
- assignee: Optional[User] = None
133
- assignees: List[User]
134
- requested_reviewers: List[User]
135
- requested_teams: List[Dict]
136
- labels: List[Dict]
137
- milestone: Optional[Dict] = None
130
+ closed_at: datetime | None = None
131
+ merged_at: datetime | None = None
132
+ merge_commit_sha: str | None = None
133
+ assignee: User | None = None
134
+ assignees: list[User]
135
+ requested_reviewers: list[User]
136
+ requested_teams: list[dict]
137
+ labels: list[dict]
138
+ milestone: dict | None = None
138
139
  draft: bool
139
140
  commits_url: HttpUrl
140
141
  review_comments_url: HttpUrl
141
142
  review_comment_url: str
142
143
  comments_url: HttpUrl
143
144
  statuses_url: HttpUrl
144
- head: Dict
145
- base: Dict
146
- _links: Dict
145
+ head: dict
146
+ base: dict
147
+ _links: dict
147
148
  author_association: str
148
- auto_merge: Optional[Dict] = None
149
- active_lock_reason: Optional[str] = None
149
+ auto_merge: dict | None = None
150
+ active_lock_reason: str | None = None
150
151
 
151
152
 
152
153
  class Content(BaseModel):
@@ -158,17 +159,17 @@ class Content(BaseModel):
158
159
  url: HttpUrl
159
160
  html_url: HttpUrl
160
161
  git_url: HttpUrl
161
- download_url: Optional[HttpUrl] = None
162
+ download_url: HttpUrl | None = None
162
163
  type: str
163
- content: Optional[str] = None
164
- encoding: Optional[str] = None
165
- _links: Dict
164
+ content: str | None = None
165
+ encoding: str | None = None
166
+ _links: dict
166
167
 
167
168
 
168
169
  class Branch(BaseModel):
169
170
  model_config = ConfigDict(extra="allow")
170
171
  name: str
171
- commit: Dict
172
+ commit: dict
172
173
  protected: bool
173
174
 
174
175
 
@@ -176,10 +177,10 @@ class Commit(BaseModel):
176
177
  model_config = ConfigDict(extra="allow")
177
178
  sha: str
178
179
  node_id: str
179
- commit: Dict
180
+ commit: dict
180
181
  url: HttpUrl
181
182
  html_url: HttpUrl
182
183
  comments_url: HttpUrl
183
- author: Optional[User] = None
184
- committer: Optional[User] = None
185
- parents: List[Dict]
184
+ author: User | None = None
185
+ committer: User | None = None
186
+ parents: list[dict]
@@ -0,0 +1,3 @@
1
+ {
2
+ "mcpServers": {}
3
+ }