@mcpdesc/validator 0.9.0 → 0.10.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 CHANGED
@@ -6,7 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this package follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
  Dates for published releases are the UTC publication dates recorded by npm.
8
8
 
9
- ## [Unreleased]
9
+ ## [0.10.0] - 2026-09-07
10
+
11
+ ### Added
12
+
13
+ - Added the immutable `0.8.0-rc.3` selector with behavior equivalent to RC.2,
14
+ its RC.3 schema identity, and frozen fixtures.
15
+ - Added component reference resolution for RC.2 and RC.3 with deterministic
16
+ terminal-target provenance.
17
+
18
+ ### Changed
19
+
20
+ - Validator integrity now relies on package-owned schema, runtime, fixture, and
21
+ package bytes. Specification repository tags and commits are retained only as
22
+ informational provenance.
23
+ - Recorded maintainer-approved additive provenance corrections to the RC.2 and
24
+ RC.3 snapshot implementations. Their schemas, conformance results,
25
+ diagnostics, and frozen fixtures remain unchanged.
10
26
 
11
27
  ## [0.9.0] - 2026-09-04
12
28
 
package/README.md CHANGED
@@ -44,16 +44,21 @@ a later draft is published.
44
44
 
45
45
  ## Supported snapshots
46
46
 
47
- Version `0.8.0` supports these immutable snapshots, newest first:
47
+ The current workspace supports these immutable selectors, newest first:
48
48
 
49
- | Selector | Tag | Embedded schema SHA-256 |
49
+ | Selector | First validator release | Embedded schema SHA-256 |
50
50
  |---|---|---|
51
- | `0.8.0-rc.2` | `v0.8.0-rc.2` | `40f6775dde052224114e91d6aa484d826eecf56b77f7ac87b4cf707ffbcb6ce8` |
52
- | `0.8.0-rc.1` | `v0.8.0-rc.1` | `936a0f24ade501fcabf3d6498c0440c445daa672a575573a35954cee49430ac4` |
53
- | `0.8.0-draft.4` | `v0.8.0-draft.4` | `93ed03f74059b5b3ce7509a96b59161bdab2c3cf7734397a9bec5a7588d0b03b` |
54
- | `0.8.0-draft.3` | `v0.8.0-draft.3` | `8823c1f1946360b2a44d00920e2092e5e4acd139a1964befad4eb0bf3ce96002` |
55
- | `0.8.0-draft.2` | `v0.8.0-draft.2` | `ab692c1a5a0f7e5f29be1940aa8c64a56d4620be0a19d00cf0a64680b7e517fa` |
56
- | `0.8.0-draft.1` | `v0.8.0-draft.1` | `4ceb6042c3fd31703199cd3db869ec5c35c17d2fe9ab7b2f5b96a2a3af0cebe4` |
51
+ | `0.8.0-rc.3` | `0.10.0` | `a9c3ff77ba37c72362909f538f6e957d055e6fdb372f8b3d529e3651af3fecf4` |
52
+ | `0.8.0-rc.2` | `0.9.0` | `40f6775dde052224114e91d6aa484d826eecf56b77f7ac87b4cf707ffbcb6ce8` |
53
+ | `0.8.0-rc.1` | `0.5.0` | `936a0f24ade501fcabf3d6498c0440c445daa672a575573a35954cee49430ac4` |
54
+ | `0.8.0-draft.4` | `0.4.0` | `93ed03f74059b5b3ce7509a96b59161bdab2c3cf7734397a9bec5a7588d0b03b` |
55
+ | `0.8.0-draft.3` | `0.3.0` | `8823c1f1946360b2a44d00920e2092e5e4acd139a1964befad4eb0bf3ce96002` |
56
+ | `0.8.0-draft.2` | `0.2.0` | `ab692c1a5a0f7e5f29be1940aa8c64a56d4620be0a19d00cf0a64680b7e517fa` |
57
+ | `0.8.0-draft.1` | `0.1.0` | `4ceb6042c3fd31703199cd3db869ec5c35c17d2fe9ab7b2f5b96a2a3af0cebe4` |
58
+
59
+ The validator package embeds the schema and executable behavior for each
60
+ selector. npm integrity and trusted-publishing provenance identify released
61
+ package bytes; specification repository tags and commits are informational.
57
62
 
58
63
  ## Usage
59
64
 
@@ -132,9 +137,9 @@ Structural paths start with AJV's instance path. A `required` error appends its
132
137
 
133
138
  ## Support metadata
134
139
 
135
- The package exports frozen `supportedSpecifications`, `supportedProtocolVersions`, and `specificationProvenance` values. Provenance records include the snapshot tag, recorded schema URI, and embedded schema SHA-256 digest. Public validation dispatches through a registry keyed by exact specification selectors. The current repository selector set is `0.8.0-draft.1`, `0.8.0-draft.2`, `0.8.0-draft.3`, `0.8.0-draft.4`, `0.8.0-rc.1`, and `0.8.0-rc.2`; the protocol-version export is the deduplicated union supported by those snapshots.
140
+ The package exports frozen `supportedSpecifications`, `supportedProtocolVersions`, and `specificationProvenance` values. Provenance records include the snapshot tag, recorded schema URI, and embedded schema SHA-256 digest. Public validation dispatches through a registry keyed by exact specification selectors. The current repository selector set is `0.8.0-draft.1`, `0.8.0-draft.2`, `0.8.0-draft.3`, `0.8.0-draft.4`, `0.8.0-rc.1`, `0.8.0-rc.2`, and `0.8.0-rc.3`; the protocol-version export is the deduplicated union supported by those snapshots.
136
141
 
137
- RC.2 also exports the frozen `mcpExtensionCatalogue` and
142
+ RC.2 and RC.3 also export the frozen `mcpExtensionCatalogue` and
138
143
  `mcpExtensionMaturity` classifier. The catalogue pins its authoritative source,
139
144
  effective date, and official or experimental identifier assignments. Catalogue
140
145
  recognition establishes authority and maturity only; it does not validate
@@ -142,10 +147,10 @@ extension-specific settings.
142
147
 
143
148
  npm package SemVer tracks implementation releases independently from specification snapshot identity. Adding a later snapshot is additive: it must use a sibling implementation and selector rather than changing an existing snapshot's schema, semantics, metadata, fixtures, or results.
144
149
 
145
- The RC.1 component resolver has one maintainer-approved additive tooling
146
- correction: successful substitutions also report authored and terminal target
147
- paths. The correction does not change the RC.1 schema, validation results,
148
- diagnostics, or fixtures and is recorded in the integrity manifest.
150
+ The RC.1, RC.2, and RC.3 component resolvers report authored and terminal target
151
+ paths for successful substitutions. The additive tooling corrections do not
152
+ change snapshot schemas, validation results, diagnostics, or fixtures and are
153
+ recorded in the integrity manifest.
149
154
 
150
155
  The runtime bundles its schema, performs no network fetches for external schema references, and imports no Node.js built-ins. Unresolved external Tool-schema references produce incomplete-validation warnings and are preserved. All three ESM entry points support Node.js 20 or later and browser bundlers.
151
156
 
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type McpDescriptionSpecification = '0.8.0-draft.1' | '0.8.0-draft.2' | '0.8.0-draft.3' | '0.8.0-draft.4' | '0.8.0-rc.1' | '0.8.0-rc.2';
1
+ export type McpDescriptionSpecification = '0.8.0-draft.1' | '0.8.0-draft.2' | '0.8.0-draft.3' | '0.8.0-draft.4' | '0.8.0-rc.1' | '0.8.0-rc.2' | '0.8.0-rc.3';
2
2
 
3
3
  export type SupportedProtocolVersion =
4
4
  | '2024-11-05'
@@ -38,7 +38,7 @@ export interface McpDescriptionComponentResolutionResult {
38
38
  }
39
39
 
40
40
  export interface ResolveMcpDescriptionComponentReferencesOptions {
41
- readonly specification: '0.8.0-rc.1';
41
+ readonly specification: '0.8.0-rc.1' | '0.8.0-rc.2' | '0.8.0-rc.3';
42
42
  }
43
43
 
44
44
  export type McpExtensionMaturity = 'official' | 'experimental' | 'uncatalogued';
@@ -86,6 +86,11 @@ export interface SpecificationProvenance {
86
86
  readonly schemaUri: 'https://mcpdesc.org/schema/mcp-description/0.8.0-rc.2.json';
87
87
  readonly schemaSha256: '40f6775dde052224114e91d6aa484d826eecf56b77f7ac87b4cf707ffbcb6ce8';
88
88
  };
89
+ readonly '0.8.0-rc.3': {
90
+ readonly snapshotTag: 'v0.8.0-rc.3';
91
+ readonly schemaUri: 'https://mcpdesc.org/schema/mcp-description/0.8.0-rc.3.json';
92
+ readonly schemaSha256: 'a9c3ff77ba37c72362909f538f6e957d055e6fdb372f8b3d529e3651af3fecf4';
93
+ };
89
94
  }
90
95
 
91
96
  export interface ResolveMcpDescriptionSpecificationOptions {
@@ -109,7 +114,7 @@ export type McpDescriptionSpecificationResolution =
109
114
  | ResolvedMcpDescriptionSpecification
110
115
  | UnresolvedMcpDescriptionSpecification;
111
116
 
112
- export declare const supportedSpecifications: readonly ['0.8.0-draft.1', '0.8.0-draft.2', '0.8.0-draft.3', '0.8.0-draft.4', '0.8.0-rc.1', '0.8.0-rc.2'];
117
+ export declare const supportedSpecifications: readonly ['0.8.0-draft.1', '0.8.0-draft.2', '0.8.0-draft.3', '0.8.0-draft.4', '0.8.0-rc.1', '0.8.0-rc.2', '0.8.0-rc.3'];
113
118
 
114
119
  export declare const supportedProtocolVersions: readonly [
115
120
  '2024-11-05',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcpdesc/validator",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Structural and semantic validation for MCP Description snapshots",
5
5
  "type": "module",
6
6
  "types": "./index.d.ts",
package/src/index.js CHANGED
@@ -4,7 +4,10 @@ import * as draft3 from './snapshots/0.8.0-draft.3/index.js';
4
4
  import * as draft4 from './snapshots/0.8.0-draft.4/index.js';
5
5
  import * as rc1 from './snapshots/0.8.0-rc.1/index.js';
6
6
  import * as rc2 from './snapshots/0.8.0-rc.2/index.js';
7
+ import * as rc3 from './snapshots/0.8.0-rc.3/index.js';
7
8
  import { resolveComponentReferences as resolveRc1ComponentReferences } from './snapshots/0.8.0-rc.1/semantic.js';
9
+ import { resolveComponentReferences as resolveRc2ComponentReferences } from './snapshots/0.8.0-rc.2/semantic.js';
10
+ import { resolveComponentReferences as resolveRc3ComponentReferences } from './snapshots/0.8.0-rc.3/semantic.js';
8
11
  import {
9
12
  mcpExtensionCatalogue,
10
13
  mcpExtensionMaturity
@@ -18,7 +21,14 @@ const snapshots = Object.freeze({
18
21
  [draft3.specification]: draft3,
19
22
  [draft4.specification]: draft4,
20
23
  [rc1.specification]: rc1,
21
- [rc2.specification]: rc2
24
+ [rc2.specification]: rc2,
25
+ [rc3.specification]: rc3
26
+ });
27
+
28
+ const componentResolvers = Object.freeze({
29
+ '0.8.0-rc.1': resolveRc1ComponentReferences,
30
+ '0.8.0-rc.2': resolveRc2ComponentReferences,
31
+ '0.8.0-rc.3': resolveRc3ComponentReferences
22
32
  });
23
33
 
24
34
  const schemaUris = Object.freeze({
@@ -27,7 +37,8 @@ const schemaUris = Object.freeze({
27
37
  '0.8.0-draft.3': 'https://mcpdesc.org/schema/0.8.0.json',
28
38
  '0.8.0-draft.4': 'https://mcpdesc.org/schema/mcp-description/0.8.0-draft.4.json',
29
39
  '0.8.0-rc.1': 'https://mcpdesc.org/schema/mcp-description/0.8.0-rc.1.json',
30
- '0.8.0-rc.2': 'https://mcpdesc.org/schema/mcp-description/0.8.0-rc.2.json'
40
+ '0.8.0-rc.2': 'https://mcpdesc.org/schema/mcp-description/0.8.0-rc.2.json',
41
+ '0.8.0-rc.3': 'https://mcpdesc.org/schema/mcp-description/0.8.0-rc.3.json'
31
42
  });
32
43
 
33
44
  export const supportedSpecifications = Object.freeze(Object.keys(snapshots));
@@ -158,9 +169,9 @@ export function resolveMcpDescriptionComponentReferences(document, options) {
158
169
  if (!options || typeof options !== 'object' || Array.isArray(options) || !Object.hasOwn(options, 'specification')) {
159
170
  throw new TypeError('options.specification is required');
160
171
  }
161
- if (options.specification !== '0.8.0-rc.1') {
172
+ if (!Object.hasOwn(componentResolvers, options.specification)) {
162
173
  throw new RangeError(`Component reference resolution does not support specification: ${String(options.specification)}`);
163
174
  }
164
175
 
165
- return resolveRc1ComponentReferences(document);
176
+ return componentResolvers[options.specification](document);
166
177
  }
@@ -62,6 +62,7 @@ function componentDiagnostic(code, rel, message, path) {
62
62
  export function resolveComponentReferences(document, rel = 'document') {
63
63
  const resolved = structuredClone(document);
64
64
  const diagnostics = [];
65
+ const provenance = [];
65
66
  let substitutions = 0;
66
67
 
67
68
  function resolve(reference, expectedNamespace, path, stack = []) {
@@ -75,7 +76,7 @@ export function resolveComponentReferences(document, rel = 'document') {
75
76
  `must target #/components/${expectedNamespace}, not #/components/${namespace}`,
76
77
  path
77
78
  ));
78
- return reference;
79
+ return { value: reference };
79
80
  }
80
81
 
81
82
  const key = `${namespace}/${name}`;
@@ -86,7 +87,7 @@ export function resolveComponentReferences(document, rel = 'document') {
86
87
  `forms a cycle through ${[...stack, key].join(' -> ')}`,
87
88
  path
88
89
  ));
89
- return reference;
90
+ return { value: reference };
90
91
  }
91
92
 
92
93
  const target = document?.components?.[namespace]?.[name];
@@ -97,18 +98,29 @@ export function resolveComponentReferences(document, rel = 'document') {
97
98
  `targets missing component ${JSON.stringify(reference.$componentRef)}`,
98
99
  path
99
100
  ));
100
- return reference;
101
+ return { value: reference };
101
102
  }
102
103
  substitutions += 1;
103
104
  return isReferenceObject(target)
104
105
  ? resolve(target, expectedNamespace, path, [...stack, key])
105
- : structuredClone(target);
106
+ : {
107
+ value: structuredClone(target),
108
+ targetPath: ['components', namespace, name]
109
+ };
110
+ }
111
+
112
+ function substitute(reference, expectedNamespace, path, stack = []) {
113
+ const result = resolve(reference, expectedNamespace, path, stack);
114
+ if (result.targetPath) {
115
+ provenance.push({ referencePath: path, targetPath: result.targetPath });
116
+ }
117
+ return result.value;
106
118
  }
107
119
 
108
120
  for (const namespace of componentNamespaces) {
109
121
  for (const [name, value] of Object.entries(document?.components?.[namespace] ?? {})) {
110
122
  if (isReferenceObject(value)) {
111
- resolved.components[namespace][name] = resolve(value, namespace, ['components', namespace, name], [`${namespace}/${name}`]);
123
+ resolved.components[namespace][name] = substitute(value, namespace, ['components', namespace, name], [`${namespace}/${name}`]);
112
124
  }
113
125
  }
114
126
  }
@@ -124,7 +136,7 @@ export function resolveComponentReferences(document, rel = 'document') {
124
136
  if (collection === 'tools') {
125
137
  for (const field of ['inputSchema', 'outputSchema']) {
126
138
  if (isReferenceObject(declaration[field])) {
127
- resolvedDeclaration[field] = resolve(declaration[field], 'schemas', [collection, declarationIndex, field]);
139
+ resolvedDeclaration[field] = substitute(declaration[field], 'schemas', [collection, declarationIndex, field]);
128
140
  }
129
141
  }
130
142
  }
@@ -138,7 +150,7 @@ export function resolveComponentReferences(document, rel = 'document') {
138
150
  : 'promptExamples';
139
151
  for (const [name, example] of Object.entries(declaration.examples ?? {})) {
140
152
  if (isReferenceObject(example)) {
141
- resolvedDeclaration.examples[name] = resolve(
153
+ resolvedDeclaration.examples[name] = substitute(
142
154
  example,
143
155
  exampleNamespace,
144
156
  [collection, declarationIndex, 'examples', name]
@@ -152,7 +164,7 @@ export function resolveComponentReferences(document, rel = 'document') {
152
164
  for (const [declarationIndex, declaration] of (document?.[collection] ?? []).entries()) {
153
165
  for (const [elicitationIndex, elicitation] of (declaration.elicitations ?? []).entries()) {
154
166
  if (isReferenceObject(elicitation.requestedSchema)) {
155
- resolved[collection][declarationIndex].elicitations[elicitationIndex].requestedSchema = resolve(
167
+ resolved[collection][declarationIndex].elicitations[elicitationIndex].requestedSchema = substitute(
156
168
  elicitation.requestedSchema,
157
169
  'schemas',
158
170
  [collection, declarationIndex, 'elicitations', elicitationIndex, 'requestedSchema']
@@ -162,7 +174,7 @@ export function resolveComponentReferences(document, rel = 'document') {
162
174
  }
163
175
  }
164
176
 
165
- return { document: resolved, diagnostics, substitutions };
177
+ return { document: resolved, diagnostics, substitutions, provenance };
166
178
  }
167
179
 
168
180
  function structuralPath(document, error) {