@jrpool/kilotest 63.1.7 → 63.2.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/content.ts +12 -0
- package/docs/AI-TOOLS.md +20 -7
- package/docs/decisions/010-content-generation.md +32 -0
- package/lhm.plugin.json +70 -27
- package/llms.txt +1 -1
- package/mcp.ts +2 -7
- package/openapi-3.0.yaml +2 -2
- package/openapi.yaml +2 -2
- package/package.json +5 -4
- package/scripts/generate-lhm.ts +118 -0
- package/scripts/generate-openapi.ts +2 -1
- package/server.json +1 -1
package/content.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
content.ts
|
|
3
|
+
Canonical prose describing what Kilotest is and does, reused verbatim by mcp.ts, scripts/generate-openapi.ts, and scripts/generate-lhm.ts, so the sentences are written once instead of being hand-copied into mcp.ts, lhm.plugin.json, llms.txt, and openapi.yaml.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// CONSTANTS
|
|
7
|
+
|
|
8
|
+
// Brief description of the Kilotest tool collection. Used as the MCP server description and as lhm.plugin.json’s top-level description. No length limit.
|
|
9
|
+
export const collectionDescription = 'Tools that test web pages with an ensemble of 12 rule engines for front-end quality (accessibility, usability, and W3C web standards, including HTML, CSS, ARIA, and WCAG)';
|
|
10
|
+
|
|
11
|
+
// Description of the Kilotest domain and data hierarchy. Used as (1) the server instructions, (2) the opening paragraph of llms.txt, (3) the start of the openapi.yaml info.description, and (4) the getKilotestOverview tool description (but not part of the other tool descriptions, to avoid diluting how models read each tool), because some MCP clients do not propagate server-level instructions or the server description to their models.
|
|
12
|
+
export const sharedContext = 'Kilotest integrates an ensemble of 12 rule engines, with about 1,300 rules, to test web pages for front-end quality (accessibility, usability, and W3C web standards, including HTML, CSS, ARIA, and WCAG). Each test job generates a standardized report describing which elements violated which rules and grouping the violations into about 380 issues. Kilotest can list the tested pages, the reports about each page, the issues in each report, the violators of each issue, and the diagnoses of each violation. Kilotest also accepts requests to test pages not yet tested and to retest already tested pages.';
|
package/docs/AI-TOOLS.md
CHANGED
|
@@ -40,6 +40,8 @@ The internal features that make Kilotest a collection of tools for language mode
|
|
|
40
40
|
|
|
41
41
|
The external features that support the use of Kilotest as a collection of AI tools are:
|
|
42
42
|
|
|
43
|
+
### Registrations
|
|
44
|
+
|
|
43
45
|
- A [pull request](https://github.com/public-apis/public-apis/pull/6346/changes) to add Kilotest to the list of public APIs in the `public-apis` repository.
|
|
44
46
|
- An [issue](https://github.com/APIs-guru/openapi-directory/issues/2677) to add Kilotest to `openapi-directory`.
|
|
45
47
|
- A [pull request](https://github.com/w3c/wai-evaluation-tools-list/pull/1153) to add Kilotest to the WAI evaluation tools list.
|
|
@@ -51,17 +53,27 @@ The external features that support the use of Kilotest as a collection of AI too
|
|
|
51
53
|
- Registration of Kilotest with the [RapidAPI](https://rapidapi.com/jrpool/api/kilotest/playground/apiendpoint_0f03577a-ff9a-472a-a0ed-533bd198981a) Hub.
|
|
52
54
|
- Registration of Kilotest with the [LobeHub MCP Market](https://lobehub.com/mcp) (identifier: `jrpool-kilotest`), enabled with an `lhm.plugin.json` file.
|
|
53
55
|
- Registration of Kilotest with [MCPTry](https://mcptry.com/servers/io.github.jrpool/kilotest).
|
|
56
|
+
- Registration of the MCP server as a community connector in the [Claude directory of plugins and connectors](https://claude.ai/directory/kilotest).
|
|
57
|
+
- Registration of Kilotest and its sitemap at [Google Search Console](https://search.google.com/search-console?resource_id=sc-domain:kilotest.com) and [Bing Webmaster Tools](https://www.bing.com/webmasters/home?siteUrl=https://kilotest.com/).
|
|
58
|
+
- Submission of the MCP server to `mcp.directory`.
|
|
59
|
+
- Submission of the MCP server to [MCP.so](https://mcp.so/). The submission is in review.
|
|
60
|
+
|
|
61
|
+
### Deployment
|
|
62
|
+
|
|
54
63
|
- Deployment of an MCP server in HTTP mode on the Kilotest service host.
|
|
55
|
-
|
|
64
|
+
|
|
65
|
+
### Local configuration
|
|
66
|
+
|
|
56
67
|
- Configuration of Claude Desktop on the local development host and the `claude.ai` web application to connect Claude Desktop models to the Kilotest MCP server. The configuration was performed in the UI of each platform with the addition of Kilotest as a _connector_. The user used the `Customize/Connectors/Add connector/Add custom connector` interface, providing these data before activating the `Add` button:
|
|
57
68
|
|
|
58
69
|
- Name: Kilotest
|
|
59
70
|
- Remote MCP server URL: `https://kilotest.com/mcp`
|
|
60
71
|
|
|
72
|
+
### Documentation
|
|
73
|
+
|
|
61
74
|
- MCP-related topics added to the [repository details](https://github.com/jrpool/kilotest).
|
|
62
|
-
-
|
|
75
|
+
- Registry badges added to the `README.md` file.
|
|
63
76
|
- A link to the MCP server on the `README.md` page of the [`testaro-issues`](https://github.com/jrpool/testaro-issues) repository.
|
|
64
|
-
- Registration of Kilotest and its sitemap at [Google Search Console](https://search.google.com/search-console?resource_id=sc-domain:kilotest.com) and [Bing Webmaster Tools](https://www.bing.com/webmasters/home?siteUrl=https://kilotest.com/).
|
|
65
77
|
|
|
66
78
|
## Required maintenance
|
|
67
79
|
|
|
@@ -69,7 +81,7 @@ The maintenance routine is prescribed in the [README.md](../README.md) file.
|
|
|
69
81
|
|
|
70
82
|
Releasing a new version is a two-script procedure:
|
|
71
83
|
|
|
72
|
-
- On a feature branch, commit all substantive changes, then execute `npm run prePR -- <level>` (`major`, `minor`, or `patch`). The script bumps `package.json` and `package-lock.json`, regenerates `openapi.yaml`, `openapi-3.0.yaml`, and the versions of `server.json` and `lhm.plugin.json` by explicitly running `npm run generate`, commits and pushes the result, and records the branch name for the next step.
|
|
84
|
+
- On a feature branch, commit all substantive changes, then execute `npm run prePR -- <level>` (`major`, `minor`, or `patch`). The script bumps `package.json` and `package-lock.json`, regenerates `openapi.yaml`, `openapi-3.0.yaml`, and the descriptions, schemas, and versions of `server.json` and `lhm.plugin.json` by explicitly running `npm run generate`, commits and pushes the result, and records the branch name for the next step.
|
|
73
85
|
- Submit a pull request and merge it to `main` (triggers `publish-mcp.yml`, which publishes `server.json` to the MCP Registry).
|
|
74
86
|
- Execute `npm run postPR`. The script checks out and pulls `main`, verifies that the merge landed, publishes to npm, updates the LobeHub listing via `npm run publish:lhm`, deletes the feature branch, and prunes remote-tracking branches. If the LobeHub update fails, retry it alone with `npm run publish:lhm`.
|
|
75
87
|
|
|
@@ -84,8 +96,9 @@ A change to the tool or API contract (tool names, parameter names, schemas, resp
|
|
|
84
96
|
- `openapi.yaml`: regenerated automatically by the `version` hook during `npm run prePR`; never edit it by hand.
|
|
85
97
|
- `openapi-3.0.yaml`: an OpenAPI 3.0.3 conversion of `openapi.yaml`, regenerated by the same hook, for registries whose importers reject 3.1 documents; never edit it by hand.
|
|
86
98
|
- `server.json`: contains only the server descriptor (name, description, repository, remotes, version), no tool schemas. Its version is synced by `update-versions.ts`; edit it manually only if the server description or remote URL changes.
|
|
87
|
-
- `lhm.plugin.json`: embeds every tool’s input and output schema and its descriptions, in the LobeHub manifest format.
|
|
88
|
-
- `
|
|
99
|
+
- `lhm.plugin.json`: embeds every tool’s input and output schema and its descriptions, in the LobeHub manifest format. Its `description` and every tool’s `description`, `inputSchema`, `outputSchema`, and `annotations` are regenerated from `mcp.ts`/`api/schemas.ts` by `scripts/generate-lhm.ts` (run by `npm run generate`, part of `npm run prePR`); never edit those fields by hand. The remaining fields (`author`, `cloudEndpoint`, `homepage`, `identifier`, `name`, `prompts`, `resources`, `tags`, `version`, and each tool’s `execution` block) have no canonical source elsewhere and are still edited manually. See `docs/decisions/010-content-generation.md`.
|
|
100
|
+
- `content.ts`: canonical prose shared verbatim by `mcp.ts`, `scripts/generate-openapi.ts`, and `scripts/generate-lhm.ts`. Also hand-check `llms.txt`’s opening paragraph, which is supposed to match `content.ts`’s `sharedContext` verbatim but is not generated.
|
|
101
|
+
- `llms-full.txt`, `capability.md`, `README.md`, `docs/openai-plugin/skills/kilotest-overview/SKILL.md`: hand-maintained, intentionally paraphrased rather than copied verbatim; search them for stale parameter and path names after a contract change.
|
|
89
102
|
|
|
90
103
|
#### External registries that update themselves, needing no maintainer action
|
|
91
104
|
|
|
@@ -123,7 +136,7 @@ To-dos recommended by Claude Sonnet 5 Medium on Devin:
|
|
|
123
136
|
|
|
124
137
|
- **No content/backlink outreach.** Registrations are passive listings; there's no blog post, Show HN, or subreddit post (r/modelcontextprotocol, r/accessibility, r/webdev) building external backlinks and social-discovery signal, which the doc's own "difficult problem…lack of standardization" framing suggests is exactly the gap left for the **human**-driven parts of discoverability.
|
|
125
138
|
|
|
126
|
-
- **Other directories not yet targeted.** The
|
|
139
|
+
- **Other directories not yet targeted.** The Docker MCP Catalog is not a fit because it requires stdio transport and a Dockerfile, and Kilotest is streamable-http.
|
|
127
140
|
|
|
128
141
|
- **Client-onboarding friction isn't documented.** Some MCP clients need the `mcp-remote` npx bridge to use a remote streamable-HTTP server like Kilotest's. Ready-made Claude Desktop/Cursor/Windsurf config instructions added to the AI tutorial would convert "discovered" into "used" faster, since case 3 of your use cases assumes a technically savvy discoverer but not all users are.
|
|
129
142
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
date: 2026-11-04
|
|
3
|
+
status: accepted
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Generating duplicated prose and schemas instead of hand-copying them
|
|
7
|
+
|
|
8
|
+
## Context and problem
|
|
9
|
+
|
|
10
|
+
`mcp.ts`, `lhm.plugin.json`, `llms.txt`, and `scripts/generate-openapi.ts` each carried their own hand-typed copy of the same sentence describing what Kilotest is (“Kilotest integrates an ensemble of 12 rule engines…”). The four copies had already drifted: `llms.txt` had an accidentally duplicated clause and said “test job” where `lhm.plugin.json` said “test session”, and `generate-openapi.ts`’s `info.description` was a fourth, independently worded variant. Separately, `lhm.plugin.json` hand-transcribes every MCP tool’s `inputSchema`, `outputSchema`, `description`, and `annotations` from `mcp.ts`/`api/schemas.ts`, a step `docs/AI-TOOLS.md` already flagged as “hand-maintained, not generated…transform it…whenever the contract changes.” Regenerating `lhm.plugin.json` from the live registrations in `mcp.ts` found that this hand-transcription had already gone stale: several tool descriptions no longer matched `mcp.ts`, and `listDiagnoses`’s output schema was missing two fields (`identifier of the rule engine` and `how to report that you disagree with this diagnosis`) that the real tool returns.
|
|
11
|
+
|
|
12
|
+
## Considered options
|
|
13
|
+
|
|
14
|
+
- Status quo: keep hand-copying prose and schemas into each surface, relying on the `docs/AI-TOOLS.md` “Contract changes” checklist to remind a maintainer to update every copy.
|
|
15
|
+
- Add a lint/test check that only flags byte-for-byte differences between the copies, without removing the duplication itself.
|
|
16
|
+
- Generate the duplicated surfaces from canonical sources, extending the pattern the project already uses for `package.json`’s `version` (copied into `server.json`/`lhm.plugin.json` by `scripts/update-versions.ts`) and for `api/schemas.ts`/`api/routes.ts` (rendered into `openapi.yaml`/`openapi-3.0.yaml` by `scripts/generate-openapi.ts`).
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
Generate, rather than hand-copy, wherever the same sentence or schema is supposed to appear verbatim in more than one file, extending the project's existing generate-from-canonical-source pattern:
|
|
21
|
+
|
|
22
|
+
- `content.ts` is the new single source of truth for prose that must be identical across surfaces (`collectionDescription`, the one-line tool-collection description; `sharedContext`, the domain/data-hierarchy paragraph). `mcp.ts` and `scripts/generate-openapi.ts` import it instead of defining their own copies.
|
|
23
|
+
- `scripts/generate-lhm.ts` regenerates `lhm.plugin.json`’s top-level `description` and every tool’s `description`, `inputSchema`, `outputSchema`, and `annotations` from `content.ts` and the same tool registrations `mcp.ts` creates at runtime (via `createMCPServer()`) and the same Zod schemas `api/schemas.ts` exports, using `z.toJSONSchema`. Fields with no canonical source elsewhere in the repository (`author`, `cloudEndpoint`, `homepage`, `identifier`, `name`, `prompts`, `resources`, `tags`, `version`, and each tool’s `execution` block) are left untouched by the generator.
|
|
24
|
+
- `scripts/generate-lhm.ts` is wired into `npm run generate`, alongside `generate-openapi.ts` and `update-versions.ts`, so `prePR.ts` regenerates it before every release the same way it already regenerates `openapi.yaml`.
|
|
25
|
+
- `llms.txt`’s opening paragraph is hand-corrected to match `content.ts`’s `sharedContext` verbatim; it is not generated, because the rest of the file (and `llms-full.txt`, `capability.md`, `README.md`, and the OpenAI-plugin `SKILL.md`) intentionally paraphrase for their audience rather than repeating the same sentence, so forcing byte-identical text there would reduce documentation quality for no benefit. These remain on the existing hand-maintained checklist in `docs/AI-TOOLS.md`.
|
|
26
|
+
- Publishing remains separate from generation, as it already was for `openapi.yaml`/`server.json`: `npm run generate` produces correct file content locally, and `npm run publish:lhm` still performs the manual, OAuth-gated push to LobeHub.
|
|
27
|
+
|
|
28
|
+
The byte-for-byte lint-check option was rejected as a primary strategy because it would still require a maintainer to hand-edit three or four files in sync on every contract change; it only catches drift after the fact rather than removing the duplication that causes it.
|
|
29
|
+
|
|
30
|
+
### Confirmation
|
|
31
|
+
|
|
32
|
+
To confirm the correctness of the implementation, run `npm run generate`, verify `lhm.plugin.json`, `openapi.yaml`, and `openapi-3.0.yaml` are unchanged (a clean `git diff`), and run `npm test` and `npm run lint` to confirm nothing else regressed.
|
package/lhm.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "Jonathan Robert Pool",
|
|
3
3
|
"cloudEndpoint": "https://kilotest.com/mcp",
|
|
4
|
-
"description": "Tools that test web pages for front-end quality (accessibility, usability, and web standards, including HTML, CSS, ARIA, and WCAG)
|
|
4
|
+
"description": "Tools that test web pages with an ensemble of 12 rule engines for front-end quality (accessibility, usability, and W3C web standards, including HTML, CSS, ARIA, and WCAG)",
|
|
5
5
|
"homepage": "https://github.com/jrpool/kilotest",
|
|
6
6
|
"identifier": "jrpool-kilotest",
|
|
7
7
|
"name": "Kilotest",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"tools": [
|
|
22
22
|
{
|
|
23
23
|
"name": "getKilotestOverview",
|
|
24
|
-
"description": "Explains what Kilotest is, what its tools do, and how its results are structured. Use this before any other tool when the available tools or workflow are unclear. Returns a prose overview of the Kilotest domain, the tool workflow, and links to further documentation. Kilotest integrates an ensemble of 12 rule engines, with about 1,300 rules, to test web pages for front-end quality (accessibility, usability, and web standards, including HTML, CSS, ARIA, and WCAG). Each test
|
|
24
|
+
"description": "Explains what Kilotest is, what its tools do, and how its results are structured. Use this before any other tool when the available tools or workflow are unclear. Returns a prose overview of the Kilotest domain, the tool workflow, and links to further documentation. Kilotest integrates an ensemble of 12 rule engines, with about 1,300 rules, to test web pages for front-end quality (accessibility, usability, and W3C web standards, including HTML, CSS, ARIA, and WCAG). Each test job generates a standardized report describing which elements violated which rules and grouping the violations into about 380 issues. Kilotest can list the tested pages, the reports about each page, the issues in each report, the violators of each issue, and the diagnoses of each violation. Kilotest also accepts requests to test pages not yet tested and to retest already tested pages.",
|
|
25
25
|
"inputSchema": {
|
|
26
26
|
"type": "object",
|
|
27
27
|
"properties": {},
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"annotations": {
|
|
31
31
|
"title": "Explain Kilotest and its tools",
|
|
32
32
|
"readOnlyHint": true,
|
|
33
|
-
"destructiveHint": false,
|
|
34
33
|
"idempotentHint": true,
|
|
34
|
+
"destructiveHint": false,
|
|
35
35
|
"openWorldHint": false
|
|
36
36
|
},
|
|
37
37
|
"execution": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
"name": "listPages",
|
|
43
|
-
"description": "Lists all pages Kilotest has tested. Use this first to discover whether a page already has
|
|
43
|
+
"description": "Lists all pages Kilotest has tested. Use this first to discover whether a page already has any reports before requesting a test. Returns, for each tested page, its description, URL, and the identifier of its latest available report.",
|
|
44
44
|
"inputSchema": {
|
|
45
45
|
"type": "object",
|
|
46
46
|
"properties": {},
|
|
@@ -382,8 +382,8 @@
|
|
|
382
382
|
"annotations": {
|
|
383
383
|
"title": "List tested pages",
|
|
384
384
|
"readOnlyHint": true,
|
|
385
|
-
"destructiveHint": false,
|
|
386
385
|
"idempotentHint": true,
|
|
386
|
+
"destructiveHint": false,
|
|
387
387
|
"openWorldHint": false
|
|
388
388
|
},
|
|
389
389
|
"execution": {
|
|
@@ -392,7 +392,7 @@
|
|
|
392
392
|
},
|
|
393
393
|
{
|
|
394
394
|
"name": "listIssues",
|
|
395
|
-
"description": "Lists the issues reported in one report, along with the history of all reports about the same page. Use this after listPages to
|
|
395
|
+
"description": "Lists the issues reported in one report, along with the history of all reports about the same page. Use this after listPages to switch to an earlier or later report or to begin examining this report. Returns the history and the basics about this report’s issues.",
|
|
396
396
|
"inputSchema": {
|
|
397
397
|
"type": "object",
|
|
398
398
|
"properties": {
|
|
@@ -1124,8 +1124,8 @@
|
|
|
1124
1124
|
"annotations": {
|
|
1125
1125
|
"title": "List issues in a report",
|
|
1126
1126
|
"readOnlyHint": true,
|
|
1127
|
-
"destructiveHint": false,
|
|
1128
1127
|
"idempotentHint": true,
|
|
1128
|
+
"destructiveHint": false,
|
|
1129
1129
|
"openWorldHint": false
|
|
1130
1130
|
},
|
|
1131
1131
|
"execution": {
|
|
@@ -1134,7 +1134,7 @@
|
|
|
1134
1134
|
},
|
|
1135
1135
|
{
|
|
1136
1136
|
"name": "listViolators",
|
|
1137
|
-
"description": "Lists the elements of a tested page that were reported as exhibiting one issue in one report. Use this after listIssues to drill from an issue
|
|
1137
|
+
"description": "Lists the elements of a tested page that were reported as exhibiting one issue in one report. Use this after listIssues to drill down from an issue to the elements violating rules belonging to that issue. Returns basics about each violating element.",
|
|
1138
1138
|
"inputSchema": {
|
|
1139
1139
|
"type": "object",
|
|
1140
1140
|
"properties": {
|
|
@@ -1633,8 +1633,8 @@
|
|
|
1633
1633
|
"annotations": {
|
|
1634
1634
|
"title": "List elements exhibiting an issue",
|
|
1635
1635
|
"readOnlyHint": true,
|
|
1636
|
-
"destructiveHint": false,
|
|
1637
1636
|
"idempotentHint": true,
|
|
1637
|
+
"destructiveHint": false,
|
|
1638
1638
|
"openWorldHint": false
|
|
1639
1639
|
},
|
|
1640
1640
|
"execution": {
|
|
@@ -1643,7 +1643,7 @@
|
|
|
1643
1643
|
},
|
|
1644
1644
|
{
|
|
1645
1645
|
"name": "listDiagnoses",
|
|
1646
|
-
"description": "Lists the diagnoses that rule engines gave for how one element exhibited one issue in one report. Use this after listViolators to
|
|
1646
|
+
"description": "Lists the diagnoses that rule engines gave for how one element exhibited one issue in one report. Use this after listViolators to get details about the element as a violator of the issue. Returns, for each rule engine that reported a violation by the element of a rule belonging to the issue, the diagnosis given by the rule engine.",
|
|
1647
1647
|
"inputSchema": {
|
|
1648
1648
|
"type": "object",
|
|
1649
1649
|
"properties": {
|
|
@@ -1802,6 +1802,9 @@
|
|
|
1802
1802
|
"items": {
|
|
1803
1803
|
"type": "object",
|
|
1804
1804
|
"properties": {
|
|
1805
|
+
"identifier of the rule engine": {
|
|
1806
|
+
"description": "Copied from the report act (which); type not guaranteed."
|
|
1807
|
+
},
|
|
1805
1808
|
"identifier of the violated rule": {
|
|
1806
1809
|
"description": "Copied from the report instance (ruleID); type not guaranteed."
|
|
1807
1810
|
},
|
|
@@ -1813,13 +1816,53 @@
|
|
|
1813
1816
|
},
|
|
1814
1817
|
"count of violations of the rule by the element": {
|
|
1815
1818
|
"type": "number"
|
|
1819
|
+
},
|
|
1820
|
+
"how to report that you disagree with this diagnosis": {
|
|
1821
|
+
"type": "object",
|
|
1822
|
+
"properties": {
|
|
1823
|
+
"method": {
|
|
1824
|
+
"type": "string",
|
|
1825
|
+
"const": "POST"
|
|
1826
|
+
},
|
|
1827
|
+
"URL": {
|
|
1828
|
+
"type": "string"
|
|
1829
|
+
},
|
|
1830
|
+
"request body": {
|
|
1831
|
+
"type": "object",
|
|
1832
|
+
"properties": {
|
|
1833
|
+
"engineID": {
|
|
1834
|
+
"type": "string"
|
|
1835
|
+
},
|
|
1836
|
+
"ruleID": {
|
|
1837
|
+
"type": "string"
|
|
1838
|
+
},
|
|
1839
|
+
"reason": {
|
|
1840
|
+
"type": "string"
|
|
1841
|
+
}
|
|
1842
|
+
},
|
|
1843
|
+
"required": [
|
|
1844
|
+
"engineID",
|
|
1845
|
+
"ruleID",
|
|
1846
|
+
"reason"
|
|
1847
|
+
],
|
|
1848
|
+
"additionalProperties": false
|
|
1849
|
+
}
|
|
1850
|
+
},
|
|
1851
|
+
"required": [
|
|
1852
|
+
"method",
|
|
1853
|
+
"URL",
|
|
1854
|
+
"request body"
|
|
1855
|
+
],
|
|
1856
|
+
"additionalProperties": false
|
|
1816
1857
|
}
|
|
1817
1858
|
},
|
|
1818
1859
|
"required": [
|
|
1860
|
+
"identifier of the rule engine",
|
|
1819
1861
|
"identifier of the violated rule",
|
|
1820
1862
|
"description of the violation",
|
|
1821
1863
|
"severity of the violation on a 0-to-3 scale",
|
|
1822
|
-
"count of violations of the rule by the element"
|
|
1864
|
+
"count of violations of the rule by the element",
|
|
1865
|
+
"how to report that you disagree with this diagnosis"
|
|
1823
1866
|
],
|
|
1824
1867
|
"additionalProperties": false
|
|
1825
1868
|
}
|
|
@@ -2160,8 +2203,8 @@
|
|
|
2160
2203
|
"annotations": {
|
|
2161
2204
|
"title": "List diagnoses of a violation",
|
|
2162
2205
|
"readOnlyHint": true,
|
|
2163
|
-
"destructiveHint": false,
|
|
2164
2206
|
"idempotentHint": true,
|
|
2207
|
+
"destructiveHint": false,
|
|
2165
2208
|
"openWorldHint": false
|
|
2166
2209
|
},
|
|
2167
2210
|
"execution": {
|
|
@@ -2170,7 +2213,7 @@
|
|
|
2170
2213
|
},
|
|
2171
2214
|
{
|
|
2172
2215
|
"name": "getReport",
|
|
2173
|
-
"description": "Retrieves one entire report as JSON, with all of its issues, violators, and diagnoses. Use this instead of drilling down through listIssues, listViolators, and listDiagnoses when the full report is needed in one call. Returns the complete report data for the specified
|
|
2216
|
+
"description": "Retrieves one entire report as JSON, with all of its issues, violators, and diagnoses. Use this instead of drilling down through listIssues, listViolators, and listDiagnoses when the full report is needed in one call. Returns the complete report data for the specified report identifier. The size of the report is disclosed by the listIssues tool.",
|
|
2174
2217
|
"inputSchema": {
|
|
2175
2218
|
"type": "object",
|
|
2176
2219
|
"properties": {
|
|
@@ -2426,8 +2469,8 @@
|
|
|
2426
2469
|
"annotations": {
|
|
2427
2470
|
"title": "Get a full report",
|
|
2428
2471
|
"readOnlyHint": true,
|
|
2429
|
-
"destructiveHint": false,
|
|
2430
2472
|
"idempotentHint": true,
|
|
2473
|
+
"destructiveHint": false,
|
|
2431
2474
|
"openWorldHint": false
|
|
2432
2475
|
},
|
|
2433
2476
|
"execution": {
|
|
@@ -2436,7 +2479,7 @@
|
|
|
2436
2479
|
},
|
|
2437
2480
|
{
|
|
2438
2481
|
"name": "requestNewTest",
|
|
2439
|
-
"description": "Submits a request to test a page that has no report yet; queued for
|
|
2482
|
+
"description": "Submits a request to test a page that has no report yet; queued for manager approval. Use orderNewTest instead for immediate, automatically approved testing. Returns a request confirmation with disposition details.",
|
|
2440
2483
|
"inputSchema": {
|
|
2441
2484
|
"type": "object",
|
|
2442
2485
|
"properties": {
|
|
@@ -2793,8 +2836,8 @@
|
|
|
2793
2836
|
"annotations": {
|
|
2794
2837
|
"title": "Request testing of a new page",
|
|
2795
2838
|
"readOnlyHint": false,
|
|
2796
|
-
"destructiveHint": false,
|
|
2797
2839
|
"idempotentHint": false,
|
|
2840
|
+
"destructiveHint": false,
|
|
2798
2841
|
"openWorldHint": true
|
|
2799
2842
|
},
|
|
2800
2843
|
"execution": {
|
|
@@ -2803,7 +2846,7 @@
|
|
|
2803
2846
|
},
|
|
2804
2847
|
{
|
|
2805
2848
|
"name": "requestRetest",
|
|
2806
|
-
"description": "Submits a request to retest a page whose existing report is obsolete; queued for
|
|
2849
|
+
"description": "Submits a request to retest a page whose existing report is considered obsolete; queued for manager approval. Use orderRetest instead for immediate, automatically approved retesting. Returns a request confirmation with disposition details.",
|
|
2807
2850
|
"inputSchema": {
|
|
2808
2851
|
"type": "object",
|
|
2809
2852
|
"properties": {
|
|
@@ -3145,8 +3188,8 @@
|
|
|
3145
3188
|
"annotations": {
|
|
3146
3189
|
"title": "Request a retest",
|
|
3147
3190
|
"readOnlyHint": false,
|
|
3148
|
-
"destructiveHint": false,
|
|
3149
3191
|
"idempotentHint": false,
|
|
3192
|
+
"destructiveHint": false,
|
|
3150
3193
|
"openWorldHint": true
|
|
3151
3194
|
},
|
|
3152
3195
|
"execution": {
|
|
@@ -3155,7 +3198,7 @@
|
|
|
3155
3198
|
},
|
|
3156
3199
|
{
|
|
3157
3200
|
"name": "requestFeature",
|
|
3158
|
-
"description": "Submits a request to add a new feature or improve an existing one; queued for
|
|
3201
|
+
"description": "Submits a request to add a new feature or improve an existing one; queued for manager review. There is no immediate-approval alternative for feature requests. Returns a request confirmation with disposition details.",
|
|
3159
3202
|
"inputSchema": {
|
|
3160
3203
|
"type": "object",
|
|
3161
3204
|
"properties": {
|
|
@@ -3427,8 +3470,8 @@
|
|
|
3427
3470
|
"annotations": {
|
|
3428
3471
|
"title": "Request a feature",
|
|
3429
3472
|
"readOnlyHint": false,
|
|
3430
|
-
"destructiveHint": false,
|
|
3431
3473
|
"idempotentHint": false,
|
|
3474
|
+
"destructiveHint": false,
|
|
3432
3475
|
"openWorldHint": false
|
|
3433
3476
|
},
|
|
3434
3477
|
"execution": {
|
|
@@ -3765,8 +3808,8 @@
|
|
|
3765
3808
|
"annotations": {
|
|
3766
3809
|
"title": "Report disagreement with a diagnosis",
|
|
3767
3810
|
"readOnlyHint": false,
|
|
3768
|
-
"destructiveHint": false,
|
|
3769
3811
|
"idempotentHint": false,
|
|
3812
|
+
"destructiveHint": false,
|
|
3770
3813
|
"openWorldHint": false
|
|
3771
3814
|
},
|
|
3772
3815
|
"execution": {
|
|
@@ -3775,7 +3818,7 @@
|
|
|
3775
3818
|
},
|
|
3776
3819
|
{
|
|
3777
3820
|
"name": "orderNewTest",
|
|
3778
|
-
"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
|
|
3821
|
+
"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 and a question about whether to wait for completion with the awaitTest tool.",
|
|
3779
3822
|
"inputSchema": {
|
|
3780
3823
|
"type": "object",
|
|
3781
3824
|
"properties": {
|
|
@@ -4140,8 +4183,8 @@
|
|
|
4140
4183
|
"annotations": {
|
|
4141
4184
|
"title": "Order immediate testing of a new page",
|
|
4142
4185
|
"readOnlyHint": false,
|
|
4143
|
-
"destructiveHint": false,
|
|
4144
4186
|
"idempotentHint": false,
|
|
4187
|
+
"destructiveHint": false,
|
|
4145
4188
|
"openWorldHint": true
|
|
4146
4189
|
},
|
|
4147
4190
|
"execution": {
|
|
@@ -4150,7 +4193,7 @@
|
|
|
4150
4193
|
},
|
|
4151
4194
|
{
|
|
4152
4195
|
"name": "orderRetest",
|
|
4153
|
-
"description": "Orders a retest of a page whose existing report is considered obsolete, with automated approval or rejection and immediate enqueuing if approved. Use this instead of requestRetest when immediate retesting is wanted; testing typically takes 2 to 4 minutes. Returns an order confirmation and disposition details, including, if approved, a report identifier
|
|
4196
|
+
"description": "Orders a retest of a page whose existing report is considered obsolete, with automated approval or rejection and immediate enqueuing if approved. Use this instead of requestRetest when immediate retesting is wanted; testing typically takes 2 to 4 minutes. Returns an order confirmation and disposition details, including, if approved, a report identifier and a question about whether to wait for completion with the awaitTest tool.",
|
|
4154
4197
|
"inputSchema": {
|
|
4155
4198
|
"type": "object",
|
|
4156
4199
|
"properties": {
|
|
@@ -4500,8 +4543,8 @@
|
|
|
4500
4543
|
"annotations": {
|
|
4501
4544
|
"title": "Order an immediate retest",
|
|
4502
4545
|
"readOnlyHint": false,
|
|
4503
|
-
"destructiveHint": false,
|
|
4504
4546
|
"idempotentHint": false,
|
|
4547
|
+
"destructiveHint": false,
|
|
4505
4548
|
"openWorldHint": true
|
|
4506
4549
|
},
|
|
4507
4550
|
"execution": {
|
|
@@ -4809,8 +4852,8 @@
|
|
|
4809
4852
|
"annotations": {
|
|
4810
4853
|
"title": "Wait for an ordered job to finish",
|
|
4811
4854
|
"readOnlyHint": true,
|
|
4812
|
-
"destructiveHint": false,
|
|
4813
4855
|
"idempotentHint": false,
|
|
4856
|
+
"destructiveHint": false,
|
|
4814
4857
|
"openWorldHint": false
|
|
4815
4858
|
},
|
|
4816
4859
|
"execution": {
|
|
@@ -4818,5 +4861,5 @@
|
|
|
4818
4861
|
}
|
|
4819
4862
|
}
|
|
4820
4863
|
],
|
|
4821
|
-
"version": "63.
|
|
4864
|
+
"version": "63.2.0"
|
|
4822
4865
|
}
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Kilotest
|
|
2
2
|
|
|
3
|
-
Kilotest integrates an ensemble of 12 rule engines, with about 1,300 rules, to test web pages for front-end quality (accessibility, usability, and web standards, including HTML, CSS, ARIA, and WCAG
|
|
3
|
+
Kilotest integrates an ensemble of 12 rule engines, with about 1,300 rules, to test web pages for front-end quality (accessibility, usability, and web standards, including HTML, CSS, ARIA, and WCAG). Each test job generates a standardized report describing which elements violated which rules and grouping the violations into about 380 issues. Kilotest can list the tested pages, the reports about each page, the issues in each report, the violators of each issue, and the diagnoses of each violation. Kilotest also accepts requests to test pages not yet tested and to retest already tested pages. Kilotest features are available via a web UI, an API, and an MCP server with streamable HTTP and no authentication.
|
|
4
4
|
|
|
5
5
|
## API
|
|
6
6
|
|
package/mcp.ts
CHANGED
|
@@ -22,6 +22,7 @@ import * as awaitTestAPI from './api/awaitTest.ts';
|
|
|
22
22
|
import * as reportDisagreementAPI from './api/reportDisagreement.ts';
|
|
23
23
|
import {version} from './api/version.ts';
|
|
24
24
|
import {recordMetric} from './util.ts';
|
|
25
|
+
import {collectionDescription, sharedContext} from './content.ts';
|
|
25
26
|
|
|
26
27
|
import {
|
|
27
28
|
getReportSchema,
|
|
@@ -53,12 +54,6 @@ import {
|
|
|
53
54
|
|
|
54
55
|
export const mcpPath = '/mcp';
|
|
55
56
|
|
|
56
|
-
// Shared statement of the Kilotest domain and data hierarchy, repeated in the server
|
|
57
|
-
// instructions and in the getKilotestOverview tool description (but not in the other tool
|
|
58
|
-
// descriptions, to avoid diluting how models read each tool), because some MCP clients do
|
|
59
|
-
// not propagate server-level instructions or the server description to their models.
|
|
60
|
-
const sharedContext = 'Kilotest integrates an ensemble of 12 rule engines, with about 1,300 rules, to test web pages for front-end quality (accessibility, usability, and web standards, including HTML, CSS, ARIA, and WCAG). Each test job generates a standardized report describing which elements violated which rules and grouping the violations into about 380 issues. Kilotest can list the tested pages, the reports about each page, the issues in each report, the violators of each issue, and the diagnoses of each violation. Kilotest also accepts requests to test pages not yet tested and to retest already tested pages.';
|
|
61
|
-
|
|
62
57
|
// Detailed overview of the Kilotest domain, data hierarchy, and tool workflow, returned by
|
|
63
58
|
// the getKilotestOverview tool and served as the docs://kilotest/overview resource.
|
|
64
59
|
const kilotestOverview = [
|
|
@@ -77,7 +72,7 @@ export const createMCPServer = (): McpServer => {
|
|
|
77
72
|
const server = new McpServer({
|
|
78
73
|
name: 'Kilotest',
|
|
79
74
|
version,
|
|
80
|
-
description:
|
|
75
|
+
description: collectionDescription
|
|
81
76
|
},
|
|
82
77
|
{
|
|
83
78
|
instructions: `${sharedContext} Use the listPages tool to start. If it shows that the page you want facts about has been tested, drill down from its latest report with the listIssues, listViolators, and listDiagnoses tools. If not, use the requestNewTest tool to request that the page be tested, or the orderNewTest tool for immediate, automatically approved testing. If the latest report about the page is obsolete, use the requestRetest tool to request that the page be retested, or the orderRetest tool for immediate, automatically approved retesting. For detailed documentation, call the getKilotestOverview tool.`
|
package/openapi-3.0.yaml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
openapi: 3.0.3
|
|
2
2
|
info:
|
|
3
3
|
title: Kilotest API
|
|
4
|
-
description: 'Kilotest
|
|
5
|
-
version: 63.
|
|
4
|
+
description: 'Kilotest integrates an ensemble of 12 rule engines, with about 1,300 rules, to test web pages for front-end quality (accessibility, usability, and W3C web standards, including HTML, CSS, ARIA, and WCAG). Each test job generates a standardized report describing which elements violated which rules and grouping the violations into about 380 issues. Kilotest can list the tested pages, the reports about each page, the issues in each report, the violators of each issue, and the diagnoses of each violation. Kilotest also accepts requests to test pages not yet tested and to retest already tested pages. Kilotest acts as a web server for human users, an MCP server for AI platforms, and an API for programmatic access. With this API you can request that a page be tested or retested, discover available reports, and incrementally retrieve facts from a report: from what reports are available, to what issues were reported in one report, to what elements violated one issue, to what diagnoses rule engines gave for one violation.'
|
|
5
|
+
version: 63.2.0
|
|
6
6
|
license:
|
|
7
7
|
name: MIT
|
|
8
8
|
url: https://github.com/jrpool/kilotest/blob/main/LICENSE
|
package/openapi.yaml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: Kilotest API
|
|
4
|
-
description: 'Kilotest
|
|
5
|
-
version: 63.
|
|
4
|
+
description: 'Kilotest integrates an ensemble of 12 rule engines, with about 1,300 rules, to test web pages for front-end quality (accessibility, usability, and W3C web standards, including HTML, CSS, ARIA, and WCAG). Each test job generates a standardized report describing which elements violated which rules and grouping the violations into about 380 issues. Kilotest can list the tested pages, the reports about each page, the issues in each report, the violators of each issue, and the diagnoses of each violation. Kilotest also accepts requests to test pages not yet tested and to retest already tested pages. Kilotest acts as a web server for human users, an MCP server for AI platforms, and an API for programmatic access. With this API you can request that a page be tested or retested, discover available reports, and incrementally retrieve facts from a report: from what reports are available, to what issues were reported in one report, to what elements violated one issue, to what diagnoses rule engines gave for one violation.'
|
|
5
|
+
version: 63.2.0
|
|
6
6
|
license:
|
|
7
7
|
name: MIT
|
|
8
8
|
identifier: MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jrpool/kilotest",
|
|
3
|
-
"version": "63.
|
|
3
|
+
"version": "63.2.0",
|
|
4
4
|
"description": "Ensemble testing for front-end web quality",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"test": "c8 node --require ./test/setup.ts --experimental-test-module-mocks --test --test-concurrency=1 --test-randomize '**/*.test.ts'",
|
|
15
15
|
"test:smoke": "node smokeTest.ts",
|
|
16
16
|
"checkinfo": "node checkinfo.ts",
|
|
17
|
-
"generate": "node scripts/generate-openapi.ts && node scripts/update-versions.ts",
|
|
17
|
+
"generate": "node scripts/generate-openapi.ts && node scripts/generate-lhm.ts && node scripts/update-versions.ts",
|
|
18
18
|
"build:media": "node scripts/buildDemoVideo.ts",
|
|
19
19
|
"publish:lhm": "npx -y @lobehub/market-cli plugin update --dir .",
|
|
20
20
|
"prePR": "node scripts/prePR.ts",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"globals": "*",
|
|
59
59
|
"htmlhint": "^1.9.2",
|
|
60
60
|
"js-yaml": "*",
|
|
61
|
-
"markdownlint-cli2": "
|
|
61
|
+
"markdownlint-cli2": "*",
|
|
62
62
|
"node-html-parser": "*",
|
|
63
63
|
"testaro": "*",
|
|
64
64
|
"typescript": "*",
|
|
@@ -108,6 +108,7 @@
|
|
|
108
108
|
"branches": 100
|
|
109
109
|
},
|
|
110
110
|
"overrides": {
|
|
111
|
-
"smol-toml": "^1.8.0"
|
|
111
|
+
"smol-toml": "^1.8.0",
|
|
112
|
+
"re2js": "^2.9.1"
|
|
112
113
|
}
|
|
113
114
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
generate-lhm.ts
|
|
3
|
+
Regenerates the description and tool entries (description, inputSchema, outputSchema,
|
|
4
|
+
annotations) of lhm.plugin.json from the single sources of truth: content.ts for prose and
|
|
5
|
+
api/schemas.ts (via the same registrations mcp.ts makes) for schemas. Everything else in
|
|
6
|
+
lhm.plugin.json (author, cloudEndpoint, homepage, identifier, name, prompts, resources,
|
|
7
|
+
tags, version, and each tool's execution block) is left untouched, because it has no
|
|
8
|
+
canonical source elsewhere in the repository; version is kept in sync separately by
|
|
9
|
+
update-versions.ts.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// IMPORTS
|
|
13
|
+
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import {z} from 'zod';
|
|
17
|
+
import {createMCPServer} from '../mcp.ts';
|
|
18
|
+
import {collectionDescription} from '../content.ts';
|
|
19
|
+
import {
|
|
20
|
+
listIssuesSchema,
|
|
21
|
+
listViolatorsSchema,
|
|
22
|
+
listDiagnosesSchema,
|
|
23
|
+
getReportSchema,
|
|
24
|
+
requestNewTestSchema,
|
|
25
|
+
requestRetestSchema,
|
|
26
|
+
requestFeatureSchema,
|
|
27
|
+
orderNewTestSchema,
|
|
28
|
+
orderRetestSchema,
|
|
29
|
+
awaitTestSchema,
|
|
30
|
+
reportDisagreementSchema,
|
|
31
|
+
listPagesResponseSchema,
|
|
32
|
+
listIssuesResponseSchema,
|
|
33
|
+
listViolatorsResponseSchema,
|
|
34
|
+
listDiagnosesResponseSchema,
|
|
35
|
+
getReportResponseSchema,
|
|
36
|
+
requestNewTestResponseSchema,
|
|
37
|
+
requestRetestResponseSchema,
|
|
38
|
+
requestFeatureResponseSchema,
|
|
39
|
+
orderNewTestResponseSchema,
|
|
40
|
+
orderRetestResponseSchema,
|
|
41
|
+
awaitTestResponseSchema,
|
|
42
|
+
reportDisagreementResponseSchema
|
|
43
|
+
} from '../api/schemas.ts';
|
|
44
|
+
|
|
45
|
+
// TYPES
|
|
46
|
+
|
|
47
|
+
type ZodRawShape = Record<string, z.ZodTypeAny>;
|
|
48
|
+
|
|
49
|
+
// CONSTANTS
|
|
50
|
+
|
|
51
|
+
const outputPath = path.join(import.meta.dirname, '..', 'lhm.plugin.json');
|
|
52
|
+
const jsonSchemaOptions = {target: 'draft-7' as const, unrepresentable: 'any' as const};
|
|
53
|
+
|
|
54
|
+
// The input and output schemas mcp.ts passes to registerTool for each tool, keyed by tool
|
|
55
|
+
// name, matching mcp.ts's own registerTool calls exactly (mcp.ts remains the source for
|
|
56
|
+
// descriptions and annotations, read at runtime below via createMCPServer).
|
|
57
|
+
const toolSchemas: Record<string, {input: ZodRawShape; output?: z.ZodTypeAny}> = {
|
|
58
|
+
getKilotestOverview: {input: {}},
|
|
59
|
+
listPages: {input: {}, output: listPagesResponseSchema},
|
|
60
|
+
listIssues: {input: listIssuesSchema, output: listIssuesResponseSchema},
|
|
61
|
+
listViolators: {input: listViolatorsSchema, output: listViolatorsResponseSchema},
|
|
62
|
+
listDiagnoses: {input: listDiagnosesSchema, output: listDiagnosesResponseSchema},
|
|
63
|
+
getReport: {input: getReportSchema, output: getReportResponseSchema},
|
|
64
|
+
requestNewTest: {input: requestNewTestSchema, output: requestNewTestResponseSchema},
|
|
65
|
+
requestRetest: {input: requestRetestSchema, output: requestRetestResponseSchema},
|
|
66
|
+
requestFeature: {input: requestFeatureSchema, output: requestFeatureResponseSchema},
|
|
67
|
+
reportDisagreement: {input: reportDisagreementSchema, output: reportDisagreementResponseSchema},
|
|
68
|
+
orderNewTest: {input: orderNewTestSchema, output: orderNewTestResponseSchema},
|
|
69
|
+
orderRetest: {input: orderRetestSchema, output: orderRetestResponseSchema},
|
|
70
|
+
awaitTest: {input: awaitTestSchema, output: awaitTestResponseSchema}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// FUNCTIONS
|
|
74
|
+
|
|
75
|
+
// Builds the lhm.plugin.json inputSchema for a tool: like outputSchema below, but LobeHub's
|
|
76
|
+
// existing input schemas never carry additionalProperties, so that key is dropped.
|
|
77
|
+
const buildInputSchema = (shape: ZodRawShape): Record<string, unknown> => {
|
|
78
|
+
const generated = z.toJSONSchema(z.object(shape), jsonSchemaOptions) as Record<string, unknown>;
|
|
79
|
+
delete generated.additionalProperties;
|
|
80
|
+
const {$schema, ...withoutSchemaKeyword} = generated;
|
|
81
|
+
return {...withoutSchemaKeyword, $schema};
|
|
82
|
+
};
|
|
83
|
+
// Builds the lhm.plugin.json outputSchema for a tool, reordering keys to match the file's
|
|
84
|
+
// existing convention ($schema placed just before additionalProperties and definitions).
|
|
85
|
+
const buildOutputSchema = (schema: z.ZodTypeAny): Record<string, unknown> => {
|
|
86
|
+
const generated = z.toJSONSchema(schema, jsonSchemaOptions) as Record<string, unknown>;
|
|
87
|
+
const {$schema, additionalProperties, definitions, ...rest} = generated;
|
|
88
|
+
return {
|
|
89
|
+
...rest,
|
|
90
|
+
...($schema !== undefined ? {$schema} : {}),
|
|
91
|
+
...(additionalProperties !== undefined ? {additionalProperties} : {}),
|
|
92
|
+
...(definitions !== undefined ? {definitions} : {})
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// EXECUTION
|
|
97
|
+
|
|
98
|
+
const server = createMCPServer();
|
|
99
|
+
const registeredTools = (server as unknown as {_registeredTools: Record<string, {description: string; annotations: unknown}>})._registeredTools;
|
|
100
|
+
const manifest = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
|
|
101
|
+
manifest.description = collectionDescription;
|
|
102
|
+
manifest.tools = manifest.tools.map((tool: {name: string; execution: unknown}) => {
|
|
103
|
+
const registered = registeredTools[tool.name];
|
|
104
|
+
const schemas = toolSchemas[tool.name];
|
|
105
|
+
if (!registered || !schemas) {
|
|
106
|
+
throw new Error(`No registration or schema mapping found for lhm.plugin.json tool "${tool.name}"`);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
name: tool.name,
|
|
110
|
+
description: registered.description,
|
|
111
|
+
inputSchema: buildInputSchema(schemas.input),
|
|
112
|
+
...(schemas.output ? {outputSchema: buildOutputSchema(schemas.output)} : {}),
|
|
113
|
+
annotations: registered.annotations,
|
|
114
|
+
execution: tool.execution
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
fs.writeFileSync(outputPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
118
|
+
console.log(`Wrote ${outputPath}`);
|
|
@@ -12,6 +12,7 @@ import {createDocument} from 'zod-openapi';
|
|
|
12
12
|
import pkg from '../package.json' with {type: 'json'};
|
|
13
13
|
import {routes} from '../api/routes.ts';
|
|
14
14
|
import {version} from '../api/version.ts';
|
|
15
|
+
import {sharedContext} from '../content.ts';
|
|
15
16
|
|
|
16
17
|
// CONSTANTS
|
|
17
18
|
|
|
@@ -137,7 +138,7 @@ const generate = () => {
|
|
|
137
138
|
openapi: '3.1.0',
|
|
138
139
|
info: {
|
|
139
140
|
title: 'Kilotest API',
|
|
140
|
-
description:
|
|
141
|
+
description: `${sharedContext} Kilotest acts as a web server for human users, an MCP server for AI platforms, and an API for programmatic access. With this API you can request that a page be tested or retested, discover available reports, and incrementally retrieve facts from a report: from what reports are available, to what issues were reported in one report, to what elements violated one issue, to what diagnoses rule engines gave for one violation.`,
|
|
141
142
|
version,
|
|
142
143
|
license: {
|
|
143
144
|
name: license,
|