Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ The Provar DX CLI ships with a built-in **Model Context Protocol (MCP) server**
- [Quality scores explained](#quality-scores-explained)
- [API compatibility — `xml` vs `xml_content`](#api-compatibility--xml-vs-xml_content)
- [Performance Tuning](#performance-tuning)
- [Warning codes](#warning-codes)

---

Expand Down Expand Up @@ -531,6 +532,23 @@ On **Windows**, path comparisons are performed case-insensitively to account for

---

## Warning codes

Cross-cutting warning codes surfaced by validation, configuration, and run tooling. These complement the per-tool `rule_id` codes (e.g. `TC_001`, `VAR-REF-001`) documented under [Available tools](#available-tools). Subsequent revisions will refine the meanings as the relevant tool surfaces stabilise.

| Code | Surfaced by | Meaning |
| ---------------- | --------------------------------------- | ------------------------------------------------------------------------- |
| `PROVARHOME-001` | properties / automation tooling | `provarHome` is missing, blank, or does not point to a Provar install |
| `DATA-001` | `provar_testcase_validate` | `<dataTable>` iteration is silently ignored in CLI standalone execution |
| `PARALLEL-001` | automation / run tooling | Parallel-mode cache mismatch between properties and active runtime config |
| `SCHEMA-001` | strict properties / config validators | Unknown or misspelled key in a JSON / properties schema (typo guard) |
| `RUN-001` | `provar_automation_testrun` and friends | Test run produced no executable results — check input selection |
| `JUNIT-001` | report / RCA tooling | JUnit results file is missing, empty, or not parseable |

Warnings emitted programmatically follow the shape `WARNING [<CODE>]: <message>` — and when a typo is detected, the message is suffixed with `Did you mean '<suggestion>'?`. See `src/mcp/utils/warningCodes.ts` for the canonical enum.

---

## Available tools

### `provardx_ping`
Expand Down
22 changes: 22 additions & 0 deletions src/mcp/utils/warningCodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2024 Provar Limited.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

export const WARNING_CODES = {
PROVARHOME_001: 'PROVARHOME-001',
DATA_001: 'DATA-001',
PARALLEL_001: 'PARALLEL-001',
SCHEMA_001: 'SCHEMA-001',
RUN_001: 'RUN-001',
JUNIT_001: 'JUNIT-001',
} as const;

export type WarningCode = (typeof WARNING_CODES)[keyof typeof WARNING_CODES];

export function formatWarning(code: WarningCode, message: string, suggestion?: string): string {
const base = `WARNING [${code}]: ${message}`;
return suggestion ? `${base} Did you mean '${suggestion}'?` : base;
}
64 changes: 64 additions & 0 deletions test/unit/mcp/utils/warningCodes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2024 Provar Limited.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { strict as assert } from 'node:assert';
import { describe, it } from 'mocha';
import { WARNING_CODES, formatWarning } from '../../../../src/mcp/utils/warningCodes.js';

describe('WARNING_CODES', () => {
const expected: Record<string, string> = {
PROVARHOME_001: 'PROVARHOME-001',
DATA_001: 'DATA-001',
PARALLEL_001: 'PARALLEL-001',
SCHEMA_001: 'SCHEMA-001',
RUN_001: 'RUN-001',
JUNIT_001: 'JUNIT-001',
};

it('maps each key to its expected wire string', () => {
for (const [key, value] of Object.entries(expected)) {
assert.equal(
(WARNING_CODES as Record<string, string>)[key],
value,
`WARNING_CODES.${key} should equal '${value}'`
);
}
});

// Guards against silent enum drift: a new code added without updating this test,
// or an accidentally-removed code, must fail the build.
it('contains exactly the expected key set (no additions, no omissions)', () => {
assert.deepEqual(Object.keys(WARNING_CODES).sort(), Object.keys(expected).sort());
});

it('contains exactly the expected value set (no additions, no omissions)', () => {
assert.deepEqual(Object.values(WARNING_CODES).sort(), Object.values(expected).sort());
});
});

describe('formatWarning', () => {
it('returns the prefixed message exactly when no suggestion is provided', () => {
assert.equal(
formatWarning(WARNING_CODES.PROVARHOME_001, 'provarHome is missing'),
'WARNING [PROVARHOME-001]: provarHome is missing'
);
});

it('appends the "Did you mean" suffix exactly when a suggestion is provided', () => {
assert.equal(
formatWarning(WARNING_CODES.SCHEMA_001, "unknown key 'parralelMode'", 'parallelMode'),
"WARNING [SCHEMA-001]: unknown key 'parralelMode' Did you mean 'parallelMode'?"
);
});

it('omits the suffix when suggestion is an empty string', () => {
assert.equal(
formatWarning(WARNING_CODES.DATA_001, 'data-table iteration not bound', ''),
'WARNING [DATA-001]: data-table iteration not bound'
);
});
});
Loading