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.
Files changed (64) hide show
  1. {github_agent-0.28.0/github_agent.egg-info → github_agent-0.30.0}/PKG-INFO +39 -4
  2. github_agent-0.28.0/PKG-INFO → github_agent-0.30.0/README.md +36 -22
  3. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/agent_server.py +1 -1
  4. github_agent-0.30.0/github_agent/api/api_client_orgs.py +293 -0
  5. github_agent-0.30.0/github_agent/api/api_client_pages.py +234 -0
  6. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api_client.py +10 -0
  7. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/auth.py +10 -0
  8. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/github_input_models.py +134 -1
  9. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/github_response_models.py +117 -0
  10. github_agent-0.30.0/github_agent/mcp/mcp_org.py +270 -0
  11. github_agent-0.30.0/github_agent/mcp/mcp_repo.py +287 -0
  12. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp_server.py +375 -6
  13. github_agent-0.28.0/README.md → github_agent-0.30.0/github_agent.egg-info/PKG-INFO +57 -1
  14. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/SOURCES.txt +3 -0
  15. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/requires.txt +1 -1
  16. {github_agent-0.28.0 → github_agent-0.30.0}/pyproject.toml +3 -3
  17. {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_init_dynamics.py +7 -1
  18. github_agent-0.30.0/tests/test_org_crud.py +587 -0
  19. github_agent-0.30.0/tests/test_pages.py +616 -0
  20. github_agent-0.28.0/github_agent/api/api_client_orgs.py +0 -40
  21. github_agent-0.28.0/github_agent/mcp/mcp_org.py +0 -75
  22. github_agent-0.28.0/github_agent/mcp/mcp_repo.py +0 -113
  23. {github_agent-0.28.0 → github_agent-0.30.0}/LICENSE +0 -0
  24. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/__init__.py +0 -0
  25. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/__main__.py +0 -0
  26. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/__init__.py +0 -0
  27. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_base.py +0 -0
  28. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_branches.py +0 -0
  29. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_commits.py +0 -0
  30. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_contents.py +0 -0
  31. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_issues.py +0 -0
  32. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_pulls.py +0 -0
  33. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_releases.py +0 -0
  34. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_repos.py +0 -0
  35. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_search.py +0 -0
  36. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/api/api_client_workflows.py +0 -0
  37. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/__init__.py +0 -0
  38. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_action.py +0 -0
  39. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_branch.py +0 -0
  40. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_collaborator.py +0 -0
  41. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_commit.py +0 -0
  42. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_content.py +0 -0
  43. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_issue.py +0 -0
  44. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_pull.py +0 -0
  45. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_release.py +0 -0
  46. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp/mcp_search.py +0 -0
  47. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent/mcp_config.json +0 -0
  48. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/dependency_links.txt +0 -0
  49. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/entry_points.txt +0 -0
  50. {github_agent-0.28.0 → github_agent-0.30.0}/github_agent.egg-info/top_level.txt +0 -0
  51. {github_agent-0.28.0 → github_agent-0.30.0}/scripts/security_sanitizer.py +0 -0
  52. {github_agent-0.28.0 → github_agent-0.30.0}/scripts/validate_a2a_agent.py +0 -0
  53. {github_agent-0.28.0 → github_agent-0.30.0}/scripts/verify_api_integration.py +0 -0
  54. {github_agent-0.28.0 → github_agent-0.30.0}/setup.cfg +0 -0
  55. {github_agent-0.28.0 → github_agent-0.30.0}/tests/conftest.py +0 -0
  56. {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_api_client_endpoints.py +0 -0
  57. {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_api_validation.py +0 -0
  58. {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_auth_client_edge_cases.py +0 -0
  59. {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_concept_parity.py +0 -0
  60. {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_github_agent_api_brute_force_coverage.py +0 -0
  61. {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_github_agent_brute_force_coverage.py +0 -0
  62. {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_mcp_coverage.py +0 -0
  63. {github_agent-0.28.0 → github_agent-0.30.0}/tests/test_model_fields.py +0 -0
  64. {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.28.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.14,>=3.11
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.45.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
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/github-agent)
42
42
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/github-agent)
43
43
 
44
- *Version: 0.28.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
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/github-agent)
42
21
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/github-agent)
43
22
 
44
- *Version: 0.28.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" />
@@ -4,7 +4,7 @@ import os
4
4
  import sys
5
5
  import warnings
6
6
 
7
- __version__ = "0.28.0"
7
+ __version__ = "0.30.0"
8
8
 
9
9
  logging.basicConfig(
10
10
  level=logging.INFO,
@@ -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())