@hai.ai/jacs 0.6.0 → 0.8.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/README.md +336 -52
- package/client.d.ts +96 -0
- package/client.js +560 -0
- package/express.d.ts +69 -0
- package/express.js +130 -0
- package/express.js.map +1 -0
- package/index.d.ts +117 -96
- package/index.js +19 -17
- package/jacs.darwin-arm64.node +0 -0
- package/jacs.darwin-x64.node +0 -0
- package/jacs.linux-arm-gnueabihf.node +0 -0
- package/jacs.linux-arm-musleabihf.node +0 -0
- package/jacs.linux-arm64-gnu.node +0 -0
- package/jacs.linux-x64-gnu.node +0 -0
- package/jacs.linux-x64-musl.node +0 -0
- package/koa.d.ts +59 -0
- package/koa.js +124 -0
- package/koa.js.map +1 -0
- package/langchain.d.ts +97 -0
- package/langchain.js +439 -0
- package/langchain.js.map +1 -0
- package/mcp.d.ts +75 -42
- package/mcp.js +449 -422
- package/mcp.js.map +1 -1
- package/package.json +91 -7
- package/scripts/install-cli.js +125 -0
- package/simple.d.ts +92 -430
- package/simple.js +507 -524
- package/src/a2a.js +2 -2
- package/testing.d.ts +39 -0
- package/testing.js +49 -0
- package/vercel-ai.d.ts +54 -0
- package/vercel-ai.js +162 -0
- package/vercel-ai.js.map +1 -0
- package/mcp.ts +0 -521
package/mcp.js
CHANGED
|
@@ -1,141 +1,61 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.JACSTransportProxy = void 0;
|
|
7
4
|
exports.createJACSTransportProxy = createJACSTransportProxy;
|
|
8
5
|
exports.createJACSTransportProxyAsync = createJACSTransportProxyAsync;
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
exports.getJacsMcpToolDefinitions = getJacsMcpToolDefinitions;
|
|
7
|
+
exports.handleJacsMcpToolCall = handleJacsMcpToolCall;
|
|
8
|
+
exports.registerJacsTools = registerJacsTools;
|
|
9
|
+
const index_js_1 = require("./index.js");
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
11
13
|
const isStdioTransport = (transport) => {
|
|
12
14
|
return transport.constructor.name === 'StdioServerTransport' ||
|
|
13
15
|
transport.constructor.name === 'StdioClientTransport';
|
|
14
16
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
function debugLog(proxyId, enabled, ...args) {
|
|
18
|
+
if (enabled)
|
|
19
|
+
console.error(`[${proxyId}]`, ...args);
|
|
18
20
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
throw jacsLoadError;
|
|
27
|
-
try {
|
|
28
|
-
jacslog(`ensureJacsLoaded: Attempting to load JACS config from: ${configPath}`);
|
|
29
|
-
jacsLoadError = null;
|
|
30
|
-
await index_js_1.default.load(configPath);
|
|
31
|
-
jacsLoaded = true;
|
|
32
|
-
jacslog(`ensureJacsLoaded: JACS agent loaded successfully from ${configPath}.`);
|
|
21
|
+
/**
|
|
22
|
+
* Extract the native JacsAgent from either a JacsAgent or JacsClient instance.
|
|
23
|
+
* JacsClient stores its native agent in a private `agent` field.
|
|
24
|
+
*/
|
|
25
|
+
function extractNativeAgent(clientOrAgent) {
|
|
26
|
+
if (clientOrAgent instanceof index_js_1.JacsAgent) {
|
|
27
|
+
return clientOrAgent;
|
|
33
28
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
throw
|
|
29
|
+
// JacsClient - access the private native agent at runtime
|
|
30
|
+
const native = clientOrAgent.agent;
|
|
31
|
+
if (!native) {
|
|
32
|
+
throw new Error('JacsClient has no loaded agent. Call quickstart(), ephemeral(), load(), or create() before wrapping with JACSTransportProxy.');
|
|
38
33
|
}
|
|
34
|
+
return native;
|
|
39
35
|
}
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// JACSTransportProxy
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
40
39
|
/**
|
|
41
|
-
* JACS Transport Proxy - Wraps any transport with JACS
|
|
40
|
+
* JACS Transport Proxy - Wraps any MCP transport with JACS signing/verification.
|
|
42
41
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
* Outgoing messages are signed with `signRequest()`.
|
|
43
|
+
* Incoming messages are verified with `verifyResponse()`, falling back to
|
|
44
|
+
* plain JSON if verification fails (the message was not JACS-signed).
|
|
45
45
|
*/
|
|
46
46
|
class JACSTransportProxy {
|
|
47
|
-
constructor(transport,
|
|
47
|
+
constructor(transport, clientOrAgent, role = "server") {
|
|
48
48
|
this.transport = transport;
|
|
49
|
-
this.
|
|
50
|
-
this.jacsOperational = true;
|
|
49
|
+
this.nativeAgent = extractNativeAgent(clientOrAgent);
|
|
51
50
|
this.proxyId = `JACS_${role.toUpperCase()}_PROXY`;
|
|
52
|
-
// Disable JACS debugging for STDIO transports to prevent stdout contamination
|
|
53
51
|
const suppressDebugForStdio = isStdioTransport(transport);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
jacslog(`[${this.proxyId}] CONSTRUCTOR: Wrapping transport with JACS. Config: ${jacsConfigPath}`);
|
|
59
|
-
if (jacsConfigPath) {
|
|
60
|
-
ensureJacsLoaded(jacsConfigPath)
|
|
61
|
-
.then(() => {
|
|
62
|
-
this.jacsOperational = true;
|
|
63
|
-
jacslog(`[${this.proxyId}] JACS Loaded and operational.`);
|
|
64
|
-
})
|
|
65
|
-
.catch(err => {
|
|
66
|
-
this.jacsOperational = false;
|
|
67
|
-
console.error(`[${this.proxyId}] JACS Load FAILED:`, err.message);
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
this.jacsOperational = false;
|
|
72
|
-
console.warn(`[${this.proxyId}] No JACS config provided. Operating in passthrough mode.`);
|
|
73
|
-
}
|
|
74
|
-
// Intercept incoming messages from the transport
|
|
75
|
-
this.transport.onmessage = async (incomingData) => {
|
|
76
|
-
const logPrefix = `[${this.proxyId}] INCOMING`;
|
|
77
|
-
try {
|
|
78
|
-
let messageForSDK;
|
|
79
|
-
if (typeof incomingData === 'string') {
|
|
80
|
-
if (enableDiagnosticLogging)
|
|
81
|
-
jacslog(`${logPrefix}: Received string from transport (len ${incomingData.length}): ${incomingData.substring(0, 100)}...`);
|
|
82
|
-
if (this.jacsOperational) {
|
|
83
|
-
// Try to decrypt/verify the string as a JACS artifact
|
|
84
|
-
try {
|
|
85
|
-
if (enableDiagnosticLogging)
|
|
86
|
-
jacslog(`${logPrefix}: Attempting JACS verification of string...`);
|
|
87
|
-
const verificationResult = await index_js_1.default.verifyResponse(incomingData);
|
|
88
|
-
let decryptedMessage;
|
|
89
|
-
if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
|
|
90
|
-
decryptedMessage = verificationResult.payload;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
decryptedMessage = verificationResult;
|
|
94
|
-
}
|
|
95
|
-
if (enableDiagnosticLogging)
|
|
96
|
-
jacslog(`${logPrefix}: JACS verification successful. Decrypted message: ${JSON.stringify(decryptedMessage).substring(0, 100)}...`);
|
|
97
|
-
messageForSDK = decryptedMessage;
|
|
98
|
-
}
|
|
99
|
-
catch (jacsError) {
|
|
100
|
-
// Not a JACS artifact, treat as plain JSON
|
|
101
|
-
const errorMessage = jacsError instanceof Error ? jacsError.message : "Unknown JACS error";
|
|
102
|
-
if (enableDiagnosticLogging)
|
|
103
|
-
jacslog(`${logPrefix}: Not a JACS artifact, parsing as plain JSON. JACS error was: ${errorMessage}`);
|
|
104
|
-
messageForSDK = JSON.parse(incomingData);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
// JACS not operational, parse as plain JSON
|
|
109
|
-
if (enableDiagnosticLogging)
|
|
110
|
-
jacslog(`${logPrefix}: JACS not operational, parsing as plain JSON.`);
|
|
111
|
-
messageForSDK = JSON.parse(incomingData);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
else if (typeof incomingData === 'object' && incomingData !== null && 'jsonrpc' in incomingData) {
|
|
115
|
-
if (enableDiagnosticLogging)
|
|
116
|
-
jacslog(`${logPrefix}: Received object from transport, using as-is.`);
|
|
117
|
-
messageForSDK = incomingData;
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
console.error(`${logPrefix}: Unexpected data type from transport:`, typeof incomingData);
|
|
121
|
-
throw new Error("Invalid data type from transport");
|
|
122
|
-
}
|
|
123
|
-
if (enableDiagnosticLogging)
|
|
124
|
-
jacslog(`${logPrefix}: Passing to MCP SDK: ${JSON.stringify(messageForSDK).substring(0, 100)}...`);
|
|
125
|
-
// Pass the clean JSON-RPC message to the MCP SDK
|
|
126
|
-
if (this.onmessage) {
|
|
127
|
-
this.onmessage(messageForSDK);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
console.error(`${logPrefix}: Error processing incoming message:`, error);
|
|
132
|
-
if (this.onerror)
|
|
133
|
-
this.onerror(error);
|
|
134
|
-
}
|
|
52
|
+
this.debug = process.env.JACS_MCP_DEBUG === 'true' && !suppressDebugForStdio;
|
|
53
|
+
// Intercept incoming messages from the wrapped transport
|
|
54
|
+
this.transport.onmessage = (incomingData) => {
|
|
55
|
+
this.handleIncoming(incomingData);
|
|
135
56
|
};
|
|
136
|
-
// Forward transport events
|
|
57
|
+
// Forward transport lifecycle events
|
|
137
58
|
this.transport.onclose = () => {
|
|
138
|
-
jacslog(`[${this.proxyId}] Transport closed.`);
|
|
139
59
|
if (this.onclose)
|
|
140
60
|
this.onclose();
|
|
141
61
|
};
|
|
@@ -144,344 +64,86 @@ class JACSTransportProxy {
|
|
|
144
64
|
if (this.onerror)
|
|
145
65
|
this.onerror(error);
|
|
146
66
|
};
|
|
147
|
-
jacslog(`[${this.proxyId}] CONSTRUCTOR: Transport proxy initialized.`);
|
|
148
|
-
if ('send' in this.transport && typeof this.transport.send === 'function') {
|
|
149
|
-
const originalSend = this.transport.send.bind(this.transport);
|
|
150
|
-
this.transport.send = async (data) => {
|
|
151
|
-
if (typeof data === 'string') {
|
|
152
|
-
// Check if this is a server-side SSE transport
|
|
153
|
-
const sseTransport = this.transport;
|
|
154
|
-
if (sseTransport._sseResponse) {
|
|
155
|
-
// Server-side: write directly to SSE stream
|
|
156
|
-
sseTransport._sseResponse.write(`event: message\ndata: ${data}\n\n`);
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
else if (sseTransport._endpoint) {
|
|
160
|
-
// Client-side: use fetch (existing code)
|
|
161
|
-
const headers = await (sseTransport._commonHeaders?.() || Promise.resolve({}));
|
|
162
|
-
const response = await fetch(sseTransport._endpoint, {
|
|
163
|
-
method: "POST",
|
|
164
|
-
headers: {
|
|
165
|
-
...headers,
|
|
166
|
-
"content-type": "application/json",
|
|
167
|
-
},
|
|
168
|
-
body: data, // Send raw string without JSON.stringify()
|
|
169
|
-
});
|
|
170
|
-
if (!response.ok) {
|
|
171
|
-
const text = await response.text().catch(() => null);
|
|
172
|
-
throw new Error(`Error POSTing to endpoint (HTTP ${response.status}): ${text}`);
|
|
173
|
-
}
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return originalSend(data);
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
// Replace the client monkey patch section in the constructor with this:
|
|
181
|
-
if (role === "client") {
|
|
182
|
-
jacslog(`[${this.proxyId}] Setting up EventSource interception for client...`);
|
|
183
|
-
// Wait for the transport to be initialized, then intercept its EventSource
|
|
184
|
-
setTimeout(() => {
|
|
185
|
-
const sseTransport = this.transport;
|
|
186
|
-
if (sseTransport._eventSource) {
|
|
187
|
-
jacslog(`[${this.proxyId}] Found EventSource, intercepting onmessage...`);
|
|
188
|
-
const originalOnMessage = sseTransport._eventSource.onmessage;
|
|
189
|
-
sseTransport._eventSource.onmessage = async (event) => {
|
|
190
|
-
jacslog(`[${this.proxyId}] EventSource received message:`, event.data?.substring(0, 100));
|
|
191
|
-
try {
|
|
192
|
-
// Try JACS verification first
|
|
193
|
-
if (this.jacsOperational) {
|
|
194
|
-
const verificationResult = await index_js_1.default.verifyResponse(event.data);
|
|
195
|
-
let decryptedMessage;
|
|
196
|
-
if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
|
|
197
|
-
decryptedMessage = verificationResult.payload;
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
decryptedMessage = verificationResult;
|
|
201
|
-
}
|
|
202
|
-
// Clean up JACS-added null values before passing to MCP SDK
|
|
203
|
-
const cleanedMessage = this.removeNullValues(decryptedMessage);
|
|
204
|
-
jacslog(`[${this.proxyId}] JACS verification successful, passing decrypted message to MCP SDK`);
|
|
205
|
-
const newEvent = new MessageEvent('message', {
|
|
206
|
-
data: JSON.stringify(cleanedMessage)
|
|
207
|
-
});
|
|
208
|
-
originalOnMessage.call(sseTransport._eventSource, newEvent);
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
catch (jacsError) {
|
|
213
|
-
jacslog(`[${this.proxyId}] Not a JACS artifact, passing original message to MCP SDK`);
|
|
214
|
-
}
|
|
215
|
-
// Not JACS or JACS failed, use original handler
|
|
216
|
-
originalOnMessage.call(sseTransport._eventSource, event);
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
jacslog(`[${this.proxyId}] EventSource not found, will retry...`);
|
|
221
|
-
// Retry after transport is fully initialized
|
|
222
|
-
setTimeout(() => {
|
|
223
|
-
if (this.transport._eventSource) {
|
|
224
|
-
jacslog(`[${this.proxyId}] Found EventSource on retry, intercepting...`);
|
|
225
|
-
// Same logic as above
|
|
226
|
-
}
|
|
227
|
-
}, 100);
|
|
228
|
-
}
|
|
229
|
-
}, 50);
|
|
230
|
-
}
|
|
231
67
|
}
|
|
68
|
+
// -------------------------------------------------------------------------
|
|
69
|
+
// Transport interface
|
|
70
|
+
// -------------------------------------------------------------------------
|
|
232
71
|
async start() {
|
|
233
|
-
jacslog(`[${this.proxyId}] Starting underlying transport...`);
|
|
234
72
|
return this.transport.start();
|
|
235
73
|
}
|
|
236
74
|
async close() {
|
|
237
|
-
jacslog(`[${this.proxyId}] Closing underlying transport...`);
|
|
238
75
|
return this.transport.close();
|
|
239
76
|
}
|
|
240
|
-
// Intercept outgoing messages to the transport
|
|
241
77
|
async send(message) {
|
|
242
|
-
|
|
78
|
+
// Skip signing for error responses
|
|
79
|
+
if ('error' in message) {
|
|
80
|
+
debugLog(this.proxyId, this.debug, 'OUTGOING: error response, skipping signing');
|
|
81
|
+
await this.transport.send(message);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
243
84
|
try {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (
|
|
247
|
-
|
|
248
|
-
if ('error' in message) {
|
|
249
|
-
if (enableDiagnosticLogging)
|
|
250
|
-
jacslog(`${logPrefix}: Error response, skipping JACS encryption.`);
|
|
251
|
-
await this.transport.send(message);
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
try {
|
|
255
|
-
if (enableDiagnosticLogging)
|
|
256
|
-
jacslog(`${logPrefix}: Applying JACS encryption to message...`);
|
|
257
|
-
// Clean up the message before JACS signing - remove null params
|
|
258
|
-
const cleanMessage = { ...message };
|
|
259
|
-
if ('params' in cleanMessage && cleanMessage.params === null) {
|
|
260
|
-
delete cleanMessage.params;
|
|
261
|
-
}
|
|
262
|
-
const jacsArtifact = await index_js_1.default.signRequest(cleanMessage);
|
|
263
|
-
await this.transport.send(jacsArtifact);
|
|
264
|
-
}
|
|
265
|
-
catch (jacsError) {
|
|
266
|
-
console.error(`${logPrefix}: JACS encryption failed, sending plain message. Error:`, jacsError);
|
|
267
|
-
await this.transport.send(message);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
if (enableDiagnosticLogging)
|
|
273
|
-
jacslog(`${logPrefix}: JACS not operational, sending plain message.`);
|
|
274
|
-
await this.transport.send(message);
|
|
85
|
+
// Clean null params before signing (MCP SDK sometimes sends null params)
|
|
86
|
+
const cleanMessage = { ...message };
|
|
87
|
+
if ('params' in cleanMessage && cleanMessage.params === null) {
|
|
88
|
+
delete cleanMessage.params;
|
|
275
89
|
}
|
|
276
|
-
|
|
277
|
-
|
|
90
|
+
debugLog(this.proxyId, this.debug, 'OUTGOING: signing message');
|
|
91
|
+
const signed = this.nativeAgent.signRequest(cleanMessage);
|
|
92
|
+
await this.transport.send(signed);
|
|
278
93
|
}
|
|
279
|
-
catch (
|
|
280
|
-
console.error(
|
|
281
|
-
|
|
94
|
+
catch (signError) {
|
|
95
|
+
console.error(`[${this.proxyId}] Signing failed, sending plain message:`, signError);
|
|
96
|
+
await this.transport.send(message);
|
|
282
97
|
}
|
|
283
98
|
}
|
|
284
|
-
// Forward transport properties
|
|
285
99
|
get sessionId() {
|
|
286
100
|
return this.transport.sessionId;
|
|
287
101
|
}
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
* WHY THIS EXISTS:
|
|
293
|
-
* SSE is inherently unidirectional (server→client), but MCP requires bidirectional communication.
|
|
294
|
-
* The MCP SSE implementation solves this with a hybrid approach:
|
|
295
|
-
* - Server→Client: Uses SSE stream for real-time messages
|
|
296
|
-
* - Client→Server: Uses HTTP POST to a specific endpoint
|
|
297
|
-
*
|
|
298
|
-
* This function intercepts those client POST requests, decrypts JACS payloads,
|
|
299
|
-
* and forwards the decrypted messages to the underlying SSE transport handler.
|
|
300
|
-
*
|
|
301
|
-
* Without this, JACS-encrypted client messages would never reach the MCP server.
|
|
302
|
-
*/
|
|
303
|
-
async handlePostMessage(req, res, rawBodyString) {
|
|
304
|
-
const logPrefix = `[${this.proxyId}] HTTP_POST`;
|
|
305
|
-
// Verify the underlying transport actually supports POST handling
|
|
306
|
-
// (not all MCP transports do - only SSE transports need this)
|
|
307
|
-
if (!('handlePostMessage' in this.transport) || typeof this.transport.handlePostMessage !== 'function') {
|
|
308
|
-
console.error(`${logPrefix}: Underlying transport does not support handlePostMessage`);
|
|
309
|
-
if (!res.writableEnded)
|
|
310
|
-
res.writeHead(500).end("Transport does not support POST handling");
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
// Extract the request body (which contains the JACS-encrypted payload)
|
|
314
|
-
let bodyToProcess;
|
|
315
|
-
if (rawBodyString !== undefined) {
|
|
316
|
-
// Body already provided (likely from Express middleware)
|
|
317
|
-
bodyToProcess = rawBodyString;
|
|
318
|
-
}
|
|
319
|
-
else {
|
|
320
|
-
// Manually read the request body from the HTTP stream
|
|
321
|
-
const bodyBuffer = [];
|
|
322
|
-
for await (const chunk of req) {
|
|
323
|
-
bodyBuffer.push(chunk);
|
|
324
|
-
}
|
|
325
|
-
bodyToProcess = Buffer.concat(bodyBuffer).toString();
|
|
326
|
-
if (!bodyToProcess) {
|
|
327
|
-
if (!res.writableEnded)
|
|
328
|
-
res.writeHead(400).end("Empty body");
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
if (enableDiagnosticLogging)
|
|
333
|
-
jacslog(`${logPrefix}: Raw body (len ${bodyToProcess.length}): ${bodyToProcess.substring(0, 100)}...`);
|
|
334
|
-
// Add this debug line before calling jacs.verifyResponse:
|
|
335
|
-
jacslog(`${logPrefix}: JACS Debug - Body type: ${typeof bodyToProcess}`);
|
|
336
|
-
jacslog(`${logPrefix}: JACS Debug - First 200 chars:`, JSON.stringify(bodyToProcess.substring(0, 200)));
|
|
337
|
-
jacslog(`${logPrefix}: JACS Debug - Is valid JSON?`, (() => {
|
|
338
|
-
try {
|
|
339
|
-
JSON.parse(bodyToProcess);
|
|
340
|
-
return true;
|
|
341
|
-
}
|
|
342
|
-
catch {
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
})());
|
|
346
|
-
try {
|
|
347
|
-
let processedBody = bodyToProcess;
|
|
348
|
-
if (this.jacsOperational) {
|
|
349
|
-
// Try normalizing the JSON string before JACS verification:
|
|
350
|
-
try {
|
|
351
|
-
// First, try to parse and re-stringify to normalize
|
|
352
|
-
const parsedJson = JSON.parse(bodyToProcess);
|
|
353
|
-
const normalizedJsonString = JSON.stringify(parsedJson);
|
|
354
|
-
if (enableDiagnosticLogging)
|
|
355
|
-
jacslog(`${logPrefix}: Attempting JACS verification with normalized JSON...`);
|
|
356
|
-
const verificationResult = await index_js_1.default.verifyResponse(normalizedJsonString);
|
|
357
|
-
let decryptedMessage;
|
|
358
|
-
if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
|
|
359
|
-
decryptedMessage = verificationResult.payload;
|
|
360
|
-
}
|
|
361
|
-
else {
|
|
362
|
-
decryptedMessage = verificationResult;
|
|
363
|
-
}
|
|
364
|
-
// Clean up JACS-added null params before passing to MCP SDK
|
|
365
|
-
if ('params' in decryptedMessage && decryptedMessage.params === null) {
|
|
366
|
-
const cleanMessage = { ...decryptedMessage };
|
|
367
|
-
delete cleanMessage.params;
|
|
368
|
-
processedBody = JSON.stringify(cleanMessage);
|
|
369
|
-
}
|
|
370
|
-
else {
|
|
371
|
-
processedBody = JSON.stringify(decryptedMessage);
|
|
372
|
-
}
|
|
373
|
-
if (enableDiagnosticLogging)
|
|
374
|
-
jacslog(`${logPrefix}: JACS verification successful. Decrypted to: ${processedBody.substring(0, 100)}...`);
|
|
375
|
-
}
|
|
376
|
-
catch (parseError) {
|
|
377
|
-
// If it's not valid JSON, try with original string
|
|
378
|
-
if (enableDiagnosticLogging)
|
|
379
|
-
jacslog(`${logPrefix}: JSON normalization failed, trying original string...`);
|
|
380
|
-
const verificationResult = await index_js_1.default.verifyResponse(bodyToProcess);
|
|
381
|
-
let decryptedMessage;
|
|
382
|
-
if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
|
|
383
|
-
decryptedMessage = verificationResult.payload;
|
|
384
|
-
}
|
|
385
|
-
else {
|
|
386
|
-
decryptedMessage = verificationResult;
|
|
387
|
-
}
|
|
388
|
-
// Clean up JACS-added null params before passing to MCP SDK
|
|
389
|
-
if ('params' in decryptedMessage && decryptedMessage.params === null) {
|
|
390
|
-
const cleanMessage = { ...decryptedMessage };
|
|
391
|
-
delete cleanMessage.params;
|
|
392
|
-
processedBody = JSON.stringify(cleanMessage);
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
processedBody = JSON.stringify(decryptedMessage);
|
|
396
|
-
}
|
|
397
|
-
if (enableDiagnosticLogging)
|
|
398
|
-
jacslog(`${logPrefix}: JACS verification successful. Decrypted to: ${processedBody.substring(0, 100)}...`);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
// Forward to underlying transport's POST handler
|
|
402
|
-
await this.transport.handlePostMessage(req, res, processedBody);
|
|
403
|
-
}
|
|
404
|
-
catch (error) {
|
|
405
|
-
console.error(`${logPrefix}: Error processing POST:`, error);
|
|
406
|
-
if (!res.writableEnded) {
|
|
407
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
408
|
-
res.writeHead(500).end(`Error: ${errorMessage}`);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
async handleIncomingMessage(incomingData) {
|
|
413
|
-
const logPrefix = `[${this.proxyId}] INCOMING`;
|
|
102
|
+
// -------------------------------------------------------------------------
|
|
103
|
+
// Internal
|
|
104
|
+
// -------------------------------------------------------------------------
|
|
105
|
+
handleIncoming(incomingData) {
|
|
414
106
|
try {
|
|
415
107
|
let messageForSDK;
|
|
416
108
|
if (typeof incomingData === 'string') {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
let decryptedMessage;
|
|
425
|
-
if (verificationResult && typeof verificationResult === 'object' && 'payload' in verificationResult) {
|
|
426
|
-
decryptedMessage = verificationResult.payload;
|
|
427
|
-
}
|
|
428
|
-
else {
|
|
429
|
-
decryptedMessage = verificationResult;
|
|
430
|
-
}
|
|
431
|
-
if (enableDiagnosticLogging)
|
|
432
|
-
jacslog(`${logPrefix}: JACS verification successful. Decrypted message: ${JSON.stringify(decryptedMessage).substring(0, 100)}...`);
|
|
433
|
-
messageForSDK = decryptedMessage;
|
|
434
|
-
}
|
|
435
|
-
catch (jacsError) {
|
|
436
|
-
const errorMessage = jacsError instanceof Error ? jacsError.message : "Unknown JACS error";
|
|
437
|
-
if (enableDiagnosticLogging)
|
|
438
|
-
jacslog(`${logPrefix}: Not a JACS artifact, parsing as plain JSON. JACS error was: ${errorMessage}`);
|
|
439
|
-
messageForSDK = JSON.parse(incomingData);
|
|
440
|
-
}
|
|
109
|
+
// Try JACS verification first
|
|
110
|
+
try {
|
|
111
|
+
debugLog(this.proxyId, this.debug, 'INCOMING: attempting JACS verification');
|
|
112
|
+
const result = this.nativeAgent.verifyResponse(incomingData);
|
|
113
|
+
messageForSDK = (result && typeof result === 'object' && 'payload' in result)
|
|
114
|
+
? result.payload
|
|
115
|
+
: result;
|
|
441
116
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
117
|
+
catch {
|
|
118
|
+
// Not a JACS artifact, parse as plain JSON
|
|
119
|
+
debugLog(this.proxyId, this.debug, 'INCOMING: not a JACS artifact, parsing as plain JSON');
|
|
445
120
|
messageForSDK = JSON.parse(incomingData);
|
|
446
121
|
}
|
|
447
122
|
}
|
|
448
123
|
else if (typeof incomingData === 'object' && incomingData !== null && 'jsonrpc' in incomingData) {
|
|
449
|
-
if (enableDiagnosticLogging)
|
|
450
|
-
jacslog(`${logPrefix}: Received object from transport, using as-is.`);
|
|
451
124
|
messageForSDK = incomingData;
|
|
452
125
|
}
|
|
453
126
|
else {
|
|
454
|
-
|
|
455
|
-
throw new Error("Invalid data type from transport");
|
|
127
|
+
throw new Error(`Unexpected incoming data type: ${typeof incomingData}`);
|
|
456
128
|
}
|
|
457
|
-
if (enableDiagnosticLogging)
|
|
458
|
-
jacslog(`${logPrefix}: Passing to MCP SDK: ${JSON.stringify(messageForSDK).substring(0, 100)}...`);
|
|
459
129
|
if (this.onmessage) {
|
|
460
130
|
this.onmessage(messageForSDK);
|
|
461
131
|
}
|
|
462
132
|
}
|
|
463
133
|
catch (error) {
|
|
464
|
-
console.error(
|
|
134
|
+
console.error(`[${this.proxyId}] Error processing incoming message:`, error);
|
|
465
135
|
if (this.onerror)
|
|
466
136
|
this.onerror(error);
|
|
467
137
|
}
|
|
468
138
|
}
|
|
469
139
|
/**
|
|
470
|
-
* Removes null and undefined values from JSON objects to prevent MCP schema
|
|
471
|
-
*
|
|
472
|
-
* WORKAROUND for MCP JSON Schema validation issues:
|
|
473
|
-
* - Addresses strict validators (like Anthropic's API) that reject schemas with null values
|
|
474
|
-
* - Handles edge cases where tools have null inputSchema causing client validation errors
|
|
475
|
-
* - Prevents "invalid_type: expected object, received undefined" errors in TypeScript SDK v1.9.0
|
|
476
|
-
* - Cleans up malformed schemas before transmission to avoid -32602 JSON-RPC errors
|
|
477
|
-
*
|
|
478
|
-
* Related issues:
|
|
479
|
-
* - https://github.com/modelcontextprotocol/typescript-sdk/issues/400 (null schema tools)
|
|
480
|
-
* - https://github.com/anthropics/claude-code/issues/586 (Anthropic strict Draft 2020-12)
|
|
481
|
-
* - https://github.com/agno-agi/agno/issues/2791 (missing type field)
|
|
140
|
+
* Removes null and undefined values from JSON objects to prevent MCP schema
|
|
141
|
+
* validation failures with strict validators.
|
|
482
142
|
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
143
|
+
* Workaround for:
|
|
144
|
+
* - https://github.com/modelcontextprotocol/typescript-sdk/issues/400
|
|
145
|
+
* - https://github.com/anthropics/claude-code/issues/586
|
|
146
|
+
* - https://github.com/agno-agi/agno/issues/2791
|
|
485
147
|
*/
|
|
486
148
|
removeNullValues(obj) {
|
|
487
149
|
if (obj === null || obj === undefined)
|
|
@@ -501,14 +163,379 @@ class JACSTransportProxy {
|
|
|
501
163
|
}
|
|
502
164
|
}
|
|
503
165
|
exports.JACSTransportProxy = JACSTransportProxy;
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
504
167
|
// Factory functions
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
/**
|
|
170
|
+
* Create a transport proxy from a pre-loaded JacsClient or JacsAgent.
|
|
171
|
+
*/
|
|
172
|
+
function createJACSTransportProxy(transport, clientOrAgent, role = "server") {
|
|
173
|
+
return new JACSTransportProxy(transport, clientOrAgent, role);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Create a transport proxy by loading a JACS agent from a config file.
|
|
177
|
+
* Awaits agent loading before returning, so the proxy is immediately usable.
|
|
178
|
+
*/
|
|
179
|
+
async function createJACSTransportProxyAsync(transport, configPath, role = "server") {
|
|
180
|
+
const agent = new index_js_1.JacsAgent();
|
|
181
|
+
await agent.load(configPath);
|
|
182
|
+
return new JACSTransportProxy(transport, agent, role);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Returns the full list of JACS MCP tool definitions.
|
|
186
|
+
*
|
|
187
|
+
* Use this with `server.setRequestHandler(ListToolsRequestSchema, ...)` to
|
|
188
|
+
* advertise JACS tools from a Node.js MCP server.
|
|
189
|
+
*/
|
|
190
|
+
function getJacsMcpToolDefinitions() {
|
|
191
|
+
return [
|
|
192
|
+
{
|
|
193
|
+
name: 'jacs_sign_document',
|
|
194
|
+
description: 'Sign arbitrary JSON data with JACS cryptographic provenance.',
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
properties: {
|
|
198
|
+
data: { type: 'string', description: 'JSON string of data to sign' },
|
|
199
|
+
},
|
|
200
|
+
required: ['data'],
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'jacs_verify_document',
|
|
205
|
+
description: 'Verify a JACS-signed document. Returns validity, signer, and errors.',
|
|
206
|
+
inputSchema: {
|
|
207
|
+
type: 'object',
|
|
208
|
+
properties: {
|
|
209
|
+
document: { type: 'string', description: 'The signed JSON document to verify' },
|
|
210
|
+
},
|
|
211
|
+
required: ['document'],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: 'jacs_verify_by_id',
|
|
216
|
+
description: 'Verify a document by its storage ID (uuid:version format).',
|
|
217
|
+
inputSchema: {
|
|
218
|
+
type: 'object',
|
|
219
|
+
properties: {
|
|
220
|
+
document_id: { type: 'string', description: 'Document ID in uuid:version format' },
|
|
221
|
+
},
|
|
222
|
+
required: ['document_id'],
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'jacs_create_agreement',
|
|
227
|
+
description: 'Create a multi-party agreement requiring signatures from specified agents.',
|
|
228
|
+
inputSchema: {
|
|
229
|
+
type: 'object',
|
|
230
|
+
properties: {
|
|
231
|
+
document: { type: 'string', description: 'JSON string of document to agree on' },
|
|
232
|
+
agent_ids: { type: 'array', items: { type: 'string' }, description: 'Agent IDs who must sign' },
|
|
233
|
+
question: { type: 'string', description: 'Question or prompt for signers' },
|
|
234
|
+
timeout: { type: 'string', description: 'ISO 8601 deadline' },
|
|
235
|
+
quorum: { type: 'number', description: 'Minimum signatures required (M-of-N)' },
|
|
236
|
+
},
|
|
237
|
+
required: ['document', 'agent_ids'],
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'jacs_sign_agreement',
|
|
242
|
+
description: 'Sign an existing multi-party agreement.',
|
|
243
|
+
inputSchema: {
|
|
244
|
+
type: 'object',
|
|
245
|
+
properties: {
|
|
246
|
+
document: { type: 'string', description: 'The agreement document to sign' },
|
|
247
|
+
},
|
|
248
|
+
required: ['document'],
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: 'jacs_check_agreement',
|
|
253
|
+
description: 'Check the status of a multi-party agreement.',
|
|
254
|
+
inputSchema: {
|
|
255
|
+
type: 'object',
|
|
256
|
+
properties: {
|
|
257
|
+
document: { type: 'string', description: 'The agreement document to check' },
|
|
258
|
+
},
|
|
259
|
+
required: ['document'],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
name: 'jacs_audit',
|
|
264
|
+
description: 'Run a JACS security audit on documents and keys.',
|
|
265
|
+
inputSchema: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
properties: {
|
|
268
|
+
recent_n: { type: 'number', description: 'Number of recent documents to audit' },
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: 'jacs_sign_file',
|
|
274
|
+
description: 'Sign a file with JACS. Optionally embed the file content.',
|
|
275
|
+
inputSchema: {
|
|
276
|
+
type: 'object',
|
|
277
|
+
properties: {
|
|
278
|
+
file_path: { type: 'string', description: 'Path to the file to sign' },
|
|
279
|
+
embed: { type: 'boolean', description: 'Embed file content in the document (default false)' },
|
|
280
|
+
},
|
|
281
|
+
required: ['file_path'],
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'jacs_verify_self',
|
|
286
|
+
description: "Verify this agent's own cryptographic integrity.",
|
|
287
|
+
inputSchema: { type: 'object', properties: {} },
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: 'jacs_agent_info',
|
|
291
|
+
description: 'Get the current agent ID, name, and diagnostics.',
|
|
292
|
+
inputSchema: { type: 'object', properties: {} },
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: 'fetch_agent_key',
|
|
296
|
+
description: "Fetch an agent's public key from HAI's key distribution service.",
|
|
297
|
+
inputSchema: {
|
|
298
|
+
type: 'object',
|
|
299
|
+
properties: {
|
|
300
|
+
agent_id: { type: 'string', description: 'Agent ID (UUID format)' },
|
|
301
|
+
version: { type: 'string', description: "Key version or 'latest'" },
|
|
302
|
+
},
|
|
303
|
+
required: ['agent_id'],
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: 'jacs_register',
|
|
308
|
+
description: 'Register this agent with HAI.ai for cross-organization key discovery.',
|
|
309
|
+
inputSchema: {
|
|
310
|
+
type: 'object',
|
|
311
|
+
properties: {
|
|
312
|
+
api_key: { type: 'string', description: 'HAI API key (optional, uses env if not set)' },
|
|
313
|
+
preview: { type: 'boolean', description: 'Preview mode (default true)' },
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: 'jacs_setup_instructions',
|
|
319
|
+
description: 'Get DNS and well-known setup instructions for a domain.',
|
|
320
|
+
inputSchema: {
|
|
321
|
+
type: 'object',
|
|
322
|
+
properties: {
|
|
323
|
+
domain: { type: 'string', description: 'Domain name for setup' },
|
|
324
|
+
},
|
|
325
|
+
required: ['domain'],
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: 'jacs_trust_agent',
|
|
330
|
+
description: 'Add an agent to the local trust store.',
|
|
331
|
+
inputSchema: {
|
|
332
|
+
type: 'object',
|
|
333
|
+
properties: {
|
|
334
|
+
agent_json: { type: 'string', description: 'Agent JSON document to trust' },
|
|
335
|
+
},
|
|
336
|
+
required: ['agent_json'],
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: 'jacs_list_trusted',
|
|
341
|
+
description: 'List all agent IDs in the local trust store.',
|
|
342
|
+
inputSchema: { type: 'object', properties: {} },
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
name: 'jacs_is_trusted',
|
|
346
|
+
description: 'Check if a specific agent is in the local trust store.',
|
|
347
|
+
inputSchema: {
|
|
348
|
+
type: 'object',
|
|
349
|
+
properties: {
|
|
350
|
+
agent_id: { type: 'string', description: 'Agent ID to check' },
|
|
351
|
+
},
|
|
352
|
+
required: ['agent_id'],
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: 'jacs_reencrypt_key',
|
|
357
|
+
description: 'Re-encrypt the private key with a new password.',
|
|
358
|
+
inputSchema: {
|
|
359
|
+
type: 'object',
|
|
360
|
+
properties: {
|
|
361
|
+
old_password: { type: 'string', description: 'Current password' },
|
|
362
|
+
new_password: { type: 'string', description: 'New password' },
|
|
363
|
+
},
|
|
364
|
+
required: ['old_password', 'new_password'],
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
];
|
|
508
368
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
369
|
+
/**
|
|
370
|
+
* Handle a JACS MCP tool call. Returns a JSON string result.
|
|
371
|
+
*
|
|
372
|
+
* Use this with `server.setRequestHandler(CallToolRequestSchema, ...)`.
|
|
373
|
+
*/
|
|
374
|
+
async function handleJacsMcpToolCall(client, toolName, args) {
|
|
375
|
+
const text = (s) => ({ content: [{ type: 'text', text: s }] });
|
|
376
|
+
try {
|
|
377
|
+
switch (toolName) {
|
|
378
|
+
case 'jacs_sign_document': {
|
|
379
|
+
const data = JSON.parse(args.data);
|
|
380
|
+
const signed = await client.signMessage(data);
|
|
381
|
+
return text(JSON.stringify({
|
|
382
|
+
success: true, documentId: signed.documentId,
|
|
383
|
+
agentId: signed.agentId, timestamp: signed.timestamp,
|
|
384
|
+
raw: signed.raw,
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
case 'jacs_verify_document': {
|
|
388
|
+
const result = await client.verify(args.document);
|
|
389
|
+
return text(JSON.stringify({
|
|
390
|
+
success: result.valid, valid: result.valid,
|
|
391
|
+
signerId: result.signerId, timestamp: result.timestamp,
|
|
392
|
+
data: result.data, errors: result.errors,
|
|
393
|
+
}));
|
|
394
|
+
}
|
|
395
|
+
case 'jacs_verify_by_id': {
|
|
396
|
+
const result = await client.verifyById(args.document_id);
|
|
397
|
+
return text(JSON.stringify({
|
|
398
|
+
success: result.valid, valid: result.valid,
|
|
399
|
+
errors: result.errors,
|
|
400
|
+
}));
|
|
401
|
+
}
|
|
402
|
+
case 'jacs_create_agreement': {
|
|
403
|
+
const doc = JSON.parse(args.document);
|
|
404
|
+
const opts = {};
|
|
405
|
+
if (args.question)
|
|
406
|
+
opts.question = args.question;
|
|
407
|
+
if (args.timeout)
|
|
408
|
+
opts.timeout = args.timeout;
|
|
409
|
+
if (args.quorum !== undefined)
|
|
410
|
+
opts.quorum = args.quorum;
|
|
411
|
+
const signed = await client.createAgreement(doc, args.agent_ids, opts);
|
|
412
|
+
return text(JSON.stringify({
|
|
413
|
+
success: true, documentId: signed.documentId,
|
|
414
|
+
agentId: signed.agentId, raw: signed.raw,
|
|
415
|
+
}));
|
|
416
|
+
}
|
|
417
|
+
case 'jacs_sign_agreement': {
|
|
418
|
+
const signed = await client.signAgreement(args.document);
|
|
419
|
+
return text(JSON.stringify({
|
|
420
|
+
success: true, documentId: signed.documentId,
|
|
421
|
+
agentId: signed.agentId, raw: signed.raw,
|
|
422
|
+
}));
|
|
423
|
+
}
|
|
424
|
+
case 'jacs_check_agreement': {
|
|
425
|
+
const status = await client.checkAgreement(args.document);
|
|
426
|
+
return text(JSON.stringify({ success: true, ...status }));
|
|
427
|
+
}
|
|
428
|
+
case 'jacs_audit': {
|
|
429
|
+
const result = await client.audit(args.recent_n !== undefined ? { recentN: args.recent_n } : undefined);
|
|
430
|
+
return text(JSON.stringify({ success: true, ...result }));
|
|
431
|
+
}
|
|
432
|
+
case 'jacs_sign_file': {
|
|
433
|
+
const signed = await client.signFile(args.file_path, args.embed || false);
|
|
434
|
+
return text(JSON.stringify({
|
|
435
|
+
success: true, documentId: signed.documentId,
|
|
436
|
+
agentId: signed.agentId, raw: signed.raw,
|
|
437
|
+
}));
|
|
438
|
+
}
|
|
439
|
+
case 'jacs_verify_self': {
|
|
440
|
+
const result = await client.verifySelf();
|
|
441
|
+
return text(JSON.stringify({
|
|
442
|
+
success: result.valid, valid: result.valid,
|
|
443
|
+
signerId: result.signerId, errors: result.errors,
|
|
444
|
+
}));
|
|
445
|
+
}
|
|
446
|
+
case 'jacs_agent_info': {
|
|
447
|
+
const nativeAgent = extractNativeAgent(client);
|
|
448
|
+
let diagnostics = {};
|
|
449
|
+
try {
|
|
450
|
+
diagnostics = JSON.parse(nativeAgent.diagnostics());
|
|
451
|
+
}
|
|
452
|
+
catch { /* ok */ }
|
|
453
|
+
return text(JSON.stringify({
|
|
454
|
+
agentId: client.agentId, name: client.name,
|
|
455
|
+
strict: client.strict, diagnostics,
|
|
456
|
+
}));
|
|
457
|
+
}
|
|
458
|
+
case 'fetch_agent_key': {
|
|
459
|
+
const keyInfo = (0, index_js_1.fetchRemoteKey)(args.agent_id, args.version || null);
|
|
460
|
+
return text(JSON.stringify({
|
|
461
|
+
success: true, agentId: keyInfo.agentId,
|
|
462
|
+
version: keyInfo.version, algorithm: keyInfo.algorithm,
|
|
463
|
+
publicKeyHash: keyInfo.publicKeyHash,
|
|
464
|
+
}));
|
|
465
|
+
}
|
|
466
|
+
case 'jacs_register': {
|
|
467
|
+
const nativeAgent = extractNativeAgent(client);
|
|
468
|
+
const result = await nativeAgent.registerWithHai(args.api_key || null, null, args.preview !== false);
|
|
469
|
+
return text(result);
|
|
470
|
+
}
|
|
471
|
+
case 'jacs_setup_instructions': {
|
|
472
|
+
const nativeAgent = extractNativeAgent(client);
|
|
473
|
+
const result = await nativeAgent.getSetupInstructions(args.domain, null);
|
|
474
|
+
return text(result);
|
|
475
|
+
}
|
|
476
|
+
case 'jacs_trust_agent': {
|
|
477
|
+
const result = client.trustAgent(args.agent_json);
|
|
478
|
+
return text(JSON.stringify({ success: true, result }));
|
|
479
|
+
}
|
|
480
|
+
case 'jacs_list_trusted': {
|
|
481
|
+
const agents = client.listTrustedAgents();
|
|
482
|
+
return text(JSON.stringify({ success: true, trustedAgents: agents }));
|
|
483
|
+
}
|
|
484
|
+
case 'jacs_is_trusted': {
|
|
485
|
+
const trusted = client.isTrusted(args.agent_id);
|
|
486
|
+
return text(JSON.stringify({ agentId: args.agent_id, trusted }));
|
|
487
|
+
}
|
|
488
|
+
case 'jacs_reencrypt_key': {
|
|
489
|
+
const nativeAgent = extractNativeAgent(client);
|
|
490
|
+
await nativeAgent.reencryptKey(args.old_password, args.new_password);
|
|
491
|
+
return text(JSON.stringify({ success: true, message: 'Key re-encrypted' }));
|
|
492
|
+
}
|
|
493
|
+
default:
|
|
494
|
+
return text(JSON.stringify({ error: `Unknown tool: ${toolName}` }));
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
catch (err) {
|
|
498
|
+
return text(JSON.stringify({ success: false, error: String(err) }));
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Register all JACS tools on an MCP Server instance.
|
|
503
|
+
*
|
|
504
|
+
* Call this once during server setup to add JACS signing, verification,
|
|
505
|
+
* agreements, trust, audit, and HAI integration tools.
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* ```typescript
|
|
509
|
+
* import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
510
|
+
* import { JacsClient } from '@hai.ai/jacs/client';
|
|
511
|
+
* import { registerJacsTools } from '@hai.ai/jacs/mcp';
|
|
512
|
+
*
|
|
513
|
+
* const server = new Server(
|
|
514
|
+
* { name: 'my-server', version: '1.0.0' },
|
|
515
|
+
* { capabilities: { tools: {} } },
|
|
516
|
+
* );
|
|
517
|
+
* const client = await JacsClient.quickstart();
|
|
518
|
+
* registerJacsTools(server, client);
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
function registerJacsTools(server, client) {
|
|
522
|
+
// Lazy import MCP SDK schemas — only needed if registering tools
|
|
523
|
+
let ListToolsRequestSchema;
|
|
524
|
+
let CallToolRequestSchema;
|
|
525
|
+
try {
|
|
526
|
+
const types = require('@modelcontextprotocol/sdk/types.js');
|
|
527
|
+
ListToolsRequestSchema = types.ListToolsRequestSchema;
|
|
528
|
+
CallToolRequestSchema = types.CallToolRequestSchema;
|
|
529
|
+
}
|
|
530
|
+
catch {
|
|
531
|
+
throw new Error('@modelcontextprotocol/sdk is required for registerJacsTools. ' +
|
|
532
|
+
'Install it with: npm install @modelcontextprotocol/sdk');
|
|
533
|
+
}
|
|
534
|
+
const tools = getJacsMcpToolDefinitions();
|
|
535
|
+
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools }));
|
|
536
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
537
|
+
const { name, arguments: args } = request.params;
|
|
538
|
+
return handleJacsMcpToolCall(client, name, args || {});
|
|
539
|
+
});
|
|
513
540
|
}
|
|
514
541
|
//# sourceMappingURL=mcp.js.map
|