gitlab-api 27.3.0__py3-none-any.whl → 27.3.2__py3-none-any.whl
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/connector_manifest.yml +439 -0
- gitlab_api/connector_prep.py +24 -15
- gitlab_api/gitlab_gql.py +22 -12
- gitlab_api/mcp_server.py +5 -5
- gitlab_api/ontology/ingest_sources.json +19 -0
- {gitlab_api-27.3.0.dist-info → gitlab_api-27.3.2.dist-info}/METADATA +4 -4
- {gitlab_api-27.3.0.dist-info → gitlab_api-27.3.2.dist-info}/RECORD +17 -13
- scripts/security_sanitizer.py +8 -0
- scripts/verify_api_integration.py +4 -1
- tests/test_auth.py +4 -4
- tests/test_connector_prep.py +1 -1
- tests/test_ingest_sources.py +35 -0
- tests/test_security_sanitizer.py +30 -0
- {gitlab_api-27.3.0.dist-info → gitlab_api-27.3.2.dist-info}/WHEEL +0 -0
- {gitlab_api-27.3.0.dist-info → gitlab_api-27.3.2.dist-info}/entry_points.txt +0 -0
- {gitlab_api-27.3.0.dist-info → gitlab_api-27.3.2.dist-info}/licenses/LICENSE +0 -0
- {gitlab_api-27.3.0.dist-info → gitlab_api-27.3.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
connector: gitlab-api
|
|
2
|
+
ontology_source: gitlab
|
|
3
|
+
schema_version: '1'
|
|
4
|
+
resources:
|
|
5
|
+
- name: Branch
|
|
6
|
+
label: Branch
|
|
7
|
+
id_prefix: branch
|
|
8
|
+
relations: []
|
|
9
|
+
- name: CheckRun
|
|
10
|
+
label: Check Run
|
|
11
|
+
id_prefix: checkrun
|
|
12
|
+
relations: []
|
|
13
|
+
- name: Commit
|
|
14
|
+
label: Commit
|
|
15
|
+
id_prefix: commit
|
|
16
|
+
relations: []
|
|
17
|
+
- name: Epic
|
|
18
|
+
label: Epic
|
|
19
|
+
id_prefix: epic
|
|
20
|
+
relations: []
|
|
21
|
+
- name: GitLabGroup
|
|
22
|
+
label: GitLab Group
|
|
23
|
+
id_prefix: gitlabgroup
|
|
24
|
+
relations: []
|
|
25
|
+
- name: Issue
|
|
26
|
+
label: Issue
|
|
27
|
+
id_prefix: issue
|
|
28
|
+
relations: []
|
|
29
|
+
- name: Job
|
|
30
|
+
label: Job
|
|
31
|
+
id_prefix: job
|
|
32
|
+
relations:
|
|
33
|
+
- name: ranOnRunner
|
|
34
|
+
label: ran on runner
|
|
35
|
+
target: Runner
|
|
36
|
+
lpg_rel_type: ''
|
|
37
|
+
- name: MergeRequest
|
|
38
|
+
label: Merge Request
|
|
39
|
+
id_prefix: mergerequest
|
|
40
|
+
relations:
|
|
41
|
+
- name: closesIssue
|
|
42
|
+
label: closes issue
|
|
43
|
+
target: Issue
|
|
44
|
+
lpg_rel_type: ''
|
|
45
|
+
- name: targetsBranch
|
|
46
|
+
label: targets branch
|
|
47
|
+
target: Branch
|
|
48
|
+
lpg_rel_type: ''
|
|
49
|
+
- name: Milestone
|
|
50
|
+
label: Milestone
|
|
51
|
+
id_prefix: milestone
|
|
52
|
+
relations: []
|
|
53
|
+
- name: Pipeline
|
|
54
|
+
label: Pipeline
|
|
55
|
+
id_prefix: pipeline
|
|
56
|
+
relations:
|
|
57
|
+
- name: hasJob
|
|
58
|
+
label: has job
|
|
59
|
+
target: Job
|
|
60
|
+
lpg_rel_type: ''
|
|
61
|
+
- name: PipelineRun
|
|
62
|
+
label: Pipeline Run
|
|
63
|
+
id_prefix: pipelinerun
|
|
64
|
+
relations:
|
|
65
|
+
- name: ranFor
|
|
66
|
+
label: ran for
|
|
67
|
+
target: owl:Thing
|
|
68
|
+
lpg_rel_type: ''
|
|
69
|
+
- name: Project
|
|
70
|
+
label: Project
|
|
71
|
+
id_prefix: project
|
|
72
|
+
relations:
|
|
73
|
+
- name: partOfGroup
|
|
74
|
+
label: part of group
|
|
75
|
+
target: GitLabGroup
|
|
76
|
+
lpg_rel_type: ''
|
|
77
|
+
- name: Release
|
|
78
|
+
label: Release
|
|
79
|
+
id_prefix: release
|
|
80
|
+
relations: []
|
|
81
|
+
- name: Runner
|
|
82
|
+
label: Runner
|
|
83
|
+
id_prefix: runner
|
|
84
|
+
relations: []
|
|
85
|
+
actions:
|
|
86
|
+
- id: epistemic-answer
|
|
87
|
+
name: Epistemic Answer
|
|
88
|
+
description: 'Answers epistemic_status/why/what_changed queries over the shared knowledge graph: calibrated
|
|
89
|
+
confidence, evidence/source citations, belief justification trees, bitemporal valid/tx history, and
|
|
90
|
+
policy-redaction-aware provenance.'
|
|
91
|
+
label: ''
|
|
92
|
+
parameters: []
|
|
93
|
+
target_resource: null
|
|
94
|
+
conflict_policy: null
|
|
95
|
+
requires_approval: true
|
|
96
|
+
approval_class: unclassified
|
|
97
|
+
effects: []
|
|
98
|
+
- id: run_graph_flow
|
|
99
|
+
name: Graph Flow Execution
|
|
100
|
+
description: Execute a workflow through the agent's graph orchestration engine
|
|
101
|
+
label: ''
|
|
102
|
+
parameters: []
|
|
103
|
+
target_resource: null
|
|
104
|
+
conflict_policy: null
|
|
105
|
+
requires_approval: true
|
|
106
|
+
approval_class: unclassified
|
|
107
|
+
effects: []
|
|
108
|
+
events:
|
|
109
|
+
- name: gitlab-projects.updated
|
|
110
|
+
resource: repository
|
|
111
|
+
description: "Watermark event for 'gitlab-projects' \u2014 advances on last_activity_at (mcp_source_presets.json)."
|
|
112
|
+
identity:
|
|
113
|
+
id_field:
|
|
114
|
+
repository: id
|
|
115
|
+
title_field:
|
|
116
|
+
repository: path_with_namespace
|
|
117
|
+
text_field:
|
|
118
|
+
repository: description
|
|
119
|
+
updated_field:
|
|
120
|
+
repository: last_activity_at
|
|
121
|
+
permissions:
|
|
122
|
+
acl_fields: []
|
|
123
|
+
tenant_field: null
|
|
124
|
+
read_roles: []
|
|
125
|
+
schema_mappings:
|
|
126
|
+
Branch:
|
|
127
|
+
ontology_class: null
|
|
128
|
+
fields:
|
|
129
|
+
createdAt: xsd:dateTime
|
|
130
|
+
duration: xsd:decimal
|
|
131
|
+
failureReason: xsd:string
|
|
132
|
+
finishedAt: xsd:dateTime
|
|
133
|
+
iid: xsd:string
|
|
134
|
+
logUrl: xsd:string
|
|
135
|
+
pipelineStatus: xsd:string
|
|
136
|
+
ref: xsd:string
|
|
137
|
+
sha: xsd:string
|
|
138
|
+
startedAt: xsd:dateTime
|
|
139
|
+
state: xsd:string
|
|
140
|
+
triggerSource: xsd:string
|
|
141
|
+
webUrl: xsd:string
|
|
142
|
+
CheckRun:
|
|
143
|
+
ontology_class: null
|
|
144
|
+
fields:
|
|
145
|
+
createdAt: xsd:dateTime
|
|
146
|
+
duration: xsd:decimal
|
|
147
|
+
failureReason: xsd:string
|
|
148
|
+
finishedAt: xsd:dateTime
|
|
149
|
+
iid: xsd:string
|
|
150
|
+
logUrl: xsd:string
|
|
151
|
+
pipelineStatus: xsd:string
|
|
152
|
+
ref: xsd:string
|
|
153
|
+
sha: xsd:string
|
|
154
|
+
startedAt: xsd:dateTime
|
|
155
|
+
state: xsd:string
|
|
156
|
+
triggerSource: xsd:string
|
|
157
|
+
webUrl: xsd:string
|
|
158
|
+
Commit:
|
|
159
|
+
ontology_class: null
|
|
160
|
+
fields:
|
|
161
|
+
createdAt: xsd:dateTime
|
|
162
|
+
duration: xsd:decimal
|
|
163
|
+
failureReason: xsd:string
|
|
164
|
+
finishedAt: xsd:dateTime
|
|
165
|
+
iid: xsd:string
|
|
166
|
+
logUrl: xsd:string
|
|
167
|
+
pipelineStatus: xsd:string
|
|
168
|
+
ref: xsd:string
|
|
169
|
+
sha: xsd:string
|
|
170
|
+
startedAt: xsd:dateTime
|
|
171
|
+
state: xsd:string
|
|
172
|
+
triggerSource: xsd:string
|
|
173
|
+
webUrl: xsd:string
|
|
174
|
+
Epic:
|
|
175
|
+
ontology_class: null
|
|
176
|
+
fields:
|
|
177
|
+
createdAt: xsd:dateTime
|
|
178
|
+
duration: xsd:decimal
|
|
179
|
+
failureReason: xsd:string
|
|
180
|
+
finishedAt: xsd:dateTime
|
|
181
|
+
iid: xsd:string
|
|
182
|
+
logUrl: xsd:string
|
|
183
|
+
pipelineStatus: xsd:string
|
|
184
|
+
ref: xsd:string
|
|
185
|
+
sha: xsd:string
|
|
186
|
+
startedAt: xsd:dateTime
|
|
187
|
+
state: xsd:string
|
|
188
|
+
triggerSource: xsd:string
|
|
189
|
+
webUrl: xsd:string
|
|
190
|
+
GitLabGroup:
|
|
191
|
+
ontology_class: Organization
|
|
192
|
+
fields:
|
|
193
|
+
createdAt: xsd:dateTime
|
|
194
|
+
duration: xsd:decimal
|
|
195
|
+
failureReason: xsd:string
|
|
196
|
+
finishedAt: xsd:dateTime
|
|
197
|
+
iid: xsd:string
|
|
198
|
+
logUrl: xsd:string
|
|
199
|
+
pipelineStatus: xsd:string
|
|
200
|
+
ref: xsd:string
|
|
201
|
+
sha: xsd:string
|
|
202
|
+
startedAt: xsd:dateTime
|
|
203
|
+
state: xsd:string
|
|
204
|
+
triggerSource: xsd:string
|
|
205
|
+
webUrl: xsd:string
|
|
206
|
+
Issue:
|
|
207
|
+
ontology_class: Incident
|
|
208
|
+
fields:
|
|
209
|
+
createdAt: xsd:dateTime
|
|
210
|
+
duration: xsd:decimal
|
|
211
|
+
failureReason: xsd:string
|
|
212
|
+
finishedAt: xsd:dateTime
|
|
213
|
+
iid: xsd:string
|
|
214
|
+
logUrl: xsd:string
|
|
215
|
+
pipelineStatus: xsd:string
|
|
216
|
+
ref: xsd:string
|
|
217
|
+
sha: xsd:string
|
|
218
|
+
startedAt: xsd:dateTime
|
|
219
|
+
state: xsd:string
|
|
220
|
+
triggerSource: xsd:string
|
|
221
|
+
webUrl: xsd:string
|
|
222
|
+
Job:
|
|
223
|
+
ontology_class: Action
|
|
224
|
+
fields:
|
|
225
|
+
createdAt: xsd:dateTime
|
|
226
|
+
duration: xsd:decimal
|
|
227
|
+
failureReason: xsd:string
|
|
228
|
+
finishedAt: xsd:dateTime
|
|
229
|
+
iid: xsd:string
|
|
230
|
+
logUrl: xsd:string
|
|
231
|
+
pipelineStatus: xsd:string
|
|
232
|
+
ref: xsd:string
|
|
233
|
+
sha: xsd:string
|
|
234
|
+
startedAt: xsd:dateTime
|
|
235
|
+
state: xsd:string
|
|
236
|
+
triggerSource: xsd:string
|
|
237
|
+
webUrl: xsd:string
|
|
238
|
+
MergeRequest:
|
|
239
|
+
ontology_class: null
|
|
240
|
+
fields:
|
|
241
|
+
createdAt: xsd:dateTime
|
|
242
|
+
duration: xsd:decimal
|
|
243
|
+
failureReason: xsd:string
|
|
244
|
+
finishedAt: xsd:dateTime
|
|
245
|
+
iid: xsd:string
|
|
246
|
+
logUrl: xsd:string
|
|
247
|
+
pipelineStatus: xsd:string
|
|
248
|
+
ref: xsd:string
|
|
249
|
+
sha: xsd:string
|
|
250
|
+
startedAt: xsd:dateTime
|
|
251
|
+
state: xsd:string
|
|
252
|
+
triggerSource: xsd:string
|
|
253
|
+
webUrl: xsd:string
|
|
254
|
+
Milestone:
|
|
255
|
+
ontology_class: null
|
|
256
|
+
fields:
|
|
257
|
+
createdAt: xsd:dateTime
|
|
258
|
+
duration: xsd:decimal
|
|
259
|
+
failureReason: xsd:string
|
|
260
|
+
finishedAt: xsd:dateTime
|
|
261
|
+
iid: xsd:string
|
|
262
|
+
logUrl: xsd:string
|
|
263
|
+
pipelineStatus: xsd:string
|
|
264
|
+
ref: xsd:string
|
|
265
|
+
sha: xsd:string
|
|
266
|
+
startedAt: xsd:dateTime
|
|
267
|
+
state: xsd:string
|
|
268
|
+
triggerSource: xsd:string
|
|
269
|
+
webUrl: xsd:string
|
|
270
|
+
Pipeline:
|
|
271
|
+
ontology_class: null
|
|
272
|
+
fields:
|
|
273
|
+
createdAt: xsd:dateTime
|
|
274
|
+
duration: xsd:decimal
|
|
275
|
+
failureReason: xsd:string
|
|
276
|
+
finishedAt: xsd:dateTime
|
|
277
|
+
iid: xsd:string
|
|
278
|
+
logUrl: xsd:string
|
|
279
|
+
pipelineStatus: xsd:string
|
|
280
|
+
ref: xsd:string
|
|
281
|
+
sha: xsd:string
|
|
282
|
+
startedAt: xsd:dateTime
|
|
283
|
+
state: xsd:string
|
|
284
|
+
triggerSource: xsd:string
|
|
285
|
+
webUrl: xsd:string
|
|
286
|
+
PipelineRun:
|
|
287
|
+
ontology_class: null
|
|
288
|
+
fields:
|
|
289
|
+
createdAt: xsd:dateTime
|
|
290
|
+
duration: xsd:decimal
|
|
291
|
+
failureReason: xsd:string
|
|
292
|
+
finishedAt: xsd:dateTime
|
|
293
|
+
iid: xsd:string
|
|
294
|
+
logUrl: xsd:string
|
|
295
|
+
pipelineStatus: xsd:string
|
|
296
|
+
ref: xsd:string
|
|
297
|
+
sha: xsd:string
|
|
298
|
+
startedAt: xsd:dateTime
|
|
299
|
+
state: xsd:string
|
|
300
|
+
triggerSource: xsd:string
|
|
301
|
+
webUrl: xsd:string
|
|
302
|
+
Project:
|
|
303
|
+
ontology_class: SoftwareProject
|
|
304
|
+
fields:
|
|
305
|
+
createdAt: xsd:dateTime
|
|
306
|
+
duration: xsd:decimal
|
|
307
|
+
failureReason: xsd:string
|
|
308
|
+
finishedAt: xsd:dateTime
|
|
309
|
+
iid: xsd:string
|
|
310
|
+
logUrl: xsd:string
|
|
311
|
+
pipelineStatus: xsd:string
|
|
312
|
+
ref: xsd:string
|
|
313
|
+
sha: xsd:string
|
|
314
|
+
startedAt: xsd:dateTime
|
|
315
|
+
state: xsd:string
|
|
316
|
+
triggerSource: xsd:string
|
|
317
|
+
webUrl: xsd:string
|
|
318
|
+
Release:
|
|
319
|
+
ontology_class: null
|
|
320
|
+
fields:
|
|
321
|
+
createdAt: xsd:dateTime
|
|
322
|
+
duration: xsd:decimal
|
|
323
|
+
failureReason: xsd:string
|
|
324
|
+
finishedAt: xsd:dateTime
|
|
325
|
+
iid: xsd:string
|
|
326
|
+
logUrl: xsd:string
|
|
327
|
+
pipelineStatus: xsd:string
|
|
328
|
+
ref: xsd:string
|
|
329
|
+
sha: xsd:string
|
|
330
|
+
startedAt: xsd:dateTime
|
|
331
|
+
state: xsd:string
|
|
332
|
+
triggerSource: xsd:string
|
|
333
|
+
webUrl: xsd:string
|
|
334
|
+
Runner:
|
|
335
|
+
ontology_class: null
|
|
336
|
+
fields:
|
|
337
|
+
createdAt: xsd:dateTime
|
|
338
|
+
duration: xsd:decimal
|
|
339
|
+
failureReason: xsd:string
|
|
340
|
+
finishedAt: xsd:dateTime
|
|
341
|
+
iid: xsd:string
|
|
342
|
+
logUrl: xsd:string
|
|
343
|
+
pipelineStatus: xsd:string
|
|
344
|
+
ref: xsd:string
|
|
345
|
+
sha: xsd:string
|
|
346
|
+
startedAt: xsd:dateTime
|
|
347
|
+
state: xsd:string
|
|
348
|
+
triggerSource: xsd:string
|
|
349
|
+
webUrl: xsd:string
|
|
350
|
+
sync:
|
|
351
|
+
- preset: gitlab-projects
|
|
352
|
+
server: gitlab-mcp
|
|
353
|
+
tool: gitlab_get_projects
|
|
354
|
+
action: null
|
|
355
|
+
records_path: data
|
|
356
|
+
id_field: id
|
|
357
|
+
title_field: path_with_namespace
|
|
358
|
+
text_field: description
|
|
359
|
+
updated_field: last_activity_at
|
|
360
|
+
pagination: page
|
|
361
|
+
doc_type: repository
|
|
362
|
+
tool_schema_sha256: 18b7c440b1165616ba8455535985becf3c14d643a22f40c34d4f321c736c9e06
|
|
363
|
+
raw:
|
|
364
|
+
server: gitlab-mcp
|
|
365
|
+
tool: gitlab_get_projects
|
|
366
|
+
records_path: data
|
|
367
|
+
id_field: id
|
|
368
|
+
title_field: path_with_namespace
|
|
369
|
+
text_field: description
|
|
370
|
+
updated_field: last_activity_at
|
|
371
|
+
pagination: page
|
|
372
|
+
page_kind: number
|
|
373
|
+
page_param: page
|
|
374
|
+
page_size_param: per_page
|
|
375
|
+
doc_type: repository
|
|
376
|
+
params_style: args
|
|
377
|
+
provenance:
|
|
378
|
+
generated_by: scripts/generate_connector_manifests.py
|
|
379
|
+
generated_at: '2026-10-18T00:00:00Z'
|
|
380
|
+
source_artifacts:
|
|
381
|
+
- gitlab_api/connectors/mcp_source_presets.json
|
|
382
|
+
- gitlab_api/connectors/tool_schema_fingerprints.json
|
|
383
|
+
- gitlab_api/ontology/gitlab.ttl
|
|
384
|
+
integrity:
|
|
385
|
+
algorithm: urdna2015-sha256
|
|
386
|
+
hash: 832bf7f47b008ce2c318e1d89f2a2a306992994ec0f44d2ee102919eac7bedb4
|
|
387
|
+
triple_count: 113
|
|
388
|
+
signer: UNSIGNED-PREVIEW
|
|
389
|
+
signature_algorithm: none
|
|
390
|
+
signing_public_key: ''
|
|
391
|
+
signature: null
|
|
392
|
+
source_commit: 868761312fcdc217b3f596fcf4cfcc8a173752e6
|
|
393
|
+
policy:
|
|
394
|
+
pii_fields: {}
|
|
395
|
+
tenant_boundary: null
|
|
396
|
+
rls: []
|
|
397
|
+
review_todos:
|
|
398
|
+
- "policy.rls / policy.tenant_boundary are unset \u2014 no row-level-security predicate has been reviewed\
|
|
399
|
+
\ for this connector."
|
|
400
|
+
- "relation 'authoredBy' has no declared rdfs:domain resolving to a known resource in this connector's\
|
|
401
|
+
\ ontology \u2014 not attached to any resource; verify its true domain manually."
|
|
402
|
+
- "relation 'belongsToProject' has no declared rdfs:domain resolving to a known resource in this connector's\
|
|
403
|
+
\ ontology \u2014 not attached to any resource; verify its true domain manually."
|
|
404
|
+
- "relation 'hasMilestone' has no declared rdfs:domain resolving to a known resource in this connector's\
|
|
405
|
+
\ ontology \u2014 not attached to any resource; verify its true domain manually."
|
|
406
|
+
- "relation 'triggeredPipeline' has no declared rdfs:domain resolving to a known resource in this connector's\
|
|
407
|
+
\ ontology \u2014 not attached to any resource; verify its true domain manually."
|
|
408
|
+
- "schema_mappings.Branch.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
409
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
|
410
|
+
- "schema_mappings.CheckRun.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
411
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
|
412
|
+
- "schema_mappings.Commit.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
413
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
|
414
|
+
- "schema_mappings.Epic.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
415
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
|
416
|
+
- "schema_mappings.GitLabGroup.ontology_class = 'Organization' [DRAFT \u2014 nearest hub-canonical-class-by-name\
|
|
417
|
+
\ heuristic (D16 residue; human sign-off required before use)] \u2014 verify manually before relying\
|
|
418
|
+
\ on this crosswalk for reasoning/joins."
|
|
419
|
+
- "schema_mappings.Issue.ontology_class = 'Incident' [DRAFT \u2014 nearest hub-canonical-class-by-name\
|
|
420
|
+
\ heuristic (D16 residue; human sign-off required before use)] \u2014 verify manually before relying\
|
|
421
|
+
\ on this crosswalk for reasoning/joins."
|
|
422
|
+
- "schema_mappings.Job.ontology_class = 'Action' [DRAFT \u2014 nearest hub-canonical-class-by-name heuristic\
|
|
423
|
+
\ (D16 residue; human sign-off required before use)] \u2014 verify manually before relying on this crosswalk\
|
|
424
|
+
\ for reasoning/joins."
|
|
425
|
+
- "schema_mappings.MergeRequest.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
426
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
|
427
|
+
- "schema_mappings.Milestone.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
428
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
|
429
|
+
- "schema_mappings.Pipeline.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
430
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
|
431
|
+
- "schema_mappings.PipelineRun.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
432
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
|
433
|
+
- "schema_mappings.Project.ontology_class = 'SoftwareProject' [DRAFT \u2014 nearest hub-canonical-class-by-name\
|
|
434
|
+
\ heuristic (D16 residue; human sign-off required before use)] \u2014 verify manually before relying\
|
|
435
|
+
\ on this crosswalk for reasoning/joins."
|
|
436
|
+
- "schema_mappings.Release.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
437
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
|
438
|
+
- "schema_mappings.Runner.ontology_class = None [UNRESOLVED \u2014 no crosswalk found by any heuristic]\
|
|
439
|
+
\ \u2014 verify manually before relying on this crosswalk for reasoning/joins."
|
gitlab_api/connector_prep.py
CHANGED
|
@@ -40,9 +40,14 @@ from pydantic import (
|
|
|
40
40
|
model_validator,
|
|
41
41
|
)
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
# A typing.Literal takes literal values, not variable names: each constant
|
|
44
|
+
# has its own literal type, and the fields annotate with that type.
|
|
45
|
+
PrepContractVersion = Literal["1"]
|
|
46
|
+
ArrowIpc = Literal["arrow_ipc"]
|
|
47
|
+
SourceName = Literal["gitlab-api"]
|
|
48
|
+
PREP_CONTRACT_VERSION: PrepContractVersion = "1"
|
|
49
|
+
ARROW_IPC: ArrowIpc = "arrow_ipc"
|
|
50
|
+
SOURCE: SourceName = "gitlab-api"
|
|
46
51
|
DOMAIN = "gitlab"
|
|
47
52
|
|
|
48
53
|
MAX_PAGE_RECORDS = 100
|
|
@@ -76,7 +81,7 @@ class PrepErrorCode(StrEnum):
|
|
|
76
81
|
FIELD_LIMIT = "field_limit"
|
|
77
82
|
PAGE_LIMIT = "page_too_large"
|
|
78
83
|
RECORD_LIMIT = "record_limit"
|
|
79
|
-
SECRET = "sensitive_payload"
|
|
84
|
+
SECRET = "sensitive_payload" # nosec B105 # sanitizer:ignore - enum value naming a refusal reason, not a credential
|
|
80
85
|
TEXT_LIMIT = "text_limit"
|
|
81
86
|
DUPLICATE_ID = "duplicate_id"
|
|
82
87
|
PATH_ABUSE = "path_or_url_abuse"
|
|
@@ -276,7 +281,7 @@ class NamespacePayload(_PrepModel):
|
|
|
276
281
|
class GitLabProjectPayload(_PrepModel):
|
|
277
282
|
"""Versioned strict projection of the GitLab project response."""
|
|
278
283
|
|
|
279
|
-
schema_version:
|
|
284
|
+
schema_version: PrepContractVersion = PREP_CONTRACT_VERSION
|
|
280
285
|
id: StrictInt = Field(gt=0, le=MAX_IDENTIFIER)
|
|
281
286
|
name: StrictStr
|
|
282
287
|
path_with_namespace: StrictStr
|
|
@@ -332,7 +337,7 @@ class GitLabProjectPayload(_PrepModel):
|
|
|
332
337
|
class GitLabIssuePayload(_PrepModel):
|
|
333
338
|
"""Versioned strict projection of the GitLab issue response."""
|
|
334
339
|
|
|
335
|
-
schema_version:
|
|
340
|
+
schema_version: PrepContractVersion = PREP_CONTRACT_VERSION
|
|
336
341
|
id: StrictInt = Field(gt=0, le=MAX_IDENTIFIER)
|
|
337
342
|
iid: StrictInt = Field(gt=0, le=MAX_IDENTIFIER)
|
|
338
343
|
project_id: StrictInt = Field(gt=0, le=MAX_IDENTIFIER)
|
|
@@ -390,7 +395,7 @@ class GitLabIssuePayload(_PrepModel):
|
|
|
390
395
|
class GitLabMergeRequestPayload(_PrepModel):
|
|
391
396
|
"""Versioned strict projection of the GitLab merge-request response."""
|
|
392
397
|
|
|
393
|
-
schema_version:
|
|
398
|
+
schema_version: PrepContractVersion = PREP_CONTRACT_VERSION
|
|
394
399
|
id: StrictInt = Field(gt=0, le=MAX_IDENTIFIER)
|
|
395
400
|
iid: StrictInt = Field(gt=0, le=MAX_IDENTIFIER)
|
|
396
401
|
project_id: StrictInt = Field(gt=0, le=MAX_IDENTIFIER)
|
|
@@ -515,9 +520,9 @@ class ArrowField(_PrepModel):
|
|
|
515
520
|
class ArrowPrepPlan(_PrepModel):
|
|
516
521
|
"""A versioned declaration of the Arrow boundary, not an Arrow runtime."""
|
|
517
522
|
|
|
518
|
-
schema_version:
|
|
523
|
+
schema_version: PrepContractVersion = PREP_CONTRACT_VERSION
|
|
519
524
|
record_kind: RecordKind
|
|
520
|
-
handoff_format:
|
|
525
|
+
handoff_format: ArrowIpc = ARROW_IPC
|
|
521
526
|
operations: tuple[StrictStr, ...]
|
|
522
527
|
arrow_schema: tuple[ArrowField, ...]
|
|
523
528
|
engine_dependencies: tuple[StrictStr, ...] = ()
|
|
@@ -577,8 +582,8 @@ def arrow_prep_plan(record_kind: RecordKind) -> ArrowPrepPlan:
|
|
|
577
582
|
class PrepEvidence(_PrepModel):
|
|
578
583
|
"""Safe lineage and validation evidence; never stores a source row."""
|
|
579
584
|
|
|
580
|
-
schema_version:
|
|
581
|
-
source:
|
|
585
|
+
schema_version: PrepContractVersion = PREP_CONTRACT_VERSION
|
|
586
|
+
source: SourceName = SOURCE
|
|
582
587
|
record_kind: RecordKind
|
|
583
588
|
record_ref: StrictStr
|
|
584
589
|
disposition: PrepDisposition
|
|
@@ -754,7 +759,7 @@ class CheckpointOutcome(_PrepModel):
|
|
|
754
759
|
class Checkpoint(_PrepModel):
|
|
755
760
|
"""A page digest plus safe terminal outcomes, suitable for CAS storage."""
|
|
756
761
|
|
|
757
|
-
schema_version:
|
|
762
|
+
schema_version: PrepContractVersion = PREP_CONTRACT_VERSION
|
|
758
763
|
stream: StrictStr
|
|
759
764
|
page: StrictInt = Field(ge=1, le=MAX_PAGES)
|
|
760
765
|
cursor_digest: StrictStr | None = None
|
|
@@ -833,7 +838,7 @@ class MemoryCheckpointStore:
|
|
|
833
838
|
class PrepResult(_PrepModel):
|
|
834
839
|
"""Page-level result with bounded evidence and checkpoint state."""
|
|
835
840
|
|
|
836
|
-
schema_version:
|
|
841
|
+
schema_version: PrepContractVersion = PREP_CONTRACT_VERSION
|
|
837
842
|
record_kind: RecordKind
|
|
838
843
|
stream: StrictStr
|
|
839
844
|
page: StrictInt = Field(ge=1, le=MAX_PAGES)
|
|
@@ -1181,7 +1186,9 @@ def _record_identity(model: BaseModel) -> tuple[object, ...]:
|
|
|
1181
1186
|
return ("project", model.id)
|
|
1182
1187
|
if isinstance(model, GitLabIssuePayload):
|
|
1183
1188
|
return ("issue", model.project_id, model.iid)
|
|
1184
|
-
|
|
1189
|
+
if isinstance(model, GitLabMergeRequestPayload):
|
|
1190
|
+
return ("merge_request", model.project_id, model.iid)
|
|
1191
|
+
raise TypeError(f"no record identity for {type(model).__name__}")
|
|
1185
1192
|
|
|
1186
1193
|
|
|
1187
1194
|
class _PageRecordContext(NamedTuple):
|
|
@@ -1450,8 +1457,10 @@ class ConnectorPrep:
|
|
|
1450
1457
|
if outcome is not None:
|
|
1451
1458
|
outcomes.append(outcome)
|
|
1452
1459
|
continue
|
|
1460
|
+
if item is None or identity is None:
|
|
1461
|
+
raise RuntimeError("an accepted record has no item or identity")
|
|
1453
1462
|
seen.setdefault(identity, []).append(ordinal)
|
|
1454
|
-
accepted.append(item)
|
|
1463
|
+
accepted.append(item)
|
|
1455
1464
|
return outcomes, accepted, seen
|
|
1456
1465
|
|
|
1457
1466
|
def _drop_duplicate_records(
|
gitlab_api/gitlab_gql.py
CHANGED
|
@@ -817,7 +817,7 @@ class GraphQL:
|
|
|
817
817
|
author_email: str | None = None,
|
|
818
818
|
author_name: str | None = None,
|
|
819
819
|
_stats: bool | None = False,
|
|
820
|
-
|
|
820
|
+
_force: bool | None = False,
|
|
821
821
|
) -> dict[str, Any]:
|
|
822
822
|
"""
|
|
823
823
|
Create a commit in a project.
|
|
@@ -1073,8 +1073,8 @@ class GraphQL:
|
|
|
1073
1073
|
state: Optional merge request state (e.g., 'opened').
|
|
1074
1074
|
labels: Optional list of labels (not supported in GraphQL).
|
|
1075
1075
|
milestone: Optional milestone title (not supported in GraphQL).
|
|
1076
|
-
author_username: Optional author username
|
|
1077
|
-
reviewer_username: Optional reviewer username
|
|
1076
|
+
author_username: Optional author username filter.
|
|
1077
|
+
reviewer_username: Optional reviewer username filter.
|
|
1078
1078
|
source_branch: Optional source branch (not supported in GraphQL).
|
|
1079
1079
|
target_branch: Optional target branch (not supported in GraphQL).
|
|
1080
1080
|
search: Optional search term (not supported in GraphQL).
|
|
@@ -1086,9 +1086,9 @@ class GraphQL:
|
|
|
1086
1086
|
"""
|
|
1087
1087
|
MergeRequestModel(project_id=project_id, state=state) # type: ignore
|
|
1088
1088
|
query = """
|
|
1089
|
-
query ($fullPath: ID!, $state: MergeRequestState, $first: Int, $after: String) {
|
|
1089
|
+
query ($fullPath: ID!, $state: MergeRequestState, $authorUsername: String, $reviewerUsername: String, $first: Int, $after: String) {
|
|
1090
1090
|
project(fullPath: $fullPath) {
|
|
1091
|
-
mergeRequests(state: $state, first: $first, after: $after) {
|
|
1091
|
+
mergeRequests(state: $state, authorUsername: $authorUsername, reviewerUsername: $reviewerUsername, first: $first, after: $after) {
|
|
1092
1092
|
nodes {
|
|
1093
1093
|
iid
|
|
1094
1094
|
title
|
|
@@ -1106,6 +1106,10 @@ class GraphQL:
|
|
|
1106
1106
|
variables = {"fullPath": str(project_id), "first": first}
|
|
1107
1107
|
if state:
|
|
1108
1108
|
variables["state"] = state
|
|
1109
|
+
if author_username:
|
|
1110
|
+
variables["authorUsername"] = author_username
|
|
1111
|
+
if reviewer_username:
|
|
1112
|
+
variables["reviewerUsername"] = reviewer_username
|
|
1109
1113
|
if after:
|
|
1110
1114
|
variables["after"] = after
|
|
1111
1115
|
return self.execute_gql(query, variables=variables)
|
|
@@ -1347,7 +1351,7 @@ class GraphQL:
|
|
|
1347
1351
|
username: str | None = None,
|
|
1348
1352
|
updated_after: str | None = None,
|
|
1349
1353
|
updated_before: str | None = None,
|
|
1350
|
-
|
|
1354
|
+
_order_by: str | None = None,
|
|
1351
1355
|
sort: str | None = None,
|
|
1352
1356
|
first: int | None = 20,
|
|
1353
1357
|
after: str | None = None,
|
|
@@ -1361,8 +1365,8 @@ class GraphQL:
|
|
|
1361
1365
|
status: Optional pipeline status filter (not supported in GraphQL).
|
|
1362
1366
|
source: Optional source filter (not supported in GraphQL).
|
|
1363
1367
|
username: Optional username filter (not supported in GraphQL).
|
|
1364
|
-
updated_after: Optional updated after
|
|
1365
|
-
updated_before: Optional updated before
|
|
1368
|
+
updated_after: Optional filter: pipelines updated after this time.
|
|
1369
|
+
updated_before: Optional filter: pipelines updated before this time.
|
|
1366
1370
|
order_by: Optional order by field (not supported in GraphQL).
|
|
1367
1371
|
sort: Optional sort order (not supported in GraphQL).
|
|
1368
1372
|
first: Number of pipelines to fetch.
|
|
@@ -1373,9 +1377,9 @@ class GraphQL:
|
|
|
1373
1377
|
"""
|
|
1374
1378
|
PipelineModel(project_id=project_id)
|
|
1375
1379
|
query = """
|
|
1376
|
-
query ($fullPath: ID!, $first: Int, $after: String) {
|
|
1380
|
+
query ($fullPath: ID!, $updatedAfter: Time, $updatedBefore: Time, $first: Int, $after: String) {
|
|
1377
1381
|
project(fullPath: $fullPath) {
|
|
1378
|
-
pipelines(first: $first, after: $after) {
|
|
1382
|
+
pipelines(updatedAfter: $updatedAfter, updatedBefore: $updatedBefore, first: $first, after: $after) {
|
|
1379
1383
|
nodes {
|
|
1380
1384
|
id
|
|
1381
1385
|
status
|
|
@@ -1390,6 +1394,10 @@ class GraphQL:
|
|
|
1390
1394
|
}
|
|
1391
1395
|
"""
|
|
1392
1396
|
variables = {"fullPath": str(project_id), "first": first}
|
|
1397
|
+
if updated_after:
|
|
1398
|
+
variables["updatedAfter"] = updated_after
|
|
1399
|
+
if updated_before:
|
|
1400
|
+
variables["updatedBefore"] = updated_before
|
|
1393
1401
|
if after:
|
|
1394
1402
|
variables["after"] = after
|
|
1395
1403
|
return self.execute_gql(query, variables=variables)
|
|
@@ -2622,9 +2630,9 @@ class GraphQL:
|
|
|
2622
2630
|
"""
|
|
2623
2631
|
IssueModel(project_id=project_id) # type: ignore
|
|
2624
2632
|
query = """
|
|
2625
|
-
query ($fullPath: ID!, $state: IssueState, $labels: [String!], $assigneeUsernames: [String!], $search: String, $first: Int, $after: String) {
|
|
2633
|
+
query ($fullPath: ID!, $state: IssueState, $labels: [String!], $authorUsername: String, $assigneeUsernames: [String!], $search: String, $first: Int, $after: String) {
|
|
2626
2634
|
project(fullPath: $fullPath) {
|
|
2627
|
-
issues(state: $state, labels: $labels, assigneeUsernames: $assigneeUsernames, search: $search, first: $first, after: $after) {
|
|
2635
|
+
issues(state: $state, labels: $labels, authorUsername: $authorUsername, assigneeUsernames: $assigneeUsernames, search: $search, first: $first, after: $after) {
|
|
2628
2636
|
nodes {
|
|
2629
2637
|
iid
|
|
2630
2638
|
title
|
|
@@ -2643,6 +2651,8 @@ class GraphQL:
|
|
|
2643
2651
|
variables["state"] = state
|
|
2644
2652
|
if labels:
|
|
2645
2653
|
variables["labels"] = labels # type: ignore
|
|
2654
|
+
if author_username:
|
|
2655
|
+
variables["authorUsername"] = author_username
|
|
2646
2656
|
if assignee_username:
|
|
2647
2657
|
variables["assigneeUsernames"] = [assignee_username] # type: ignore
|
|
2648
2658
|
if search:
|
gitlab_api/mcp_server.py
CHANGED
|
@@ -35,7 +35,7 @@ from agent_connector_sdk.mcp.tool_surface import register_tool_surface
|
|
|
35
35
|
from gitlab_api.api_client import Api
|
|
36
36
|
from gitlab_api.auth import get_client
|
|
37
37
|
|
|
38
|
-
__version__ = "27.3.
|
|
38
|
+
__version__ = "27.3.2"
|
|
39
39
|
print(f"Gitlab MCP v{__version__}", file=sys.stderr)
|
|
40
40
|
|
|
41
41
|
logger = get_logger(name="mcp_server")
|
|
@@ -354,10 +354,10 @@ _RUNNERS_ACTIONS: dict[str, Any] = {
|
|
|
354
354
|
"register": "register_new_runner",
|
|
355
355
|
"delete": "delete_runner",
|
|
356
356
|
"verify_auth": "verify_runner_authentication",
|
|
357
|
-
"reset_gitlab_token": "reset_gitlab_runner_token",
|
|
358
|
-
"reset_project_token": "reset_project_runner_token",
|
|
359
|
-
"reset_group_token": "reset_group_runner_token",
|
|
360
|
-
"reset_token": "reset_token",
|
|
357
|
+
"reset_gitlab_token": "reset_gitlab_runner_token", # nosec B105 - an action name, not a credential
|
|
358
|
+
"reset_project_token": "reset_project_runner_token", # nosec B105 - an action name, not a credential
|
|
359
|
+
"reset_group_token": "reset_group_runner_token", # nosec B105 - an action name, not a credential
|
|
360
|
+
"reset_token": "reset_token", # nosec B105 - an action name, not a credential
|
|
361
361
|
}
|
|
362
362
|
|
|
363
363
|
#: Action -> Api method for the ``gitlab_tags`` tool.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"connector": "gitlab-api",
|
|
3
|
+
"sources": [
|
|
4
|
+
{
|
|
5
|
+
"source_id": "projects",
|
|
6
|
+
"record_type": "Project",
|
|
7
|
+
"tools": [
|
|
8
|
+
"gitlab_get_projects"
|
|
9
|
+
],
|
|
10
|
+
"records_path": "data",
|
|
11
|
+
"id_field": "id",
|
|
12
|
+
"title_field": "path_with_namespace",
|
|
13
|
+
"text_field": "description",
|
|
14
|
+
"cursor_field": "last_activity_at",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"description": "GitLab projects visible to the configured token."
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlab-api
|
|
3
|
-
Version: 27.3.
|
|
3
|
+
Version: 27.3.2
|
|
4
4
|
Summary: GitLab API + MCP Server
|
|
5
5
|
Author-email: Project Maintainers <maintainers@users.noreply.github.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,11 +12,11 @@ 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[mcp]<1.0.0,>=0.3.
|
|
15
|
+
Requires-Dist: agent-connector-sdk[mcp]<1.0.0,>=0.3.5
|
|
16
16
|
Requires-Dist: python-dotenv>=1.2.4
|
|
17
17
|
Requires-Dist: gql[requests]>=4.4.0
|
|
18
18
|
Provides-Extra: mcp
|
|
19
|
-
Requires-Dist: agent-connector-sdk[mcp]<1.0.0,>=0.3.
|
|
19
|
+
Requires-Dist: agent-connector-sdk[mcp]<1.0.0,>=0.3.5; extra == "mcp"
|
|
20
20
|
Provides-Extra: gql
|
|
21
21
|
Requires-Dist: gql[requests]>=4.4.0; extra == "gql"
|
|
22
22
|
Provides-Extra: all
|
|
@@ -50,7 +50,7 @@ Dynamic: license-file
|
|
|
50
50
|

|
|
51
51
|

|
|
52
52
|
|
|
53
|
-
*Version: 27.3.
|
|
53
|
+
*Version: 27.3.2*
|
|
54
54
|
|
|
55
55
|
> **Documentation** — Installation, deployment, usage across the API, CLI, and MCP
|
|
56
56
|
> backing GitLab instance are maintained in the
|
|
@@ -2,14 +2,15 @@ gitlab_api/__init__.py,sha256=AL1xZwPaU1Gzu53VrWYsDptCebPdfucmt3A6yStoPkc,2811
|
|
|
2
2
|
gitlab_api/__main__.py,sha256=MlKO_lzO6h18UmnI-D20s27-Ej94gQn8Pa0EqOlwFOM,90
|
|
3
3
|
gitlab_api/api_client.py,sha256=Id5iu7ID3fjc-sIAu3QhKyGCUCPy_uXxfZi5VxDkmUc,1087
|
|
4
4
|
gitlab_api/auth.py,sha256=OOY6o1XWDG-ysfgUuOFb_W1bIb9Ub-734ezGVkSirJw,8919
|
|
5
|
-
gitlab_api/
|
|
6
|
-
gitlab_api/
|
|
5
|
+
gitlab_api/connector_manifest.yml,sha256=mUypgulRxSm7w64CBrvpTg9cxm1f-2WclOLZJaFa8TA,13588
|
|
6
|
+
gitlab_api/connector_prep.py,sha256=f_awZY5i9rvTLNde9iqVA1XLs_iqt5UmK4yLXPSO0Cw,60174
|
|
7
|
+
gitlab_api/gitlab_gql.py,sha256=nBEU7K1RzLmZeZ1qD1U0-9NcRBwMu0xt_I0wDfRkGw8,106270
|
|
7
8
|
gitlab_api/gitlab_input_models.py,sha256=35_ySA8E2GEbuGEjBtFeyiWiuqJMMEp3D2vRELiKwwE,115437
|
|
8
9
|
gitlab_api/gitlab_response_models.py,sha256=m0jU8Xtwbq2y0AogCptVCslWlvi6SztLMDQZLSkefs8,150449
|
|
9
10
|
gitlab_api/instances.py,sha256=RW7aKRvHWMQAQ5ZRRcjpKgpPgq_5YoncXt-_rC4ReXk,5423
|
|
10
11
|
gitlab_api/kg_ingest.py,sha256=Verv5y0OtMYDXmzMoUD5EDUC8PZBiVfTddU5nND38b0,10750
|
|
11
12
|
gitlab_api/mcp_config.json,sha256=2OOXrwO1sDLyHQqpZwhvDHizPIe3by6YmK4KFE333gI,23
|
|
12
|
-
gitlab_api/mcp_server.py,sha256=
|
|
13
|
+
gitlab_api/mcp_server.py,sha256=0Cq9QStZ_YDvciiSNArNJPwALsNWUQbdcE6ffSKRvE8,51850
|
|
13
14
|
gitlab_api/api/__init__.py,sha256=MQblcZQoMdWyzpfnlAOEmyVlP7pbNcz_BQgAs5JINOI,27
|
|
14
15
|
gitlab_api/api/api_client_base.py,sha256=0jdaCaV0tCxbNop6mdtq4nXgbrxEXu-ENoOxVmlaORE,8670
|
|
15
16
|
gitlab_api/api/api_client_environments.py,sha256=2OQ88aStucaKO-VNc6NbXJxQdB761h0aJnPcVaHwBxo,26403
|
|
@@ -28,6 +29,7 @@ gitlab_api/connectors/tool_schema_fingerprints.json,sha256=Xnko-DbeAdxlLCbv89u9v
|
|
|
28
29
|
gitlab_api/ontology/__init__.py,sha256=508Z6ZA4AHEfTFVfTLsenl0Lj_3sfI-MZG8icDzlOSo,568
|
|
29
30
|
gitlab_api/ontology/certification.json,sha256=1Gfxi5Nu-EdYhtsHJYaHW2dqblLhclfVCzaWgM5Cs4w,1650
|
|
30
31
|
gitlab_api/ontology/gitlab.ttl,sha256=qJvCWigVMhMF80HpMwegHGC3cG5pAtC8jBESD4BchXU,8028
|
|
32
|
+
gitlab_api/ontology/ingest_sources.json,sha256=ss1dw8bXIQ4NJLlfji1mZhKTK9zsXxUwUR4zCu3BsPk,457
|
|
31
33
|
gitlab_api/ontology/fixtures/records.json,sha256=XwQXggBTlBkz8Vs_RZdQdT9OjjTPHnhT_QMMEUM1a0A,553
|
|
32
34
|
gitlab_api/ontology/mappings/source.yaml,sha256=FnIGtTh_fKl84C4PFRZPj-QfBrQTtZHQAIwp6fCpXn8,545
|
|
33
35
|
gitlab_api/ontology/migrations/manifest.json,sha256=RCw8U98LQshm7vclWxiJuK2m-fnFElytZda1DcY2Kj4,116
|
|
@@ -48,32 +50,34 @@ gitlab_api/skills/gitlab-pipelines/SKILL.md,sha256=OuEk9sRMjJQQzCE2eZtRq9-wgtWRO
|
|
|
48
50
|
gitlab_api/skills/gitlab-pipelines/WORKFLOW.md,sha256=6C61qo-byMs-7bvry3NKCtyzdRWOEjhC-mOG0ZJvsL0,4314
|
|
49
51
|
gitlab_api/skills/gitlab-vulnerabilities/SKILL.md,sha256=acBODboYBGccTtuCAv5fv5cY_gEXedMSmepur3q9f9Q,5212
|
|
50
52
|
gitlab_api/skills/gitlab-vulnerabilities/WORKFLOW.md,sha256=OKnEKILIrhsnnNQTu0MLSQAWR6jL2w6PuX6ztu0Nhdk,5041
|
|
51
|
-
gitlab_api-27.3.
|
|
53
|
+
gitlab_api-27.3.2.dist-info/licenses/LICENSE,sha256=9eAPQLU4cIYQNvAIt6PNoiVQozaVoPRhMV_N7_UHG7A,1068
|
|
52
54
|
scripts/check_import_safety.py,sha256=IjaMe7nWwK261tNPO8vmLdDy-skX1mpMACQKJhvp0ik,13503
|
|
53
55
|
scripts/security_contract.py,sha256=3517zbvFsLEaWhZOiBwjb9lQiNvH8C-8HzlK4i4SBF8,25137
|
|
54
|
-
scripts/security_sanitizer.py,sha256=
|
|
55
|
-
scripts/verify_api_integration.py,sha256=
|
|
56
|
+
scripts/security_sanitizer.py,sha256=W1LGgsq_uksvJ-oqwHIIcE4szNCCDAWA_PheBYSPnXg,9813
|
|
57
|
+
scripts/verify_api_integration.py,sha256=hblaw-oAf8zas-t-OGgm6XOF-wfSHZihFw7uLO3YzfA,10347
|
|
56
58
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
59
|
tests/conftest.py,sha256=srq80Qcj_VFRxMFCYZ6pXtEDfXO1PzVY-HEI4kmm29s,2090
|
|
58
60
|
tests/test_api_wrapper.py,sha256=jUXDVR4lKOZLTvE3vAS8Xjkc4q57smd6MJr7u_eBU9U,5717
|
|
59
|
-
tests/test_auth.py,sha256=
|
|
60
|
-
tests/test_connector_prep.py,sha256=
|
|
61
|
+
tests/test_auth.py,sha256=dpiDD9Z5SfN6re59tb22PqkbsY8A8pWOBX9KuoXoTTc,4404
|
|
62
|
+
tests/test_connector_prep.py,sha256=YgPoTq0nFqsZ4HA1Iq7F_jJlKxscePvgQJREZB3bczk,10770
|
|
61
63
|
tests/test_gitlab_a2a_validation.py,sha256=Wxu7zSIxnmT5bOg-BwFvR6z9TprSzObG5mIEC4NBwwM,5130
|
|
62
64
|
tests/test_gitlab_api_brute_force_coverage.py,sha256=yS3LoPE6JCC2jcI2agYmNI8kpSBK9jAZsmGnrYq-mPU,14294
|
|
63
65
|
tests/test_gitlab_gql.py,sha256=UVMGoYCkaB3EjyGouADp98AYzkyRv7iR-NweHhMrwtQ,11198
|
|
64
66
|
tests/test_gitlab_mcp_validation.py,sha256=Ma2e3ZchIVUHF0OJPPAW1DRYEG6X-I6dUFRpLE31Rwk,2423
|
|
65
67
|
tests/test_gitlab_models.py,sha256=9buJwE0VmioDXRcKlCfcKYgg6PdQnx7HvDRBPnMC3Ak,542220
|
|
66
68
|
tests/test_identity_context_scoping.py,sha256=wY_xb37E7qd91F5NNhf0g2Hv1uRyrM0wnCO_6WEEcW4,2269
|
|
69
|
+
tests/test_ingest_sources.py,sha256=XCPTezZIdauzQdrIR9P5KiXG9ZuB1YfvIXGVuAo6Hks,1347
|
|
67
70
|
tests/test_init_dynamics.py,sha256=fsfRhuT6yCY91JXoHd7gbHfuPjZfLW0A7aJgyXptL6E,1693
|
|
68
71
|
tests/test_instances.py,sha256=C8csQIJPv1Wdbh4Le2JBg1pM7iFo5rYfL-vp_aGwOtA,4167
|
|
69
72
|
tests/test_kg_ingest.py,sha256=sgo6KNYKs7xbvbmKZDOXpHDhb3YkXvxjWsIELpxcAGI,7633
|
|
70
73
|
tests/test_mock_coverage.py,sha256=yO5A8ZP3UxTx1fO-T15p2Dy74EZaXRHgUyBXfhqN96k,11053
|
|
74
|
+
tests/test_security_sanitizer.py,sha256=l7huytJgfHmS8fmTiNPkX85eRrcRrnqu3fOJYKugD4E,1070
|
|
71
75
|
tests/test_startup.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
76
|
tests/test_tool_modes.py,sha256=aiAaQE0yXp9SKGz9BVgik4HIjE5kar8FBD9uVOK5pwA,721
|
|
73
77
|
tests/test_verify_agent.py,sha256=XdW2NtR5643rgjDYLu6RajWu-4wqrZjCUsRehfsKCqQ,6465
|
|
74
78
|
tests/verify_a2a_queries.py,sha256=JMKBexwzo6c59NpVrBfYgW4HnmSG8-qqfdeku5YFtwc,3613
|
|
75
|
-
gitlab_api-27.3.
|
|
76
|
-
gitlab_api-27.3.
|
|
77
|
-
gitlab_api-27.3.
|
|
78
|
-
gitlab_api-27.3.
|
|
79
|
-
gitlab_api-27.3.
|
|
79
|
+
gitlab_api-27.3.2.dist-info/METADATA,sha256=umVBzzDghYU6_Ey2fpEuH4PPnDm1BiMgxKBcNXZ_cUs,40527
|
|
80
|
+
gitlab_api-27.3.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
81
|
+
gitlab_api-27.3.2.dist-info/entry_points.txt,sha256=-FUZqPa8FPHty3mGFhNlFRajrMh6fgSgV8fcxAI1KTA,366
|
|
82
|
+
gitlab_api-27.3.2.dist-info/top_level.txt,sha256=fJ-T_zkuttbq8Kn1dq0mBvyjtKqPdMic01oCdbzxh_k,48
|
|
83
|
+
gitlab_api-27.3.2.dist-info/RECORD,,
|
scripts/security_sanitizer.py
CHANGED
|
@@ -232,7 +232,15 @@ def _should_secret_scan(file_path: Path) -> bool:
|
|
|
232
232
|
return True
|
|
233
233
|
|
|
234
234
|
|
|
235
|
+
#: The fleet's one inline exemption: ``# sanitizer:ignore - <reason>`` on the
|
|
236
|
+
#: exact line of a reviewed synthetic literal. A bare marker without a reason
|
|
237
|
+
#: exempts nothing; there is no file-, path- or value-level exemption.
|
|
238
|
+
SANITIZER_IGNORE_RE = re.compile(r"#\s*sanitizer:ignore\s*[-\u2014:]\s*\S")
|
|
239
|
+
|
|
240
|
+
|
|
235
241
|
def _secret_violations_in_line(line: str, rel_path: Path, idx: int):
|
|
242
|
+
if SANITIZER_IGNORE_RE.search(line):
|
|
243
|
+
return []
|
|
236
244
|
violations = []
|
|
237
245
|
for label, pattern in SECRET_PATTERNS:
|
|
238
246
|
for match in pattern.findall(line):
|
|
@@ -3,6 +3,7 @@ import os
|
|
|
3
3
|
import ast
|
|
4
4
|
import glob
|
|
5
5
|
import sys
|
|
6
|
+
from typing import TypeGuard
|
|
6
7
|
|
|
7
8
|
BASELINES = {
|
|
8
9
|
"adguard-home-agent": 89.2,
|
|
@@ -35,7 +36,9 @@ def _is_api_client_class(node: ast.ClassDef) -> bool:
|
|
|
35
36
|
return "api" in class_name or "client" in class_name or node.name == "Api"
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def _is_public_client_method(
|
|
39
|
+
def _is_public_client_method(
|
|
40
|
+
item: ast.stmt,
|
|
41
|
+
) -> TypeGuard[ast.FunctionDef | ast.AsyncFunctionDef]:
|
|
39
42
|
return (
|
|
40
43
|
isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
41
44
|
and not item.name.startswith("_")
|
tests/test_auth.py
CHANGED
|
@@ -44,7 +44,7 @@ def test_get_client_oidc_delegation(monkeypatch):
|
|
|
44
44
|
monkeypatch.setattr(
|
|
45
45
|
delegation,
|
|
46
46
|
"exchange_token",
|
|
47
|
-
lambda settings, *, subject_token, http_client,
|
|
47
|
+
lambda settings, *, subject_token, http_client, **_unused: AccessToken(
|
|
48
48
|
value="delegated_tok", ttl_seconds=3600, expires_at=0.0
|
|
49
49
|
),
|
|
50
50
|
)
|
|
@@ -63,7 +63,7 @@ def test_get_client_oidc_delegation_failed(monkeypatch):
|
|
|
63
63
|
|
|
64
64
|
monkeypatch.setattr(delegation, "current_user_token", lambda: "caller-token")
|
|
65
65
|
|
|
66
|
-
def _boom(settings, *, subject_token, http_client,
|
|
66
|
+
def _boom(settings, *, subject_token, http_client, **_unused):
|
|
67
67
|
raise ValueError("Exchange failed")
|
|
68
68
|
|
|
69
69
|
monkeypatch.setattr(delegation, "exchange_token", _boom)
|
|
@@ -98,7 +98,7 @@ def test_get_graphql_client_oidc_delegation(monkeypatch):
|
|
|
98
98
|
monkeypatch.setattr(
|
|
99
99
|
delegation,
|
|
100
100
|
"exchange_token",
|
|
101
|
-
lambda settings, *, subject_token, http_client,
|
|
101
|
+
lambda settings, *, subject_token, http_client, **_unused: AccessToken(
|
|
102
102
|
value="delegated_tok", ttl_seconds=3600, expires_at=0.0
|
|
103
103
|
),
|
|
104
104
|
)
|
|
@@ -116,7 +116,7 @@ def test_get_graphql_client_oidc_delegation_failed(monkeypatch):
|
|
|
116
116
|
|
|
117
117
|
monkeypatch.setattr(delegation, "current_user_token", lambda: "caller-token")
|
|
118
118
|
|
|
119
|
-
def _boom(settings, *, subject_token, http_client,
|
|
119
|
+
def _boom(settings, *, subject_token, http_client, **_unused):
|
|
120
120
|
raise ValueError("Exchange failed")
|
|
121
121
|
|
|
122
122
|
monkeypatch.setattr(delegation, "exchange_token", _boom)
|
tests/test_connector_prep.py
CHANGED
|
@@ -217,7 +217,7 @@ def test_malformed_records_are_quarantined_without_commit(
|
|
|
217
217
|
|
|
218
218
|
def test_secret_payload_is_redacted_from_evidence(context: PrepContext) -> None:
|
|
219
219
|
prep, recorder = _prep()
|
|
220
|
-
secret = "glpat-should-never-be-retained"
|
|
220
|
+
secret = "glpat-should-never-be-retained" # sanitizer:ignore - synthetic token proving redaction
|
|
221
221
|
result = prep.process_page(
|
|
222
222
|
"project",
|
|
223
223
|
[{**_project(), "private_token": secret}],
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""The packaged ingestible-source declaration (CONN-GITLAB-API-R001)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
import yaml
|
|
9
|
+
from agent_connector_sdk.ingest.sources import load_ingest_sources
|
|
10
|
+
from agent_connector_sdk.mcp.method_tools import operation_annotations, operation_verb
|
|
11
|
+
from agent_connector_sdk.mcp.tool_surface import operation_prefix
|
|
12
|
+
|
|
13
|
+
import gitlab_api
|
|
14
|
+
from gitlab_api.api_client import Api
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.mark.spec("CONN-GITLAB-API-R001")
|
|
18
|
+
def test_declares_ingestible_sources() -> None:
|
|
19
|
+
declaration = load_ingest_sources(Path(gitlab_api.__file__).parent)
|
|
20
|
+
assert declaration is not None
|
|
21
|
+
assert declaration.connector == "gitlab-api"
|
|
22
|
+
source = declaration.source("projects")
|
|
23
|
+
assert source is not None and source.record_type == "Project"
|
|
24
|
+
prefix = operation_prefix("gitlab-api")
|
|
25
|
+
assert source.tools == (f"{prefix}_get_projects",)
|
|
26
|
+
# The engine admits a record only under the pack mapping of its type.
|
|
27
|
+
manifest = yaml.safe_load(
|
|
28
|
+
(Path(gitlab_api.__file__).parent / "connector_manifest.yml").read_text(
|
|
29
|
+
"utf-8"
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
assert source.record_type in manifest["schema_mappings"]
|
|
33
|
+
method = getattr(Api, "get_projects")
|
|
34
|
+
annotations = operation_annotations("get_projects", operation_verb(method))
|
|
35
|
+
assert annotations.read_only_hint is True
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""The sanitizer's inline exemption marker is narrow (one justified line)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
_SPEC = importlib.util.spec_from_file_location(
|
|
9
|
+
"security_sanitizer", Path(__file__).parents[1] / "scripts" / "security_sanitizer.py"
|
|
10
|
+
)
|
|
11
|
+
assert _SPEC is not None and _SPEC.loader is not None
|
|
12
|
+
sanitizer = importlib.util.module_from_spec(_SPEC)
|
|
13
|
+
_SPEC.loader.exec_module(sanitizer)
|
|
14
|
+
|
|
15
|
+
TOKEN = "glpat-" + "abcdefghijklmnopqrst" # sanitizer:ignore - synthetic test token
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_unmarked_secret_is_reported() -> None:
|
|
19
|
+
line = f'token = "{TOKEN}"'
|
|
20
|
+
assert sanitizer._secret_violations_in_line(line, Path("x.py"), 1)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_justified_marker_exempts_only_its_line() -> None:
|
|
24
|
+
marked = f'token = "{TOKEN}" # sanitizer:ignore - synthetic fixture'
|
|
25
|
+
assert sanitizer._secret_violations_in_line(marked, Path("x.py"), 1) == []
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_bare_marker_exempts_nothing() -> None:
|
|
29
|
+
bare = f'token = "{TOKEN}" # sanitizer:ignore'
|
|
30
|
+
assert sanitizer._secret_violations_in_line(bare, Path("x.py"), 1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|