@kya-os/mcp-i 0.1.0-alpha.2.9 → 0.1.0-alpha.3.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 (85) hide show
  1. package/dist/{crypto.js → cjs/crypto.js} +17 -2
  2. package/dist/esm/auto.d.ts +13 -0
  3. package/dist/esm/auto.d.ts.map +1 -0
  4. package/dist/esm/auto.js +30 -0
  5. package/dist/esm/auto.js.map +1 -0
  6. package/dist/esm/crypto.d.ts +51 -0
  7. package/dist/esm/crypto.d.ts.map +1 -0
  8. package/dist/esm/crypto.js +230 -0
  9. package/dist/esm/crypto.js.map +1 -0
  10. package/dist/esm/dev-helper.d.ts +15 -0
  11. package/dist/esm/dev-helper.d.ts.map +1 -0
  12. package/dist/esm/dev-helper.js +63 -0
  13. package/dist/esm/dev-helper.js.map +1 -0
  14. package/dist/esm/encrypted-storage.d.ts +19 -0
  15. package/dist/esm/encrypted-storage.d.ts.map +1 -0
  16. package/dist/esm/encrypted-storage.js +48 -0
  17. package/dist/esm/encrypted-storage.js.map +1 -0
  18. package/dist/esm/index.d.ts +128 -0
  19. package/dist/esm/index.d.ts.map +1 -0
  20. package/dist/esm/index.js +671 -0
  21. package/dist/esm/index.js.map +1 -0
  22. package/dist/esm/logger.d.ts +46 -0
  23. package/dist/esm/logger.d.ts.map +1 -0
  24. package/dist/esm/logger.js +76 -0
  25. package/dist/esm/logger.js.map +1 -0
  26. package/dist/esm/nextjs.d.ts +22 -0
  27. package/dist/esm/nextjs.d.ts.map +1 -0
  28. package/dist/esm/nextjs.js +82 -0
  29. package/dist/esm/nextjs.js.map +1 -0
  30. package/dist/esm/registry/index.d.ts +43 -0
  31. package/dist/esm/registry/index.d.ts.map +1 -0
  32. package/dist/esm/registry/index.js +89 -0
  33. package/dist/esm/registry/index.js.map +1 -0
  34. package/dist/esm/registry/knowthat.d.ts +30 -0
  35. package/dist/esm/registry/knowthat.d.ts.map +1 -0
  36. package/dist/esm/registry/knowthat.js +106 -0
  37. package/dist/esm/registry/knowthat.js.map +1 -0
  38. package/dist/esm/rotation.d.ts +57 -0
  39. package/dist/esm/rotation.d.ts.map +1 -0
  40. package/dist/esm/rotation.js +133 -0
  41. package/dist/esm/rotation.js.map +1 -0
  42. package/dist/esm/storage.d.ts +65 -0
  43. package/dist/esm/storage.d.ts.map +1 -0
  44. package/dist/esm/storage.js +160 -0
  45. package/dist/esm/storage.js.map +1 -0
  46. package/dist/esm/transport.d.ts +52 -0
  47. package/dist/esm/transport.d.ts.map +1 -0
  48. package/dist/esm/transport.js +209 -0
  49. package/dist/esm/transport.js.map +1 -0
  50. package/dist/esm/types.d.ts +188 -0
  51. package/dist/esm/types.d.ts.map +1 -0
  52. package/dist/esm/types.js +5 -0
  53. package/dist/esm/types.js.map +1 -0
  54. package/dist/esm/vercel-adapter.d.ts +26 -0
  55. package/dist/esm/vercel-adapter.d.ts.map +1 -0
  56. package/dist/esm/vercel-adapter.js +80 -0
  57. package/dist/esm/vercel-adapter.js.map +1 -0
  58. package/package.json +23 -19
  59. /package/dist/{auto.d.ts → cjs/auto.d.ts} +0 -0
  60. /package/dist/{auto.js → cjs/auto.js} +0 -0
  61. /package/dist/{crypto.d.ts → cjs/crypto.d.ts} +0 -0
  62. /package/dist/{dev-helper.d.ts → cjs/dev-helper.d.ts} +0 -0
  63. /package/dist/{dev-helper.js → cjs/dev-helper.js} +0 -0
  64. /package/dist/{encrypted-storage.d.ts → cjs/encrypted-storage.d.ts} +0 -0
  65. /package/dist/{encrypted-storage.js → cjs/encrypted-storage.js} +0 -0
  66. /package/dist/{index.d.ts → cjs/index.d.ts} +0 -0
  67. /package/dist/{index.js → cjs/index.js} +0 -0
  68. /package/dist/{logger.d.ts → cjs/logger.d.ts} +0 -0
  69. /package/dist/{logger.js → cjs/logger.js} +0 -0
  70. /package/dist/{nextjs.d.ts → cjs/nextjs.d.ts} +0 -0
  71. /package/dist/{nextjs.js → cjs/nextjs.js} +0 -0
  72. /package/dist/{registry → cjs/registry}/index.d.ts +0 -0
  73. /package/dist/{registry → cjs/registry}/index.js +0 -0
  74. /package/dist/{registry → cjs/registry}/knowthat.d.ts +0 -0
  75. /package/dist/{registry → cjs/registry}/knowthat.js +0 -0
  76. /package/dist/{rotation.d.ts → cjs/rotation.d.ts} +0 -0
  77. /package/dist/{rotation.js → cjs/rotation.js} +0 -0
  78. /package/dist/{storage.d.ts → cjs/storage.d.ts} +0 -0
  79. /package/dist/{storage.js → cjs/storage.js} +0 -0
  80. /package/dist/{transport.d.ts → cjs/transport.d.ts} +0 -0
  81. /package/dist/{transport.js → cjs/transport.js} +0 -0
  82. /package/dist/{types.d.ts → cjs/types.d.ts} +0 -0
  83. /package/dist/{types.js → cjs/types.js} +0 -0
  84. /package/dist/{vercel-adapter.d.ts → cjs/vercel-adapter.d.ts} +0 -0
  85. /package/dist/{vercel-adapter.js → cjs/vercel-adapter.js} +0 -0
@@ -0,0 +1,671 @@
1
+ /**
2
+ * @kya-os/mcp-i - Optimized MCP Identity with production features
3
+ *
4
+ * Enable any MCP server to get a verifiable identity with just 2 lines of code:
5
+ *
6
+ * ```typescript
7
+ * import "@kya-os/mcp-i/auto"; // That's it! Your server now has identity
8
+ * ```
9
+ */
10
+ import * as crypto from './crypto.js';
11
+ import { StorageFactory } from './storage.js';
12
+ import { TransportFactory } from './transport.js';
13
+ import { LoggerFactory, getLogger } from './logger.js';
14
+ import { KeyRotationManager } from './rotation.js';
15
+ import { loadIdentityFromEnv, showVercelDeveloperInstructions } from './vercel-adapter.js';
16
+ // Re-export types and utilities
17
+ export * from './types.js';
18
+ export * from './vercel-adapter.js';
19
+ export { RegistryFactory, REGISTRY_TIERS, resolveRegistries } from './registry.js';
20
+ export { LoggerFactory, ConsoleLogger, SilentLogger } from './logger.js';
21
+ export { StorageFactory, MemoryStorage, FileStorage } from './storage.js';
22
+ export { TransportFactory, RuntimeDetector } from './transport.js';
23
+ export { KeyRotationManager } from './rotation.js';
24
+ export { initWithDevExperience, showAgentStatus } from './dev-helper.js';
25
+ // Global identity instance
26
+ let globalIdentity = null;
27
+ export class MCPIdentity {
28
+ did;
29
+ publicKey;
30
+ privateKey;
31
+ timestampTolerance;
32
+ enableNonceTracking;
33
+ usedNonces = new Set();
34
+ nonceCleanupInterval;
35
+ encryptionPassword;
36
+ decryptedPrivateKey;
37
+ // Directory preferences
38
+ directories;
39
+ // Optimized storage
40
+ storage;
41
+ transport;
42
+ logger;
43
+ // Key rotation
44
+ rotationManager;
45
+ // Precomputed values
46
+ precomputed;
47
+ constructor(identity, options = {}) {
48
+ // Initialize logger first
49
+ if (options.logger) {
50
+ LoggerFactory.setLogger(options.logger);
51
+ }
52
+ else if (options.logLevel && options.logLevel !== 'silent') {
53
+ LoggerFactory.setLogger(LoggerFactory.createConsoleLogger(options.logLevel));
54
+ }
55
+ this.logger = getLogger();
56
+ // Core identity
57
+ this.did = identity.did;
58
+ this.publicKey = identity.publicKey;
59
+ this.privateKey = identity.privateKey;
60
+ // Store encryption password for later decryption
61
+ this.encryptionPassword = options.encryptionPassword;
62
+ // Security options
63
+ this.timestampTolerance = options.timestampTolerance || 60000; // 60 seconds
64
+ this.enableNonceTracking = options.enableNonceTracking !== false;
65
+ // Directory preferences
66
+ this.directories = identity.directories || 'verified';
67
+ // Initialize storage and transport
68
+ this.storage = StorageFactory.create({
69
+ storage: options.storage,
70
+ customPath: options.persistencePath,
71
+ memoryKey: options.memoryKey || this.did,
72
+ encryptionPassword: options.encryptionPassword
73
+ });
74
+ this.transport = TransportFactory.create({
75
+ transport: options.transport
76
+ });
77
+ // Precompute values for performance
78
+ this.precomputed = {
79
+ did: this.did,
80
+ publicKey: this.publicKey,
81
+ didBytes: new TextEncoder().encode(this.did),
82
+ signatureCache: new Map()
83
+ };
84
+ // Initialize key rotation if not in memory storage
85
+ if (options.storage !== 'memory') {
86
+ this.rotationManager = new KeyRotationManager(identity, this.transport, {});
87
+ }
88
+ // Start nonce cleanup if tracking is enabled
89
+ if (this.enableNonceTracking) {
90
+ this.startNonceCleanup();
91
+ }
92
+ }
93
+ /**
94
+ * Initialize MCP Identity - the main entry point
95
+ */
96
+ static async init(options) {
97
+ const logger = options?.logger || getLogger();
98
+ // Return existing global identity if already initialized
99
+ if (globalIdentity) {
100
+ return globalIdentity;
101
+ }
102
+ // Check for Vercel/serverless environment
103
+ const isVercel = process.env.VERCEL || process.env.VERCEL_ENV;
104
+ const isServerless = isVercel || process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.FUNCTIONS_WORKER_RUNTIME;
105
+ // Try to load from environment variables first (for serverless)
106
+ let identity = null;
107
+ if (isServerless || options?.storage === 'memory') {
108
+ identity = loadIdentityFromEnv();
109
+ if (identity) {
110
+ logger.info('✅ Loaded existing identity from environment variables');
111
+ }
112
+ }
113
+ // Initialize storage
114
+ const storage = StorageFactory.create({
115
+ storage: options?.storage,
116
+ customPath: options?.persistencePath,
117
+ memoryKey: options?.memoryKey,
118
+ encryptionPassword: options?.encryptionPassword
119
+ });
120
+ // Try to load from storage if not found in env
121
+ if (!identity) {
122
+ identity = await storage.load();
123
+ }
124
+ // Handle existing identity
125
+ if (identity) {
126
+ logger.info('Loaded existing identity:', identity.did);
127
+ globalIdentity = new MCPIdentity(identity, options);
128
+ return globalIdentity;
129
+ }
130
+ // No existing identity - need to create new one
131
+ logger.info('No existing identity found, creating new identity...');
132
+ // Initialize transport for registration
133
+ const transport = TransportFactory.create({
134
+ transport: options?.transport
135
+ });
136
+ // Always use knowthat.ai as the registry
137
+ const apiEndpoint = options?.apiEndpoint || 'https://knowthat.ai';
138
+ logger.info('Registering with knowthat.ai...');
139
+ // Generate keys first
140
+ logger.info('Generating cryptographic keys...');
141
+ const keyPair = await crypto.generateKeyPair();
142
+ // Prepare registration data with public key
143
+ const registrationData = {
144
+ name: options?.name || process.env.MCP_SERVER_NAME || 'Unnamed MCP Server',
145
+ description: options?.description,
146
+ repository: options?.repository,
147
+ publicKey: keyPair.publicKey,
148
+ directories: options?.directories,
149
+ isDraft: options?.mode !== 'production' // Default to draft mode for safety
150
+ };
151
+ logger.debug('Registration data:', {
152
+ name: registrationData.name,
153
+ hasDescription: !!registrationData.description,
154
+ hasRepository: !!registrationData.repository,
155
+ hasPublicKey: !!registrationData.publicKey,
156
+ directories: registrationData.directories,
157
+ isDraft: registrationData.isDraft
158
+ });
159
+ let response;
160
+ try {
161
+ // Register with knowthat.ai
162
+ response = await autoRegister(transport, {
163
+ ...registrationData,
164
+ apiEndpoint,
165
+ directories: options?.directories
166
+ });
167
+ }
168
+ catch (registrationError) {
169
+ logger.error('Failed to register with knowthat.ai:', registrationError.message);
170
+ // For development/testing, allow offline mode with a temporary DID
171
+ if (options?.mode === 'development' || process.env.NODE_ENV === 'development') {
172
+ logger.warn('Running in offline development mode with temporary identity');
173
+ // Generate a temporary development DID
174
+ const tempSlug = `temp-${Date.now()}-${Math.random().toString(36).substring(7)}`;
175
+ response = {
176
+ did: `did:web:localhost:agents:${tempSlug}`,
177
+ agent: {
178
+ id: tempSlug,
179
+ slug: tempSlug,
180
+ name: registrationData.name,
181
+ url: `http://localhost:3000/agents/${tempSlug}`,
182
+ claimUrl: `http://localhost:3000/agents/claim?did=${tempSlug}`
183
+ },
184
+ keys: {
185
+ publicKey: keyPair.publicKey,
186
+ privateKey: keyPair.privateKey
187
+ }
188
+ };
189
+ logger.warn('⚠️ Using temporary development identity. This will not persist across restarts.');
190
+ logger.warn('⚠️ To use a permanent identity, ensure you can connect to knowthat.ai');
191
+ }
192
+ else {
193
+ // In production, registration is required
194
+ throw registrationError;
195
+ }
196
+ }
197
+ // Create persisted identity
198
+ identity = {
199
+ did: response.did,
200
+ publicKey: keyPair.publicKey,
201
+ privateKey: keyPair.privateKey,
202
+ agentId: response.agent.id,
203
+ agentSlug: response.agent.slug,
204
+ registeredAt: new Date().toISOString(),
205
+ directories: options?.directories || 'verified'
206
+ };
207
+ // Save identity
208
+ await storage.save(identity);
209
+ // Show enhanced developer instructions for Vercel/serverless
210
+ if (isServerless) {
211
+ showVercelDeveloperInstructions(identity, response.agent.claimUrl);
212
+ }
213
+ else {
214
+ logger.info('✅ Success! Your agent has been registered.');
215
+ logger.info(`DID: ${response.did}`);
216
+ logger.info(`Profile: ${response.agent.url}`);
217
+ // Show claim URL if in draft mode
218
+ if (response.agent.claimUrl) {
219
+ logger.info(`Claim your agent: ${response.agent.claimUrl}`);
220
+ }
221
+ // Show directory submission info
222
+ if (options?.directories && options.directories !== 'none') {
223
+ const dirMessage = options.directories === 'verified'
224
+ ? 'Your agent will be submitted to all verified directories'
225
+ : `Your agent will be submitted to: ${options.directories.join(', ')}`;
226
+ logger.info(dirMessage);
227
+ }
228
+ }
229
+ // Create MCPIdentity instance
230
+ globalIdentity = new MCPIdentity(identity, options);
231
+ return globalIdentity;
232
+ }
233
+ /**
234
+ * Enable automatic key rotation
235
+ */
236
+ async enableAutoRotation(policy) {
237
+ if (!this.rotationManager) {
238
+ throw new Error('Key rotation not available in memory storage mode');
239
+ }
240
+ this.rotationManager.setupAutoRotation((result) => {
241
+ if (result.success) {
242
+ this.logger.info('Keys rotated successfully');
243
+ // Update storage with new keys
244
+ this.persistIdentity();
245
+ }
246
+ else {
247
+ this.logger.error('Key rotation failed:', result.error);
248
+ }
249
+ });
250
+ }
251
+ /**
252
+ * Manually rotate keys
253
+ */
254
+ async rotateKeys(reason) {
255
+ if (!this.rotationManager) {
256
+ throw new Error('Key rotation not available in memory storage mode');
257
+ }
258
+ const result = await this.rotationManager.rotateKeys(reason);
259
+ if (result.success) {
260
+ await this.persistIdentity();
261
+ }
262
+ return result;
263
+ }
264
+ /**
265
+ * Check key health
266
+ */
267
+ checkKeyHealth() {
268
+ if (!this.rotationManager) {
269
+ return null;
270
+ }
271
+ return this.rotationManager.checkKeyHealth();
272
+ }
273
+ /**
274
+ * Get decrypted private key (lazy decryption)
275
+ */
276
+ async getPrivateKey() {
277
+ if (this.decryptedPrivateKey) {
278
+ return this.decryptedPrivateKey;
279
+ }
280
+ if (this.encryptionPassword && this.privateKey.startsWith('enc:')) {
281
+ try {
282
+ this.decryptedPrivateKey = await crypto.decrypt(this.privateKey, this.encryptionPassword);
283
+ this.logger.debug('Private key decrypted successfully');
284
+ return this.decryptedPrivateKey;
285
+ }
286
+ catch (error) {
287
+ this.logger.error('Failed to decrypt private key:', error);
288
+ throw new Error('Invalid encryption password');
289
+ }
290
+ }
291
+ return this.privateKey;
292
+ }
293
+ /**
294
+ * Sign a message with caching
295
+ */
296
+ async sign(message) {
297
+ const messageStr = typeof message === 'string' ? message : message.toString('base64');
298
+ // Check cache
299
+ const cached = this.precomputed.signatureCache.get(messageStr);
300
+ if (cached) {
301
+ return cached;
302
+ }
303
+ // Get decrypted private key
304
+ const privateKey = await this.getPrivateKey();
305
+ // Sign and cache
306
+ const signature = await crypto.sign(message, privateKey);
307
+ // Limit cache size
308
+ if (this.precomputed.signatureCache.size > 100) {
309
+ const firstKey = this.precomputed.signatureCache.keys().next().value;
310
+ if (firstKey) {
311
+ this.precomputed.signatureCache.delete(firstKey);
312
+ }
313
+ }
314
+ this.precomputed.signatureCache.set(messageStr, signature);
315
+ // Track signature count for rotation
316
+ if (this.rotationManager) {
317
+ this.rotationManager.incrementSignatureCount();
318
+ }
319
+ return signature;
320
+ }
321
+ /**
322
+ * Request edit access with claim URL support
323
+ */
324
+ async requestEditAccess() {
325
+ const timestamp = Date.now();
326
+ const message = `edit-request:${this.did}:knowthat.ai:${timestamp}`;
327
+ const signature = await this.sign(message);
328
+ // Construct edit URL with proof of ownership
329
+ const baseUrl = 'https://knowthat.ai';
330
+ const editUrl = new URL(`${baseUrl}/agents/edit`);
331
+ editUrl.searchParams.set('did', this.did);
332
+ editUrl.searchParams.set('timestamp', timestamp.toString());
333
+ editUrl.searchParams.set('signature', signature);
334
+ // Also generate claim URL if agent is not yet claimed
335
+ const claimUrl = new URL(`${baseUrl}/agents/claim`);
336
+ claimUrl.searchParams.set('did', this.did);
337
+ claimUrl.searchParams.set('timestamp', timestamp.toString());
338
+ claimUrl.searchParams.set('signature', signature);
339
+ return {
340
+ editUrl: editUrl.toString(),
341
+ claimUrl: claimUrl.toString()
342
+ };
343
+ }
344
+ /**
345
+ * Verify a signature
346
+ */
347
+ async verify(message, signature, publicKey) {
348
+ return crypto.verify(message, signature, publicKey || this.publicKey);
349
+ }
350
+ /**
351
+ * Respond to an MCP-I challenge
352
+ */
353
+ async respondToChallenge(challenge) {
354
+ // Validate timestamp to prevent replay attacks
355
+ const now = Date.now();
356
+ const challengeAge = now - challenge.timestamp;
357
+ if (challengeAge > this.timestampTolerance) {
358
+ throw new Error('Challenge expired');
359
+ }
360
+ if (challengeAge < 0) {
361
+ throw new Error('Challenge timestamp is in the future');
362
+ }
363
+ // Check for nonce reuse if tracking is enabled
364
+ if (this.enableNonceTracking) {
365
+ if (this.usedNonces.has(challenge.nonce)) {
366
+ throw new Error('Nonce already used');
367
+ }
368
+ this.usedNonces.add(challenge.nonce);
369
+ }
370
+ // Create the message to sign
371
+ const messageComponents = [
372
+ challenge.nonce,
373
+ challenge.timestamp.toString(),
374
+ this.did,
375
+ challenge.verifier_did || '',
376
+ (challenge.scope || []).join(',')
377
+ ];
378
+ const message = messageComponents.join(':');
379
+ // Sign the challenge
380
+ const signature = await this.sign(message);
381
+ // Return the response
382
+ return {
383
+ did: this.did,
384
+ signature,
385
+ timestamp: now,
386
+ nonce: challenge.nonce,
387
+ publicKey: this.publicKey
388
+ };
389
+ }
390
+ /**
391
+ * Get MCP-I capabilities for advertisement
392
+ */
393
+ getCapabilities() {
394
+ return {
395
+ version: '1.0',
396
+ did: this.did,
397
+ publicKey: this.publicKey,
398
+ conformanceLevel: 2, // Level 2: Full crypto with challenge-response
399
+ handshakeSupported: true,
400
+ handshakeEndpoint: '/_mcp-i/handshake',
401
+ verificationEndpoint: `https://knowthat.ai/api/agents/${this.did}/verify`,
402
+ registry: 'knowthat.ai'
403
+ };
404
+ }
405
+ /**
406
+ * Sign an MCP response with identity metadata
407
+ */
408
+ async signResponse(response) {
409
+ const timestamp = new Date().toISOString();
410
+ const responseWithIdentity = {
411
+ ...response,
412
+ _mcp_identity: {
413
+ did: this.did,
414
+ signature: '', // Will be filled below
415
+ timestamp,
416
+ conformanceLevel: 2
417
+ }
418
+ };
419
+ // Sign the response content (excluding the signature field)
420
+ const contentToSign = JSON.stringify({
421
+ ...response,
422
+ _mcp_identity: {
423
+ did: this.did,
424
+ timestamp,
425
+ conformanceLevel: 2
426
+ }
427
+ });
428
+ responseWithIdentity._mcp_identity.signature = await this.sign(contentToSign);
429
+ return responseWithIdentity;
430
+ }
431
+ /**
432
+ * Generate a new nonce for challenges
433
+ */
434
+ static generateNonce() {
435
+ return crypto.generateNonceSync();
436
+ }
437
+ /**
438
+ * Get directory preferences
439
+ */
440
+ getDirectories() {
441
+ return this.directories;
442
+ }
443
+ /**
444
+ * Clean up old nonces periodically to prevent memory leaks
445
+ */
446
+ startNonceCleanup() {
447
+ // Clean up nonces older than 2x the timestamp tolerance
448
+ this.nonceCleanupInterval = setInterval(() => {
449
+ // In a production system, you'd track nonce timestamps
450
+ // For now, we'll clear all nonces periodically
451
+ if (this.usedNonces.size > 10000) {
452
+ this.usedNonces.clear();
453
+ }
454
+ }, this.timestampTolerance * 2);
455
+ }
456
+ /**
457
+ * Clean up resources
458
+ */
459
+ destroy() {
460
+ if (this.nonceCleanupInterval) {
461
+ clearInterval(this.nonceCleanupInterval);
462
+ }
463
+ this.usedNonces.clear();
464
+ this.precomputed.signatureCache.clear();
465
+ }
466
+ /**
467
+ * Helper to extract agent name from DID
468
+ */
469
+ extractAgentName() {
470
+ // Try to get from persisted data or environment
471
+ return process.env.MCP_SERVER_NAME || 'Unknown Agent';
472
+ }
473
+ /**
474
+ * Helper to extract agent ID
475
+ */
476
+ extractAgentId() {
477
+ return process.env.AGENT_ID || '';
478
+ }
479
+ /**
480
+ * Helper to extract agent slug
481
+ */
482
+ extractAgentSlug() {
483
+ const parts = this.did.split(':');
484
+ return parts[parts.length - 1];
485
+ }
486
+ /**
487
+ * Persist full identity (for key rotation)
488
+ */
489
+ async persistIdentity() {
490
+ try {
491
+ const identity = {
492
+ did: this.did,
493
+ publicKey: this.publicKey,
494
+ privateKey: this.privateKey,
495
+ agentId: this.extractAgentId(),
496
+ agentSlug: this.extractAgentSlug(),
497
+ registeredAt: new Date().toISOString(),
498
+ directories: this.directories
499
+ };
500
+ await this.storage.save(identity);
501
+ }
502
+ catch (error) {
503
+ this.logger.error('Failed to persist identity:', error);
504
+ }
505
+ }
506
+ }
507
+ /**
508
+ * Enable MCP Identity for any MCP server
509
+ */
510
+ export async function enableMCPIdentity(options) {
511
+ const identity = await MCPIdentity.init(options);
512
+ // Try to patch MCP Server if available
513
+ try {
514
+ patchMCPServer(identity);
515
+ }
516
+ catch (error) {
517
+ const logger = getLogger();
518
+ logger.debug('MCP Server not found, identity initialized for manual use');
519
+ }
520
+ return identity;
521
+ }
522
+ /**
523
+ * Create MCP middleware for manual integration
524
+ */
525
+ export function createMCPMiddleware(identity) {
526
+ return (server) => {
527
+ // Validate server object
528
+ if (!server || typeof server !== 'object') {
529
+ const logger = getLogger();
530
+ logger.warn('Invalid MCP Server object passed to middleware');
531
+ return;
532
+ }
533
+ // Check if methods exist before patching
534
+ if (!server.setRequestHandler || typeof server.setRequestHandler !== 'function') {
535
+ const logger = getLogger();
536
+ logger.warn('MCP Server missing setRequestHandler method');
537
+ return;
538
+ }
539
+ // Store original methods
540
+ const originalSetRequestHandler = server.setRequestHandler.bind(server);
541
+ const originalConnect = server.connect ? server.connect.bind(server) : null;
542
+ // Patch setRequestHandler to wrap all responses
543
+ server.setRequestHandler = function (method, handler) {
544
+ // Add defensive check for undefined method
545
+ if (!method || typeof method !== 'string') {
546
+ const logger = getLogger();
547
+ logger.warn('setRequestHandler called with invalid method:', method);
548
+ return originalSetRequestHandler.call(this, method, handler);
549
+ }
550
+ const wrappedHandler = async (...args) => {
551
+ const result = await handler(...args);
552
+ // If result has content, sign it
553
+ if (result && typeof result === 'object' && 'content' in result) {
554
+ return await identity.signResponse(result);
555
+ }
556
+ return result;
557
+ };
558
+ return originalSetRequestHandler(method, wrappedHandler);
559
+ };
560
+ // Patch connect to advertise MCP-I capabilities
561
+ if (originalConnect) {
562
+ server.connect = async function (transport) {
563
+ // Add MCP-I capabilities to server info
564
+ if (this.serverInfo && this.serverInfo.capabilities) {
565
+ this.serverInfo.capabilities['mcp-i'] = identity.getCapabilities();
566
+ }
567
+ // Set up MCP-I handshake handler
568
+ this.setRequestHandler('mcp-i/challenge', async (request) => {
569
+ return identity.respondToChallenge(request.params);
570
+ });
571
+ // Call original connect
572
+ return originalConnect.call(this, transport);
573
+ };
574
+ }
575
+ };
576
+ }
577
+ /**
578
+ * Patch the MCP Server to automatically add identity
579
+ */
580
+ function patchMCPServer(identity) {
581
+ try {
582
+ // Try to import the MCP SDK
583
+ const MCPModule = require('@modelcontextprotocol/sdk/server/index.js');
584
+ const OriginalServer = MCPModule.Server;
585
+ if (!OriginalServer) {
586
+ return;
587
+ }
588
+ // Apply middleware
589
+ const middleware = createMCPMiddleware(identity);
590
+ // Patch the constructor
591
+ const OriginalConstructor = OriginalServer;
592
+ MCPModule.Server = function (...args) {
593
+ const instance = new OriginalConstructor(...args);
594
+ try {
595
+ middleware(instance, identity);
596
+ }
597
+ catch (error) {
598
+ const logger = getLogger();
599
+ logger.warn('Failed to apply MCP-I middleware:', error);
600
+ }
601
+ return instance;
602
+ };
603
+ // Copy static properties
604
+ Object.setPrototypeOf(MCPModule.Server, OriginalConstructor);
605
+ Object.setPrototypeOf(MCPModule.Server.prototype, OriginalConstructor.prototype);
606
+ const logger = getLogger();
607
+ logger.info('✨ MCP Server patched - all responses will be automatically signed');
608
+ }
609
+ catch (error) {
610
+ // MCP SDK not available, that's okay
611
+ }
612
+ }
613
+ // Helper function for auto-registration
614
+ async function autoRegister(transport, options) {
615
+ const logger = getLogger();
616
+ try {
617
+ const requestBody = {
618
+ metadata: {
619
+ name: options.name,
620
+ description: options.description,
621
+ repository: options.repository,
622
+ publicKey: options.publicKey,
623
+ version: '1.0.0',
624
+ isDraft: options.isDraft,
625
+ directories: options.directories || 'verified' // Default to all verified directories
626
+ },
627
+ clientInfo: {
628
+ sdkVersion: '0.1.0-alpha.2.5',
629
+ language: 'typescript',
630
+ platform: typeof process !== 'undefined' ? 'node' : 'browser'
631
+ }
632
+ };
633
+ logger.debug('Sending registration request to:', `${options.apiEndpoint}/api/agents/auto-register`);
634
+ const response = await transport.post(`${options.apiEndpoint}/api/agents/auto-register`, requestBody, {
635
+ timeout: 30000,
636
+ headers: {
637
+ 'Content-Type': 'application/json',
638
+ 'User-Agent': '@kya-os/mcp-i/0.1.0-alpha.2.5'
639
+ }
640
+ });
641
+ logger.debug('Registration response received:', {
642
+ status: response.status,
643
+ hasData: !!response.data,
644
+ hasDid: !!response.data?.did,
645
+ hasAgent: !!response.data?.agent
646
+ });
647
+ return response.data;
648
+ }
649
+ catch (error) {
650
+ logger.error('Registration failed:', {
651
+ message: error.message,
652
+ status: error.status,
653
+ response: error.response
654
+ });
655
+ if (error.message?.includes('429')) {
656
+ throw new Error('Rate limit exceeded. Please try again later.');
657
+ }
658
+ // Provide more helpful error messages
659
+ if (error.message?.includes('500')) {
660
+ throw new Error('Server error during registration. This might be a temporary issue.\n' +
661
+ 'Please try again in a few moments, or register manually at https://knowthat.ai/submit-agent');
662
+ }
663
+ if (error.message?.includes('400')) {
664
+ throw new Error('Invalid registration data. Please ensure all required fields are provided:\n' +
665
+ '- name: A unique name for your agent\n' +
666
+ '- publicKey: Generated automatically (check if generation succeeded)');
667
+ }
668
+ throw new Error(error.message || 'Failed to auto-register agent');
669
+ }
670
+ }
671
+ //# sourceMappingURL=index.js.map