@hai.ai/jacs 0.6.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/mcp.ts ADDED
@@ -0,0 +1,521 @@
1
+ // Transport Proxy Pattern - Intercepts at network boundary, not message level
2
+ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
3
+ import { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
4
+ import jacs from './index.js';
5
+ import { IncomingMessage, ServerResponse } from "node:http";
6
+
7
+ // Add near the top, after imports:
8
+ const isStdioTransport = (transport: any): boolean => {
9
+ return transport.constructor.name === 'StdioServerTransport' ||
10
+ transport.constructor.name === 'StdioClientTransport';
11
+ };
12
+ let enableDiagnosticLogging = false;
13
+
14
+ function jacslog(...args: any[]): void {
15
+ console.error(...args);
16
+ }
17
+
18
+ // Load JACS config only once
19
+ let jacsLoaded = false;
20
+ let jacsLoadError: Error | null = null;
21
+
22
+ async function ensureJacsLoaded(configPath: string): Promise<void> {
23
+ if (jacsLoaded) return;
24
+ if (jacsLoadError) throw jacsLoadError;
25
+
26
+ try {
27
+ jacslog(`ensureJacsLoaded: Attempting to load JACS config from: ${configPath}`);
28
+ jacsLoadError = null;
29
+ await jacs.load(configPath);
30
+ jacsLoaded = true;
31
+ jacslog(`ensureJacsLoaded: JACS agent loaded successfully from ${configPath}.`);
32
+ } catch (error) {
33
+ jacsLoadError = error as Error;
34
+ console.error(`ensureJacsLoaded: CRITICAL: Failed to load JACS config from '${configPath}'. Error:`, jacsLoadError.message);
35
+ throw jacsLoadError;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * JACS Transport Proxy - Wraps any transport with JACS encryption
41
+ *
42
+ * This proxy sits between the MCP SDK and the actual transport,
43
+ * intercepting serialized JSON strings (not JSON-RPC objects)
44
+ */
45
+ export class JACSTransportProxy implements Transport {
46
+ private jacsOperational = true;
47
+ private proxyId: string;
48
+
49
+ constructor(
50
+ private transport: Transport,
51
+ role: "client" | "server",
52
+ private jacsConfigPath?: string
53
+ ) {
54
+ this.proxyId = `JACS_${role.toUpperCase()}_PROXY`;
55
+
56
+ // Disable JACS debugging for STDIO transports to prevent stdout contamination
57
+ const suppressDebugForStdio = isStdioTransport(transport);
58
+ const enableDiagnosticLogging = process.env.JACS_MCP_DEBUG === 'true' && !suppressDebugForStdio;
59
+
60
+ if (suppressDebugForStdio) {
61
+ console.error(`[${this.proxyId}] STDIO transport detected, suppressing debug output`);
62
+ }
63
+
64
+ jacslog(`[${this.proxyId}] CONSTRUCTOR: Wrapping transport with JACS. Config: ${jacsConfigPath}`);
65
+
66
+ if (jacsConfigPath) {
67
+ ensureJacsLoaded(jacsConfigPath)
68
+ .then(() => {
69
+ this.jacsOperational = true;
70
+ jacslog(`[${this.proxyId}] JACS Loaded and operational.`);
71
+ })
72
+ .catch(err => {
73
+ this.jacsOperational = false;
74
+ console.error(`[${this.proxyId}] JACS Load FAILED:`, err.message);
75
+ });
76
+ } else {
77
+ this.jacsOperational = false;
78
+ console.warn(`[${this.proxyId}] No JACS config provided. Operating in passthrough mode.`);
79
+ }
80
+
81
+ // Intercept incoming messages from the transport
82
+ this.transport.onmessage = async (incomingData: string | JSONRPCMessage | object) => {
83
+ const logPrefix = `[${this.proxyId}] INCOMING`;
84
+
85
+ try {
86
+ let messageForSDK: JSONRPCMessage;
87
+
88
+ if (typeof incomingData === 'string') {
89
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Received string from transport (len ${incomingData.length}): ${incomingData.substring(0,100)}...`);
90
+
91
+ if (this.jacsOperational) {
92
+ // Try to decrypt/verify the string as a JACS artifact
93
+ try {
94
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Attempting JACS verification of string...`);
95
+ const verificationResult = await jacs.verifyResponse(incomingData);
96
+
97
+ let decryptedMessage: JSONRPCMessage;
98
+ if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
99
+ decryptedMessage = verificationResult.payload as JSONRPCMessage;
100
+ } else {
101
+ decryptedMessage = verificationResult as JSONRPCMessage;
102
+ }
103
+
104
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: JACS verification successful. Decrypted message: ${JSON.stringify(decryptedMessage).substring(0,100)}...`);
105
+ messageForSDK = decryptedMessage;
106
+ } catch (jacsError) {
107
+ // Not a JACS artifact, treat as plain JSON
108
+ const errorMessage = jacsError instanceof Error ? jacsError.message : "Unknown JACS error";
109
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Not a JACS artifact, parsing as plain JSON. JACS error was: ${errorMessage}`);
110
+ messageForSDK = JSON.parse(incomingData) as JSONRPCMessage;
111
+ }
112
+ } else {
113
+ // JACS not operational, parse as plain JSON
114
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: JACS not operational, parsing as plain JSON.`);
115
+ messageForSDK = JSON.parse(incomingData) as JSONRPCMessage;
116
+ }
117
+ } else if (typeof incomingData === 'object' && incomingData !== null && 'jsonrpc' in incomingData) {
118
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Received object from transport, using as-is.`);
119
+ messageForSDK = incomingData as JSONRPCMessage;
120
+ } else {
121
+ console.error(`${logPrefix}: Unexpected data type from transport:`, typeof incomingData);
122
+ throw new Error("Invalid data type from transport");
123
+ }
124
+
125
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Passing to MCP SDK: ${JSON.stringify(messageForSDK).substring(0,100)}...`);
126
+
127
+ // Pass the clean JSON-RPC message to the MCP SDK
128
+ if (this.onmessage) {
129
+ this.onmessage(messageForSDK);
130
+ }
131
+ } catch (error) {
132
+ console.error(`${logPrefix}: Error processing incoming message:`, error);
133
+ if (this.onerror) this.onerror(error as Error);
134
+ }
135
+ };
136
+
137
+ // Forward transport events
138
+ this.transport.onclose = () => {
139
+ jacslog(`[${this.proxyId}] Transport closed.`);
140
+ if (this.onclose) this.onclose();
141
+ };
142
+
143
+ this.transport.onerror = (error) => {
144
+ console.error(`[${this.proxyId}] Transport error:`, error);
145
+ if (this.onerror) this.onerror(error);
146
+ };
147
+
148
+ jacslog(`[${this.proxyId}] CONSTRUCTOR: Transport proxy initialized.`);
149
+
150
+ if ('send' in this.transport && typeof this.transport.send === 'function') {
151
+ const originalSend = this.transport.send.bind(this.transport);
152
+ this.transport.send = async (data: any) => {
153
+ if (typeof data === 'string') {
154
+ // Check if this is a server-side SSE transport
155
+ const sseTransport = this.transport as any;
156
+ if (sseTransport._sseResponse) {
157
+ // Server-side: write directly to SSE stream
158
+ sseTransport._sseResponse.write(`event: message\ndata: ${data}\n\n`);
159
+ return;
160
+ } else if (sseTransport._endpoint) {
161
+ // Client-side: use fetch (existing code)
162
+ const headers = await (sseTransport._commonHeaders?.() || Promise.resolve({}));
163
+ const response = await fetch(sseTransport._endpoint, {
164
+ method: "POST",
165
+ headers: {
166
+ ...headers,
167
+ "content-type": "application/json",
168
+ },
169
+ body: data, // Send raw string without JSON.stringify()
170
+ });
171
+ if (!response.ok) {
172
+ const text = await response.text().catch(() => null);
173
+ throw new Error(`Error POSTing to endpoint (HTTP ${response.status}): ${text}`);
174
+ }
175
+ return;
176
+ }
177
+ }
178
+ return originalSend(data);
179
+ };
180
+ }
181
+
182
+ // Replace the client monkey patch section in the constructor with this:
183
+ if (role === "client") {
184
+ jacslog(`[${this.proxyId}] Setting up EventSource interception for client...`);
185
+
186
+ // Wait for the transport to be initialized, then intercept its EventSource
187
+ setTimeout(() => {
188
+ const sseTransport = this.transport as any;
189
+ if (sseTransport._eventSource) {
190
+ jacslog(`[${this.proxyId}] Found EventSource, intercepting onmessage...`);
191
+ const originalOnMessage = sseTransport._eventSource.onmessage;
192
+
193
+ sseTransport._eventSource.onmessage = async (event: MessageEvent) => {
194
+ jacslog(`[${this.proxyId}] EventSource received message:`, event.data?.substring(0, 100));
195
+
196
+ try {
197
+ // Try JACS verification first
198
+ if (this.jacsOperational) {
199
+ const verificationResult = await jacs.verifyResponse(event.data);
200
+
201
+ let decryptedMessage: JSONRPCMessage;
202
+ if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
203
+ decryptedMessage = verificationResult.payload as JSONRPCMessage;
204
+ } else {
205
+ decryptedMessage = verificationResult as JSONRPCMessage;
206
+ }
207
+
208
+ // Clean up JACS-added null values before passing to MCP SDK
209
+ const cleanedMessage = this.removeNullValues(decryptedMessage);
210
+
211
+ jacslog(`[${this.proxyId}] JACS verification successful, passing decrypted message to MCP SDK`);
212
+ const newEvent = new MessageEvent('message', {
213
+ data: JSON.stringify(cleanedMessage)
214
+ });
215
+ originalOnMessage.call(sseTransport._eventSource, newEvent);
216
+ return;
217
+ }
218
+ } catch (jacsError) {
219
+ jacslog(`[${this.proxyId}] Not a JACS artifact, passing original message to MCP SDK`);
220
+ }
221
+
222
+ // Not JACS or JACS failed, use original handler
223
+ originalOnMessage.call(sseTransport._eventSource, event);
224
+ };
225
+ } else {
226
+ jacslog(`[${this.proxyId}] EventSource not found, will retry...`);
227
+ // Retry after transport is fully initialized
228
+ setTimeout(() => {
229
+ if ((this.transport as any)._eventSource) {
230
+ jacslog(`[${this.proxyId}] Found EventSource on retry, intercepting...`);
231
+ // Same logic as above
232
+ }
233
+ }, 100);
234
+ }
235
+ }, 50);
236
+ }
237
+ }
238
+
239
+ // MCP SDK will set these
240
+ onclose?: () => void;
241
+ onerror?: (error: Error) => void;
242
+ onmessage?: (message: JSONRPCMessage) => void;
243
+
244
+ async start(): Promise<void> {
245
+ jacslog(`[${this.proxyId}] Starting underlying transport...`);
246
+ return this.transport.start();
247
+ }
248
+
249
+ async close(): Promise<void> {
250
+ jacslog(`[${this.proxyId}] Closing underlying transport...`);
251
+ return this.transport.close();
252
+ }
253
+
254
+ // Intercept outgoing messages to the transport
255
+ async send(message: JSONRPCMessage): Promise<void> {
256
+ const logPrefix = `[${this.proxyId}] OUTGOING`;
257
+
258
+ try {
259
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: MCP SDK sending message: ${JSON.stringify(message).substring(0,100)}...`);
260
+
261
+ if (this.jacsOperational) {
262
+ // Skip JACS for error responses
263
+ if ('error' in message) {
264
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Error response, skipping JACS encryption.`);
265
+ await this.transport.send(message);
266
+ } else {
267
+ try {
268
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Applying JACS encryption to message...`);
269
+
270
+ // Clean up the message before JACS signing - remove null params
271
+ const cleanMessage = { ...message };
272
+ if ('params' in cleanMessage && cleanMessage.params === null) {
273
+ delete cleanMessage.params;
274
+ }
275
+
276
+ const jacsArtifact = await jacs.signRequest(cleanMessage);
277
+ await this.transport.send(jacsArtifact as any);
278
+
279
+ } catch (jacsError) {
280
+ console.error(`${logPrefix}: JACS encryption failed, sending plain message. Error:`, jacsError);
281
+ await this.transport.send(message);
282
+ }
283
+ }
284
+ } else {
285
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: JACS not operational, sending plain message.`);
286
+ await this.transport.send(message);
287
+ }
288
+
289
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Successfully sent to transport.`);
290
+ } catch (error) {
291
+ console.error(`${logPrefix}: Error sending message:`, error);
292
+ throw error;
293
+ }
294
+ }
295
+
296
+ // Forward transport properties
297
+ get sessionId(): string | undefined {
298
+ return (this.transport as any).sessionId;
299
+ }
300
+
301
+ // Handle HTTP POST for SSE transports (if applicable)
302
+ /**
303
+ * REQUIRED for SSE (Server-Sent Events) transport pattern in MCP.
304
+ *
305
+ * WHY THIS EXISTS:
306
+ * SSE is inherently unidirectional (server→client), but MCP requires bidirectional communication.
307
+ * The MCP SSE implementation solves this with a hybrid approach:
308
+ * - Server→Client: Uses SSE stream for real-time messages
309
+ * - Client→Server: Uses HTTP POST to a specific endpoint
310
+ *
311
+ * This function intercepts those client POST requests, decrypts JACS payloads,
312
+ * and forwards the decrypted messages to the underlying SSE transport handler.
313
+ *
314
+ * Without this, JACS-encrypted client messages would never reach the MCP server.
315
+ */
316
+ async handlePostMessage?(req: IncomingMessage & { auth?: any }, res: ServerResponse, rawBodyString?: string): Promise<void> {
317
+ const logPrefix = `[${this.proxyId}] HTTP_POST`;
318
+
319
+ // Verify the underlying transport actually supports POST handling
320
+ // (not all MCP transports do - only SSE transports need this)
321
+ if (!('handlePostMessage' in this.transport) || typeof this.transport.handlePostMessage !== 'function') {
322
+ console.error(`${logPrefix}: Underlying transport does not support handlePostMessage`);
323
+ if (!res.writableEnded) res.writeHead(500).end("Transport does not support POST handling");
324
+ return;
325
+ }
326
+
327
+ // Extract the request body (which contains the JACS-encrypted payload)
328
+ let bodyToProcess: string;
329
+ if (rawBodyString !== undefined) {
330
+ // Body already provided (likely from Express middleware)
331
+ bodyToProcess = rawBodyString;
332
+ } else {
333
+ // Manually read the request body from the HTTP stream
334
+ const bodyBuffer = [];
335
+ for await (const chunk of req) { bodyBuffer.push(chunk); }
336
+ bodyToProcess = Buffer.concat(bodyBuffer).toString();
337
+ if (!bodyToProcess) {
338
+ if (!res.writableEnded) res.writeHead(400).end("Empty body");
339
+ return;
340
+ }
341
+ }
342
+
343
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Raw body (len ${bodyToProcess.length}): ${bodyToProcess.substring(0,100)}...`);
344
+
345
+ // Add this debug line before calling jacs.verifyResponse:
346
+ jacslog(`${logPrefix}: JACS Debug - Body type: ${typeof bodyToProcess}`);
347
+ jacslog(`${logPrefix}: JACS Debug - First 200 chars:`, JSON.stringify(bodyToProcess.substring(0, 200)));
348
+ jacslog(`${logPrefix}: JACS Debug - Is valid JSON?`, (() => {
349
+ try { JSON.parse(bodyToProcess); return true; } catch { return false; }
350
+ })());
351
+
352
+ try {
353
+ let processedBody = bodyToProcess;
354
+
355
+ if (this.jacsOperational) {
356
+ // Try normalizing the JSON string before JACS verification:
357
+ try {
358
+ // First, try to parse and re-stringify to normalize
359
+ const parsedJson = JSON.parse(bodyToProcess);
360
+ const normalizedJsonString = JSON.stringify(parsedJson);
361
+
362
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Attempting JACS verification with normalized JSON...`);
363
+ const verificationResult = await jacs.verifyResponse(normalizedJsonString);
364
+
365
+ let decryptedMessage: JSONRPCMessage;
366
+ if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
367
+ decryptedMessage = verificationResult.payload as JSONRPCMessage;
368
+ } else {
369
+ decryptedMessage = verificationResult as JSONRPCMessage;
370
+ }
371
+
372
+ // Clean up JACS-added null params before passing to MCP SDK
373
+ if ('params' in decryptedMessage && decryptedMessage.params === null) {
374
+ const cleanMessage = { ...decryptedMessage };
375
+ delete cleanMessage.params;
376
+ processedBody = JSON.stringify(cleanMessage);
377
+ } else {
378
+ processedBody = JSON.stringify(decryptedMessage);
379
+ }
380
+
381
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: JACS verification successful. Decrypted to: ${processedBody.substring(0,100)}...`);
382
+ } catch (parseError) {
383
+ // If it's not valid JSON, try with original string
384
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: JSON normalization failed, trying original string...`);
385
+ const verificationResult = await jacs.verifyResponse(bodyToProcess);
386
+
387
+ let decryptedMessage: JSONRPCMessage;
388
+ if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
389
+ decryptedMessage = verificationResult.payload as JSONRPCMessage;
390
+ } else {
391
+ decryptedMessage = verificationResult as JSONRPCMessage;
392
+ }
393
+
394
+ // Clean up JACS-added null params before passing to MCP SDK
395
+ if ('params' in decryptedMessage && decryptedMessage.params === null) {
396
+ const cleanMessage = { ...decryptedMessage };
397
+ delete cleanMessage.params;
398
+ processedBody = JSON.stringify(cleanMessage);
399
+ } else {
400
+ processedBody = JSON.stringify(decryptedMessage);
401
+ }
402
+
403
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: JACS verification successful. Decrypted to: ${processedBody.substring(0,100)}...`);
404
+ }
405
+ }
406
+
407
+ // Forward to underlying transport's POST handler
408
+ await this.transport.handlePostMessage(req, res, processedBody);
409
+
410
+ } catch (error) {
411
+ console.error(`${logPrefix}: Error processing POST:`, error);
412
+ if (!res.writableEnded) {
413
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
414
+ res.writeHead(500).end(`Error: ${errorMessage}`);
415
+ }
416
+ }
417
+ }
418
+
419
+ private async handleIncomingMessage(incomingData: string | JSONRPCMessage | object): Promise<void> {
420
+ const logPrefix = `[${this.proxyId}] INCOMING`;
421
+
422
+ try {
423
+ let messageForSDK: JSONRPCMessage;
424
+
425
+ if (typeof incomingData === 'string') {
426
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Received string from transport (len ${incomingData.length}): ${incomingData.substring(0,100)}...`);
427
+
428
+ if (this.jacsOperational) {
429
+ try {
430
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Attempting JACS verification of string...`);
431
+ const verificationResult = await jacs.verifyResponse(incomingData);
432
+
433
+ let decryptedMessage: JSONRPCMessage;
434
+ if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
435
+ decryptedMessage = verificationResult.payload as JSONRPCMessage;
436
+ } else {
437
+ decryptedMessage = verificationResult as JSONRPCMessage;
438
+ }
439
+
440
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: JACS verification successful. Decrypted message: ${JSON.stringify(decryptedMessage).substring(0,100)}...`);
441
+ messageForSDK = decryptedMessage;
442
+ } catch (jacsError) {
443
+ const errorMessage = jacsError instanceof Error ? jacsError.message : "Unknown JACS error";
444
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Not a JACS artifact, parsing as plain JSON. JACS error was: ${errorMessage}`);
445
+ messageForSDK = JSON.parse(incomingData) as JSONRPCMessage;
446
+ }
447
+ } else {
448
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: JACS not operational, parsing as plain JSON.`);
449
+ messageForSDK = JSON.parse(incomingData) as JSONRPCMessage;
450
+ }
451
+ } else if (typeof incomingData === 'object' && incomingData !== null && 'jsonrpc' in incomingData) {
452
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Received object from transport, using as-is.`);
453
+ messageForSDK = incomingData as JSONRPCMessage;
454
+ } else {
455
+ console.error(`${logPrefix}: Unexpected data type from transport:`, typeof incomingData);
456
+ throw new Error("Invalid data type from transport");
457
+ }
458
+
459
+ if (enableDiagnosticLogging) jacslog(`${logPrefix}: Passing to MCP SDK: ${JSON.stringify(messageForSDK).substring(0,100)}...`);
460
+
461
+ if (this.onmessage) {
462
+ this.onmessage(messageForSDK);
463
+ }
464
+ } catch (error) {
465
+ console.error(`${logPrefix}: Error processing incoming message:`, error);
466
+ if (this.onerror) this.onerror(error as Error);
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Removes null and undefined values from JSON objects to prevent MCP schema validation failures.
472
+ *
473
+ * WORKAROUND for MCP JSON Schema validation issues:
474
+ * - Addresses strict validators (like Anthropic's API) that reject schemas with null values
475
+ * - Handles edge cases where tools have null inputSchema causing client validation errors
476
+ * - Prevents "invalid_type: expected object, received undefined" errors in TypeScript SDK v1.9.0
477
+ * - Cleans up malformed schemas before transmission to avoid -32602 JSON-RPC errors
478
+ *
479
+ * Related issues:
480
+ * - https://github.com/modelcontextprotocol/typescript-sdk/issues/400 (null schema tools)
481
+ * - https://github.com/anthropics/claude-code/issues/586 (Anthropic strict Draft 2020-12)
482
+ * - https://github.com/agno-agi/agno/issues/2791 (missing type field)
483
+ *
484
+ * @param obj - The object to clean (typically MCP tool/resource schemas)
485
+ * @returns A new object with all null/undefined values recursively removed
486
+ */
487
+ private removeNullValues(obj: any): any {
488
+ if (obj === null || obj === undefined) return undefined;
489
+ if (typeof obj !== 'object') return obj;
490
+ if (Array.isArray(obj)) return obj.map(item => this.removeNullValues(item));
491
+
492
+ const cleaned: any = {};
493
+ for (const [key, value] of Object.entries(obj)) {
494
+ const cleanedValue = this.removeNullValues(value);
495
+ if (cleanedValue !== null && cleanedValue !== undefined) {
496
+ cleaned[key] = cleanedValue;
497
+ }
498
+ }
499
+ return cleaned;
500
+ }
501
+ }
502
+
503
+ // Factory functions
504
+ export function createJACSTransportProxy(
505
+ transport: Transport,
506
+ configPath: string,
507
+ role: "client" | "server"
508
+ ): JACSTransportProxy {
509
+ jacslog(`Creating JACS Transport Proxy for role: ${role}`);
510
+ return new JACSTransportProxy(transport, role, configPath);
511
+ }
512
+
513
+ export async function createJACSTransportProxyAsync(
514
+ transport: Transport,
515
+ configPath: string,
516
+ role: "client" | "server"
517
+ ): Promise<JACSTransportProxy> {
518
+ jacslog(`Creating JACS Transport Proxy (async) for role: ${role}`);
519
+ await ensureJacsLoaded(configPath);
520
+ return new JACSTransportProxy(transport, role, configPath);
521
+ }
package/package.json ADDED
@@ -0,0 +1,103 @@
1
+ {
2
+ "name": "@hai.ai/jacs",
3
+ "version": "0.6.0",
4
+ "description": "JACS (JSON Agent Communication Standard) - Data provenance and cryptographic signing for AI agents",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "overrides": {
8
+ "body-parser": "^2.2.1",
9
+ "qs": "^6.14.0"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "import": "./index.js",
14
+ "require": "./index.js",
15
+ "types": "./index.d.ts"
16
+ },
17
+ "./simple": {
18
+ "import": "./simple.js",
19
+ "require": "./simple.js",
20
+ "types": "./simple.d.ts"
21
+ },
22
+ "./a2a": {
23
+ "import": "./src/a2a.js",
24
+ "require": "./src/a2a.js",
25
+ "types": "./src/a2a.d.ts"
26
+ },
27
+ "./mcp": {
28
+ "import": "./mcp.js",
29
+ "require": "./mcp.js",
30
+ "types": "./mcp.d.ts"
31
+ },
32
+ "./http": {
33
+ "import": "./http.js",
34
+ "require": "./http.js",
35
+ "types": "./http.d.ts"
36
+ }
37
+ },
38
+ "scripts": {
39
+ "build": "napi build --platform --release && npm run build:ts",
40
+ "build:debug": "napi build --platform && npm run build:ts",
41
+ "build:ts": "tsc",
42
+ "test": "JACS_PRIVATE_KEY_PASSWORD='TestP@ss123!#' mocha --timeout 15000 'test/**/*.test.js'",
43
+ "test:utils": "mocha --timeout 15000 'test/utils.test.js'",
44
+ "test:agent": "JACS_PRIVATE_KEY_PASSWORD='TestP@ss123!#' mocha --timeout 15000 'test/agent.test.js'",
45
+ "test:simple": "JACS_PRIVATE_KEY_PASSWORD='TestP@ss123!#' mocha --timeout 15000 'test/simple.test.js'",
46
+ "test:a2a": "mocha --timeout 15000 'test/a2a.test.js'",
47
+ "test:integration": "JACS_PRIVATE_KEY_PASSWORD='TestP@ss123!#' mocha --timeout 15000 'test/integration.test.js'"
48
+ },
49
+ "dependencies": {
50
+ "@modelcontextprotocol/sdk": "^1.25.3",
51
+ "@napi-rs/cli": "^2.16.3",
52
+ "uuid": "^13.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.15",
56
+ "chai": "^6.2.2",
57
+ "mocha": "^11.7.5",
58
+ "sinon": "^21.0.1",
59
+ "typescript": "^5.7.0"
60
+ },
61
+ "napi": {
62
+ "name": "jacs",
63
+ "triples": {
64
+ "defaults": true,
65
+ "additional": [
66
+ "x86_64-apple-darwin",
67
+ "aarch64-apple-darwin"
68
+ ]
69
+ }
70
+ },
71
+ "author": "HAI.AI",
72
+ "license": "ISC",
73
+ "homepage": "https://hai.ai/jacs",
74
+ "repository": {
75
+ "type": "git",
76
+ "url": "https://github.com/HumanAssisted/JACS.git"
77
+ },
78
+ "files": [
79
+ "index.js",
80
+ "index.d.ts",
81
+ "simple.js",
82
+ "simple.d.ts",
83
+ "src/a2a.js",
84
+ "src/a2a.d.ts",
85
+ "mcp.ts",
86
+ "mcp.d.ts",
87
+ "mcp.js",
88
+ "mcp.js.map",
89
+ "http.js",
90
+ "http.d.ts",
91
+ "jacs.*.node"
92
+ ],
93
+ "keywords": [
94
+ "jacs",
95
+ "ai",
96
+ "agent",
97
+ "signing",
98
+ "verification",
99
+ "cryptography",
100
+ "mcp",
101
+ "model-context-protocol"
102
+ ]
103
+ }