@kya-os/mcp-i 1.7.13 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/225.js +1 -0
  2. package/dist/227.js +1 -0
  3. package/dist/242.js +1 -0
  4. package/dist/283.js +1 -0
  5. package/dist/295.js +1 -1
  6. package/dist/354.js +1 -0
  7. package/dist/361.js +1 -0
  8. package/dist/452.js +1 -0
  9. package/dist/533.js +1 -0
  10. package/dist/622.js +1 -0
  11. package/dist/65.js +1 -0
  12. package/dist/843.js +1 -0
  13. package/dist/861.js +1 -0
  14. package/dist/866.js +1 -1
  15. package/dist/914.js +1 -0
  16. package/dist/95.js +1 -0
  17. package/dist/{941.js → 966.js} +1 -1
  18. package/dist/providers/node-providers.d.ts +1 -1
  19. package/dist/providers/node-providers.js +2 -2
  20. package/dist/runtime/adapter-express.js +6 -6
  21. package/dist/runtime/adapter-nextjs.js +6 -6
  22. package/dist/runtime/auth-handshake.d.ts +4 -159
  23. package/dist/runtime/auth-handshake.js +8 -249
  24. package/dist/runtime/http.js +6 -6
  25. package/dist/runtime/mcpi-runtime.d.ts +4 -0
  26. package/dist/runtime/mcpi-runtime.js +58 -43
  27. package/dist/runtime/outbound-delegation.d.ts +34 -0
  28. package/dist/runtime/outbound-delegation.js +134 -0
  29. package/dist/runtime/proof.d.ts +13 -88
  30. package/dist/runtime/proof.js +11 -225
  31. package/dist/runtime/request-context.d.ts +41 -0
  32. package/dist/runtime/request-context.js +48 -0
  33. package/dist/runtime/session.d.ts +13 -104
  34. package/dist/runtime/session.js +31 -267
  35. package/dist/runtime/stdio.js +6 -6
  36. package/dist/runtime/utils/tools.js +17 -3
  37. package/dist/runtime/verifier-middleware.js +4 -4
  38. package/package.json +19 -19
  39. package/dist/207.js +0 -1
  40. package/dist/25.js +0 -1
  41. package/dist/360.js +0 -1
  42. package/dist/387.js +0 -1
  43. package/dist/406.js +0 -1
  44. package/dist/448.js +0 -1
  45. package/dist/478.js +0 -1
  46. package/dist/575.js +0 -1
  47. package/dist/67.js +0 -1
  48. package/dist/743.js +0 -1
  49. package/dist/784.js +0 -1
  50. package/dist/844.js +0 -1
  51. package/dist/936.js +0 -1
  52. package/dist/988.js +0 -1
@@ -82,6 +82,8 @@ export interface MCPIRuntimeConfig {
82
82
  kta?: {
83
83
  apiUrl: string;
84
84
  apiKey?: string;
85
+ /** API format: 'kta' (default, GET /api/v1/reputation/{did}) or 'argus' (POST /v1/reputation/{did}) */
86
+ apiFormat?: 'kta' | 'argus';
85
87
  };
86
88
  /** Minimum reputation score to bypass authorization (0-100) */
87
89
  minReputationScore?: number;
@@ -144,6 +146,8 @@ export declare class MCPIRuntime {
144
146
  processToolCall(request: ToolRequest, session: SessionContext, toolHandler: (request: ToolRequest) => Promise<any>, options?: {
145
147
  scopeId?: string;
146
148
  delegationRef?: string;
149
+ delegationChain?: string;
150
+ delegationScopes?: string[];
147
151
  requiresDelegation?: boolean;
148
152
  requiredScopes?: string[];
149
153
  agentDid?: string;
@@ -17,8 +17,10 @@ const debug_1 = require("./debug");
17
17
  const demo_1 = require("./demo");
18
18
  const delegation_verifier_1 = require("./delegation-verifier");
19
19
  const auth_handshake_1 = require("./auth-handshake");
20
+ const request_context_1 = require("./request-context");
20
21
  const tool_protection_1 = require("./tool-protection");
21
22
  const tool_protection_registry_1 = require("./tool-protection-registry");
23
+ const mcp_i_core_1 = require("@kya-os/mcp-i-core");
22
24
  /**
23
25
  * XMCP-I Runtime class
24
26
  */
@@ -195,55 +197,68 @@ class MCPIRuntime {
195
197
  }
196
198
  return verifyResult.authError;
197
199
  }
198
- // If authorized, log the delegation ID for audit trail
200
+ // If authorized, capture delegation metadata for outbound header propagation
199
201
  if (verifyResult.delegation) {
200
202
  options.delegationRef = verifyResult.delegation.id;
203
+ options.delegationChain = (0, mcp_i_core_1.buildChainString)(verifyResult.delegation);
204
+ options.delegationScopes = options.requiredScopes ?? [];
205
+ const identity = await this.identityManager.ensureIdentity();
206
+ (0, request_context_1.setDelegationContextFromIdentity)({
207
+ delegationId: options.delegationRef,
208
+ delegationChain: options.delegationChain,
209
+ delegationScopes: options.delegationScopes,
210
+ identity,
211
+ });
201
212
  }
202
213
  }
203
- try {
204
- // Execute the tool (only if delegation check passed)
205
- let data = await toolHandler(request);
206
- // Add identity badge if enabled
207
- if (this.demoManager?.isIdentityBadgeEnabled()) {
208
- data = this.demoManager.addIdentityBadgeToResponse(data);
214
+ // Capture before async boundary — TS narrowing doesn't survive across async callbacks
215
+ const cachedIdentity = this.cachedIdentity;
216
+ return (0, request_context_1.runWithContext)({ session: session ?? undefined }, async () => {
217
+ try {
218
+ // Execute the tool (only if delegation check passed)
219
+ let data = await toolHandler(request);
220
+ // Add identity badge if enabled
221
+ if (this.demoManager?.isIdentityBadgeEnabled()) {
222
+ data = this.demoManager.addIdentityBadgeToResponse(data);
223
+ }
224
+ // Create response with proof
225
+ const proofOptions = {
226
+ ...options,
227
+ ...(session && session.clientDid
228
+ ? { clientDid: session.clientDid }
229
+ : {}),
230
+ };
231
+ const response = await (0, proof_1.createProofResponse)(request, data, cachedIdentity, session, proofOptions);
232
+ // Update debug state with latest proof
233
+ if (this.debugManager && response.meta?.proof) {
234
+ this.debugManager.updateDebugState(response.meta.proof, session);
235
+ }
236
+ // Log audit record (first call per session)
237
+ const auditContext = {
238
+ identity: cachedIdentity,
239
+ session,
240
+ requestHash: response.meta.proof.meta.requestHash,
241
+ responseHash: response.meta.proof.meta.responseHash,
242
+ verified: "yes",
243
+ scopeId: options.scopeId,
244
+ };
245
+ await this.auditLogger.logAuditRecord(auditContext);
246
+ return response;
209
247
  }
210
- // Create response with proof
211
- const proofOptions = {
212
- ...options,
213
- ...(session && session.clientDid
214
- ? { clientDid: session.clientDid }
215
- : {}),
216
- };
217
- const response = await (0, proof_1.createProofResponse)(request, data, this.cachedIdentity, session, proofOptions);
218
- // Update debug state with latest proof
219
- if (this.debugManager && response.meta?.proof) {
220
- this.debugManager.updateDebugState(response.meta.proof, session);
248
+ catch (error) {
249
+ // Log failed audit record
250
+ const auditContext = {
251
+ identity: cachedIdentity,
252
+ session,
253
+ requestHash: "sha256:error",
254
+ responseHash: "sha256:error",
255
+ verified: "no",
256
+ scopeId: options.scopeId,
257
+ };
258
+ await this.auditLogger.logAuditRecord(auditContext);
259
+ throw error;
221
260
  }
222
- // Log audit record (first call per session)
223
- const auditContext = {
224
- identity: this.cachedIdentity,
225
- session,
226
- requestHash: response.meta.proof.meta.requestHash,
227
- responseHash: response.meta.proof.meta.responseHash,
228
- verified: "yes",
229
- scopeId: options.scopeId,
230
- };
231
- await this.auditLogger.logAuditRecord(auditContext);
232
- return response;
233
- }
234
- catch (error) {
235
- // Log failed audit record
236
- const auditContext = {
237
- identity: this.cachedIdentity,
238
- session,
239
- requestHash: "sha256:error",
240
- responseHash: "sha256:error",
241
- verified: "no",
242
- scopeId: options.scopeId,
243
- };
244
- await this.auditLogger.logAuditRecord(auditContext);
245
- throw error;
246
- }
261
+ }); // end runWithContext
247
262
  }
248
263
  /**
249
264
  * Get well-known endpoint handler
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Outbound Delegation Header Injection
3
+ *
4
+ * Wraps globalThis.fetch to inject delegation proof headers on outbound
5
+ * HTTP requests made during delegated tool handler execution.
6
+ *
7
+ * Reads delegation context from AsyncLocalStorage (via getContext()),
8
+ * builds a signed Ed25519 JWT, and injects:
9
+ * KYA-Delegation-Id, KYA-Delegation-Chain, KYA-Delegation-Proof, KYA-Granted-Scopes
10
+ *
11
+ * Behavior:
12
+ * - Skips injection for internal hostnames (localhost, *.vouched.id, Fly IPs)
13
+ * - On signing failure: logs warning, continues WITHOUT delegation headers
14
+ * - Anonymous/non-delegated calls: no headers injected
15
+ *
16
+ * Related Spec: MCP-I §2 — Outbound Delegation Propagation
17
+ */
18
+ interface Logger {
19
+ debug: (msg: string, meta?: Record<string, unknown>) => void;
20
+ warn: (msg: string, meta?: Record<string, unknown>) => void;
21
+ }
22
+ /**
23
+ * Determine if a URL is an internal request that should not carry
24
+ * delegation headers. Matches the same guard as the compute interceptor.
25
+ */
26
+ export declare function isInternalDelegationTarget(urlString: string): boolean;
27
+ /**
28
+ * Install the delegation header interceptor on globalThis.fetch.
29
+ * Returns a teardown function that restores the original fetch.
30
+ *
31
+ * Idempotent — calling multiple times is safe.
32
+ */
33
+ export declare function installDelegationInterceptor(logger?: Logger): () => void;
34
+ export {};
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ /**
3
+ * Outbound Delegation Header Injection
4
+ *
5
+ * Wraps globalThis.fetch to inject delegation proof headers on outbound
6
+ * HTTP requests made during delegated tool handler execution.
7
+ *
8
+ * Reads delegation context from AsyncLocalStorage (via getContext()),
9
+ * builds a signed Ed25519 JWT, and injects:
10
+ * KYA-Delegation-Id, KYA-Delegation-Chain, KYA-Delegation-Proof, KYA-Granted-Scopes
11
+ *
12
+ * Behavior:
13
+ * - Skips injection for internal hostnames (localhost, *.vouched.id, Fly IPs)
14
+ * - On signing failure: logs warning, continues WITHOUT delegation headers
15
+ * - Anonymous/non-delegated calls: no headers injected
16
+ *
17
+ * Related Spec: MCP-I §2 — Outbound Delegation Propagation
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.isInternalDelegationTarget = isInternalDelegationTarget;
21
+ exports.installDelegationInterceptor = installDelegationInterceptor;
22
+ const mcp_i_core_1 = require("@kya-os/mcp-i-core");
23
+ const request_context_js_1 = require("./request-context.js");
24
+ // Flag to avoid double-installing
25
+ const DELEGATION_INTERCEPTED_FLAG = "__kyaDelegationIntercepted";
26
+ /**
27
+ * Determine if a URL is an internal request that should not carry
28
+ * delegation headers. Matches the same guard as the compute interceptor.
29
+ */
30
+ function isInternalDelegationTarget(urlString) {
31
+ let url;
32
+ try {
33
+ url = new URL(urlString);
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ const hostname = url.hostname.toLowerCase();
39
+ if (hostname === "localhost" ||
40
+ hostname === "127.0.0.1" ||
41
+ hostname === "[::1]" ||
42
+ hostname === "::1" ||
43
+ hostname === "0.0.0.0") {
44
+ return true;
45
+ }
46
+ if (hostname.endsWith(".vouched.id") ||
47
+ hostname === "vouched.id") {
48
+ return true;
49
+ }
50
+ // Internal hostnames (*.internal, bare "internal")
51
+ if (hostname.endsWith(".internal") || hostname === "internal") {
52
+ return true;
53
+ }
54
+ // AWS EC2 metadata service
55
+ if (hostname === "169.254.169.254") {
56
+ return true;
57
+ }
58
+ // Fly.io private network ranges (fdaa:*, fd10::*)
59
+ if (hostname.startsWith("fdaa:") || hostname.startsWith("fd10:")) {
60
+ return true;
61
+ }
62
+ return false;
63
+ }
64
+ /**
65
+ * Install the delegation header interceptor on globalThis.fetch.
66
+ * Returns a teardown function that restores the original fetch.
67
+ *
68
+ * Idempotent — calling multiple times is safe.
69
+ */
70
+ function installDelegationInterceptor(logger) {
71
+ if (globalThis.fetch[DELEGATION_INTERCEPTED_FLAG]) {
72
+ logger?.debug("Delegation interceptor already installed, skipping");
73
+ return () => { };
74
+ }
75
+ const originalFetch = globalThis.fetch;
76
+ const delegationFetch = async function (input, init) {
77
+ const urlString = typeof input === "string"
78
+ ? input
79
+ : input instanceof URL
80
+ ? input.toString()
81
+ : input.url;
82
+ if (isInternalDelegationTarget(urlString)) {
83
+ return originalFetch(input, init);
84
+ }
85
+ const baseHeaders = input instanceof Request ? input.headers : undefined;
86
+ const headers = new Headers(init?.headers ?? baseHeaders);
87
+ // Only inject if we have a delegation context and haven't already injected
88
+ if (!headers.has("KYA-Delegation-Proof")) {
89
+ const ctx = (0, request_context_js_1.getContext)();
90
+ if (ctx?.delegationRef &&
91
+ ctx.delegationPrivateKeyJwk &&
92
+ ctx.delegationAgentKid) {
93
+ try {
94
+ let targetHostname = "";
95
+ try {
96
+ targetHostname = new URL(urlString).hostname;
97
+ }
98
+ catch {
99
+ // malformed URL — leave aud empty
100
+ }
101
+ const proof = await (0, mcp_i_core_1.buildDelegationProofJWT)({
102
+ agentDid: ctx.session?.serverDid ?? "",
103
+ userDid: ctx.session?.userDid ?? "",
104
+ delegationId: ctx.delegationRef,
105
+ delegationChain: ctx.delegationChain ?? ctx.delegationRef,
106
+ scopes: ctx.delegationScopes ?? [],
107
+ privateKeyJwk: ctx.delegationPrivateKeyJwk,
108
+ kid: ctx.delegationAgentKid,
109
+ targetHostname,
110
+ });
111
+ headers.set("KYA-Delegation-Id", ctx.delegationRef);
112
+ headers.set("KYA-Delegation-Chain", ctx.delegationChain ?? ctx.delegationRef);
113
+ headers.set("KYA-Delegation-Proof", proof);
114
+ headers.set("KYA-Granted-Scopes", (ctx.delegationScopes ?? []).join(","));
115
+ logger?.debug("Delegation headers injected", {
116
+ delegationRef: ctx.delegationRef,
117
+ targetHostname,
118
+ });
119
+ }
120
+ catch (error) {
121
+ logger?.warn(`Failed to build delegation proof, continuing without delegation headers: ${error.message}`);
122
+ }
123
+ }
124
+ }
125
+ return originalFetch(input, { ...init, headers });
126
+ };
127
+ delegationFetch[DELEGATION_INTERCEPTED_FLAG] = true;
128
+ globalThis.fetch = delegationFetch;
129
+ return () => {
130
+ if (globalThis.fetch === delegationFetch) {
131
+ globalThis.fetch = originalFetch;
132
+ }
133
+ };
134
+ }
@@ -1,93 +1,18 @@
1
1
  /**
2
- * Proof Generation for XMCP-I Runtime
2
+ * Proof Generation for XMCP-I Runtime — Node.js adapter
3
3
  *
4
- * Handles JCS canonicalization, SHA-256 digest generation, and Ed25519 JWS signing (compact format)
5
- * according to requirements 5.1, 5.2, 5.3, 5.6.
4
+ * Re-exports ProofGenerator from @kya-os/mcp-i-core and wires in
5
+ * NodeCryptoProvider as the default. All existing call sites remain
6
+ * compatible: `new ProofGenerator(identity)` continues to work.
7
+ *
8
+ * Requirements: 5.1, 5.2, 5.3, 5.6
6
9
  */
7
- import { DetachedProof } from "@kya-os/contracts/proof";
10
+ import { ProofGenerator as CoreProofGenerator, type ProofAgentIdentity, type ToolRequest, type ToolResponse, type ProofOptions, extractCanonicalData } from "@kya-os/mcp-i-core";
11
+ import { CryptoProvider } from "@kya-os/mcp-i-core";
8
12
  import { SessionContext } from "@kya-os/contracts/handshake";
9
- import { AgentIdentity } from "./identity";
10
- /**
11
- * Tool request structure for proof generation
12
- */
13
- export interface ToolRequest {
14
- method: string;
15
- params?: any;
16
- }
17
- /**
18
- * Tool response structure for proof generation
19
- */
20
- export interface ToolResponse {
21
- data: any;
22
- meta?: {
23
- proof?: DetachedProof;
24
- [key: string]: any;
25
- };
26
- }
27
- /**
28
- * Proof generation options
29
- */
30
- export interface ProofOptions {
31
- scopeId?: string;
32
- delegationRef?: string;
33
- clientDid?: string;
13
+ export declare class ProofGenerator extends CoreProofGenerator {
14
+ constructor(identity: ProofAgentIdentity, cryptoProvider?: CryptoProvider);
34
15
  }
35
- /**
36
- * Proof generator class
37
- */
38
- export declare class ProofGenerator {
39
- private identity;
40
- constructor(identity: AgentIdentity);
41
- /**
42
- * Generate proof for tool request/response
43
- * Requirements: 5.1, 5.2, 5.3, 5.6
44
- */
45
- generateProof(request: ToolRequest, response: ToolResponse, session: SessionContext, options?: ProofOptions): Promise<DetachedProof>;
46
- /**
47
- * Generate canonical hashes for request and response
48
- * Requirement: 5.1
49
- */
50
- private generateCanonicalHashes;
51
- /**
52
- * Generate SHA-256 hash with JCS canonicalization
53
- * Requirement: 5.2
54
- */
55
- private generateSHA256Hash;
56
- /**
57
- * JCS canonicalization implementation (RFC 8785)
58
- */
59
- private canonicalizeJSON;
60
- /**
61
- * Generate Ed25519 JWS in compact format (header.payload.signature)
62
- * Requirement: 5.3
63
- *
64
- * Uses standard JWT claims (aud, sub, iss) in addition to custom claims
65
- */
66
- private generateJWS;
67
- /**
68
- * Format base64 private key as PKCS#8 PEM for JOSE library
69
- */
70
- private formatPrivateKeyAsPEM;
71
- /**
72
- * Verify a proof (for testing/validation)
73
- */
74
- verifyProof(proof: DetachedProof, request: ToolRequest, response: ToolResponse): Promise<boolean>;
75
- /**
76
- * Convert base64 public key to Ed25519 JWK format
77
- */
78
- private base64PublicKeyToJWK;
79
- }
80
- /**
81
- * Utility functions
82
- */
83
- /**
84
- * Create a tool response with proof
85
- */
86
- export declare function createProofResponse(request: ToolRequest, data: any, identity: AgentIdentity, session: SessionContext, options?: ProofOptions): Promise<ToolResponse>;
87
- /**
88
- * Extract canonical data for hashing (utility for testing)
89
- */
90
- export declare function extractCanonicalData(request: ToolRequest, response: ToolResponse): {
91
- request: any;
92
- response: any;
93
- };
16
+ export type { ProofAgentIdentity, ToolRequest, ToolResponse, ProofOptions };
17
+ export { extractCanonicalData };
18
+ export declare function createProofResponse(request: ToolRequest, data: unknown, identity: ProofAgentIdentity, session: SessionContext, options?: ProofOptions): Promise<ToolResponse>;
@@ -1,227 +1,25 @@
1
1
  "use strict";
2
2
  /**
3
- * Proof Generation for XMCP-I Runtime
3
+ * Proof Generation for XMCP-I Runtime — Node.js adapter
4
4
  *
5
- * Handles JCS canonicalization, SHA-256 digest generation, and Ed25519 JWS signing (compact format)
6
- * according to requirements 5.1, 5.2, 5.3, 5.6.
5
+ * Re-exports ProofGenerator from @kya-os/mcp-i-core and wires in
6
+ * NodeCryptoProvider as the default. All existing call sites remain
7
+ * compatible: `new ProofGenerator(identity)` continues to work.
8
+ *
9
+ * Requirements: 5.1, 5.2, 5.3, 5.6
7
10
  */
8
11
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.ProofGenerator = void 0;
12
+ exports.extractCanonicalData = exports.ProofGenerator = void 0;
10
13
  exports.createProofResponse = createProofResponse;
11
- exports.extractCanonicalData = extractCanonicalData;
12
- const crypto_1 = require("crypto");
13
- const jose_1 = require("jose");
14
- const json_canonicalize_1 = require("json-canonicalize");
15
14
  const mcp_i_core_1 = require("@kya-os/mcp-i-core");
15
+ Object.defineProperty(exports, "extractCanonicalData", { enumerable: true, get: function () { return mcp_i_core_1.extractCanonicalData; } });
16
16
  const node_providers_1 = require("../providers/node-providers");
17
- /**
18
- * Proof generator class
19
- */
20
- class ProofGenerator {
21
- identity;
22
- constructor(identity) {
23
- this.identity = identity;
24
- }
25
- /**
26
- * Generate proof for tool request/response
27
- * Requirements: 5.1, 5.2, 5.3, 5.6
28
- */
29
- async generateProof(request, response, session, options = {}) {
30
- // Generate canonical hashes
31
- const hashes = this.generateCanonicalHashes(request, response);
32
- // Create proof metadata
33
- const meta = {
34
- did: this.identity.did,
35
- kid: this.identity.kid,
36
- ts: Math.floor(Date.now() / 1000),
37
- nonce: session.nonce,
38
- audience: session.audience,
39
- sessionId: session.sessionId,
40
- requestHash: hashes.requestHash,
41
- responseHash: hashes.responseHash,
42
- ...options, // Include scopeId, delegationRef, and clientDid if provided
43
- };
44
- // Generate JWS (compact format)
45
- const jws = await this.generateJWS(meta);
46
- return {
47
- jws,
48
- meta,
49
- };
50
- }
51
- /**
52
- * Generate canonical hashes for request and response
53
- * Requirement: 5.1
54
- */
55
- generateCanonicalHashes(request, response) {
56
- // Canonicalize request (exclude transport metadata, include method and params)
57
- const canonicalRequest = {
58
- method: request.method,
59
- ...(request.params && { params: request.params }),
60
- };
61
- // Canonicalize response (only the data part, exclude meta)
62
- const canonicalResponse = response.data;
63
- // Generate SHA-256 hashes with JCS canonicalization
64
- const requestHash = this.generateSHA256Hash(canonicalRequest);
65
- const responseHash = this.generateSHA256Hash(canonicalResponse);
66
- return {
67
- requestHash,
68
- responseHash,
69
- };
70
- }
71
- /**
72
- * Generate SHA-256 hash with JCS canonicalization
73
- * Requirement: 5.2
74
- */
75
- generateSHA256Hash(data) {
76
- // JCS (JSON Canonicalization Scheme) canonicalization
77
- const canonicalJson = this.canonicalizeJSON(data);
78
- // Generate SHA-256 hash
79
- const hash = (0, crypto_1.createHash)("sha256")
80
- .update(canonicalJson, "utf8")
81
- .digest("hex");
82
- return `sha256:${hash}`;
83
- }
84
- /**
85
- * JCS canonicalization implementation (RFC 8785)
86
- */
87
- canonicalizeJSON(obj) {
88
- return (0, json_canonicalize_1.canonicalize)(obj);
89
- }
90
- /**
91
- * Generate Ed25519 JWS in compact format (header.payload.signature)
92
- * Requirement: 5.3
93
- *
94
- * Uses standard JWT claims (aud, sub, iss) in addition to custom claims
95
- */
96
- async generateJWS(meta) {
97
- try {
98
- // Import the private key
99
- const privateKeyPem = this.formatPrivateKeyAsPEM(this.identity.privateKey);
100
- const privateKey = await (0, jose_1.importPKCS8)(privateKeyPem, "EdDSA");
101
- // Create JWT payload with standard claims + custom proof data
102
- // Standard JWT claims: aud (audience), sub (subject), iss (issuer)
103
- // Custom claims: requestHash, responseHash, nonce, sessionId, etc.
104
- const payload = {
105
- // Standard JWT claims (RFC 7519)
106
- aud: meta.audience, // Audience (who the token is for)
107
- sub: meta.did, // Subject (agent DID)
108
- iss: meta.did, // Issuer (agent DID - self-issued)
109
- // Custom MCP-I proof claims
110
- requestHash: meta.requestHash,
111
- responseHash: meta.responseHash,
112
- ts: meta.ts,
113
- nonce: meta.nonce,
114
- sessionId: meta.sessionId,
115
- // Optional claims
116
- ...(meta.scopeId && { scopeId: meta.scopeId }),
117
- ...(meta.delegationRef && { delegationRef: meta.delegationRef }),
118
- ...(meta.clientDid && { clientDid: meta.clientDid }),
119
- };
120
- // Create and sign JWT (compact format: header.payload.signature)
121
- const jwt = await new jose_1.SignJWT(payload)
122
- .setProtectedHeader({
123
- alg: "EdDSA",
124
- kid: this.identity.kid,
125
- })
126
- .sign(privateKey);
127
- // Return full compact JWS (NOT detached)
128
- return jwt;
129
- }
130
- catch (error) {
131
- throw new Error(`Failed to generate JWS: ${error instanceof Error ? error.message : "Unknown error"}`);
132
- }
133
- }
134
- /**
135
- * Format base64 private key as PKCS#8 PEM for JOSE library
136
- */
137
- formatPrivateKeyAsPEM(base64PrivateKey) {
138
- const keyData = Buffer.from(base64PrivateKey, "base64");
139
- // Ed25519 PKCS#8 header and footer
140
- const header = "-----BEGIN PRIVATE KEY-----\n";
141
- const footer = "\n-----END PRIVATE KEY-----";
142
- // Wrap Ed25519 raw key in PKCS#8 structure (ASN.1 encoding)
143
- const pkcs8Header = Buffer.from([
144
- 0x30,
145
- 0x2e, // SEQUENCE, length 46
146
- 0x02,
147
- 0x01,
148
- 0x00, // INTEGER version 0
149
- 0x30,
150
- 0x05, // SEQUENCE, length 5
151
- 0x06,
152
- 0x03,
153
- 0x2b,
154
- 0x65,
155
- 0x70, // OID for Ed25519
156
- 0x04,
157
- 0x22, // OCTET STRING, length 34
158
- 0x04,
159
- 0x20, // OCTET STRING, length 32 (the actual key)
160
- ]);
161
- const fullKey = Buffer.concat([pkcs8Header, keyData.subarray(0, 32)]);
162
- const base64Key = fullKey.toString("base64");
163
- // Format as PEM with line breaks every 64 characters
164
- const formattedKey = base64Key.match(/.{1,64}/g)?.join("\n") || base64Key;
165
- return header + formattedKey + footer;
166
- }
167
- /**
168
- * Verify a proof (for testing/validation)
169
- */
170
- async verifyProof(proof, request, response) {
171
- try {
172
- // Regenerate hashes
173
- const expectedHashes = this.generateCanonicalHashes(request, response);
174
- // Check if hashes match
175
- if (proof.meta.requestHash !== expectedHashes.requestHash ||
176
- proof.meta.responseHash !== expectedHashes.responseHash) {
177
- return false;
178
- }
179
- // Verify JWS signature using CryptoService
180
- const publicKeyJwk = this.base64PublicKeyToJWK(this.identity.publicKey);
181
- const cryptoProvider = new node_providers_1.NodeCryptoProvider();
182
- const cryptoService = new mcp_i_core_1.CryptoService(cryptoProvider);
183
- const isValid = await cryptoService.verifyJWS(proof.jws, publicKeyJwk, {
184
- expectedKid: this.identity.kid,
185
- alg: "EdDSA",
186
- });
187
- return isValid;
188
- }
189
- catch (error) {
190
- console.error("[ProofGenerator] Proof verification error:", error);
191
- return false;
192
- }
193
- }
194
- /**
195
- * Convert base64 public key to Ed25519 JWK format
196
- */
197
- base64PublicKeyToJWK(publicKeyBase64) {
198
- // Decode base64 to bytes
199
- const publicKeyBytes = Buffer.from(publicKeyBase64, "base64");
200
- // Verify key length (Ed25519 public keys are 32 bytes)
201
- if (publicKeyBytes.length !== 32) {
202
- throw new Error(`Invalid Ed25519 public key length: ${publicKeyBytes.length}`);
203
- }
204
- // Convert to base64url encoding
205
- const base64url = Buffer.from(publicKeyBytes)
206
- .toString("base64")
207
- .replace(/\+/g, "-")
208
- .replace(/\//g, "_")
209
- .replace(/=/g, "");
210
- return {
211
- kty: "OKP",
212
- crv: "Ed25519",
213
- x: base64url,
214
- kid: this.identity.kid,
215
- };
17
+ class ProofGenerator extends mcp_i_core_1.ProofGenerator {
18
+ constructor(identity, cryptoProvider = new node_providers_1.NodeCryptoProvider()) {
19
+ super(identity, cryptoProvider);
216
20
  }
217
21
  }
218
22
  exports.ProofGenerator = ProofGenerator;
219
- /**
220
- * Utility functions
221
- */
222
- /**
223
- * Create a tool response with proof
224
- */
225
23
  async function createProofResponse(request, data, identity, session, options = {}) {
226
24
  const response = { data };
227
25
  const proofGenerator = new ProofGenerator(identity);
@@ -229,15 +27,3 @@ async function createProofResponse(request, data, identity, session, options = {
229
27
  response.meta = { proof };
230
28
  return response;
231
29
  }
232
- /**
233
- * Extract canonical data for hashing (utility for testing)
234
- */
235
- function extractCanonicalData(request, response) {
236
- return {
237
- request: {
238
- method: request.method,
239
- ...(request.params && { params: request.params }),
240
- },
241
- response: response.data,
242
- };
243
- }