gitlab-api 27.3.12__tar.gz → 27.3.14__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 (85) hide show
  1. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/PKG-INFO +2 -2
  2. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/connector_sources.py +45 -0
  3. gitlab_api-27.3.14/gitlab_api/ontology/access_contracts.ttl +287 -0
  4. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/ontology/certification.json +2 -2
  5. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/ontology/gitlab.ttl +20 -0
  6. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api.egg-info/PKG-INFO +2 -2
  7. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api.egg-info/SOURCES.txt +3 -0
  8. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api.egg-info/requires.txt +1 -1
  9. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/pyproject.toml +2 -2
  10. gitlab_api-27.3.14/tests/test_access_contracts.py +75 -0
  11. gitlab_api-27.3.14/tests/test_catalog_edges.py +53 -0
  12. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/LICENSE +0 -0
  13. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/README.md +0 -0
  14. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/__init__.py +0 -0
  15. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/__main__.py +0 -0
  16. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/__init__.py +0 -0
  17. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_base.py +0 -0
  18. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_environments.py +0 -0
  19. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_issues.py +0 -0
  20. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_merge_requests.py +0 -0
  21. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_other.py +0 -0
  22. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_pipelines.py +0 -0
  23. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_projects.py +0 -0
  24. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_repositories.py +0 -0
  25. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_system.py +0 -0
  26. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_users_groups.py +0 -0
  27. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api/api_client_vulnerabilities.py +0 -0
  28. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/api_client.py +0 -0
  29. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/auth.py +0 -0
  30. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/connector_prep.py +0 -0
  31. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/gitlab_gql.py +0 -0
  32. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/gitlab_input_models.py +0 -0
  33. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/gitlab_response_models.py +0 -0
  34. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/instances.py +0 -0
  35. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/kg_ingest.py +0 -0
  36. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/mcp_config.json +0 -0
  37. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/mcp_server.py +0 -0
  38. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/ontology/__init__.py +0 -0
  39. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/ontology/fixtures/records.json +0 -0
  40. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/ontology/mappings/source.yaml +0 -0
  41. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/ontology/migrations/manifest.json +0 -0
  42. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/ontology/shapes/connector.shacl.ttl +0 -0
  43. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/prompts/devops_specialist.json +0 -0
  44. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-api-operations/SKILL.md +0 -0
  45. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-api-operations/agents/openai.yaml +0 -0
  46. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-api-operations/references/catalog.md +0 -0
  47. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-issues/SKILL.md +0 -0
  48. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-issues/WORKFLOW.md +0 -0
  49. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-merge-request-create/SKILL.md +0 -0
  50. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-merge-request-create/WORKFLOW.md +0 -0
  51. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-merge-request-review/SKILL.md +0 -0
  52. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-merge-request-review/WORKFLOW.md +0 -0
  53. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-pipelines/SKILL.md +0 -0
  54. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-pipelines/WORKFLOW.md +0 -0
  55. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-vulnerabilities/SKILL.md +0 -0
  56. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api/skills/gitlab-vulnerabilities/WORKFLOW.md +0 -0
  57. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api.egg-info/dependency_links.txt +0 -0
  58. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api.egg-info/entry_points.txt +0 -0
  59. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/gitlab_api.egg-info/top_level.txt +0 -0
  60. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/scripts/check_import_safety.py +0 -0
  61. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/scripts/security_contract.py +0 -0
  62. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/setup.cfg +0 -0
  63. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/__init__.py +0 -0
  64. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/conftest.py +0 -0
  65. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_api_wrapper.py +0 -0
  66. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_auth.py +0 -0
  67. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_connector_prep.py +0 -0
  68. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_cq_paths.py +0 -0
  69. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_fleet_name.py +0 -0
  70. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_gitlab_a2a_validation.py +0 -0
  71. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_gitlab_api_brute_force_coverage.py +0 -0
  72. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_gitlab_gql.py +0 -0
  73. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_gitlab_mcp_validation.py +0 -0
  74. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_gitlab_models.py +0 -0
  75. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_identity_context_scoping.py +0 -0
  76. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_ingest_sources.py +0 -0
  77. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_init_dynamics.py +0 -0
  78. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_instances.py +0 -0
  79. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_kg_ingest.py +0 -0
  80. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_mock_coverage.py +0 -0
  81. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_ontology_parent_link.py +0 -0
  82. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_startup.py +0 -0
  83. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_tool_modes.py +0 -0
  84. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/test_verify_agent.py +0 -0
  85. {gitlab_api-27.3.12 → gitlab_api-27.3.14}/tests/verify_a2a_queries.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitlab-api
3
- Version: 27.3.12
3
+ Version: 27.3.14
4
4
  Summary: GitLab API + MCP Server
5
5
  Author-email: Repository Maintainers <maintainers@knuckles.team>
6
6
  License: MIT
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.12
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: agent-connector-sdk[graphql,mcp]<0.6.0,>=0.5.3
15
+ Requires-Dist: agent-connector-sdk[graphql,mcp]<0.6.0,>=0.5.5
16
16
  Requires-Dist: python-dotenv>=1.2.4
17
17
  Provides-Extra: mcp
18
18
  Provides-Extra: all
@@ -24,6 +24,24 @@ SOURCES = (
24
24
  records_path="data",
25
25
  text_field="description",
26
26
  title_field="title",
27
+ edges=[
28
+ {
29
+ "relationship": "issueAuthoredBy",
30
+ "target_field": "author",
31
+ "target_type": "Person",
32
+ "target_id_key": "id",
33
+ "target_id_template": "user:{id}",
34
+ "target_fields": {"name": "name", "username": "username"},
35
+ },
36
+ {
37
+ "relationship": "issueAssignedTo",
38
+ "target_field": "assignees",
39
+ "target_type": "Person",
40
+ "target_id_key": "id",
41
+ "target_id_template": "user:{id}",
42
+ "target_fields": {"name": "name", "username": "username"},
43
+ },
44
+ ],
27
45
  ),
28
46
  source_meta(
29
47
  tool="gitlab_get_projects",
@@ -45,6 +63,24 @@ SOURCES = (
45
63
  server="gitlab-mcp",
46
64
  text_field="description",
47
65
  title_field="path_with_namespace",
66
+ edges=[
67
+ {
68
+ "relationship": "projectOwnedBy",
69
+ "target_field": "owner",
70
+ "target_type": "Person",
71
+ "target_id_key": "id",
72
+ "target_id_template": "user:{id}",
73
+ "target_fields": {"name": "name", "username": "username"},
74
+ },
75
+ {
76
+ "relationship": "partOfGroup",
77
+ "target_field": "namespace",
78
+ "target_type": "GitLabGroup",
79
+ "target_id_key": "id",
80
+ "target_id_template": "group:{id}",
81
+ "target_fields": {"name": "full_path"},
82
+ },
83
+ ],
48
84
  ),
49
85
  )
50
86
 
@@ -97,6 +133,10 @@ SCHEMA_MAPPINGS = {
97
133
  "field_sets": ["fields_1"],
98
134
  "ontology_class": "PipelineRun",
99
135
  },
136
+ "Person": {
137
+ "fields": {},
138
+ "ontology_class": "Person",
139
+ },
100
140
  "Project": {
101
141
  "field_sets": ["fields_1"],
102
142
  "ontology_class": "SoftwareProject",
@@ -134,6 +174,11 @@ RESOURCES = (
134
174
  "label": "Merge Request",
135
175
  "name": "MergeRequest",
136
176
  },
177
+ {
178
+ "id_prefix": "person",
179
+ "label": "Person",
180
+ "name": "Person",
181
+ },
137
182
  {
138
183
  "id_prefix": "pipeline",
139
184
  "label": "Pipeline",
@@ -0,0 +1,287 @@
1
+ @prefix ac: <https://knuckles-team.github.io/agent-connector-sdk/access#> .
2
+ @prefix gl: <http://knuckles.team/kg#> .
3
+
4
+ # Read contracts for the classes declared by gitlab.ttl. The MCP tools expose
5
+ # both read and write actions, but these mappings are read-only query routes:
6
+ # callers select the listed get action through the operation's action envelope.
7
+ # The source classes stay in GitLab's ontology namespace. No heuristic
8
+ # crosswalk to a hub class is asserted here.
9
+
10
+ gl:gitlabGroupAccess a ac:AccessContract ;
11
+ ac:classIri gl:GitLabGroup ;
12
+ ac:sourceId "gitlab-api" ;
13
+ ac:entity "group" ;
14
+ ac:keyField "id" ;
15
+ ac:accessKind "mcp_tool" ;
16
+ ac:operation "gitlab_groups" ;
17
+ ac:parameter "action", "params_json" ;
18
+ ac:pagination "page" ;
19
+ ac:pushdown "filter", "limit", "sort" ;
20
+ ac:authRef "env://GITLAB_TOKEN" ;
21
+ ac:rateLimitPerMinute 60 ;
22
+ ac:freshnessSeconds 300 ;
23
+ ac:costHint "medium" .
24
+
25
+ gl:gitlabGroupWebUrlBinding a ac:PropertyBinding ;
26
+ ac:contract gl:gitlabGroupAccess ; ac:property gl:webUrl ; ac:field "web_url" .
27
+
28
+ gl:projectAccess a ac:AccessContract ;
29
+ ac:classIri gl:Project ;
30
+ ac:sourceId "gitlab-api" ;
31
+ ac:entity "project" ;
32
+ ac:keyField "id" ;
33
+ ac:accessKind "mcp_tool" ;
34
+ ac:operation "gitlab_projects" ;
35
+ ac:parameter "action", "params_json" ;
36
+ ac:pagination "page" ;
37
+ ac:pushdown "filter", "limit", "sort" ;
38
+ ac:authRef "env://GITLAB_TOKEN" ;
39
+ ac:rateLimitPerMinute 60 ;
40
+ ac:freshnessSeconds 300 ;
41
+ ac:costHint "low" .
42
+
43
+ gl:projectWebUrlBinding a ac:PropertyBinding ;
44
+ ac:contract gl:projectAccess ; ac:property gl:webUrl ; ac:field "web_url" .
45
+
46
+ gl:mergeRequestAccess a ac:AccessContract ;
47
+ ac:classIri gl:MergeRequest ;
48
+ ac:sourceId "gitlab-api" ;
49
+ ac:entity "merge_request" ;
50
+ ac:keyField "id" ;
51
+ ac:accessKind "mcp_tool" ;
52
+ ac:operation "gitlab_merge_requests" ;
53
+ ac:parameter "action", "params_json" ;
54
+ ac:pagination "page" ;
55
+ ac:pushdown "filter", "limit", "sort" ;
56
+ ac:authRef "env://GITLAB_TOKEN" ;
57
+ ac:rateLimitPerMinute 60 ;
58
+ ac:freshnessSeconds 60 ;
59
+ ac:costHint "medium" .
60
+
61
+ gl:mergeRequestIidBinding a ac:PropertyBinding ;
62
+ ac:contract gl:mergeRequestAccess ; ac:property gl:iid ; ac:field "iid" .
63
+
64
+ gl:mergeRequestStateBinding a ac:PropertyBinding ;
65
+ ac:contract gl:mergeRequestAccess ; ac:property gl:state ; ac:field "state" .
66
+
67
+ gl:mergeRequestWebUrlBinding a ac:PropertyBinding ;
68
+ ac:contract gl:mergeRequestAccess ; ac:property gl:webUrl ; ac:field "web_url" .
69
+
70
+ gl:pipelineAccess a ac:AccessContract ;
71
+ ac:classIri gl:Pipeline ;
72
+ ac:sourceId "gitlab-api" ;
73
+ ac:entity "pipeline" ;
74
+ ac:keyField "id" ;
75
+ ac:accessKind "mcp_tool" ;
76
+ ac:operation "gitlab_pipelines" ;
77
+ ac:parameter "action", "params_json" ;
78
+ ac:pagination "page" ;
79
+ ac:pushdown "filter", "limit", "sort" ;
80
+ ac:authRef "env://GITLAB_TOKEN" ;
81
+ ac:rateLimitPerMinute 60 ;
82
+ ac:freshnessSeconds 60 ;
83
+ ac:costHint "medium" .
84
+
85
+ gl:pipelineStatusBinding a ac:PropertyBinding ;
86
+ ac:contract gl:pipelineAccess ; ac:property gl:pipelineStatus ; ac:field "status" .
87
+
88
+ gl:pipelineRefBinding a ac:PropertyBinding ;
89
+ ac:contract gl:pipelineAccess ; ac:property gl:ref ; ac:field "ref" .
90
+
91
+ gl:pipelineShaBinding a ac:PropertyBinding ;
92
+ ac:contract gl:pipelineAccess ; ac:property gl:sha ; ac:field "sha" .
93
+
94
+ gl:pipelineWebUrlBinding a ac:PropertyBinding ;
95
+ ac:contract gl:pipelineAccess ; ac:property gl:webUrl ; ac:field "web_url" .
96
+
97
+ gl:pipelineRunAccess a ac:AccessContract ;
98
+ ac:classIri gl:PipelineRun ;
99
+ ac:sourceId "gitlab-api" ;
100
+ ac:entity "pipeline" ;
101
+ ac:keyField "id" ;
102
+ ac:accessKind "mcp_tool" ;
103
+ ac:operation "gitlab_pipelines" ;
104
+ ac:parameter "action", "params_json" ;
105
+ ac:pagination "page" ;
106
+ ac:pushdown "filter", "limit", "sort" ;
107
+ ac:authRef "env://GITLAB_TOKEN" ;
108
+ ac:rateLimitPerMinute 60 ;
109
+ ac:freshnessSeconds 60 ;
110
+ ac:costHint "medium" .
111
+
112
+ gl:jobAccess a ac:AccessContract ;
113
+ ac:classIri gl:Job ;
114
+ ac:sourceId "gitlab-api" ;
115
+ ac:entity "job" ;
116
+ ac:keyField "id" ;
117
+ ac:accessKind "mcp_tool" ;
118
+ ac:operation "gitlab_jobs" ;
119
+ ac:parameter "action", "params_json" ;
120
+ ac:pagination "page" ;
121
+ ac:pushdown "filter", "limit", "sort" ;
122
+ ac:authRef "env://GITLAB_TOKEN" ;
123
+ ac:rateLimitPerMinute 60 ;
124
+ ac:freshnessSeconds 60 ;
125
+ ac:costHint "medium" .
126
+
127
+ gl:jobStatusBinding a ac:PropertyBinding ;
128
+ ac:contract gl:jobAccess ; ac:property gl:pipelineStatus ; ac:field "status" .
129
+
130
+ gl:jobRefBinding a ac:PropertyBinding ;
131
+ ac:contract gl:jobAccess ; ac:property gl:ref ; ac:field "ref" .
132
+
133
+ gl:jobWebUrlBinding a ac:PropertyBinding ;
134
+ ac:contract gl:jobAccess ; ac:property gl:webUrl ; ac:field "web_url" .
135
+
136
+ gl:checkRunAccess a ac:AccessContract ;
137
+ ac:classIri gl:CheckRun ;
138
+ ac:sourceId "gitlab-api" ;
139
+ ac:entity "job" ;
140
+ ac:keyField "id" ;
141
+ ac:accessKind "mcp_tool" ;
142
+ ac:operation "gitlab_jobs" ;
143
+ ac:parameter "action", "params_json" ;
144
+ ac:pagination "page" ;
145
+ ac:pushdown "filter", "limit", "sort" ;
146
+ ac:authRef "env://GITLAB_TOKEN" ;
147
+ ac:rateLimitPerMinute 60 ;
148
+ ac:freshnessSeconds 60 ;
149
+ ac:costHint "medium" .
150
+
151
+ gl:issueAccess a ac:AccessContract ;
152
+ ac:classIri gl:Issue ;
153
+ ac:sourceId "gitlab-api" ;
154
+ ac:entity "issue" ;
155
+ ac:keyField "id" ;
156
+ ac:accessKind "mcp_tool" ;
157
+ ac:operation "gitlab_issues" ;
158
+ ac:parameter "action", "params_json" ;
159
+ ac:pagination "page" ;
160
+ ac:pushdown "filter", "limit", "sort" ;
161
+ ac:authRef "env://GITLAB_TOKEN" ;
162
+ ac:rateLimitPerMinute 60 ;
163
+ ac:freshnessSeconds 60 ;
164
+ ac:costHint "medium" .
165
+
166
+ gl:issueIidBinding a ac:PropertyBinding ;
167
+ ac:contract gl:issueAccess ; ac:property gl:iid ; ac:field "iid" .
168
+
169
+ gl:issueStateBinding a ac:PropertyBinding ;
170
+ ac:contract gl:issueAccess ; ac:property gl:state ; ac:field "state" .
171
+
172
+ gl:issueWebUrlBinding a ac:PropertyBinding ;
173
+ ac:contract gl:issueAccess ; ac:property gl:webUrl ; ac:field "web_url" .
174
+
175
+ gl:epicAccess a ac:AccessContract ;
176
+ ac:classIri gl:Epic ;
177
+ ac:sourceId "gitlab-api" ;
178
+ ac:entity "epic" ;
179
+ ac:keyField "id" ;
180
+ ac:accessKind "mcp_tool" ;
181
+ ac:operation "gitlab_epics" ;
182
+ ac:parameter "action", "params_json" ;
183
+ ac:pagination "page" ;
184
+ ac:pushdown "filter", "limit", "sort" ;
185
+ ac:authRef "env://GITLAB_TOKEN" ;
186
+ ac:rateLimitPerMinute 60 ;
187
+ ac:freshnessSeconds 300 ;
188
+ ac:costHint "medium" .
189
+
190
+ gl:epicIidBinding a ac:PropertyBinding ;
191
+ ac:contract gl:epicAccess ; ac:property gl:iid ; ac:field "iid" .
192
+
193
+ gl:epicWebUrlBinding a ac:PropertyBinding ;
194
+ ac:contract gl:epicAccess ; ac:property gl:webUrl ; ac:field "web_url" .
195
+
196
+ gl:milestoneAccess a ac:AccessContract ;
197
+ ac:classIri gl:Milestone ;
198
+ ac:sourceId "gitlab-api" ;
199
+ ac:entity "milestone" ;
200
+ ac:keyField "id" ;
201
+ ac:accessKind "mcp_tool" ;
202
+ ac:operation "gitlab_milestones" ;
203
+ ac:parameter "action", "params_json" ;
204
+ ac:pagination "page" ;
205
+ ac:pushdown "filter", "limit", "sort" ;
206
+ ac:authRef "env://GITLAB_TOKEN" ;
207
+ ac:rateLimitPerMinute 60 ;
208
+ ac:freshnessSeconds 300 ;
209
+ ac:costHint "medium" .
210
+
211
+ gl:milestoneStateBinding a ac:PropertyBinding ;
212
+ ac:contract gl:milestoneAccess ; ac:property gl:state ; ac:field "state" .
213
+
214
+ gl:milestoneWebUrlBinding a ac:PropertyBinding ;
215
+ ac:contract gl:milestoneAccess ; ac:property gl:webUrl ; ac:field "web_url" .
216
+
217
+ gl:commitAccess a ac:AccessContract ;
218
+ ac:classIri gl:Commit ;
219
+ ac:sourceId "gitlab-api" ;
220
+ ac:entity "commit" ;
221
+ ac:keyField "id" ;
222
+ ac:accessKind "mcp_tool" ;
223
+ ac:operation "gitlab_commits" ;
224
+ ac:parameter "action", "params_json" ;
225
+ ac:pagination "page" ;
226
+ ac:pushdown "filter", "limit", "sort" ;
227
+ ac:authRef "env://GITLAB_TOKEN" ;
228
+ ac:rateLimitPerMinute 60 ;
229
+ ac:freshnessSeconds 60 ;
230
+ ac:costHint "medium" .
231
+
232
+ gl:commitShaBinding a ac:PropertyBinding ;
233
+ ac:contract gl:commitAccess ; ac:property gl:sha ; ac:field "sha" .
234
+
235
+ gl:commitWebUrlBinding a ac:PropertyBinding ;
236
+ ac:contract gl:commitAccess ; ac:property gl:webUrl ; ac:field "web_url" .
237
+
238
+ gl:branchAccess a ac:AccessContract ;
239
+ ac:classIri gl:Branch ;
240
+ ac:sourceId "gitlab-api" ;
241
+ ac:entity "branch" ;
242
+ ac:keyField "name" ;
243
+ ac:accessKind "mcp_tool" ;
244
+ ac:operation "gitlab_branches" ;
245
+ ac:parameter "action", "params_json" ;
246
+ ac:pagination "page" ;
247
+ ac:pushdown "filter", "limit", "sort" ;
248
+ ac:authRef "env://GITLAB_TOKEN" ;
249
+ ac:rateLimitPerMinute 60 ;
250
+ ac:freshnessSeconds 300 ;
251
+ ac:costHint "low" .
252
+
253
+ gl:branchWebUrlBinding a ac:PropertyBinding ;
254
+ ac:contract gl:branchAccess ; ac:property gl:webUrl ; ac:field "web_url" .
255
+
256
+ gl:releaseAccess a ac:AccessContract ;
257
+ ac:classIri gl:Release ;
258
+ ac:sourceId "gitlab-api" ;
259
+ ac:entity "release" ;
260
+ ac:keyField "tag_name" ;
261
+ ac:accessKind "mcp_tool" ;
262
+ ac:operation "gitlab_releases" ;
263
+ ac:parameter "action", "params_json" ;
264
+ ac:pagination "page" ;
265
+ ac:pushdown "filter", "limit", "sort" ;
266
+ ac:authRef "env://GITLAB_TOKEN" ;
267
+ ac:rateLimitPerMinute 60 ;
268
+ ac:freshnessSeconds 300 ;
269
+ ac:costHint "medium" .
270
+
271
+ gl:runnerAccess a ac:AccessContract ;
272
+ ac:classIri gl:Runner ;
273
+ ac:sourceId "gitlab-api" ;
274
+ ac:entity "runner" ;
275
+ ac:keyField "id" ;
276
+ ac:accessKind "mcp_tool" ;
277
+ ac:operation "gitlab_runners" ;
278
+ ac:parameter "action", "params_json" ;
279
+ ac:pagination "page" ;
280
+ ac:pushdown "filter", "limit", "sort" ;
281
+ ac:authRef "env://GITLAB_TOKEN" ;
282
+ ac:rateLimitPerMinute 60 ;
283
+ ac:freshnessSeconds 300 ;
284
+ ac:costHint "medium" .
285
+
286
+ gl:runnerStateBinding a ac:PropertyBinding ;
287
+ ac:contract gl:runnerAccess ; ac:property gl:state ; ac:field "status" .
@@ -2,7 +2,7 @@
2
2
  "api_version": "graphos.io/v1",
3
3
  "artifacts": {
4
4
  "gitlab_api/ontology/fixtures/records.json": "5f0417820053941933f15b3f459750753f4e8e34cf1e7853fd030c1143356b40",
5
- "gitlab_api/ontology/gitlab.ttl": "c7f170c5ba51d657be7bad3f27595c8d91639f46e3259efffd486cfb60969240",
5
+ "gitlab_api/ontology/gitlab.ttl": "4a674b5efb7f6f0f6e1f46084940ffe550f0a95d9c6338366df9da01dd8c207c",
6
6
  "gitlab_api/ontology/mappings/source.yaml": "4d845dd7df9503ae3a5d133ff00a98191d11adf3af7f6868f45c22bcbf098bb5",
7
7
  "gitlab_api/ontology/migrations/manifest.json": "442c3c53df0b42c866eef7255b1889b8ada6f9f9c5125cad65d6b50dc6362a3e",
8
8
  "gitlab_api/ontology/shapes/connector.shacl.ttl": "20d2bfc3827fd6327a3b6f25828b41d9de3f855867c46264a7e0aa7f951c76fa"
@@ -29,5 +29,5 @@
29
29
  "signer": "UNSIGNED-PREVIEW",
30
30
  "signing_public_key": "",
31
31
  "status": "source-validated",
32
- "validated_at": "2026-10-24T06:26:59Z"
32
+ "validated_at": "2026-10-25T22:08:01Z"
33
33
  }
@@ -114,6 +114,26 @@
114
114
  rdfs:comment "Links a MergeRequest, Issue, or Commit to the Person who created it." ;
115
115
  rdfs:range :Person .
116
116
 
117
+ :projectOwnedBy a owl:ObjectProperty ;
118
+ rdfs:label "project owned by" ;
119
+ rdfs:comment "Links a Project to the Person who owns it (projects.owner, user-namespace projects)." ;
120
+ rdfs:subPropertyOf :ownedBy ;
121
+ rdfs:domain :Project ;
122
+ rdfs:range :Person .
123
+
124
+ :issueAuthoredBy a owl:ObjectProperty ;
125
+ rdfs:label "issue authored by" ;
126
+ rdfs:comment "Links an Issue to the Person who opened it (issues.author)." ;
127
+ rdfs:subPropertyOf :authoredBy ;
128
+ rdfs:domain :Issue ;
129
+ rdfs:range :Person .
130
+
131
+ :issueAssignedTo a owl:ObjectProperty ;
132
+ rdfs:label "issue assigned to" ;
133
+ rdfs:comment "Links an Issue to a Person it is assigned to (issues.assignees)." ;
134
+ rdfs:domain :Issue ;
135
+ rdfs:range :Person .
136
+
117
137
  # --- fields (owl:DatatypeProperty) ---
118
138
 
119
139
  :iid a owl:DatatypeProperty ;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitlab-api
3
- Version: 27.3.12
3
+ Version: 27.3.14
4
4
  Summary: GitLab API + MCP Server
5
5
  Author-email: Repository Maintainers <maintainers@knuckles.team>
6
6
  License: MIT
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.12
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: agent-connector-sdk[graphql,mcp]<0.6.0,>=0.5.3
15
+ Requires-Dist: agent-connector-sdk[graphql,mcp]<0.6.0,>=0.5.5
16
16
  Requires-Dist: python-dotenv>=1.2.4
17
17
  Provides-Extra: mcp
18
18
  Provides-Extra: all
@@ -33,6 +33,7 @@ gitlab_api/api/api_client_system.py
33
33
  gitlab_api/api/api_client_users_groups.py
34
34
  gitlab_api/api/api_client_vulnerabilities.py
35
35
  gitlab_api/ontology/__init__.py
36
+ gitlab_api/ontology/access_contracts.ttl
36
37
  gitlab_api/ontology/certification.json
37
38
  gitlab_api/ontology/gitlab.ttl
38
39
  gitlab_api/ontology/fixtures/records.json
@@ -57,8 +58,10 @@ scripts/check_import_safety.py
57
58
  scripts/security_contract.py
58
59
  tests/__init__.py
59
60
  tests/conftest.py
61
+ tests/test_access_contracts.py
60
62
  tests/test_api_wrapper.py
61
63
  tests/test_auth.py
64
+ tests/test_catalog_edges.py
62
65
  tests/test_connector_prep.py
63
66
  tests/test_cq_paths.py
64
67
  tests/test_fleet_name.py
@@ -1,4 +1,4 @@
1
- agent-connector-sdk[graphql,mcp]<0.6.0,>=0.5.3
1
+ agent-connector-sdk[graphql,mcp]<0.6.0,>=0.5.5
2
2
  python-dotenv>=1.2.4
3
3
 
4
4
  [all]
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gitlab-api"
7
- version = "27.3.12"
7
+ version = "27.3.14"
8
8
  description = "GitLab API + MCP Server"
9
9
  readme = "README.md"
10
10
  classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Environment :: Console", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3",]
11
11
  requires-python = ">=3.12"
12
- dependencies = [ "agent-connector-sdk[graphql,mcp]>=0.5.3,<0.6.0", "python-dotenv>=1.2.4",]
12
+ dependencies = [ "agent-connector-sdk[graphql,mcp]>=0.5.5,<0.6.0", "python-dotenv>=1.2.4",]
13
13
  [[project.authors]]
14
14
  name = "Repository Maintainers"
15
15
  email = "maintainers@knuckles.team"
@@ -0,0 +1,75 @@
1
+ """GitLab's read-only source access contracts stay source-local."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from agent_connector_sdk.access_contract import parse_access_contracts
8
+
9
+
10
+ ROOT = Path(__file__).resolve().parents[1]
11
+ CONTRACTS = ROOT / "gitlab_api" / "ontology" / "access_contracts.ttl"
12
+ KG = "http://knuckles.team/kg#"
13
+
14
+ EXPECTED = {
15
+ "GitLabGroup": ("gitlab_groups", "group", "id"),
16
+ "Project": ("gitlab_projects", "project", "id"),
17
+ "MergeRequest": ("gitlab_merge_requests", "merge_request", "id"),
18
+ "Pipeline": ("gitlab_pipelines", "pipeline", "id"),
19
+ "PipelineRun": ("gitlab_pipelines", "pipeline", "id"),
20
+ "Job": ("gitlab_jobs", "job", "id"),
21
+ "CheckRun": ("gitlab_jobs", "job", "id"),
22
+ "Issue": ("gitlab_issues", "issue", "id"),
23
+ "Epic": ("gitlab_epics", "epic", "id"),
24
+ "Milestone": ("gitlab_milestones", "milestone", "id"),
25
+ "Commit": ("gitlab_commits", "commit", "id"),
26
+ "Branch": ("gitlab_branches", "branch", "name"),
27
+ "Release": ("gitlab_releases", "release", "tag_name"),
28
+ "Runner": ("gitlab_runners", "runner", "id"),
29
+ }
30
+
31
+
32
+ def _contracts():
33
+ return parse_access_contracts(CONTRACTS.read_text(encoding="utf-8"))
34
+
35
+
36
+ def test_every_declared_gitlab_class_has_a_typed_read_contract() -> None:
37
+ contracts = _contracts()
38
+ by_class = {contract.class_iri.rsplit("#", 1)[-1]: contract for contract in contracts}
39
+
40
+ assert set(by_class) == set(EXPECTED)
41
+ for class_name, (operation, entity, key_field) in EXPECTED.items():
42
+ contract = by_class[class_name]
43
+ assert contract.class_iri == KG + class_name
44
+ assert contract.source_id == "gitlab-api"
45
+ assert contract.access_kind == "mcp_tool"
46
+ assert contract.operation == operation
47
+ assert contract.entity == entity
48
+ assert contract.key_field == key_field
49
+ assert contract.parameters == ("action", "params_json")
50
+ assert contract.auth_ref == "env://GITLAB_TOKEN"
51
+ assert contract.pagination == "page"
52
+ assert contract.pushdown == frozenset({"filter", "limit", "sort"})
53
+
54
+
55
+ def test_gitlab_contracts_do_not_promote_draft_crosswalks() -> None:
56
+ contracts = _contracts()
57
+
58
+ assert all(contract.class_iri.startswith(KG) for contract in contracts)
59
+ assert all(
60
+ contract.class_iri not in {KG + "Incident", KG + "SoftwareProject"}
61
+ for contract in contracts
62
+ )
63
+ assert all(contract.source_id == "gitlab-api" for contract in contracts)
64
+
65
+
66
+ def test_gitlab_contract_artifact_is_packaged_under_ontology() -> None:
67
+ assert CONTRACTS.is_file()
68
+
69
+
70
+ def test_gitlab_contract_bindings_use_declared_fields() -> None:
71
+ contracts = {contract.class_iri.rsplit("#", 1)[-1]: contract for contract in _contracts()}
72
+
73
+ assert (KG + "webUrl", "web_url") in contracts["Project"].predicates
74
+ assert (KG + "pipelineStatus", "status") in contracts["Pipeline"].predicates
75
+ assert (KG + "sha", "sha") in contracts["Commit"].predicates
@@ -0,0 +1,53 @@
1
+ """Owner and assignee edges declared on the ingest sources (CONN-GITLAB-API-CONNECTOR-CONTENT-R005)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+ from agent_connector_sdk.ingest.live_declaration import load_declaration
9
+ from agent_connector_sdk.ingest.source_sync import _edges # noqa: PLC2701
10
+
11
+ import gitlab_api
12
+
13
+ ONTOLOGY = Path(gitlab_api.__file__).parent / "ontology" / "gitlab.ttl"
14
+
15
+
16
+ def _declaration():
17
+ return load_declaration("gitlab_api")
18
+
19
+
20
+ def _domain_and_range(name: str) -> tuple[str, str]:
21
+ block = ONTOLOGY.read_text(encoding="utf-8").split(f"\n:{name} a owl:ObjectProperty")[1]
22
+ block = block.split(" .\n")[0]
23
+ return block.split("rdfs:domain :")[1].split()[0], block.split("rdfs:range :")[1].split()[0]
24
+
25
+
26
+ @pytest.mark.spec("CONN-GITLAB-API-CONNECTOR-CONTENT-R005")
27
+ def test_every_declared_edge_is_an_ontology_property_of_its_record_type() -> None:
28
+ declaration = _declaration()
29
+ seen = set()
30
+ for source in declaration.sources:
31
+ for edge in source.edges:
32
+ assert _domain_and_range(edge.relationship) == (source.record_type, edge.target_type)
33
+ assert edge.target_type in declaration.schema_mappings
34
+ seen.add((source.source_id, edge.relationship))
35
+ assert seen == {
36
+ ("issues", "issueAuthoredBy"),
37
+ ("issues", "issueAssignedTo"),
38
+ ("projects", "projectOwnedBy"),
39
+ ("projects", "partOfGroup"),
40
+ }
41
+
42
+
43
+ @pytest.mark.spec("CONN-GITLAB-API-CONNECTOR-CONTENT-R005")
44
+ def test_project_and_issue_references_become_owner_and_assignee_edges() -> None:
45
+ declaration = _declaration()
46
+ project = {"id": 213, "owner": {"id": 5, "username": "ann", "name": "Ann"}, "namespace": {"id": 9, "full_path": "homelab"}}
47
+ issue = {"id": 2, "author": {"id": 5, "username": "ann"}, "assignees": [{"id": 6, "username": "bob"}, {"id": 5}]}
48
+ projects = {(r.relationship, r.target) for _, r in _edges("213", project, declaration.source("projects"))}
49
+ issues = {(r.relationship, r.target) for _, r in _edges("2", issue, declaration.source("issues"))}
50
+ assert projects == {("projectOwnedBy", "user:5"), ("partOfGroup", "group:9")}
51
+ assert issues == {("issueAuthoredBy", "user:5"), ("issueAssignedTo", "user:6"), ("issueAssignedTo", "user:5")}
52
+ owner = next(t for t, r in _edges("213", project, declaration.source("projects")) if r.relationship == "projectOwnedBy")
53
+ assert owner.node_type == "Person" and owner.properties == {"name": "Ann", "username": "ann"}
File without changes
File without changes
File without changes