@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
|
@@ -8,10 +8,21 @@
|
|
|
8
8
|
* was set during handshake processing.
|
|
9
9
|
*/
|
|
10
10
|
import type { SessionContext } from '@kya-os/contracts/handshake';
|
|
11
|
+
import type { Ed25519PrivateJWK } from '@kya-os/mcp-i-core';
|
|
11
12
|
export interface RequestContext {
|
|
12
13
|
session?: SessionContext;
|
|
13
14
|
requestId?: string;
|
|
14
15
|
startTime?: number;
|
|
16
|
+
/** Delegation VC credential ID (set after delegation verification succeeds) */
|
|
17
|
+
delegationRef?: string;
|
|
18
|
+
/** Reference chain string — format: vc_id_1>del_id_1>... */
|
|
19
|
+
delegationChain?: string;
|
|
20
|
+
/** Scopes granted by the active delegation */
|
|
21
|
+
delegationScopes?: string[];
|
|
22
|
+
/** Agent Ed25519 private key JWK for delegation proof signing */
|
|
23
|
+
delegationPrivateKeyJwk?: Ed25519PrivateJWK;
|
|
24
|
+
/** Key ID used in delegation proof JWT header */
|
|
25
|
+
delegationAgentKid?: string;
|
|
15
26
|
}
|
|
16
27
|
/**
|
|
17
28
|
* Run a function with request context
|
|
@@ -35,3 +46,33 @@ export declare function getCurrentAgentDid(): string | undefined;
|
|
|
35
46
|
* NOTE: This only works if called within a runWithContext block
|
|
36
47
|
*/
|
|
37
48
|
export declare function setSession(session: SessionContext): void;
|
|
49
|
+
/**
|
|
50
|
+
* Update delegation context fields in the current request context.
|
|
51
|
+
* Called after delegation is verified for a protected tool call.
|
|
52
|
+
*
|
|
53
|
+
* NOTE: This only works if called within a runWithContext block
|
|
54
|
+
*/
|
|
55
|
+
export declare function setDelegationContext(fields: {
|
|
56
|
+
delegationRef: string;
|
|
57
|
+
delegationChain: string;
|
|
58
|
+
delegationScopes: string[];
|
|
59
|
+
delegationPrivateKeyJwk?: Ed25519PrivateJWK;
|
|
60
|
+
delegationAgentKid?: string;
|
|
61
|
+
}): void;
|
|
62
|
+
/**
|
|
63
|
+
* Convenience helper that wires delegation metadata from a verified delegation
|
|
64
|
+
* result and agent identity into AsyncLocalStorage in a single call.
|
|
65
|
+
*
|
|
66
|
+
* Avoids duplicating the base64-to-base64url JWK conversion and the
|
|
67
|
+
* setDelegationContext call across mcpi-runtime.ts and utils/tools.ts.
|
|
68
|
+
*/
|
|
69
|
+
export declare function setDelegationContextFromIdentity(params: {
|
|
70
|
+
delegationId: string;
|
|
71
|
+
delegationChain: string;
|
|
72
|
+
delegationScopes: string[];
|
|
73
|
+
identity: {
|
|
74
|
+
privateKey: string;
|
|
75
|
+
publicKey: string;
|
|
76
|
+
kid: string;
|
|
77
|
+
} | null | undefined;
|
|
78
|
+
}): void;
|
|
@@ -14,6 +14,8 @@ exports.getContext = getContext;
|
|
|
14
14
|
exports.getCurrentSession = getCurrentSession;
|
|
15
15
|
exports.getCurrentAgentDid = getCurrentAgentDid;
|
|
16
16
|
exports.setSession = setSession;
|
|
17
|
+
exports.setDelegationContext = setDelegationContext;
|
|
18
|
+
exports.setDelegationContextFromIdentity = setDelegationContextFromIdentity;
|
|
17
19
|
const async_hooks_1 = require("async_hooks");
|
|
18
20
|
const asyncLocalStorage = new async_hooks_1.AsyncLocalStorage();
|
|
19
21
|
/**
|
|
@@ -54,3 +56,49 @@ function setSession(session) {
|
|
|
54
56
|
console.warn('[RequestContext] Attempted to set session outside of request context');
|
|
55
57
|
}
|
|
56
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Update delegation context fields in the current request context.
|
|
61
|
+
* Called after delegation is verified for a protected tool call.
|
|
62
|
+
*
|
|
63
|
+
* NOTE: This only works if called within a runWithContext block
|
|
64
|
+
*/
|
|
65
|
+
function setDelegationContext(fields) {
|
|
66
|
+
const context = getContext();
|
|
67
|
+
if (!context) {
|
|
68
|
+
console.warn('[RequestContext] setDelegationContext called outside of runWithContext — delegation headers will not be injected');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
context.delegationRef = fields.delegationRef;
|
|
72
|
+
context.delegationChain = fields.delegationChain;
|
|
73
|
+
context.delegationScopes = fields.delegationScopes;
|
|
74
|
+
if (fields.delegationPrivateKeyJwk !== undefined) {
|
|
75
|
+
context.delegationPrivateKeyJwk = fields.delegationPrivateKeyJwk;
|
|
76
|
+
}
|
|
77
|
+
if (fields.delegationAgentKid !== undefined) {
|
|
78
|
+
context.delegationAgentKid = fields.delegationAgentKid;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Convenience helper that wires delegation metadata from a verified delegation
|
|
83
|
+
* result and agent identity into AsyncLocalStorage in a single call.
|
|
84
|
+
*
|
|
85
|
+
* Avoids duplicating the base64-to-base64url JWK conversion and the
|
|
86
|
+
* setDelegationContext call across mcpi-runtime.ts and utils/tools.ts.
|
|
87
|
+
*/
|
|
88
|
+
function setDelegationContextFromIdentity(params) {
|
|
89
|
+
setDelegationContext({
|
|
90
|
+
delegationRef: params.delegationId,
|
|
91
|
+
delegationChain: params.delegationChain,
|
|
92
|
+
delegationScopes: params.delegationScopes,
|
|
93
|
+
delegationPrivateKeyJwk: params.identity?.privateKey
|
|
94
|
+
? {
|
|
95
|
+
kty: "OKP",
|
|
96
|
+
crv: "Ed25519",
|
|
97
|
+
d: Buffer.from(params.identity.privateKey, "base64").toString("base64url"),
|
|
98
|
+
x: Buffer.from(params.identity.publicKey, "base64").toString("base64url"),
|
|
99
|
+
kid: params.identity.kid,
|
|
100
|
+
}
|
|
101
|
+
: undefined,
|
|
102
|
+
delegationAgentKid: params.identity?.kid,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -1,108 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Session Management for XMCP-I Runtime â Node.js adapter
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
serverDid?: string;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Handshake validation result
|
|
20
|
-
*/
|
|
21
|
-
export interface HandshakeResult {
|
|
22
|
-
success: boolean;
|
|
23
|
-
session?: SessionContext;
|
|
24
|
-
error?: {
|
|
25
|
-
code: string;
|
|
26
|
-
message: string;
|
|
27
|
-
remediation?: string;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Session manager class
|
|
32
|
-
*/
|
|
33
|
-
export declare class SessionManager {
|
|
34
|
-
private config;
|
|
35
|
-
private sessions;
|
|
36
|
-
constructor(config?: SessionConfig);
|
|
37
|
-
/**
|
|
38
|
-
* Set server DID for session creation
|
|
39
|
-
* Called after identity is loaded
|
|
40
|
-
*/
|
|
41
|
-
setServerDid(serverDid: string): void;
|
|
42
|
-
/**
|
|
43
|
-
* Validate handshake and create or retrieve session
|
|
44
|
-
* Requirements: 4.5, 4.6, 4.7, 4.8, 4.9
|
|
45
|
-
*/
|
|
46
|
-
validateHandshake(request: HandshakeRequest): Promise<HandshakeResult>;
|
|
47
|
-
/**
|
|
48
|
-
* Get session by ID and update last activity
|
|
49
|
-
*/
|
|
50
|
-
getSession(sessionId: string): Promise<SessionContext | null>;
|
|
51
|
-
/**
|
|
52
|
-
* Generate a unique session ID
|
|
53
|
-
* Uses mcpi_{uuid} format for MCP protocol compliance and traceability
|
|
54
|
-
*/
|
|
55
|
-
private generateSessionId;
|
|
56
|
-
/**
|
|
57
|
-
* Generate a deterministic client identifier when the client
|
|
58
|
-
* does not provide one during the handshake.
|
|
59
|
-
*/
|
|
60
|
-
private generateClientId;
|
|
61
|
-
/**
|
|
62
|
-
* Normalize string fields from handshake metadata
|
|
63
|
-
*/
|
|
64
|
-
private normalizeClientInfoString;
|
|
65
|
-
/**
|
|
66
|
-
* Build MCP client metadata for the session when provided during handshake
|
|
67
|
-
*/
|
|
68
|
-
private buildClientInfo;
|
|
69
|
-
/**
|
|
70
|
-
* Generate a cryptographically secure nonce
|
|
71
|
-
*/
|
|
72
|
-
static generateNonce(): string;
|
|
73
|
-
/**
|
|
74
|
-
* Cleanup expired sessions and nonces
|
|
75
|
-
*/
|
|
76
|
-
cleanup(): Promise<void>;
|
|
77
|
-
/**
|
|
78
|
-
* Get session statistics
|
|
79
|
-
*/
|
|
80
|
-
getStats(): {
|
|
81
|
-
activeSessions: number;
|
|
82
|
-
config: {
|
|
83
|
-
timestampSkewSeconds: number;
|
|
84
|
-
sessionTtlMinutes: number;
|
|
85
|
-
absoluteSessionLifetime?: number;
|
|
86
|
-
cacheType: string;
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
/**
|
|
90
|
-
* Clear all sessions (useful for testing)
|
|
91
|
-
*/
|
|
92
|
-
clearSessions(): void;
|
|
4
|
+
* Re-exports SessionManager from @kya-os/mcp-i-core and wires in
|
|
5
|
+
* NodeCryptoProvider as the default. All existing call sites remain
|
|
6
|
+
* compatible: `new SessionManager()` and `new SessionManager(config)` work.
|
|
7
|
+
*
|
|
8
|
+
* Requirements: 4.5-4.9, 19.1-19.2
|
|
9
|
+
*/
|
|
10
|
+
import { SessionManager as CoreSessionManager, createHandshakeRequest, validateHandshakeFormat, type SessionConfig, type HandshakeResult } from "@kya-os/mcp-i-core";
|
|
11
|
+
import { CryptoProvider } from "@kya-os/mcp-i-core";
|
|
12
|
+
export type { SessionConfig, HandshakeResult };
|
|
13
|
+
export { createHandshakeRequest, validateHandshakeFormat };
|
|
14
|
+
export declare class SessionManager extends CoreSessionManager {
|
|
15
|
+
constructor(configOrCrypto?: SessionConfig | CryptoProvider, config?: SessionConfig);
|
|
93
16
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Default session manager instance
|
|
96
|
-
*/
|
|
97
17
|
export declare const defaultSessionManager: SessionManager;
|
|
98
|
-
/**
|
|
99
|
-
* Utility functions
|
|
100
|
-
*/
|
|
101
|
-
/**
|
|
102
|
-
* Create a handshake request
|
|
103
|
-
*/
|
|
104
|
-
export declare function createHandshakeRequest(audience: string): HandshakeRequest;
|
|
105
|
-
/**
|
|
106
|
-
* Validate handshake request format
|
|
107
|
-
*/
|
|
108
|
-
export declare function validateHandshakeFormat(request: any): request is HandshakeRequest;
|
package/dist/runtime/session.js
CHANGED
|
@@ -1,278 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Session Management for XMCP-I Runtime â Node.js adapter
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Re-exports SessionManager from @kya-os/mcp-i-core and wires in
|
|
6
|
+
* NodeCryptoProvider as the default. All existing call sites remain
|
|
7
|
+
* compatible: `new SessionManager()` and `new SessionManager(config)` work.
|
|
8
|
+
*
|
|
9
|
+
* Requirements: 4.5-4.9, 19.1-19.2
|
|
7
10
|
*/
|
|
8
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.defaultSessionManager = exports.SessionManager = void 0;
|
|
10
|
-
|
|
11
|
-
exports.
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
console.warn("Warning: Using MemoryNonceCache - not suitable for multi-instance deployments. " +
|
|
33
|
-
"Consider using Redis, DynamoDB, or Cloudflare KV for production.");
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Set server DID for session creation
|
|
38
|
-
* Called after identity is loaded
|
|
39
|
-
*/
|
|
40
|
-
setServerDid(serverDid) {
|
|
41
|
-
this.config.serverDid = serverDid;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Validate handshake and create or retrieve session
|
|
45
|
-
* Requirements: 4.5, 4.6, 4.7, 4.8, 4.9
|
|
46
|
-
*/
|
|
47
|
-
async validateHandshake(request) {
|
|
48
|
-
try {
|
|
49
|
-
// Validate timestamp (±120s clock skew by default)
|
|
50
|
-
const now = Math.floor(Date.now() / 1000);
|
|
51
|
-
const timeDiff = Math.abs(now - request.timestamp);
|
|
52
|
-
if (timeDiff > this.config.timestampSkewSeconds) {
|
|
53
|
-
return {
|
|
54
|
-
success: false,
|
|
55
|
-
error: {
|
|
56
|
-
code: "XMCP_I_EHANDSHAKE",
|
|
57
|
-
message: `Timestamp outside acceptable range (±${this.config.timestampSkewSeconds}s)`,
|
|
58
|
-
remediation: `Check NTP sync on client and server. Current server time: ${now}, received: ${request.timestamp}, diff: ${timeDiff}s. Adjust XMCP_I_TS_SKEW_SEC if needed.`,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
// Validate nonce (must be unique within session window)
|
|
63
|
-
const nonceExists = await this.config.nonceCache.has(request.nonce, request.agentDid);
|
|
64
|
-
if (nonceExists) {
|
|
65
|
-
return {
|
|
66
|
-
success: false,
|
|
67
|
-
error: {
|
|
68
|
-
code: "XMCP_I_EHANDSHAKE",
|
|
69
|
-
message: "Nonce already used (replay attack prevention)",
|
|
70
|
-
remediation: "Generate a new unique nonce for each request",
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
// Add nonce to cache with TTL >= session TTL
|
|
75
|
-
const nonceTtlSeconds = this.config.sessionTtlMinutes * 60 + 60; // Session TTL + 1 minute buffer
|
|
76
|
-
await this.config.nonceCache.add(request.nonce, nonceTtlSeconds, request.agentDid);
|
|
77
|
-
// Generate session ID
|
|
78
|
-
const sessionId = this.generateSessionId();
|
|
79
|
-
const clientInfo = this.buildClientInfo(request);
|
|
80
|
-
// Create session context
|
|
81
|
-
// Phase 5: Sessions start anonymous until OAuth completes
|
|
82
|
-
const session = {
|
|
83
|
-
sessionId,
|
|
84
|
-
audience: request.audience,
|
|
85
|
-
nonce: request.nonce,
|
|
86
|
-
timestamp: request.timestamp,
|
|
87
|
-
createdAt: now,
|
|
88
|
-
lastActivity: now,
|
|
89
|
-
ttlMinutes: this.config.sessionTtlMinutes,
|
|
90
|
-
identityState: "anonymous", // Phase 5: Anonymous until OAuth
|
|
91
|
-
agentDid: request.agentDid, // Pass through agent DID for delegation verification
|
|
92
|
-
...(this.config.serverDid && { serverDid: this.config.serverDid }), // Include server DID if provided
|
|
93
|
-
...(clientInfo && { clientInfo }),
|
|
94
|
-
};
|
|
95
|
-
// Store session
|
|
96
|
-
this.sessions.set(sessionId, session);
|
|
97
|
-
return {
|
|
98
|
-
success: true,
|
|
99
|
-
session,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
catch (error) {
|
|
103
|
-
return {
|
|
104
|
-
success: false,
|
|
105
|
-
error: {
|
|
106
|
-
code: "XMCP_I_EHANDSHAKE",
|
|
107
|
-
message: `Handshake validation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Get session by ID and update last activity
|
|
114
|
-
*/
|
|
115
|
-
async getSession(sessionId) {
|
|
116
|
-
const session = this.sessions.get(sessionId);
|
|
117
|
-
if (!session) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
const now = Math.floor(Date.now() / 1000);
|
|
121
|
-
// Check if session has expired (idle timeout)
|
|
122
|
-
const idleTimeSeconds = now - session.lastActivity;
|
|
123
|
-
const maxIdleSeconds = session.ttlMinutes * 60;
|
|
124
|
-
if (idleTimeSeconds > maxIdleSeconds) {
|
|
125
|
-
this.sessions.delete(sessionId);
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
// Check absolute session lifetime if configured
|
|
129
|
-
if (this.config.absoluteSessionLifetime) {
|
|
130
|
-
const sessionAgeSeconds = now - session.createdAt;
|
|
131
|
-
const maxAgeSeconds = this.config.absoluteSessionLifetime * 60;
|
|
132
|
-
if (sessionAgeSeconds > maxAgeSeconds) {
|
|
133
|
-
this.sessions.delete(sessionId);
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// Update last activity
|
|
138
|
-
session.lastActivity = now;
|
|
139
|
-
this.sessions.set(sessionId, session);
|
|
140
|
-
return session;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Generate a unique session ID
|
|
144
|
-
* Uses mcpi_{uuid} format for MCP protocol compliance and traceability
|
|
145
|
-
*/
|
|
146
|
-
generateSessionId() {
|
|
147
|
-
// Generate UUID v4 using crypto random bytes
|
|
148
|
-
const bytes = (0, crypto_1.randomBytes)(16);
|
|
149
|
-
// Set version (4) and variant (RFC4122)
|
|
150
|
-
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
151
|
-
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
152
|
-
const hex = bytes.toString("hex");
|
|
153
|
-
const uuid = `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
154
|
-
return `mcpi_${uuid}`;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Generate a deterministic client identifier when the client
|
|
158
|
-
* does not provide one during the handshake.
|
|
159
|
-
*/
|
|
160
|
-
generateClientId() {
|
|
161
|
-
return `client_${(0, crypto_1.randomBytes)(6).toString("hex")}`;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Normalize string fields from handshake metadata
|
|
165
|
-
*/
|
|
166
|
-
normalizeClientInfoString(value) {
|
|
167
|
-
if (typeof value !== "string") {
|
|
168
|
-
return undefined;
|
|
169
|
-
}
|
|
170
|
-
const trimmed = value.trim();
|
|
171
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Build MCP client metadata for the session when provided during handshake
|
|
175
|
-
*/
|
|
176
|
-
buildClientInfo(request) {
|
|
177
|
-
const hasMetadata = !!request.clientInfo ||
|
|
178
|
-
typeof request.clientProtocolVersion === "string" ||
|
|
179
|
-
request.clientCapabilities !== undefined;
|
|
180
|
-
if (!hasMetadata) {
|
|
181
|
-
return undefined;
|
|
12
|
+
exports.defaultSessionManager = exports.SessionManager = exports.validateHandshakeFormat = exports.createHandshakeRequest = void 0;
|
|
13
|
+
const mcp_i_core_1 = require("@kya-os/mcp-i-core");
|
|
14
|
+
Object.defineProperty(exports, "createHandshakeRequest", { enumerable: true, get: function () { return mcp_i_core_1.createHandshakeRequest; } });
|
|
15
|
+
Object.defineProperty(exports, "validateHandshakeFormat", { enumerable: true, get: function () { return mcp_i_core_1.validateHandshakeFormat; } });
|
|
16
|
+
const mcp_i_core_2 = require("@kya-os/mcp-i-core");
|
|
17
|
+
const node_providers_1 = require("../providers/node-providers");
|
|
18
|
+
function resolveNodeConfig(config) {
|
|
19
|
+
const skewEnv = process.env["XMCP_I_TS_SKEW_SEC"];
|
|
20
|
+
const ttlEnv = process.env["XMCP_I_SESSION_TTL_MIN"];
|
|
21
|
+
const parsedSkew = skewEnv ? parseInt(skewEnv, 10) : undefined;
|
|
22
|
+
const parsedTtl = ttlEnv ? parseInt(ttlEnv, 10) : undefined;
|
|
23
|
+
return {
|
|
24
|
+
...config,
|
|
25
|
+
timestampSkewSeconds: config.timestampSkewSeconds ??
|
|
26
|
+
(parsedSkew !== undefined && !isNaN(parsedSkew) ? parsedSkew : undefined),
|
|
27
|
+
sessionTtlMinutes: config.sessionTtlMinutes ??
|
|
28
|
+
(parsedTtl !== undefined && !isNaN(parsedTtl) ? parsedTtl : undefined),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
class SessionManager extends mcp_i_core_1.SessionManager {
|
|
32
|
+
constructor(configOrCrypto, config) {
|
|
33
|
+
if (configOrCrypto instanceof mcp_i_core_2.CryptoProvider) {
|
|
34
|
+
super(configOrCrypto, resolveNodeConfig(config ?? {}));
|
|
182
35
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
name: this.normalizeClientInfoString(source?.name) ?? "unknown",
|
|
186
|
-
title: this.normalizeClientInfoString(source?.title),
|
|
187
|
-
version: this.normalizeClientInfoString(source?.version),
|
|
188
|
-
platform: this.normalizeClientInfoString(source?.platform),
|
|
189
|
-
vendor: this.normalizeClientInfoString(source?.vendor),
|
|
190
|
-
persistentId: this.normalizeClientInfoString(source?.persistentId),
|
|
191
|
-
clientId: this.normalizeClientInfoString(source?.clientId) ??
|
|
192
|
-
this.generateClientId(),
|
|
193
|
-
protocolVersion: this.normalizeClientInfoString(request.clientProtocolVersion),
|
|
194
|
-
capabilities: request.clientCapabilities,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Generate a cryptographically secure nonce
|
|
199
|
-
*/
|
|
200
|
-
static generateNonce() {
|
|
201
|
-
return (0, crypto_1.randomBytes)(16).toString("base64url"); // 128-bit nonce
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Cleanup expired sessions and nonces
|
|
205
|
-
*/
|
|
206
|
-
async cleanup() {
|
|
207
|
-
const now = Math.floor(Date.now() / 1000);
|
|
208
|
-
// Clean up expired sessions
|
|
209
|
-
for (const [sessionId, session] of this.sessions.entries()) {
|
|
210
|
-
const idleTimeSeconds = now - session.lastActivity;
|
|
211
|
-
const maxIdleSeconds = session.ttlMinutes * 60;
|
|
212
|
-
let expired = idleTimeSeconds > maxIdleSeconds;
|
|
213
|
-
// Check absolute lifetime
|
|
214
|
-
if (!expired && this.config.absoluteSessionLifetime) {
|
|
215
|
-
const sessionAgeSeconds = now - session.createdAt;
|
|
216
|
-
const maxAgeSeconds = this.config.absoluteSessionLifetime * 60;
|
|
217
|
-
expired = sessionAgeSeconds > maxAgeSeconds;
|
|
218
|
-
}
|
|
219
|
-
if (expired) {
|
|
220
|
-
this.sessions.delete(sessionId);
|
|
221
|
-
}
|
|
36
|
+
else {
|
|
37
|
+
super(new node_providers_1.NodeCryptoProvider(), resolveNodeConfig(configOrCrypto ?? {}));
|
|
222
38
|
}
|
|
223
|
-
// Clean up expired nonces
|
|
224
|
-
await this.config.nonceCache.cleanup();
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Get session statistics
|
|
228
|
-
*/
|
|
229
|
-
getStats() {
|
|
230
|
-
return {
|
|
231
|
-
activeSessions: this.sessions.size,
|
|
232
|
-
config: {
|
|
233
|
-
timestampSkewSeconds: this.config.timestampSkewSeconds,
|
|
234
|
-
sessionTtlMinutes: this.config.sessionTtlMinutes,
|
|
235
|
-
absoluteSessionLifetime: this.config.absoluteSessionLifetime,
|
|
236
|
-
cacheType: this.config.nonceCache.constructor.name,
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Clear all sessions (useful for testing)
|
|
242
|
-
*/
|
|
243
|
-
clearSessions() {
|
|
244
|
-
this.sessions.clear();
|
|
245
39
|
}
|
|
246
40
|
}
|
|
247
41
|
exports.SessionManager = SessionManager;
|
|
248
|
-
/**
|
|
249
|
-
* Default session manager instance
|
|
250
|
-
*/
|
|
251
42
|
exports.defaultSessionManager = new SessionManager();
|
|
252
|
-
/**
|
|
253
|
-
* Utility functions
|
|
254
|
-
*/
|
|
255
|
-
/**
|
|
256
|
-
* Create a handshake request
|
|
257
|
-
*/
|
|
258
|
-
function createHandshakeRequest(audience) {
|
|
259
|
-
return {
|
|
260
|
-
nonce: SessionManager.generateNonce(),
|
|
261
|
-
audience,
|
|
262
|
-
timestamp: Math.floor(Date.now() / 1000),
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Validate handshake request format
|
|
267
|
-
*/
|
|
268
|
-
function validateHandshakeFormat(request) {
|
|
269
|
-
return (typeof request === "object" &&
|
|
270
|
-
request !== null &&
|
|
271
|
-
typeof request.nonce === "string" &&
|
|
272
|
-
request.nonce.length > 0 &&
|
|
273
|
-
typeof request.audience === "string" &&
|
|
274
|
-
request.audience.length > 0 &&
|
|
275
|
-
typeof request.timestamp === "number" &&
|
|
276
|
-
request.timestamp > 0 &&
|
|
277
|
-
Number.isInteger(request.timestamp));
|
|
278
|
-
}
|