mcp-elm 1.0.33__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 (54) hide show
  1. mcp_elm-1.0.33/.gitignore +8 -0
  2. mcp_elm-1.0.33/LICENSE +21 -0
  3. mcp_elm-1.0.33/PKG-INFO +268 -0
  4. mcp_elm-1.0.33/README.md +253 -0
  5. mcp_elm-1.0.33/docs/manual-mcp-alm.md +1044 -0
  6. mcp_elm-1.0.33/mcp_elm/__init__.py +0 -0
  7. mcp_elm-1.0.33/mcp_elm/bundle.py +158 -0
  8. mcp_elm-1.0.33/mcp_elm/ccm.py +876 -0
  9. mcp_elm-1.0.33/mcp_elm/ibm/__init__.py +0 -0
  10. mcp_elm-1.0.33/mcp_elm/ibm/common.py +362 -0
  11. mcp_elm-1.0.33/mcp_elm/ibm/requirements.py +167 -0
  12. mcp_elm-1.0.33/mcp_elm/ibm/test.py +150 -0
  13. mcp_elm-1.0.33/mcp_elm/ibm/workitems.py +400 -0
  14. mcp_elm-1.0.33/mcp_elm/infra/__init__.py +0 -0
  15. mcp_elm-1.0.33/mcp_elm/infra/auth.py +48 -0
  16. mcp_elm-1.0.33/mcp_elm/infra/config.py +46 -0
  17. mcp_elm-1.0.33/mcp_elm/infra/document.py +376 -0
  18. mcp_elm-1.0.33/mcp_elm/infra/http.py +112 -0
  19. mcp_elm-1.0.33/mcp_elm/infra/oslc.py +520 -0
  20. mcp_elm-1.0.33/mcp_elm/qm.py +1 -0
  21. mcp_elm-1.0.33/mcp_elm/rm.py +645 -0
  22. mcp_elm-1.0.33/mcp_elm/server.py +53 -0
  23. mcp_elm-1.0.33/pyproject.toml +31 -0
  24. mcp_elm-1.0.33/tests/conftest.py +72 -0
  25. mcp_elm-1.0.33/tests/fixtures/allowed_filedagainst.xml +8 -0
  26. mcp_elm-1.0.33/tests/fixtures/apt_plans.xml +2 -0
  27. mcp_elm-1.0.33/tests/fixtures/catalog_ccm.xml +18 -0
  28. mcp_elm-1.0.33/tests/fixtures/category_c1.xml +6 -0
  29. mcp_elm-1.0.33/tests/fixtures/enum_priority.xml +7 -0
  30. mcp_elm-1.0.33/tests/fixtures/iteration_it1.xml +6 -0
  31. mcp_elm-1.0.33/tests/fixtures/query_page1.xml +17 -0
  32. mcp_elm-1.0.33/tests/fixtures/query_page2.xml +13 -0
  33. mcp_elm-1.0.33/tests/fixtures/rootservices_ccm.xml +10 -0
  34. mcp_elm-1.0.33/tests/fixtures/shape_defect.xml +36 -0
  35. mcp_elm-1.0.33/tests/fixtures/sp_ccm.xml +28 -0
  36. mcp_elm-1.0.33/tests/fixtures/team_areas.xml +11 -0
  37. mcp_elm-1.0.33/tests/fixtures/team_iterations.xml +4 -0
  38. mcp_elm-1.0.33/tests/fixtures/team_iterations_children.xml +3 -0
  39. mcp_elm-1.0.33/tests/fixtures/team_members.xml +9 -0
  40. mcp_elm-1.0.33/tests/fixtures/team_roles.xml +3 -0
  41. mcp_elm-1.0.33/tests/fixtures/user_joao.xml +7 -0
  42. mcp_elm-1.0.33/tests/fixtures/wf_actions.xml +5 -0
  43. mcp_elm-1.0.33/tests/fixtures/wf_states.xml +6 -0
  44. mcp_elm-1.0.33/tests/fixtures/workitem_42.xml +21 -0
  45. mcp_elm-1.0.33/tests/fixtures/workitem_7.xml +8 -0
  46. mcp_elm-1.0.33/tests/test_bundle.py +47 -0
  47. mcp_elm-1.0.33/tests/test_ccm.py +430 -0
  48. mcp_elm-1.0.33/tests/test_common.py +193 -0
  49. mcp_elm-1.0.33/tests/test_document.py +206 -0
  50. mcp_elm-1.0.33/tests/test_infra.py +275 -0
  51. mcp_elm-1.0.33/tests/test_requirements.py +158 -0
  52. mcp_elm-1.0.33/tests/test_rm.py +395 -0
  53. mcp_elm-1.0.33/tests/test_test.py +112 -0
  54. mcp_elm-1.0.33/tests/test_workitems.py +222 -0
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ .pytest_cache/
5
+ .superpowers/
6
+ dist/
7
+ .DS_Store
8
+ uv.lock
mcp_elm-1.0.33/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Xavier Araújo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,268 @@
1
+ Metadata-Version: 2.5
2
+ Name: mcp-elm
3
+ Version: 1.0.33
4
+ Summary: MCP server para IBM ELM (DOORS Next, EWM, ETM) via OSLC
5
+ Project-URL: Repository, https://github.com/dxaraujo/mcp-elm
6
+ Author-email: Daniel Xavier Araújo <danielxaraujo@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: markdown-it-py>=3
11
+ Requires-Dist: mcp[cli]>=2.2
12
+ Requires-Dist: rdflib>=7.0
13
+ Requires-Dist: requests>=2.31
14
+ Description-Content-Type: text/markdown
15
+
16
+ # mcp-elm
17
+
18
+ Servidor MCP para o IBM ELM via OSLC: **rm** (DOORS Next), **ccm** (EWM) e **qm** (ETM).
19
+ Ele fornece as capacidades que as skills vão consumir. As tools seguem os nomes e parâmetros do
20
+ [IBM Engineering AI Hub MCP 1.3.0](https://www.ibm.com/docs/en/engineering-lifecycle-management-suite/engineering-ai-hub/1.3.0?topic=tools-mcp-engineering-ai-hub).
21
+
22
+ ## Credenciais
23
+
24
+ | SO | Arquivo |
25
+ |---|---|
26
+ | Linux/Mac | `~/.config/mcp-elm/elm.properties` |
27
+ | Windows | `%APPDATA%\mcp-elm\elm.properties` |
28
+
29
+ Para usar outro caminho, defina a variável de ambiente `MCP_ELM_CONFIG`.
30
+
31
+ ```ini
32
+ [DEFAULT]
33
+ server = https://elm.SEU-SERVIDOR
34
+ user = SEU_USUARIO
35
+ password = SUA_SENHA
36
+ ```
37
+
38
+ Certificado com CA próprio: `export REQUESTS_CA_BUNDLE=/caminho/ca.pem`.
39
+
40
+ A autenticação usa o form Jazz (`j_security_check`). Se o servidor responder 401 pedindo Basic, o Basic é usado automaticamente.
41
+
42
+ ## Instalação
43
+
44
+ ```bash
45
+ uv sync
46
+ claude mcp add elm -- uv run --directory /caminho/para/mcp-elm mcp-elm
47
+ # depuração com o MCP Inspector
48
+ uv run mcp dev mcp_elm/server.py
49
+ ```
50
+
51
+ ## Contrato de saída
52
+
53
+ - Recursos OSLC: `{url, id, title, types, properties{}, links{}}`, com chaves em qname (`dcterms:title`).
54
+ Cada link é `{url, title?}`. `get_workitem` e `search_workitems` trazem o `title` dos recursos ligados (estado,
55
+ prioridade, iteração...) na mesma requisição (`oslc.properties`/`oslc.select` com `{dcterms:title}`) e completam
56
+ o nome dos usuários do JTS pela lista em cache do Reportable REST.
57
+ - Dados da API de processo e do Reportable REST usam os nomes de campo da doc IBM
58
+ (`get_user` → `userUUID, userId, name, emailAddress, archived`; `get_project_area` → `name, project_area_uuid,
59
+ summary, description, cm_enabled, team_areas, timelines, associations`).
60
+ - Entradas por UUID/URL, como na doc IBM. Descubra-os com `list_project_areas`, `get_project_area`,
61
+ `get_project_components` (rm) e `get_qm_component` (qm).
62
+ - `configuration`/`configuration_url`/`global_configuration_url`/`gc_uri`/`gc_context` vão no header
63
+ `Configuration-Context`. No DOORS Next, sem configuração, é usada a primeira stream do componente.
64
+
65
+ ## Tools (IBM Engineering AI Hub 1.3.0)
66
+
67
+ | Conjunto | Tools |
68
+ |---|---|
69
+ | Common | get_user, get_project_area, get_global_configuration, search_global_configuration, list_project_areas, list_linked_requirements, list_linked_workitems, list_linked_testartifacts, link_workitem_and_requirement, link_workitem_and_testartifact, link_testartifact_and_requirement |
70
+ | Requirements | get_project_components, get_rm_component_types, list_rm_component_folders, get_rm_component_configuration, get_requirement, search_requirement, create_requirement |
71
+ | Work Items | create_workitem, get_workitem, get_workitem_schema, list_workitem_categories, list_workitem_releases, search_workitems, add_comment_to_workitem |
72
+ | Test | get_testartifact, get_testartifact_schema, search_testartifact, get_qm_component, get_qm_component_configuration |
73
+
74
+ Aliases da doc (`get_rm_component`, `get_project_component_types`...) não são registrados: vale o primeiro nome.
75
+
76
+ ## Skills
77
+
78
+ As skills ficam em `skills/` e documentam, para a IA, as tools, suas entradas e saídas, os fluxos e os erros, para
79
+ evitar consultas desnecessárias ao servidor.
80
+
81
+ | Skill | Assunto |
82
+ |---|---|
83
+ | elm-setup | Cria/atualiza a configuração do projeto em `elm/pa_<project area em snake_case>.json` (um arquivo por project area do CCM) |
84
+ | elm-ccm | Work items: item de trabalho (WI), tarefa, defeito, item de backlog (IB)... |
85
+ | elm-rm | Requisitos: RF, RNF, HU, UC, regra, mensagem, especificação... |
86
+ | elm-qm | Testes (só leitura): caso de teste (CT), plano (PT), execução (TER)... |
87
+ | elm-gc | Comum: usuários, project areas, associações, configuração global e links de rastreabilidade |
88
+
89
+ Instalação por projeto (links relativos, sem cópia):
90
+
91
+ ```bash
92
+ mkdir -p .claude/skills .kiro/skills
93
+ for s in elm-setup elm-ccm elm-rm elm-qm elm-gc; do
94
+ ln -sfn ../../skills/$s .claude/skills/$s # Claude Code
95
+ ln -sfn ../../skills/$s .kiro/skills/$s # Kiro
96
+ done
97
+ ```
98
+
99
+ ### Tools das skills
100
+
101
+ Saída com as chaves do elm.json (`elm/pa_*.json`; a skill grava o arquivo, o MCP não o lê). Listas voltam como
102
+ `[{name, identifier, ...}]` e a skill grava o mapa `{name: identifier}`.
103
+
104
+ | Skill | Tools |
105
+ |---|---|
106
+ | elm-setup (ccm) | whoami, list_project_areas("CCM"), ccm_list_team_areas, ccm_list_members, ccm_list_workitem_types, ccm_list_workitem_fields, ccm_list_link_types, ccm_list_iterations, ccm_list_iteration_plans, ccm_create_iteration, ccm_create_iteration_plan |
107
+ | elm-setup (rm) | list_project_areas("RM"), get_project_area(include_associations), rm_get_configuration, rm_list_members, rm_list_folders, rm_list_requirement_types |
108
+ | elm-ccm | ccm_list_workitems, ccm_get_workitem, ccm_list_field_values, ccm_create_workitem, ccm_update_workitem, ccm_list_workitem_states (+ add_comment_to_workitem, link_*) |
109
+ | elm-rm | rm_search_requirements, rm_count_folder, rm_list_folder, rm_list_modified, rm_sync_plan, rm_download_requirements, rm_upload_requirements, rm_get_requirement, rm_create_requirement, rm_update_requirement (+ link_workitem_and_requirement) |
110
+ | elm-qm | search_testartifact, get_testartifact, get_testartifact_schema, get_qm_component, get_qm_component_configuration |
111
+ | elm-gc | whoami, get_user, list_project_areas, get_project_area, get_global_configuration, search_global_configuration, list_linked_*, link_* |
112
+
113
+ ### Leitura em Markdown + YAML
114
+
115
+ `ccm_get_workitem` e `rm_get_requirement` devolvem um documento Markdown com cabeçalho YAML. O MCP não traduz
116
+ nomes:
117
+
118
+ - **Work item:** o cabeçalho traz só os campos e links mapeados no elm.json, com o nome de lá. A skill passa
119
+ `fields=workitem-types[tipo].fields` e `link_types=ccm.link-types` e, para gravar, procura a chave no mesmo mapa
120
+ (`fields["Estimativa"]` → `rtc_cm:estimate`).
121
+ - **Requisito:** o cabeçalho traz os links preenchidos, com os nomes do DOORS Next (só leitura: não são gravados), e
122
+ `embedded` (artefatos embutidos no texto). A gravação é só de título e texto. O elm.json do RM guarda só tipos e pastas.
123
+
124
+ O cabeçalho do requisito segue o [Google OKF v0.2](https://okf.md/): primeiro os campos OKF padrão (`type`,
125
+ `title`, `description?`, `resource`, `tags` = [pasta]), depois `sources` (a fonte no DOORS Next: `author` = quem
126
+ criou, `last_modified` = última modificação no ELM e `last_modified_by` = quem modificou por último) e `generated` (quem gerou e **quando o documento foi gerado**), ambos em ISO 8601 UTC, e, por
127
+ fim, as extensões RM. `sources[0].last_modified` > `generated.at` indica cópia desatualizada; `rm_list_modified`
128
+ devolve essa data para um ou vários ids sem ler o conteúdo. Para download/sync de uma pasta, use
129
+ `rm_count_folder` (total pelo servidor) e `rm_list_folder` (todos os `{id, title, modified}`, sem teto) como
130
+ inventário: só a pasta, sem subpastas; `count` diferente do tamanho da lista indica listagem inconsistente. Para o bundle da elm-sync, o MCP cuida de tudo sem passar listas nem conteúdo pela conversa
131
+ (ver [Sincronismo do bundle](#sincronismo-do-bundle-elm-sync)). Extensões RM: `id`, `created` (UTC), `links` e `embedded`, só quando preenchidos. `verified`, `status` e
132
+ `stale_after` só aparecem quando o artefato traz um sinal real (requisitos do DOORS Next não os definem).
133
+
134
+ Exemplo (dados fictícios):
135
+
136
+ ```markdown
137
+ ---
138
+ type: Caso de Uso
139
+ title: UC - Cadastrar cliente
140
+ resource: "https://elm.example.com/rm/resources/TX_exemplo2010"
141
+ tags:
142
+ - "03-Casos de Uso"
143
+ sources:
144
+ - id: doors-next
145
+ resource: "https://elm.example.com/rm/resources/TX_exemplo2010"
146
+ author: "human:ana.souza"
147
+ last_modified: "2026-01-10T13:05:00Z"
148
+ last_modified_by: "human:bruno.lima"
149
+ generated:
150
+ by: "process:elm-mcp/1.0.14"
151
+ at: "2026-01-11T19:20:00Z"
152
+ id: 2010
153
+ created: "2026-01-05T13:00:00Z"
154
+ links:
155
+ Vincular A:
156
+ - "2002: RN - Cliente deve ser maior de idade"
157
+ embedded:
158
+ - "2001: RN - Validar CPF do cliente"
159
+ ---
160
+ ## Fluxo Básico
161
+
162
+ 1. O usuário informa os dados do cliente.
163
+ 2. O sistema valida o documento: [2001 RN - Validar CPF do cliente](https://elm.example.com/rm/resources/TX_exemplo2001)
164
+ ```
165
+
166
+ O corpo é Markdown nos dois sentidos: `text` (RM) e `description` (WI) aceitam Markdown na gravação. No RM, a
167
+ leitura sai sempre no mesmo padrão: todo artefato, embutido ou hyperlink, é `[<id> <título>](<alvo>)`, com o alvo =
168
+ URL do ELM; o que é embed está na lista `embedded` do cabeçalho (artefato ilegível sai como link e fica fora da
169
+ lista). Na gravação, o link cujo artefato está em `embedded` (parâmetro de
170
+ `rm_create_requirement`/`rm_update_requirement`) vira embed e o resto, hyperlink; alvo por URL (inclusive link da UI
171
+ web com `artifactURI`), id (`2001`) ou arquivo cujo nome começa pelo id. No EWM a descrição só tem texto, `<br/>`, `<b>`, `<i>` e `<a>` (listas viram `• ` / `1. `); para citar outro
172
+ WI, escreva "Tarefa 1002" no texto e o EWM cria o link "Menções". `[texto](url)` é hyperlink comum.
173
+
174
+ ### Sincronismo do bundle (elm-sync)
175
+
176
+ O bundle é uma pasta de md, um por requisito (`<pasta>/<id>-<slug>.md`), com `sync.md` (situação de cada artefato),
177
+ `index.md` na raiz e em cada pasta (OKF §8, como o `build_index` do okf-kit) e `log.md` (cada sync registra, sob
178
+ `## AAAA-MM-DD`, o que criou, atualizou, subiu, moveu ou apagou). O sincronismo baixa primeiro e depois sobe o que foi editado no md:
179
+
180
+ 1. `rm_sync_plan(folders, dest)`: lista as pastas, regrava `sync.md`, apaga os removidos e devolve um resumo com
181
+ `a_baixar`, `a_subir` e `conflitos`;
182
+ 2. `rm_download_requirements(dest)` até `restantes = 0`;
183
+ 3. para cada item de `conflitos`, a skill **pergunta ao usuário qual versão fica**. Se for o ELM, chama
184
+ `rm_download_requirements(dest, requirement_ids=[id])`; se for o md, chama `rm_upload_requirements(dest, requirement_ids=[id])`;
185
+ 4. `rm_upload_requirements(dest)` até `restantes = 0` (sobe título e corpo e rebaixa o requisito).
186
+
187
+ | Estado | Quando | Ação |
188
+ |---|---|---|
189
+ | `novo` | id ainda não baixado | download |
190
+ | `sincronizado` | md == ELM | — |
191
+ | `desatualizado` | `modified` do ELM ≠ `Última atualização ELM` | download |
192
+ | `atualizado` | md alterado (sha256 ≠ `Hash`) e ELM sem mudança | upload |
193
+ | `normalizado` | md **não** alterado (hash igual), mas o ELM foge da regra embed/link do bundle (diferença invisível no md) | upload |
194
+ | `conflito` | md alterado **e** ELM mudou | perguntar ao usuário: md ou ELM |
195
+ | `erro: <msg>` | falha no download | download de novo |
196
+
197
+ Mudar de pasta é tratado como remoção na antiga e criação na nova: o arquivo é apagado e a linha volta a `novo`
198
+ (com edição local no md, vira `conflito` e o arquivo fica).
199
+
200
+ ```markdown
201
+ ---
202
+ type: Relatório de Sincronismo
203
+ title: Sincronismo ELM → OKF
204
+ description: Situação de cada artefato do DOORS Next baixado neste bundle.
205
+ generated:
206
+ by: "process:elm-mcp/1.0.14"
207
+ at: "2026-10-07T12:00:00Z"
208
+ ---
209
+ | Artefato | Pasta | Última atualização ELM | Hash | Status |
210
+ |---|---|---|---|---|
211
+ | [123 — Login](</01-Requisitos/123-login.md>) | 01-Requisitos | 2026-10-01T13:45:10Z | 9f2c…e1 | sincronizado |
212
+ | 125 — Sessão | 01-Requisitos | | | novo |
213
+ ```
214
+
215
+ `Última atualização ELM` é o `modified` do ELM no último download/upload, e `Hash` é o sha256 completo do arquivo
216
+ gravado (abreviado no exemplo).
217
+
218
+ No corpo do md do bundle vale uma regra fixa: **artefato do bundle (id no `sync.md`) é sempre embed; o resto é
219
+ sempre link**. Por isso o md do bundle não tem `embedded`, e o `links:` do cabeçalho é só de leitura. O download
220
+ grava o artefato do bundle como `[id título](../03-Regras/2001-x.md)` e o de fora como `[id título](URL do ELM)`.
221
+ Se o ELM estiver diferente da regra (um embed de fora ou um hyperlink para o bundle), a linha fica `normalizado` e o
222
+ upload corrige. Formatos sugeridos para escrever no md:
223
+
224
+ | Referência | Formato |
225
+ |---|---|
226
+ | embed (artefato do bundle) | `[<id>](<id>)` |
227
+ | link para artefato de outra PA | `[<id>](<URL do ELM>)` |
228
+ | link externo | `[<texto descritivo>](<url>)` |
229
+
230
+ Um plano do elm.json vira filtro de `ccm_list_workitems` com `iteration=iterations[it].identifier` e
231
+ `team_areas=[iterations[it].plans[nome].team-area]` (plano sem `team-area`: omita `team_areas`).
232
+
233
+ ## Limites em relação à doc IBM
234
+
235
+ - `ccm_create_iteration` e `ccm_create_iteration_plan` usam os serviços internos da UI web
236
+ (`IPlanProcessRestService/createIteration` e `IPlanRestService/putItems`), pois o EWM não tem API pública para
237
+ criá-los. Exigem as permissões de processo correspondentes (ex.: "Modify structures of iterations").
238
+
239
+ - Fora do escopo: Models e Source control; `create_requirement_change_set` e `deliver_requirement_change_set`
240
+ (removidos a pedido); `add_comment_to_testartifact` (o ETM não expõe comentários de revisão
241
+ formal numa API pública).
242
+ - `search_workitems`: `filter` só com AND (OR com uma expressão; outras chaves ou lista vazia dão erro), operadores is/equals, is not, in, before/after e
243
+ contains (summary/description). `termExpressions`/`similarityExpressions` não são suportados. Estado e tipo são
244
+ filtrados pelo id (`com.ibm.team.workitem.taskWorkflow.state.s2`, `task`).
245
+ - `get_workitem_schema`: `include=approvals` não é suportado.
246
+ - `search_testartifact`: `customAttributeFilters`, `categoryFilters` e `linkFilters` não são suportados.
247
+ - `rm_update_requirement`: `text` substitui o texto inteiro; atributos e links não são gravados. `ccm_update_workitem`: `description` substitui a descrição inteira.
248
+ - `rm_search_requirements`: exige ao menos um filtro (`text`, `folder` ou `requirement_type`). O DOORS Next
249
+ responde HTTP 400 quando `text` vem junto com `folder`/`requirement_type`: busque só pelo texto e filtre o
250
+ resultado pelo `type`/`folder`.
251
+ - `get_rm_component_types`: `filter_text_only` é redundante. `list_rm_component_folders`: `include_private` é ignorado.
252
+ - `get_qm_component`/`get_qm_component_configuration`: dependem da query de componentes do provider de configuração do
253
+ ETM, que pode exigir permissão de administrador (403) e só faz sentido em project areas opt-in.
254
+
255
+ ## Arquitetura
256
+
257
+ ```
258
+ mcp_elm/
259
+ server.py instância MCP + @tool (erros esperados viram ToolError com a mensagem)
260
+ infra/ config, auth (login Jazz), http (sessão, XML, Reportable), oslc (RDF, query, descoberta),
261
+ document (leitura em Markdown + YAML)
262
+ ibm/ um módulo por conjunto de tools da doc: common, requirements, workitems, test
263
+ ccm.py rm.py qm.py tools das skills (prefixos ccm_/rm_); compõem ibm/ e infra/
264
+ skills/ SKILL.md de elm-setup, elm-ccm, elm-rm, elm-qm e elm-gc
265
+ elm/ configuração do projeto (pa_<project area>.json), gravada pela elm-setup
266
+ ```
267
+
268
+ Testes: `uv run pytest` (servidor Jazz falso em `tests/conftest.py`).
@@ -0,0 +1,253 @@
1
+ # mcp-elm
2
+
3
+ Servidor MCP para o IBM ELM via OSLC: **rm** (DOORS Next), **ccm** (EWM) e **qm** (ETM).
4
+ Ele fornece as capacidades que as skills vão consumir. As tools seguem os nomes e parâmetros do
5
+ [IBM Engineering AI Hub MCP 1.3.0](https://www.ibm.com/docs/en/engineering-lifecycle-management-suite/engineering-ai-hub/1.3.0?topic=tools-mcp-engineering-ai-hub).
6
+
7
+ ## Credenciais
8
+
9
+ | SO | Arquivo |
10
+ |---|---|
11
+ | Linux/Mac | `~/.config/mcp-elm/elm.properties` |
12
+ | Windows | `%APPDATA%\mcp-elm\elm.properties` |
13
+
14
+ Para usar outro caminho, defina a variável de ambiente `MCP_ELM_CONFIG`.
15
+
16
+ ```ini
17
+ [DEFAULT]
18
+ server = https://elm.SEU-SERVIDOR
19
+ user = SEU_USUARIO
20
+ password = SUA_SENHA
21
+ ```
22
+
23
+ Certificado com CA próprio: `export REQUESTS_CA_BUNDLE=/caminho/ca.pem`.
24
+
25
+ A autenticação usa o form Jazz (`j_security_check`). Se o servidor responder 401 pedindo Basic, o Basic é usado automaticamente.
26
+
27
+ ## Instalação
28
+
29
+ ```bash
30
+ uv sync
31
+ claude mcp add elm -- uv run --directory /caminho/para/mcp-elm mcp-elm
32
+ # depuração com o MCP Inspector
33
+ uv run mcp dev mcp_elm/server.py
34
+ ```
35
+
36
+ ## Contrato de saída
37
+
38
+ - Recursos OSLC: `{url, id, title, types, properties{}, links{}}`, com chaves em qname (`dcterms:title`).
39
+ Cada link é `{url, title?}`. `get_workitem` e `search_workitems` trazem o `title` dos recursos ligados (estado,
40
+ prioridade, iteração...) na mesma requisição (`oslc.properties`/`oslc.select` com `{dcterms:title}`) e completam
41
+ o nome dos usuários do JTS pela lista em cache do Reportable REST.
42
+ - Dados da API de processo e do Reportable REST usam os nomes de campo da doc IBM
43
+ (`get_user` → `userUUID, userId, name, emailAddress, archived`; `get_project_area` → `name, project_area_uuid,
44
+ summary, description, cm_enabled, team_areas, timelines, associations`).
45
+ - Entradas por UUID/URL, como na doc IBM. Descubra-os com `list_project_areas`, `get_project_area`,
46
+ `get_project_components` (rm) e `get_qm_component` (qm).
47
+ - `configuration`/`configuration_url`/`global_configuration_url`/`gc_uri`/`gc_context` vão no header
48
+ `Configuration-Context`. No DOORS Next, sem configuração, é usada a primeira stream do componente.
49
+
50
+ ## Tools (IBM Engineering AI Hub 1.3.0)
51
+
52
+ | Conjunto | Tools |
53
+ |---|---|
54
+ | Common | get_user, get_project_area, get_global_configuration, search_global_configuration, list_project_areas, list_linked_requirements, list_linked_workitems, list_linked_testartifacts, link_workitem_and_requirement, link_workitem_and_testartifact, link_testartifact_and_requirement |
55
+ | Requirements | get_project_components, get_rm_component_types, list_rm_component_folders, get_rm_component_configuration, get_requirement, search_requirement, create_requirement |
56
+ | Work Items | create_workitem, get_workitem, get_workitem_schema, list_workitem_categories, list_workitem_releases, search_workitems, add_comment_to_workitem |
57
+ | Test | get_testartifact, get_testartifact_schema, search_testartifact, get_qm_component, get_qm_component_configuration |
58
+
59
+ Aliases da doc (`get_rm_component`, `get_project_component_types`...) não são registrados: vale o primeiro nome.
60
+
61
+ ## Skills
62
+
63
+ As skills ficam em `skills/` e documentam, para a IA, as tools, suas entradas e saídas, os fluxos e os erros, para
64
+ evitar consultas desnecessárias ao servidor.
65
+
66
+ | Skill | Assunto |
67
+ |---|---|
68
+ | elm-setup | Cria/atualiza a configuração do projeto em `elm/pa_<project area em snake_case>.json` (um arquivo por project area do CCM) |
69
+ | elm-ccm | Work items: item de trabalho (WI), tarefa, defeito, item de backlog (IB)... |
70
+ | elm-rm | Requisitos: RF, RNF, HU, UC, regra, mensagem, especificação... |
71
+ | elm-qm | Testes (só leitura): caso de teste (CT), plano (PT), execução (TER)... |
72
+ | elm-gc | Comum: usuários, project areas, associações, configuração global e links de rastreabilidade |
73
+
74
+ Instalação por projeto (links relativos, sem cópia):
75
+
76
+ ```bash
77
+ mkdir -p .claude/skills .kiro/skills
78
+ for s in elm-setup elm-ccm elm-rm elm-qm elm-gc; do
79
+ ln -sfn ../../skills/$s .claude/skills/$s # Claude Code
80
+ ln -sfn ../../skills/$s .kiro/skills/$s # Kiro
81
+ done
82
+ ```
83
+
84
+ ### Tools das skills
85
+
86
+ Saída com as chaves do elm.json (`elm/pa_*.json`; a skill grava o arquivo, o MCP não o lê). Listas voltam como
87
+ `[{name, identifier, ...}]` e a skill grava o mapa `{name: identifier}`.
88
+
89
+ | Skill | Tools |
90
+ |---|---|
91
+ | elm-setup (ccm) | whoami, list_project_areas("CCM"), ccm_list_team_areas, ccm_list_members, ccm_list_workitem_types, ccm_list_workitem_fields, ccm_list_link_types, ccm_list_iterations, ccm_list_iteration_plans, ccm_create_iteration, ccm_create_iteration_plan |
92
+ | elm-setup (rm) | list_project_areas("RM"), get_project_area(include_associations), rm_get_configuration, rm_list_members, rm_list_folders, rm_list_requirement_types |
93
+ | elm-ccm | ccm_list_workitems, ccm_get_workitem, ccm_list_field_values, ccm_create_workitem, ccm_update_workitem, ccm_list_workitem_states (+ add_comment_to_workitem, link_*) |
94
+ | elm-rm | rm_search_requirements, rm_count_folder, rm_list_folder, rm_list_modified, rm_sync_plan, rm_download_requirements, rm_upload_requirements, rm_get_requirement, rm_create_requirement, rm_update_requirement (+ link_workitem_and_requirement) |
95
+ | elm-qm | search_testartifact, get_testartifact, get_testartifact_schema, get_qm_component, get_qm_component_configuration |
96
+ | elm-gc | whoami, get_user, list_project_areas, get_project_area, get_global_configuration, search_global_configuration, list_linked_*, link_* |
97
+
98
+ ### Leitura em Markdown + YAML
99
+
100
+ `ccm_get_workitem` e `rm_get_requirement` devolvem um documento Markdown com cabeçalho YAML. O MCP não traduz
101
+ nomes:
102
+
103
+ - **Work item:** o cabeçalho traz só os campos e links mapeados no elm.json, com o nome de lá. A skill passa
104
+ `fields=workitem-types[tipo].fields` e `link_types=ccm.link-types` e, para gravar, procura a chave no mesmo mapa
105
+ (`fields["Estimativa"]` → `rtc_cm:estimate`).
106
+ - **Requisito:** o cabeçalho traz os links preenchidos, com os nomes do DOORS Next (só leitura: não são gravados), e
107
+ `embedded` (artefatos embutidos no texto). A gravação é só de título e texto. O elm.json do RM guarda só tipos e pastas.
108
+
109
+ O cabeçalho do requisito segue o [Google OKF v0.2](https://okf.md/): primeiro os campos OKF padrão (`type`,
110
+ `title`, `description?`, `resource`, `tags` = [pasta]), depois `sources` (a fonte no DOORS Next: `author` = quem
111
+ criou, `last_modified` = última modificação no ELM e `last_modified_by` = quem modificou por último) e `generated` (quem gerou e **quando o documento foi gerado**), ambos em ISO 8601 UTC, e, por
112
+ fim, as extensões RM. `sources[0].last_modified` > `generated.at` indica cópia desatualizada; `rm_list_modified`
113
+ devolve essa data para um ou vários ids sem ler o conteúdo. Para download/sync de uma pasta, use
114
+ `rm_count_folder` (total pelo servidor) e `rm_list_folder` (todos os `{id, title, modified}`, sem teto) como
115
+ inventário: só a pasta, sem subpastas; `count` diferente do tamanho da lista indica listagem inconsistente. Para o bundle da elm-sync, o MCP cuida de tudo sem passar listas nem conteúdo pela conversa
116
+ (ver [Sincronismo do bundle](#sincronismo-do-bundle-elm-sync)). Extensões RM: `id`, `created` (UTC), `links` e `embedded`, só quando preenchidos. `verified`, `status` e
117
+ `stale_after` só aparecem quando o artefato traz um sinal real (requisitos do DOORS Next não os definem).
118
+
119
+ Exemplo (dados fictícios):
120
+
121
+ ```markdown
122
+ ---
123
+ type: Caso de Uso
124
+ title: UC - Cadastrar cliente
125
+ resource: "https://elm.example.com/rm/resources/TX_exemplo2010"
126
+ tags:
127
+ - "03-Casos de Uso"
128
+ sources:
129
+ - id: doors-next
130
+ resource: "https://elm.example.com/rm/resources/TX_exemplo2010"
131
+ author: "human:ana.souza"
132
+ last_modified: "2026-01-10T13:05:00Z"
133
+ last_modified_by: "human:bruno.lima"
134
+ generated:
135
+ by: "process:elm-mcp/1.0.14"
136
+ at: "2026-01-11T19:20:00Z"
137
+ id: 2010
138
+ created: "2026-01-05T13:00:00Z"
139
+ links:
140
+ Vincular A:
141
+ - "2002: RN - Cliente deve ser maior de idade"
142
+ embedded:
143
+ - "2001: RN - Validar CPF do cliente"
144
+ ---
145
+ ## Fluxo Básico
146
+
147
+ 1. O usuário informa os dados do cliente.
148
+ 2. O sistema valida o documento: [2001 RN - Validar CPF do cliente](https://elm.example.com/rm/resources/TX_exemplo2001)
149
+ ```
150
+
151
+ O corpo é Markdown nos dois sentidos: `text` (RM) e `description` (WI) aceitam Markdown na gravação. No RM, a
152
+ leitura sai sempre no mesmo padrão: todo artefato, embutido ou hyperlink, é `[<id> <título>](<alvo>)`, com o alvo =
153
+ URL do ELM; o que é embed está na lista `embedded` do cabeçalho (artefato ilegível sai como link e fica fora da
154
+ lista). Na gravação, o link cujo artefato está em `embedded` (parâmetro de
155
+ `rm_create_requirement`/`rm_update_requirement`) vira embed e o resto, hyperlink; alvo por URL (inclusive link da UI
156
+ web com `artifactURI`), id (`2001`) ou arquivo cujo nome começa pelo id. No EWM a descrição só tem texto, `<br/>`, `<b>`, `<i>` e `<a>` (listas viram `• ` / `1. `); para citar outro
157
+ WI, escreva "Tarefa 1002" no texto e o EWM cria o link "Menções". `[texto](url)` é hyperlink comum.
158
+
159
+ ### Sincronismo do bundle (elm-sync)
160
+
161
+ O bundle é uma pasta de md, um por requisito (`<pasta>/<id>-<slug>.md`), com `sync.md` (situação de cada artefato),
162
+ `index.md` na raiz e em cada pasta (OKF §8, como o `build_index` do okf-kit) e `log.md` (cada sync registra, sob
163
+ `## AAAA-MM-DD`, o que criou, atualizou, subiu, moveu ou apagou). O sincronismo baixa primeiro e depois sobe o que foi editado no md:
164
+
165
+ 1. `rm_sync_plan(folders, dest)`: lista as pastas, regrava `sync.md`, apaga os removidos e devolve um resumo com
166
+ `a_baixar`, `a_subir` e `conflitos`;
167
+ 2. `rm_download_requirements(dest)` até `restantes = 0`;
168
+ 3. para cada item de `conflitos`, a skill **pergunta ao usuário qual versão fica**. Se for o ELM, chama
169
+ `rm_download_requirements(dest, requirement_ids=[id])`; se for o md, chama `rm_upload_requirements(dest, requirement_ids=[id])`;
170
+ 4. `rm_upload_requirements(dest)` até `restantes = 0` (sobe título e corpo e rebaixa o requisito).
171
+
172
+ | Estado | Quando | Ação |
173
+ |---|---|---|
174
+ | `novo` | id ainda não baixado | download |
175
+ | `sincronizado` | md == ELM | — |
176
+ | `desatualizado` | `modified` do ELM ≠ `Última atualização ELM` | download |
177
+ | `atualizado` | md alterado (sha256 ≠ `Hash`) e ELM sem mudança | upload |
178
+ | `normalizado` | md **não** alterado (hash igual), mas o ELM foge da regra embed/link do bundle (diferença invisível no md) | upload |
179
+ | `conflito` | md alterado **e** ELM mudou | perguntar ao usuário: md ou ELM |
180
+ | `erro: <msg>` | falha no download | download de novo |
181
+
182
+ Mudar de pasta é tratado como remoção na antiga e criação na nova: o arquivo é apagado e a linha volta a `novo`
183
+ (com edição local no md, vira `conflito` e o arquivo fica).
184
+
185
+ ```markdown
186
+ ---
187
+ type: Relatório de Sincronismo
188
+ title: Sincronismo ELM → OKF
189
+ description: Situação de cada artefato do DOORS Next baixado neste bundle.
190
+ generated:
191
+ by: "process:elm-mcp/1.0.14"
192
+ at: "2026-10-07T12:00:00Z"
193
+ ---
194
+ | Artefato | Pasta | Última atualização ELM | Hash | Status |
195
+ |---|---|---|---|---|
196
+ | [123 — Login](</01-Requisitos/123-login.md>) | 01-Requisitos | 2026-10-01T13:45:10Z | 9f2c…e1 | sincronizado |
197
+ | 125 — Sessão | 01-Requisitos | | | novo |
198
+ ```
199
+
200
+ `Última atualização ELM` é o `modified` do ELM no último download/upload, e `Hash` é o sha256 completo do arquivo
201
+ gravado (abreviado no exemplo).
202
+
203
+ No corpo do md do bundle vale uma regra fixa: **artefato do bundle (id no `sync.md`) é sempre embed; o resto é
204
+ sempre link**. Por isso o md do bundle não tem `embedded`, e o `links:` do cabeçalho é só de leitura. O download
205
+ grava o artefato do bundle como `[id título](../03-Regras/2001-x.md)` e o de fora como `[id título](URL do ELM)`.
206
+ Se o ELM estiver diferente da regra (um embed de fora ou um hyperlink para o bundle), a linha fica `normalizado` e o
207
+ upload corrige. Formatos sugeridos para escrever no md:
208
+
209
+ | Referência | Formato |
210
+ |---|---|
211
+ | embed (artefato do bundle) | `[<id>](<id>)` |
212
+ | link para artefato de outra PA | `[<id>](<URL do ELM>)` |
213
+ | link externo | `[<texto descritivo>](<url>)` |
214
+
215
+ Um plano do elm.json vira filtro de `ccm_list_workitems` com `iteration=iterations[it].identifier` e
216
+ `team_areas=[iterations[it].plans[nome].team-area]` (plano sem `team-area`: omita `team_areas`).
217
+
218
+ ## Limites em relação à doc IBM
219
+
220
+ - `ccm_create_iteration` e `ccm_create_iteration_plan` usam os serviços internos da UI web
221
+ (`IPlanProcessRestService/createIteration` e `IPlanRestService/putItems`), pois o EWM não tem API pública para
222
+ criá-los. Exigem as permissões de processo correspondentes (ex.: "Modify structures of iterations").
223
+
224
+ - Fora do escopo: Models e Source control; `create_requirement_change_set` e `deliver_requirement_change_set`
225
+ (removidos a pedido); `add_comment_to_testartifact` (o ETM não expõe comentários de revisão
226
+ formal numa API pública).
227
+ - `search_workitems`: `filter` só com AND (OR com uma expressão; outras chaves ou lista vazia dão erro), operadores is/equals, is not, in, before/after e
228
+ contains (summary/description). `termExpressions`/`similarityExpressions` não são suportados. Estado e tipo são
229
+ filtrados pelo id (`com.ibm.team.workitem.taskWorkflow.state.s2`, `task`).
230
+ - `get_workitem_schema`: `include=approvals` não é suportado.
231
+ - `search_testartifact`: `customAttributeFilters`, `categoryFilters` e `linkFilters` não são suportados.
232
+ - `rm_update_requirement`: `text` substitui o texto inteiro; atributos e links não são gravados. `ccm_update_workitem`: `description` substitui a descrição inteira.
233
+ - `rm_search_requirements`: exige ao menos um filtro (`text`, `folder` ou `requirement_type`). O DOORS Next
234
+ responde HTTP 400 quando `text` vem junto com `folder`/`requirement_type`: busque só pelo texto e filtre o
235
+ resultado pelo `type`/`folder`.
236
+ - `get_rm_component_types`: `filter_text_only` é redundante. `list_rm_component_folders`: `include_private` é ignorado.
237
+ - `get_qm_component`/`get_qm_component_configuration`: dependem da query de componentes do provider de configuração do
238
+ ETM, que pode exigir permissão de administrador (403) e só faz sentido em project areas opt-in.
239
+
240
+ ## Arquitetura
241
+
242
+ ```
243
+ mcp_elm/
244
+ server.py instância MCP + @tool (erros esperados viram ToolError com a mensagem)
245
+ infra/ config, auth (login Jazz), http (sessão, XML, Reportable), oslc (RDF, query, descoberta),
246
+ document (leitura em Markdown + YAML)
247
+ ibm/ um módulo por conjunto de tools da doc: common, requirements, workitems, test
248
+ ccm.py rm.py qm.py tools das skills (prefixos ccm_/rm_); compõem ibm/ e infra/
249
+ skills/ SKILL.md de elm-setup, elm-ccm, elm-rm, elm-qm e elm-gc
250
+ elm/ configuração do projeto (pa_<project area>.json), gravada pela elm-setup
251
+ ```
252
+
253
+ Testes: `uv run pytest` (servidor Jazz falso em `tests/conftest.py`).