@kya-os/mcp-i-core 1.4.19 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -0
  3. package/dist/auth/handshake.d.ts +119 -0
  4. package/dist/auth/handshake.js +250 -0
  5. package/dist/auth/index.d.ts +6 -0
  6. package/dist/auth/index.js +11 -0
  7. package/dist/auth/types.d.ts +46 -0
  8. package/dist/auth/types.js +10 -0
  9. package/dist/delegation/index.d.ts +1 -0
  10. package/dist/delegation/index.js +1 -0
  11. package/dist/delegation/outbound-proof.d.ts +70 -0
  12. package/dist/delegation/outbound-proof.js +67 -0
  13. package/dist/identity/user-did-manager.js +5 -3
  14. package/dist/index.d.ts +5 -0
  15. package/dist/index.js +25 -2
  16. package/dist/proof/generator.d.ts +109 -0
  17. package/dist/proof/generator.js +236 -0
  18. package/dist/proof/index.d.ts +5 -0
  19. package/dist/proof/index.js +11 -0
  20. package/dist/providers/base.d.ts +5 -1
  21. package/dist/runtime/base.d.ts +127 -13
  22. package/dist/runtime/base.js +195 -50
  23. package/dist/runtime/ext-apps-constants.d.ts +14 -0
  24. package/dist/runtime/ext-apps-constants.js +17 -0
  25. package/dist/services/batch-delegation.service.d.ts +1 -1
  26. package/dist/services/batch-delegation.service.js +4 -4
  27. package/dist/services/proof-verifier.js +1 -1
  28. package/dist/session/index.d.ts +5 -0
  29. package/dist/session/index.js +11 -0
  30. package/dist/session/manager.d.ts +113 -0
  31. package/dist/session/manager.js +273 -0
  32. package/docs/API_REFERENCE.md +76 -0
  33. package/docs/COMPLIANCE_MATRIX.md +691 -0
  34. package/docs/STATUSLIST2021_GUIDE.md +696 -0
  35. package/docs/W3C_VC_DELEGATION_GUIDE.md +710 -0
  36. package/package.json +21 -5
  37. package/vitest.config.mts +8 -7
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 MCP-I Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -282,6 +282,20 @@ async function verifyMCPIProof(data: any, proof: any): Promise<boolean> {
282
282
  4. **Session Lifecycle**: Absolute lifetime limits
283
283
  5. **Audit Logging**: Built-in audit trail support
284
284
 
285
+ ### Progressive Verification
286
+
287
+ **Signature verification requires a DID resolver and signature verifier.** If neither is configured, signature checks are skipped and the credential is treated as structurally valid. This is intentional for environments where DID resolution is not available (e.g., offline operation, testing, or edge deployments without network access).
288
+
289
+ In production deployments, always configure a DID resolver to enable full cryptographic verification:
290
+
291
+ ```typescript
292
+ const verifier = new DelegationCredentialVerifier({
293
+ resolveDID: async (did) => fetchDIDDocument(did),
294
+ verifySignature: async (vc, publicKey) => verifyEd25519(vc, publicKey),
295
+ resolveStatusList: async (url) => fetchStatusList(url),
296
+ });
297
+ ```
298
+
285
299
  ## Testing
286
300
 
287
301
  The provider-based architecture makes testing straightforward:
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Authorization Handshake — Platform-agnostic Protocol Reference
3
+ *
4
+ * Orchestrates the authorization flow for MCP-I bouncer:
5
+ * 1. Check agent reputation (optional)
6
+ * 2. Verify delegation exists
7
+ * 3. Return needs_authorization error if missing
8
+ *
9
+ * This module uses only the global fetch API — no Node-specific imports.
10
+ * It is safe to run on Node.js, Cloudflare Workers, and any fetch-capable runtime.
11
+ *
12
+ * Flow:
13
+ * - If delegation exists + valid → allow (fast path)
14
+ * - If delegation missing → return needs_authorization with hints
15
+ * - If reputation too low (optional) → require authorization
16
+ */
17
+ import { NeedsAuthorizationError } from "@kya-os/contracts/runtime";
18
+ import { DelegationRecord } from "@kya-os/contracts/delegation";
19
+ import { DelegationVerifier, VerifyDelegationResult } from "./types";
20
+ export type { DelegationVerifier, VerifyDelegationResult };
21
+ /**
22
+ * Agent reputation data from KTA
23
+ */
24
+ export interface AgentReputation {
25
+ agentDid: string;
26
+ score: number;
27
+ totalInteractions: number;
28
+ successRate: number;
29
+ riskLevel: "low" | "medium" | "high" | "unknown";
30
+ updatedAt: number;
31
+ }
32
+ /**
33
+ * Configuration for auth handshake
34
+ */
35
+ export interface AuthHandshakeConfig {
36
+ delegationVerifier: DelegationVerifier;
37
+ resumeTokenStore: ResumeTokenStore;
38
+ kta?: {
39
+ apiUrl: string;
40
+ apiKey?: string;
41
+ apiFormat?: "kta" | "argus";
42
+ };
43
+ bouncer: {
44
+ authorizationUrl: string;
45
+ resumeTokenTtl?: number;
46
+ requireAuthForUnknown?: boolean;
47
+ minReputationScore?: number;
48
+ };
49
+ debug?: boolean;
50
+ }
51
+ /**
52
+ * Result of auth handshake verification
53
+ */
54
+ export interface VerifyOrHintsResult {
55
+ authorized: boolean;
56
+ delegation?: DelegationRecord;
57
+ credential?: {
58
+ agent_did: string;
59
+ user_did: string;
60
+ scopes: string[];
61
+ authorization: {
62
+ type: "oauth" | "oauth2" | "password" | "credential" | "webauthn" | "siwe" | "none";
63
+ provider?: string;
64
+ credentialType?: string;
65
+ rpId?: string;
66
+ userVerification?: "required" | "preferred" | "discouraged";
67
+ chainId?: number;
68
+ domain?: string;
69
+ };
70
+ [key: string]: unknown;
71
+ };
72
+ authError?: NeedsAuthorizationError;
73
+ reputation?: AgentReputation;
74
+ reason?: string;
75
+ }
76
+ /**
77
+ * Resume token store interface
78
+ */
79
+ export interface ResumeTokenStore {
80
+ create(agentDid: string, scopes: string[], metadata?: Record<string, unknown>): Promise<string>;
81
+ get(token: string): Promise<{
82
+ agentDid: string;
83
+ scopes: string[];
84
+ createdAt: number;
85
+ expiresAt: number;
86
+ metadata?: Record<string, unknown>;
87
+ } | null>;
88
+ fulfill(token: string): Promise<void>;
89
+ }
90
+ /**
91
+ * Simple in-memory resume token store (for testing/development).
92
+ * Not suitable for multi-instance deployments.
93
+ */
94
+ export declare class MemoryResumeTokenStore implements ResumeTokenStore {
95
+ private tokens;
96
+ private ttl;
97
+ constructor(ttlMs?: number);
98
+ create(agentDid: string, scopes: string[], metadata?: Record<string, unknown>): Promise<string>;
99
+ get(token: string): Promise<{
100
+ agentDid: string;
101
+ scopes: string[];
102
+ createdAt: number;
103
+ expiresAt: number;
104
+ metadata?: Record<string, unknown>;
105
+ } | null>;
106
+ fulfill(token: string): Promise<void>;
107
+ clear(): void;
108
+ }
109
+ /**
110
+ * Main auth handshake function.
111
+ *
112
+ * Verifies agent authorization or returns authorization hints.
113
+ */
114
+ export declare function verifyOrHints(agentDid: string, scopes: string[], config: AuthHandshakeConfig, _resumeToken?: string): Promise<VerifyOrHintsResult>;
115
+ /**
116
+ * Helper: Check if scopes are sensitive and require authorization.
117
+ */
118
+ export declare function hasSensitiveScopes(scopes: string[]): boolean;
119
+ //# sourceMappingURL=handshake.d.ts.map
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ /**
3
+ * Authorization Handshake — Platform-agnostic Protocol Reference
4
+ *
5
+ * Orchestrates the authorization flow for MCP-I bouncer:
6
+ * 1. Check agent reputation (optional)
7
+ * 2. Verify delegation exists
8
+ * 3. Return needs_authorization error if missing
9
+ *
10
+ * This module uses only the global fetch API — no Node-specific imports.
11
+ * It is safe to run on Node.js, Cloudflare Workers, and any fetch-capable runtime.
12
+ *
13
+ * Flow:
14
+ * - If delegation exists + valid → allow (fast path)
15
+ * - If delegation missing → return needs_authorization with hints
16
+ * - If reputation too low (optional) → require authorization
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.MemoryResumeTokenStore = void 0;
20
+ exports.verifyOrHints = verifyOrHints;
21
+ exports.hasSensitiveScopes = hasSensitiveScopes;
22
+ const runtime_1 = require("@kya-os/contracts/runtime");
23
+ const logging_1 = require("../logging");
24
+ /**
25
+ * Simple in-memory resume token store (for testing/development).
26
+ * Not suitable for multi-instance deployments.
27
+ */
28
+ class MemoryResumeTokenStore {
29
+ tokens = new Map();
30
+ ttl;
31
+ constructor(ttlMs = 600_000) {
32
+ this.ttl = ttlMs;
33
+ }
34
+ async create(agentDid, scopes, metadata) {
35
+ const token = `rt_${Date.now()}_${Math.random().toString(36).substring(2, 18)}`;
36
+ const now = Date.now();
37
+ this.tokens.set(token, {
38
+ agentDid,
39
+ scopes,
40
+ createdAt: now,
41
+ expiresAt: now + this.ttl,
42
+ metadata,
43
+ fulfilled: false,
44
+ });
45
+ return token;
46
+ }
47
+ async get(token) {
48
+ const data = this.tokens.get(token);
49
+ if (!data)
50
+ return null;
51
+ if (Date.now() > data.expiresAt) {
52
+ this.tokens.delete(token);
53
+ return null;
54
+ }
55
+ if (data.fulfilled)
56
+ return null;
57
+ return {
58
+ agentDid: data.agentDid,
59
+ scopes: data.scopes,
60
+ createdAt: data.createdAt,
61
+ expiresAt: data.expiresAt,
62
+ metadata: data.metadata,
63
+ };
64
+ }
65
+ async fulfill(token) {
66
+ const data = this.tokens.get(token);
67
+ if (data) {
68
+ data.fulfilled = true;
69
+ }
70
+ }
71
+ clear() {
72
+ this.tokens.clear();
73
+ }
74
+ }
75
+ exports.MemoryResumeTokenStore = MemoryResumeTokenStore;
76
+ /**
77
+ * Main auth handshake function.
78
+ *
79
+ * Verifies agent authorization or returns authorization hints.
80
+ */
81
+ async function verifyOrHints(agentDid, scopes, config, _resumeToken) {
82
+ const startTime = Date.now();
83
+ if (config.debug) {
84
+ logging_1.logger.debug(`[AuthHandshake] Verifying ${agentDid} for scopes: ${scopes.join(", ")}`);
85
+ }
86
+ let reputation;
87
+ if (config.kta && config.bouncer.minReputationScore !== undefined) {
88
+ try {
89
+ reputation = await fetchAgentReputation(agentDid, config.kta);
90
+ if (config.debug) {
91
+ logging_1.logger.debug(`[AuthHandshake] Reputation score: ${reputation.score}`);
92
+ }
93
+ if (reputation.score < config.bouncer.minReputationScore) {
94
+ if (config.debug) {
95
+ logging_1.logger.debug(`[AuthHandshake] Reputation ${reputation.score} < ${config.bouncer.minReputationScore}, requiring authorization`);
96
+ }
97
+ const authError = await buildNeedsAuthorizationError(agentDid, scopes, config, "Agent reputation score below threshold");
98
+ return {
99
+ authorized: false,
100
+ authError,
101
+ reputation,
102
+ reason: "Low reputation score",
103
+ };
104
+ }
105
+ }
106
+ catch (error) {
107
+ logging_1.logger.warn("[AuthHandshake] Failed to check reputation:", error);
108
+ }
109
+ }
110
+ let delegationResult;
111
+ try {
112
+ delegationResult = await config.delegationVerifier.verify(agentDid, scopes);
113
+ }
114
+ catch (error) {
115
+ logging_1.logger.error("[AuthHandshake] Delegation verification failed:", error);
116
+ const errorMessage = `Delegation verification error: ${error instanceof Error ? error.message : "Unknown error"}`;
117
+ const authError = await buildNeedsAuthorizationError(agentDid, scopes, config, errorMessage);
118
+ return {
119
+ authorized: false,
120
+ authError,
121
+ reason: errorMessage,
122
+ };
123
+ }
124
+ if (delegationResult.valid && delegationResult.delegation) {
125
+ if (config.debug) {
126
+ logging_1.logger.debug(`[AuthHandshake] Delegation valid, authorized (${Date.now() - startTime}ms)`);
127
+ }
128
+ return {
129
+ authorized: true,
130
+ delegation: delegationResult.delegation,
131
+ credential: delegationResult.credential,
132
+ reputation,
133
+ reason: "Valid delegation found",
134
+ };
135
+ }
136
+ if (config.debug) {
137
+ logging_1.logger.debug(`[AuthHandshake] No delegation found, returning needs_authorization (${Date.now() - startTime}ms)`);
138
+ }
139
+ const authError = await buildNeedsAuthorizationError(agentDid, scopes, config, delegationResult.reason ?? "No valid delegation found");
140
+ return {
141
+ authorized: false,
142
+ authError,
143
+ reputation,
144
+ reason: delegationResult.reason ?? "No delegation",
145
+ };
146
+ }
147
+ /**
148
+ * Fetch agent reputation from KTA.
149
+ */
150
+ async function fetchAgentReputation(agentDid, ktaConfig) {
151
+ const apiUrl = ktaConfig.apiUrl.replace(/\/$/, "");
152
+ const headers = {
153
+ "Content-Type": "application/json",
154
+ };
155
+ if (ktaConfig.apiKey) {
156
+ headers["X-API-Key"] = ktaConfig.apiKey;
157
+ }
158
+ const isArgusFormat = ktaConfig.apiFormat === "argus";
159
+ let response;
160
+ if (isArgusFormat) {
161
+ response = await fetch(`${apiUrl}/v1/reputation/${encodeURIComponent(agentDid)}`, {
162
+ method: "POST",
163
+ headers,
164
+ body: JSON.stringify({ include_details: false }),
165
+ });
166
+ }
167
+ else {
168
+ response = await fetch(`${apiUrl}/api/v1/reputation/${encodeURIComponent(agentDid)}`, {
169
+ method: "GET",
170
+ headers,
171
+ });
172
+ }
173
+ if (!response.ok) {
174
+ if (response.status === 404) {
175
+ return {
176
+ agentDid,
177
+ score: 50,
178
+ totalInteractions: 0,
179
+ successRate: 0,
180
+ riskLevel: "unknown",
181
+ updatedAt: Date.now(),
182
+ };
183
+ }
184
+ throw new Error(`KTA API error: ${response.status} ${response.statusText}`);
185
+ }
186
+ const data = (await response.json());
187
+ const score = data.score ?? 50;
188
+ const levelRaw = (data.level ??
189
+ data.riskLevel ??
190
+ "unknown").toLowerCase();
191
+ const riskLevel = levelRaw === "low" || levelRaw === "medium" || levelRaw === "high"
192
+ ? levelRaw
193
+ : "unknown";
194
+ return {
195
+ agentDid: data.agent_did ??
196
+ data.agentDid ??
197
+ agentDid,
198
+ score,
199
+ totalInteractions: data.totalInteractions ?? 0,
200
+ successRate: data.successRate ?? 0,
201
+ riskLevel,
202
+ updatedAt: data.calculatedAt
203
+ ? new Date(data.calculatedAt).getTime()
204
+ : (data.updatedAt ?? Date.now()),
205
+ };
206
+ }
207
+ /**
208
+ * Build needs_authorization error with hints.
209
+ */
210
+ async function buildNeedsAuthorizationError(agentDid, scopes, config, message) {
211
+ const resumeToken = await config.resumeTokenStore.create(agentDid, scopes, {
212
+ requestedAt: Date.now(),
213
+ });
214
+ const expiresAt = Date.now() + (config.bouncer.resumeTokenTtl ?? 600_000);
215
+ const authUrl = new URL(config.bouncer.authorizationUrl);
216
+ authUrl.searchParams.set("agent_did", agentDid);
217
+ authUrl.searchParams.set("scopes", scopes.join(","));
218
+ authUrl.searchParams.set("resume_token", resumeToken);
219
+ const authCode = resumeToken.substring(0, 8).toUpperCase();
220
+ const display = {
221
+ title: "Authorization Required",
222
+ hint: ["link", "qr"],
223
+ authorizationCode: authCode,
224
+ qrUrl: `https://chart.googleapis.com/chart?cht=qr&chs=300x300&chl=${encodeURIComponent(authUrl.toString())}`,
225
+ };
226
+ return (0, runtime_1.createNeedsAuthorizationError)({
227
+ message,
228
+ authorizationUrl: authUrl.toString(),
229
+ resumeToken,
230
+ expiresAt,
231
+ scopes,
232
+ display,
233
+ });
234
+ }
235
+ /**
236
+ * Helper: Check if scopes are sensitive and require authorization.
237
+ */
238
+ function hasSensitiveScopes(scopes) {
239
+ const sensitivePatterns = [
240
+ "write",
241
+ "delete",
242
+ "admin",
243
+ "payment",
244
+ "transfer",
245
+ "execute",
246
+ "modify",
247
+ ];
248
+ return scopes.some((scope) => sensitivePatterns.some((pattern) => scope.toLowerCase().includes(pattern)));
249
+ }
250
+ //# sourceMappingURL=handshake.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Auth module — platform-agnostic authorization handshake reference implementation.
3
+ */
4
+ export { verifyOrHints, hasSensitiveScopes, MemoryResumeTokenStore, type AuthHandshakeConfig, type VerifyOrHintsResult, type AgentReputation, type ResumeTokenStore, } from "./handshake";
5
+ export type { DelegationVerifier, VerifyDelegationResult } from "./types";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryResumeTokenStore = exports.hasSensitiveScopes = exports.verifyOrHints = void 0;
4
+ /**
5
+ * Auth module — platform-agnostic authorization handshake reference implementation.
6
+ */
7
+ var handshake_1 = require("./handshake");
8
+ Object.defineProperty(exports, "verifyOrHints", { enumerable: true, get: function () { return handshake_1.verifyOrHints; } });
9
+ Object.defineProperty(exports, "hasSensitiveScopes", { enumerable: true, get: function () { return handshake_1.hasSensitiveScopes; } });
10
+ Object.defineProperty(exports, "MemoryResumeTokenStore", { enumerable: true, get: function () { return handshake_1.MemoryResumeTokenStore; } });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Authorization types for mcp-i-core auth module.
3
+ *
4
+ * These are the minimal interfaces required by the auth handshake.
5
+ * Platform adapters (packages/mcp-i, packages/mcp-i-cloudflare) may extend
6
+ * these types with additional fields.
7
+ */
8
+ import { DelegationRecord } from "@kya-os/contracts/delegation";
9
+ /**
10
+ * Result of a delegation verification check.
11
+ * Structurally compatible with the full VerifyDelegationResult in packages/mcp-i.
12
+ */
13
+ export interface VerifyDelegationResult {
14
+ /** Whether a valid delegation was found */
15
+ valid: boolean;
16
+ /** The delegation record (if found) */
17
+ delegation?: DelegationRecord;
18
+ /** Credential from AgentShield API (includes authorization method) */
19
+ credential?: {
20
+ agent_did: string;
21
+ user_did: string;
22
+ scopes: string[];
23
+ authorization: {
24
+ type: "oauth" | "oauth2" | "password" | "credential" | "webauthn" | "siwe" | "none";
25
+ provider?: string;
26
+ credentialType?: string;
27
+ rpId?: string;
28
+ userVerification?: "required" | "preferred" | "discouraged";
29
+ chainId?: number;
30
+ domain?: string;
31
+ };
32
+ [key: string]: unknown;
33
+ };
34
+ /** Reason for invalid result */
35
+ reason?: string;
36
+ /** Whether result came from cache */
37
+ cached?: boolean;
38
+ }
39
+ /**
40
+ * Minimal delegation verifier interface required by verifyOrHints().
41
+ * Concrete implementations live in packages/mcp-i (AgentShieldAPIDelegationVerifier, etc.).
42
+ */
43
+ export interface DelegationVerifier {
44
+ verify(agentDid: string, scopes: string[]): Promise<VerifyDelegationResult>;
45
+ }
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * Authorization types for mcp-i-core auth module.
4
+ *
5
+ * These are the minimal interfaces required by the auth handshake.
6
+ * Platform adapters (packages/mcp-i, packages/mcp-i-cloudflare) may extend
7
+ * these types with additional fields.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ //# sourceMappingURL=types.js.map
@@ -11,4 +11,5 @@ export * from './statuslist-manager';
11
11
  export * from './delegation-graph';
12
12
  export * from './cascading-revocation';
13
13
  export * from './utils';
14
+ export * from './outbound-proof';
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -27,4 +27,5 @@ __exportStar(require("./statuslist-manager"), exports);
27
27
  __exportStar(require("./delegation-graph"), exports);
28
28
  __exportStar(require("./cascading-revocation"), exports);
29
29
  __exportStar(require("./utils"), exports);
30
+ __exportStar(require("./outbound-proof"), exports);
30
31
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Outbound Delegation Proof
3
+ *
4
+ * Builds signed delegation proof JWTs for injection on outbound HTTP requests.
5
+ * Enables downstream services to independently verify the delegation chain
6
+ * without trusting the MCP server.
7
+ *
8
+ * Wire format: signed compact EdDSA JWT (60s TTL, per-call jti)
9
+ * Header injection: X-Delegation-Id, X-Delegation-Chain, X-Delegation-Proof, X-Scopes
10
+ *
11
+ * Related Spec: MCP-I §2 — Outbound Delegation Propagation
12
+ */
13
+ import type { DelegationRecord } from "@kya-os/contracts/delegation";
14
+ /**
15
+ * Ed25519 private key JWK (includes d for signing)
16
+ */
17
+ export interface Ed25519PrivateJWK {
18
+ kty: "OKP";
19
+ crv: "Ed25519";
20
+ /** Base64url-encoded public key scalar */
21
+ x: string;
22
+ /** Base64url-encoded private key scalar */
23
+ d: string;
24
+ kid?: string;
25
+ use?: string;
26
+ }
27
+ /**
28
+ * Options for building a delegation proof JWT
29
+ */
30
+ export interface DelegationProofOptions {
31
+ /** DID of the agent issuing the proof */
32
+ agentDid: string;
33
+ /** DID of the delegating user */
34
+ userDid: string;
35
+ /** Delegation VC credential ID */
36
+ delegationId: string;
37
+ /** Reference chain string (format: vc_id_1>del_id_1>...) */
38
+ delegationChain: string;
39
+ /** Scopes granted by the delegation */
40
+ scopes: string[];
41
+ /** Agent Ed25519 private key JWK (with d field) */
42
+ privateKeyJwk: Ed25519PrivateJWK;
43
+ /** Key ID for the JWT header (e.g. did:web:agent.example.com#key-1) */
44
+ kid: string;
45
+ /** Target service hostname (used as aud claim) */
46
+ targetHostname: string;
47
+ }
48
+ /**
49
+ * Build a signed delegation proof JWT.
50
+ *
51
+ * JWT is EdDSA-signed with the agent's Ed25519 key, 60s TTL, jti per call.
52
+ * Returns the compact JWT string.
53
+ *
54
+ * @throws when the private key cannot be imported or signing fails
55
+ */
56
+ export declare function buildDelegationProofJWT(options: DelegationProofOptions): Promise<string>;
57
+ /**
58
+ * Build a delegation reference chain string.
59
+ *
60
+ * For a single delegation: "vcId>delegationId"
61
+ * If vcId is absent: returns delegation.id
62
+ * If both are absent: returns empty string
63
+ *
64
+ * For multi-hop chains, callers concatenate per-hop results with ">".
65
+ *
66
+ * @example
67
+ * buildChainString(del) // "vc_abc>del_123"
68
+ */
69
+ export declare function buildChainString(delegation: DelegationRecord): string;
70
+ //# sourceMappingURL=outbound-proof.d.ts.map
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ /**
3
+ * Outbound Delegation Proof
4
+ *
5
+ * Builds signed delegation proof JWTs for injection on outbound HTTP requests.
6
+ * Enables downstream services to independently verify the delegation chain
7
+ * without trusting the MCP server.
8
+ *
9
+ * Wire format: signed compact EdDSA JWT (60s TTL, per-call jti)
10
+ * Header injection: X-Delegation-Id, X-Delegation-Chain, X-Delegation-Proof, X-Scopes
11
+ *
12
+ * Related Spec: MCP-I §2 — Outbound Delegation Propagation
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.buildDelegationProofJWT = buildDelegationProofJWT;
16
+ exports.buildChainString = buildChainString;
17
+ const jose_1 = require("jose");
18
+ /**
19
+ * Build a signed delegation proof JWT.
20
+ *
21
+ * JWT is EdDSA-signed with the agent's Ed25519 key, 60s TTL, jti per call.
22
+ * Returns the compact JWT string.
23
+ *
24
+ * @throws when the private key cannot be imported or signing fails
25
+ */
26
+ async function buildDelegationProofJWT(options) {
27
+ const { agentDid, userDid, delegationId, delegationChain, scopes, privateKeyJwk, kid, targetHostname, } = options;
28
+ const privateKey = await (0, jose_1.importJWK)(privateKeyJwk, "EdDSA");
29
+ const iat = Math.floor(Date.now() / 1000);
30
+ const exp = iat + 60;
31
+ const jwt = await new jose_1.SignJWT({
32
+ delegation_id: delegationId,
33
+ delegation_chain: delegationChain,
34
+ scope: scopes.join(","),
35
+ })
36
+ .setProtectedHeader({ alg: "EdDSA", kid })
37
+ .setIssuer(agentDid)
38
+ .setSubject(userDid)
39
+ .setJti(crypto.randomUUID())
40
+ .setAudience(targetHostname)
41
+ .setIssuedAt(iat)
42
+ .setExpirationTime(exp)
43
+ .sign(privateKey);
44
+ return jwt;
45
+ }
46
+ /**
47
+ * Build a delegation reference chain string.
48
+ *
49
+ * For a single delegation: "vcId>delegationId"
50
+ * If vcId is absent: returns delegation.id
51
+ * If both are absent: returns empty string
52
+ *
53
+ * For multi-hop chains, callers concatenate per-hop results with ">".
54
+ *
55
+ * @example
56
+ * buildChainString(del) // "vc_abc>del_123"
57
+ */
58
+ function buildChainString(delegation) {
59
+ if (!delegation.id && !delegation.vcId) {
60
+ return "";
61
+ }
62
+ if (!delegation.vcId) {
63
+ return delegation.id;
64
+ }
65
+ return `${delegation.vcId}>${delegation.id}`;
66
+ }
67
+ //# sourceMappingURL=outbound-proof.js.map
@@ -222,9 +222,11 @@ class UserDidManager {
222
222
  */
223
223
  async generateUserDidWithKeyPair() {
224
224
  if (this.config.useDidWeb && this.config.didWebBaseUrl) {
225
- // Generate did:web (requires web server setup)
226
- // For now, fall back to did:key
227
- // TODO: Implement did:web generation if needed
225
+ // did:web generation requires a web server to host the DID document.
226
+ // Currently falls back to did:key which is self-resolving.
227
+ // TODO(feature): Implement did:web generation when required for production use cases.
228
+ // This would involve generating the DID document and coordinating with
229
+ // the hosting infrastructure to serve it at the well-known URL.
228
230
  console.warn("[UserDidManager] did:web not yet implemented, using did:key");
229
231
  }
230
232
  // Generate Ed25519 keypair for user DID