@kya-os/mcp-i-core 1.3.6 → 1.3.7-canary.clientinfo.20251126041014

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.
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ /**
3
+ * Session Registration Service
4
+ *
5
+ * Registers MCP sessions with the AgentShield dashboard, enabling
6
+ * visibility into which MCP clients are connecting to agents.
7
+ *
8
+ * This is a fire-and-forget service - session registration should not
9
+ * block tool execution or affect the user experience.
10
+ *
11
+ * @package @kya-os/mcp-i-core
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.SessionRegistrationService = void 0;
15
+ exports.createSessionRegistrationService = createSessionRegistrationService;
16
+ const agentshield_api_1 = require("@kya-os/contracts/agentshield-api");
17
+ /**
18
+ * Session Registration Service
19
+ *
20
+ * Registers MCP sessions with AgentShield for dashboard visibility.
21
+ * Designed to be non-blocking - failures are logged but don't throw.
22
+ */
23
+ class SessionRegistrationService {
24
+ config;
25
+ constructor(config) {
26
+ this.config = {
27
+ baseUrl: config.baseUrl,
28
+ apiKey: config.apiKey,
29
+ fetchProvider: config.fetchProvider,
30
+ logger: config.logger || (() => { }),
31
+ timeoutMs: config.timeoutMs ?? 5000,
32
+ };
33
+ }
34
+ /**
35
+ * Register a session with AgentShield
36
+ *
37
+ * This is a fire-and-forget operation. Failures are logged but don't throw.
38
+ * The method returns quickly and doesn't block the caller.
39
+ *
40
+ * @param request - Session registration request data
41
+ * @returns Result indicating success or failure
42
+ */
43
+ async registerSession(request) {
44
+ const sessionId = request.session_id;
45
+ try {
46
+ // Validate request
47
+ const validationResult = agentshield_api_1.registerSessionRequestSchema.safeParse(request);
48
+ if (!validationResult.success) {
49
+ const errorMsg = `Invalid session registration request: ${validationResult.error.message}`;
50
+ this.config.logger("[SessionRegistration] Validation failed", {
51
+ sessionId,
52
+ error: errorMsg,
53
+ });
54
+ return { success: false, sessionId, error: errorMsg };
55
+ }
56
+ const url = `${this.config.baseUrl}${agentshield_api_1.AGENTSHIELD_ENDPOINTS.SESSIONS}`;
57
+ this.config.logger("[SessionRegistration] Registering session", {
58
+ sessionId,
59
+ agentDid: request.agent_did,
60
+ clientName: request.client_info.name,
61
+ url,
62
+ });
63
+ // ✅ EMPIRICAL PROOF: Prepare request headers with correct auth format
64
+ const requestHeaders = {
65
+ "Content-Type": "application/json",
66
+ "X-AgentShield-Key": this.config.apiKey, // Fixed: Use X-AgentShield-Key instead of Authorization: Bearer
67
+ };
68
+ // ✅ EMPIRICAL PROOF: Log exact request details (sanitized for security)
69
+ const sanitizedHeaders = {
70
+ ...requestHeaders,
71
+ "X-AgentShield-Key": `${this.config.apiKey.slice(0, 8)}...${this.config.apiKey.slice(-4)}`, // Show first 8 and last 4 chars
72
+ };
73
+ const sanitizedBody = {
74
+ session_id: request.session_id,
75
+ agent_did: request.agent_did,
76
+ project_id: request.project_id,
77
+ created_at: request.created_at,
78
+ client_info: request.client_info,
79
+ client_identity: request.client_identity,
80
+ server_did: request.server_did,
81
+ ttl_minutes: request.ttl_minutes,
82
+ };
83
+ this.config.logger("[SessionRegistration] 🔍 EMPIRICAL DEBUG - Request details", {
84
+ url,
85
+ method: "POST",
86
+ headers: sanitizedHeaders,
87
+ headerKeys: Object.keys(requestHeaders),
88
+ authHeaderPresent: !!requestHeaders["X-AgentShield-Key"],
89
+ authHeaderName: "X-AgentShield-Key",
90
+ body: sanitizedBody,
91
+ bodySize: JSON.stringify(request).length,
92
+ });
93
+ // Make the request with timeout
94
+ const controller = new AbortController();
95
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
96
+ try {
97
+ const response = await this.config.fetchProvider.fetch(url, {
98
+ method: "POST",
99
+ headers: requestHeaders,
100
+ body: JSON.stringify(request),
101
+ signal: controller.signal,
102
+ });
103
+ clearTimeout(timeoutId);
104
+ // ✅ EMPIRICAL PROOF: Capture exact response details
105
+ const responseHeaders = {};
106
+ response.headers.forEach((value, key) => {
107
+ responseHeaders[key] = value;
108
+ });
109
+ const responseText = await response
110
+ .text()
111
+ .catch(() => "Failed to read response");
112
+ let responseBody;
113
+ try {
114
+ responseBody = JSON.parse(responseText);
115
+ }
116
+ catch {
117
+ responseBody = responseText;
118
+ }
119
+ // ✅ EMPIRICAL PROOF: Log exact response details
120
+ this.config.logger("[SessionRegistration] 🔍 EMPIRICAL DEBUG - Response details", {
121
+ status: response.status,
122
+ statusText: response.statusText,
123
+ ok: response.ok,
124
+ headers: responseHeaders,
125
+ body: responseBody,
126
+ bodyLength: responseText.length,
127
+ clientName: request.client_info.name,
128
+ });
129
+ if (!response.ok) {
130
+ // Log error but don't throw - this is fire-and-forget
131
+ this.config.logger("[SessionRegistration] Registration failed", {
132
+ sessionId,
133
+ status: response.status,
134
+ error: responseText,
135
+ // ✅ EMPIRICAL PROOF: Include full response details
136
+ responseHeaders,
137
+ responseBody,
138
+ clientName: request.client_info.name,
139
+ });
140
+ return {
141
+ success: false,
142
+ sessionId,
143
+ error: `HTTP ${response.status}: ${responseText}`,
144
+ };
145
+ }
146
+ // Parse response (using already-captured responseBody)
147
+ const responseData = responseBody;
148
+ const parseResult = agentshield_api_1.registerSessionResponseSchema.safeParse(responseData.data ||
149
+ responseData);
150
+ if (!parseResult.success) {
151
+ this.config.logger("[SessionRegistration] Invalid response format", {
152
+ sessionId,
153
+ response: responseData,
154
+ });
155
+ // Still consider it a success if we got a 200 OK
156
+ return { success: true, sessionId };
157
+ }
158
+ this.config.logger("[SessionRegistration] Session registered", {
159
+ sessionId,
160
+ registered: parseResult.data.registered,
161
+ });
162
+ return { success: true, sessionId };
163
+ }
164
+ finally {
165
+ clearTimeout(timeoutId);
166
+ }
167
+ }
168
+ catch (error) {
169
+ // Handle abort/timeout
170
+ if (error instanceof Error && error.name === "AbortError") {
171
+ this.config.logger("[SessionRegistration] Request timed out", {
172
+ sessionId,
173
+ timeoutMs: this.config.timeoutMs,
174
+ });
175
+ return { success: false, sessionId, error: "Request timed out" };
176
+ }
177
+ // Log any other error
178
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
179
+ this.config.logger("[SessionRegistration] Unexpected error", {
180
+ sessionId,
181
+ error: errorMsg,
182
+ });
183
+ return { success: false, sessionId, error: errorMsg };
184
+ }
185
+ }
186
+ /**
187
+ * Fire-and-forget session registration
188
+ *
189
+ * Starts registration in the background without waiting for completion.
190
+ * Useful when you want to register a session but not delay the response.
191
+ *
192
+ * @param request - Session registration request data
193
+ */
194
+ registerSessionAsync(request) {
195
+ // Start registration in background - don't await
196
+ this.registerSession(request).catch((error) => {
197
+ // This should never happen since registerSession catches all errors,
198
+ // but just in case
199
+ this.config.logger("[SessionRegistration] Background registration failed", {
200
+ sessionId: request.session_id,
201
+ error: error instanceof Error ? error.message : "Unknown error",
202
+ });
203
+ });
204
+ }
205
+ }
206
+ exports.SessionRegistrationService = SessionRegistrationService;
207
+ /**
208
+ * Create a session registration service from common runtime config
209
+ *
210
+ * Helper function to create the service from typical environment config.
211
+ */
212
+ function createSessionRegistrationService(options) {
213
+ // Validate required config
214
+ if (!options.apiUrl || !options.apiKey) {
215
+ options.logger?.("[SessionRegistration] Missing required config - session registration disabled", {
216
+ hasApiUrl: !!options.apiUrl,
217
+ hasApiKey: !!options.apiKey,
218
+ });
219
+ return null;
220
+ }
221
+ return new SessionRegistrationService({
222
+ baseUrl: options.apiUrl,
223
+ apiKey: options.apiKey,
224
+ fetchProvider: options.fetchProvider,
225
+ logger: options.logger,
226
+ });
227
+ }
228
+ //# sourceMappingURL=session-registration.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-registration.service.js","sourceRoot":"","sources":["../../src/services/session-registration.service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kya-os/mcp-i-core",
3
- "version": "1.3.6",
3
+ "version": "1.3.7-canary.clientinfo.20251126041014",
4
4
  "description": "Core runtime and types for MCP-I framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -56,6 +56,17 @@ export type {
56
56
  AccessControlApiServiceMetrics,
57
57
  } from "./services/access-control.service";
58
58
 
59
+ // Session Registration Service
60
+ export {
61
+ SessionRegistrationService,
62
+ createSessionRegistrationService,
63
+ } from "./services/session-registration.service";
64
+
65
+ export type {
66
+ SessionRegistrationServiceConfig,
67
+ SessionRegistrationResult,
68
+ } from "./services/session-registration.service";
69
+
59
70
  // OAuth Config Service (Phase 1)
60
71
  export { OAuthConfigService } from "./services/oauth-config.service";
61
72
 
@@ -7,3 +7,12 @@ export type {
7
7
  AccessControlApiServiceMetrics,
8
8
  } from './access-control.service.js';
9
9
 
10
+ export {
11
+ SessionRegistrationService,
12
+ createSessionRegistrationService,
13
+ } from './session-registration.service.js';
14
+ export type {
15
+ SessionRegistrationServiceConfig,
16
+ SessionRegistrationResult,
17
+ } from './session-registration.service.js';
18
+
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Session Registration Service
3
+ *
4
+ * Registers MCP sessions with the AgentShield dashboard, enabling
5
+ * visibility into which MCP clients are connecting to agents.
6
+ *
7
+ * This is a fire-and-forget service - session registration should not
8
+ * block tool execution or affect the user experience.
9
+ *
10
+ * @package @kya-os/mcp-i-core
11
+ */
12
+
13
+ import type {
14
+ RegisterSessionRequest,
15
+ RegisterSessionResponse,
16
+ } from "@kya-os/contracts/agentshield-api";
17
+ import {
18
+ registerSessionRequestSchema,
19
+ registerSessionResponseSchema,
20
+ AGENTSHIELD_ENDPOINTS,
21
+ } from "@kya-os/contracts/agentshield-api";
22
+ import type { FetchProvider } from "../providers/base.js";
23
+
24
+ /**
25
+ * Configuration for the session registration service
26
+ */
27
+ export interface SessionRegistrationServiceConfig {
28
+ /** Base URL for the AgentShield API (e.g., "https://kya.vouched.id") */
29
+ baseUrl: string;
30
+
31
+ /** API key for authentication */
32
+ apiKey: string;
33
+
34
+ /** Fetch provider for making HTTP requests (platform-agnostic) */
35
+ fetchProvider: FetchProvider;
36
+
37
+ /** Optional logger callback for diagnostics */
38
+ logger?: (message: string, data?: unknown) => void;
39
+
40
+ /** Timeout in milliseconds for the registration request (default: 5000) */
41
+ timeoutMs?: number;
42
+ }
43
+
44
+ /**
45
+ * Result of a session registration attempt
46
+ */
47
+ export interface SessionRegistrationResult {
48
+ /** Whether registration was successful */
49
+ success: boolean;
50
+ /** Session ID that was registered */
51
+ sessionId: string;
52
+ /** Error message if registration failed */
53
+ error?: string;
54
+ }
55
+
56
+ /**
57
+ * Session Registration Service
58
+ *
59
+ * Registers MCP sessions with AgentShield for dashboard visibility.
60
+ * Designed to be non-blocking - failures are logged but don't throw.
61
+ */
62
+ export class SessionRegistrationService {
63
+ private config: Required<
64
+ Omit<SessionRegistrationServiceConfig, "logger" | "timeoutMs">
65
+ > & {
66
+ logger: NonNullable<SessionRegistrationServiceConfig["logger"]>;
67
+ timeoutMs: number;
68
+ };
69
+
70
+ constructor(config: SessionRegistrationServiceConfig) {
71
+ this.config = {
72
+ baseUrl: config.baseUrl,
73
+ apiKey: config.apiKey,
74
+ fetchProvider: config.fetchProvider,
75
+ logger: config.logger || (() => {}),
76
+ timeoutMs: config.timeoutMs ?? 5000,
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Register a session with AgentShield
82
+ *
83
+ * This is a fire-and-forget operation. Failures are logged but don't throw.
84
+ * The method returns quickly and doesn't block the caller.
85
+ *
86
+ * @param request - Session registration request data
87
+ * @returns Result indicating success or failure
88
+ */
89
+ async registerSession(
90
+ request: RegisterSessionRequest
91
+ ): Promise<SessionRegistrationResult> {
92
+ const sessionId = request.session_id;
93
+
94
+ try {
95
+ // Validate request
96
+ const validationResult = registerSessionRequestSchema.safeParse(request);
97
+ if (!validationResult.success) {
98
+ const errorMsg = `Invalid session registration request: ${validationResult.error.message}`;
99
+ this.config.logger("[SessionRegistration] Validation failed", {
100
+ sessionId,
101
+ error: errorMsg,
102
+ });
103
+ return { success: false, sessionId, error: errorMsg };
104
+ }
105
+
106
+ const url = `${this.config.baseUrl}${AGENTSHIELD_ENDPOINTS.SESSIONS}`;
107
+
108
+ this.config.logger("[SessionRegistration] Registering session", {
109
+ sessionId,
110
+ agentDid: request.agent_did,
111
+ clientName: request.client_info.name,
112
+ url,
113
+ });
114
+
115
+ // ✅ EMPIRICAL PROOF: Prepare request headers with correct auth format
116
+ const requestHeaders = {
117
+ "Content-Type": "application/json",
118
+ "X-AgentShield-Key": this.config.apiKey, // Fixed: Use X-AgentShield-Key instead of Authorization: Bearer
119
+ };
120
+
121
+ // ✅ EMPIRICAL PROOF: Log exact request details (sanitized for security)
122
+ const sanitizedHeaders = {
123
+ ...requestHeaders,
124
+ "X-AgentShield-Key": `${this.config.apiKey.slice(0, 8)}...${this.config.apiKey.slice(-4)}`, // Show first 8 and last 4 chars
125
+ };
126
+
127
+ const sanitizedBody = {
128
+ session_id: request.session_id,
129
+ agent_did: request.agent_did,
130
+ project_id: request.project_id,
131
+ created_at: request.created_at,
132
+ client_info: request.client_info,
133
+ client_identity: request.client_identity,
134
+ server_did: request.server_did,
135
+ ttl_minutes: request.ttl_minutes,
136
+ };
137
+
138
+ this.config.logger(
139
+ "[SessionRegistration] 🔍 EMPIRICAL DEBUG - Request details",
140
+ {
141
+ url,
142
+ method: "POST",
143
+ headers: sanitizedHeaders,
144
+ headerKeys: Object.keys(requestHeaders),
145
+ authHeaderPresent: !!requestHeaders["X-AgentShield-Key"],
146
+ authHeaderName: "X-AgentShield-Key",
147
+ body: sanitizedBody,
148
+ bodySize: JSON.stringify(request).length,
149
+ }
150
+ );
151
+
152
+ // Make the request with timeout
153
+ const controller = new AbortController();
154
+ const timeoutId = setTimeout(
155
+ () => controller.abort(),
156
+ this.config.timeoutMs
157
+ );
158
+
159
+ try {
160
+ const response = await this.config.fetchProvider.fetch(url, {
161
+ method: "POST",
162
+ headers: requestHeaders,
163
+ body: JSON.stringify(request),
164
+ signal: controller.signal,
165
+ });
166
+
167
+ clearTimeout(timeoutId);
168
+
169
+ // ✅ EMPIRICAL PROOF: Capture exact response details
170
+ const responseHeaders: Record<string, string> = {};
171
+ response.headers.forEach((value, key) => {
172
+ responseHeaders[key] = value;
173
+ });
174
+
175
+ const responseText = await response
176
+ .text()
177
+ .catch(() => "Failed to read response");
178
+ let responseBody: unknown;
179
+ try {
180
+ responseBody = JSON.parse(responseText);
181
+ } catch {
182
+ responseBody = responseText;
183
+ }
184
+
185
+ // ✅ EMPIRICAL PROOF: Log exact response details
186
+ this.config.logger(
187
+ "[SessionRegistration] 🔍 EMPIRICAL DEBUG - Response details",
188
+ {
189
+ status: response.status,
190
+ statusText: response.statusText,
191
+ ok: response.ok,
192
+ headers: responseHeaders,
193
+ body: responseBody,
194
+ bodyLength: responseText.length,
195
+ clientName: request.client_info.name,
196
+ }
197
+ );
198
+
199
+ if (!response.ok) {
200
+ // Log error but don't throw - this is fire-and-forget
201
+ this.config.logger("[SessionRegistration] Registration failed", {
202
+ sessionId,
203
+ status: response.status,
204
+ error: responseText,
205
+ // ✅ EMPIRICAL PROOF: Include full response details
206
+ responseHeaders,
207
+ responseBody,
208
+ clientName: request.client_info.name,
209
+ });
210
+ return {
211
+ success: false,
212
+ sessionId,
213
+ error: `HTTP ${response.status}: ${responseText}`,
214
+ };
215
+ }
216
+
217
+ // Parse response (using already-captured responseBody)
218
+ const responseData = responseBody as
219
+ | { data?: RegisterSessionResponse }
220
+ | RegisterSessionResponse;
221
+ const parseResult = registerSessionResponseSchema.safeParse(
222
+ (responseData as { data?: RegisterSessionResponse }).data ||
223
+ responseData
224
+ );
225
+
226
+ if (!parseResult.success) {
227
+ this.config.logger("[SessionRegistration] Invalid response format", {
228
+ sessionId,
229
+ response: responseData,
230
+ });
231
+ // Still consider it a success if we got a 200 OK
232
+ return { success: true, sessionId };
233
+ }
234
+
235
+ this.config.logger("[SessionRegistration] Session registered", {
236
+ sessionId,
237
+ registered: parseResult.data.registered,
238
+ });
239
+
240
+ return { success: true, sessionId };
241
+ } finally {
242
+ clearTimeout(timeoutId);
243
+ }
244
+ } catch (error) {
245
+ // Handle abort/timeout
246
+ if (error instanceof Error && error.name === "AbortError") {
247
+ this.config.logger("[SessionRegistration] Request timed out", {
248
+ sessionId,
249
+ timeoutMs: this.config.timeoutMs,
250
+ });
251
+ return { success: false, sessionId, error: "Request timed out" };
252
+ }
253
+
254
+ // Log any other error
255
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
256
+ this.config.logger("[SessionRegistration] Unexpected error", {
257
+ sessionId,
258
+ error: errorMsg,
259
+ });
260
+ return { success: false, sessionId, error: errorMsg };
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Fire-and-forget session registration
266
+ *
267
+ * Starts registration in the background without waiting for completion.
268
+ * Useful when you want to register a session but not delay the response.
269
+ *
270
+ * @param request - Session registration request data
271
+ */
272
+ registerSessionAsync(request: RegisterSessionRequest): void {
273
+ // Start registration in background - don't await
274
+ this.registerSession(request).catch((error) => {
275
+ // This should never happen since registerSession catches all errors,
276
+ // but just in case
277
+ this.config.logger(
278
+ "[SessionRegistration] Background registration failed",
279
+ {
280
+ sessionId: request.session_id,
281
+ error: error instanceof Error ? error.message : "Unknown error",
282
+ }
283
+ );
284
+ });
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Create a session registration service from common runtime config
290
+ *
291
+ * Helper function to create the service from typical environment config.
292
+ */
293
+ export function createSessionRegistrationService(options: {
294
+ apiUrl: string;
295
+ apiKey: string;
296
+ fetchProvider: FetchProvider;
297
+ logger?: (message: string, data?: unknown) => void;
298
+ }): SessionRegistrationService | null {
299
+ // Validate required config
300
+ if (!options.apiUrl || !options.apiKey) {
301
+ options.logger?.(
302
+ "[SessionRegistration] Missing required config - session registration disabled",
303
+ {
304
+ hasApiUrl: !!options.apiUrl,
305
+ hasApiKey: !!options.apiKey,
306
+ }
307
+ );
308
+ return null;
309
+ }
310
+
311
+ return new SessionRegistrationService({
312
+ baseUrl: options.apiUrl,
313
+ apiKey: options.apiKey,
314
+ fetchProvider: options.fetchProvider,
315
+ logger: options.logger,
316
+ });
317
+ }
@@ -1,9 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Read(//Users/dylanhobbs/Documents/@kya-os/xmcp-i/packages/contracts/**)"
5
- ],
6
- "deny": [],
7
- "ask": []
8
- }
9
- }