@kya-os/mcp-i-core 1.7.0 → 1.8.1

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.
Files changed (121) hide show
  1. package/README.md +1 -1
  2. package/dist/conformance/capability-verifier-adapter.d.ts +66 -0
  3. package/dist/conformance/capability-verifier-adapter.js +61 -0
  4. package/dist/conformance/index.d.ts +10 -0
  5. package/dist/conformance/index.js +19 -0
  6. package/dist/conformance/validate-level2.d.ts +203 -0
  7. package/dist/conformance/validate-level2.js +294 -0
  8. package/dist/conformance/vc-jwt-verifier.d.ts +55 -0
  9. package/dist/conformance/vc-jwt-verifier.js +147 -0
  10. package/dist/delegation/did-web-resolver.d.ts +60 -0
  11. package/dist/delegation/did-web-resolver.js +129 -0
  12. package/dist/delegation/outbound-proof.d.ts +3 -2
  13. package/dist/delegation/outbound-proof.js +3 -2
  14. package/dist/delegation/utils.d.ts +3 -1
  15. package/dist/index.d.ts +9 -63
  16. package/dist/index.js +22 -161
  17. package/dist/utils/base58.d.ts +4 -28
  18. package/dist/utils/base58.js +18 -98
  19. package/dist/utils/base64.d.ts +4 -29
  20. package/dist/utils/base64.js +18 -167
  21. package/dist/utils/did-helpers.d.ts +4 -165
  22. package/dist/utils/did-helpers.js +18 -217
  23. package/package.json +5 -7
  24. package/dist/auth/handshake.d.ts +0 -119
  25. package/dist/auth/handshake.js +0 -250
  26. package/dist/auth/index.d.ts +0 -6
  27. package/dist/auth/index.js +0 -11
  28. package/dist/auth/types.d.ts +0 -46
  29. package/dist/auth/types.js +0 -10
  30. package/dist/cache/oauth-config-cache.d.ts +0 -69
  31. package/dist/cache/oauth-config-cache.js +0 -76
  32. package/dist/cache/tool-protection-cache.d.ts +0 -89
  33. package/dist/cache/tool-protection-cache.js +0 -118
  34. package/dist/compliance/index.d.ts +0 -8
  35. package/dist/compliance/index.js +0 -24
  36. package/dist/compliance/schema-registry.d.ts +0 -50
  37. package/dist/compliance/schema-registry.js +0 -438
  38. package/dist/compliance/schema-verifier.d.ts +0 -110
  39. package/dist/compliance/schema-verifier.js +0 -518
  40. package/dist/config/remote-config.d.ts +0 -173
  41. package/dist/config/remote-config.js +0 -253
  42. package/dist/config.d.ts +0 -182
  43. package/dist/config.js +0 -148
  44. package/dist/identity/idp-token-resolver.d.ts +0 -69
  45. package/dist/identity/idp-token-resolver.js +0 -136
  46. package/dist/identity/idp-token-storage.interface.d.ts +0 -80
  47. package/dist/identity/idp-token-storage.interface.js +0 -14
  48. package/dist/identity/user-did-manager.d.ts +0 -228
  49. package/dist/identity/user-did-manager.js +0 -356
  50. package/dist/logging/index.d.ts +0 -5
  51. package/dist/logging/index.js +0 -10
  52. package/dist/logging/logger.d.ts +0 -33
  53. package/dist/logging/logger.js +0 -104
  54. package/dist/proof/generator.d.ts +0 -109
  55. package/dist/proof/generator.js +0 -236
  56. package/dist/proof/index.d.ts +0 -5
  57. package/dist/proof/index.js +0 -11
  58. package/dist/providers/base.d.ts +0 -95
  59. package/dist/providers/base.js +0 -47
  60. package/dist/providers/memory.d.ts +0 -42
  61. package/dist/providers/memory.js +0 -121
  62. package/dist/runtime/audit-logger.d.ts +0 -37
  63. package/dist/runtime/audit-logger.js +0 -9
  64. package/dist/runtime/base.d.ts +0 -375
  65. package/dist/runtime/base.js +0 -1491
  66. package/dist/runtime/ext-apps-constants.d.ts +0 -14
  67. package/dist/runtime/ext-apps-constants.js +0 -17
  68. package/dist/services/access-control.service.d.ts +0 -138
  69. package/dist/services/access-control.service.js +0 -721
  70. package/dist/services/authorization/authorization-registry.d.ts +0 -29
  71. package/dist/services/authorization/authorization-registry.js +0 -58
  72. package/dist/services/authorization/types.d.ts +0 -53
  73. package/dist/services/authorization/types.js +0 -10
  74. package/dist/services/batch-delegation.service.d.ts +0 -53
  75. package/dist/services/batch-delegation.service.js +0 -95
  76. package/dist/services/crypto.service.d.ts +0 -69
  77. package/dist/services/crypto.service.js +0 -225
  78. package/dist/services/delegation-error-formatter.d.ts +0 -124
  79. package/dist/services/delegation-error-formatter.js +0 -214
  80. package/dist/services/errors.d.ts +0 -49
  81. package/dist/services/errors.js +0 -66
  82. package/dist/services/index.d.ts +0 -9
  83. package/dist/services/index.js +0 -15
  84. package/dist/services/oauth-config.service.d.ts +0 -53
  85. package/dist/services/oauth-config.service.js +0 -141
  86. package/dist/services/oauth-provider-registry.d.ts +0 -88
  87. package/dist/services/oauth-provider-registry.js +0 -128
  88. package/dist/services/oauth-service.d.ts +0 -122
  89. package/dist/services/oauth-service.js +0 -442
  90. package/dist/services/oauth-token-retrieval.service.d.ts +0 -49
  91. package/dist/services/oauth-token-retrieval.service.js +0 -150
  92. package/dist/services/policy.service.d.ts +0 -130
  93. package/dist/services/policy.service.js +0 -399
  94. package/dist/services/proof-verifier.d.ts +0 -98
  95. package/dist/services/proof-verifier.js +0 -319
  96. package/dist/services/provider-resolver.d.ts +0 -89
  97. package/dist/services/provider-resolver.js +0 -196
  98. package/dist/services/provider-validator.d.ts +0 -55
  99. package/dist/services/provider-validator.js +0 -135
  100. package/dist/services/session-registration.service.d.ts +0 -80
  101. package/dist/services/session-registration.service.js +0 -174
  102. package/dist/services/storage.service.d.ts +0 -116
  103. package/dist/services/storage.service.js +0 -411
  104. package/dist/services/tool-context-builder.d.ts +0 -74
  105. package/dist/services/tool-context-builder.js +0 -215
  106. package/dist/services/tool-protection.service.d.ts +0 -193
  107. package/dist/services/tool-protection.service.js +0 -736
  108. package/dist/session/index.d.ts +0 -5
  109. package/dist/session/index.js +0 -11
  110. package/dist/session/manager.d.ts +0 -113
  111. package/dist/session/manager.js +0 -273
  112. package/dist/types/oauth-required-error.d.ts +0 -46
  113. package/dist/types/oauth-required-error.js +0 -42
  114. package/dist/types/tool-protection.d.ts +0 -144
  115. package/dist/types/tool-protection.js +0 -48
  116. package/dist/utils/cors.d.ts +0 -44
  117. package/dist/utils/cors.js +0 -71
  118. package/dist/utils/index.d.ts +0 -8
  119. package/dist/utils/index.js +0 -24
  120. package/dist/utils/storage-keys.d.ts +0 -120
  121. package/dist/utils/storage-keys.js +0 -217
package/README.md CHANGED
@@ -151,7 +151,7 @@ import { MCPIRuntimeBaseV2 } from '@kya-os/mcp-i-core';
151
151
  class MyPlatformRuntime extends MCPIRuntimeBaseV2 {
152
152
  constructor() {
153
153
  super({
154
- cryptoProvider: new MyeCryptoProvider(),
154
+ cryptoProvider: new MyCryptoProvider(),
155
155
  identityProvider: new MyIdentityProvider(),
156
156
  storageProvider: new MyStorageProvider(),
157
157
  nonceCacheProvider: new MyNonceCacheProvider(),
@@ -0,0 +1,66 @@
1
+ /**
2
+ * CapabilityVerifier Adapter
3
+ *
4
+ * Adapts {@link verifyCapabilities} into the small, duck-typed verifier shape an
5
+ * entity-card consumer expects: given a list of capabilities and a verification
6
+ * context, return which capability names are `verified` (backed by a verified
7
+ * attestation) and which are `unverified` (self-declared or failed).
8
+ *
9
+ * The capability and verifier types are defined locally rather than imported
10
+ * from `@kya-os/mcp`: mcp-i-core pins `@kya-os/mcp@1.6.1`, which predates the
11
+ * entity-card export, so importing it would not resolve.
12
+ *
13
+ * Mapping:
14
+ * - bare-string capability → `unverified` (Level 1, self-declared)
15
+ * - L2 object capability → checked via {@link verifyCapabilities}; its
16
+ * `valid` names become `verified`, its `invalid` names become `unverified`.
17
+ */
18
+ import { type CapabilityVCSignatureVerifier } from './validate-level2.js';
19
+ import type { DIDResolver } from '../delegation/vc-verifier.js';
20
+ /**
21
+ * Duck-typed capability. A bare string is a Level 1 self-declared capability;
22
+ * an object with a `name` (and optional `attestations`) is a Level 2 attested
23
+ * capability.
24
+ */
25
+ export type DuckTypedCapability = string | {
26
+ name: string;
27
+ attestations?: readonly unknown[];
28
+ [key: string]: unknown;
29
+ };
30
+ /** Context describing whose capabilities are being checked, and against whom. */
31
+ export interface CapabilityVerificationContext {
32
+ /** The subject (agent) DID the capabilities are claimed for. */
33
+ subjectDid: string;
34
+ /** Issuer DIDs trusted to attest capabilities. */
35
+ trustedIssuers: readonly string[];
36
+ }
37
+ /** Partition of capability names by verification outcome. */
38
+ export interface CapabilityVerificationResult {
39
+ /** Capability names backed by a verified attestation. */
40
+ verified: string[];
41
+ /** Capability names that are self-declared or failed verification. */
42
+ unverified: string[];
43
+ }
44
+ /** The duck-typed verifier returned by {@link createCapabilityVerifier}. */
45
+ export type CapabilityVerifier = (capabilities: readonly DuckTypedCapability[], ctx: CapabilityVerificationContext) => Promise<CapabilityVerificationResult>;
46
+ /** Options for {@link createCapabilityVerifier}. */
47
+ export interface CreateCapabilityVerifierOptions {
48
+ /**
49
+ * Signature verifier for Level 2 attestations. Defaults to a VC-JWT verifier
50
+ * (see {@link createVcJwtSignatureVerifier}).
51
+ */
52
+ signatureVerifier?: CapabilityVCSignatureVerifier;
53
+ /**
54
+ * DID resolver for the default signature verifier. Ignored when a
55
+ * `signatureVerifier` is supplied.
56
+ */
57
+ didResolver?: DIDResolver;
58
+ }
59
+ /**
60
+ * Create a capability verifier.
61
+ *
62
+ * @param options - Optional signature-verifier / DID-resolver overrides.
63
+ * @returns A verifier mapping capabilities to `{ verified, unverified }`.
64
+ */
65
+ export declare function createCapabilityVerifier(options?: CreateCapabilityVerifierOptions): CapabilityVerifier;
66
+ //# sourceMappingURL=capability-verifier-adapter.d.ts.map
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /**
3
+ * CapabilityVerifier Adapter
4
+ *
5
+ * Adapts {@link verifyCapabilities} into the small, duck-typed verifier shape an
6
+ * entity-card consumer expects: given a list of capabilities and a verification
7
+ * context, return which capability names are `verified` (backed by a verified
8
+ * attestation) and which are `unverified` (self-declared or failed).
9
+ *
10
+ * The capability and verifier types are defined locally rather than imported
11
+ * from `@kya-os/mcp`: mcp-i-core pins `@kya-os/mcp@1.6.1`, which predates the
12
+ * entity-card export, so importing it would not resolve.
13
+ *
14
+ * Mapping:
15
+ * - bare-string capability → `unverified` (Level 1, self-declared)
16
+ * - L2 object capability → checked via {@link verifyCapabilities}; its
17
+ * `valid` names become `verified`, its `invalid` names become `unverified`.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.createCapabilityVerifier = createCapabilityVerifier;
21
+ const validate_level2_js_1 = require("./validate-level2.js");
22
+ const vc_jwt_verifier_js_1 = require("./vc-jwt-verifier.js");
23
+ /**
24
+ * Create a capability verifier.
25
+ *
26
+ * @param options - Optional signature-verifier / DID-resolver overrides.
27
+ * @returns A verifier mapping capabilities to `{ verified, unverified }`.
28
+ */
29
+ function createCapabilityVerifier(options = {}) {
30
+ const signatureVerifier = options.signatureVerifier ??
31
+ (0, vc_jwt_verifier_js_1.createVcJwtSignatureVerifier)({ didResolver: options.didResolver });
32
+ return async (capabilities, ctx) => {
33
+ const list = Array.isArray(capabilities) ? capabilities : [];
34
+ const bareStrings = [];
35
+ const attested = [];
36
+ for (const capability of list) {
37
+ if (typeof capability === 'string') {
38
+ bareStrings.push(capability);
39
+ }
40
+ else {
41
+ attested.push(capability);
42
+ }
43
+ }
44
+ const result = await (0, validate_level2_js_1.verifyCapabilities)(attested, {
45
+ agentDid: ctx.subjectDid,
46
+ trustedIssuers: [...ctx.trustedIssuers],
47
+ signatureVerifier,
48
+ });
49
+ const verified = result.valid;
50
+ const verifiedSet = new Set(verified);
51
+ // A capability verified via its L2 attestation must not also appear as
52
+ // unverified — e.g. when the input mixes a bare-string and an attested
53
+ // object for the same name. Exclude already-verified names, and dedupe.
54
+ const unverified = Array.from(new Set([
55
+ ...bareStrings,
56
+ ...result.invalid.map((entry) => entry.capability),
57
+ ])).filter((name) => !verifiedSet.has(name));
58
+ return { verified, unverified };
59
+ };
60
+ }
61
+ //# sourceMappingURL=capability-verifier-adapter.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * MCP-I Conformance Level helpers.
3
+ *
4
+ * See docs/conformance-levels for the spec page (introduced in
5
+ * modelcontextprotocol-identity#46).
6
+ */
7
+ export { verifyCapabilities, validateLevel2, DEFAULT_TRUSTED_ISSUERS, type CapabilityName, type CapabilityAttestation, type CapabilityAttestationCredential, type Level2Capability, type ManifestCapabilities, type CapabilityVCSignatureVerifier, type CapabilityStatusListResolver, type ValidateLevel2Options, type ValidateLevel2Result, type InvalidCapability, } from './validate-level2.js';
8
+ export { createVcJwtSignatureVerifier, createCombinedDidResolver, type VcJwtVerifierOptions, } from './vc-jwt-verifier.js';
9
+ export { createCapabilityVerifier, type DuckTypedCapability, type CapabilityVerificationContext, type CapabilityVerificationResult, type CapabilityVerifier, type CreateCapabilityVerifierOptions, } from './capability-verifier-adapter.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ /**
3
+ * MCP-I Conformance Level helpers.
4
+ *
5
+ * See docs/conformance-levels for the spec page (introduced in
6
+ * modelcontextprotocol-identity#46).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createCapabilityVerifier = exports.createCombinedDidResolver = exports.createVcJwtSignatureVerifier = exports.DEFAULT_TRUSTED_ISSUERS = exports.validateLevel2 = exports.verifyCapabilities = void 0;
10
+ var validate_level2_js_1 = require("./validate-level2.js");
11
+ Object.defineProperty(exports, "verifyCapabilities", { enumerable: true, get: function () { return validate_level2_js_1.verifyCapabilities; } });
12
+ Object.defineProperty(exports, "validateLevel2", { enumerable: true, get: function () { return validate_level2_js_1.validateLevel2; } });
13
+ Object.defineProperty(exports, "DEFAULT_TRUSTED_ISSUERS", { enumerable: true, get: function () { return validate_level2_js_1.DEFAULT_TRUSTED_ISSUERS; } });
14
+ var vc_jwt_verifier_js_1 = require("./vc-jwt-verifier.js");
15
+ Object.defineProperty(exports, "createVcJwtSignatureVerifier", { enumerable: true, get: function () { return vc_jwt_verifier_js_1.createVcJwtSignatureVerifier; } });
16
+ Object.defineProperty(exports, "createCombinedDidResolver", { enumerable: true, get: function () { return vc_jwt_verifier_js_1.createCombinedDidResolver; } });
17
+ var capability_verifier_adapter_js_1 = require("./capability-verifier-adapter.js");
18
+ Object.defineProperty(exports, "createCapabilityVerifier", { enumerable: true, get: function () { return capability_verifier_adapter_js_1.createCapabilityVerifier; } });
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Level 2 Capability Attestation Validator
3
+ *
4
+ * Implements the Level 2 conformance behavior described in the MCP-I spec:
5
+ * https://github.com/modelcontextprotocol-identity/modelcontextprotocol-identity/pull/46
6
+ *
7
+ * For each declared capability in the agent manifest, verify at least one
8
+ * attached Verifiable Credential where ALL of the following hold:
9
+ * - VC signature verifies (delegated to a platform-specific verifier)
10
+ * - issuer DID is in the trusted issuer registry
11
+ * - credentialSubject.id matches the agent DID
12
+ * - credentialSubject.capability matches the declared capability name
13
+ * - validUntil is in the future (and validFrom is in the past, when present)
14
+ *
15
+ * Revocation (StatusList2021) is OPTIONAL for v1 and not enforced here.
16
+ *
17
+ * Capabilities that fail validation are NOT thrown — they are returned in the
18
+ * `invalid` array along with the reasons, so callers can surface diagnostics
19
+ * to UI without a hard failure.
20
+ *
21
+ * The legacy Level 1 `string[]` form is accepted as input: every entry is
22
+ * returned under `valid` along with a global warning so callers can surface
23
+ * the missing attestation context.
24
+ */
25
+ /**
26
+ * Default trusted issuer registry.
27
+ *
28
+ * Matches the spec default: `did:web:knowthat.ai`. Callers MAY substitute or
29
+ * extend this allowlist via {@link ValidateLevel2Options.trustedIssuers}.
30
+ */
31
+ export declare const DEFAULT_TRUSTED_ISSUERS: readonly string[];
32
+ /**
33
+ * Capability name as declared in the agent manifest (e.g. `payments.transfer`).
34
+ */
35
+ export type CapabilityName = string;
36
+ /**
37
+ * Minimal shape of a `CapabilityAttestationCredential` per the spec.
38
+ *
39
+ * Only the fields the validator inspects are typed strictly — additional
40
+ * fields (`@context`, `type`, `proof`, etc.) are passed through.
41
+ */
42
+ export interface CapabilityAttestationCredential {
43
+ '@context'?: string | string[];
44
+ type?: string | string[];
45
+ issuer: string | {
46
+ id: string;
47
+ };
48
+ validFrom?: string;
49
+ validUntil?: string;
50
+ credentialSubject: {
51
+ id: string;
52
+ capability: string;
53
+ scope?: Record<string, unknown>;
54
+ };
55
+ proof?: unknown;
56
+ [key: string]: unknown;
57
+ }
58
+ /**
59
+ * Single attestation entry attached to a Level 2 capability.
60
+ *
61
+ * The `vc` field MAY be a compact JWT string (per the spec example) OR a
62
+ * pre-parsed credential object (convenient for tests and callers that have
63
+ * already decoded the JWT).
64
+ *
65
+ * `issuer` and `type` are OPTIONAL envelope metadata hints — they are
66
+ * informational only and play NO role in trust evaluation. The validator
67
+ * derives the authoritative issuer from `vc.issuer` (the signed credential
68
+ * itself), so a wrong or attacker-supplied envelope `issuer` cannot grant
69
+ * trust. If you need to record the credential type, prefer `vc.type` inside
70
+ * the VC, which is covered by the signature.
71
+ */
72
+ export interface CapabilityAttestation {
73
+ vc: string | CapabilityAttestationCredential;
74
+ /**
75
+ * Optional metadata hint. NOT consulted during validation — `vc.issuer` is
76
+ * authoritative for the trusted-issuer check.
77
+ */
78
+ issuer?: string;
79
+ /**
80
+ * Optional metadata hint. NOT consulted during validation.
81
+ */
82
+ type?: string;
83
+ }
84
+ /**
85
+ * Level 2 capability entry.
86
+ */
87
+ export interface Level2Capability {
88
+ name: CapabilityName;
89
+ attestations: CapabilityAttestation[];
90
+ }
91
+ /**
92
+ * Manifest capabilities — accepts either the legacy Level 1 string array or
93
+ * the Level 2 object array. Mixed entries within a single Level 2 array are
94
+ * tolerated (string entries are dropped with a per-capability warning).
95
+ */
96
+ export type ManifestCapabilities = readonly CapabilityName[] | readonly (Level2Capability | CapabilityName)[];
97
+ /**
98
+ * Pluggable signature verifier for capability attestations.
99
+ *
100
+ * Cryptographic verification (Ed25519, ES256, etc.) is platform-specific, so
101
+ * the validator delegates the signature check. Implementations should resolve
102
+ * the issuer DID, locate the verification method, and verify the proof.
103
+ *
104
+ * If no verifier is supplied, the validator emits a single global warning and
105
+ * treats every signature as valid — useful for development but unsafe in
106
+ * production. This mirrors `DelegationCredentialVerifier`'s behavior when no
107
+ * `signatureVerifier` is provided.
108
+ */
109
+ export type CapabilityVCSignatureVerifier = (vc: CapabilityAttestationCredential, rawJwt: string | undefined) => Promise<{
110
+ valid: boolean;
111
+ reason?: string;
112
+ }>;
113
+ /**
114
+ * Reserved hook for StatusList2021-based revocation checks.
115
+ *
116
+ * Not invoked in v1. Reserved so callers can pass a resolver today and have
117
+ * it activate transparently once revocation enforcement lands.
118
+ */
119
+ export type CapabilityStatusListResolver = (vc: CapabilityAttestationCredential) => Promise<{
120
+ revoked: boolean;
121
+ reason?: string;
122
+ }>;
123
+ /**
124
+ * Validator options.
125
+ */
126
+ export interface ValidateLevel2Options {
127
+ /**
128
+ * The agent DID being validated. `credentialSubject.id` on each VC MUST
129
+ * match this exactly.
130
+ */
131
+ agentDid: string;
132
+ /**
133
+ * Trusted issuer DID allowlist. Defaults to {@link DEFAULT_TRUSTED_ISSUERS}.
134
+ * If supplied, replaces (does NOT merge with) the default — callers wanting
135
+ * to extend the default should spread it themselves.
136
+ */
137
+ trustedIssuers?: readonly string[];
138
+ /**
139
+ * Platform-specific signature verifier. If omitted, signatures are accepted
140
+ * unverified and a global warning is emitted.
141
+ */
142
+ signatureVerifier?: CapabilityVCSignatureVerifier;
143
+ /**
144
+ * StatusList2021 revocation hook. Reserved — not invoked in v1.
145
+ *
146
+ * TODO(v1.x): wire this into the per-attestation check loop once the
147
+ * StatusList2021 contract is finalized for capability attestations.
148
+ */
149
+ statusListResolver?: CapabilityStatusListResolver;
150
+ /**
151
+ * Clock injection for tests. Defaults to `new Date()`.
152
+ */
153
+ now?: () => Date;
154
+ }
155
+ /**
156
+ * Per-capability failure record.
157
+ */
158
+ export interface InvalidCapability {
159
+ capability: CapabilityName;
160
+ reasons: string[];
161
+ }
162
+ /**
163
+ * Validator result.
164
+ *
165
+ * - `valid` lists the capability names that passed validation.
166
+ * - `invalid` lists the capabilities that were declared but failed, with the
167
+ * accumulated reasons across every attached attestation.
168
+ * - `warnings` carries non-fatal diagnostics (e.g. "no signature verifier
169
+ * provided", or Level 1 input).
170
+ */
171
+ export interface ValidateLevel2Result {
172
+ valid: CapabilityName[];
173
+ invalid: InvalidCapability[];
174
+ warnings: string[];
175
+ }
176
+ /**
177
+ * Verify an MCP-I agent's declared capability attestations (Level 2).
178
+ *
179
+ * Named for the action it performs — verifying capability ATTESTATIONS — rather
180
+ * than the conformance level. {@link validateLevel2} remains exported as a
181
+ * non-breaking `@deprecated` alias.
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * const result = await verifyCapabilities(manifest.capabilities, {
186
+ * agentDid: 'did:web:securebank.example',
187
+ * trustedIssuers: ['did:web:knowthat.ai'],
188
+ * signatureVerifier: myEd25519Verifier,
189
+ * });
190
+ *
191
+ * console.log(result.valid); // ['payments.transfer']
192
+ * console.log(result.invalid); // [{ capability: 'accounts.read', reasons: [...] }]
193
+ * console.log(result.warnings); // []
194
+ * ```
195
+ */
196
+ export declare function verifyCapabilities(capabilities: unknown, options: ValidateLevel2Options): Promise<ValidateLevel2Result>;
197
+ /**
198
+ * @deprecated Renamed to {@link verifyCapabilities}. Retained as a non-breaking
199
+ * alias — the two are referentially identical. Prefer `verifyCapabilities` in
200
+ * new code; this alias will be removed in a future major.
201
+ */
202
+ export declare const validateLevel2: typeof verifyCapabilities;
203
+ //# sourceMappingURL=validate-level2.d.ts.map
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ /**
3
+ * Level 2 Capability Attestation Validator
4
+ *
5
+ * Implements the Level 2 conformance behavior described in the MCP-I spec:
6
+ * https://github.com/modelcontextprotocol-identity/modelcontextprotocol-identity/pull/46
7
+ *
8
+ * For each declared capability in the agent manifest, verify at least one
9
+ * attached Verifiable Credential where ALL of the following hold:
10
+ * - VC signature verifies (delegated to a platform-specific verifier)
11
+ * - issuer DID is in the trusted issuer registry
12
+ * - credentialSubject.id matches the agent DID
13
+ * - credentialSubject.capability matches the declared capability name
14
+ * - validUntil is in the future (and validFrom is in the past, when present)
15
+ *
16
+ * Revocation (StatusList2021) is OPTIONAL for v1 and not enforced here.
17
+ *
18
+ * Capabilities that fail validation are NOT thrown — they are returned in the
19
+ * `invalid` array along with the reasons, so callers can surface diagnostics
20
+ * to UI without a hard failure.
21
+ *
22
+ * The legacy Level 1 `string[]` form is accepted as input: every entry is
23
+ * returned under `valid` along with a global warning so callers can surface
24
+ * the missing attestation context.
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.validateLevel2 = exports.DEFAULT_TRUSTED_ISSUERS = void 0;
28
+ exports.verifyCapabilities = verifyCapabilities;
29
+ const utils_js_1 = require("../delegation/utils.js");
30
+ /**
31
+ * Default trusted issuer registry.
32
+ *
33
+ * Matches the spec default: `did:web:knowthat.ai`. Callers MAY substitute or
34
+ * extend this allowlist via {@link ValidateLevel2Options.trustedIssuers}.
35
+ */
36
+ exports.DEFAULT_TRUSTED_ISSUERS = [
37
+ 'did:web:knowthat.ai',
38
+ ];
39
+ /**
40
+ * Verify an MCP-I agent's declared capability attestations (Level 2).
41
+ *
42
+ * Named for the action it performs — verifying capability ATTESTATIONS — rather
43
+ * than the conformance level. {@link validateLevel2} remains exported as a
44
+ * non-breaking `@deprecated` alias.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const result = await verifyCapabilities(manifest.capabilities, {
49
+ * agentDid: 'did:web:securebank.example',
50
+ * trustedIssuers: ['did:web:knowthat.ai'],
51
+ * signatureVerifier: myEd25519Verifier,
52
+ * });
53
+ *
54
+ * console.log(result.valid); // ['payments.transfer']
55
+ * console.log(result.invalid); // [{ capability: 'accounts.read', reasons: [...] }]
56
+ * console.log(result.warnings); // []
57
+ * ```
58
+ */
59
+ async function verifyCapabilities(capabilities, options) {
60
+ const result = {
61
+ valid: [],
62
+ invalid: [],
63
+ warnings: [],
64
+ };
65
+ if (!Array.isArray(capabilities)) {
66
+ result.warnings.push('capabilities is not an array — no capabilities validated');
67
+ return result;
68
+ }
69
+ // Level 1 string[] passthrough: every entry is a bare capability name.
70
+ if (capabilities.length > 0 && capabilities.every((c) => typeof c === 'string')) {
71
+ result.valid.push(...capabilities);
72
+ result.warnings.push('Level 1 self-declared — no attestation verified');
73
+ return result;
74
+ }
75
+ const trustedIssuers = new Set(options.trustedIssuers ?? exports.DEFAULT_TRUSTED_ISSUERS);
76
+ const now = (options.now?.() ?? new Date()).getTime();
77
+ if (!options.signatureVerifier) {
78
+ result.warnings.push('No signatureVerifier provided — VC signatures accepted without cryptographic verification');
79
+ }
80
+ for (const entry of capabilities) {
81
+ // Mixed-array case: a bare string in a Level 2 array is unattested per
82
+ // spec ("MUST treat any capability supplied as a string … as unattested
83
+ // and therefore ignore it"). We surface it as invalid with a clear reason
84
+ // so callers can diagnose, rather than dropping silently.
85
+ if (typeof entry === 'string') {
86
+ result.invalid.push({
87
+ capability: entry,
88
+ reasons: ['capability declared as bare string with no attestations'],
89
+ });
90
+ continue;
91
+ }
92
+ if (!isLevel2Capability(entry)) {
93
+ // Entry has a name but malformed/missing attestations — report in invalid.
94
+ if (typeof entry === 'object' &&
95
+ entry !== null &&
96
+ typeof entry.name === 'string') {
97
+ result.invalid.push({
98
+ capability: entry.name,
99
+ reasons: ['capability has invalid or missing attestations array'],
100
+ });
101
+ }
102
+ else {
103
+ result.warnings.push(`Skipping capability entry of unrecognized shape: ${safeStringify(entry)}`);
104
+ }
105
+ continue;
106
+ }
107
+ const capabilityResult = await validateCapability(entry, {
108
+ agentDid: options.agentDid,
109
+ trustedIssuers,
110
+ now,
111
+ signatureVerifier: options.signatureVerifier,
112
+ });
113
+ if (capabilityResult.valid) {
114
+ result.valid.push(entry.name);
115
+ }
116
+ else {
117
+ result.invalid.push({
118
+ capability: entry.name,
119
+ reasons: capabilityResult.reasons,
120
+ });
121
+ }
122
+ }
123
+ return result;
124
+ }
125
+ /**
126
+ * @deprecated Renamed to {@link verifyCapabilities}. Retained as a non-breaking
127
+ * alias — the two are referentially identical. Prefer `verifyCapabilities` in
128
+ * new code; this alias will be removed in a future major.
129
+ */
130
+ exports.validateLevel2 = verifyCapabilities;
131
+ async function validateCapability(capability, ctx) {
132
+ const reasons = [];
133
+ if (!Array.isArray(capability.attestations) || capability.attestations.length === 0) {
134
+ return {
135
+ valid: false,
136
+ reasons: ['no attestations attached'],
137
+ };
138
+ }
139
+ for (let i = 0; i < capability.attestations.length; i++) {
140
+ const attestation = capability.attestations[i];
141
+ const attestationLabel = `attestation[${i}]`;
142
+ const parsed = decodeAttestationVC(attestation);
143
+ if (!parsed.ok) {
144
+ reasons.push(`${attestationLabel}: ${parsed.reason}`);
145
+ continue;
146
+ }
147
+ const { vc, rawJwt } = parsed;
148
+ const issuerDid = extractIssuerDid(vc);
149
+ if (!issuerDid) {
150
+ reasons.push(`${attestationLabel}: VC missing issuer`);
151
+ continue;
152
+ }
153
+ if (!ctx.trustedIssuers.has(issuerDid)) {
154
+ reasons.push(`${attestationLabel}: issuer ${issuerDid} not in trusted registry`);
155
+ continue;
156
+ }
157
+ if (vc.credentialSubject?.id !== ctx.agentDid) {
158
+ reasons.push(`${attestationLabel}: credentialSubject.id ${vc.credentialSubject?.id ?? '(missing)'} does not match agent DID ${ctx.agentDid}`);
159
+ continue;
160
+ }
161
+ if (vc.credentialSubject?.capability !== capability.name) {
162
+ reasons.push(`${attestationLabel}: credentialSubject.capability ${vc.credentialSubject?.capability ?? '(missing)'} does not match declared capability ${capability.name}`);
163
+ continue;
164
+ }
165
+ const validUntilMs = vc.validUntil ? Date.parse(vc.validUntil) : NaN;
166
+ if (!vc.validUntil || Number.isNaN(validUntilMs)) {
167
+ reasons.push(`${attestationLabel}: VC missing or unparseable validUntil`);
168
+ continue;
169
+ }
170
+ if (validUntilMs <= ctx.now) {
171
+ reasons.push(`${attestationLabel}: VC expired (validUntil ${vc.validUntil})`);
172
+ continue;
173
+ }
174
+ if (vc.validFrom) {
175
+ const validFromMs = Date.parse(vc.validFrom);
176
+ if (Number.isNaN(validFromMs)) {
177
+ reasons.push(`${attestationLabel}: unparseable validFrom`);
178
+ continue;
179
+ }
180
+ if (validFromMs > ctx.now) {
181
+ reasons.push(`${attestationLabel}: VC not yet valid (validFrom ${vc.validFrom})`);
182
+ continue;
183
+ }
184
+ }
185
+ if (ctx.signatureVerifier) {
186
+ let sig;
187
+ try {
188
+ sig = await ctx.signatureVerifier(vc, rawJwt);
189
+ }
190
+ catch (err) {
191
+ const message = err instanceof Error ? err.message : String(err);
192
+ reasons.push(`${attestationLabel}: signature verifier threw: ${message}`);
193
+ continue;
194
+ }
195
+ if (!sig.valid) {
196
+ reasons.push(`${attestationLabel}: signature invalid${sig.reason ? ` (${sig.reason})` : ''}`);
197
+ continue;
198
+ }
199
+ }
200
+ // TODO(v1.x): when StatusList2021 hook lands, invoke
201
+ // ctx.statusListResolver here and reject revoked attestations. v1 skips
202
+ // revocation per the spec design doc.
203
+ return { valid: true, reasons: [] };
204
+ }
205
+ return { valid: false, reasons };
206
+ }
207
+ /**
208
+ * Decode an attestation `vc` field (string JWT or object) into a credential.
209
+ */
210
+ function decodeAttestationVC(attestation) {
211
+ if (attestation === null || typeof attestation !== 'object') {
212
+ return { ok: false, reason: 'attestation entry is not an object' };
213
+ }
214
+ const vc = attestation.vc;
215
+ if (typeof vc === 'string') {
216
+ const parsed = (0, utils_js_1.parseVCJWT)(vc);
217
+ if (!parsed) {
218
+ return { ok: false, reason: 'VC JWT could not be parsed' };
219
+ }
220
+ const inner = parsed.payload.vc;
221
+ if (!inner || typeof inner !== 'object') {
222
+ return { ok: false, reason: 'VC JWT payload missing vc claim' };
223
+ }
224
+ // Per W3C VC-JWT spec §6.3: promote JWT-level iss→vc.issuer and
225
+ // sub→vc.credentialSubject.id when absent from the embedded vc object so
226
+ // that externally-issued spec-compliant JWTs are not falsely rejected.
227
+ const merged = { ...inner };
228
+ if (!merged.issuer && typeof parsed.payload.iss === 'string') {
229
+ merged.issuer = parsed.payload.iss;
230
+ }
231
+ if (typeof parsed.payload.sub === 'string' &&
232
+ merged.credentialSubject &&
233
+ typeof merged.credentialSubject === 'object' &&
234
+ !merged.credentialSubject.id) {
235
+ merged.credentialSubject = {
236
+ ...merged.credentialSubject,
237
+ id: parsed.payload.sub,
238
+ };
239
+ }
240
+ // Per W3C VC-JWT spec §6.3: also promote exp→validUntil and nbf→validFrom
241
+ // when absent from the embedded vc object, so externally-issued JWTs that
242
+ // carry validity only in the standard JWT claims are not falsely rejected.
243
+ // JWT exp/nbf are Unix seconds (NumericDate). Guard the conversion: an
244
+ // out-of-range claim yields an Invalid Date, which we leave unset (→ the VC
245
+ // fails closed at validation) rather than letting toISOString() throw a
246
+ // RangeError out of this otherwise-total decoder.
247
+ if (!merged.validUntil && typeof parsed.payload.exp === 'number') {
248
+ const d = new Date(parsed.payload.exp * 1000);
249
+ if (!Number.isNaN(d.getTime()))
250
+ merged.validUntil = d.toISOString();
251
+ }
252
+ if (!merged.validFrom && typeof parsed.payload.nbf === 'number') {
253
+ const d = new Date(parsed.payload.nbf * 1000);
254
+ if (!Number.isNaN(d.getTime()))
255
+ merged.validFrom = d.toISOString();
256
+ }
257
+ return {
258
+ ok: true,
259
+ vc: merged,
260
+ rawJwt: vc,
261
+ };
262
+ }
263
+ if (vc && typeof vc === 'object') {
264
+ return {
265
+ ok: true,
266
+ vc: vc,
267
+ rawJwt: undefined,
268
+ };
269
+ }
270
+ return { ok: false, reason: 'attestation.vc missing or invalid type' };
271
+ }
272
+ function extractIssuerDid(vc) {
273
+ if (typeof vc.issuer === 'string')
274
+ return vc.issuer;
275
+ if (vc.issuer && typeof vc.issuer === 'object' && typeof vc.issuer.id === 'string') {
276
+ return vc.issuer.id;
277
+ }
278
+ return undefined;
279
+ }
280
+ function isLevel2Capability(value) {
281
+ return (typeof value === 'object' &&
282
+ value !== null &&
283
+ typeof value.name === 'string' &&
284
+ Array.isArray(value.attestations));
285
+ }
286
+ function safeStringify(value) {
287
+ try {
288
+ return JSON.stringify(value);
289
+ }
290
+ catch {
291
+ return String(value);
292
+ }
293
+ }
294
+ //# sourceMappingURL=validate-level2.js.map