pack-mcp-gitlab 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pack_mcp_gitlab-0.1.0/.hypothesis/unicode_data/15.0.0/charmap.json.gz +0 -0
- pack_mcp_gitlab-0.1.0/.hypothesis/unicode_data/15.0.0/codec-utf-8.json.gz +0 -0
- pack_mcp_gitlab-0.1.0/.kiro/specs/pack-mcp-gitlab/.config.kiro +1 -0
- pack_mcp_gitlab-0.1.0/.kiro/specs/pack-mcp-gitlab/design.md +462 -0
- pack_mcp_gitlab-0.1.0/.kiro/specs/pack-mcp-gitlab/requirements.md +135 -0
- pack_mcp_gitlab-0.1.0/.kiro/specs/pack-mcp-gitlab/tasks.md +215 -0
- pack_mcp_gitlab-0.1.0/PKG-INFO +11 -0
- pack_mcp_gitlab-0.1.0/README.md +93 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.amazonq/rules/MAINTENANCE.md +310 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.amazonq/rules/memory-bank/guidelines.md +378 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.amazonq/rules/memory-bank/product.md +75 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.amazonq/rules/memory-bank/structure.md +184 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.amazonq/rules/memory-bank/tech.md +190 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.amazonq/rules/review_rules.md +394 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.env +9 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.env.example +8 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.gitignore +41 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.gitlab-ci.yml +21 -0
- pack_mcp_gitlab-0.1.0/kiroreview/.vscode/settings.json +2 -0
- pack_mcp_gitlab-0.1.0/kiroreview/CHANGELOG.md +74 -0
- pack_mcp_gitlab-0.1.0/kiroreview/README.md +398 -0
- pack_mcp_gitlab-0.1.0/kiroreview/VISION.md +234 -0
- pack_mcp_gitlab-0.1.0/kiroreview/arquivos-gerados/code_reviews/review_mr_143.md +298 -0
- pack_mcp_gitlab-0.1.0/kiroreview/arquivos-gerados/code_reviews/review_mr_144.md +246 -0
- pack_mcp_gitlab-0.1.0/kiroreview/arquivos-gerados/code_reviews/review_mr_4638.md +219 -0
- pack_mcp_gitlab-0.1.0/kiroreview/arquivos-gerados/code_reviews/review_mr_481.md +159 -0
- pack_mcp_gitlab-0.1.0/kiroreview/arquivos-gerados/code_reviews/review_mr_5218.md +180 -0
- pack_mcp_gitlab-0.1.0/kiroreview/arquivos-gerados/code_reviews/review_mr_874.md +117 -0
- pack_mcp_gitlab-0.1.0/kiroreview/arquivos-gerados/code_reviews/review_mr_89.md +216 -0
- pack_mcp_gitlab-0.1.0/kiroreview/arquivos-gerados/code_reviews/review_mr_964.md +284 -0
- pack_mcp_gitlab-0.1.0/kiroreview/arquivos-gerados/pareceres_tecnicos/parecer-jira-PKW-57990.html +129 -0
- pack_mcp_gitlab-0.1.0/kiroreview/collect_mr_data.py +183 -0
- pack_mcp_gitlab-0.1.0/kiroreview/config.py +30 -0
- pack_mcp_gitlab-0.1.0/kiroreview/generate_review_md.py +124 -0
- pack_mcp_gitlab-0.1.0/kiroreview/jira_client.py +451 -0
- pack_mcp_gitlab-0.1.0/kiroreview/pipeline_review.py +160 -0
- pack_mcp_gitlab-0.1.0/kiroreview/requirements.txt +3 -0
- pack_mcp_gitlab-0.1.0/kiroreview/run_review_chat.py +72 -0
- pack_mcp_gitlab-0.1.0/kiroreview/templates/jira-parecer-prompt.md +73 -0
- pack_mcp_gitlab-0.1.0/kiroreview/templates/jira-parecer-template.md +158 -0
- pack_mcp_gitlab-0.1.0/pyproject.toml +26 -0
- pack_mcp_gitlab-0.1.0/src/pack_mcp_gitlab/__init__.py +1 -0
- pack_mcp_gitlab-0.1.0/src/pack_mcp_gitlab/client.py +147 -0
- pack_mcp_gitlab-0.1.0/src/pack_mcp_gitlab/errors.py +71 -0
- pack_mcp_gitlab-0.1.0/src/pack_mcp_gitlab/server.py +162 -0
- pack_mcp_gitlab-0.1.0/tests/__init__.py +1 -0
- pack_mcp_gitlab-0.1.0/tests/conftest.py +23 -0
- pack_mcp_gitlab-0.1.0/tests/strategies.py +263 -0
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"specId": "cc8549c0-6591-415d-8157-47290feae813", "workflowType": "requirements-first", "specType": "feature"}
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
# Documento de Design — pack-mcp-gitlab
|
|
2
|
+
|
|
3
|
+
## Visão Geral
|
|
4
|
+
|
|
5
|
+
O `pack-mcp-gitlab` é um servidor MCP (Model Context Protocol) leve e standalone que expõe ferramentas para agentes de IA interagirem com a API do GitLab. O servidor é publicado no PyPI e executado via `uvx`, comunicando-se por protocolo stdio.
|
|
6
|
+
|
|
7
|
+
A arquitetura segue o padrão de servidores MCP em Python: um módulo principal registra tools no framework `mcp`, cada tool delega operações a um cliente GitLab centralizado que encapsula a biblioteca `python-gitlab`. O servidor lê configuração (`GITLAB_URL`, `GITLAB_TOKEN`) de variáveis de ambiente injetadas pelo Config JSON do MCP.
|
|
8
|
+
|
|
9
|
+
### Decisões de Design
|
|
10
|
+
|
|
11
|
+
1. **Módulo único `server.py`**: Como o servidor é simples (8 tools, sem estado complexo), toda a lógica de registro de tools e inicialização fica em um único arquivo. Isso simplifica empacotamento e manutenção.
|
|
12
|
+
2. **Classe `GitLabClient` separada**: Encapsula toda interação com `python-gitlab`, facilitando testes unitários via mock e isolando a camada de transporte.
|
|
13
|
+
3. **Tratamento de erros centralizado**: Um decorator/wrapper converte exceções do `python-gitlab` em respostas MCP padronizadas, evitando repetição de try/except em cada tool.
|
|
14
|
+
4. **Sem estado entre chamadas**: Cada invocação de tool é independente. O `GitLabClient` é instanciado uma vez na inicialização e reutilizado.
|
|
15
|
+
|
|
16
|
+
## Arquitetura
|
|
17
|
+
|
|
18
|
+
```mermaid
|
|
19
|
+
graph TB
|
|
20
|
+
Agent[Agente de IA] -->|stdio / MCP Protocol| Server[MCP Server]
|
|
21
|
+
|
|
22
|
+
subgraph "pack-mcp-gitlab"
|
|
23
|
+
Server -->|registra tools| Tools[Tool Handlers]
|
|
24
|
+
Tools -->|delega| Client[GitLabClient]
|
|
25
|
+
Client -->|python-gitlab| API[GitLab API]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
ENV[Variáveis de Ambiente<br/>GITLAB_URL, GITLAB_TOKEN] -->|leitura na inicialização| Server
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Fluxo de Execução
|
|
32
|
+
|
|
33
|
+
```mermaid
|
|
34
|
+
sequenceDiagram
|
|
35
|
+
participant A as Agente de IA
|
|
36
|
+
participant S as MCP Server (stdio)
|
|
37
|
+
participant T as Tool Handler
|
|
38
|
+
participant C as GitLabClient
|
|
39
|
+
participant G as GitLab API
|
|
40
|
+
|
|
41
|
+
A->>S: Invoca tool (ex: list_merge_requests)
|
|
42
|
+
S->>T: Despacha para handler registrado
|
|
43
|
+
T->>C: Chama método correspondente
|
|
44
|
+
C->>G: Requisição HTTP via python-gitlab
|
|
45
|
+
G-->>C: Resposta JSON
|
|
46
|
+
C-->>T: Dados processados
|
|
47
|
+
T-->>S: Resultado formatado (TextContent)
|
|
48
|
+
S-->>A: Resposta MCP
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Componentes e Interfaces
|
|
52
|
+
|
|
53
|
+
### 1. Módulo `server.py` — Ponto de Entrada e Registro de Tools
|
|
54
|
+
|
|
55
|
+
Responsável por:
|
|
56
|
+
- Ler variáveis de ambiente (`GITLAB_URL`, `GITLAB_TOKEN`)
|
|
57
|
+
- Validar configuração obrigatória na inicialização
|
|
58
|
+
- Instanciar `GitLabClient`
|
|
59
|
+
- Registrar todas as tools no servidor MCP via decorators `@mcp.tool()`
|
|
60
|
+
- Iniciar o servidor com transporte stdio
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# Assinatura simplificada
|
|
64
|
+
import os
|
|
65
|
+
import logging
|
|
66
|
+
from mcp.server.fastmcp import FastMCP
|
|
67
|
+
|
|
68
|
+
mcp = FastMCP("pack-mcp-gitlab")
|
|
69
|
+
|
|
70
|
+
def get_gitlab_client() -> GitLabClient:
|
|
71
|
+
"""Inicializa e retorna o cliente GitLab com validação de config."""
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
# Cada tool é uma função decorada:
|
|
75
|
+
@mcp.tool()
|
|
76
|
+
async def list_merge_requests(project_path: str, state: str = "opened") -> str:
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
@mcp.tool()
|
|
80
|
+
async def get_merge_request(project_path: str, mr_iid: int) -> str:
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
# ... demais tools
|
|
84
|
+
|
|
85
|
+
def main():
|
|
86
|
+
mcp.run(transport="stdio")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 2. Classe `GitLabClient` — Camada de Acesso ao GitLab
|
|
90
|
+
|
|
91
|
+
Encapsula toda interação com a API do GitLab via `python-gitlab`.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
class GitLabClient:
|
|
95
|
+
def __init__(self, gitlab_url: str, gitlab_token: str):
|
|
96
|
+
"""Inicializa conexão com GitLab. Levanta ValueError se params inválidos."""
|
|
97
|
+
self.gl = gitlab.Gitlab(gitlab_url, private_token=gitlab_token)
|
|
98
|
+
|
|
99
|
+
def list_merge_requests(self, project_path: str, state: str = "opened") -> list[dict]:
|
|
100
|
+
"""Retorna lista de MRs do projeto filtrados por estado."""
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
def get_merge_request(self, project_path: str, mr_iid: int) -> dict:
|
|
104
|
+
"""Retorna detalhes completos de um MR."""
|
|
105
|
+
...
|
|
106
|
+
|
|
107
|
+
def get_merge_request_changes(self, project_path: str, mr_iid: int) -> list[dict]:
|
|
108
|
+
"""Retorna diffs/alterações de um MR."""
|
|
109
|
+
...
|
|
110
|
+
|
|
111
|
+
def get_file_content(self, project_path: str, file_path: str, ref: str = "HEAD") -> str:
|
|
112
|
+
"""Retorna conteúdo de arquivo decodificado em UTF-8."""
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
def create_merge_request_note(self, project_path: str, mr_iid: int, body: str) -> dict:
|
|
116
|
+
"""Cria comentário no MR. Retorna id e confirmação."""
|
|
117
|
+
...
|
|
118
|
+
|
|
119
|
+
def list_merge_request_notes(self, project_path: str, mr_iid: int) -> list[dict]:
|
|
120
|
+
"""Retorna lista de comentários do MR."""
|
|
121
|
+
...
|
|
122
|
+
|
|
123
|
+
def get_project(self, project_path: str) -> dict:
|
|
124
|
+
"""Retorna informações do projeto."""
|
|
125
|
+
...
|
|
126
|
+
|
|
127
|
+
def search_projects(self, search: str) -> list[dict]:
|
|
128
|
+
"""Busca projetos por nome."""
|
|
129
|
+
...
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 3. Módulo `errors.py` — Tratamento de Erros Centralizado
|
|
133
|
+
|
|
134
|
+
Converte exceções do `python-gitlab` em mensagens de erro padronizadas para o MCP.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from gitlab.exceptions import GitlabAuthenticationError, GitlabGetError, GitlabHttpError
|
|
138
|
+
|
|
139
|
+
def handle_gitlab_error(func):
|
|
140
|
+
"""Decorator que converte exceções GitLab em respostas de erro MCP."""
|
|
141
|
+
@functools.wraps(func)
|
|
142
|
+
def wrapper(*args, **kwargs):
|
|
143
|
+
try:
|
|
144
|
+
return func(*args, **kwargs)
|
|
145
|
+
except GitlabAuthenticationError:
|
|
146
|
+
raise McpError("Falha de autenticação: token inválido ou expirado")
|
|
147
|
+
except GitlabGetError as e:
|
|
148
|
+
if e.response_code == 404:
|
|
149
|
+
raise McpError("Recurso não encontrado")
|
|
150
|
+
raise McpError(f"Erro GitLab: {e.error_message}")
|
|
151
|
+
except GitlabHttpError as e:
|
|
152
|
+
if e.response_code == 403:
|
|
153
|
+
raise McpError("Permissão negada: token sem acesso à operação")
|
|
154
|
+
raise McpError(f"Erro HTTP GitLab: {e.error_message}")
|
|
155
|
+
except Exception as e:
|
|
156
|
+
raise McpError(f"Erro inesperado: {type(e).__name__}: {str(e)}")
|
|
157
|
+
return wrapper
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Modelos de Dados
|
|
161
|
+
|
|
162
|
+
### Estruturas de Retorno das Tools
|
|
163
|
+
|
|
164
|
+
Cada tool retorna dados serializados como JSON string via `TextContent` do MCP. Abaixo os schemas de cada retorno:
|
|
165
|
+
|
|
166
|
+
#### MergeRequestSummary (list_merge_requests)
|
|
167
|
+
```python
|
|
168
|
+
{
|
|
169
|
+
"iid": int,
|
|
170
|
+
"title": str,
|
|
171
|
+
"author": str, # username do autor
|
|
172
|
+
"source_branch": str,
|
|
173
|
+
"target_branch": str,
|
|
174
|
+
"state": str, # "opened" | "closed" | "merged"
|
|
175
|
+
"web_url": str,
|
|
176
|
+
"created_at": str, # ISO 8601
|
|
177
|
+
"updated_at": str # ISO 8601
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### MergeRequestDetail (get_merge_request)
|
|
182
|
+
```python
|
|
183
|
+
{
|
|
184
|
+
"iid": int,
|
|
185
|
+
"title": str,
|
|
186
|
+
"description": str | None,
|
|
187
|
+
"state": str,
|
|
188
|
+
"author": {
|
|
189
|
+
"name": str,
|
|
190
|
+
"username": str
|
|
191
|
+
},
|
|
192
|
+
"source_branch": str,
|
|
193
|
+
"target_branch": str,
|
|
194
|
+
"web_url": str,
|
|
195
|
+
"created_at": str,
|
|
196
|
+
"updated_at": str
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### MergeRequestChange (get_merge_request_changes)
|
|
201
|
+
```python
|
|
202
|
+
[
|
|
203
|
+
{
|
|
204
|
+
"new_path": str,
|
|
205
|
+
"old_path": str,
|
|
206
|
+
"new_file": bool,
|
|
207
|
+
"renamed_file": bool,
|
|
208
|
+
"deleted_file": bool,
|
|
209
|
+
"diff": str
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
#### FileContent (get_file_content)
|
|
215
|
+
```python
|
|
216
|
+
{
|
|
217
|
+
"file_path": str,
|
|
218
|
+
"ref": str,
|
|
219
|
+
"content": str # conteúdo UTF-8
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### NoteCreated (create_merge_request_note)
|
|
224
|
+
```python
|
|
225
|
+
{
|
|
226
|
+
"id": int,
|
|
227
|
+
"success": True
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### Note (list_merge_request_notes)
|
|
232
|
+
```python
|
|
233
|
+
{
|
|
234
|
+
"id": int,
|
|
235
|
+
"body": str,
|
|
236
|
+
"author": {
|
|
237
|
+
"name": str,
|
|
238
|
+
"username": str
|
|
239
|
+
},
|
|
240
|
+
"created_at": str,
|
|
241
|
+
"system": bool
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### ProjectInfo (get_project)
|
|
246
|
+
```python
|
|
247
|
+
{
|
|
248
|
+
"id": int,
|
|
249
|
+
"name": str,
|
|
250
|
+
"path_with_namespace": str,
|
|
251
|
+
"description": str | None,
|
|
252
|
+
"default_branch": str,
|
|
253
|
+
"web_url": str,
|
|
254
|
+
"visibility": str
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
#### ProjectSearchResult (search_projects)
|
|
259
|
+
```python
|
|
260
|
+
{
|
|
261
|
+
"id": int,
|
|
262
|
+
"name": str,
|
|
263
|
+
"path_with_namespace": str,
|
|
264
|
+
"description": str | None,
|
|
265
|
+
"web_url": str
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Estrutura do Pacote
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
pack-mcp-gitlab/
|
|
273
|
+
├── pyproject.toml
|
|
274
|
+
├── README.md
|
|
275
|
+
├── LICENSE
|
|
276
|
+
└── src/
|
|
277
|
+
└── pack_mcp_gitlab/
|
|
278
|
+
├── __init__.py
|
|
279
|
+
├── server.py # Ponto de entrada, registro de tools
|
|
280
|
+
├── client.py # GitLabClient
|
|
281
|
+
└── errors.py # Tratamento de erros centralizado
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### pyproject.toml (Estrutura)
|
|
285
|
+
|
|
286
|
+
```toml
|
|
287
|
+
[project]
|
|
288
|
+
name = "pack-mcp-gitlab"
|
|
289
|
+
version = "0.1.0"
|
|
290
|
+
description = "MCP server for GitLab API integration"
|
|
291
|
+
requires-python = ">=3.10"
|
|
292
|
+
dependencies = [
|
|
293
|
+
"python-gitlab>=4.0.0",
|
|
294
|
+
"mcp>=1.0.0",
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
[project.scripts]
|
|
298
|
+
pack-mcp-gitlab = "pack_mcp_gitlab.server:main"
|
|
299
|
+
|
|
300
|
+
[build-system]
|
|
301
|
+
requires = ["hatchling"]
|
|
302
|
+
build-backend = "hatchling.build"
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
## Propriedades de Corretude
|
|
307
|
+
|
|
308
|
+
*Uma propriedade é uma característica ou comportamento que deve ser verdadeiro em todas as execuções válidas de um sistema — essencialmente, uma declaração formal sobre o que o sistema deve fazer. Propriedades servem como ponte entre especificações legíveis por humanos e garantias de corretude verificáveis por máquina.*
|
|
309
|
+
|
|
310
|
+
### Propriedade 1: Validação de parâmetros obrigatórios na inicialização
|
|
311
|
+
|
|
312
|
+
*Para qualquer* string vazia ou composta apenas de espaços em branco fornecida como `GITLAB_URL` ou `GITLAB_TOKEN`, a inicialização do servidor deve levantar um erro descritivo indicando qual parâmetro está ausente.
|
|
313
|
+
|
|
314
|
+
**Valida: Requisitos 1.1, 1.3, 1.4**
|
|
315
|
+
|
|
316
|
+
### Propriedade 2: Formatação de MR preserva todos os campos obrigatórios
|
|
317
|
+
|
|
318
|
+
*Para qualquer* dado de Merge Request válido retornado pela API do GitLab (contendo iid, title, author, branches, state, urls, timestamps), a saída formatada da tool deve conter todos os campos especificados no modelo de dados correspondente.
|
|
319
|
+
|
|
320
|
+
**Valida: Requisitos 2.2, 3.2**
|
|
321
|
+
|
|
322
|
+
### Propriedade 3: Formatação de changes preserva todos os campos obrigatórios
|
|
323
|
+
|
|
324
|
+
*Para qualquer* lista de alterações de arquivo válida retornada pela API do GitLab, a saída formatada deve conter para cada arquivo: `new_path`, `old_path`, `new_file`, `renamed_file`, `deleted_file` e `diff`.
|
|
325
|
+
|
|
326
|
+
**Valida: Requisitos 4.2**
|
|
327
|
+
|
|
328
|
+
### Propriedade 4: Formatação de notes preserva todos os campos obrigatórios
|
|
329
|
+
|
|
330
|
+
*Para qualquer* lista de comentários válida retornada pela API do GitLab, a saída formatada deve conter para cada note: `id`, `body`, `author` (name e username), `created_at` e `system`.
|
|
331
|
+
|
|
332
|
+
**Valida: Requisitos 7.2**
|
|
333
|
+
|
|
334
|
+
### Propriedade 5: Formatação de projeto preserva todos os campos obrigatórios
|
|
335
|
+
|
|
336
|
+
*Para qualquer* dado de projeto válido retornado pela API do GitLab, a saída formatada deve conter: `id`, `name`, `path_with_namespace`, `description`, `default_branch`, `web_url` e `visibility` (para get_project) ou `id`, `name`, `path_with_namespace`, `description` e `web_url` (para search_projects).
|
|
337
|
+
|
|
338
|
+
**Valida: Requisitos 8.2, 9.2**
|
|
339
|
+
|
|
340
|
+
### Propriedade 6: Tratamento de erros por tipo de exceção
|
|
341
|
+
|
|
342
|
+
*Para qualquer* exceção do `python-gitlab`, o error handler deve produzir uma mensagem de erro descritiva apropriada ao tipo: erros 404 devem mencionar "não encontrado", erros 403 devem mencionar "permissão", erros de autenticação devem mencionar "autenticação", e erros de rede devem incluir o tipo e mensagem original da exceção.
|
|
343
|
+
|
|
344
|
+
**Valida: Requisitos 2.4, 3.3, 4.3, 5.4, 6.4, 7.3, 8.3, 11.1, 11.2, 11.3**
|
|
345
|
+
|
|
346
|
+
### Propriedade 7: Parâmetros opcionais são repassados corretamente à API
|
|
347
|
+
|
|
348
|
+
*Para qualquer* valor válido do parâmetro `state` (opened, closed, merged, all) em `list_merge_requests`, e *para qualquer* valor de `ref` em `get_file_content`, o cliente deve repassar esses parâmetros à chamada da API do GitLab.
|
|
349
|
+
|
|
350
|
+
**Valida: Requisitos 2.3, 5.3**
|
|
351
|
+
|
|
352
|
+
### Propriedade 8: Validação de body vazio em create_merge_request_note
|
|
353
|
+
|
|
354
|
+
*Para qualquer* string vazia ou composta apenas de espaços em branco fornecida como `body`, a tool `create_merge_request_note` deve rejeitar a chamada com erro descritivo, sem realizar chamada à API.
|
|
355
|
+
|
|
356
|
+
**Valida: Requisitos 6.5**
|
|
357
|
+
|
|
358
|
+
### Propriedade 9: Logs de erro não expõem dados sensíveis
|
|
359
|
+
|
|
360
|
+
*Para qualquer* token de acesso configurado e *para qualquer* erro que gere log, a mensagem de log registrada não deve conter o valor do token.
|
|
361
|
+
|
|
362
|
+
**Valida: Requisitos 11.4**
|
|
363
|
+
|
|
364
|
+
### Propriedade 10: Resposta de criação de nota contém id e confirmação
|
|
365
|
+
|
|
366
|
+
*Para qualquer* nota criada com sucesso pela API do GitLab (retornando um id numérico), a saída formatada da tool deve conter o `id` da nota e o campo `success` como `true`.
|
|
367
|
+
|
|
368
|
+
**Valida: Requisitos 6.3**
|
|
369
|
+
|
|
370
|
+
## Tratamento de Erros
|
|
371
|
+
|
|
372
|
+
### Estratégia
|
|
373
|
+
|
|
374
|
+
O tratamento de erros é centralizado no módulo `errors.py` via decorator `@handle_gitlab_error` aplicado a todos os métodos do `GitLabClient`. Isso garante consistência e evita duplicação.
|
|
375
|
+
|
|
376
|
+
### Mapeamento de Exceções
|
|
377
|
+
|
|
378
|
+
| Exceção python-gitlab | HTTP Code | Mensagem MCP |
|
|
379
|
+
|---|---|---|
|
|
380
|
+
| `GitlabAuthenticationError` | 401 | "Falha de autenticação: token inválido ou expirado" |
|
|
381
|
+
| `GitlabGetError` (404) | 404 | "Recurso não encontrado: {contexto}" |
|
|
382
|
+
| `GitlabHttpError` (403) | 403 | "Permissão negada: token sem acesso para esta operação" |
|
|
383
|
+
| `requests.ConnectionError` | — | "Erro de conexão: {mensagem original}" |
|
|
384
|
+
| `requests.Timeout` | — | "Timeout na conexão com GitLab: {mensagem}" |
|
|
385
|
+
| Outras exceções | — | "Erro inesperado: {tipo}: {mensagem}" |
|
|
386
|
+
|
|
387
|
+
### Validações na Inicialização
|
|
388
|
+
|
|
389
|
+
- `GITLAB_URL` ausente/vazio → `ValueError("GITLAB_URL é obrigatório. Configure a variável de ambiente.")`
|
|
390
|
+
- `GITLAB_TOKEN` ausente/vazio → `ValueError("GITLAB_TOKEN é obrigatório. Configure a variável de ambiente.")`
|
|
391
|
+
|
|
392
|
+
### Validações em Tools
|
|
393
|
+
|
|
394
|
+
- `body` vazio em `create_merge_request_note` → erro descritivo sem chamar API
|
|
395
|
+
- `state` inválido em `list_merge_requests` → repassado ao GitLab que retorna erro (sem validação local extra)
|
|
396
|
+
|
|
397
|
+
### Logging
|
|
398
|
+
|
|
399
|
+
- Logs de erro com `logging.error()` incluindo tipo de exceção e mensagem
|
|
400
|
+
- Token NUNCA aparece em logs — usar mascaramento ou omissão
|
|
401
|
+
- Nível de log configurável via variável de ambiente `LOG_LEVEL` (padrão: `WARNING`)
|
|
402
|
+
|
|
403
|
+
## Estratégia de Testes
|
|
404
|
+
|
|
405
|
+
### Abordagem Dual
|
|
406
|
+
|
|
407
|
+
O projeto utiliza duas abordagens complementares de teste:
|
|
408
|
+
|
|
409
|
+
1. **Testes unitários** (pytest): Verificam exemplos específicos, edge cases e condições de erro
|
|
410
|
+
2. **Testes de propriedade** (Hypothesis): Verificam propriedades universais com inputs gerados aleatoriamente
|
|
411
|
+
|
|
412
|
+
### Biblioteca de Property-Based Testing
|
|
413
|
+
|
|
414
|
+
- **Hypothesis** (Python) — biblioteca padrão para PBT em Python
|
|
415
|
+
- Configuração: mínimo de 100 iterações por teste de propriedade
|
|
416
|
+
- Cada teste de propriedade deve referenciar a propriedade do design via tag no formato:
|
|
417
|
+
`Feature: pack-mcp-gitlab, Property {N}: {título}`
|
|
418
|
+
|
|
419
|
+
### Testes Unitários
|
|
420
|
+
|
|
421
|
+
Focam em:
|
|
422
|
+
- Verificação de registro correto das 8 tools (nomes, parâmetros)
|
|
423
|
+
- Exemplos específicos de formatação de dados
|
|
424
|
+
- Edge cases: lista vazia de MRs, projeto sem descrição, MR sem description
|
|
425
|
+
- Integração do error handler com exceções reais do python-gitlab (via mock)
|
|
426
|
+
- Verificação do pyproject.toml (entry point, dependências)
|
|
427
|
+
- Transporte stdio configurado corretamente
|
|
428
|
+
|
|
429
|
+
### Testes de Propriedade
|
|
430
|
+
|
|
431
|
+
Cada propriedade do design é implementada como um único teste Hypothesis:
|
|
432
|
+
|
|
433
|
+
| Propriedade | Estratégia de Geração |
|
|
434
|
+
|---|---|
|
|
435
|
+
| P1: Validação de config | Gerar strings vazias/whitespace para URL e token |
|
|
436
|
+
| P2: Formatação de MR | Gerar dicts com campos de MR aleatórios |
|
|
437
|
+
| P3: Formatação de changes | Gerar listas de dicts com campos de change aleatórios |
|
|
438
|
+
| P4: Formatação de notes | Gerar listas de dicts com campos de note aleatórios |
|
|
439
|
+
| P5: Formatação de projeto | Gerar dicts com campos de projeto aleatórios |
|
|
440
|
+
| P6: Error handler | Gerar exceções de diferentes tipos com mensagens aleatórias |
|
|
441
|
+
| P7: Parâmetros opcionais | Gerar valores de state e ref aleatórios válidos |
|
|
442
|
+
| P8: Body vazio | Gerar strings whitespace-only |
|
|
443
|
+
| P9: Logs sem token | Gerar tokens aleatórios e provocar erros |
|
|
444
|
+
| P10: Resposta de nota | Gerar IDs numéricos aleatórios |
|
|
445
|
+
|
|
446
|
+
### Mocking
|
|
447
|
+
|
|
448
|
+
- Todas as chamadas à API do GitLab são mockadas nos testes unitários e de propriedade
|
|
449
|
+
- O `python-gitlab` é mockado no nível do `gitlab.Gitlab` para isolar o `GitLabClient`
|
|
450
|
+
- Nenhum teste faz chamada real à API
|
|
451
|
+
|
|
452
|
+
### Estrutura de Testes
|
|
453
|
+
|
|
454
|
+
```
|
|
455
|
+
tests/
|
|
456
|
+
├── conftest.py # Fixtures compartilhadas (mock GitLab client)
|
|
457
|
+
├── test_server.py # Testes unitários do registro de tools
|
|
458
|
+
├── test_client.py # Testes unitários do GitLabClient
|
|
459
|
+
├── test_errors.py # Testes unitários do error handler
|
|
460
|
+
├── test_properties.py # Testes de propriedade (Hypothesis)
|
|
461
|
+
└── strategies.py # Estratégias Hypothesis para geração de dados
|
|
462
|
+
```
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Documento de Requisitos — pack-mcp-gitlab
|
|
2
|
+
|
|
3
|
+
## Introdução
|
|
4
|
+
|
|
5
|
+
O `pack-mcp-gitlab` é um servidor MCP (Model Context Protocol) standalone, publicado no PyPI, que expõe ferramentas para agentes de IA interagirem com a API do GitLab. O servidor recebe URL e token do GitLab como parâmetros de configuração via JSON do MCP e disponibiliza tools para consultar Merge Requests, obter diffs, ler conteúdo de arquivos, postar comentários e listar projetos. Este servidor substitui a camada de interação com o GitLab que hoje existe no projeto KiroReview, isolando-a como pacote reutilizável.
|
|
6
|
+
|
|
7
|
+
## Glossário
|
|
8
|
+
|
|
9
|
+
- **MCP_Server**: O servidor MCP `pack-mcp-gitlab` que recebe requisições de agentes de IA e interage com a API do GitLab
|
|
10
|
+
- **GitLab_Client**: Componente interno do MCP_Server responsável por autenticar e executar chamadas à API do GitLab usando a biblioteca `python-gitlab`
|
|
11
|
+
- **MR**: Merge Request no GitLab — solicitação de mesclagem de código entre branches
|
|
12
|
+
- **Tool**: Uma ferramenta exposta pelo MCP_Server que agentes de IA podem invocar via protocolo MCP
|
|
13
|
+
- **Config_JSON**: Arquivo de configuração JSON do MCP que contém URL do GitLab, token de acesso e demais parâmetros
|
|
14
|
+
- **Diff**: Representação textual das diferenças entre versões de um arquivo em um MR
|
|
15
|
+
- **Note**: Comentário postado em um MR no GitLab
|
|
16
|
+
- **Project**: Repositório/projeto no GitLab identificado por path (ex: `grupo/subgrupo/projeto`)
|
|
17
|
+
|
|
18
|
+
## Requisitos
|
|
19
|
+
|
|
20
|
+
### Requisito 1: Configuração e Autenticação
|
|
21
|
+
|
|
22
|
+
**User Story:** Como agente de IA, eu quero configurar o MCP_Server com URL e token do GitLab via Config_JSON, para que o servidor autentique nas chamadas à API do GitLab.
|
|
23
|
+
|
|
24
|
+
#### Critérios de Aceitação
|
|
25
|
+
|
|
26
|
+
1. WHEN o MCP_Server é iniciado, THE MCP_Server SHALL ler os parâmetros `GITLAB_URL` e `GITLAB_TOKEN` das variáveis de ambiente fornecidas no Config_JSON
|
|
27
|
+
2. WHEN o MCP_Server é iniciado com `GITLAB_URL` e `GITLAB_TOKEN` válidos, THE GitLab_Client SHALL autenticar com sucesso na API do GitLab
|
|
28
|
+
3. IF o parâmetro `GITLAB_TOKEN` estiver ausente ou vazio, THEN THE MCP_Server SHALL retornar um erro descritivo informando que o token é obrigatório
|
|
29
|
+
4. IF o parâmetro `GITLAB_URL` estiver ausente ou vazio, THEN THE MCP_Server SHALL retornar um erro descritivo informando que a URL é obrigatória
|
|
30
|
+
5. IF o token fornecido for inválido ou expirado, THEN THE GitLab_Client SHALL retornar um erro descritivo informando falha de autenticação
|
|
31
|
+
|
|
32
|
+
### Requisito 2: Listar Merge Requests
|
|
33
|
+
|
|
34
|
+
**User Story:** Como agente de IA, eu quero listar Merge Requests de um projeto no GitLab, para que eu possa identificar MRs relevantes para análise.
|
|
35
|
+
|
|
36
|
+
#### Critérios de Aceitação
|
|
37
|
+
|
|
38
|
+
1. THE MCP_Server SHALL expor uma Tool chamada `list_merge_requests` que aceita os parâmetros `project_path` (obrigatório) e `state` (opcional, padrão: `opened`)
|
|
39
|
+
2. WHEN a Tool `list_merge_requests` é invocada com um `project_path` válido, THE GitLab_Client SHALL retornar uma lista de MRs contendo para cada MR: `iid`, `title`, `author`, `source_branch`, `target_branch`, `state`, `web_url`, `created_at` e `updated_at`
|
|
40
|
+
3. WHERE o parâmetro `state` é fornecido, THE GitLab_Client SHALL filtrar os MRs pelo estado informado (valores aceitos: `opened`, `closed`, `merged`, `all`)
|
|
41
|
+
4. IF o `project_path` não corresponder a um projeto existente, THEN THE MCP_Server SHALL retornar um erro descritivo informando que o projeto não foi encontrado
|
|
42
|
+
|
|
43
|
+
### Requisito 3: Obter Detalhes de um Merge Request
|
|
44
|
+
|
|
45
|
+
**User Story:** Como agente de IA, eu quero obter os detalhes completos de um MR específico, para que eu possa analisar o contexto da mudança.
|
|
46
|
+
|
|
47
|
+
#### Critérios de Aceitação
|
|
48
|
+
|
|
49
|
+
1. THE MCP_Server SHALL expor uma Tool chamada `get_merge_request` que aceita os parâmetros `project_path` (obrigatório) e `mr_iid` (obrigatório)
|
|
50
|
+
2. WHEN a Tool `get_merge_request` é invocada com parâmetros válidos, THE GitLab_Client SHALL retornar os dados do MR contendo: `iid`, `title`, `description`, `state`, `author` (nome e username), `source_branch`, `target_branch`, `web_url`, `created_at` e `updated_at`
|
|
51
|
+
3. IF o `mr_iid` não corresponder a um MR existente no projeto, THEN THE MCP_Server SHALL retornar um erro descritivo informando que o MR não foi encontrado
|
|
52
|
+
|
|
53
|
+
### Requisito 4: Obter Alterações (Diffs) de um Merge Request
|
|
54
|
+
|
|
55
|
+
**User Story:** Como agente de IA, eu quero obter os diffs de um MR, para que eu possa realizar code review analisando as mudanças linha a linha.
|
|
56
|
+
|
|
57
|
+
#### Critérios de Aceitação
|
|
58
|
+
|
|
59
|
+
1. THE MCP_Server SHALL expor uma Tool chamada `get_merge_request_changes` que aceita os parâmetros `project_path` (obrigatório) e `mr_iid` (obrigatório)
|
|
60
|
+
2. WHEN a Tool `get_merge_request_changes` é invocada com parâmetros válidos, THE GitLab_Client SHALL retornar a lista de arquivos alterados contendo para cada arquivo: `new_path`, `old_path`, `new_file` (booleano), `renamed_file` (booleano), `deleted_file` (booleano) e `diff` (texto do diff)
|
|
61
|
+
3. IF o MR não existir no projeto informado, THEN THE MCP_Server SHALL retornar um erro descritivo informando que o MR não foi encontrado
|
|
62
|
+
|
|
63
|
+
### Requisito 5: Ler Conteúdo de Arquivo do Repositório
|
|
64
|
+
|
|
65
|
+
**User Story:** Como agente de IA, eu quero ler o conteúdo completo de um arquivo no repositório, para que eu possa analisar o contexto além do diff durante um code review.
|
|
66
|
+
|
|
67
|
+
#### Critérios de Aceitação
|
|
68
|
+
|
|
69
|
+
1. THE MCP_Server SHALL expor uma Tool chamada `get_file_content` que aceita os parâmetros `project_path` (obrigatório), `file_path` (obrigatório) e `ref` (opcional, padrão: `HEAD`)
|
|
70
|
+
2. WHEN a Tool `get_file_content` é invocada com parâmetros válidos, THE GitLab_Client SHALL retornar o conteúdo do arquivo decodificado em UTF-8
|
|
71
|
+
3. WHERE o parâmetro `ref` é fornecido, THE GitLab_Client SHALL buscar o conteúdo do arquivo na branch ou commit especificado
|
|
72
|
+
4. IF o arquivo não existir no caminho ou ref informados, THEN THE MCP_Server SHALL retornar um erro descritivo informando que o arquivo não foi encontrado
|
|
73
|
+
|
|
74
|
+
### Requisito 6: Postar Comentário em um Merge Request
|
|
75
|
+
|
|
76
|
+
**User Story:** Como agente de IA, eu quero postar comentários em um MR, para que eu possa registrar resultados de code review diretamente no GitLab.
|
|
77
|
+
|
|
78
|
+
#### Critérios de Aceitação
|
|
79
|
+
|
|
80
|
+
1. THE MCP_Server SHALL expor uma Tool chamada `create_merge_request_note` que aceita os parâmetros `project_path` (obrigatório), `mr_iid` (obrigatório) e `body` (obrigatório)
|
|
81
|
+
2. WHEN a Tool `create_merge_request_note` é invocada com parâmetros válidos, THE GitLab_Client SHALL criar um comentário (Note) no MR com o conteúdo fornecido em `body`
|
|
82
|
+
3. WHEN o comentário é criado com sucesso, THE MCP_Server SHALL retornar o `id` do comentário criado e a confirmação de sucesso
|
|
83
|
+
4. IF o MR não existir no projeto informado, THEN THE MCP_Server SHALL retornar um erro descritivo informando que o MR não foi encontrado
|
|
84
|
+
5. IF o `body` estiver vazio, THEN THE MCP_Server SHALL retornar um erro descritivo informando que o conteúdo do comentário é obrigatório
|
|
85
|
+
|
|
86
|
+
### Requisito 7: Listar Comentários de um Merge Request
|
|
87
|
+
|
|
88
|
+
**User Story:** Como agente de IA, eu quero listar os comentários existentes em um MR, para que eu possa entender o histórico de discussões e evitar duplicar feedback.
|
|
89
|
+
|
|
90
|
+
#### Critérios de Aceitação
|
|
91
|
+
|
|
92
|
+
1. THE MCP_Server SHALL expor uma Tool chamada `list_merge_request_notes` que aceita os parâmetros `project_path` (obrigatório) e `mr_iid` (obrigatório)
|
|
93
|
+
2. WHEN a Tool `list_merge_request_notes` é invocada com parâmetros válidos, THE GitLab_Client SHALL retornar a lista de comentários do MR contendo para cada Note: `id`, `body`, `author` (nome e username), `created_at` e `system` (booleano indicando se é nota do sistema)
|
|
94
|
+
3. IF o MR não existir no projeto informado, THEN THE MCP_Server SHALL retornar um erro descritivo informando que o MR não foi encontrado
|
|
95
|
+
|
|
96
|
+
### Requisito 8: Obter Informações de um Projeto
|
|
97
|
+
|
|
98
|
+
**User Story:** Como agente de IA, eu quero obter informações de um projeto no GitLab, para que eu possa contextualizar análises com dados do repositório.
|
|
99
|
+
|
|
100
|
+
#### Critérios de Aceitação
|
|
101
|
+
|
|
102
|
+
1. THE MCP_Server SHALL expor uma Tool chamada `get_project` que aceita o parâmetro `project_path` (obrigatório)
|
|
103
|
+
2. WHEN a Tool `get_project` é invocada com um `project_path` válido, THE GitLab_Client SHALL retornar os dados do projeto contendo: `id`, `name`, `path_with_namespace`, `description`, `default_branch`, `web_url` e `visibility`
|
|
104
|
+
3. IF o `project_path` não corresponder a um projeto existente, THEN THE MCP_Server SHALL retornar um erro descritivo informando que o projeto não foi encontrado
|
|
105
|
+
|
|
106
|
+
### Requisito 9: Buscar Projetos
|
|
107
|
+
|
|
108
|
+
**User Story:** Como agente de IA, eu quero buscar projetos no GitLab por nome, para que eu possa descobrir o path correto de um projeto quando não o conheço.
|
|
109
|
+
|
|
110
|
+
#### Critérios de Aceitação
|
|
111
|
+
|
|
112
|
+
1. THE MCP_Server SHALL expor uma Tool chamada `search_projects` que aceita o parâmetro `search` (obrigatório)
|
|
113
|
+
2. WHEN a Tool `search_projects` é invocada com um termo de busca, THE GitLab_Client SHALL retornar uma lista de projetos correspondentes contendo para cada projeto: `id`, `name`, `path_with_namespace`, `description` e `web_url`
|
|
114
|
+
3. WHEN nenhum projeto corresponder ao termo de busca, THE MCP_Server SHALL retornar uma lista vazia
|
|
115
|
+
|
|
116
|
+
### Requisito 10: Publicação no PyPI
|
|
117
|
+
|
|
118
|
+
**User Story:** Como desenvolvedor, eu quero que o pacote seja publicável no PyPI como `pack-mcp-gitlab`, para que usuários possam instalá-lo via `uvx pack-mcp-gitlab@latest`.
|
|
119
|
+
|
|
120
|
+
#### Critérios de Aceitação
|
|
121
|
+
|
|
122
|
+
1. THE MCP_Server SHALL ser empacotado com `pyproject.toml` contendo o nome `pack-mcp-gitlab`, versão semântica, dependências (`python-gitlab`, `mcp`) e entry point para execução via `uvx`
|
|
123
|
+
2. THE MCP_Server SHALL utilizar o protocolo stdio para comunicação MCP
|
|
124
|
+
3. THE MCP_Server SHALL ser compatível com a configuração MCP no formato: `{"command": "uvx", "args": ["pack-mcp-gitlab@latest"], "env": {"GITLAB_URL": "...", "GITLAB_TOKEN": "..."}}`
|
|
125
|
+
|
|
126
|
+
### Requisito 11: Tratamento de Erros
|
|
127
|
+
|
|
128
|
+
**User Story:** Como agente de IA, eu quero receber mensagens de erro claras e estruturadas, para que eu possa entender e comunicar falhas ao usuário.
|
|
129
|
+
|
|
130
|
+
#### Critérios de Aceitação
|
|
131
|
+
|
|
132
|
+
1. IF uma chamada à API do GitLab falhar por erro de rede, THEN THE MCP_Server SHALL retornar um erro descritivo contendo o tipo de falha e a mensagem original da exceção
|
|
133
|
+
2. IF uma chamada à API do GitLab retornar erro de permissão (HTTP 403), THEN THE MCP_Server SHALL retornar um erro descritivo informando que o token não possui permissão para a operação solicitada
|
|
134
|
+
3. IF uma chamada à API do GitLab retornar erro de recurso não encontrado (HTTP 404), THEN THE MCP_Server SHALL retornar um erro descritivo informando que o recurso solicitado não existe
|
|
135
|
+
4. THE MCP_Server SHALL registrar logs de erro com detalhes suficientes para diagnóstico sem expor dados sensíveis como tokens
|