biaws-mcp 0.5.0 → 0.7.0

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.
package/README.md CHANGED
@@ -9,19 +9,18 @@ O servidor separa os domínios:
9
9
  topologia operacional.
10
10
  - `issues_*`: chamados, incidentes, requisições operacionais, taxonomia e classificação.
11
11
  - `demands_*`: melhorias, especificações, jornadas, prazos e contexto para desenvolvimento.
12
- - `procedures_*`: procedimentos operacionais em Markdown, com tags e classificação taxonômica compartilhadas com issues.
13
- - `documents_*`: regras, decisões, guidelines, features e referências técnicas
14
- relacionado a aplicações e componentes.
12
+ - `documents_*`: regras, decisões, guidelines, features, referências técnicas e
13
+ procedimentos relacionados ao workspace, a aplicações e a componentes.
15
14
  - `knowledge_context_load`: regras ativas e decisões aceitas para o contexto
16
15
  solicitado.
17
16
  - `attachments_*`: envio, download, classificação e exclusão de arquivos de
18
- chamados, melhorias, tarefas e procedimentos.
17
+ chamados, melhorias, tarefas e documentos.
19
18
  - `secrets_*`: consulta e registro de metadados de segredos, sem acesso aos valores.
20
19
  - `monitoring_templates_*`, `runtime_active_monitors_*` e
21
20
  `runtime_monitoring_results_list`: administração versionada de templates,
22
21
  configuração de monitores e consulta do histórico dos runtimes.
23
22
  - `resource_collections_*` e `*_move_to_collection`: organização hierárquica
24
- de aplicações, regras, decisões, procedimentos, segredos, skills e servidores.
23
+ de aplicações, documentos, melhorias, segredos, skills e servidores.
25
24
 
26
25
  O carregamento de ambiente é autocontido no pacote. Em um checkout, ele preserva
27
26
  o fallback para o `.env` da raiz; no pacote publicado, usa o `.env` local ou o
@@ -66,13 +65,13 @@ O pacote público expõe o executável `biaws-mcp`. Clientes configurados pelo C
66
65
  usam uma versão fixada por meio do cache local do npm:
67
66
 
68
67
  ```bash
69
- npx --yes biaws-mcp@0.5.0
68
+ npx --yes biaws-mcp@0.6.0
70
69
  ```
71
70
 
72
71
  Também é possível instalá-lo explicitamente:
73
72
 
74
73
  ```bash
75
- npm install --global biaws-mcp@0.5.0
74
+ npm install --global biaws-mcp@0.6.0
76
75
  biaws-mcp
77
76
  ```
78
77
 
@@ -91,7 +90,7 @@ npm start
91
90
  Em um cliente MCP, configure o comando:
92
91
 
93
92
  ```bash
94
- npx --yes biaws-mcp@0.5.0
93
+ npx --yes biaws-mcp@0.6.0
95
94
  ```
96
95
 
97
96
  O fluxo recomendado é gerar a configuração completa com:
@@ -158,7 +157,8 @@ As escritas estruturadas são:
158
157
  - `integrations_create` e `integrations_update`;
159
158
  - `repositories_create` e `repositories_update`;
160
159
  - `servers_create` e `servers_update`;
161
- - `deployments_create` e `deployments_update`;
160
+ - `deployments_create`, `deployments_update` e
161
+ `deployments_record_publication`;
162
162
  - `runtimes_create` e `runtimes_update`.
163
163
 
164
164
  As ferramentas de escrita usam `POST` ou `PATCH` da API e, portanto, herdam
@@ -220,19 +220,19 @@ monitor antes de provisionar o executor, crie-o com `enabled: false`.
220
220
  - `resource_collections_delete`: exclui somente uma coleção vazia, sem
221
221
  subcoleções nem itens vinculados;
222
222
  - `applications_move_to_collection`, `servers_move_to_collection`,
223
- `secrets_move_to_collection`, `skills_move_to_collection` e
224
- `procedures_move_to_collection`: movem um item para uma coleção validada ou
223
+ `secrets_move_to_collection`, `skills_move_to_collection`,
224
+ `demands_move_to_collection` e `documents_move_to_collection`: movem um item
225
+ para uma coleção validada ou
225
226
  para a raiz quando `collectionId` é vazio.
226
227
 
227
228
  As quatro ferramentas `resource_collections_*` aceitam `resourceType` com os
228
- valores `applications`, `procedures`, `secrets`, `skills` ou `servers`.
229
- Procedimentos usam sua árvore própria na API; os demais recursos usam a árvore
230
- compartilhada por tipo. Todas as mutações mantêm as validações contra ciclos,
229
+ valores `applications`, `demands`, `documents`, `secrets`, `skills` ou
230
+ `servers`. Todas as mutações mantêm as validações contra ciclos,
231
231
  escopo do workspace, permissões e auditoria da API.
232
232
 
233
233
  `applications_get_context` entrega, em uma única consulta, a aplicação,
234
234
  integrações, componentes, repositórios, deployments, runtimes, servidores referenciados e
235
- resumos de issues, melhorias e procedimentos associados. O argumento `limit`
235
+ resumos de issues, melhorias e documentos associados. O argumento `limit`
236
236
  vale separadamente para cada grupo, tem teto de 100 e padrão 25. O resultado
237
237
  informa totais e truncamentos e não contém credenciais, endereços dos
238
238
  servidores, metadata dos runtimes, anexos nem os textos extensos da base de
@@ -294,7 +294,7 @@ conhecimento.
294
294
  conteúdo armazenado.
295
295
 
296
296
  As quatro ferramentas aceitam `entityType` com `issue`, `demand`, `task` ou
297
- `procedure`. `entityId` identifica o chamado, melhoria ou procedimento. Para
297
+ `document`. `entityId` identifica o chamado, melhoria ou documento. Para
298
298
  `task`, `entityId` deve ser o ID da melhoria pai e `taskId` deve identificar a
299
299
  tarefa por ID ou código.
300
300
 
@@ -315,15 +315,25 @@ quando essas permissões tiverem sido removidas ou ainda não existirem, um
315
315
  administrador deverá concedê-las. Operações no contexto de tarefa usam as
316
316
  permissões de anexos da melhoria pai.
317
317
 
318
- ### Procedimentos
319
-
320
- - `procedures_search`: pesquisa por ID, texto (título, sumário e conteúdo),
321
- taxonomia (incluindo descendentes), tag e contexto.
322
- - `procedures_get_classification_catalog`: obtém a árvore taxonômica e os grupos de tags; opcionalmente inclui listas achatadas de IDs válidos.
323
- - `procedures_create`: cria um procedimento com título, sumário, conteúdo,
324
- classificação e contexto opcionais.
325
- - `procedures_update`: atualiza parcialmente título, sumário, conteúdo,
326
- classificação ou contexto.
318
+ ### Documentos
319
+
320
+ - `document_types_list`: consulta o contrato oficial de cada tipo, incluindo
321
+ estados, contexto obrigatório e campos específicos de `details`;
322
+ - `documents_search` e `documents_get`: localizam documentos e carregam seu
323
+ conteúdo completo sob demanda;
324
+ - `documents_create`: cria regras, decisões, guidelines, features, referências
325
+ técnicas e procedimentos usando um schema discriminado por `documentType`;
326
+ - `documents_update`: atualiza conteúdo, contexto e metadados, preservando o
327
+ tipo imutável;
328
+ - `documents_add_observation`: acrescenta uma observação imutável.
329
+
330
+ Regras de negócio, decisões arquiteturais e features exigem `applicationId`.
331
+ Referências técnicas podem pertencer ao workspace ou a uma aplicação.
332
+ Procedimentos também podem pertencer ao workspace ou ser vinculados a uma
333
+ aplicação e seus componentes; são documentos com `documentType=procedure`.
334
+ Guidelines usam `details.scope`: `workspace` proíbe `applicationId`,
335
+ `application` o exige e `component` exige também `affectedComponentIds`.
336
+ Componentes sempre devem estar ativos e pertencer à aplicação informada.
327
337
 
328
338
  ### Metadados de segredos
329
339
 
@@ -343,8 +353,8 @@ O grupo padrão `agent-operator` recebe `secrets.metadata.read` e
343
353
  `secrets.value.reveal`.
344
354
 
345
355
  Issues e melhorias criadas pelo MCP sempre pertencem a uma aplicação e podem
346
- informar `affectedComponentIds`. Procedimentos podem permanecer gerais ao
347
- workspace ou ser vinculados opcionalmente a uma aplicação e seus componentes.
356
+ informar `affectedComponentIds`. Documentos seguem as regras de contexto
357
+ declaradas por `document_types_list`.
348
358
  Nas ferramentas de consulta, os filtros comuns são `workspaceId`,
349
359
  `applicationId` e `componentId`.
350
360
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "biaws-mcp",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Servidor MCP de domínio para o Bondia Workspaces",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -256,6 +256,16 @@ export async function updateDeployment(args = {}) {
256
256
  );
257
257
  }
258
258
 
259
+ export async function recordDeploymentPublication(args = {}) {
260
+ const deploymentId = requiredId(args, "deploymentId");
261
+ return sendJson(
262
+ `${entityPath("deployments", deploymentId)}/publications`,
263
+ mutationPayload(args, ["deploymentId"]),
264
+ {},
265
+ "POST",
266
+ );
267
+ }
268
+
259
269
  export async function listRuntimes(args = {}) {
260
270
  const deploymentId = requiredId(args, "deploymentId");
261
271
  return fetchJson(
@@ -23,6 +23,7 @@ import {
23
23
  listRuntimes,
24
24
  listServers,
25
25
  listWorkspaces,
26
+ recordDeploymentPublication,
26
27
  updateApplication,
27
28
  updateComponent,
28
29
  updateDeployment,
@@ -165,6 +166,18 @@ const SERVER_PROPERTIES = {
165
166
  tags: TAGS,
166
167
  };
167
168
 
169
+ const PUBLICATION_PROPERTIES = {
170
+ version: STRING,
171
+ revision: STRING,
172
+ repositoryId: { type: ["string", "null"] },
173
+ status: {
174
+ type: "string",
175
+ enum: ["planned", "canceled", "deployed"],
176
+ },
177
+ publishedAt: { type: ["string", "null"] },
178
+ description: STRING,
179
+ };
180
+
168
181
  const DEPLOYMENT_PROPERTIES = {
169
182
  key: APPLICATION_PROPERTIES.key,
170
183
  name: STRING,
@@ -183,15 +196,7 @@ const DEPLOYMENT_PROPERTIES = {
183
196
  required: ["version"],
184
197
  properties: {
185
198
  id: ID,
186
- version: STRING,
187
- revision: STRING,
188
- repositoryId: { type: ["string", "null"] },
189
- status: {
190
- type: "string",
191
- enum: ["planned", "canceled", "deployed"],
192
- },
193
- publishedAt: { type: ["string", "null"] },
194
- description: STRING,
199
+ ...PUBLICATION_PROPERTIES,
195
200
  recordedAt: { type: ["string", "null"] },
196
201
  recordedBy: STRING,
197
202
  },
@@ -553,6 +558,15 @@ export const catalogTools = [
553
558
  ["deploymentId"],
554
559
  ),
555
560
  ),
561
+ definition(
562
+ "deployments_record_publication",
563
+ "Registra uma publicação no histórico append-only de um deployment sem reenviar as entradas existentes.",
564
+ recordDeploymentPublication,
565
+ schema({ deploymentId: ID, ...PUBLICATION_PROPERTIES }, [
566
+ "deploymentId",
567
+ "version",
568
+ ]),
569
+ ),
556
570
  definition(
557
571
  "runtimes_create",
558
572
  "Cria um runtime de deployment com metadata limitada e sem segredos.",
@@ -0,0 +1,7 @@
1
+ // Keep this published copy synchronized with shared/documentTypes.js. The MCP
2
+ // package cannot import files outside its package root; the parity test prevents
3
+ // the API and MCP contracts from drifting.
4
+ export {
5
+ DOCUMENT_TYPE_CATALOG,
6
+ DOCUMENT_TYPES,
7
+ } from "./documentTypeCatalogData.js";
@@ -0,0 +1,104 @@
1
+ export const DOCUMENT_TYPE_CATALOG = Object.freeze({
2
+ "business-rule": Object.freeze({
3
+ type: "business-rule",
4
+ label: "Regra de negócio",
5
+ description: "Condições e comportamentos esperados do domínio.",
6
+ defaultStatus: "draft",
7
+ statuses: Object.freeze(["draft", "active", "retired", "archived"]),
8
+ currentStatuses: Object.freeze(["active"]),
9
+ applicationRequired: true,
10
+ details: Object.freeze({
11
+ ruleCode: Object.freeze({ type: "string", maxLength: 80 }),
12
+ effectiveFrom: Object.freeze({ type: "date" }),
13
+ }),
14
+ }),
15
+ "architecture-decision": Object.freeze({
16
+ type: "architecture-decision",
17
+ label: "Decisão arquitetural",
18
+ description: "Escolhas técnicas, contexto, alternativas e consequências.",
19
+ defaultStatus: "proposed",
20
+ statuses: Object.freeze([
21
+ "proposed",
22
+ "accepted",
23
+ "rejected",
24
+ "superseded",
25
+ "archived",
26
+ ]),
27
+ currentStatuses: Object.freeze(["accepted"]),
28
+ applicationRequired: true,
29
+ details: Object.freeze({
30
+ decidedAt: Object.freeze({ type: "date" }),
31
+ }),
32
+ }),
33
+ guideline: Object.freeze({
34
+ type: "guideline",
35
+ label: "Guideline",
36
+ description: "Diretrizes e padrões de desenvolvimento.",
37
+ defaultStatus: "draft",
38
+ statuses: Object.freeze(["draft", "published", "deprecated", "archived"]),
39
+ currentStatuses: Object.freeze(["published"]),
40
+ applicationRequired: false,
41
+ details: Object.freeze({
42
+ scope: Object.freeze({
43
+ type: "string",
44
+ enum: Object.freeze(["workspace", "application", "component"]),
45
+ default: "workspace",
46
+ }),
47
+ enforcement: Object.freeze({
48
+ type: "string",
49
+ enum: Object.freeze(["required", "recommended", "informative"]),
50
+ default: "recommended",
51
+ }),
52
+ }),
53
+ }),
54
+ feature: Object.freeze({
55
+ type: "feature",
56
+ label: "Feature",
57
+ description: "Descrição funcional e técnica aprofundada de uma capacidade.",
58
+ defaultStatus: "draft",
59
+ statuses: Object.freeze(["draft", "published", "deprecated", "archived"]),
60
+ currentStatuses: Object.freeze(["published"]),
61
+ applicationRequired: true,
62
+ details: Object.freeze({
63
+ maturity: Object.freeze({
64
+ type: "string",
65
+ enum: Object.freeze(["planned", "beta", "stable", "retired"]),
66
+ default: "stable",
67
+ }),
68
+ }),
69
+ }),
70
+ "technical-reference": Object.freeze({
71
+ type: "technical-reference",
72
+ label: "Referência técnica",
73
+ description: "Arquitetura atual, contratos, schemas e mecanismos.",
74
+ defaultStatus: "draft",
75
+ statuses: Object.freeze(["draft", "published", "deprecated", "archived"]),
76
+ currentStatuses: Object.freeze(["published"]),
77
+ applicationRequired: false,
78
+ details: Object.freeze({
79
+ referenceKind: Object.freeze({
80
+ type: "string",
81
+ enum: Object.freeze([
82
+ "architecture",
83
+ "contract",
84
+ "schema",
85
+ "protocol",
86
+ "mechanism",
87
+ ]),
88
+ default: "architecture",
89
+ }),
90
+ }),
91
+ }),
92
+ procedure: Object.freeze({
93
+ type: "procedure",
94
+ label: "Procedimento",
95
+ description: "Instruções operacionais reutilizáveis.",
96
+ defaultStatus: "draft",
97
+ statuses: Object.freeze(["draft", "published", "deprecated", "archived"]),
98
+ currentStatuses: Object.freeze(["published"]),
99
+ applicationRequired: false,
100
+ details: Object.freeze({}),
101
+ }),
102
+ });
103
+
104
+ export const DOCUMENT_TYPES = Object.freeze(Object.keys(DOCUMENT_TYPE_CATALOG));
@@ -1,9 +1,11 @@
1
1
  import { cleanParams, fetchJson, sendJson } from "../../httpClient.js";
2
+ import { DOCUMENT_TYPE_CATALOG } from "./documentTypeCatalog.js";
2
3
 
3
4
  const BASE_PATH = "/api/knowledge/documents";
4
5
 
5
6
  function documentPayload(args = {}, current = {}) {
6
7
  return {
8
+ identifier: args.identifier ?? current.identifier,
7
9
  documentType: args.documentType ?? current.documentType,
8
10
  title: args.title ?? current.title,
9
11
  summary: args.summary ?? current.summary,
@@ -29,6 +31,28 @@ function documentPayload(args = {}, current = {}) {
29
31
  };
30
32
  }
31
33
 
34
+ export function listDocumentTypes() {
35
+ return {
36
+ documentTypes: Object.values(DOCUMENT_TYPE_CATALOG).map((config) => ({
37
+ ...config,
38
+ context: config.applicationRequired
39
+ ? { applicationId: "required", affectedComponentIds: "optional" }
40
+ : config.type === "guideline"
41
+ ? {
42
+ applicationId: "depends-on-details.scope",
43
+ affectedComponentIds: "required-when-scope-is-component",
44
+ }
45
+ : { applicationId: "optional", affectedComponentIds: "optional" },
46
+ })),
47
+ commonRules: {
48
+ workspaceId: "implicit-from-mcp-configuration",
49
+ affectedComponentIds: "requires-applicationId",
50
+ documentType: "immutable-after-creation",
51
+ repositorySource: "repositoryId-and-path-required",
52
+ },
53
+ };
54
+ }
55
+
32
56
  export async function searchDocuments(args = {}) {
33
57
  return fetchJson(
34
58
  BASE_PATH,
@@ -2,78 +2,271 @@ import {
2
2
  addDocumentObservation,
3
3
  createDocument,
4
4
  getDocument,
5
+ listDocumentTypes,
5
6
  loadKnowledgeContext,
6
7
  searchDocuments,
7
8
  updateDocument,
8
9
  } from "./service.js";
10
+ import {
11
+ DOCUMENT_TYPE_CATALOG,
12
+ DOCUMENT_TYPES,
13
+ } from "./documentTypeCatalog.js";
9
14
 
10
15
  const ID = { type: "string", minLength: 1 };
16
+ const DATE = {
17
+ type: "string",
18
+ pattern: "^\\d{4}-\\d{2}-\\d{2}$",
19
+ description: "Data no formato YYYY-MM-DD.",
20
+ };
11
21
  const DOCUMENT_TYPE = {
12
22
  type: "string",
13
- enum: [
14
- "business-rule",
15
- "architecture-decision",
16
- "guideline",
17
- "feature",
18
- "technical-reference",
19
- "procedure",
20
- ],
23
+ enum: DOCUMENT_TYPES,
24
+ description:
25
+ "Tipo imutável do documento. Consulte document_types_list para o contrato completo.",
21
26
  };
22
27
  const REFERENCES = {
23
28
  type: "array",
24
29
  maxItems: 100,
30
+ description: "Relações com outros documentos do mesmo workspace.",
25
31
  items: {
26
32
  type: "object",
27
33
  required: ["targetDocumentId"],
28
34
  additionalProperties: false,
29
35
  properties: {
30
36
  targetDocumentId: ID,
31
- relationship: { type: "string" },
37
+ relationship: {
38
+ type: "string",
39
+ maxLength: 80,
40
+ description: "Tipo da relação; usa related quando omitido.",
41
+ },
42
+ },
43
+ },
44
+ };
45
+ const SOURCE = {
46
+ type: "object",
47
+ description:
48
+ "Origem canônica. Em mode=repository, repositoryId e path são obrigatórios.",
49
+ additionalProperties: false,
50
+ properties: {
51
+ mode: { type: "string", enum: ["native", "repository"] },
52
+ repositoryId: { type: "string", maxLength: 160 },
53
+ path: { type: "string", maxLength: 500 },
54
+ },
55
+ oneOf: [
56
+ {
57
+ properties: { mode: { const: "native" } },
58
+ required: ["mode"],
59
+ },
60
+ {
61
+ properties: { mode: { const: "repository" } },
62
+ required: ["mode", "repositoryId", "path"],
63
+ },
64
+ ],
65
+ };
66
+ const CLASSIFICATION = {
67
+ type: "object",
68
+ description: "Taxonomias e tags aplicáveis ao contexto do documento.",
69
+ additionalProperties: false,
70
+ properties: {
71
+ primaryTaxonomyId: { type: "string" },
72
+ secondaryTaxonomyIds: { type: "array", items: ID },
73
+ tags: {
74
+ type: "object",
75
+ additionalProperties: { type: "array", items: ID },
32
76
  },
33
77
  },
34
78
  };
79
+
80
+ function detailProperty(definition) {
81
+ if (definition.type === "date") return DATE;
82
+ return {
83
+ type: definition.type,
84
+ ...(definition.enum ? { enum: definition.enum } : {}),
85
+ ...(definition.maxLength ? { maxLength: definition.maxLength } : {}),
86
+ ...(definition.default !== undefined
87
+ ? { default: definition.default }
88
+ : {}),
89
+ };
90
+ }
91
+
92
+ function detailsSchema(type, scope) {
93
+ const config = DOCUMENT_TYPE_CATALOG[type];
94
+ const properties = Object.fromEntries(
95
+ Object.entries(config.details).map(([field, definition]) => [
96
+ field,
97
+ detailProperty(definition),
98
+ ]),
99
+ );
100
+ if (scope) properties.scope = { const: scope };
101
+ return {
102
+ type: "object",
103
+ additionalProperties: false,
104
+ properties,
105
+ ...(scope ? { required: ["scope"] } : {}),
106
+ };
107
+ }
108
+
109
+ const ALL_STATUSES = [
110
+ ...new Set(
111
+ Object.values(DOCUMENT_TYPE_CATALOG).flatMap(({ statuses }) => statuses),
112
+ ),
113
+ ];
114
+
35
115
  const COMMON = {
116
+ identifier: {
117
+ type: "string",
118
+ pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$",
119
+ maxLength: 80,
120
+ description:
121
+ "Identificador opcional e único no workspace, usando minúsculas, números e hífens simples.",
122
+ },
36
123
  documentType: DOCUMENT_TYPE,
37
- title: ID,
124
+ title: { ...ID, maxLength: 240 },
38
125
  summary: { type: "string", minLength: 1, maxLength: 500 },
39
126
  markdown: ID,
40
- applicationId: { type: "string" },
41
- affectedComponentIds: { type: "array", maxItems: 100, items: ID },
127
+ applicationId: {
128
+ type: "string",
129
+ minLength: 1,
130
+ description:
131
+ "Obrigatório para regras, decisões, features e documentos cujo escopo seja aplicação ou componente; omita no escopo do workspace.",
132
+ },
133
+ affectedComponentIds: {
134
+ type: "array",
135
+ maxItems: 100,
136
+ items: ID,
137
+ description:
138
+ "Componentes ativos da applicationId. Não pode ser informado sem aplicação.",
139
+ },
42
140
  collectionId: { type: "string" },
43
- status: { type: "string" },
44
- details: { type: "object" },
45
- classification: {
141
+ status: {
142
+ type: "string",
143
+ enum: ALL_STATUSES,
144
+ description:
145
+ "Estado válido para o documentType; quando omitido, usa o estado inicial do tipo.",
146
+ },
147
+ details: {
46
148
  type: "object",
149
+ description:
150
+ "Metadados específicos do documentType, validados pelo ramo correspondente do schema.",
151
+ },
152
+ classification: CLASSIFICATION,
153
+ source: SOURCE,
154
+ references: REFERENCES,
155
+ definedAt: DATE,
156
+ lastReviewedAt: {
157
+ type: "string",
158
+ description: "YYYY-MM-DD ou vazio.",
159
+ },
160
+ nextReviewAt: { type: "string", description: "YYYY-MM-DD ou vazio." },
161
+ changeSummary: { type: "string" },
162
+ };
163
+
164
+ function typeBranch(type) {
165
+ const config = DOCUMENT_TYPE_CATALOG[type];
166
+ return {
167
+ description: `${config.label}: ${config.description}`,
168
+ properties: {
169
+ documentType: { const: type },
170
+ status: { type: "string", enum: config.statuses },
171
+ details: detailsSchema(type),
172
+ },
173
+ required: [
174
+ "documentType",
175
+ ...(config.applicationRequired ? ["applicationId"] : []),
176
+ ],
177
+ };
178
+ }
179
+
180
+ function optionalApplicationBranches(type) {
181
+ const config = DOCUMENT_TYPE_CATALOG[type];
182
+ const properties = {
183
+ documentType: { const: type },
184
+ status: { type: "string", enum: config.statuses },
185
+ details: detailsSchema(type),
186
+ };
187
+ return [
188
+ {
189
+ description: `${config.label} geral do workspace; applicationId e componentes devem ser omitidos.`,
190
+ properties: {
191
+ ...properties,
192
+ applicationId: { const: null },
193
+ affectedComponentIds: { type: "array", maxItems: 0 },
194
+ },
195
+ required: ["documentType"],
196
+ },
197
+ {
198
+ description: `${config.label} vinculada a uma aplicação e, opcionalmente, a seus componentes.`,
199
+ properties,
200
+ required: ["documentType", "applicationId"],
201
+ },
202
+ ];
203
+ }
204
+
205
+ const CREATE_VARIANTS = [
206
+ typeBranch("business-rule"),
207
+ typeBranch("architecture-decision"),
208
+ typeBranch("feature"),
209
+ ...optionalApplicationBranches("technical-reference"),
210
+ ...optionalApplicationBranches("procedure"),
211
+ {
212
+ description:
213
+ "Guideline geral do workspace; applicationId deve ser omitido.",
47
214
  properties: {
48
- primaryTaxonomyId: { type: "string" },
49
- secondaryTaxonomyIds: { type: "array", items: ID },
50
- tags: {
51
- type: "object",
52
- additionalProperties: { type: "array", items: ID },
215
+ documentType: { const: "guideline" },
216
+ status: {
217
+ type: "string",
218
+ enum: DOCUMENT_TYPE_CATALOG.guideline.statuses,
53
219
  },
220
+ details: detailsSchema("guideline", "workspace"),
221
+ applicationId: { const: null },
222
+ affectedComponentIds: { type: "array", maxItems: 0 },
54
223
  },
224
+ required: ["documentType", "details"],
55
225
  },
56
- source: {
57
- type: "object",
226
+ {
227
+ description: "Guideline vinculada a uma aplicação.",
58
228
  properties: {
59
- mode: { type: "string", enum: ["native", "repository"] },
60
- repositoryId: { type: "string" },
61
- path: { type: "string" },
229
+ documentType: { const: "guideline" },
230
+ status: {
231
+ type: "string",
232
+ enum: DOCUMENT_TYPE_CATALOG.guideline.statuses,
233
+ },
234
+ details: detailsSchema("guideline", "application"),
62
235
  },
236
+ required: ["documentType", "details", "applicationId"],
63
237
  },
64
- references: REFERENCES,
65
- definedAt: { type: "string", description: "YYYY-MM-DD" },
66
- lastReviewedAt: { type: "string", description: "YYYY-MM-DD ou vazio" },
67
- nextReviewAt: { type: "string", description: "YYYY-MM-DD ou vazio" },
68
- changeSummary: { type: "string" },
69
- };
238
+ {
239
+ description: "Guideline vinculada a ao menos um componente da aplicação.",
240
+ properties: {
241
+ documentType: { const: "guideline" },
242
+ status: {
243
+ type: "string",
244
+ enum: DOCUMENT_TYPE_CATALOG.guideline.statuses,
245
+ },
246
+ details: detailsSchema("guideline", "component"),
247
+ affectedComponentIds: {
248
+ type: "array",
249
+ minItems: 1,
250
+ maxItems: 100,
251
+ items: ID,
252
+ },
253
+ },
254
+ required: [
255
+ "documentType",
256
+ "details",
257
+ "applicationId",
258
+ "affectedComponentIds",
259
+ ],
260
+ },
261
+ ];
70
262
 
71
- function schema(properties, required = []) {
263
+ function schema(properties, required = [], extra = {}) {
72
264
  return {
73
265
  type: "object",
74
266
  ...(required.length ? { required } : {}),
75
267
  additionalProperties: false,
76
268
  properties,
269
+ ...extra,
77
270
  };
78
271
  }
79
272
 
@@ -96,6 +289,12 @@ export const knowledgeTools = [
96
289
  ["applicationId"],
97
290
  ),
98
291
  ),
292
+ definition(
293
+ "document_types_list",
294
+ "Lista o contrato oficial de cada tipo de documento: contexto exigido, estados e campos details.",
295
+ listDocumentTypes,
296
+ schema({}),
297
+ ),
99
298
  definition(
100
299
  "documents_search",
101
300
  "Busca documentos por tipo, aplicação, componente, coleção, estado ou texto.",
@@ -106,7 +305,7 @@ export const knowledgeTools = [
106
305
  applicationId: ID,
107
306
  componentId: ID,
108
307
  collectionId: ID,
109
- status: { type: "string" },
308
+ status: { type: "string", enum: ALL_STATUSES },
110
309
  currentOnly: { type: "boolean" },
111
310
  includeWorkspace: { type: "boolean" },
112
311
  includeArchived: { type: "boolean" },
@@ -122,13 +321,15 @@ export const knowledgeTools = [
122
321
  ),
123
322
  definition(
124
323
  "documents_create",
125
- "Cria um documento tipado no workspace ou em uma aplicação.",
324
+ "Cria um documento usando o contrato discriminado pelo documentType. O workspace vem da configuração do MCP; applicationId e details seguem as regras de cada tipo.",
126
325
  createDocument,
127
- schema(COMMON, ["documentType", "title", "summary", "markdown"]),
326
+ schema(COMMON, ["documentType", "title", "summary", "markdown"], {
327
+ oneOf: CREATE_VARIANTS,
328
+ }),
128
329
  ),
129
330
  definition(
130
331
  "documents_update",
131
- "Atualiza conteúdo, contexto, metadados e relações de um documento.",
332
+ "Atualiza um documento existente. documentType é imutável; consulte documents_get antes de alterar contexto, status ou details.",
132
333
  updateDocument,
133
334
  schema({ documentId: ID, ...COMMON }, ["documentId"]),
134
335
  ),
package/src/tools.js CHANGED
@@ -236,7 +236,7 @@ const tools = [
236
236
  {
237
237
  name: "issues_create_taxonomy_item",
238
238
  description:
239
- "Inclui um item na taxonomia compartilhada de issues e procedimentos, na raiz ou sob um item pai.",
239
+ "Inclui um item na taxonomia compartilhada de issues e documentos, na raiz ou sob um item pai.",
240
240
  inputSchema: {
241
241
  type: "object",
242
242
  required: ["id", "label"],
@@ -831,6 +831,45 @@ function validateEnum(value, schema, path, fields) {
831
831
  }
832
832
  }
833
833
 
834
+ function validateConst(value, schema, path, fields) {
835
+ if (Object.hasOwn(schema, "const") && value !== schema.const) {
836
+ addValidationField(
837
+ fields,
838
+ path,
839
+ "invalid_const",
840
+ `${path} must be ${JSON.stringify(schema.const)}`,
841
+ );
842
+ }
843
+ }
844
+
845
+ function validateString(value, schema, path, fields) {
846
+ if (typeof value !== "string") return;
847
+ if (schema.minLength !== undefined && value.length < schema.minLength) {
848
+ addValidationField(
849
+ fields,
850
+ path,
851
+ "min_length",
852
+ `${path} must contain at least ${schema.minLength} characters`,
853
+ );
854
+ }
855
+ if (schema.maxLength !== undefined && value.length > schema.maxLength) {
856
+ addValidationField(
857
+ fields,
858
+ path,
859
+ "max_length",
860
+ `${path} must contain at most ${schema.maxLength} characters`,
861
+ );
862
+ }
863
+ if (schema.pattern && !new RegExp(schema.pattern, "u").test(value)) {
864
+ addValidationField(
865
+ fields,
866
+ path,
867
+ "pattern",
868
+ `${path} must match ${schema.pattern}`,
869
+ );
870
+ }
871
+ }
872
+
834
873
  function validateNumber(value, schema, path, fields) {
835
874
  if (typeof value !== "number") return;
836
875
  if (schema.minimum !== undefined && value < schema.minimum) {
@@ -921,13 +960,73 @@ function validateObject(value, schema, path, fields) {
921
960
  validateObjectEntries(value, schema, path, fields);
922
961
  }
923
962
 
963
+ function validateNot(value, schema, path, fields) {
964
+ if (!schema.not) return;
965
+ const nestedFields = [];
966
+ validateValue(value, schema.not, path, nestedFields);
967
+ if (!nestedFields.length) {
968
+ addValidationField(
969
+ fields,
970
+ path,
971
+ "not",
972
+ `${path || "arguments"} contains a forbidden combination of fields`,
973
+ );
974
+ }
975
+ }
976
+
977
+ function validateOneOf(value, schema, path, fields) {
978
+ if (!schema.oneOf?.length) return;
979
+ const candidates = schema.oneOf.map((candidate) => {
980
+ const candidateFields = [];
981
+ validateValue(value, candidate, path, candidateFields);
982
+ return candidateFields;
983
+ });
984
+ const matches = candidates.filter(
985
+ (candidateFields) => !candidateFields.length,
986
+ );
987
+ if (matches.length === 1) return;
988
+ if (!matches.length) {
989
+ const discriminatedIndexes = schema.oneOf.flatMap((candidate, index) => {
990
+ const matchesDiscriminator = Object.entries(
991
+ candidate.properties || {},
992
+ ).some(
993
+ ([property, definition]) =>
994
+ Object.hasOwn(definition, "const") &&
995
+ isPlainObject(value) &&
996
+ value[property] === definition.const,
997
+ );
998
+ return matchesDiscriminator ? [index] : [];
999
+ });
1000
+ const eligible = discriminatedIndexes.length
1001
+ ? discriminatedIndexes.map((index) => candidates[index])
1002
+ : candidates;
1003
+ const best = eligible.reduce((selected, candidate) =>
1004
+ candidate.length < selected.length ? candidate : selected,
1005
+ );
1006
+ if (best.length) {
1007
+ fields.push(...best);
1008
+ return;
1009
+ }
1010
+ }
1011
+ addValidationField(
1012
+ fields,
1013
+ path,
1014
+ "one_of",
1015
+ `${path || "arguments"} must match exactly one supported schema variant`,
1016
+ );
1017
+ }
1018
+
924
1019
  function validateValue(value, schema, path, fields) {
925
1020
  if (!schema || value === undefined) return;
926
1021
  if (!validateSchemaType(value, schema, path, fields)) return;
1022
+ validateConst(value, schema, path, fields);
927
1023
  validateEnum(value, schema, path, fields);
1024
+ validateString(value, schema, path, fields);
928
1025
  validateNumber(value, schema, path, fields);
929
1026
  validateArray(value, schema, path, fields);
930
1027
  validateObject(value, schema, path, fields);
1028
+ validateNot(value, schema, path, fields);
1029
+ validateOneOf(value, schema, path, fields);
931
1030
  }
932
1031
 
933
1032
  function validateArguments(tool, args) {
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export const SERVER_NAME = "biaws-mcp";
2
- export const SERVER_VERSION = "0.5.0";
2
+ export const SERVER_VERSION = "0.7.0";