@vantageos/mcp-doctor 0.4.0 → 0.6.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/CHANGELOG.md +132 -4
- package/README.md +26 -4
- package/dist/doctor.d.ts +9 -0
- package/dist/doctor.js +1880 -176
- package/package.json +3 -7
package/CHANGELOG.md
CHANGED
|
@@ -3,13 +3,122 @@
|
|
|
3
3
|
All notable changes to `@vantageos/mcp-doctor` are documented here.
|
|
4
4
|
This project adheres to [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## 0.6.0 — 2026-10-10
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Rebased onto 0.5.0 (S-28, PR #53); this release ships as 0.6.0.
|
|
9
|
+
|
|
10
|
+
Measured on `vantage-mcp-boilerplate` @ `577c6e0b` (`--mode=static`: 14 non-conforming, 3
|
|
11
|
+
could-not-judge under 0.4.0) and against the standard as merged in ElPi-Corp #241 (`572a8835`) +
|
|
12
|
+
fix-forward #242. Every changed reading below has a RED test committed before its fix and a bite
|
|
13
|
+
mutation on foreign material (`scripts/v050-foreign-bite-probe.sh`).
|
|
14
|
+
|
|
15
|
+
### Fixed (false positives and blind spots)
|
|
16
|
+
- **C15 reads `annotations: TOOL_ANNOTATIONS`.** An `annotations:` value that is not an inline
|
|
17
|
+
object is resolved to a `const X = { ... }` in the same file (or the only such definition in the
|
|
18
|
+
tree; `...SPREAD` entries are followed). Unresolvable (imported from a package, a call, a member
|
|
19
|
+
access) is `could-not-judge` naming the expression, never "missing hint". The same abstention
|
|
20
|
+
now protects C-8, C-9, C-10, C-11 and C-13, which read the same annotations window.
|
|
21
|
+
- **DServer14 Element 1 recognises the delegated challenge.** A call to `unauthorizedChallenge(` or
|
|
22
|
+
`verifyMcpAccessToken(` imported from `@vantageos/cloud-identity` (or its `/resource-server`
|
|
23
|
+
subpath, multi-line and aliased imports included) with the result's `status` and `WWW-Authenticate`
|
|
24
|
+
header forwarded — in the same module or another — is the 401 challenge. A delegation call whose
|
|
25
|
+
forwarding is not readable is `could-not-judge`. The kit's verifier also counts as the RS256/JWKS
|
|
26
|
+
verification (Element 4). A same-named function imported from another package is not a delegation.
|
|
27
|
+
- **DServer14 Element 2 reads a PRM route registered through a constant**
|
|
28
|
+
(`for (const path of PRM_PATHS) app.get(path, ...)`), a blind spot that Element 1 had been masking.
|
|
29
|
+
- **C19 reads the description forms servers actually write:** a `+` concatenation of literals across
|
|
30
|
+
lines, a template literal without expressions, and a string holding the other quote character
|
|
31
|
+
(`"the caller's org"` ended the literal at the apostrophe). A `description:` whose value is not
|
|
32
|
+
readable (a call, an interpolated template) is `could-not-judge` naming it. A tool's window now
|
|
33
|
+
stops at the previous tool's call, so a neighbour's description is never claimed.
|
|
34
|
+
- **D10 reads `description_fr:` with the literal on the next line,** a `+` concatenation, or an
|
|
35
|
+
identifier bound to a same-tree `const X = "..."`. Any other value (`t(locale, ...)`, a call) stays
|
|
36
|
+
`could-not-judge` and the reason now names the form.
|
|
37
|
+
- **The code mask lexes JSX/TSX.** In `.tsx`/`.jsx` files a `<` at an expression position opens an
|
|
38
|
+
element; the text between tags is not code, `/>` is not a regex, an apostrophe in JSX text is not a
|
|
39
|
+
string. `src/ui/entry.tsx` no longer reads "brackets never balance". A file that ends inside an
|
|
40
|
+
unterminated JSX element, template literal or block comment is now unreadable (fail closed) instead
|
|
41
|
+
of having its tail swallowed.
|
|
42
|
+
- **D16 recognises the SDK v2 server construction.** `new McpServer({ name, version })` whose
|
|
43
|
+
`version` is read from `package.json` (directly, or through a `const` bound to a statement that
|
|
44
|
+
reads `package.json`'s `.version`, in any file) is the observable version: the SDK serves it on
|
|
45
|
+
`server/discover` and `initialize`. The verdict is `[static read]` and declares the residual
|
|
46
|
+
(pre-auth reachability of the SDK transport is the dynamic half). A literal version, or an
|
|
47
|
+
identifier bound to anything else, is not credited.
|
|
48
|
+
|
|
49
|
+
### Fixed (measured on vantage-peers `mcp-server` @ `2f0e5469`, Sigma's triage)
|
|
50
|
+
- **D2 stateless: a header NAME in a CORS list is not session keying.** Every `mcp-session-id` mention
|
|
51
|
+
is classified: a keying form (a `sessionIdGenerator` other than `undefined`, the header read or
|
|
52
|
+
written by call or index, sent as an object key, or a store keyed by a session id) is
|
|
53
|
+
`non-conforming`; a CORS `allowHeaders` / `exposeHeaders` entry is inert and named in the verdict;
|
|
54
|
+
anything else is `could-not-judge` naming the site. The old oracle fixture, which was only CORS
|
|
55
|
+
lists, now carries a real generator.
|
|
56
|
+
- **DServer6 reports every site and resolves literal values.** It returned on its first match; it now
|
|
57
|
+
lists all sites (Forms A, B, C, D). A name match whose value is proven a literal constant
|
|
58
|
+
(`HOSTNAME = "0.0.0.0"` reached through `server.hostname`) is not a finding and is named as not
|
|
59
|
+
counted; an unresolved value is kept and says "identifier-name match, value unresolved". A call is
|
|
60
|
+
never resolved.
|
|
61
|
+
- **C17 reads the repository-root workflows.** For a package scanned as a subdirectory the check
|
|
62
|
+
walks from the scan root up to the git root (the nearest `.git`) and reads that
|
|
63
|
+
`.github/workflows`; it never reads above the git root and never leaves the scanned tree when
|
|
64
|
+
there is no `.git`.
|
|
65
|
+
- **C25 recognises a build identity on `/health`.** A `/health` handler that serves `version` and a
|
|
66
|
+
derived `commit` (or any C25 identity key) beside it is the build identity of the tree served; a
|
|
67
|
+
hand-written literal there is red.
|
|
68
|
+
- **Registration wrappers (`defineTool(...)`).** A function whose body calls `registerTool` / `.tool`
|
|
69
|
+
(directly or through a local alias) with one of its own parameters as the tool name is a
|
|
70
|
+
registration wrapper; the parameter index is derived, and the literal at that index of every call
|
|
71
|
+
is a tool name (a constant bound to a string literal is resolved; anything else is an unresolved
|
|
72
|
+
site, as before). The tool's annotations are read from its call, its description from the argument
|
|
73
|
+
the wrapper forwards, and the wrapper's own internal `registerTool(name, ...)` is no longer
|
|
74
|
+
reported as an unresolved tool. C8-C13, C15, C19 and DServer2 judge those tools instead of
|
|
75
|
+
abstaining.
|
|
76
|
+
|
|
77
|
+
### Corrected after review (PR #54)
|
|
78
|
+
- **C17 reads a publish step, nothing else.** Provenance is an actual package-manager `publish`
|
|
79
|
+
invocation carrying `--provenance` (YAML/shell comments stripped, `\` continuations joined) or
|
|
80
|
+
`publishConfig.provenance: true`. A comment that names the flag and the `id-token: write`
|
|
81
|
+
permission (necessary for OIDC, attests nothing) are no longer a claim.
|
|
82
|
+
- **C15: a one-line constant closes on its own line.** `const PING_ANN = { title: "x" };` no longer
|
|
83
|
+
reads on into the next constant and borrows its hints. A tool whose own `annotations: X` is an
|
|
84
|
+
expression reads only X; the sibling-attribution fix now has a test that fails without it.
|
|
85
|
+
- **C15 reads spreads in order.** In `annotations: { ...CONST, key: value }` a later member
|
|
86
|
+
overrides the spread (`key: undefined` un-declares a hint, a boolean keeps it); a spread that
|
|
87
|
+
cannot be resolved is `could-not-judge` naming it.
|
|
88
|
+
- **C25 resolves a constant.** `commit: GIT_SHA` with `const GIT_SHA = "deadbeef"` is the same
|
|
89
|
+
hand-written literal and stays red.
|
|
90
|
+
|
|
91
|
+
### Changed (rule changes from the merged standard)
|
|
92
|
+
- **D7:** conformant when the tree depends on `@modelcontextprotocol/server` >= 2 and no result
|
|
93
|
+
builder is written by hand (`resultType` / `ttlMs` / `cacheScope` spelled in source, outside the
|
|
94
|
+
SDK's `cacheHints` option). The protocol-kit requirement applies only to hand-rolled builders:
|
|
95
|
+
SDK v2 plus a hand-rolled builder and no kit is `non-conforming` at the builder. A dependency range
|
|
96
|
+
that is a tag (`latest`) is `could-not-judge`. SDK v1 / no SDK keeps the 0.4.0 `absent`.
|
|
97
|
+
- **C-17 non-CI path.** A package published without CI has no npm provenance; the standard accepts the
|
|
98
|
+
`npm pack` shasum the reviewer reproduced and read back at the registry. **Proposed evidence form —
|
|
99
|
+
the reviewer rules:** one line `Provenance: <pkg>@<version> npm-pack shasum <40-hex> reproduced by
|
|
100
|
+
<who>, read back from the registry <date>` in `PROVENANCE`, `PROVENANCE.md`, `CHANGELOG.md` or
|
|
101
|
+
`README.md`, naming `<pkg>@<version>` equal to `package.json`'s `name@version`. A record for another version or
|
|
102
|
+
another package, one that names only a version, or one with no 40-hex shasum, is
|
|
103
|
+
`non-conforming` and says why. Without `--provenance` and without a record it is
|
|
104
|
+
still `non-conforming`. Whether the shasum equals the registry's `dist.shasum` stays the gate.
|
|
105
|
+
- **C-18 identifies the server manifest by content.** A root `mcp.json` carrying `mcpServers` is the
|
|
106
|
+
OpenAI plugin config, not the server manifest; the server manifest is the root file (`mcp.json` or
|
|
107
|
+
`mcp-server.json`) carrying capabilities + transport + version. `non-conforming` when no root file
|
|
108
|
+
qualifies (the verdict says why `mcp.json` did not count) and when two root files each qualify.
|
|
109
|
+
- **S-5 package rule (DServer14):** `non-conforming` on a shipped dependency (`dependencies`,
|
|
110
|
+
`peerDependencies`, `optionalDependencies`) on `@vantageos/mcp-auth-kit`, or on
|
|
111
|
+
`@vantageos/cloud-identity` whose lowest admissible version is below 0.15.0; a range whose floor
|
|
112
|
+
cannot be read (`latest`) is `could-not-judge`. The import-subpath half of S-5 is not read.
|
|
113
|
+
- **DLocalIdentity: minting is not resolving the caller.** Sites that sign or mint (`new SignJWT`,
|
|
114
|
+
`jwt.sign`, `importPKCS8`, `createPrivateKey`, `importJWK` of a private key) and the claims written
|
|
115
|
+
into a signing chain's payload are listed under `MINTING/SIGNING` and never counted. Local signature
|
|
116
|
+
verification (`jwtVerify`, `createLocalJWKSet`, ...) and raw claim reads that identify the caller
|
|
117
|
+
stay violations whoever issued the token: there is no issuer carve-out.
|
|
118
|
+
|
|
119
|
+
### Also
|
|
9
120
|
- **DLocalIdentity exemption marker renamed** `local-identity-exempt:` -> `allow-local-identity:`
|
|
10
121
|
(standard R-57), reason of 8 characters or more (was 10). The old spelling no longer exempts.
|
|
11
|
-
|
|
12
|
-
### Added
|
|
13
122
|
- **DLocalIdentity, third REVISE (PR #52):** reported now are `claimedName` (and any
|
|
14
123
|
`caller*`/`actor*`/`acting*`/`claimed*` property) compared to an agent's `.name`;
|
|
15
124
|
`switch (row.createdBy) { case caller: }`; `[caller].includes(row.createdBy)` and
|
|
@@ -17,6 +126,15 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
17
126
|
imported or destructured under an alias (`import { createHash as h }`). Implemented as regex
|
|
18
127
|
matchers, not an AST: `typescript` is a devDependency only and the report contract is line-based.
|
|
19
128
|
|
|
129
|
+
## 0.5.0 — unreleased
|
|
130
|
+
|
|
131
|
+
### Added — S-28, a tool forwards the verified claim of its caller (standard `bacc54a`)
|
|
132
|
+
- **`S-28 claim-forwarded`** (`src/detectors/s28-claim-forwarded.ts`), the MCP half of the door audit's cause 1: every MCP caller reaches the backend as one service account, so a tool that does not forward the claim it holds leaves the door nothing to confine to. For each `<client>.query|mutation|action("<module>:<fn>", args)` it reads the call's argument text, the helpers it spreads or attaches (resolved in the tree), the refusal that precedes the call in the same handler, and the backend door list. Red: a call to a door that is not an exit and resolves its caller, with no `verifiedOrg` / `verifiedPerson` in it (a `verifiedActor` added by a wrapped client covers only a bearer that resolved an agent), or one forwarded only through an optional helper with no refusal before the call. Green: the claim is forwarded (and refused-without through an optional helper), or the door is service-account-only / `fleet-wide:` (read from the door). Could-not-judge: no backend found, a door the backend does not define, a door named by an expression, a door whose own source shows no caller resolution. Absent: no Convex client imported at all.
|
|
133
|
+
- **New option `--backend=<convex-dir>`** (and `RunDoctorOptions.backend`, `Tree.backendRoot`, `loadTree(root, backendRoot?)`): the backend source whose door list S-28 reads. Default: the nearest `convex/` (holding a `schema.ts`) above the scanned tree.
|
|
134
|
+
- **Pin.** ElPi-Corp `bacc54a53d5f6f9f20e5254908e2b46807902faa` on `main`, `resources/references/mcp-server-standard.md` blob `fe30518`: the merged S-28 text of ElPi-Corp #239. Its merge commit `7d7df89` was left off `main` by the 2026-10-10 history rewrite; `bacc54a` is its rewritten counterpart and carries the same file blob (as do `7d7df89` and the PR head `6f7794e`, the former pin). Later `main` commits amend other rules and leave S-28 unchanged.
|
|
135
|
+
- Tests: `tests/s28-claim-forwarded.test.ts`. Real material: vantage-peers `16f09070ffd49488ad65b12ec2f8026865d4ab5d` (the commit the door audit measured), `tools.ts` + `verifiedActor.ts` and ten `convex/` modules vendored byte-exact in `fixtures/s28-vp-16f0907/`.
|
|
136
|
+
- Version 0.5.0 (0.4.0 shipped DLocalIdentity, PR #52). The registry has one more detector (47); the oracle snapshot gains one `absent` row, no existing row changed.
|
|
137
|
+
|
|
20
138
|
## 0.4.0 — 2026-10-09
|
|
21
139
|
|
|
22
140
|
### Added
|
|
@@ -46,10 +164,20 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
46
164
|
stylesheet, `authorize.ts:373`) is not reported. A keyed `createHmac`, and any digest of a value the
|
|
47
165
|
caller supplied, stay sites.
|
|
48
166
|
- Measured on vantage-peers `mcp-server/src` at `8fb7c12`: 33 sites, exit 1.
|
|
167
|
+
- **DLocalIdentity, third REVISE (PR #52):** reported now are `claimedName` (and any
|
|
168
|
+
`caller*`/`actor*`/`acting*`/`claimed*` property) compared to an agent's `.name`;
|
|
169
|
+
`switch (row.createdBy) { case caller: }`; `[caller].includes(row.createdBy)` and
|
|
170
|
+
`new Set([caller]).has(...)`; `a.localeCompare(b) === 0` on names or tenants; a primitive
|
|
171
|
+
imported or destructured under an alias (`import { createHash as h }`). Implemented as regex
|
|
172
|
+
matchers, not an AST: `typescript` is a devDependency only and the report contract is line-based.
|
|
49
173
|
|
|
50
174
|
### Changed
|
|
51
175
|
- The registry has one more detector (46). `tests/__oracle__/oracle-complete-server.static.json`
|
|
52
176
|
gains one `present` row; no existing row changed.
|
|
177
|
+
- **DLocalIdentity exemption marker renamed** `local-identity-exempt:` -> `allow-local-identity:`
|
|
178
|
+
(standard R-57), reason of 8 characters or more (was 10). The old spelling no longer exempts.
|
|
179
|
+
- Fix-forward (0.5.0): the third-REVISE items above and the marker rename were first logged under
|
|
180
|
+
`Unreleased`, but shipped in 0.4.0 (npm `@vantageos/mcp-doctor@0.4.0`, gitHead `16a7531`); moved here.
|
|
53
181
|
|
|
54
182
|
---
|
|
55
183
|
|
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ The path is the root of the MCP server sources (the folder holding `server.ts` /
|
|
|
46
46
|
The binary's own `--help` is the authority; this is what it prints:
|
|
47
47
|
|
|
48
48
|
```
|
|
49
|
-
usage: mcp-doctor <source-tree-path> [--mode=static|dynamic|both]
|
|
49
|
+
usage: mcp-doctor <source-tree-path> [--mode=static|dynamic|both] [--backend=<convex-dir>]
|
|
50
50
|
|
|
51
51
|
--mode=static read the tree only (default)
|
|
52
52
|
--mode=dynamic launch the server and judge what it serves
|
|
@@ -54,6 +54,8 @@ usage: mcp-doctor <source-tree-path> [--mode=static|dynamic|both]
|
|
|
54
54
|
--local-identity-ceiling=N
|
|
55
55
|
local identity primitives tolerated before DLocalIdentity
|
|
56
56
|
fails (default 0)
|
|
57
|
+
--backend=DIR the backend source (a convex/ dir) whose door list S-28 reads;
|
|
58
|
+
default: the nearest convex/ above the tree
|
|
57
59
|
--version print the version and exit
|
|
58
60
|
--help print this and exit
|
|
59
61
|
|
|
@@ -117,10 +119,11 @@ Each detector maps one standard item and is proven **bipolar** on real material:
|
|
|
117
119
|
| **D4** CacheableResult | `ttlMs` / `cacheScope` on the list/read result types | present / absent |
|
|
118
120
|
| **D5** server/discover | a `server/discover` handler is registered (absence-based, with a positive control) | present / absent |
|
|
119
121
|
| **D6** mcp-apps ui-key | MCP Apps UI key is the GA nested `_meta.ui.resourceUri` — not flat `ui/resourceUri`, not community `io.modelcontextprotocol/ui` | GA-nested / flat-deprecated / community-nonGA / none |
|
|
120
|
-
| **D7** protocol-kit | `@vantageos/mcp-protocol-kit` listed in `package.json` **and actually called** (builder invoked, not merely declared) | listed+called / listed-not-called / hand-rolled |
|
|
122
|
+
| **D7** protocol-kit | `@vantageos/mcp-protocol-kit` listed in `package.json` **and actually called** (builder invoked, not merely declared) | listed+called / listed-not-called / hand-rolled. Conformant too on `@modelcontextprotocol/server` >= 2 with no hand-rolled result builder (the kit is required only where builders are written by hand) |
|
|
121
123
|
| **D15** cache-coherence | Server Rule 15 — every tool-set mutation (RegisteredTool `.enable/.disable/.update/.remove`, `unregisterTool`) is correlated with a `notifications/tools/list_changed` emission | present / absent / non-conforming / could-not-judge |
|
|
122
|
-
| **D16** version-observable | Server Rule 16 — `server/discover` (or an HTTP `/health` route) exposes a `version` field sourced from `package.json`, reachable pre-auth | present / absent / non-conforming / could-not-judge |
|
|
123
|
-
| **DLocalIdentity** no-local-identity | No MCP server defines its own identity primitive: resolving a caller to a tenant or agent (local JWT verify, a populated allow-list attached to a bearer), asserting org membership or role, filtering rows by caller scope (`fromAllowList`, a name allow-list, a row's `orgId` or acting-name field such as `createdBy` / `assignedTo` compared to the caller's, by `===`, `==`, `Object.is` or after `toLowerCase()`), or validating a credential (local `timingSafeEqual`, a digest of a presented value, HMAC; a hash over a static literal is not a site). A call to a binding imported from `@vantageos/cloud-identity` is not a site. | present (0, or EXEMPT) / non-conforming (count > ceiling, default 0) / could-not-judge |
|
|
124
|
+
| **D16** version-observable | Server Rule 16 — `server/discover` (or an HTTP `/health` route) exposes a `version` field sourced from `package.json`, reachable pre-auth; an SDK v2 `new McpServer({ name, version })` carrying a `package.json`-derived version counts (pre-auth reach is the dynamic half) | present / absent / non-conforming / could-not-judge |
|
|
125
|
+
| **DLocalIdentity** no-local-identity | No MCP server defines its own identity primitive: resolving a caller to a tenant or agent (local JWT verify, a populated allow-list attached to a bearer), asserting org membership or role, filtering rows by caller scope (`fromAllowList`, a name allow-list, a row's `orgId` or acting-name field such as `createdBy` / `assignedTo` compared to the caller's, by `===`, `==`, `Object.is` or after `toLowerCase()`), or validating a credential (local `timingSafeEqual`, a digest of a presented value, HMAC; a hash over a static literal is not a site). A call to a binding imported from `@vantageos/cloud-identity` is not a site. Minting/signing sites (`new SignJWT`, `jwt.sign`, a private-key import) are listed as `MINTING/SIGNING` and not counted; verification and claim reads stay violations whoever issued the token (no issuer carve-out). | present (0, or EXEMPT) / non-conforming (count > ceiling, default 0) / could-not-judge |
|
|
126
|
+
| **S-28** claim-forwarded | Server complement S-28 (standard `bacc54a`) — a tool forwards the verified claim of its caller (`verifiedOrg` / `verifiedPerson`) to every backend door it calls and refuses when it holds none. Reads each `<client>.query\|mutation\|action` call, the helpers it spreads, the refusal before it, and the backend source's door list (`--backend=<convex-dir>`, else the nearest `convex/` above the tree). A door that is service-account-only or declared `fleet-wide:` needs no claim; no backend found, an unknown door, a computed door name are could-not-judge. | present / non-conforming / could-not-judge / absent (no Convex client imported) |
|
|
124
127
|
|
|
125
128
|
**DLocalIdentity exemptions.** Two, both reported as `EXEMPT` rather than hidden: the
|
|
126
129
|
`@vantageos/cloud-identity` package itself (read from the target's `package.json` name), and a site
|
|
@@ -131,6 +134,25 @@ not UTF-8 text, is `could-not-judge`. To adopt the rule on a tree that still hol
|
|
|
131
134
|
`--local-identity-ceiling=<current count>` as a ratchet; the row then reads `present` and still lists
|
|
132
135
|
every site.
|
|
133
136
|
|
|
137
|
+
**Reading rules, 0.6.0** (each changed detector; details and rationale in [CHANGELOG.md](./CHANGELOG.md)).
|
|
138
|
+
- **C15 / C8 / C9 / C10 / C11 / C13** resolve `annotations: SOME_CONST` to a same-tree `const` object; an
|
|
139
|
+
unresolvable value is `could-not-judge`, never "missing".
|
|
140
|
+
- **C19** counts `"a" + "b"`, an expression-free template literal and apostrophe-bearing strings as a
|
|
141
|
+
declared description; **D10** reads `description_fr:` with the literal on the next line or a const.
|
|
142
|
+
- **DServer14** accepts a 401 challenge delegated to `@vantageos/cloud-identity`
|
|
143
|
+
(`unauthorizedChallenge` / `verifyMcpAccessToken`, status + header forwarded), a PRM route registered
|
|
144
|
+
through a path constant, and is red on `@vantageos/mcp-auth-kit` or `cloud-identity` below 0.15.0.
|
|
145
|
+
- **C17** accepts, without CI, a `Provenance: <pkg>@<version> npm-pack shasum <40-hex> ...` record naming the
|
|
146
|
+
published version (**proposed evidence form, reviewer rules**); **C18** identifies the server manifest by
|
|
147
|
+
content (`mcpServers` = OpenAI plugin config; the manifest is `mcp-server.json` then) and is red on two.
|
|
148
|
+
- **D2** names `mcp-session-id` in a CORS list as inert and flags only real session keying;
|
|
149
|
+
**DServer6** reports every site and does not count a name whose value is a literal constant;
|
|
150
|
+
**C17** reads the repository-root `.github/workflows` for a subpackage; **C25** accepts a derived
|
|
151
|
+
`commit` served on `/health` beside `version`; a project-local registration wrapper
|
|
152
|
+
(`defineTool(...)`) is derived from the function that forwards a parameter to `registerTool`, so its
|
|
153
|
+
tools are judged.
|
|
154
|
+
- **TSX/JSX files** are lexed; a file that ends inside an unterminated JSX element is unreadable, not skipped.
|
|
155
|
+
|
|
134
156
|
**This table is a sample, not the inventory.** Every registered detector runs on every invocation,
|
|
135
157
|
so the authoritative list is simply the output of a run — one row per detector. The registered set
|
|
136
158
|
lives in `src/detectors/index.ts`. No count is written here on purpose: a number typed into a README
|
package/dist/doctor.d.ts
CHANGED
|
@@ -43,6 +43,13 @@ interface Tree {
|
|
|
43
43
|
* configured list: the report states what was walked past, not what could be.
|
|
44
44
|
*/
|
|
45
45
|
skippedDirs: SkippedDir[];
|
|
46
|
+
/**
|
|
47
|
+
* The backend source root (a `convex/` directory) a detector may read the
|
|
48
|
+
* backend's door list from. Set by the caller (`--backend=<dir>`); when unset a
|
|
49
|
+
* detector that needs it looks for the nearest `convex/` above `root` and says
|
|
50
|
+
* so when it finds none. Unset means "not supplied", never "no backend".
|
|
51
|
+
*/
|
|
52
|
+
backendRoot?: string;
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
type State = "present" | "absent" | "non-conforming" | "could-not-judge";
|
|
@@ -217,6 +224,8 @@ declare function isLiveDetector(d: Detector | LiveDetector): d is LiveDetector;
|
|
|
217
224
|
type RunMode = "static" | "dynamic" | "both";
|
|
218
225
|
interface RunDoctorOptions {
|
|
219
226
|
mode?: RunMode;
|
|
227
|
+
/** The backend source root (`convex/`) a detector reads the backend's door list from (S-28). */
|
|
228
|
+
backend?: string;
|
|
220
229
|
}
|
|
221
230
|
/**
|
|
222
231
|
* Run every detector against the target.
|