@jrpool/kilotest 37.0.0 → 38.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/alerts.js CHANGED
@@ -19,7 +19,7 @@ const ALERT_FROM = process.env.ALERT_FROM;
19
19
  // FUNCTIONS
20
20
 
21
21
  // Sends an email alert to a manager.
22
- exports.sendAlert = (subject, body) => new Promise((resolve, reject) => {
22
+ exports.sendAlert = (subject, body) => new Promise(resolve => {
23
23
  // If the alert configuration is complete:
24
24
  if (MANAGER_EMAIL && ALERT_API_HOST && ALERT_API_PATH && ALERT_API_KEY && ALERT_FROM) {
25
25
  const payload = JSON.stringify({
@@ -23,10 +23,27 @@ const path = require('path');
23
23
 
24
24
  // Adds parameters to a query for the answer page.
25
25
  const populateQuery = async (issueID, timeStamp, jobID, catalogIndex, pathID, query) => {
26
+ // Get descriptions of the page facts.
26
27
  const pageDataStrings = await getPageDataStrings(timeStamp, jobID);
27
- const {what, url, urlLink, testInfo} = pageDataStrings;
28
+ // If this failed:
29
+ if (pageDataStrings.error) {
30
+ // Populate the query with the reason.
31
+ query.error = pageDataStrings.error;
32
+ // Stop populating the query.
33
+ return;
34
+ }
35
+ const {testInfo, url, urlLink, what} = pageDataStrings;
36
+ // Otherwise, i.e. if it succeeded, get the report.
28
37
  const report = await getReport(timeStamp, jobID);
29
38
  const {acts, catalog} = report;
39
+ // If this failed:
40
+ if (report.error) {
41
+ // Populate the query with the reason.
42
+ query.error = report.error;
43
+ // Stop populating the query.
44
+ return;
45
+ }
46
+ // Otherwise, i.e. if it succeeded, get the catalog item of the specified violator.
30
47
  const catalogItem = catalog[catalogIndex] ?? {};
31
48
  const {boxID, startTag, tagName, text} = catalogItem;
32
49
  query.catalogIndex = catalogIndex;
@@ -45,7 +62,15 @@ const populateQuery = async (issueID, timeStamp, jobID, catalogIndex, pathID, qu
45
62
  query.target = what;
46
63
  query.urlLink = urlLink;
47
64
  query.testInfo = testInfo;
48
- query.issue = issues[issueID].summary;
65
+ query.issue = issues[issueID]?.summary;
66
+ // If adding the issue summary failed:
67
+ if (! query.issue) {
68
+ // Populate the query with the reason.
69
+ query.error = 'Issue not found';
70
+ // Stop populating the query.
71
+ return;
72
+ }
73
+ // Otherwise, i.e. if it succeeded, get the issue details.
49
74
  const issue = issues[issueID];
50
75
  const {wcag, weight, why} = issue;
51
76
  query.why = why;
@@ -118,7 +143,15 @@ exports.answer = async (pageArgs, search) => {
118
143
  const query = {};
119
144
  // Create a query to replace the placeholders.
120
145
  await populateQuery(issueID, timeStamp, jobID, catalogIndex, pathID, query);
121
- // If the test specifications are valid:
146
+ // If this failed:
147
+ if (query.error) {
148
+ // Return why.
149
+ return {
150
+ status: 'error',
151
+ message: query.error
152
+ };
153
+ }
154
+ // Otherwise, if it succeeded and the report facts were obtained:
122
155
  if (query.testInfo) {
123
156
  // Get the template.
124
157
  let answerPage = await fs.readFile(path.join(__dirname, 'index.html'), 'utf8');
@@ -132,9 +165,10 @@ exports.answer = async (pageArgs, search) => {
132
165
  answerPage
133
166
  };
134
167
  }
135
- // Otherwise, report this.
168
+ // Otherwise, i.e. if the report facts were not obtained:
169
+ // Report this.
136
170
  return {
137
171
  status: 'error',
138
- message: 'Invalid report specification'
172
+ message: 'Report facts not obtained'
139
173
  };
140
174
  };
package/eslint.config.mjs CHANGED
@@ -3,7 +3,7 @@ import globals from "globals";
3
3
  import json from "@eslint/json";
4
4
  import markdown from "@eslint/markdown";
5
5
  import css from "@eslint/css";
6
- import { defineConfig } from "eslint/config";
6
+ import {defineConfig} from "eslint/config";
7
7
 
8
8
  export default defineConfig([
9
9
  {
@@ -18,6 +18,9 @@ export default defineConfig([
18
18
  extends: ["js/recommended"],
19
19
  languageOptions: {
20
20
  globals: globals.node
21
+ },
22
+ rules: {
23
+ "no-control-regex": "off"
21
24
  }
22
25
  },
23
26
  {
package/index.js CHANGED
@@ -30,6 +30,7 @@ const {
30
30
  } = require('./util');
31
31
  const {handleMCP, mcpPath} = require('./mcp');
32
32
  const fs = require('fs/promises');
33
+ const {handleComment} = require('./tutorial/index');
33
34
  const http = require('http');
34
35
  const https = require('https');
35
36
  const path = require('path');
@@ -273,26 +274,30 @@ const requestHandler = async (request, response) => {
273
274
  if (isTimeStamp(timeStamp) && isJobID(jobID)) {
274
275
  const reportHidden = await isHidden(timeStamp, jobID);
275
276
  // If the report exists and is hidden:
276
- if (reportHidden === true) {
277
- console.log(`ERROR: Hidden report ${timeStamp}-${jobID} requested`);
277
+ if (reportHidden) {
278
+ console.error(`Hidden report ${timeStamp}-${jobID} requested`);
278
279
  // Report this as suspected abuse.
279
280
  await serveError(
280
- getAbuseError(request, `Requested report ${timeStamp}-${jobID} not available`),
281
+ getAbuseError(request, `Hidden report ${timeStamp}-${jobID} requested`),
281
282
  response,
282
283
  true
283
284
  );
284
285
  }
285
- // Otherwise, if the report does not exist or another error occurred:
286
- else if (typeof reportHidden === 'string') {
287
- // Report this as suspected abuse.
288
- await serveError(getAbuseError(request, reportHidden), response, true);
289
- }
290
- // Otherwise, i.e. if the report log is valid and not hidden:
286
+ // Otherwise, i.e. if the report is not hidden:
291
287
  else {
292
- // Get the report.
288
+ // Get it.
293
289
  const report = await getReport(timeStamp, jobID);
294
- // If it exists and is valid:
295
- if (typeof report === 'object') {
290
+ // If this failed:
291
+ if (report.error) {
292
+ // Report this as suspected abuse.
293
+ await serveError(
294
+ getAbuseError(request, `Nonexistent report ${timeStamp}-${jobID} requested`),
295
+ response,
296
+ true
297
+ );
298
+ }
299
+ // Otherwise, i.e. if it succeeded:
300
+ else {
296
301
  // Serve response headers for a JSON download.
297
302
  setHeaders('application/json', null, 'low');
298
303
  response.setHeader(
@@ -301,11 +306,6 @@ const requestHandler = async (request, response) => {
301
306
  // Download the report.
302
307
  response.end(getJSON(report));
303
308
  }
304
- // Otherwise, i.e. if an error occurred:
305
- else {
306
- // Report it.
307
- await serveError({message: report}, response, true);
308
- }
309
309
  }
310
310
  }
311
311
  // Otherwise, i.e. if the request is syntactically invalid:
@@ -350,10 +350,10 @@ const requestHandler = async (request, response) => {
350
350
  setHeaders('application/json', null, 'ultra');
351
351
  response.end(JSON.stringify(responseData));
352
352
  }
353
- // Otherwise, if the service is provision of issue statistics for a report:
354
- else if (service === 'reportIssues') {
353
+ // Otherwise, if the service is provision of a report summary:
354
+ else if (service === 'reportSummary') {
355
355
  // Get the response (potentially error) data.
356
- const responseData = await require(path.join(__dirname, 'reportIssues', 'api'))
356
+ const responseData = await require(path.join(__dirname, 'reportSummary', 'api'))
357
357
  .response(specs);
358
358
  // Send them.
359
359
  setHeaders('application/json', null, 'high');
@@ -701,12 +701,12 @@ const requestHandler = async (request, response) => {
701
701
  }
702
702
  // Otherwise, if the first segment is the report finding service:
703
703
  else if (segments[0] === 'target') {
704
- const {what = '', hostname = ''} = postData;
705
- // If the payload contains a description fragment or hostname fragment:
706
- if (what || hostname) {
704
+ const {description = '', hostname = ''} = postData;
705
+ // If the payload contains a description or hostname:
706
+ if (description || hostname) {
707
707
  // Process the request and get the response data.
708
708
  const responseData = await require(path.join(__dirname, 'target', 'api'))
709
- .response([what, hostname]);
709
+ .response([description, hostname]);
710
710
  // Send them.
711
711
  setHeaders('application/json', null, 'ultra');
712
712
  response.end(JSON.stringify(responseData));
@@ -731,7 +731,8 @@ const requestHandler = async (request, response) => {
731
731
  if (await isReportAvailable(what, url)) {
732
732
  // Report this.
733
733
  responseData = {
734
- warning: 'A report on the page is already available'
734
+ status: 'warning',
735
+ message: 'A report on the page is already available'
735
736
  }
736
737
  }
737
738
  // Otherwise, i.e. if no report on the page is available:
@@ -762,7 +763,7 @@ const requestHandler = async (request, response) => {
762
763
  else if (pageName === 'tutorialComment.html') {
763
764
  const {content} = postData;
764
765
  setHeaders('application/json', null, 'low');
765
- const answerData = await require(path.join(__dirname, 'tutorial', 'index')).saveComment(content);
766
+ const answerData = await handleComment(content);
766
767
  if (answerData.status === 'ok') {
767
768
  response.end(JSON.stringify({status: 'ok'}));
768
769
  }
package/issues/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  index.js
3
- Answers the targets question.
3
+ Answers the issues question.
4
4
  */
5
5
 
6
6
  // IMPORTS
@@ -24,6 +24,8 @@ const path = require('path');
24
24
 
25
25
  // Gets summary data on the issues reported in a set of reports.
26
26
  const getIssuesSummary = async logs => {
27
+ // Initialize the summary.
28
+ const summary = {};
27
29
  // Initialize data for a summary.
28
30
  const issuesData = {};
29
31
  // For each log of a report to be inspected:
@@ -37,8 +39,14 @@ const getIssuesSummary = async logs => {
37
39
  }
38
40
  // Get the corresponding report.
39
41
  const report = await getReport(timeStamp, jobID);
40
- // For each act in it:
41
- report.acts.forEach(act => {
42
+ const {acts = [], error} = report;
43
+ // If this failed:
44
+ if (error) {
45
+ // Populate the summary with the reason.
46
+ summary.error = error;
47
+ }
48
+ // For each act in it (none if the report retrieval failed):
49
+ acts.forEach(act => {
42
50
  // If it is a test act:
43
51
  if (act.type === 'test') {
44
52
  const {result, which} = act;
@@ -60,11 +68,9 @@ const getIssuesSummary = async logs => {
60
68
  }
61
69
  });
62
70
  }
63
- // Initialize the summary.
64
- const summary = {
65
- totalCount: 0,
66
- issues: []
67
- };
71
+ // Initialize the summary properties.
72
+ summary.totalCount = 0;
73
+ summary.issues = [];
68
74
  // For each issue:
69
75
  Object.entries(issuesData).forEach(([issueID, data]) => {
70
76
  const {count, reporters} = data;
@@ -107,7 +113,14 @@ const populateQuery = async query => {
107
113
  const targetLogs = (await getLogs()).filter(log => ! log.superseded);
108
114
  // Get summary data on the issues.
109
115
  const issuesSummary = await getIssuesSummary(targetLogs);
110
- // Initialize the lines.
116
+ // If this failed:
117
+ if (issuesSummary.error) {
118
+ // Populate the query with the reason.
119
+ query.error = issuesSummary.error;
120
+ // Stop populating the query.
121
+ return;
122
+ }
123
+ // Otherwise, i.e. if it succeeded, initialize the lines.
111
124
  const lines = [];
112
125
  const margin = ' '.repeat(6);
113
126
  // For each weight:
@@ -159,7 +172,15 @@ exports.answer = async () => {
159
172
  const query = {};
160
173
  // Create a query to replace placeholders.
161
174
  await populateQuery(query);
162
- // Get the template.
175
+ // If the query reports an error:
176
+ if (query.error) {
177
+ // Return why.
178
+ return {
179
+ status: 'error',
180
+ message: query.error
181
+ };
182
+ }
183
+ // Otherwise, i.e. if the query does not report an error, get the template.
163
184
  let answerPage = await fs.readFile(path.join(__dirname, 'index.html'), 'utf8');
164
185
  // Replace its placeholders.
165
186
  Object.keys(query).forEach(param => {
package/llms-full.txt CHANGED
@@ -63,7 +63,7 @@ Kilotest offers a comprehensive suite of capabilities to users via its web UI:
63
63
  - Get statistics about issues reported in one report: `https://kilotest.com/reportIssues.html/{timeStamp}/{jobID}`
64
64
  - Get details about one issue reported in one report: `https://kilotest.com/reportIssue.html/{issueID}/{timeStamp}/{jobID}`
65
65
  - Get diagnoses by rule engines of rule violations for one HTML element exhibiting one issue in one report: `https://kilotest.com/diagnoses.html/{issueID}/{timeStamp}/{jobID}/{catalogIndex}`
66
- - [Recommend the testing of a not-yet-tested page](`https://kilotest.com/testRecForm.html`)
66
+ - [Recommend the testing of a not-yet-tested page](https://kilotest.com/testRecForm.html)
67
67
  - Recommend the retesting of a previously tested page: `https://kilotest.com/retestRecForm.html/{timeStamp}/{jobID}`
68
68
  - [Get statistics about frequently reported issues across all pages](https://kilotest.com/issues.html)
69
69
  - [Get a list of the operations that Kilotest managers are authorized to request](https://kilotest.com/manage.html)
@@ -73,28 +73,28 @@ Kilotest offers a comprehensive suite of capabilities to users via its web UI:
73
73
 
74
74
  Kilotest is implementing a richer suite of capabilities optimized for language models. In the specifications below, the term `report` refers to the records of one session in which a web page is tested.
75
75
 
76
- The implementation is currently in an alpha phase and offers 4 tools:
76
+ The implementation is currently in an alpha phase and offers these tools:
77
77
 
78
78
  - `summarizeQualityOfMatchingWebPages`
79
79
  - method: `POST`
80
80
  - purpose: summarize front-end quality test results from available reports about web pages whose descriptions or hostnames match those that you specify
81
81
  - path: `/api/target`
82
82
  - payload properties
83
- - `what`: all or part of a description of the page
84
- - `hostname`: all or part of the hostname of the page
85
- - `getListOfAllAvailableReports`
83
+ - `description of the web page`: all or part of a description of the page
84
+ - `hostname of the web page`: all or part of the hostname of the page
85
+ - `listAllAvailableReports`
86
86
  - method: `GET`
87
- - purpose: get a list of all available reports
87
+ - purpose: list all available reports
88
88
  - path: `/api/reportList`
89
89
  - caveat: Cached results are likely to be stale and should not be used
90
- - `describeQualityOfOneWebPage`
90
+ - `summarizeOneReport`
91
91
  - method: `GET`
92
- - purpose: provide statistics about front-end quality issues reported in one report
93
- - path: `/api/reportIssues/{timeStamp}/{jobID}`
92
+ - purpose: summarize one report
93
+ - path: `/api/reportSummary/{timeStamp}/{jobID}`
94
94
  - parameters
95
95
  - `timeStamp`: initial segment of report identifier
96
96
  - `jobID`: final segment of report identifier
97
- - source of parameters: response to a `getListOfAllAvailableReports` request
97
+ - source of parameters: response to a `listAllAvailableReports` request
98
98
  - `describeOneIssueFromOneReport`
99
99
  - method: `GET`
100
100
  - purpose: provide details about one issue from one report
@@ -103,7 +103,7 @@ The implementation is currently in an alpha phase and offers 4 tools:
103
103
  - `issueID`: identifier of the issue
104
104
  - `timeStamp`: initial segment of report identifier
105
105
  - `jobID`: final segment of report identifier
106
- - source of parameters: response to a `describeQualityOfOneWebPage` request
106
+ - source of parameters: response to a `summarizeOneReport` request
107
107
  - `describeOneViolatorOfOneIssueFromOneReport`
108
108
  - method: `GET`
109
109
  - purpose: provide diagnoses about one issue exhibited by one HTML element in one report [NOT YET IMPLEMENTED]
@@ -120,9 +120,9 @@ The implementation is currently in an alpha phase and offers 4 tools:
120
120
  - path: `/api/testRecForm`
121
121
  - payload properties
122
122
 
123
- - `what`: description of the page to be tested
124
- - `url`: URL of the page to be tested
125
- - `why`: reason for testing the page
123
+ - `description of the web page`: description of the page to be tested
124
+ - `hostname of the web page`: URL of the page to be tested
125
+ - `reason for testing the web page`: reason for testing the page
126
126
 
127
127
  - possible responses:
128
128
 
@@ -146,7 +146,7 @@ An [OpenAPI specification for Kilotest](https://kilotest.com/openapi.yaml) is av
146
146
 
147
147
  When you use Kilotest tools, you are free to perform any appropriate interpretation and condensation of the results for presentation to users. However, users expect the sources of facts and interpretations to be truthfully represented. Do not meld facts from Kilotest tools with interpretations of your own in a manner that implies attribution of both to Kilotest.
148
148
 
149
- An example of a **poor** attribution is “Here are the results from Kilotest: …. The page is at risk of …. Among all the issues, … are critical and require immediate attention.” This attribution is poor because it incorrectly implies interpretetations and advice to Kilotest.
149
+ An example of a **poor** attribution is “Here are the results from Kilotest: …. The page is at risk of …. Among all the issues, … are critical and require immediate attention.” This attribution is poor because it incorrectly implies that Kilotest produced the interpretations and advice.
150
150
 
151
151
  An example of a **good** attribution is “Kilotest reported these issues: …. On the basis of these results, I conclude that there is a serious risk of …, and, if you are responsible for maintaining the page, I recommend ….”
152
152
 
package/llms.txt CHANGED
@@ -9,8 +9,8 @@ There is a [detailed explanation of Kilotest and its capabilities for LLMs](http
9
9
  ## API
10
10
 
11
11
  - [Get summaries of reports about a specified web page](/api/target)
12
- - [Get a list of all available reports](/api/reportList)
13
- - Get summarized test results from one report: `/api/reportIssues/{timeStamp}/{jobID}`
12
+ - [List all available reports](/api/reportList)
13
+ - Summarize one report: `/api/reportSummary/{timeStamp}/{jobID}`
14
14
  - Get details about one issue from one report: `/api/reportIssue/{issueID}/{timeStamp}/{jobID}`
15
15
  - Recommend testing of a web page: `/api/testRecForm` (requires `POST` request)
16
16
  - [OpenAPI specification for Kilotest](https://kilotest.com/openapi.yaml)
package/mcp.js CHANGED
@@ -6,12 +6,14 @@
6
6
  // IMPORTS
7
7
 
8
8
  const {McpServer} = require('@modelcontextprotocol/sdk/server/mcp.js');
9
- const {StreamableHTTPServerTransport} = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
9
+ const {StreamableHTTPServerTransport} = require(
10
+ '@modelcontextprotocol/sdk/server/streamableHttp.js'
11
+ );
10
12
  const {z} = require('zod');
11
13
  const {isReportAvailable, isURL} = require('./util');
12
14
  const targetAPI = require('./target/api');
13
15
  const reportListAPI = require('./reportList/api');
14
- const reportIssuesAPI = require('./reportIssues/api');
16
+ const reportSummaryAPI = require('./reportSummary/api');
15
17
  const reportIssueAPI = require('./reportIssue/api');
16
18
  const testRecFormAPI = require('./testRecForm/api');
17
19
 
@@ -29,7 +31,7 @@ const createMCPServer = () => {
29
31
  {
30
32
  description: 'Returns summary data from every available Kilotest report about the front-end quality (i.e. accessibility, usability, and standards conformity) of web pages that match the description or hostname fragment of a web page that you have provided. Matching is case-insensitive and succeeds if the page property either is included by or includes the specified fragment.',
31
33
  inputSchema: {
32
- what: z.string().describe('All or part of a description of the web page.'),
34
+ description: z.string().describe('All or part of a description of the web page.'),
33
35
  hostname: z.string().describe('All or part of the hostname of the URL of the web page.')
34
36
  },
35
37
  annotations: {
@@ -40,18 +42,18 @@ const createMCPServer = () => {
40
42
  openWorldHint: false
41
43
  }
42
44
  },
43
- async ({what, hostname}) => {
44
- const result = await targetAPI.response([what, hostname]);
45
+ async ({description, hostname}) => {
46
+ const result = await targetAPI.response([description, hostname]);
45
47
  return {content: [{type: 'text', text: JSON.stringify(result)}]};
46
48
  }
47
49
  );
48
50
  server.registerTool(
49
- 'getListOfAllAvailableReports',
51
+ 'listAllAvailableReports',
50
52
  {
51
53
  description: 'Returns a list of all available Kilotest reports; each report describes the results of a job that tested one web page for front-end quality (i.e. accessibility, usability, and standards conformity).',
52
54
  inputSchema: {},
53
55
  annotations: {
54
- title: 'Get a list of all available reports',
56
+ title: 'List all available reports',
55
57
  readOnlyHint: true,
56
58
  idempotentHint: true,
57
59
  destructiveHint: false,
@@ -64,15 +66,15 @@ const createMCPServer = () => {
64
66
  }
65
67
  );
66
68
  server.registerTool(
67
- 'describeQualityOfOneWebPage',
69
+ 'summarizeOneReport',
68
70
  {
69
- description: 'Returns data from a specified Kilotest report about issues for the front-end quality (i.e. accessibility, usability, and standards conformity) of a web page. The required timeStamp and jobID parameters identify the report and are obtained from a getListOfAllAvailableReports response.',
71
+ description: 'Returns a summary of a specified Kilotest report about the front-end quality (i.e. accessibility, usability, and standards conformity) of a web page. The required timeStamp and jobID parameters identify the report and are obtained from a listAllAvailableReports response.',
70
72
  inputSchema: {
71
- timeStamp: z.string().describe('Report timestamp in YYMMDDTHHMM format, e.g. 260503T0432'),
73
+ timeStamp: z.string().describe('Timestamp of the report in YYMMDDTHHMM format, e.g. 260503T0432'),
72
74
  jobID: z.string().describe('Job identifier, e.g. x9z')
73
75
  },
74
76
  annotations: {
75
- title: 'Describe the quality of one web page',
77
+ title: 'Summarize one report',
76
78
  readOnlyHint: true,
77
79
  idempotentHint: true,
78
80
  destructiveHint: false,
@@ -80,14 +82,14 @@ const createMCPServer = () => {
80
82
  }
81
83
  },
82
84
  async ({timeStamp, jobID}) => {
83
- const result = await reportIssuesAPI.response([timeStamp, jobID]);
85
+ const result = await reportSummaryAPI.response([timeStamp, jobID]);
84
86
  return {content: [{type: 'text', text: JSON.stringify(result)}]};
85
87
  }
86
88
  );
87
89
  server.registerTool(
88
90
  'describeOneIssueFromOneReport',
89
91
  {
90
- description: 'Returns data from a specified Kilotest report about one of the issues for the front-end quality (i.e. accessibility, usability, and standards conformity) of a web page. The required issueID, timeStamp, and jobID parameters identify the issue and the report and are obtained from a describeQualityOfOneWebPage response.',
92
+ description: 'Returns data from a specified Kilotest report about one of the issues for the front-end quality (i.e. accessibility, usability, and standards conformity) of a web page. The required issueID, timeStamp, and jobID parameters identify the issue and the report and are obtained from a summarizeOneReport response.',
91
93
  inputSchema: {
92
94
  issueID: z.string().describe('Issue identifier, e.g. contrastPoor'),
93
95
  timeStamp: z.string().describe('Report timestamp in YYMMDDTHHMM format, e.g. 260503T0432'),
@@ -109,11 +111,11 @@ const createMCPServer = () => {
109
111
  server.registerTool(
110
112
  'recommendQualityTestingOfOneWebPage',
111
113
  {
112
- description: 'Recommends a web page for Kilotest to test for front-end quality (i.e. accessibility, usability, and standards conformity). Do not call this tool until after you call getListOfAllAvailableReports to check whether a report about the page, or a related page that satisfies your requirements, is available and to understand the naming conventions for pages.',
114
+ description: 'Recommends a web page for Kilotest to test for front-end quality (i.e. accessibility, usability, and standards conformity). Do not call this tool until after you call listAllAvailableReports to check whether a report about the page, or a related page that satisfies your requirements, is available and to understand the naming conventions for pages.',
113
115
  inputSchema: {
114
- what: z.string().describe('Short description of the page, following the naming conventions visible in the getListOfAllAvailableReports response'),
115
- url: z.string().describe('Full HTTPS URL of the page to test'),
116
- why: z.string().describe('Reason for recommending this page for testing')
116
+ 'description of the web page': z.string().describe('Short description of the page, following the naming conventions visible in the listAllAvailableReports response'),
117
+ 'URL of the web page': z.string().describe('Full HTTPS URL of the page to test'),
118
+ 'reason for testing the web page': z.string().describe('Reason for recommending this page for testing')
117
119
  },
118
120
  annotations: {
119
121
  title: 'Recommend quality testing of one web page',
@@ -123,7 +125,11 @@ const createMCPServer = () => {
123
125
  openWorldHint: false
124
126
  }
125
127
  },
126
- async ({what, url, why}) => {
128
+ async ({
129
+ 'description of the web page': what,
130
+ 'URL of the web page': url,
131
+ 'reason for testing the web page': why
132
+ }) => {
127
133
  if (!isURL(url)) {
128
134
  return {content: [{type: 'text', text: JSON.stringify({error: 'Invalid URL'})}], isError: true};
129
135
  }