@mcp-i/core 0.1.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/LICENSE +21 -0
- package/README.md +390 -0
- package/dist/auth/handshake.d.ts +104 -0
- package/dist/auth/handshake.d.ts.map +1 -0
- package/dist/auth/handshake.js +230 -0
- package/dist/auth/handshake.js.map +1 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/types.d.ts +31 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +7 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/delegation/audience-validator.d.ts +9 -0
- package/dist/delegation/audience-validator.d.ts.map +1 -0
- package/dist/delegation/audience-validator.js +17 -0
- package/dist/delegation/audience-validator.js.map +1 -0
- package/dist/delegation/bitstring.d.ts +37 -0
- package/dist/delegation/bitstring.d.ts.map +1 -0
- package/dist/delegation/bitstring.js +117 -0
- package/dist/delegation/bitstring.js.map +1 -0
- package/dist/delegation/cascading-revocation.d.ts +45 -0
- package/dist/delegation/cascading-revocation.d.ts.map +1 -0
- package/dist/delegation/cascading-revocation.js +148 -0
- package/dist/delegation/cascading-revocation.js.map +1 -0
- package/dist/delegation/delegation-graph.d.ts +49 -0
- package/dist/delegation/delegation-graph.d.ts.map +1 -0
- package/dist/delegation/delegation-graph.js +99 -0
- package/dist/delegation/delegation-graph.js.map +1 -0
- package/dist/delegation/did-key-resolver.d.ts +64 -0
- package/dist/delegation/did-key-resolver.d.ts.map +1 -0
- package/dist/delegation/did-key-resolver.js +154 -0
- package/dist/delegation/did-key-resolver.js.map +1 -0
- package/dist/delegation/did-web-resolver.d.ts +83 -0
- package/dist/delegation/did-web-resolver.d.ts.map +1 -0
- package/dist/delegation/did-web-resolver.js +218 -0
- package/dist/delegation/did-web-resolver.js.map +1 -0
- package/dist/delegation/index.d.ts +21 -0
- package/dist/delegation/index.d.ts.map +1 -0
- package/dist/delegation/index.js +21 -0
- package/dist/delegation/index.js.map +1 -0
- package/dist/delegation/outbound-headers.d.ts +81 -0
- package/dist/delegation/outbound-headers.d.ts.map +1 -0
- package/dist/delegation/outbound-headers.js +139 -0
- package/dist/delegation/outbound-headers.js.map +1 -0
- package/dist/delegation/outbound-proof.d.ts +43 -0
- package/dist/delegation/outbound-proof.d.ts.map +1 -0
- package/dist/delegation/outbound-proof.js +52 -0
- package/dist/delegation/outbound-proof.js.map +1 -0
- package/dist/delegation/statuslist-manager.d.ts +44 -0
- package/dist/delegation/statuslist-manager.d.ts.map +1 -0
- package/dist/delegation/statuslist-manager.js +126 -0
- package/dist/delegation/statuslist-manager.js.map +1 -0
- package/dist/delegation/storage/memory-graph-storage.d.ts +70 -0
- package/dist/delegation/storage/memory-graph-storage.d.ts.map +1 -0
- package/dist/delegation/storage/memory-graph-storage.js +145 -0
- package/dist/delegation/storage/memory-graph-storage.js.map +1 -0
- package/dist/delegation/storage/memory-statuslist-storage.d.ts +19 -0
- package/dist/delegation/storage/memory-statuslist-storage.d.ts.map +1 -0
- package/dist/delegation/storage/memory-statuslist-storage.js +33 -0
- package/dist/delegation/storage/memory-statuslist-storage.js.map +1 -0
- package/dist/delegation/utils.d.ts +49 -0
- package/dist/delegation/utils.d.ts.map +1 -0
- package/dist/delegation/utils.js +131 -0
- package/dist/delegation/utils.js.map +1 -0
- package/dist/delegation/vc-issuer.d.ts +56 -0
- package/dist/delegation/vc-issuer.d.ts.map +1 -0
- package/dist/delegation/vc-issuer.js +80 -0
- package/dist/delegation/vc-issuer.js.map +1 -0
- package/dist/delegation/vc-verifier.d.ts +112 -0
- package/dist/delegation/vc-verifier.d.ts.map +1 -0
- package/dist/delegation/vc-verifier.js +280 -0
- package/dist/delegation/vc-verifier.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/index.d.ts +2 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +2 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/logger.d.ts +23 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +82 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/middleware/index.d.ts +7 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +7 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/with-mcpi.d.ts +152 -0
- package/dist/middleware/with-mcpi.d.ts.map +1 -0
- package/dist/middleware/with-mcpi.js +472 -0
- package/dist/middleware/with-mcpi.js.map +1 -0
- package/dist/proof/errors.d.ts +49 -0
- package/dist/proof/errors.d.ts.map +1 -0
- package/dist/proof/errors.js +61 -0
- package/dist/proof/errors.js.map +1 -0
- package/dist/proof/generator.d.ts +65 -0
- package/dist/proof/generator.d.ts.map +1 -0
- package/dist/proof/generator.js +163 -0
- package/dist/proof/generator.js.map +1 -0
- package/dist/proof/index.d.ts +4 -0
- package/dist/proof/index.d.ts.map +1 -0
- package/dist/proof/index.js +4 -0
- package/dist/proof/index.js.map +1 -0
- package/dist/proof/verifier.d.ts +108 -0
- package/dist/proof/verifier.d.ts.map +1 -0
- package/dist/proof/verifier.js +299 -0
- package/dist/proof/verifier.js.map +1 -0
- package/dist/providers/base.d.ts +64 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +19 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +3 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/memory.d.ts +33 -0
- package/dist/providers/memory.d.ts.map +1 -0
- package/dist/providers/memory.js +102 -0
- package/dist/providers/memory.js.map +1 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +2 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/manager.d.ts +77 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +251 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/types/protocol.d.ts +320 -0
- package/dist/types/protocol.d.ts.map +1 -0
- package/dist/types/protocol.js +229 -0
- package/dist/types/protocol.js.map +1 -0
- package/dist/utils/base58.d.ts +31 -0
- package/dist/utils/base58.d.ts.map +1 -0
- package/dist/utils/base58.js +104 -0
- package/dist/utils/base58.js.map +1 -0
- package/dist/utils/base64.d.ts +13 -0
- package/dist/utils/base64.d.ts.map +1 -0
- package/dist/utils/base64.js +99 -0
- package/dist/utils/base64.js.map +1 -0
- package/dist/utils/crypto-service.d.ts +37 -0
- package/dist/utils/crypto-service.d.ts.map +1 -0
- package/dist/utils/crypto-service.js +153 -0
- package/dist/utils/crypto-service.js.map +1 -0
- package/dist/utils/did-helpers.d.ts +156 -0
- package/dist/utils/did-helpers.d.ts.map +1 -0
- package/dist/utils/did-helpers.js +193 -0
- package/dist/utils/did-helpers.js.map +1 -0
- package/dist/utils/ed25519-constants.d.ts +18 -0
- package/dist/utils/ed25519-constants.d.ts.map +1 -0
- package/dist/utils/ed25519-constants.js +21 -0
- package/dist/utils/ed25519-constants.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +105 -0
- package/src/__tests__/integration/full-flow.test.ts +362 -0
- package/src/__tests__/providers/base.test.ts +173 -0
- package/src/__tests__/providers/memory.test.ts +332 -0
- package/src/__tests__/utils/mock-providers.ts +319 -0
- package/src/__tests__/utils/node-crypto-provider.ts +93 -0
- package/src/auth/handshake.ts +411 -0
- package/src/auth/index.ts +11 -0
- package/src/auth/types.ts +40 -0
- package/src/delegation/__tests__/audience-validator.test.ts +110 -0
- package/src/delegation/__tests__/bitstring.test.ts +346 -0
- package/src/delegation/__tests__/cascading-revocation.test.ts +624 -0
- package/src/delegation/__tests__/delegation-graph.test.ts +623 -0
- package/src/delegation/__tests__/did-key-resolver.test.ts +265 -0
- package/src/delegation/__tests__/did-web-resolver.test.ts +467 -0
- package/src/delegation/__tests__/outbound-headers.test.ts +230 -0
- package/src/delegation/__tests__/outbound-proof.test.ts +179 -0
- package/src/delegation/__tests__/statuslist-manager.test.ts +515 -0
- package/src/delegation/__tests__/utils.test.ts +185 -0
- package/src/delegation/__tests__/vc-issuer.test.ts +487 -0
- package/src/delegation/__tests__/vc-verifier.test.ts +1029 -0
- package/src/delegation/audience-validator.ts +24 -0
- package/src/delegation/bitstring.ts +160 -0
- package/src/delegation/cascading-revocation.ts +224 -0
- package/src/delegation/delegation-graph.ts +143 -0
- package/src/delegation/did-key-resolver.ts +181 -0
- package/src/delegation/did-web-resolver.ts +270 -0
- package/src/delegation/index.ts +33 -0
- package/src/delegation/outbound-headers.ts +193 -0
- package/src/delegation/outbound-proof.ts +90 -0
- package/src/delegation/statuslist-manager.ts +219 -0
- package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
- package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
- package/src/delegation/storage/memory-graph-storage.ts +178 -0
- package/src/delegation/storage/memory-statuslist-storage.ts +42 -0
- package/src/delegation/utils.ts +189 -0
- package/src/delegation/vc-issuer.ts +137 -0
- package/src/delegation/vc-verifier.ts +440 -0
- package/src/index.ts +264 -0
- package/src/logging/__tests__/logger.test.ts +366 -0
- package/src/logging/index.ts +6 -0
- package/src/logging/logger.ts +91 -0
- package/src/middleware/__tests__/with-mcpi.test.ts +504 -0
- package/src/middleware/index.ts +16 -0
- package/src/middleware/with-mcpi.ts +766 -0
- package/src/proof/__tests__/proof-generator.test.ts +483 -0
- package/src/proof/__tests__/verifier.test.ts +488 -0
- package/src/proof/errors.ts +75 -0
- package/src/proof/generator.ts +255 -0
- package/src/proof/index.ts +22 -0
- package/src/proof/verifier.ts +449 -0
- package/src/providers/base.ts +68 -0
- package/src/providers/index.ts +15 -0
- package/src/providers/memory.ts +130 -0
- package/src/session/__tests__/session-manager.test.ts +342 -0
- package/src/session/index.ts +7 -0
- package/src/session/manager.ts +332 -0
- package/src/types/protocol.ts +596 -0
- package/src/utils/__tests__/base58.test.ts +281 -0
- package/src/utils/__tests__/base64.test.ts +239 -0
- package/src/utils/__tests__/crypto-service.test.ts +530 -0
- package/src/utils/__tests__/did-helpers.test.ts +156 -0
- package/src/utils/base58.ts +115 -0
- package/src/utils/base64.ts +116 -0
- package/src/utils/crypto-service.ts +209 -0
- package/src/utils/did-helpers.ts +210 -0
- package/src/utils/ed25519-constants.ts +23 -0
- package/src/utils/index.ts +9 -0
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP-I Protocol Types
|
|
3
|
+
*
|
|
4
|
+
* Inlined type definitions for the MCP-I protocol reference implementation.
|
|
5
|
+
* All types are pure TypeScript — no external dependencies.
|
|
6
|
+
*
|
|
7
|
+
* Related Spec: MCP-I §3, §4, §5, §6
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// CRISP Delegation Constraints (MCP-I §4.2)
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
export interface CrispBudget {
|
|
15
|
+
unit: 'USD' | 'ops' | 'points';
|
|
16
|
+
cap: number;
|
|
17
|
+
window?: {
|
|
18
|
+
kind: 'rolling' | 'fixed';
|
|
19
|
+
durationSec: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CrispScope {
|
|
24
|
+
resource: string;
|
|
25
|
+
matcher: 'exact' | 'prefix' | 'regex';
|
|
26
|
+
constraints?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface DelegationConstraints {
|
|
30
|
+
notBefore?: number;
|
|
31
|
+
notAfter?: number;
|
|
32
|
+
scopes?: string[];
|
|
33
|
+
audience?: string | string[];
|
|
34
|
+
crisp?: {
|
|
35
|
+
budget?: CrispBudget;
|
|
36
|
+
scopes: CrispScope[];
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
};
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Delegation Record (MCP-I §4.1)
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
export type DelegationStatus = 'active' | 'revoked' | 'expired';
|
|
47
|
+
|
|
48
|
+
export interface DelegationRecord {
|
|
49
|
+
id: string;
|
|
50
|
+
issuerDid: string;
|
|
51
|
+
subjectDid: string;
|
|
52
|
+
controller?: string;
|
|
53
|
+
vcId: string;
|
|
54
|
+
parentId?: string;
|
|
55
|
+
constraints: DelegationConstraints;
|
|
56
|
+
signature: string;
|
|
57
|
+
status: DelegationStatus;
|
|
58
|
+
createdAt?: number;
|
|
59
|
+
revokedAt?: number;
|
|
60
|
+
revokedReason?: string;
|
|
61
|
+
metadata?: Record<string, unknown>;
|
|
62
|
+
[key: string]: unknown;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// W3C Verifiable Credential types
|
|
67
|
+
// ============================================================================
|
|
68
|
+
|
|
69
|
+
export interface Proof {
|
|
70
|
+
type: string;
|
|
71
|
+
created?: string;
|
|
72
|
+
verificationMethod?: string;
|
|
73
|
+
proofPurpose?: string;
|
|
74
|
+
proofValue?: string;
|
|
75
|
+
jws?: string;
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface CredentialStatus {
|
|
80
|
+
id: string;
|
|
81
|
+
type: 'StatusList2021Entry';
|
|
82
|
+
statusPurpose: 'revocation' | 'suspension';
|
|
83
|
+
statusListIndex: string;
|
|
84
|
+
statusListCredential: string;
|
|
85
|
+
[key: string]: unknown;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface DelegationCredentialSubject {
|
|
89
|
+
id: string;
|
|
90
|
+
delegation: {
|
|
91
|
+
id: string;
|
|
92
|
+
issuerDid: string;
|
|
93
|
+
subjectDid: string;
|
|
94
|
+
userDid?: string;
|
|
95
|
+
userIdentifier?: string;
|
|
96
|
+
sessionId?: string;
|
|
97
|
+
scopes?: string[];
|
|
98
|
+
controller?: string;
|
|
99
|
+
parentId?: string;
|
|
100
|
+
constraints: DelegationConstraints;
|
|
101
|
+
status: DelegationStatus;
|
|
102
|
+
createdAt?: number;
|
|
103
|
+
metadata?: Record<string, unknown>;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface DelegationCredential {
|
|
108
|
+
'@context': (string | Record<string, unknown>)[];
|
|
109
|
+
id?: string;
|
|
110
|
+
type: string[];
|
|
111
|
+
issuer: string | { id: string; [key: string]: unknown };
|
|
112
|
+
issuanceDate: string;
|
|
113
|
+
expirationDate?: string;
|
|
114
|
+
credentialSubject: DelegationCredentialSubject;
|
|
115
|
+
credentialStatus?: CredentialStatus;
|
|
116
|
+
proof?: Proof;
|
|
117
|
+
[key: string]: unknown;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const DELEGATION_CREDENTIAL_CONTEXT =
|
|
121
|
+
'https://schema.modelcontextprotocol-identity.io/xmcp-i/credentials/delegation.v1.0.0.json' as const;
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// StatusList2021 (W3C)
|
|
125
|
+
// ============================================================================
|
|
126
|
+
|
|
127
|
+
export interface StatusList2021Credential {
|
|
128
|
+
'@context': (string | Record<string, unknown>)[];
|
|
129
|
+
id: string;
|
|
130
|
+
type: string[];
|
|
131
|
+
issuer: string | { id: string };
|
|
132
|
+
issuanceDate: string;
|
|
133
|
+
credentialSubject: {
|
|
134
|
+
id?: string;
|
|
135
|
+
type: 'StatusList2021';
|
|
136
|
+
statusPurpose: 'revocation' | 'suspension';
|
|
137
|
+
encodedList: string;
|
|
138
|
+
};
|
|
139
|
+
proof?: Record<string, unknown>;
|
|
140
|
+
[key: string]: unknown;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// Delegation VC utility functions
|
|
145
|
+
// ============================================================================
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Wrap a DelegationRecord in an unsigned W3C VC structure.
|
|
149
|
+
*/
|
|
150
|
+
export function wrapDelegationAsVC(
|
|
151
|
+
delegation: DelegationRecord,
|
|
152
|
+
options?: {
|
|
153
|
+
id?: string;
|
|
154
|
+
issuanceDate?: string;
|
|
155
|
+
expirationDate?: string;
|
|
156
|
+
credentialStatus?: CredentialStatus;
|
|
157
|
+
userDid?: string;
|
|
158
|
+
userIdentifier?: string;
|
|
159
|
+
sessionId?: string;
|
|
160
|
+
scopes?: string[];
|
|
161
|
+
}
|
|
162
|
+
): Omit<DelegationCredential, 'proof'> {
|
|
163
|
+
const now = new Date().toISOString();
|
|
164
|
+
const expirationDate = delegation.constraints.notAfter
|
|
165
|
+
? new Date(delegation.constraints.notAfter * 1000).toISOString()
|
|
166
|
+
: options?.expirationDate;
|
|
167
|
+
|
|
168
|
+
let issuanceDate = options?.issuanceDate || now;
|
|
169
|
+
if (!options?.issuanceDate && delegation.createdAt) {
|
|
170
|
+
issuanceDate = new Date(delegation.createdAt).toISOString();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const scopes = options?.scopes || delegation.constraints.scopes;
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
'@context': [
|
|
177
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
178
|
+
DELEGATION_CREDENTIAL_CONTEXT,
|
|
179
|
+
],
|
|
180
|
+
id: options?.id || delegation.vcId || `urn:uuid:${delegation.id}`,
|
|
181
|
+
type: ['VerifiableCredential', 'DelegationCredential'],
|
|
182
|
+
issuer: delegation.issuerDid,
|
|
183
|
+
issuanceDate,
|
|
184
|
+
...(expirationDate !== undefined && { expirationDate }),
|
|
185
|
+
credentialSubject: {
|
|
186
|
+
id: delegation.subjectDid,
|
|
187
|
+
delegation: {
|
|
188
|
+
id: delegation.id,
|
|
189
|
+
issuerDid: delegation.issuerDid,
|
|
190
|
+
subjectDid: delegation.subjectDid,
|
|
191
|
+
...(options?.userDid && { userDid: options.userDid }),
|
|
192
|
+
...(options?.userIdentifier && { userIdentifier: options.userIdentifier }),
|
|
193
|
+
...(options?.sessionId && { sessionId: options.sessionId }),
|
|
194
|
+
...(scopes && scopes.length > 0 && { scopes }),
|
|
195
|
+
...(delegation.controller !== undefined && { controller: delegation.controller }),
|
|
196
|
+
...(delegation.parentId !== undefined && { parentId: delegation.parentId }),
|
|
197
|
+
constraints: delegation.constraints,
|
|
198
|
+
status: delegation.status,
|
|
199
|
+
...(delegation.createdAt !== undefined && { createdAt: delegation.createdAt }),
|
|
200
|
+
...(delegation.metadata !== undefined && { metadata: delegation.metadata }),
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
...(options?.credentialStatus !== undefined && { credentialStatus: options.credentialStatus }),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Extract a DelegationRecord from a DelegationCredential.
|
|
209
|
+
*/
|
|
210
|
+
export function extractDelegationFromVC(vc: DelegationCredential): DelegationRecord {
|
|
211
|
+
const delegation = vc.credentialSubject.delegation;
|
|
212
|
+
|
|
213
|
+
let signature = '';
|
|
214
|
+
if (vc.proof) {
|
|
215
|
+
const proof = vc.proof as Record<string, unknown>;
|
|
216
|
+
signature = (proof['proofValue'] || proof['jws'] || proof['signatureValue'] || '') as string;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
id: delegation.id,
|
|
221
|
+
issuerDid: delegation.issuerDid,
|
|
222
|
+
subjectDid: delegation.subjectDid,
|
|
223
|
+
controller: delegation.controller,
|
|
224
|
+
vcId: vc.id || `vc:${delegation.id}`,
|
|
225
|
+
parentId: delegation.parentId,
|
|
226
|
+
constraints: delegation.constraints,
|
|
227
|
+
signature,
|
|
228
|
+
status: delegation.status,
|
|
229
|
+
createdAt: delegation.createdAt,
|
|
230
|
+
revokedAt: undefined,
|
|
231
|
+
revokedReason: undefined,
|
|
232
|
+
metadata: delegation.metadata,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Check if a DelegationCredential is expired.
|
|
238
|
+
*/
|
|
239
|
+
export function isDelegationCredentialExpired(vc: DelegationCredential): boolean {
|
|
240
|
+
if (vc.expirationDate) {
|
|
241
|
+
if (new Date(vc.expirationDate) < new Date()) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const delegation = vc.credentialSubject.delegation;
|
|
247
|
+
if (delegation.constraints.notAfter) {
|
|
248
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
249
|
+
if (nowSec > delegation.constraints.notAfter) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Check if a DelegationCredential is not yet valid.
|
|
259
|
+
*/
|
|
260
|
+
export function isDelegationCredentialNotYetValid(vc: DelegationCredential): boolean {
|
|
261
|
+
const delegation = vc.credentialSubject.delegation;
|
|
262
|
+
|
|
263
|
+
if (delegation.constraints.notBefore) {
|
|
264
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
265
|
+
if (nowSec < delegation.constraints.notBefore) {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Validate a DelegationCredential.
|
|
275
|
+
* Returns a Zod-compatible result shape.
|
|
276
|
+
*/
|
|
277
|
+
export function validateDelegationCredential(vc: unknown): {
|
|
278
|
+
success: boolean;
|
|
279
|
+
error?: { message: string };
|
|
280
|
+
data?: DelegationCredential;
|
|
281
|
+
} {
|
|
282
|
+
if (!vc || typeof vc !== 'object') {
|
|
283
|
+
return { success: false, error: { message: 'Not an object' } };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const v = vc as Record<string, unknown>;
|
|
287
|
+
|
|
288
|
+
// Check @context
|
|
289
|
+
if (!Array.isArray(v['@context']) || v['@context'].length === 0) {
|
|
290
|
+
return { success: false, error: { message: 'Missing or invalid @context' } };
|
|
291
|
+
}
|
|
292
|
+
if (v['@context'][0] !== 'https://www.w3.org/2018/credentials/v1') {
|
|
293
|
+
return { success: false, error: { message: 'First @context must be W3C VC context' } };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Check type
|
|
297
|
+
if (!Array.isArray(v['type'])) {
|
|
298
|
+
return { success: false, error: { message: 'Missing type array' } };
|
|
299
|
+
}
|
|
300
|
+
if (!v['type'].includes('VerifiableCredential') || !v['type'].includes('DelegationCredential')) {
|
|
301
|
+
return { success: false, error: { message: 'type must include VerifiableCredential and DelegationCredential' } };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Check issuer
|
|
305
|
+
if (!v['issuer'] || (typeof v['issuer'] !== 'string' && typeof v['issuer'] !== 'object')) {
|
|
306
|
+
return { success: false, error: { message: 'Missing or invalid issuer' } };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Check issuanceDate
|
|
310
|
+
if (!v['issuanceDate'] || typeof v['issuanceDate'] !== 'string') {
|
|
311
|
+
return { success: false, error: { message: 'Missing issuanceDate' } };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Check credentialSubject
|
|
315
|
+
const cs = v['credentialSubject'] as Record<string, unknown> | undefined;
|
|
316
|
+
if (!cs || typeof cs !== 'object') {
|
|
317
|
+
return { success: false, error: { message: 'Missing credentialSubject' } };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (!cs['id'] || typeof cs['id'] !== 'string') {
|
|
321
|
+
return { success: false, error: { message: 'credentialSubject.id missing' } };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const del = cs['delegation'] as Record<string, unknown> | undefined;
|
|
325
|
+
if (!del || typeof del !== 'object') {
|
|
326
|
+
return { success: false, error: { message: 'credentialSubject.delegation missing' } };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (!del['id'] || !del['issuerDid'] || !del['subjectDid'] || !del['constraints']) {
|
|
330
|
+
return { success: false, error: { message: 'delegation fields missing' } };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return { success: true, data: vc as DelegationCredential };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ============================================================================
|
|
337
|
+
// Handshake and Session (MCP-I §4.5–4.9)
|
|
338
|
+
// ============================================================================
|
|
339
|
+
|
|
340
|
+
export interface MCPClientInfo {
|
|
341
|
+
name: string;
|
|
342
|
+
title?: string;
|
|
343
|
+
version?: string;
|
|
344
|
+
platform?: string;
|
|
345
|
+
vendor?: string;
|
|
346
|
+
persistentId?: string;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export interface MCPClientSessionInfo extends MCPClientInfo {
|
|
350
|
+
clientId: string;
|
|
351
|
+
protocolVersion?: string;
|
|
352
|
+
capabilities?: Record<string, unknown>;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export interface HandshakeRequest {
|
|
356
|
+
nonce: string;
|
|
357
|
+
audience: string;
|
|
358
|
+
timestamp: number;
|
|
359
|
+
agentDid?: string;
|
|
360
|
+
clientInfo?: MCPClientInfo & { clientId?: string };
|
|
361
|
+
clientProtocolVersion?: string;
|
|
362
|
+
clientCapabilities?: Record<string, unknown>;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export type SessionIdentityState = 'anonymous' | 'authenticated';
|
|
366
|
+
|
|
367
|
+
export interface SessionContext {
|
|
368
|
+
sessionId: string;
|
|
369
|
+
audience: string;
|
|
370
|
+
nonce: string;
|
|
371
|
+
timestamp: number;
|
|
372
|
+
createdAt: number;
|
|
373
|
+
lastActivity: number;
|
|
374
|
+
ttlMinutes: number;
|
|
375
|
+
agentDid?: string;
|
|
376
|
+
serverDid?: string;
|
|
377
|
+
clientDid?: string;
|
|
378
|
+
userDid?: string;
|
|
379
|
+
clientInfo?: MCPClientSessionInfo;
|
|
380
|
+
identityState: SessionIdentityState;
|
|
381
|
+
oauthIdentity?: {
|
|
382
|
+
provider: string;
|
|
383
|
+
subject: string;
|
|
384
|
+
email?: string;
|
|
385
|
+
name?: string;
|
|
386
|
+
};
|
|
387
|
+
delegationRef?: string;
|
|
388
|
+
delegationChain?: string;
|
|
389
|
+
delegationScopes?: string[];
|
|
390
|
+
[key: string]: unknown;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Nonce cache interface for replay prevention.
|
|
395
|
+
*/
|
|
396
|
+
export interface NonceCache {
|
|
397
|
+
has(nonce: string, agentDid?: string): Promise<boolean>;
|
|
398
|
+
add(nonce: string, ttl: number, agentDid?: string): Promise<void>;
|
|
399
|
+
cleanup(): Promise<void>;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export const DEFAULT_SESSION_TTL_MINUTES = 30;
|
|
403
|
+
export const DEFAULT_TIMESTAMP_SKEW_SECONDS = 120;
|
|
404
|
+
export const NONCE_LENGTH_BYTES = 16;
|
|
405
|
+
|
|
406
|
+
// ============================================================================
|
|
407
|
+
// Proof types (MCP-I §5)
|
|
408
|
+
// ============================================================================
|
|
409
|
+
|
|
410
|
+
export interface ProofMeta {
|
|
411
|
+
did: string;
|
|
412
|
+
kid: string;
|
|
413
|
+
ts: number;
|
|
414
|
+
nonce: string;
|
|
415
|
+
audience: string;
|
|
416
|
+
sessionId: string;
|
|
417
|
+
requestHash: string;
|
|
418
|
+
responseHash: string;
|
|
419
|
+
scopeId?: string;
|
|
420
|
+
delegationRef?: string;
|
|
421
|
+
clientDid?: string;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export interface DetachedProof {
|
|
425
|
+
jws: string;
|
|
426
|
+
meta: ProofMeta;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export interface CanonicalHashes {
|
|
430
|
+
requestHash: string;
|
|
431
|
+
responseHash: string;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export interface AuditRecord {
|
|
435
|
+
version: 'audit.v1';
|
|
436
|
+
ts: number;
|
|
437
|
+
session: string;
|
|
438
|
+
audience: string;
|
|
439
|
+
did: string;
|
|
440
|
+
kid: string;
|
|
441
|
+
reqHash: string;
|
|
442
|
+
resHash: string;
|
|
443
|
+
verified: 'yes' | 'no';
|
|
444
|
+
scope: string;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ============================================================================
|
|
448
|
+
// Audit types
|
|
449
|
+
// ============================================================================
|
|
450
|
+
|
|
451
|
+
export interface AuditContext {
|
|
452
|
+
identity: {
|
|
453
|
+
did: string;
|
|
454
|
+
kid: string;
|
|
455
|
+
[key: string]: unknown;
|
|
456
|
+
};
|
|
457
|
+
session: {
|
|
458
|
+
sessionId: string;
|
|
459
|
+
audience: string;
|
|
460
|
+
[key: string]: unknown;
|
|
461
|
+
};
|
|
462
|
+
requestHash: string;
|
|
463
|
+
responseHash: string;
|
|
464
|
+
verified: 'yes' | 'no';
|
|
465
|
+
scopeId?: string;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export interface AuditEventContext {
|
|
469
|
+
eventType: string;
|
|
470
|
+
identity: {
|
|
471
|
+
did: string;
|
|
472
|
+
kid: string;
|
|
473
|
+
[key: string]: unknown;
|
|
474
|
+
};
|
|
475
|
+
session: {
|
|
476
|
+
sessionId: string;
|
|
477
|
+
audience: string;
|
|
478
|
+
[key: string]: unknown;
|
|
479
|
+
};
|
|
480
|
+
eventData?: Record<string, unknown>;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ============================================================================
|
|
484
|
+
// Authorization error types (MCP-I §6)
|
|
485
|
+
// ============================================================================
|
|
486
|
+
|
|
487
|
+
export interface AuthorizationDisplay {
|
|
488
|
+
title?: string;
|
|
489
|
+
hint?: Array<'link' | 'qr' | 'code'>;
|
|
490
|
+
authorizationCode?: string;
|
|
491
|
+
qrUrl?: string;
|
|
492
|
+
[key: string]: unknown;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export interface NeedsAuthorizationError {
|
|
496
|
+
error: 'needs_authorization';
|
|
497
|
+
message: string;
|
|
498
|
+
authorizationUrl: string;
|
|
499
|
+
resumeToken: string;
|
|
500
|
+
expiresAt: number;
|
|
501
|
+
scopes: string[];
|
|
502
|
+
display?: AuthorizationDisplay;
|
|
503
|
+
context?: Record<string, unknown>;
|
|
504
|
+
[key: string]: unknown;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export function createNeedsAuthorizationError(config: {
|
|
508
|
+
message: string;
|
|
509
|
+
authorizationUrl: string;
|
|
510
|
+
resumeToken: string;
|
|
511
|
+
expiresAt: number;
|
|
512
|
+
scopes: string[];
|
|
513
|
+
display?: AuthorizationDisplay;
|
|
514
|
+
}): NeedsAuthorizationError {
|
|
515
|
+
return {
|
|
516
|
+
error: 'needs_authorization',
|
|
517
|
+
...config,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export function isNeedsAuthorizationError(error: unknown): error is NeedsAuthorizationError {
|
|
522
|
+
return (
|
|
523
|
+
typeof error === 'object' &&
|
|
524
|
+
error !== null &&
|
|
525
|
+
(error as Record<string, unknown>)['error'] === 'needs_authorization'
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// ============================================================================
|
|
530
|
+
// DetachedProof validation
|
|
531
|
+
// ============================================================================
|
|
532
|
+
|
|
533
|
+
const HASH_REGEX = /^sha256:[a-f0-9]{64}$/;
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Validate a DetachedProof structure.
|
|
537
|
+
* Returns a Zod-compatible result shape.
|
|
538
|
+
*/
|
|
539
|
+
export function validateDetachedProof(proof: unknown): {
|
|
540
|
+
success: boolean;
|
|
541
|
+
error?: { message: string; errors?: Array<{ message: string }> };
|
|
542
|
+
data?: DetachedProof;
|
|
543
|
+
} {
|
|
544
|
+
if (!proof || typeof proof !== 'object') {
|
|
545
|
+
return { success: false, error: { message: 'Not an object' } };
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const p = proof as Record<string, unknown>;
|
|
549
|
+
|
|
550
|
+
// Validate jws
|
|
551
|
+
if (typeof p['jws'] !== 'string' || p['jws'].length < 1) {
|
|
552
|
+
return { success: false, error: { message: 'jws must be a non-empty string' } };
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Validate meta
|
|
556
|
+
const meta = p['meta'];
|
|
557
|
+
if (!meta || typeof meta !== 'object') {
|
|
558
|
+
return { success: false, error: { message: 'meta must be an object' } };
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const m = meta as Record<string, unknown>;
|
|
562
|
+
|
|
563
|
+
// Required string fields
|
|
564
|
+
const requiredStrings = ['did', 'kid', 'nonce', 'audience', 'sessionId'] as const;
|
|
565
|
+
for (const field of requiredStrings) {
|
|
566
|
+
if (typeof m[field] !== 'string' || (m[field] as string).length < 1) {
|
|
567
|
+
return { success: false, error: { message: `meta.${field} must be a non-empty string` } };
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Validate ts (positive integer)
|
|
572
|
+
if (typeof m['ts'] !== 'number' || !Number.isInteger(m['ts']) || m['ts'] <= 0) {
|
|
573
|
+
return { success: false, error: { message: 'meta.ts must be a positive integer' } };
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Validate hash fields
|
|
577
|
+
const hashFields = ['requestHash', 'responseHash'] as const;
|
|
578
|
+
for (const field of hashFields) {
|
|
579
|
+
if (typeof m[field] !== 'string' || !HASH_REGEX.test(m[field] as string)) {
|
|
580
|
+
return { success: false, error: { message: `meta.${field} must match sha256:<64 hex chars>` } };
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Optional string fields
|
|
585
|
+
const optionalStrings = ['scopeId', 'delegationRef', 'clientDid'] as const;
|
|
586
|
+
for (const field of optionalStrings) {
|
|
587
|
+
if (m[field] !== undefined && typeof m[field] !== 'string') {
|
|
588
|
+
return { success: false, error: { message: `meta.${field} must be a string if present` } };
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return {
|
|
593
|
+
success: true,
|
|
594
|
+
data: proof as DetachedProof,
|
|
595
|
+
};
|
|
596
|
+
}
|