@jrpool/kilotest 61.0.0 → 62.0.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/api/schemas.ts CHANGED
@@ -73,6 +73,16 @@ export const awaitTestSchema = {
73
73
  reportID: z.string().describe('Report identifier returned by orderNewTest or orderRetest (example: 260503T0432-x9z)')
74
74
  };
75
75
 
76
+ // reportDisagreement: POST /api/reportDisagreement/{issueID}/{reportID}/{catalogIndex}
77
+ export const reportDisagreementSchema = {
78
+ issueID: z.string().describe('Issue identifier (example: contrastPoor)'),
79
+ reportID: z.string().describe('Identifier of the report (example: 260503T0432-x9z)'),
80
+ catalogIndex: z.string().describe('Identifier of the issue-exhibiting element in the catalog of elements on the page (example: 372)'),
81
+ engineID: z.string().describe('Identifier of the rule engine that produced the disputed diagnosis (example: axe), as shown in the listDiagnoses output'),
82
+ ruleID: z.string().describe('Identifier of the rule whose diagnosis is disputed, as shown in the listDiagnoses output (example: color-contrast); if that output shows null for the rule identifier, use its description of the violation instead'),
83
+ reason: z.string().describe('20- to 1000-character explanation of why the diagnosis is disputed')
84
+ };
85
+
76
86
  // listPages takes no input; omitted (mcp.ts already uses inputSchema: {}).
77
87
 
78
88
  // RESPONSE SCHEMAS
@@ -278,7 +288,7 @@ export const listViolatorsResponseSchema = envelope('GET', z.object({
278
288
  'tag name': z.unknown().describe('Copied from the report catalog entry; type not guaranteed.'),
279
289
  'inner text': z.unknown().describe('Copied from the report catalog entry; type not guaranteed.'),
280
290
  'count of rule engines reporting that the element exhibited the issue': z.number(),
281
- 'how to get details about the element': z.object({URL: z.string(), 'request method': z.literal('GET')}),
291
+ 'how to get details about the element': z.object({method: z.literal('GET'), URL: z.string()}),
282
292
  'web users can get details about the element at': z.string()
283
293
  })).nullable()
284
294
  }));
@@ -297,10 +307,20 @@ export const listDiagnosesResponseSchema = envelope('GET', z.object({
297
307
  'x, y, width, and height of bounding box': z.unknown().describe('Copied from the report catalog entry; type not guaranteed.')
298
308
  }).nullable(),
299
309
  'diagnoses of how the element exhibited the issue': z.array(z.object({
310
+ 'identifier of the rule engine': z.unknown().describe('Copied from the report act (which); type not guaranteed.'),
300
311
  'identifier of the violated rule': z.unknown().describe('Copied from the report instance (ruleID); type not guaranteed.'),
301
312
  'description of the violation': z.unknown().describe('Copied from the report instance (what); type not guaranteed.'),
302
313
  'severity of the violation on a 0-to-3 scale': z.unknown().describe('Copied from the report instance (ordinalSeverity); type not guaranteed.'),
303
- 'count of violations of the rule by the element': z.number()
314
+ 'count of violations of the rule by the element': z.number(),
315
+ 'how to report that you disagree with this diagnosis': z.object({
316
+ method: z.literal('POST'),
317
+ URL: z.string(),
318
+ 'request body': z.object({
319
+ engineID: z.string(),
320
+ ruleID: z.string(),
321
+ reason: z.string()
322
+ })
323
+ })
304
324
  })).or(z.object({error: z.string()})).nullable()
305
325
  }));
306
326
 
@@ -362,6 +382,28 @@ export const requestFeatureResponseSchema = envelope(
362
382
  z.null()
363
383
  );
364
384
 
385
+ export const reportDisagreementResponseSchema = envelope(
386
+ 'POST',
387
+ z.object({'details about your request': z.union([
388
+ z.object({error: z.string()}),
389
+ z.object({
390
+ 'date and time received': z.string(),
391
+ 'diagnosis you disagree with': z.object({
392
+ 'issue identifier': z.string(),
393
+ 'identifier of the rule engine': z.string(),
394
+ 'identifier of the violated rule': z.string()
395
+ }),
396
+ disposition: z.string()
397
+ })
398
+ ])}),
399
+ z.object({
400
+ engineID: reportDisagreementSchema.engineID,
401
+ ruleID: reportDisagreementSchema.ruleID,
402
+ reason: reportDisagreementSchema.reason
403
+ }),
404
+ z.null()
405
+ );
406
+
365
407
  // Identifies the report a successfully ordered or awaited job will produce, once complete.
366
408
  const reportIdentifierSchema = z.string().describe('Identifier of the report, to pass to listIssues once the report is ready (example: 260503T0432-x9z).');
367
409
 
@@ -16,11 +16,13 @@ import {requestHandler} from './index.ts';
16
16
 
17
17
  const port = 3998;
18
18
  const featureRequestsPath = path.join(import.meta.dirname, 'test/fixtures/apiIntegration/featureRequests.json');
19
+ const disagreementsPath = path.join(import.meta.dirname, 'test/fixtures/apiIntegration/disagreements.json');
19
20
 
20
21
  // SETUP AND TEARDOWN
21
22
 
22
23
  const savedDBDir = process.env.DB_DIR;
23
24
  const savedFeatureRequestsPath = process.env.FEATURE_REQUESTS_PATH;
25
+ const savedDisagreementsPath = process.env.DISAGREEMENTS_PATH;
24
26
  let server: http.Server;
25
27
 
26
28
  before(async () => {
@@ -31,6 +33,9 @@ before(async () => {
31
33
  process.env.FEATURE_REQUESTS_PATH = featureRequestsPath;
32
34
  await fs.mkdir(path.dirname(featureRequestsPath), {recursive: true});
33
35
  await fs.writeFile(featureRequestsPath, '[]\n');
36
+ // The same for diagnosis disagreements.
37
+ process.env.DISAGREEMENTS_PATH = disagreementsPath;
38
+ await fs.writeFile(disagreementsPath, '[]\n');
34
39
  server = http.createServer(requestHandler);
35
40
  await new Promise<void>(resolve => server.listen(port, () => resolve()));
36
41
  });
@@ -48,6 +53,12 @@ after(async () => {
48
53
  else {
49
54
  delete process.env.FEATURE_REQUESTS_PATH;
50
55
  }
56
+ if (savedDisagreementsPath !== undefined) {
57
+ process.env.DISAGREEMENTS_PATH = savedDisagreementsPath;
58
+ }
59
+ else {
60
+ delete process.env.DISAGREEMENTS_PATH;
61
+ }
51
62
  await fs.rm(path.dirname(featureRequestsPath), {recursive: true, force: true});
52
63
  server.closeAllConnections?.();
53
64
  await new Promise<void>(resolve => {
@@ -106,3 +117,14 @@ test('POST /api/requestFeature parses a JSON body and returns a JSON response th
106
117
  assert.equal(details.error, undefined);
107
118
  assert.equal(details.disposition, 'received and logged; manager notified');
108
119
  });
120
+
121
+ test('POST /api/reportDisagreement parses path parameters and a JSON body and returns a JSON response through the HTTP routing layer', async () => {
122
+ const {body} = await request('POST', '/api/reportDisagreement/linkNoText/260101T0000-mix/0', {
123
+ engineID: 'axe',
124
+ ruleID: 'r11',
125
+ reason: 'The element does have an accessible name'
126
+ });
127
+ const details = body['response content']['details about your request'];
128
+ assert.equal(details.error, undefined);
129
+ assert.equal(details.disposition, 'received and logged; manager notified');
130
+ });
package/docs/SERVICE.md CHANGED
@@ -409,9 +409,11 @@ Alerts are sent to the maintainer in the following conditions:
409
409
 
410
410
  7. **MCP feature request received**: When a user submits a feature request via the MCP server's `requestFeature` tool, an alert is sent with subject `"MCP feature request received"`, including the text of the feature request, and the request is also appended to `db/featureRequests.json`, the same way tutorial comments are appended to their own files (see item 1), so that a manager can review or delete it later via `/deleteNotesForm.html`.
411
411
 
412
- 8. **WAVE balance low**: When Kilotest detects that the account balance for the WAVE API service (used for accessibility testing) has fallen below a threshold, an alert is sent with subject `"Kilotest: WAVE balance low"`, indicating the number of credits remaining.
412
+ 8. **Diagnosis disagreement received**: When a user reports disagreement with a diagnosis via the API’s or MCP server’s `reportDisagreement` operation, an alert is sent with subject `"Kilotest: diagnosis disagreement received"`, including the reason, the disputed diagnosis (rule engine, rule, issue, element, and report), and the disagreement is also appended to `db/disagreements.json`, so that a manager can review it and, when warranted, propose a correction to the `testaro-issues` classification (see item 3 for how such corrections take effect).
413
413
 
414
- 9. **AI service balance low**: When Kilotest detects that the account balance for an AI service account (such as Anthropic) has fallen below a threshold, an alert is sent with subject `"Kilotest: AI service [N] balance low"`, indicating the approximate remaining balance in dollars and the estimated cost per job.
414
+ 9. **WAVE balance low**: When Kilotest detects that the account balance for the WAVE API service (used for accessibility testing) has fallen below a threshold, an alert is sent with subject `"Kilotest: WAVE balance low"`, indicating the number of credits remaining.
415
+
416
+ 10. **AI service balance low**: When Kilotest detects that the account balance for an AI service account (such as Anthropic) has fallen below a threshold, an alert is sent with subject `"Kilotest: AI service [N] balance low"`, indicating the approximate remaining balance in dollars and the estimated cost per job.
415
417
 
416
418
  ### Alert configuration
417
419
 
@@ -427,7 +429,7 @@ If any of these five variables is missing or empty at alert time, the alert is n
427
429
 
428
430
  ### Inbox size limits
429
431
 
430
- Four submission types (test requests and retest requests together, web tutorial comments, AI tutorial comments, and MCP feature requests) are each held in a pending "inbox" (`db/jobs/testRequests.json`, `db/comments/tutorialWeb.json`, `db/comments/tutorialAI.json`, and `db/featureRequests.json` respectively) awaiting manual review by the maintainer. Each inbox has a maximum size; once reached, a new, distinct submission is rejected (with a message suggesting the requester instead post to the [GitHub issues page](https://github.com/jrpool/kilotest/issues) or email `info@kilotest.com`) rather than added, so neither the inbox file nor the manager alert emails it generates can grow without bound (see GitHub issue #3, abuse type 3, repeated/automated submissions). This is modeled on a full mailbox rejecting new mail, rather than on a submission rate limit: it is the backlog size itself, not how quickly it grows, that costs the maintainer review time and risks unbounded storage. Every alert sent for an accepted submission reports the resulting inbox count against its cap, so a maintainer returning after an absence can immediately see how close an inbox is to full.
432
+ Five submission types (test requests and retest requests together, web tutorial comments, AI tutorial comments, MCP feature requests, and diagnosis disagreements) are each held in a pending "inbox" (`db/jobs/testRequests.json`, `db/comments/tutorialWeb.json`, `db/comments/tutorialAI.json`, `db/featureRequests.json`, and `db/disagreements.json` respectively) awaiting manual review by the maintainer. Each inbox has a maximum size; once reached, a new, distinct submission is rejected (with a message suggesting the requester instead post to the [GitHub issues page](https://github.com/jrpool/kilotest/issues) or email `info@kilotest.com`) rather than added, so neither the inbox file nor the manager alert emails it generates can grow without bound (see GitHub issue #3, abuse type 3, repeated/automated submissions). This is modeled on a full mailbox rejecting new mail, rather than on a submission rate limit: it is the backlog size itself, not how quickly it grows, that costs the maintainer review time and risks unbounded storage. Every alert sent for an accepted submission reports the resulting inbox count against its cap, so a maintainer returning after an absence can immediately see how close an inbox is to full.
431
433
 
432
434
  Each cap defaults to 20 and can be set independently via an environment variable:
433
435
 
@@ -435,6 +437,7 @@ Each cap defaults to 20 and can be set independently via an environment variable
435
437
  2. **`TUTORIAL_WEB_COMMENTS_MAX`**: The maximum number of comments held on the web tutorial page (`/tutorialWeb.html`).
436
438
  3. **`TUTORIAL_AI_COMMENTS_MAX`**: The maximum number of comments held on the AI agent tutorial page (`/tutorialAI.html`).
437
439
  4. **`FEATURE_REQUESTS_MAX`**: The maximum number of feature requests held via the MCP server's `requestFeature` tool.
440
+ 5. **`DISAGREEMENTS_MAX`**: The maximum number of disagreements with diagnoses held via the API’s or MCP server’s `reportDisagreement` operation.
438
441
 
439
442
  Each variable, if set, must be a non-negative integer; if unset, empty, or not a non-negative integer, the default of 20 is used instead. A value of `0` means no limit: that inbox is never treated as full, regardless of size. Unlike the alert-configuration variables above, these are not required: an unset value never disables the limit, it only selects the default size.
440
443
 
package/env.example CHANGED
@@ -35,6 +35,8 @@ TUTORIAL_WEB_COMMENTS_PATH=__placeholder__
35
35
  TUTORIAL_AI_COMMENTS_PATH=__placeholder__
36
36
  # Path of the feature requests file (defaults to db/featureRequests.json).
37
37
  FEATURE_REQUESTS_PATH=__placeholder__
38
+ # Path of the diagnosis disagreements file (defaults to db/disagreements.json).
39
+ DISAGREEMENTS_PATH=__placeholder__
38
40
 
39
41
  # Inbox size limits: each defaults to 20 if unset, empty, or not a non-negative integer.
40
42
  # A value of 0 means no limit.
@@ -47,6 +49,9 @@ TUTORIAL_WEB_COMMENTS_MAX=__placeholder__
47
49
  TUTORIAL_AI_COMMENTS_MAX=__placeholder__
48
50
  # Maximum number of feature requests held via the MCP server's requestFeature tool.
49
51
  FEATURE_REQUESTS_MAX=__placeholder__
52
+ # Maximum number of disagreements with diagnoses held via the MCP server's
53
+ # reportDisagreement tool.
54
+ DISAGREEMENTS_MAX=__placeholder__
50
55
  # Maximum number of jobs allowed in the job queue (db/jobs/queue) at once. Shared by every
51
56
  # path that enqueues a job: manager approval of a pending test/retest request, and the
52
57
  # orderNewTest/orderRetest MCP tools, which enqueue directly.
package/index.ts CHANGED
@@ -88,6 +88,7 @@ import {response as requestNewTestAPI} from './api/requestNewTest.ts';
88
88
  import {response as orderNewTestAPI} from './api/orderNewTest.ts';
89
89
  import {response as orderRetestAPI} from './api/orderRetest.ts';
90
90
  import {response as awaitTestAPI} from './api/awaitTest.ts';
91
+ import {response as reportDisagreementAPI} from './api/reportDisagreement.ts';
91
92
 
92
93
  // ENVIRONMENT
93
94
 
@@ -190,6 +191,7 @@ const apiRespond: {
190
191
  orderNewTest: ApiResponder;
191
192
  orderRetest: ApiResponder;
192
193
  awaitTest: ApiResponder;
194
+ reportDisagreement: ApiResponder;
193
195
  } = {
194
196
  getReport: getReportAPI,
195
197
  listDiagnoses: listDiagnosesAPI,
@@ -201,7 +203,8 @@ const apiRespond: {
201
203
  requestNewTest: requestNewTestAPI,
202
204
  orderNewTest: orderNewTestAPI,
203
205
  orderRetest: orderRetestAPI,
204
- awaitTest: awaitTestAPI
206
+ awaitTest: awaitTestAPI,
207
+ reportDisagreement: reportDisagreementAPI
205
208
  };
206
209
 
207
210
  // CONSTANTS
@@ -1396,6 +1399,16 @@ const handleRequest = async (request: IncomingMessage, response: ServerResponse)
1396
1399
  setHeaders('application/json', null, 'ultra');
1397
1400
  response.end(JSON.stringify(responseBody));
1398
1401
  }
1402
+ // Otherwise, if the service is to receive a disagreement with a diagnosis:
1403
+ else if (segments[0] === 'reportDisagreement') {
1404
+ const {engineID, ruleID, reason} = postData as {engineID: string; ruleID: string; reason: string};
1405
+ // Get the response body.
1406
+ const responseBody = await apiRespond.reportDisagreement(segments.slice(1).concat(engineID, ruleID, reason));
1407
+ await recordPageMetric('apiOperations', 'reportDisagreement');
1408
+ // Send it.
1409
+ setHeaders('application/json', null, 'ultra');
1410
+ response.end(JSON.stringify(responseBody));
1411
+ }
1399
1412
  // Otherwise, if the service is to order an immediate, automatically approved test:
1400
1413
  else if (segments[0] === 'orderNewTest') {
1401
1414
  const {description, URL, reason} = postData as {description: string; URL: string; reason: string};
package/lhm.plugin.json CHANGED
@@ -1241,17 +1241,17 @@
1241
1241
  "how to get details about the element": {
1242
1242
  "type": "object",
1243
1243
  "properties": {
1244
- "URL": {
1245
- "type": "string"
1246
- },
1247
- "request method": {
1244
+ "method": {
1248
1245
  "type": "string",
1249
1246
  "const": "GET"
1247
+ },
1248
+ "URL": {
1249
+ "type": "string"
1250
1250
  }
1251
1251
  },
1252
1252
  "required": [
1253
- "URL",
1254
- "request method"
1253
+ "method",
1254
+ "URL"
1255
1255
  ],
1256
1256
  "additionalProperties": false
1257
1257
  },
@@ -3371,6 +3371,336 @@
3371
3371
  "taskSupport": "forbidden"
3372
3372
  }
3373
3373
  },
3374
+ {
3375
+ "name": "reportDisagreement",
3376
+ "description": "Reports that you disagree with a diagnosis shown by the listDiagnoses tool; queued for manager review. Use this after listDiagnoses when you believe a rule violation was incorrectly diagnosed or classified into an issue, providing the engineID and ruleID shown in the diagnosis and your reason. Returns a confirmation with disposition details.",
3377
+ "inputSchema": {
3378
+ "type": "object",
3379
+ "properties": {
3380
+ "issueID": {
3381
+ "type": "string",
3382
+ "description": "Issue identifier (example: contrastPoor)"
3383
+ },
3384
+ "reportID": {
3385
+ "type": "string",
3386
+ "description": "Identifier of the report (example: 260503T0432-x9z)"
3387
+ },
3388
+ "catalogIndex": {
3389
+ "type": "string",
3390
+ "description": "Identifier of the issue-exhibiting element in the catalog of elements on the page (example: 372)"
3391
+ },
3392
+ "engineID": {
3393
+ "type": "string",
3394
+ "description": "Identifier of the rule engine that produced the disputed diagnosis (example: axe), as shown in the listDiagnoses output"
3395
+ },
3396
+ "ruleID": {
3397
+ "type": "string",
3398
+ "description": "Identifier of the rule whose diagnosis is disputed, as shown in the listDiagnoses output (example: color-contrast); if that output shows null for the rule identifier, use its description of the violation instead"
3399
+ },
3400
+ "reason": {
3401
+ "type": "string",
3402
+ "description": "20- to 1000-character explanation of why the diagnosis is disputed"
3403
+ }
3404
+ },
3405
+ "required": [
3406
+ "issueID",
3407
+ "reportID",
3408
+ "catalogIndex",
3409
+ "engineID",
3410
+ "ruleID",
3411
+ "reason"
3412
+ ],
3413
+ "$schema": "http://json-schema.org/draft-07/schema#"
3414
+ },
3415
+ "outputSchema": {
3416
+ "type": "object",
3417
+ "properties": {
3418
+ "tool collection": {
3419
+ "$ref": "#/definitions/ToolsFacts"
3420
+ },
3421
+ "tool name": {
3422
+ "type": "string"
3423
+ },
3424
+ "this request": {
3425
+ "type": "object",
3426
+ "properties": {
3427
+ "description": {
3428
+ "type": "string"
3429
+ },
3430
+ "method": {
3431
+ "type": "string",
3432
+ "const": "POST"
3433
+ },
3434
+ "URL": {
3435
+ "type": "string"
3436
+ },
3437
+ "body": {
3438
+ "type": "object",
3439
+ "properties": {
3440
+ "engineID": {
3441
+ "type": "string",
3442
+ "description": "Identifier of the rule engine that produced the disputed diagnosis (example: axe), as shown in the listDiagnoses output"
3443
+ },
3444
+ "ruleID": {
3445
+ "type": "string",
3446
+ "description": "Identifier of the rule whose diagnosis is disputed, as shown in the listDiagnoses output (example: color-contrast); if that output shows null for the rule identifier, use its description of the violation instead"
3447
+ },
3448
+ "reason": {
3449
+ "type": "string",
3450
+ "description": "20- to 1000-character explanation of why the diagnosis is disputed"
3451
+ }
3452
+ },
3453
+ "required": [
3454
+ "engineID",
3455
+ "ruleID",
3456
+ "reason"
3457
+ ],
3458
+ "additionalProperties": false
3459
+ },
3460
+ "closest ancestor request": {
3461
+ "anyOf": [
3462
+ {
3463
+ "$ref": "#/definitions/RequestReference"
3464
+ },
3465
+ {
3466
+ "type": "null"
3467
+ }
3468
+ ]
3469
+ }
3470
+ },
3471
+ "required": [
3472
+ "description",
3473
+ "method",
3474
+ "URL",
3475
+ "body",
3476
+ "closest ancestor request"
3477
+ ],
3478
+ "additionalProperties": false
3479
+ },
3480
+ "URLs of similar requests for web users": {
3481
+ "type": "null"
3482
+ },
3483
+ "response metadata": {
3484
+ "$ref": "#/definitions/ResponseMetadata"
3485
+ },
3486
+ "response content": {
3487
+ "type": "object",
3488
+ "properties": {
3489
+ "details about your request": {
3490
+ "anyOf": [
3491
+ {
3492
+ "type": "object",
3493
+ "properties": {
3494
+ "error": {
3495
+ "type": "string"
3496
+ }
3497
+ },
3498
+ "required": [
3499
+ "error"
3500
+ ],
3501
+ "additionalProperties": false
3502
+ },
3503
+ {
3504
+ "type": "object",
3505
+ "properties": {
3506
+ "date and time received": {
3507
+ "type": "string"
3508
+ },
3509
+ "diagnosis you disagree with": {
3510
+ "type": "object",
3511
+ "properties": {
3512
+ "issue identifier": {
3513
+ "type": "string"
3514
+ },
3515
+ "identifier of the rule engine": {
3516
+ "type": "string"
3517
+ },
3518
+ "identifier of the violated rule": {
3519
+ "type": "string"
3520
+ }
3521
+ },
3522
+ "required": [
3523
+ "issue identifier",
3524
+ "identifier of the rule engine",
3525
+ "identifier of the violated rule"
3526
+ ],
3527
+ "additionalProperties": false
3528
+ },
3529
+ "disposition": {
3530
+ "type": "string"
3531
+ }
3532
+ },
3533
+ "required": [
3534
+ "date and time received",
3535
+ "diagnosis you disagree with",
3536
+ "disposition"
3537
+ ],
3538
+ "additionalProperties": false
3539
+ }
3540
+ ]
3541
+ }
3542
+ },
3543
+ "required": [
3544
+ "details about your request"
3545
+ ],
3546
+ "additionalProperties": false
3547
+ }
3548
+ },
3549
+ "required": [
3550
+ "tool collection",
3551
+ "tool name",
3552
+ "this request",
3553
+ "URLs of similar requests for web users",
3554
+ "response metadata",
3555
+ "response content"
3556
+ ],
3557
+ "$schema": "http://json-schema.org/draft-07/schema#",
3558
+ "additionalProperties": false,
3559
+ "definitions": {
3560
+ "ToolsFacts": {
3561
+ "type": "object",
3562
+ "properties": {
3563
+ "name": {
3564
+ "type": "string"
3565
+ },
3566
+ "description": {
3567
+ "type": "object",
3568
+ "properties": {
3569
+ "what Kilotest does": {
3570
+ "type": "string"
3571
+ },
3572
+ "how to retrieve findings": {
3573
+ "type": "object",
3574
+ "properties": {
3575
+ "level 1": {
3576
+ "type": "string"
3577
+ },
3578
+ "level 2": {
3579
+ "type": "string"
3580
+ },
3581
+ "level 3": {
3582
+ "type": "string"
3583
+ },
3584
+ "level 4": {
3585
+ "type": "string"
3586
+ }
3587
+ },
3588
+ "required": [
3589
+ "level 1",
3590
+ "level 2",
3591
+ "level 3",
3592
+ "level 4"
3593
+ ],
3594
+ "additionalProperties": false
3595
+ },
3596
+ "how to generate more findings": {
3597
+ "type": "object",
3598
+ "properties": {
3599
+ "new testing": {
3600
+ "type": "string"
3601
+ },
3602
+ "retesting": {
3603
+ "type": "string"
3604
+ },
3605
+ "latency": {
3606
+ "type": "string"
3607
+ },
3608
+ "confirmation": {
3609
+ "type": "string"
3610
+ }
3611
+ },
3612
+ "required": [
3613
+ "new testing",
3614
+ "retesting",
3615
+ "latency",
3616
+ "confirmation"
3617
+ ],
3618
+ "additionalProperties": false
3619
+ }
3620
+ },
3621
+ "required": [
3622
+ "what Kilotest does",
3623
+ "how to retrieve findings",
3624
+ "how to generate more findings"
3625
+ ],
3626
+ "additionalProperties": false,
3627
+ "description": "Explanation of what Kilotest does and how to use its tools."
3628
+ },
3629
+ "URL": {
3630
+ "type": "string",
3631
+ "description": "URL of the Kilotest MCP server."
3632
+ },
3633
+ "web users can obtain similar functionalities at": {
3634
+ "type": "string"
3635
+ }
3636
+ },
3637
+ "required": [
3638
+ "name",
3639
+ "description",
3640
+ "URL",
3641
+ "web users can obtain similar functionalities at"
3642
+ ],
3643
+ "additionalProperties": false
3644
+ },
3645
+ "RequestReference": {
3646
+ "type": "object",
3647
+ "properties": {
3648
+ "tool name": {
3649
+ "type": "string"
3650
+ },
3651
+ "description": {
3652
+ "type": "string"
3653
+ },
3654
+ "method": {
3655
+ "type": "string",
3656
+ "enum": [
3657
+ "GET",
3658
+ "POST"
3659
+ ]
3660
+ },
3661
+ "URL": {
3662
+ "type": "string"
3663
+ }
3664
+ },
3665
+ "required": [
3666
+ "tool name",
3667
+ "description",
3668
+ "method",
3669
+ "URL"
3670
+ ],
3671
+ "additionalProperties": false
3672
+ },
3673
+ "ResponseMetadata": {
3674
+ "type": "object",
3675
+ "properties": {
3676
+ "identifier": {
3677
+ "type": "string",
3678
+ "description": "Unique identifier of this response (timestamp and random suffix)."
3679
+ },
3680
+ "date and time": {
3681
+ "type": "string",
3682
+ "description": "UTC date and time when the response was generated, in ISO 8601 format."
3683
+ }
3684
+ },
3685
+ "required": [
3686
+ "identifier",
3687
+ "date and time"
3688
+ ],
3689
+ "additionalProperties": false
3690
+ }
3691
+ }
3692
+ },
3693
+ "annotations": {
3694
+ "title": "Report disagreement with a diagnosis",
3695
+ "readOnlyHint": false,
3696
+ "destructiveHint": false,
3697
+ "idempotentHint": false,
3698
+ "openWorldHint": false
3699
+ },
3700
+ "execution": {
3701
+ "taskSupport": "forbidden"
3702
+ }
3703
+ },
3374
3704
  {
3375
3705
  "name": "orderNewTest",
3376
3706
  "description": "Orders testing of a page that has no report yet, with automated approval or rejection and immediate enqueueing if approved. Use this instead of requestNewTest when immediate testing is wanted; testing typically takes 2 to 4 minutes. Returns an order confirmation and disposition details, including, if approved, a report identifier (timestamp and job identifier) and a question about whether to wait for completion with the awaitTest tool.",
@@ -4392,5 +4722,5 @@
4392
4722
  }
4393
4723
  }
4394
4724
  ],
4395
- "version": "61.0.0"
4725
+ "version": "62.0.0"
4396
4726
  }
package/llms-full.txt CHANGED
@@ -124,6 +124,19 @@ Kilotest offers these tools:
124
124
  - path: `/api/requestFeature`
125
125
  - payload properties
126
126
  - `feature`: description and motivation of the feature improvement or the new feature
127
+ - `reportDisagreement`
128
+ - method: `POST`
129
+ - purpose: report disagreement with a diagnosis shown by `listDiagnoses` of how an element exhibited an issue
130
+ - path: `/api/reportDisagreement/{issueID}/{reportID}/{catalogIndex}`
131
+ - parameters
132
+ - `issueID`: identifier of the issue
133
+ - `reportID`: identifier of the report, with YYMMDDTHHmm format for its initial segment (example: 260503T0432-x9z)
134
+ - `catalogIndex`: index of the catalog entry of the element
135
+ - source of parameters: response to a `listDiagnoses` request
136
+ - payload properties
137
+ - `engineID`: identifier of the rule engine that produced the disputed diagnosis, shown in the `listDiagnoses` response
138
+ - `ruleID`: identifier of the rule whose diagnosis is disputed, shown in the `listDiagnoses` response, or, if it is null, the description of the violation
139
+ - `reason`: 20- to 1000-character explanation of why the diagnosis is disputed
127
140
  - `orderNewTest`
128
141
  - method: `POST`
129
142
  - purpose: order immediate, automatically approved testing of one web page about which no report is available, instead of waiting for manual approval
package/llms.txt CHANGED
@@ -12,6 +12,7 @@ Kilotest integrates an ensemble of 12 rule engines, with about 1,300 rules, to t
12
12
  - Request testing of a web page: `/api/requestNewTest` (`POST`)
13
13
  - Request retesting of a web page: `/api/requestRetest/{reportID}` (`POST`)
14
14
  - Request adding or improving a feature: `/api/requestFeature` (`POST`)
15
+ - Report disagreement with a diagnosis: `/api/reportDisagreement/{issueID}/{reportID}/{catalogIndex}` (`POST`)
15
16
  - Order immediate, automatically approved testing of a web page: `/api/orderNewTest` (`POST`)
16
17
  - Order immediate, automatically approved retesting of a web page: `/api/orderRetest/{reportID}` (`POST`)
17
18
  - Wait for a job ordered via orderNewTest or orderRetest to complete: `/api/awaitTest/{reportID}` (`POST`)