gitlab-api 25.23.0__tar.gz → 25.24.1__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.
- {gitlab_api-25.23.0/gitlab_api.egg-info → gitlab_api-25.24.1}/PKG-INFO +53 -90
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/README.md +47 -86
- gitlab_api-25.24.1/gitlab_api/__init__.py +80 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api/agent_server.py +29 -24
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api/api_client.py +30 -0
- gitlab_api-25.24.1/gitlab_api/auth.py +132 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api/gitlab_gql.py +3 -3
- gitlab_api-25.24.1/gitlab_api/mcp_server.py +1021 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1/gitlab_api.egg-info}/PKG-INFO +53 -90
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api.egg-info/SOURCES.txt +2 -0
- gitlab_api-25.24.1/gitlab_api.egg-info/requires.txt +19 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api.egg-info/top_level.txt +1 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/pyproject.toml +8 -5
- gitlab_api-25.24.1/scripts/verify_api_integration.py +279 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/test_setup.py +8 -6
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/conftest.py +3 -1
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/test_api_wrapper.py +1 -1
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/test_concept_parity.py +27 -19
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/test_gitlab_a2a_validation.py +3 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/test_gitlab_api_brute_force_coverage.py +2 -2
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/test_gitlab_mcp_validation.py +2 -1
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/test_gitlab_models.py +183 -354
- gitlab_api-25.24.1/tests/test_startup.py +12 -0
- gitlab_api-25.23.0/gitlab_api/__init__.py +0 -66
- gitlab_api-25.23.0/gitlab_api/auth.py +0 -93
- gitlab_api-25.23.0/gitlab_api/mcp_server.py +0 -7339
- gitlab_api-25.23.0/gitlab_api.egg-info/requires.txt +0 -17
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/LICENSE +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/MANIFEST.in +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api/__main__.py +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api/gitlab_input_models.py +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api/gitlab_response_models.py +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api/mcp_config.json +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api.egg-info/dependency_links.txt +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/gitlab_api.egg-info/entry_points.txt +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/requirements.txt +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/scripts/validate_a2a_agent.py +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/scripts/validate_agent.py +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/setup.cfg +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/__init__.py +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/test_verify_agent.py +0 -0
- {gitlab_api-25.23.0 → gitlab_api-25.24.1}/tests/verify_a2a_queries.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlab-api
|
|
3
|
-
Version: 25.
|
|
3
|
+
Version: 25.24.1
|
|
4
4
|
Summary: GitLab API + MCP Server + A2A Server
|
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,11 +12,12 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: <3.14,>=3.11
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: agent-utilities>=0.
|
|
15
|
+
Requires-Dist: agent-utilities>=0.11.0
|
|
16
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
16
17
|
Provides-Extra: mcp
|
|
17
|
-
Requires-Dist: agent-utilities[mcp]>=0.
|
|
18
|
+
Requires-Dist: agent-utilities[mcp]>=0.11.0; extra == "mcp"
|
|
18
19
|
Provides-Extra: agent
|
|
19
|
-
Requires-Dist: agent-utilities[agent,logfire]>=0.
|
|
20
|
+
Requires-Dist: agent-utilities[agent,logfire]>=0.11.0; extra == "agent"
|
|
20
21
|
Provides-Extra: gql
|
|
21
22
|
Requires-Dist: gql>=4.0.0; extra == "gql"
|
|
22
23
|
Provides-Extra: all
|
|
@@ -24,6 +25,7 @@ Requires-Dist: gitlab-api[agent,gql,logfire,mcp]>=25.15.56; extra == "all"
|
|
|
24
25
|
Provides-Extra: test
|
|
25
26
|
Requires-Dist: pytest; extra == "test"
|
|
26
27
|
Requires-Dist: pytest-asyncio; extra == "test"
|
|
28
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
27
29
|
Dynamic: license-file
|
|
28
30
|
|
|
29
31
|
# GitLab API - A2A | AG-UI | MCP
|
|
@@ -49,7 +51,7 @@ Dynamic: license-file
|
|
|
49
51
|

|
|
50
52
|

|
|
51
53
|
|
|
52
|
-
*Version: 25.
|
|
54
|
+
*Version: 25.24.1*
|
|
53
55
|
|
|
54
56
|
## Overview
|
|
55
57
|
|
|
@@ -880,6 +882,42 @@ For Testing Only: Plain text storage will also work, although **not** recommende
|
|
|
880
882
|
}
|
|
881
883
|
```
|
|
882
884
|
|
|
885
|
+
## Security & Governance
|
|
886
|
+
|
|
887
|
+
This project is built on [`agent-utilities`](https://github.com/Knuckles-Team/agent-utilities), inheriting enterprise-grade security and governance features.
|
|
888
|
+
|
|
889
|
+
### Authentication & Authorization
|
|
890
|
+
| Feature | Description |
|
|
891
|
+
|---------|-------------|
|
|
892
|
+
| **OIDC Token Delegation** | RFC 8693 token exchange for user-context propagation from A2A → MCP |
|
|
893
|
+
| **Eunomia Policies** | Fine-grained, policy-driven tool authorization (`none`, `embedded`, `remote`) |
|
|
894
|
+
| **Scoped Credentials** | Tools execute with the caller's scoped identity where possible |
|
|
895
|
+
| **3LO / OAuth / API Token** | Multiple auth strategies with graceful fallback |
|
|
896
|
+
|
|
897
|
+
### Eunomia Policy Enforcement
|
|
898
|
+
Eunomia provides a policy enforcement point for all tool calls:
|
|
899
|
+
- **Embedded mode**: Load local `mcp_policies.json` for role-based access, sensitivity gating, and audit logging
|
|
900
|
+
- **Remote mode**: Forward authorization decisions to a central Eunomia policy server for multi-agent governance
|
|
901
|
+
- Enable via CLI: `--eunomia-type embedded --eunomia-policy-file mcp_policies.json`
|
|
902
|
+
|
|
903
|
+
### Runtime Protections
|
|
904
|
+
| Protection | Description |
|
|
905
|
+
|------------|-------------|
|
|
906
|
+
| **Tool Guard** | Sensitivity detection with human-in-the-loop approval gating |
|
|
907
|
+
| **Prompt Injection Defense** | Input scanning and repetition/loop guards |
|
|
908
|
+
| **Content Filtering** | Output schema enforcement and cost budget controls |
|
|
909
|
+
| **Stuck Loop Detection** | Automatic detection and recovery from agent loops |
|
|
910
|
+
| **Context Limit Warnings** | Proactive alerts before context window exhaustion |
|
|
911
|
+
|
|
912
|
+
### Graph Agent Architecture
|
|
913
|
+
The A2A agent uses `pydantic-graph` orchestration with:
|
|
914
|
+
- **RouterNode**: Lightweight classifier that routes queries to specialized domains
|
|
915
|
+
- **DomainNode**: Focused executor with only relevant tools loaded, preventing tool hallucination
|
|
916
|
+
- **Approval Gates**: Policy-driven approval workflows before sensitive operations
|
|
917
|
+
- **Usage Guards**: Budget and rate limiting enforcement
|
|
918
|
+
|
|
919
|
+
> **Production Recommendation**: Enable `--eunomia-type embedded` (or `remote`) + OIDC delegation + containerized deployment. See [`agent-utilities` documentation](https://github.com/Knuckles-Team/agent-utilities) for full policy configuration.
|
|
920
|
+
|
|
883
921
|
## Install Python Package
|
|
884
922
|
|
|
885
923
|
Install Python Package
|
|
@@ -931,103 +969,28 @@ npx @modelcontextprotocol/inspector gitlab-mcp
|
|
|
931
969
|
|
|
932
970
|
## MCP Configuration Examples
|
|
933
971
|
|
|
934
|
-
###
|
|
935
|
-
|
|
972
|
+
### stdio (recommended for local development)
|
|
936
973
|
```json
|
|
937
974
|
{
|
|
938
975
|
"mcpServers": {
|
|
939
|
-
"gitlab
|
|
940
|
-
"command": "
|
|
941
|
-
"args": [
|
|
942
|
-
"run",
|
|
943
|
-
"gitlab-mcp"
|
|
944
|
-
],
|
|
976
|
+
"gitlab": {
|
|
977
|
+
"command": ".venv/bin/gitlab-mcp",
|
|
978
|
+
"args": [],
|
|
945
979
|
"env": {
|
|
946
|
-
"
|
|
947
|
-
"
|
|
948
|
-
|
|
949
|
-
"COMMITSTOOL": "True",
|
|
950
|
-
"CUSTOM_APITOOL": "True",
|
|
951
|
-
"DEFAULT_AGENT_NAME": "<YOUR_DEFAULT_AGENT_NAME>",
|
|
952
|
-
"DEPLOY_TOKENSTOOL": "True",
|
|
953
|
-
"ENVIRONMENTSTOOL": "True",
|
|
954
|
-
"GITLAB_SSL_VERIFY": "<YOUR_GITLAB_SSL_VERIFY>",
|
|
955
|
-
"GITLAB_TOKEN": "<YOUR_GITLAB_TOKEN>",
|
|
956
|
-
"GITLAB_URL": "<YOUR_GITLAB_URL>",
|
|
957
|
-
"GITLAB_VERIFY": "<YOUR_GITLAB_VERIFY>",
|
|
958
|
-
"GROUPSTOOL": "True",
|
|
959
|
-
"JOBSTOOL": "True",
|
|
960
|
-
"LLM_API_KEY": "<YOUR_LLM_API_KEY>",
|
|
961
|
-
"LLM_BASE_URL": "<YOUR_LLM_BASE_URL>",
|
|
962
|
-
"MCP_URL": "<YOUR_MCP_URL>",
|
|
963
|
-
"MEMBERSTOOL": "True",
|
|
964
|
-
"MERGE_REQUESTSTOOL": "True",
|
|
965
|
-
"MERGE_RULESTOOL": "True",
|
|
966
|
-
"MISCTOOL": "True",
|
|
967
|
-
"MODEL_ID": "<YOUR_MODEL_ID>",
|
|
968
|
-
"PACKAGESTOOL": "True",
|
|
969
|
-
"PIPELINESTOOL": "True",
|
|
970
|
-
"PIPELINE_SCHEDULESTOOL": "True",
|
|
971
|
-
"PROJECTSTOOL": "True",
|
|
972
|
-
"PROTECTED_BRANCHESTOOL": "True",
|
|
973
|
-
"RELEASESTOOL": "True",
|
|
974
|
-
"RUNNERSTOOL": "True",
|
|
975
|
-
"TAGSTOOL": "True"
|
|
976
|
-
}
|
|
980
|
+
"GITLAB_URL": "",
|
|
981
|
+
"GITLAB_TOKEN": ""
|
|
982
|
+
}
|
|
977
983
|
}
|
|
978
984
|
}
|
|
979
985
|
}
|
|
980
986
|
```
|
|
981
987
|
|
|
982
|
-
###
|
|
983
|
-
|
|
988
|
+
### Streamable HTTP (recommended for production)
|
|
984
989
|
```json
|
|
985
990
|
{
|
|
986
991
|
"mcpServers": {
|
|
987
|
-
"gitlab
|
|
988
|
-
"
|
|
989
|
-
"args": [
|
|
990
|
-
"run",
|
|
991
|
-
"gitlab-mcp",
|
|
992
|
-
"--transport",
|
|
993
|
-
"http",
|
|
994
|
-
"--host",
|
|
995
|
-
"0.0.0.0",
|
|
996
|
-
"--port",
|
|
997
|
-
"8000"
|
|
998
|
-
],
|
|
999
|
-
"env": {
|
|
1000
|
-
"AGENT_DESCRIPTION": "<YOUR_AGENT_DESCRIPTION>",
|
|
1001
|
-
"AGENT_SYSTEM_PROMPT": "<YOUR_AGENT_SYSTEM_PROMPT>",
|
|
1002
|
-
"BRANCHESTOOL": "True",
|
|
1003
|
-
"COMMITSTOOL": "True",
|
|
1004
|
-
"CUSTOM_APITOOL": "True",
|
|
1005
|
-
"DEFAULT_AGENT_NAME": "<YOUR_DEFAULT_AGENT_NAME>",
|
|
1006
|
-
"DEPLOY_TOKENSTOOL": "True",
|
|
1007
|
-
"ENVIRONMENTSTOOL": "True",
|
|
1008
|
-
"GITLAB_SSL_VERIFY": "<YOUR_GITLAB_SSL_VERIFY>",
|
|
1009
|
-
"GITLAB_TOKEN": "<YOUR_GITLAB_TOKEN>",
|
|
1010
|
-
"GITLAB_URL": "<YOUR_GITLAB_URL>",
|
|
1011
|
-
"GITLAB_VERIFY": "<YOUR_GITLAB_VERIFY>",
|
|
1012
|
-
"GROUPSTOOL": "True",
|
|
1013
|
-
"JOBSTOOL": "True",
|
|
1014
|
-
"LLM_API_KEY": "<YOUR_LLM_API_KEY>",
|
|
1015
|
-
"LLM_BASE_URL": "<YOUR_LLM_BASE_URL>",
|
|
1016
|
-
"MCP_URL": "<YOUR_MCP_URL>",
|
|
1017
|
-
"MEMBERSTOOL": "True",
|
|
1018
|
-
"MERGE_REQUESTSTOOL": "True",
|
|
1019
|
-
"MERGE_RULESTOOL": "True",
|
|
1020
|
-
"MISCTOOL": "True",
|
|
1021
|
-
"MODEL_ID": "<YOUR_MODEL_ID>",
|
|
1022
|
-
"PACKAGESTOOL": "True",
|
|
1023
|
-
"PIPELINESTOOL": "True",
|
|
1024
|
-
"PIPELINE_SCHEDULESTOOL": "True",
|
|
1025
|
-
"PROJECTSTOOL": "True",
|
|
1026
|
-
"PROTECTED_BRANCHESTOOL": "True",
|
|
1027
|
-
"RELEASESTOOL": "True",
|
|
1028
|
-
"RUNNERSTOOL": "True",
|
|
1029
|
-
"TAGSTOOL": "True"
|
|
1030
|
-
}
|
|
992
|
+
"gitlab": {
|
|
993
|
+
"url": "http://localhost:8080/gitlab-mcp/mcp"
|
|
1031
994
|
}
|
|
1032
995
|
}
|
|
1033
996
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|

|
|
22
22
|

|
|
23
23
|
|
|
24
|
-
*Version: 25.
|
|
24
|
+
*Version: 25.24.1*
|
|
25
25
|
|
|
26
26
|
## Overview
|
|
27
27
|
|
|
@@ -852,6 +852,42 @@ For Testing Only: Plain text storage will also work, although **not** recommende
|
|
|
852
852
|
}
|
|
853
853
|
```
|
|
854
854
|
|
|
855
|
+
## Security & Governance
|
|
856
|
+
|
|
857
|
+
This project is built on [`agent-utilities`](https://github.com/Knuckles-Team/agent-utilities), inheriting enterprise-grade security and governance features.
|
|
858
|
+
|
|
859
|
+
### Authentication & Authorization
|
|
860
|
+
| Feature | Description |
|
|
861
|
+
|---------|-------------|
|
|
862
|
+
| **OIDC Token Delegation** | RFC 8693 token exchange for user-context propagation from A2A → MCP |
|
|
863
|
+
| **Eunomia Policies** | Fine-grained, policy-driven tool authorization (`none`, `embedded`, `remote`) |
|
|
864
|
+
| **Scoped Credentials** | Tools execute with the caller's scoped identity where possible |
|
|
865
|
+
| **3LO / OAuth / API Token** | Multiple auth strategies with graceful fallback |
|
|
866
|
+
|
|
867
|
+
### Eunomia Policy Enforcement
|
|
868
|
+
Eunomia provides a policy enforcement point for all tool calls:
|
|
869
|
+
- **Embedded mode**: Load local `mcp_policies.json` for role-based access, sensitivity gating, and audit logging
|
|
870
|
+
- **Remote mode**: Forward authorization decisions to a central Eunomia policy server for multi-agent governance
|
|
871
|
+
- Enable via CLI: `--eunomia-type embedded --eunomia-policy-file mcp_policies.json`
|
|
872
|
+
|
|
873
|
+
### Runtime Protections
|
|
874
|
+
| Protection | Description |
|
|
875
|
+
|------------|-------------|
|
|
876
|
+
| **Tool Guard** | Sensitivity detection with human-in-the-loop approval gating |
|
|
877
|
+
| **Prompt Injection Defense** | Input scanning and repetition/loop guards |
|
|
878
|
+
| **Content Filtering** | Output schema enforcement and cost budget controls |
|
|
879
|
+
| **Stuck Loop Detection** | Automatic detection and recovery from agent loops |
|
|
880
|
+
| **Context Limit Warnings** | Proactive alerts before context window exhaustion |
|
|
881
|
+
|
|
882
|
+
### Graph Agent Architecture
|
|
883
|
+
The A2A agent uses `pydantic-graph` orchestration with:
|
|
884
|
+
- **RouterNode**: Lightweight classifier that routes queries to specialized domains
|
|
885
|
+
- **DomainNode**: Focused executor with only relevant tools loaded, preventing tool hallucination
|
|
886
|
+
- **Approval Gates**: Policy-driven approval workflows before sensitive operations
|
|
887
|
+
- **Usage Guards**: Budget and rate limiting enforcement
|
|
888
|
+
|
|
889
|
+
> **Production Recommendation**: Enable `--eunomia-type embedded` (or `remote`) + OIDC delegation + containerized deployment. See [`agent-utilities` documentation](https://github.com/Knuckles-Team/agent-utilities) for full policy configuration.
|
|
890
|
+
|
|
855
891
|
## Install Python Package
|
|
856
892
|
|
|
857
893
|
Install Python Package
|
|
@@ -903,103 +939,28 @@ npx @modelcontextprotocol/inspector gitlab-mcp
|
|
|
903
939
|
|
|
904
940
|
## MCP Configuration Examples
|
|
905
941
|
|
|
906
|
-
###
|
|
907
|
-
|
|
942
|
+
### stdio (recommended for local development)
|
|
908
943
|
```json
|
|
909
944
|
{
|
|
910
945
|
"mcpServers": {
|
|
911
|
-
"gitlab
|
|
912
|
-
"command": "
|
|
913
|
-
"args": [
|
|
914
|
-
"run",
|
|
915
|
-
"gitlab-mcp"
|
|
916
|
-
],
|
|
946
|
+
"gitlab": {
|
|
947
|
+
"command": ".venv/bin/gitlab-mcp",
|
|
948
|
+
"args": [],
|
|
917
949
|
"env": {
|
|
918
|
-
"
|
|
919
|
-
"
|
|
920
|
-
|
|
921
|
-
"COMMITSTOOL": "True",
|
|
922
|
-
"CUSTOM_APITOOL": "True",
|
|
923
|
-
"DEFAULT_AGENT_NAME": "<YOUR_DEFAULT_AGENT_NAME>",
|
|
924
|
-
"DEPLOY_TOKENSTOOL": "True",
|
|
925
|
-
"ENVIRONMENTSTOOL": "True",
|
|
926
|
-
"GITLAB_SSL_VERIFY": "<YOUR_GITLAB_SSL_VERIFY>",
|
|
927
|
-
"GITLAB_TOKEN": "<YOUR_GITLAB_TOKEN>",
|
|
928
|
-
"GITLAB_URL": "<YOUR_GITLAB_URL>",
|
|
929
|
-
"GITLAB_VERIFY": "<YOUR_GITLAB_VERIFY>",
|
|
930
|
-
"GROUPSTOOL": "True",
|
|
931
|
-
"JOBSTOOL": "True",
|
|
932
|
-
"LLM_API_KEY": "<YOUR_LLM_API_KEY>",
|
|
933
|
-
"LLM_BASE_URL": "<YOUR_LLM_BASE_URL>",
|
|
934
|
-
"MCP_URL": "<YOUR_MCP_URL>",
|
|
935
|
-
"MEMBERSTOOL": "True",
|
|
936
|
-
"MERGE_REQUESTSTOOL": "True",
|
|
937
|
-
"MERGE_RULESTOOL": "True",
|
|
938
|
-
"MISCTOOL": "True",
|
|
939
|
-
"MODEL_ID": "<YOUR_MODEL_ID>",
|
|
940
|
-
"PACKAGESTOOL": "True",
|
|
941
|
-
"PIPELINESTOOL": "True",
|
|
942
|
-
"PIPELINE_SCHEDULESTOOL": "True",
|
|
943
|
-
"PROJECTSTOOL": "True",
|
|
944
|
-
"PROTECTED_BRANCHESTOOL": "True",
|
|
945
|
-
"RELEASESTOOL": "True",
|
|
946
|
-
"RUNNERSTOOL": "True",
|
|
947
|
-
"TAGSTOOL": "True"
|
|
948
|
-
}
|
|
950
|
+
"GITLAB_URL": "",
|
|
951
|
+
"GITLAB_TOKEN": ""
|
|
952
|
+
}
|
|
949
953
|
}
|
|
950
954
|
}
|
|
951
955
|
}
|
|
952
956
|
```
|
|
953
957
|
|
|
954
|
-
###
|
|
955
|
-
|
|
958
|
+
### Streamable HTTP (recommended for production)
|
|
956
959
|
```json
|
|
957
960
|
{
|
|
958
961
|
"mcpServers": {
|
|
959
|
-
"gitlab
|
|
960
|
-
"
|
|
961
|
-
"args": [
|
|
962
|
-
"run",
|
|
963
|
-
"gitlab-mcp",
|
|
964
|
-
"--transport",
|
|
965
|
-
"http",
|
|
966
|
-
"--host",
|
|
967
|
-
"0.0.0.0",
|
|
968
|
-
"--port",
|
|
969
|
-
"8000"
|
|
970
|
-
],
|
|
971
|
-
"env": {
|
|
972
|
-
"AGENT_DESCRIPTION": "<YOUR_AGENT_DESCRIPTION>",
|
|
973
|
-
"AGENT_SYSTEM_PROMPT": "<YOUR_AGENT_SYSTEM_PROMPT>",
|
|
974
|
-
"BRANCHESTOOL": "True",
|
|
975
|
-
"COMMITSTOOL": "True",
|
|
976
|
-
"CUSTOM_APITOOL": "True",
|
|
977
|
-
"DEFAULT_AGENT_NAME": "<YOUR_DEFAULT_AGENT_NAME>",
|
|
978
|
-
"DEPLOY_TOKENSTOOL": "True",
|
|
979
|
-
"ENVIRONMENTSTOOL": "True",
|
|
980
|
-
"GITLAB_SSL_VERIFY": "<YOUR_GITLAB_SSL_VERIFY>",
|
|
981
|
-
"GITLAB_TOKEN": "<YOUR_GITLAB_TOKEN>",
|
|
982
|
-
"GITLAB_URL": "<YOUR_GITLAB_URL>",
|
|
983
|
-
"GITLAB_VERIFY": "<YOUR_GITLAB_VERIFY>",
|
|
984
|
-
"GROUPSTOOL": "True",
|
|
985
|
-
"JOBSTOOL": "True",
|
|
986
|
-
"LLM_API_KEY": "<YOUR_LLM_API_KEY>",
|
|
987
|
-
"LLM_BASE_URL": "<YOUR_LLM_BASE_URL>",
|
|
988
|
-
"MCP_URL": "<YOUR_MCP_URL>",
|
|
989
|
-
"MEMBERSTOOL": "True",
|
|
990
|
-
"MERGE_REQUESTSTOOL": "True",
|
|
991
|
-
"MERGE_RULESTOOL": "True",
|
|
992
|
-
"MISCTOOL": "True",
|
|
993
|
-
"MODEL_ID": "<YOUR_MODEL_ID>",
|
|
994
|
-
"PACKAGESTOOL": "True",
|
|
995
|
-
"PIPELINESTOOL": "True",
|
|
996
|
-
"PIPELINE_SCHEDULESTOOL": "True",
|
|
997
|
-
"PROJECTSTOOL": "True",
|
|
998
|
-
"PROTECTED_BRANCHESTOOL": "True",
|
|
999
|
-
"RELEASESTOOL": "True",
|
|
1000
|
-
"RUNNERSTOOL": "True",
|
|
1001
|
-
"TAGSTOOL": "True"
|
|
1002
|
-
}
|
|
962
|
+
"gitlab": {
|
|
963
|
+
"url": "http://localhost:8080/gitlab-mcp/mcp"
|
|
1003
964
|
}
|
|
1004
965
|
}
|
|
1005
966
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import inspect
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
__all__: list[str] = []
|
|
8
|
+
|
|
9
|
+
CORE_MODULES: list[str] = [
|
|
10
|
+
"gitlab_api.gitlab_input_models",
|
|
11
|
+
"gitlab_api.gitlab_response_models",
|
|
12
|
+
"gitlab_api.api_client",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
OPTIONAL_MODULES = {
|
|
16
|
+
"gitlab_api.gitlab_gql": "gql",
|
|
17
|
+
"gitlab_api.agent_server": "agent",
|
|
18
|
+
"gitlab_api.mcp_server": "mcp",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _expose_members(module):
|
|
23
|
+
"""Expose public classes and functions from a module into globals and __all__."""
|
|
24
|
+
for name, obj in inspect.getmembers(module):
|
|
25
|
+
if (inspect.isclass(obj) or inspect.isfunction(obj)) and not name.startswith(
|
|
26
|
+
"_"
|
|
27
|
+
):
|
|
28
|
+
globals()[name] = obj
|
|
29
|
+
if name not in __all__:
|
|
30
|
+
__all__.append(name)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Eagerly import core modules (keeps API wrappers fast & light)
|
|
34
|
+
for module_name in CORE_MODULES:
|
|
35
|
+
if module_name:
|
|
36
|
+
module = importlib.import_module(module_name)
|
|
37
|
+
_expose_members(module)
|
|
38
|
+
|
|
39
|
+
# Dynamic/lazy loading of optional modules (agent_server, mcp_server)
|
|
40
|
+
_loaded_optional_modules = {}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _import_module_safely(module_name: str):
|
|
44
|
+
"""Try to import a module and return it, or None if not available."""
|
|
45
|
+
try:
|
|
46
|
+
return importlib.import_module(module_name)
|
|
47
|
+
except ImportError:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def __getattr__(name: str) -> Any:
|
|
52
|
+
# Handle availability flags dynamically without eager imports
|
|
53
|
+
if name == "_MCP_AVAILABLE":
|
|
54
|
+
mcp_key = next((k for k in OPTIONAL_MODULES if "mcp_server" in k), None)
|
|
55
|
+
if mcp_key:
|
|
56
|
+
return _import_module_safely(mcp_key) is not None
|
|
57
|
+
return False
|
|
58
|
+
if name == "_AGENT_AVAILABLE":
|
|
59
|
+
agent_key = next((k for k in OPTIONAL_MODULES if "agent_server" in k), None)
|
|
60
|
+
if agent_key:
|
|
61
|
+
return _import_module_safely(agent_key) is not None
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
# Check optional modules
|
|
65
|
+
for module_name in OPTIONAL_MODULES:
|
|
66
|
+
if module_name not in _loaded_optional_modules:
|
|
67
|
+
module = _import_module_safely(module_name)
|
|
68
|
+
if module is not None:
|
|
69
|
+
_loaded_optional_modules[module_name] = module
|
|
70
|
+
_expose_members(module)
|
|
71
|
+
|
|
72
|
+
module = _loaded_optional_modules.get(module_name)
|
|
73
|
+
if module is not None and hasattr(module, name):
|
|
74
|
+
return getattr(module, name)
|
|
75
|
+
|
|
76
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def __dir__() -> list[str]:
|
|
80
|
+
return sorted(list(globals().keys()) + __all__)
|
|
@@ -4,15 +4,7 @@ import os
|
|
|
4
4
|
import sys
|
|
5
5
|
import warnings
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
build_system_prompt_from_workspace,
|
|
9
|
-
create_agent_parser,
|
|
10
|
-
create_graph_agent_server,
|
|
11
|
-
initialize_workspace,
|
|
12
|
-
load_identity,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
__version__ = "25.23.0"
|
|
7
|
+
__version__ = "25.24.1"
|
|
16
8
|
|
|
17
9
|
logging.basicConfig(
|
|
18
10
|
level=logging.INFO,
|
|
@@ -22,23 +14,36 @@ logging.basicConfig(
|
|
|
22
14
|
logger = logging.getLogger(__name__)
|
|
23
15
|
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
DEFAULT_AGENT_DESCRIPTION = os.getenv(
|
|
29
|
-
"AGENT_DESCRIPTION",
|
|
30
|
-
meta.get(
|
|
31
|
-
"description",
|
|
32
|
-
"AI agent for GitLab Api management.",
|
|
33
|
-
),
|
|
34
|
-
)
|
|
35
|
-
DEFAULT_AGENT_SYSTEM_PROMPT = os.getenv(
|
|
36
|
-
"AGENT_SYSTEM_PROMPT",
|
|
37
|
-
meta.get("content") or build_system_prompt_from_workspace(),
|
|
38
|
-
)
|
|
17
|
+
DEFAULT_AGENT_NAME = None
|
|
18
|
+
DEFAULT_AGENT_DESCRIPTION = None
|
|
19
|
+
DEFAULT_AGENT_SYSTEM_PROMPT = None
|
|
39
20
|
|
|
40
21
|
|
|
41
22
|
def agent_server():
|
|
23
|
+
from agent_utilities import (
|
|
24
|
+
build_system_prompt_from_workspace,
|
|
25
|
+
create_agent_parser,
|
|
26
|
+
create_agent_server,
|
|
27
|
+
initialize_workspace,
|
|
28
|
+
load_identity,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
global DEFAULT_AGENT_NAME, DEFAULT_AGENT_DESCRIPTION, DEFAULT_AGENT_SYSTEM_PROMPT
|
|
32
|
+
initialize_workspace()
|
|
33
|
+
meta = load_identity()
|
|
34
|
+
DEFAULT_AGENT_NAME = os.getenv("DEFAULT_AGENT_NAME", meta.get("name", "Gitlab Api"))
|
|
35
|
+
DEFAULT_AGENT_DESCRIPTION = os.getenv(
|
|
36
|
+
"AGENT_DESCRIPTION",
|
|
37
|
+
meta.get(
|
|
38
|
+
"description",
|
|
39
|
+
"AI agent for GitLab Api management.",
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
DEFAULT_AGENT_SYSTEM_PROMPT = os.getenv(
|
|
43
|
+
"AGENT_SYSTEM_PROMPT",
|
|
44
|
+
meta.get("content") or build_system_prompt_from_workspace(),
|
|
45
|
+
)
|
|
46
|
+
|
|
42
47
|
warnings.filterwarnings("ignore", message=".*urllib3.*or chardet.*")
|
|
43
48
|
warnings.filterwarnings("ignore", category=DeprecationWarning, module="fastmcp")
|
|
44
49
|
|
|
@@ -51,7 +56,7 @@ def agent_server():
|
|
|
51
56
|
logger.debug("Debug mode enabled")
|
|
52
57
|
|
|
53
58
|
# Start server using the auto-discovery pattern (from mcp_config.json)
|
|
54
|
-
|
|
59
|
+
create_agent_server(
|
|
55
60
|
mcp_url=args.mcp_url,
|
|
56
61
|
mcp_config=args.mcp_config or "mcp_config.json",
|
|
57
62
|
host=args.host,
|
|
@@ -3456,6 +3456,36 @@ class Api:
|
|
|
3456
3456
|
except ValidationError as e:
|
|
3457
3457
|
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
3458
3458
|
|
|
3459
|
+
@require_auth
|
|
3460
|
+
def delete_shared_project_link(self, **kwargs) -> Response:
|
|
3461
|
+
"""
|
|
3462
|
+
Unshare a specific project from a group.
|
|
3463
|
+
|
|
3464
|
+
Args:
|
|
3465
|
+
**kwargs: Additional parameters for the request (e.g., project_id, group_id).
|
|
3466
|
+
|
|
3467
|
+
Returns:
|
|
3468
|
+
Response: A wrapper containing the original response (no data for successful deletion).
|
|
3469
|
+
|
|
3470
|
+
Raises:
|
|
3471
|
+
MissingParameterError: If the project_id or group_id is missing.
|
|
3472
|
+
ParameterError: If invalid parameters are provided.
|
|
3473
|
+
"""
|
|
3474
|
+
project = ProjectModel(**kwargs)
|
|
3475
|
+
if project.project_id is None or project.group_id is None:
|
|
3476
|
+
raise MissingParameterError
|
|
3477
|
+
try:
|
|
3478
|
+
response = self._session.delete(
|
|
3479
|
+
url=f"{self.url}/projects/{project.project_id}/share/{project.group_id}",
|
|
3480
|
+
headers=self.headers,
|
|
3481
|
+
verify=self.verify,
|
|
3482
|
+
proxies=self.proxies,
|
|
3483
|
+
)
|
|
3484
|
+
response.raise_for_status()
|
|
3485
|
+
return Response(response=response)
|
|
3486
|
+
except ValidationError as e:
|
|
3487
|
+
raise ParameterError(f"Invalid parameters: {e.errors()}") from e
|
|
3488
|
+
|
|
3459
3489
|
@require_auth
|
|
3460
3490
|
def get_protected_branches(self, **kwargs) -> Response:
|
|
3461
3491
|
"""
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""GitLab Authentication Module.
|
|
2
|
+
|
|
3
|
+
Authentication priority:
|
|
4
|
+
1. **OIDC Delegation** — If ``ENABLE_DELEGATION`` is active, exchanges
|
|
5
|
+
the IdP-issued user token for a downstream GitLab access token
|
|
6
|
+
via RFC 8693 Token Exchange using the shared ``delegated_auth`` helper.
|
|
7
|
+
2. **Fixed Credentials** — Falls back to ``GITLAB_TOKEN`` env var.
|
|
8
|
+
|
|
9
|
+
See ``docs/guides/oauth_sso.md`` in agent-utilities for full details.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import threading
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from agent_utilities.base_utilities import get_logger, to_boolean
|
|
17
|
+
from agent_utilities.core.exceptions import AuthError, UnauthorizedError
|
|
18
|
+
|
|
19
|
+
local = threading.local()
|
|
20
|
+
from gitlab_api.api_client import Api
|
|
21
|
+
|
|
22
|
+
logger = get_logger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_client(
|
|
26
|
+
instance: str = os.getenv("GITLAB_URL", "https://gitlab.com"),
|
|
27
|
+
token: str | None = os.getenv("GITLAB_TOKEN", None),
|
|
28
|
+
verify: bool = to_boolean(string=os.getenv("GITLAB_SSL_VERIFY", "True")),
|
|
29
|
+
config: dict | None = None,
|
|
30
|
+
) -> Api:
|
|
31
|
+
"""Factory function to create the GitLab Api client.
|
|
32
|
+
|
|
33
|
+
Supports OIDC delegation and fixed credentials (token).
|
|
34
|
+
Uses the shared ``delegated_auth`` helper from agent-utilities.
|
|
35
|
+
"""
|
|
36
|
+
from agent_utilities.mcp.delegated_auth import (
|
|
37
|
+
get_delegated_token,
|
|
38
|
+
get_user_identity,
|
|
39
|
+
is_delegation_enabled,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Resolve delegation config — prefer shared mcp_auth_config
|
|
43
|
+
delegation_enabled = is_delegation_enabled(config)
|
|
44
|
+
|
|
45
|
+
# --- Path 1: OIDC Delegation (RFC 8693 Token Exchange) ---
|
|
46
|
+
if delegation_enabled:
|
|
47
|
+
try:
|
|
48
|
+
delegated_token = get_delegated_token(
|
|
49
|
+
config=config,
|
|
50
|
+
audience=(config or {}).get("audience", instance),
|
|
51
|
+
scopes=(config or {}).get("delegated_scopes", "api"),
|
|
52
|
+
verify=verify,
|
|
53
|
+
)
|
|
54
|
+
identity = get_user_identity()
|
|
55
|
+
logger.info(
|
|
56
|
+
"Using OIDC delegated token for GitLab API",
|
|
57
|
+
extra={
|
|
58
|
+
"user_email": identity.get("email"),
|
|
59
|
+
"instance": instance,
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
return Api(url=instance, token=delegated_token, verify=verify)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error(
|
|
65
|
+
"OIDC delegation failed for GitLab",
|
|
66
|
+
extra={"error_type": type(e).__name__, "error_message": str(e)},
|
|
67
|
+
)
|
|
68
|
+
raise RuntimeError(f"Token exchange failed: {str(e)}") from e
|
|
69
|
+
|
|
70
|
+
# --- Path 2: Fixed Credentials (GITLAB_TOKEN) ---
|
|
71
|
+
logger.info("Using fixed credentials for GitLab API")
|
|
72
|
+
try:
|
|
73
|
+
return Api(url=instance, token=token, verify=verify)
|
|
74
|
+
except (AuthError, UnauthorizedError) as e:
|
|
75
|
+
raise RuntimeError(
|
|
76
|
+
f"AUTHENTICATION ERROR: The GitLab credentials provided are not valid for '{instance}'. "
|
|
77
|
+
f"Please check your GITLAB_TOKEN and GITLAB_URL environment variables. "
|
|
78
|
+
f"Error details: {str(e)}"
|
|
79
|
+
) from e
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_graphql_client(
|
|
83
|
+
instance: str = os.getenv("GITLAB_URL", "https://gitlab.com"),
|
|
84
|
+
token: str | None = os.getenv("GITLAB_TOKEN", None),
|
|
85
|
+
verify: bool = to_boolean(string=os.getenv("GITLAB_SSL_VERIFY", "True")),
|
|
86
|
+
config: dict | None = None,
|
|
87
|
+
) -> Any:
|
|
88
|
+
"""Factory function to create the GitLab GraphQL client.
|
|
89
|
+
|
|
90
|
+
Supports OIDC delegation and fixed credentials (token).
|
|
91
|
+
"""
|
|
92
|
+
from agent_utilities.mcp.delegated_auth import (
|
|
93
|
+
get_delegated_token,
|
|
94
|
+
get_user_identity,
|
|
95
|
+
is_delegation_enabled,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
from gitlab_api.gitlab_gql import GraphQL
|
|
99
|
+
|
|
100
|
+
# Resolve delegation config — prefer shared mcp_auth_config
|
|
101
|
+
delegation_enabled = is_delegation_enabled(config)
|
|
102
|
+
|
|
103
|
+
# --- Path 1: OIDC Delegation (RFC 8693 Token Exchange) ---
|
|
104
|
+
if delegation_enabled:
|
|
105
|
+
try:
|
|
106
|
+
delegated_token = get_delegated_token(
|
|
107
|
+
config=config,
|
|
108
|
+
audience=(config or {}).get("audience", instance),
|
|
109
|
+
scopes=(config or {}).get("delegated_scopes", "api"),
|
|
110
|
+
verify=verify,
|
|
111
|
+
)
|
|
112
|
+
identity = get_user_identity()
|
|
113
|
+
logger.info(
|
|
114
|
+
"Using OIDC delegated token for GitLab GraphQL API",
|
|
115
|
+
extra={
|
|
116
|
+
"user_email": identity.get("email"),
|
|
117
|
+
"instance": instance,
|
|
118
|
+
},
|
|
119
|
+
)
|
|
120
|
+
return GraphQL(url=instance, token=delegated_token, verify=verify)
|
|
121
|
+
except Exception as e:
|
|
122
|
+
logger.error(
|
|
123
|
+
"OIDC delegation failed for GitLab GraphQL",
|
|
124
|
+
extra={"error_type": type(e).__name__, "error_message": str(e)},
|
|
125
|
+
)
|
|
126
|
+
raise RuntimeError(f"Token exchange failed: {str(e)}") from e
|
|
127
|
+
|
|
128
|
+
# --- Path 2: Fixed Credentials (GITLAB_TOKEN) ---
|
|
129
|
+
logger.info("Using fixed credentials for GitLab GraphQL API")
|
|
130
|
+
if not token:
|
|
131
|
+
raise RuntimeError("GITLAB_TOKEN environment variable or parameter is missing.")
|
|
132
|
+
return GraphQL(url=instance, token=token, verify=verify)
|