@mcpdesc/validator 0.7.1 → 0.9.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
@@ -8,6 +8,38 @@ Dates for published releases are the UTC publication dates recorded by npm.
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.9.0] - 2026-09-04
12
+
13
+ ### Added
14
+
15
+ - Added the immutable `0.8.0-rc.2` selector from specification tag
16
+ `v0.8.0-rc.2`, including its manifest-verified runtime, schema, and frozen
17
+ fixture corpus.
18
+ - Exported the RC.2 MCP extension catalogue metadata and maturity classifier.
19
+
20
+ ### Changed
21
+
22
+ - RC.2 preserves structurally valid pre-standard server extension maps and
23
+ reports `extensions-not-supported-by-version` as a warning while retaining
24
+ strict client extension requirements and independent reserved-identifier
25
+ diagnostics. This is a compatible validator relaxation; RC.1 and earlier
26
+ selectors are unchanged.
27
+
28
+ ## [0.8.0] - 2026-09-03
29
+
30
+ ### Added
31
+
32
+ - Added RC.1-only `resolveMcpDescriptionComponentReferences` to the default,
33
+ browser, and standalone entries, returning the existing resolved clone,
34
+ diagnostics, and substitution count plus deterministic terminal-target
35
+ provenance.
36
+
37
+ ### Changed
38
+
39
+ - Recorded the maintainer-approved additive provenance correction to the RC.1
40
+ snapshot implementation. Its schema, conformance results, diagnostics, and
41
+ frozen fixtures remain unchanged.
42
+
11
43
  ## [0.7.1] - 2026-09-02
12
44
 
13
45
  ### Changed
@@ -79,7 +111,9 @@ Dates for published releases are the UTC publication dates recorded by npm.
79
111
  - Added synchronous structural and semantic validation for parsed JavaScript values with deterministic diagnostics and exact selector dispatch.
80
112
  - Added ESM browser support, TypeScript declarations, embedded schema provenance, frozen fixtures, and package-content checks.
81
113
 
82
- [Unreleased]: https://github.com/mcpdesc/core/compare/validator-v0.7.1...HEAD
114
+ [Unreleased]: https://github.com/mcpdesc/core/compare/validator-v0.9.0...HEAD
115
+ [0.9.0]: https://github.com/mcpdesc/core/compare/validator-v0.8.0...validator-v0.9.0
116
+ [0.8.0]: https://github.com/mcpdesc/core/compare/validator-v0.7.1...validator-v0.8.0
83
117
  [0.7.1]: https://github.com/mcpdesc/core/compare/validator-v0.7.0...validator-v0.7.1
84
118
  [0.7.0]: https://github.com/mcpdesc/core/releases/tag/validator-v0.7.0
85
119
  [0.6.0]: https://github.com/mcpdesc/mcpdesc-specification/compare/validator-v0.5.0...validator-v0.6.0
package/README.md CHANGED
@@ -25,7 +25,7 @@ npm install @mcpdesc/validator
25
25
  import { validateMcpDescription } from '@mcpdesc/validator';
26
26
 
27
27
  const result = validateMcpDescription(parsedDocument, {
28
- specification: '0.8.0-rc.1',
28
+ specification: '0.8.0-rc.2',
29
29
  });
30
30
 
31
31
  for (const diagnostic of result.diagnostics) {
@@ -44,10 +44,11 @@ a later draft is published.
44
44
 
45
45
  ## Supported snapshots
46
46
 
47
- Version `0.7.1` supports these immutable snapshots, newest first:
47
+ Version `0.8.0` supports these immutable snapshots, newest first:
48
48
 
49
49
  | Selector | Tag | Embedded schema SHA-256 |
50
50
  |---|---|---|
51
+ | `0.8.0-rc.2` | `v0.8.0-rc.2` | `40f6775dde052224114e91d6aa484d826eecf56b77f7ac87b4cf707ffbcb6ce8` |
51
52
  | `0.8.0-rc.1` | `v0.8.0-rc.1` | `936a0f24ade501fcabf3d6498c0440c445daa672a575573a35954cee49430ac4` |
52
53
  | `0.8.0-draft.4` | `v0.8.0-draft.4` | `93ed03f74059b5b3ce7509a96b59161bdab2c3cf7734397a9bec5a7588d0b03b` |
53
54
  | `0.8.0-draft.3` | `v0.8.0-draft.3` | `8823c1f1946360b2a44d00920e2092e5e4acd139a1964befad4eb0bf3ce96002` |
@@ -131,10 +132,21 @@ Structural paths start with AJV's instance path. A `required` error appends its
131
132
 
132
133
  ## Support metadata
133
134
 
134
- 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`, and `0.8.0-rc.1`; the protocol-version export is the deduplicated union supported by those snapshots.
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.
136
+
137
+ RC.2 also exports the frozen `mcpExtensionCatalogue` and
138
+ `mcpExtensionMaturity` classifier. The catalogue pins its authoritative source,
139
+ effective date, and official or experimental identifier assignments. Catalogue
140
+ recognition establishes authority and maturity only; it does not validate
141
+ extension-specific settings.
135
142
 
136
143
  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.
137
144
 
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.
149
+
138
150
  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.
139
151
 
140
152
  ## Snapshot lifecycle
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';
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';
2
2
 
3
3
  export type SupportedProtocolVersion =
4
4
  | '2024-11-05'
@@ -25,6 +25,36 @@ export interface ValidateMcpDescriptionOptions {
25
25
  specification: McpDescriptionSpecification;
26
26
  }
27
27
 
28
+ export interface McpDescriptionComponentReferenceProvenance {
29
+ readonly referencePath: Array<string | number>;
30
+ readonly targetPath: Array<string | number>;
31
+ }
32
+
33
+ export interface McpDescriptionComponentResolutionResult {
34
+ readonly document: unknown;
35
+ readonly diagnostics: McpDescriptionDiagnostic[];
36
+ readonly substitutions: number;
37
+ readonly provenance: McpDescriptionComponentReferenceProvenance[];
38
+ }
39
+
40
+ export interface ResolveMcpDescriptionComponentReferencesOptions {
41
+ readonly specification: '0.8.0-rc.1';
42
+ }
43
+
44
+ export type McpExtensionMaturity = 'official' | 'experimental' | 'uncatalogued';
45
+
46
+ export interface McpExtensionCatalogue {
47
+ readonly effectiveDate: '2026-09-04';
48
+ readonly source: 'https://modelcontextprotocol.io/extensions/overview';
49
+ readonly officialIdentifiers: readonly [
50
+ 'io.modelcontextprotocol/enterprise-managed-authorization',
51
+ 'io.modelcontextprotocol/oauth-client-credentials',
52
+ 'io.modelcontextprotocol/tasks',
53
+ 'io.modelcontextprotocol/ui'
54
+ ];
55
+ readonly experimentalIdentifiers: readonly [];
56
+ }
57
+
28
58
  export interface SpecificationProvenance {
29
59
  readonly '0.8.0-draft.1': {
30
60
  readonly snapshotTag: 'v0.8.0-draft.1';
@@ -51,6 +81,11 @@ export interface SpecificationProvenance {
51
81
  readonly schemaUri: 'https://mcpdesc.org/schema/mcp-description/0.8.0-rc.1.json';
52
82
  readonly schemaSha256: '936a0f24ade501fcabf3d6498c0440c445daa672a575573a35954cee49430ac4';
53
83
  };
84
+ readonly '0.8.0-rc.2': {
85
+ readonly snapshotTag: 'v0.8.0-rc.2';
86
+ readonly schemaUri: 'https://mcpdesc.org/schema/mcp-description/0.8.0-rc.2.json';
87
+ readonly schemaSha256: '40f6775dde052224114e91d6aa484d826eecf56b77f7ac87b4cf707ffbcb6ce8';
88
+ };
54
89
  }
55
90
 
56
91
  export interface ResolveMcpDescriptionSpecificationOptions {
@@ -74,7 +109,7 @@ export type McpDescriptionSpecificationResolution =
74
109
  | ResolvedMcpDescriptionSpecification
75
110
  | UnresolvedMcpDescriptionSpecification;
76
111
 
77
- 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'];
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'];
78
113
 
79
114
  export declare const supportedProtocolVersions: readonly [
80
115
  '2024-11-05',
@@ -85,6 +120,8 @@ export declare const supportedProtocolVersions: readonly [
85
120
  ];
86
121
 
87
122
  export declare const specificationProvenance: Readonly<SpecificationProvenance>;
123
+ export declare const mcpExtensionCatalogue: Readonly<McpExtensionCatalogue>;
124
+ export declare function mcpExtensionMaturity(identifier: string): McpExtensionMaturity;
88
125
 
89
126
  export declare function resolveMcpDescriptionSpecification(
90
127
  document: unknown,
@@ -94,4 +131,9 @@ export declare function resolveMcpDescriptionSpecification(
94
131
  export declare function validateMcpDescription(
95
132
  document: unknown,
96
133
  options: ValidateMcpDescriptionOptions
97
- ): McpDescriptionValidationResult;
134
+ ): McpDescriptionValidationResult;
135
+
136
+ export declare function resolveMcpDescriptionComponentReferences(
137
+ document: unknown,
138
+ options: ResolveMcpDescriptionComponentReferencesOptions
139
+ ): McpDescriptionComponentResolutionResult;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcpdesc/validator",
3
- "version": "0.7.1",
3
+ "version": "0.9.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
@@ -3,13 +3,22 @@ import * as draft2 from './snapshots/0.8.0-draft.2/index.js';
3
3
  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
+ import * as rc2 from './snapshots/0.8.0-rc.2/index.js';
7
+ import { resolveComponentReferences as resolveRc1ComponentReferences } from './snapshots/0.8.0-rc.1/semantic.js';
8
+ import {
9
+ mcpExtensionCatalogue,
10
+ mcpExtensionMaturity
11
+ } from './snapshots/0.8.0-rc.2/semantic.js';
12
+
13
+ export { mcpExtensionCatalogue, mcpExtensionMaturity };
6
14
 
7
15
  const snapshots = Object.freeze({
8
16
  [draft1.specification]: draft1,
9
17
  [draft2.specification]: draft2,
10
18
  [draft3.specification]: draft3,
11
19
  [draft4.specification]: draft4,
12
- [rc1.specification]: rc1
20
+ [rc1.specification]: rc1,
21
+ [rc2.specification]: rc2
13
22
  });
14
23
 
15
24
  const schemaUris = Object.freeze({
@@ -17,7 +26,8 @@ const schemaUris = Object.freeze({
17
26
  '0.8.0-draft.2': 'https://mcpdesc.org/schema/0.8.0.json',
18
27
  '0.8.0-draft.3': 'https://mcpdesc.org/schema/0.8.0.json',
19
28
  '0.8.0-draft.4': 'https://mcpdesc.org/schema/mcp-description/0.8.0-draft.4.json',
20
- '0.8.0-rc.1': 'https://mcpdesc.org/schema/mcp-description/0.8.0-rc.1.json'
29
+ '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'
21
31
  });
22
32
 
23
33
  export const supportedSpecifications = Object.freeze(Object.keys(snapshots));
@@ -142,4 +152,15 @@ export function validateMcpDescription(document, options) {
142
152
  }
143
153
 
144
154
  return snapshots[options.specification].validate(document);
155
+ }
156
+
157
+ export function resolveMcpDescriptionComponentReferences(document, options) {
158
+ if (!options || typeof options !== 'object' || Array.isArray(options) || !Object.hasOwn(options, 'specification')) {
159
+ throw new TypeError('options.specification is required');
160
+ }
161
+ if (options.specification !== '0.8.0-rc.1') {
162
+ throw new RangeError(`Component reference resolution does not support specification: ${String(options.specification)}`);
163
+ }
164
+
165
+ return resolveRc1ComponentReferences(document);
145
166
  }
@@ -61,11 +61,12 @@ function componentDiagnostic(code, rel, message, path) {
61
61
  export function resolveComponentReferences(document, rel = 'document') {
62
62
  const resolved = structuredClone(document);
63
63
  const diagnostics = [];
64
+ const provenance = [];
64
65
  let substitutions = 0;
65
66
 
66
67
  function resolve(reference, expectedNamespace, path, stack = []) {
67
68
  const match = /^#\/components\/([^/]+)\/([^/]+)$/.exec(reference?.$componentRef ?? '');
68
- if (!match) return reference;
69
+ if (!match) return { value: reference };
69
70
  const [, namespace, name] = match;
70
71
  if (namespace !== expectedNamespace) {
71
72
  diagnostics.push(componentDiagnostic(
@@ -74,7 +75,7 @@ export function resolveComponentReferences(document, rel = 'document') {
74
75
  `must target #/components/${expectedNamespace}, not #/components/${namespace}`,
75
76
  path
76
77
  ));
77
- return reference;
78
+ return { value: reference };
78
79
  }
79
80
 
80
81
  const key = `${namespace}/${name}`;
@@ -85,7 +86,7 @@ export function resolveComponentReferences(document, rel = 'document') {
85
86
  `forms a cycle through ${[...stack, key].join(' -> ')}`,
86
87
  path
87
88
  ));
88
- return reference;
89
+ return { value: reference };
89
90
  }
90
91
 
91
92
  const target = document?.components?.[namespace]?.[name];
@@ -96,18 +97,29 @@ export function resolveComponentReferences(document, rel = 'document') {
96
97
  `targets missing component ${JSON.stringify(reference.$componentRef)}`,
97
98
  path
98
99
  ));
99
- return reference;
100
+ return { value: reference };
100
101
  }
101
102
  substitutions += 1;
102
103
  return isReferenceObject(target)
103
104
  ? resolve(target, expectedNamespace, path, [...stack, key])
104
- : structuredClone(target);
105
+ : {
106
+ value: structuredClone(target),
107
+ targetPath: ['components', namespace, name]
108
+ };
109
+ }
110
+
111
+ function substitute(reference, expectedNamespace, path, stack = []) {
112
+ const result = resolve(reference, expectedNamespace, path, stack);
113
+ if (result.targetPath) {
114
+ provenance.push({ referencePath: path, targetPath: result.targetPath });
115
+ }
116
+ return result.value;
105
117
  }
106
118
 
107
119
  for (const namespace of componentNamespaces) {
108
120
  for (const [name, value] of Object.entries(document?.components?.[namespace] ?? {})) {
109
121
  if (isReferenceObject(value)) {
110
- resolved.components[namespace][name] = resolve(value, namespace, ['components', namespace, name], [`${namespace}/${name}`]);
122
+ resolved.components[namespace][name] = substitute(value, namespace, ['components', namespace, name], [`${namespace}/${name}`]);
111
123
  }
112
124
  }
113
125
  }
@@ -123,7 +135,7 @@ export function resolveComponentReferences(document, rel = 'document') {
123
135
  if (collection === 'tools') {
124
136
  for (const field of ['inputSchema', 'outputSchema']) {
125
137
  if (isReferenceObject(declaration[field])) {
126
- resolvedDeclaration[field] = resolve(declaration[field], 'schemas', [collection, declarationIndex, field]);
138
+ resolvedDeclaration[field] = substitute(declaration[field], 'schemas', [collection, declarationIndex, field]);
127
139
  }
128
140
  }
129
141
  }
@@ -137,7 +149,7 @@ export function resolveComponentReferences(document, rel = 'document') {
137
149
  : 'promptExamples';
138
150
  for (const [name, example] of Object.entries(declaration.examples ?? {})) {
139
151
  if (isReferenceObject(example)) {
140
- resolvedDeclaration.examples[name] = resolve(
152
+ resolvedDeclaration.examples[name] = substitute(
141
153
  example,
142
154
  exampleNamespace,
143
155
  [collection, declarationIndex, 'examples', name]
@@ -151,7 +163,7 @@ export function resolveComponentReferences(document, rel = 'document') {
151
163
  for (const [declarationIndex, declaration] of (document?.[collection] ?? []).entries()) {
152
164
  for (const [elicitationIndex, elicitation] of (declaration.elicitations ?? []).entries()) {
153
165
  if (isReferenceObject(elicitation.requestedSchema)) {
154
- resolved[collection][declarationIndex].elicitations[elicitationIndex].requestedSchema = resolve(
166
+ resolved[collection][declarationIndex].elicitations[elicitationIndex].requestedSchema = substitute(
155
167
  elicitation.requestedSchema,
156
168
  'schemas',
157
169
  [collection, declarationIndex, 'elicitations', elicitationIndex, 'requestedSchema']
@@ -161,7 +173,7 @@ export function resolveComponentReferences(document, rel = 'document') {
161
173
  }
162
174
  }
163
175
 
164
- return { document: resolved, diagnostics, substitutions };
176
+ return { document: resolved, diagnostics, substitutions, provenance };
165
177
  }
166
178
 
167
179
  function structuralPath(document, error) {