github-agent 0.28.0__tar.gz → 0.30.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.
- {github_agent-0.28.0/github_agent.egg-info → github_agent-0.30.0}/PKG-INFO +39 -4
- github_agent-0.28.0/PKG-INFO → github_agent-0.30.0/README.md +36 -22
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/agent_server.py +1 -1
- github_agent-0.30.0/github_agent/api/api_client_orgs.py +293 -0
- github_agent-0.30.0/github_agent/api/api_client_pages.py +234 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api_client.py +10 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/auth.py +10 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/github_input_models.py +134 -1
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/github_response_models.py +117 -0
- github_agent-0.30.0/github_agent/mcp/mcp_org.py +270 -0
- github_agent-0.30.0/github_agent/mcp/mcp_repo.py +287 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp_server.py +375 -6
- github_agent-0.28.0/README.md → github_agent-0.30.0/github_agent.egg-info/PKG-INFO +57 -1
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/SOURCES.txt +3 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/requires.txt +1 -1
- {github_agent-0.28.0 → github_agent-0.30.0}/pyproject.toml +3 -3
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_init_dynamics.py +7 -1
- github_agent-0.30.0/tests/test_org_crud.py +587 -0
- github_agent-0.30.0/tests/test_pages.py +616 -0
- github_agent-0.28.0/github_agent/api/api_client_orgs.py +0 -40
- github_agent-0.28.0/github_agent/mcp/mcp_org.py +0 -75
- github_agent-0.28.0/github_agent/mcp/mcp_repo.py +0 -113
- {github_agent-0.28.0 → github_agent-0.30.0}/LICENSE +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/__init__.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/__main__.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/__init__.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_base.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_branches.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_commits.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_contents.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_issues.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_pulls.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_releases.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_repos.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_search.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_workflows.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/__init__.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_action.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_branch.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_collaborator.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_commit.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_content.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_issue.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_pull.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_release.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_search.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp_config.json +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/dependency_links.txt +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/entry_points.txt +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/top_level.txt +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/scripts/security_sanitizer.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/scripts/validate_a2a_agent.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/scripts/verify_api_integration.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/setup.cfg +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/conftest.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_api_client_endpoints.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_api_validation.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_auth_client_edge_cases.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_concept_parity.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_github_agent_api_brute_force_coverage.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_github_agent_brute_force_coverage.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_mcp_coverage.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_model_fields.py +0 -0
- {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_startup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-agent
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: GitHub Agent for MCP
|
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -9,10 +9,10 @@ Classifier: License :: Public Domain
|
|
|
9
9
|
Classifier: Environment :: Console
|
|
10
10
|
Classifier: Operating System :: POSIX :: Linux
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Requires-Python: <3.
|
|
12
|
+
Requires-Python: <3.15,>=3.11
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: agent-utilities[agent,logfire]>=0.
|
|
15
|
+
Requires-Dist: agent-utilities[agent,logfire]>=0.48.0
|
|
16
16
|
Provides-Extra: test
|
|
17
17
|
Requires-Dist: pytest-xdist>=3.6.0; extra == "test"
|
|
18
18
|
Requires-Dist: pytest; extra == "test"
|
|
@@ -41,7 +41,11 @@ Dynamic: license-file
|
|
|
41
41
|

|
|
42
42
|

|
|
43
43
|
|
|
44
|
-
*Version: 0.
|
|
44
|
+
*Version: 0.30.0*
|
|
45
|
+
|
|
46
|
+
> **Documentation** — Installation, deployment, and usage across the MCP, API, and
|
|
47
|
+
> CLI interfaces, including the integrated A2A agent server, are maintained in the
|
|
48
|
+
> [official documentation](https://knuckles-team.github.io/github-agent/).
|
|
45
49
|
|
|
46
50
|
---
|
|
47
51
|
|
|
@@ -195,6 +199,21 @@ docker run -d \
|
|
|
195
199
|
|
|
196
200
|
---
|
|
197
201
|
|
|
202
|
+
<!-- BEGIN GENERATED: additional-deployment-options -->
|
|
203
|
+
### Additional Deployment Options
|
|
204
|
+
|
|
205
|
+
`github-agent` can also run as a **local container** (Docker / Podman / `uv`) or be
|
|
206
|
+
consumed from a **remote deployment**. The
|
|
207
|
+
[Deployment guide](https://knuckles-team.github.io/github-agent/deployment/) has full, copy-paste
|
|
208
|
+
`mcp_config.json` for all four transports — **stdio**, **streamable-http**,
|
|
209
|
+
**local container / uv**, and **remote URL**:
|
|
210
|
+
|
|
211
|
+
- **Local container / uv** — launch the server from `mcp_config.json` via `uvx`,
|
|
212
|
+
`docker run`, or `podman run`, or point at a local streamable-http container by `url`.
|
|
213
|
+
- **Remote URL** — connect to a server deployed behind Caddy at
|
|
214
|
+
`http://github-mcp.arpa/mcp` using the `"url"` key.
|
|
215
|
+
<!-- END GENERATED: additional-deployment-options -->
|
|
216
|
+
|
|
198
217
|
## Agent
|
|
199
218
|
|
|
200
219
|
This repository features a fully integrated Pydantic AI Graph Agent. It communicates over the **Agent Control Protocol (ACP)** and interacts seamlessly with the **Agent Web UI (AG-UI)** and Terminal interface.
|
|
@@ -318,6 +337,22 @@ python -m pip install github-agent[all]
|
|
|
318
337
|
|
|
319
338
|
---
|
|
320
339
|
|
|
340
|
+
## Documentation
|
|
341
|
+
|
|
342
|
+
The complete documentation is published as the
|
|
343
|
+
[official documentation site](https://knuckles-team.github.io/github-agent/) and is the
|
|
344
|
+
recommended reference for installation, deployment, and day-to-day operation.
|
|
345
|
+
|
|
346
|
+
| Page | Contents |
|
|
347
|
+
|---|---|
|
|
348
|
+
| [Installation](https://knuckles-team.github.io/github-agent/installation/) | pip, source, extras, prebuilt Docker image |
|
|
349
|
+
| [Deployment](https://knuckles-team.github.io/github-agent/deployment/) | run the MCP and agent servers, Compose, Caddy + Technitium, env config |
|
|
350
|
+
| [Usage](https://knuckles-team.github.io/github-agent/usage/) | the MCP tools, the `Api` client, the CLI |
|
|
351
|
+
| [Overview](https://knuckles-team.github.io/github-agent/overview/) | supervisor architecture and specialized agents |
|
|
352
|
+
| [Concepts](https://knuckles-team.github.io/github-agent/concepts/) | concept registry (`CONCEPT:GH-*`) |
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
321
356
|
## Repository Owners
|
|
322
357
|
|
|
323
358
|
<img width="100%" height="180em" src="https://github-readme-stats.vercel.app/api?username=Knucklessg1&show_icons=true&hide_border=true&&count_private=true&include_all_commits=true" />
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: github-agent
|
|
3
|
-
Version: 0.28.0
|
|
4
|
-
Summary: GitHub Agent for MCP
|
|
5
|
-
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
8
|
-
Classifier: License :: Public Domain
|
|
9
|
-
Classifier: Environment :: Console
|
|
10
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Requires-Python: <3.14,>=3.11
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: LICENSE
|
|
15
|
-
Requires-Dist: agent-utilities[agent,logfire]>=0.45.0
|
|
16
|
-
Provides-Extra: test
|
|
17
|
-
Requires-Dist: pytest-xdist>=3.6.0; extra == "test"
|
|
18
|
-
Requires-Dist: pytest; extra == "test"
|
|
19
|
-
Requires-Dist: pytest-asyncio; extra == "test"
|
|
20
|
-
Dynamic: license-file
|
|
21
|
-
|
|
22
1
|
# Github Agent
|
|
23
2
|
## CLI or API | MCP | Agent
|
|
24
3
|
|
|
@@ -41,7 +20,11 @@ Dynamic: license-file
|
|
|
41
20
|

|
|
42
21
|

|
|
43
22
|
|
|
44
|
-
*Version: 0.
|
|
23
|
+
*Version: 0.30.0*
|
|
24
|
+
|
|
25
|
+
> **Documentation** — Installation, deployment, and usage across the MCP, API, and
|
|
26
|
+
> CLI interfaces, including the integrated A2A agent server, are maintained in the
|
|
27
|
+
> [official documentation](https://knuckles-team.github.io/github-agent/).
|
|
45
28
|
|
|
46
29
|
---
|
|
47
30
|
|
|
@@ -195,6 +178,21 @@ docker run -d \
|
|
|
195
178
|
|
|
196
179
|
---
|
|
197
180
|
|
|
181
|
+
<!-- BEGIN GENERATED: additional-deployment-options -->
|
|
182
|
+
### Additional Deployment Options
|
|
183
|
+
|
|
184
|
+
`github-agent` can also run as a **local container** (Docker / Podman / `uv`) or be
|
|
185
|
+
consumed from a **remote deployment**. The
|
|
186
|
+
[Deployment guide](https://knuckles-team.github.io/github-agent/deployment/) has full, copy-paste
|
|
187
|
+
`mcp_config.json` for all four transports — **stdio**, **streamable-http**,
|
|
188
|
+
**local container / uv**, and **remote URL**:
|
|
189
|
+
|
|
190
|
+
- **Local container / uv** — launch the server from `mcp_config.json` via `uvx`,
|
|
191
|
+
`docker run`, or `podman run`, or point at a local streamable-http container by `url`.
|
|
192
|
+
- **Remote URL** — connect to a server deployed behind Caddy at
|
|
193
|
+
`http://github-mcp.arpa/mcp` using the `"url"` key.
|
|
194
|
+
<!-- END GENERATED: additional-deployment-options -->
|
|
195
|
+
|
|
198
196
|
## Agent
|
|
199
197
|
|
|
200
198
|
This repository features a fully integrated Pydantic AI Graph Agent. It communicates over the **Agent Control Protocol (ACP)** and interacts seamlessly with the **Agent Web UI (AG-UI)** and Terminal interface.
|
|
@@ -318,6 +316,22 @@ python -m pip install github-agent[all]
|
|
|
318
316
|
|
|
319
317
|
---
|
|
320
318
|
|
|
319
|
+
## Documentation
|
|
320
|
+
|
|
321
|
+
The complete documentation is published as the
|
|
322
|
+
[official documentation site](https://knuckles-team.github.io/github-agent/) and is the
|
|
323
|
+
recommended reference for installation, deployment, and day-to-day operation.
|
|
324
|
+
|
|
325
|
+
| Page | Contents |
|
|
326
|
+
|---|---|
|
|
327
|
+
| [Installation](https://knuckles-team.github.io/github-agent/installation/) | pip, source, extras, prebuilt Docker image |
|
|
328
|
+
| [Deployment](https://knuckles-team.github.io/github-agent/deployment/) | run the MCP and agent servers, Compose, Caddy + Technitium, env config |
|
|
329
|
+
| [Usage](https://knuckles-team.github.io/github-agent/usage/) | the MCP tools, the `Api` client, the CLI |
|
|
330
|
+
| [Overview](https://knuckles-team.github.io/github-agent/overview/) | supervisor architecture and specialized agents |
|
|
331
|
+
| [Concepts](https://knuckles-team.github.io/github-agent/concepts/) | concept registry (`CONCEPT:GH-*`) |
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
321
335
|
## Repository Owners
|
|
322
336
|
|
|
323
337
|
<img width="100%" height="180em" src="https://github-readme-stats.vercel.app/api?username=Knucklessg1&show_icons=true&hide_border=true&&count_private=true&include_all_commits=true" />
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from urllib.parse import urlparse
|
|
3
|
+
|
|
4
|
+
from agent_utilities.decorators import require_auth
|
|
5
|
+
from agent_utilities.exceptions import (
|
|
6
|
+
ParameterError,
|
|
7
|
+
)
|
|
8
|
+
from pydantic import ValidationError
|
|
9
|
+
|
|
10
|
+
from github_agent.api.api_client_base import BaseApiClient
|
|
11
|
+
from github_agent.github_input_models import (
|
|
12
|
+
OrgListModel,
|
|
13
|
+
OrgMemberModel,
|
|
14
|
+
OrgUpdateModel,
|
|
15
|
+
)
|
|
16
|
+
from github_agent.github_response_models import (
|
|
17
|
+
Organization,
|
|
18
|
+
OrganizationMembership,
|
|
19
|
+
OrganizationSummary,
|
|
20
|
+
Repository,
|
|
21
|
+
Response,
|
|
22
|
+
User,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OrganizationCreationNotSupportedError(Exception):
|
|
27
|
+
"""Raised when organization creation is requested against github.com.
|
|
28
|
+
|
|
29
|
+
github.com does not expose an API to create organizations — they can only
|
|
30
|
+
be created through the web UI. The POST /admin/organizations endpoint
|
|
31
|
+
exists only on GitHub Enterprise Server.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Api(BaseApiClient):
|
|
36
|
+
@require_auth
|
|
37
|
+
def get_organization(self, org: str) -> Response:
|
|
38
|
+
"""Get an organization's full profile.
|
|
39
|
+
|
|
40
|
+
GET /orgs/{org}
|
|
41
|
+
https://docs.github.com/en/rest/orgs/orgs#get-an-organization
|
|
42
|
+
"""
|
|
43
|
+
response = self._session.get(
|
|
44
|
+
url=f"{self.url}/orgs/{org}",
|
|
45
|
+
headers=self.headers,
|
|
46
|
+
verify=self.verify,
|
|
47
|
+
proxies=self.proxies,
|
|
48
|
+
)
|
|
49
|
+
response.raise_for_status()
|
|
50
|
+
try:
|
|
51
|
+
parsed_data = Organization(**response.json())
|
|
52
|
+
return Response(response=response, data=parsed_data)
|
|
53
|
+
except ValidationError as e:
|
|
54
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
55
|
+
|
|
56
|
+
@require_auth
|
|
57
|
+
def list_organizations(self, **kwargs) -> Response:
|
|
58
|
+
"""List organizations.
|
|
59
|
+
|
|
60
|
+
With scope='member' (default): organizations the authenticated user
|
|
61
|
+
belongs to.
|
|
62
|
+
GET /user/orgs
|
|
63
|
+
https://docs.github.com/en/rest/orgs/orgs#list-organizations-for-the-authenticated-user
|
|
64
|
+
|
|
65
|
+
With scope='all': every organization, in ascending ID order. GitHub
|
|
66
|
+
paginates this endpoint with a 'since' ID cursor — pass the highest
|
|
67
|
+
organization ID from the previous page to fetch the next one.
|
|
68
|
+
GET /organizations
|
|
69
|
+
https://docs.github.com/en/rest/orgs/orgs#list-organizations
|
|
70
|
+
"""
|
|
71
|
+
model = OrgListModel(**kwargs)
|
|
72
|
+
endpoint = "/user/orgs" if model.scope == "member" else "/organizations"
|
|
73
|
+
response, data = self._fetch_all_pages(endpoint, model)
|
|
74
|
+
try:
|
|
75
|
+
parsed_data = [OrganizationSummary(**item) for item in data]
|
|
76
|
+
return Response(response=response, data=parsed_data)
|
|
77
|
+
except ValidationError as e:
|
|
78
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
79
|
+
|
|
80
|
+
@require_auth
|
|
81
|
+
def update_organization(self, org: str, **kwargs) -> Response:
|
|
82
|
+
"""Update an organization's profile and member settings.
|
|
83
|
+
|
|
84
|
+
Accepts the documented mutable fields (validated by OrgUpdateModel):
|
|
85
|
+
billing_email, company, email, twitter_username, location, name,
|
|
86
|
+
description, blog, has_organization_projects, has_repository_projects,
|
|
87
|
+
default_repository_permission, members_can_create_repositories,
|
|
88
|
+
members_can_create_public/private/internal_repositories,
|
|
89
|
+
members_can_create_pages, members_can_fork_private_repositories,
|
|
90
|
+
web_commit_signoff_required.
|
|
91
|
+
|
|
92
|
+
PATCH /orgs/{org}
|
|
93
|
+
https://docs.github.com/en/rest/orgs/orgs#update-an-organization
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
model = OrgUpdateModel(**kwargs)
|
|
97
|
+
except ValidationError as e:
|
|
98
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
99
|
+
response = self._session.patch(
|
|
100
|
+
url=f"{self.url}/orgs/{org}",
|
|
101
|
+
json=model.model_dump(exclude_none=True),
|
|
102
|
+
headers=self.headers,
|
|
103
|
+
verify=self.verify,
|
|
104
|
+
proxies=self.proxies,
|
|
105
|
+
)
|
|
106
|
+
response.raise_for_status()
|
|
107
|
+
try:
|
|
108
|
+
parsed_data = Organization(**response.json())
|
|
109
|
+
return Response(response=response, data=parsed_data)
|
|
110
|
+
except ValidationError as e:
|
|
111
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
112
|
+
|
|
113
|
+
@require_auth
|
|
114
|
+
def delete_organization(self, org: str) -> Response:
|
|
115
|
+
"""Schedule an organization for deletion. IRREVERSIBLE.
|
|
116
|
+
|
|
117
|
+
GitHub responds 202 Accepted and deletes the organization, all of its
|
|
118
|
+
repositories, and all of its data asynchronously. The MCP layer gates
|
|
119
|
+
this action behind allow_destructive / GITHUB_ALLOW_DESTRUCTIVE.
|
|
120
|
+
|
|
121
|
+
DELETE /orgs/{org}
|
|
122
|
+
https://docs.github.com/en/rest/orgs/orgs#delete-an-organization
|
|
123
|
+
"""
|
|
124
|
+
response = self._session.delete(
|
|
125
|
+
url=f"{self.url}/orgs/{org}",
|
|
126
|
+
headers=self.headers,
|
|
127
|
+
verify=self.verify,
|
|
128
|
+
proxies=self.proxies,
|
|
129
|
+
)
|
|
130
|
+
response.raise_for_status()
|
|
131
|
+
return Response(response=response, data={"status": "deletion_scheduled"})
|
|
132
|
+
|
|
133
|
+
@require_auth
|
|
134
|
+
def create_organization(
|
|
135
|
+
self, login: str, admin: str, profile_name: str | None = None
|
|
136
|
+
) -> Response:
|
|
137
|
+
"""Create an organization — GitHub Enterprise Server ONLY.
|
|
138
|
+
|
|
139
|
+
POST /admin/organizations (site-admin API)
|
|
140
|
+
https://docs.github.com/en/enterprise-server@latest/rest/enterprise-admin/orgs#create-an-organization
|
|
141
|
+
|
|
142
|
+
github.com does NOT expose an API to create organizations; on
|
|
143
|
+
github.com they can only be created through the web UI
|
|
144
|
+
(https://github.com/account/organizations/new). When this client
|
|
145
|
+
points at api.github.com this method raises
|
|
146
|
+
OrganizationCreationNotSupportedError instead of calling the API.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
login: The organization's username.
|
|
150
|
+
admin: The login of the user who will manage this organization.
|
|
151
|
+
profile_name: The organization's display name.
|
|
152
|
+
"""
|
|
153
|
+
host = urlparse(self.url).hostname or ""
|
|
154
|
+
if host == "api.github.com":
|
|
155
|
+
raise OrganizationCreationNotSupportedError(
|
|
156
|
+
"github.com organizations cannot be created via the API — "
|
|
157
|
+
"create them in the web UI at "
|
|
158
|
+
"https://github.com/account/organizations/new. "
|
|
159
|
+
"POST /admin/organizations is only available on GitHub "
|
|
160
|
+
"Enterprise Server (point GITHUB_URL at your enterprise "
|
|
161
|
+
"instance's API, e.g. https://ghe.example.com/api/v3)."
|
|
162
|
+
)
|
|
163
|
+
payload: dict = {"login": login, "admin": admin}
|
|
164
|
+
if profile_name is not None:
|
|
165
|
+
payload["profile_name"] = profile_name
|
|
166
|
+
response = self._session.post(
|
|
167
|
+
url=f"{self.url}/admin/organizations",
|
|
168
|
+
json=payload,
|
|
169
|
+
headers=self.headers,
|
|
170
|
+
verify=self.verify,
|
|
171
|
+
proxies=self.proxies,
|
|
172
|
+
)
|
|
173
|
+
response.raise_for_status()
|
|
174
|
+
try:
|
|
175
|
+
parsed_data = OrganizationSummary(**response.json())
|
|
176
|
+
return Response(response=response, data=parsed_data)
|
|
177
|
+
except ValidationError as e:
|
|
178
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
179
|
+
|
|
180
|
+
@require_auth
|
|
181
|
+
def create_organization_repository(self, org: str, name: str, **kwargs) -> Response:
|
|
182
|
+
"""Create a repository in an organization.
|
|
183
|
+
|
|
184
|
+
Accepts the same payload fields as create_repository (which posts to
|
|
185
|
+
/user/repos): description, homepage, private, visibility, has_issues,
|
|
186
|
+
has_projects, has_wiki, auto_init, gitignore_template,
|
|
187
|
+
license_template, etc.
|
|
188
|
+
|
|
189
|
+
POST /orgs/{org}/repos
|
|
190
|
+
https://docs.github.com/en/rest/repos/repos#create-an-organization-repository
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
payload = {"name": name, **kwargs}
|
|
194
|
+
response = self._session.post(
|
|
195
|
+
url=f"{self.url}/orgs/{org}/repos",
|
|
196
|
+
json=payload,
|
|
197
|
+
headers=self.headers,
|
|
198
|
+
verify=self.verify,
|
|
199
|
+
proxies=self.proxies,
|
|
200
|
+
)
|
|
201
|
+
response.raise_for_status()
|
|
202
|
+
parsed_data = Repository(**response.json())
|
|
203
|
+
return Response(response=response, data=parsed_data)
|
|
204
|
+
except ValidationError as e:
|
|
205
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
206
|
+
|
|
207
|
+
@require_auth
|
|
208
|
+
def get_organization_membership(self, org: str, username: str) -> Response:
|
|
209
|
+
"""Get a user's organization membership (state and role).
|
|
210
|
+
|
|
211
|
+
GET /orgs/{org}/memberships/{username}
|
|
212
|
+
https://docs.github.com/en/rest/orgs/members#get-organization-membership-for-a-user
|
|
213
|
+
"""
|
|
214
|
+
response = self._session.get(
|
|
215
|
+
url=f"{self.url}/orgs/{org}/memberships/{username}",
|
|
216
|
+
headers=self.headers,
|
|
217
|
+
verify=self.verify,
|
|
218
|
+
proxies=self.proxies,
|
|
219
|
+
)
|
|
220
|
+
response.raise_for_status()
|
|
221
|
+
try:
|
|
222
|
+
parsed_data = OrganizationMembership(**response.json())
|
|
223
|
+
return Response(response=response, data=parsed_data)
|
|
224
|
+
except ValidationError as e:
|
|
225
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
226
|
+
|
|
227
|
+
@require_auth
|
|
228
|
+
def set_organization_membership(
|
|
229
|
+
self, org: str, username: str, role: str = "member"
|
|
230
|
+
) -> Response:
|
|
231
|
+
"""Add a user to an organization or update their role.
|
|
232
|
+
|
|
233
|
+
Invites the user when they are not yet a member (membership state
|
|
234
|
+
'pending' until accepted). Role is 'member' or 'admin'.
|
|
235
|
+
|
|
236
|
+
PUT /orgs/{org}/memberships/{username}
|
|
237
|
+
https://docs.github.com/en/rest/orgs/members#set-organization-membership-for-a-user
|
|
238
|
+
"""
|
|
239
|
+
response = self._session.put(
|
|
240
|
+
url=f"{self.url}/orgs/{org}/memberships/{username}",
|
|
241
|
+
json={"role": role},
|
|
242
|
+
headers=self.headers,
|
|
243
|
+
verify=self.verify,
|
|
244
|
+
proxies=self.proxies,
|
|
245
|
+
)
|
|
246
|
+
response.raise_for_status()
|
|
247
|
+
try:
|
|
248
|
+
parsed_data = OrganizationMembership(**response.json())
|
|
249
|
+
return Response(response=response, data=parsed_data)
|
|
250
|
+
except ValidationError as e:
|
|
251
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
252
|
+
|
|
253
|
+
@require_auth
|
|
254
|
+
def remove_organization_member(self, org: str, username: str) -> Response:
|
|
255
|
+
"""Remove a user from an organization (repositories access included).
|
|
256
|
+
|
|
257
|
+
Destructive: the MCP layer gates this action behind
|
|
258
|
+
allow_destructive / GITHUB_ALLOW_DESTRUCTIVE. GitHub responds 204.
|
|
259
|
+
|
|
260
|
+
DELETE /orgs/{org}/members/{username}
|
|
261
|
+
https://docs.github.com/en/rest/orgs/members#remove-an-organization-member
|
|
262
|
+
"""
|
|
263
|
+
response = self._session.delete(
|
|
264
|
+
url=f"{self.url}/orgs/{org}/members/{username}",
|
|
265
|
+
headers=self.headers,
|
|
266
|
+
verify=self.verify,
|
|
267
|
+
proxies=self.proxies,
|
|
268
|
+
)
|
|
269
|
+
response.raise_for_status()
|
|
270
|
+
return Response(response=response, data={"status": "removed"})
|
|
271
|
+
|
|
272
|
+
@require_auth
|
|
273
|
+
def get_org_members(self, **kwargs) -> Response:
|
|
274
|
+
"""List members for an organization."""
|
|
275
|
+
model = OrgMemberModel(**kwargs)
|
|
276
|
+
response, data = self._fetch_all_pages(f"/orgs/{model.org}/members", model)
|
|
277
|
+
try:
|
|
278
|
+
parsed_data = [User(**item) for item in data]
|
|
279
|
+
return Response(response=response, data=parsed_data)
|
|
280
|
+
except ValidationError as e:
|
|
281
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
282
|
+
|
|
283
|
+
@require_auth
|
|
284
|
+
def get_org_teams(self, org: str) -> Response:
|
|
285
|
+
"""List teams for an organization."""
|
|
286
|
+
response = self._session.get(
|
|
287
|
+
url=f"{self.url}/orgs/{org}/teams",
|
|
288
|
+
headers=self.headers,
|
|
289
|
+
verify=self.verify,
|
|
290
|
+
proxies=self.proxies,
|
|
291
|
+
)
|
|
292
|
+
response.raise_for_status()
|
|
293
|
+
return Response(response=response, data=response.json())
|