@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.
- package/dist/225.js +1 -0
- package/dist/227.js +1 -0
- package/dist/242.js +1 -0
- package/dist/283.js +1 -0
- package/dist/295.js +1 -1
- package/dist/354.js +1 -0
- package/dist/361.js +1 -0
- package/dist/452.js +1 -0
- package/dist/533.js +1 -0
- package/dist/622.js +1 -0
- package/dist/65.js +1 -0
- package/dist/843.js +1 -0
- package/dist/861.js +1 -0
- package/dist/866.js +1 -1
- package/dist/914.js +1 -0
- package/dist/95.js +1 -0
- package/dist/{941.js → 966.js} +1 -1
- package/dist/providers/node-providers.d.ts +1 -1
- package/dist/providers/node-providers.js +2 -2
- package/dist/runtime/adapter-express.js +6 -6
- package/dist/runtime/adapter-nextjs.js +6 -6
- package/dist/runtime/auth-handshake.d.ts +4 -159
- package/dist/runtime/auth-handshake.js +8 -249
- package/dist/runtime/http.js +6 -6
- package/dist/runtime/mcpi-runtime.d.ts +4 -0
- package/dist/runtime/mcpi-runtime.js +58 -43
- package/dist/runtime/outbound-delegation.d.ts +34 -0
- package/dist/runtime/outbound-delegation.js +134 -0
- package/dist/runtime/proof.d.ts +13 -88
- package/dist/runtime/proof.js +11 -225
- package/dist/runtime/request-context.d.ts +41 -0
- package/dist/runtime/request-context.js +48 -0
- package/dist/runtime/session.d.ts +13 -104
- package/dist/runtime/session.js +31 -267
- package/dist/runtime/stdio.js +6 -6
- package/dist/runtime/utils/tools.js +17 -3
- package/dist/runtime/verifier-middleware.js +4 -4
- package/package.json +19 -19
- package/dist/207.js +0 -1
- package/dist/25.js +0 -1
- package/dist/360.js +0 -1
- package/dist/387.js +0 -1
- package/dist/406.js +0 -1
- package/dist/448.js +0 -1
- package/dist/478.js +0 -1
- package/dist/575.js +0 -1
- package/dist/67.js +0 -1
- package/dist/743.js +0 -1
- package/dist/784.js +0 -1
- package/dist/844.js +0 -1
- package/dist/936.js +0 -1
- 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,
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
this.
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/runtime/proof.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
5
|
-
*
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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>;
|
package/dist/runtime/proof.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
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
|
-
}
|