@easynet-run/node 0.27.14 → 0.36.9

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 (35) hide show
  1. package/README.md +12 -0
  2. package/native/dendrite-bridge-manifest.json +5 -4
  3. package/native/dendrite-bridge.json +1 -1
  4. package/native/include/axon_dendrite_bridge.h +18 -4
  5. package/native/libaxon_dendrite_bridge.so +0 -0
  6. package/package.json +7 -5
  7. package/runtime/easynet-runtime-rs-0.36.9-x86_64-unknown-linux-gnu.tar.gz +0 -0
  8. package/runtime/runtime-bridge-manifest.json +4 -4
  9. package/runtime/runtime-bridge.json +3 -3
  10. package/src/ability_lifecycle.d.ts +1 -0
  11. package/src/ability_lifecycle.js +12 -6
  12. package/src/capability_request.js +3 -1
  13. package/src/dendrite_bridge/bridge.d.ts +9 -2
  14. package/src/dendrite_bridge/bridge.js +54 -5
  15. package/src/dendrite_bridge/ffi.d.ts +3 -0
  16. package/src/dendrite_bridge/ffi.js +2 -0
  17. package/src/dendrite_bridge/types.d.ts +4 -0
  18. package/src/errors.js +9 -3
  19. package/src/index.d.ts +2 -2
  20. package/src/mcp/server.d.ts +24 -2
  21. package/src/mcp/server.js +218 -18
  22. package/src/mcp/server.test.js +62 -0
  23. package/src/presets/remote_control/config.d.ts +3 -0
  24. package/src/presets/remote_control/config.js +17 -3
  25. package/src/presets/remote_control/descriptor.js +28 -11
  26. package/src/presets/remote_control/handlers.d.ts +2 -0
  27. package/src/presets/remote_control/handlers.js +22 -17
  28. package/src/presets/remote_control/kit.d.ts +4 -2
  29. package/src/presets/remote_control/kit.js +70 -1
  30. package/src/presets/remote_control/kit.test.js +449 -0
  31. package/src/presets/remote_control/orchestrator.d.ts +5 -0
  32. package/src/presets/remote_control/orchestrator.js +9 -1
  33. package/src/presets/remote_control/specs.js +80 -61
  34. package/src/receipt.js +6 -3
  35. package/runtime/easynet-runtime-rs-0.27.14-x86_64-unknown-linux-gnu.tar.gz +0 -0
package/README.md CHANGED
@@ -33,6 +33,18 @@ The SDK ships a native Dendrite bridge binary (loaded via `koffi`) that provides
33
33
  npm install @easynet-run/node
34
34
  ```
35
35
 
36
+ ## Development
37
+
38
+ Runtime `.js` files and package-facing `.d.ts` files under `src/` are generated from the TypeScript sources with `tsc`.
39
+ Edit `src/**/*.ts`, then run:
40
+
41
+ ```bash
42
+ npm run build
43
+ npm run generated:check
44
+ ```
45
+
46
+ Do not hand-edit generated `src/**/*.js` or `src/**/*.d.ts` files.
47
+
36
48
  ## Quick Start
37
49
 
38
50
  ### Expose an ability
@@ -1,15 +1,15 @@
1
1
  {
2
- "dendrite_bridge_version": "0.27.14",
2
+ "dendrite_bridge_version": "0.36.9",
3
3
  "target": "x86_64-unknown-linux-gnu",
4
4
  "os": "linux",
5
5
  "arch": "x86_64",
6
6
  "library": {
7
7
  "path": "native/libaxon_dendrite_bridge.so",
8
- "sha256": "48d24b376d4c233a4d836f01ad6ede4dd38677a63fd6b687eeabc6a387ee910b"
8
+ "sha256": "e802bd6d15684b1b3f4c7fd2fc7159b591aaa93c58de78c369cac5d0315f7688"
9
9
  },
10
10
  "header": {
11
11
  "path": "native/include/axon_dendrite_bridge.h",
12
- "sha256": "b54e9ce0898c95276e5a2f5752cf7c09255ab04d0ff6c50b1da86f194faf6b75"
12
+ "sha256": "bb6fdce765a6b1c2fb91a1cee037d00f7ec4297c9a1fd03b39fb829edf2d0fc4"
13
13
  },
14
14
  "protocol_coverage": {
15
15
  "rpc_shapes": ["unary", "server_stream", "client_stream", "bidi_stream"],
@@ -29,10 +29,11 @@
29
29
  "axon_dendrite_deploy_mcp_list_dir_json",
30
30
  "axon_dendrite_list_mcp_tools_json",
31
31
  "axon_dendrite_call_mcp_tool_json",
32
+ "axon_dendrite_call_mcp_tool_stream_open_json",
32
33
  "axon_dendrite_uninstall_capability_json",
33
34
  "axon_dendrite_update_mcp_list_dir_json",
34
35
  "axon_dendrite_protocol_coverage_json",
35
36
  "axon_dendrite_string_free"
36
37
  ],
37
- "generated_at_utc": "2026-03-19T17:45:33Z"
38
+ "generated_at_utc": "2026-03-27T17:24:23Z"
38
39
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "language": "node",
3
- "dendrite_bridge_version": "0.27.14",
3
+ "dendrite_bridge_version": "0.36.9",
4
4
  "target": "x86_64-unknown-linux-gnu",
5
5
  "dendrite_manifest_path": "native/dendrite-bridge-manifest.json",
6
6
  "dendrite_library_path": "native/libaxon_dendrite_bridge.so",
@@ -251,6 +251,20 @@ char *axon_dendrite_list_mcp_tools_json(uint64_t handle,
251
251
  char *axon_dendrite_call_mcp_tool_json(uint64_t handle,
252
252
  const char *request_json);
253
253
 
254
+ // Control helper: open streaming MCP tool call. Returns {"stream_handle": <u64>}.
255
+ // Chunks are read via axon_dendrite_stream_next_json.
256
+ // IMPORTANT: Caller MUST call axon_dendrite_stream_close_json on the returned
257
+ // stream_handle to release the background task and channel. Failure to close
258
+ // will leak resources until the session itself is closed.
259
+ // timeout_ms is split as follows:
260
+ // - setup: min(timeout_ms, 30000) — capped at 30s for stream open
261
+ // - per-chunk: timeout_ms — each stream_next call waits up to this value
262
+ // Per-session limit: max 16 concurrent MCP tool streams.
263
+ // input JSON:
264
+ // {"tenant_id":"...","tool_name":"...","target_node_id":"...","arguments_json":{},"timeout_ms":60000}
265
+ char *axon_dendrite_call_mcp_tool_stream_open_json(uint64_t handle,
266
+ const char *request_json);
267
+
254
268
  // Control helper: deactivate (optional) and uninstall installed capability.
255
269
  // input JSON:
256
270
  // {
@@ -392,8 +406,8 @@ char *axon_dendrite_voice_watch_transport_events_json(uint64_t handle,
392
406
  // "path":"/axon.v1.Invocation/WatchInvocation",
393
407
  // "request_base64":"...",
394
408
  // "metadata":{"x-foo":"bar"},
395
- // "timeout_ms":10000,
396
- // "chunk_timeout_ms":30000,
409
+ // "timeout_ms":10000, // setup/open timeout
410
+ // "chunk_timeout_ms":30000, // per-chunk wait timeout
397
411
  // "chunk_buffer_size":64
398
412
  // }
399
413
  // output JSON:
@@ -426,8 +440,8 @@ char *axon_dendrite_stream_close_json(uint64_t stream_handle);
426
440
  // "request_base64":"...",
427
441
  // "request_chunks_base64":["..."],
428
442
  // "metadata":{"x-foo":"bar"},
429
- // "timeout_ms":10000,
430
- // "chunk_timeout_ms":30000,
443
+ // "timeout_ms":10000, // setup/open timeout
444
+ // "chunk_timeout_ms":30000, // per-chunk wait timeout
431
445
  // "chunk_buffer_size":64,
432
446
  // "request_buffer_size":64
433
447
  // }
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easynet-run/node",
3
- "version": "0.27.14",
3
+ "version": "0.36.9",
4
4
  "description": "EasyNet Axon Node.js SDK (sidecar-first with Dendrite bridge payload).",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://easynet.run",
@@ -15,12 +15,14 @@
15
15
  },
16
16
  "type": "module",
17
17
  "scripts": {
18
- "build": "tsc",
19
- "check": "tsc --noEmit",
18
+ "build": "tsc -p tsconfig.json",
19
+ "check": "tsc -p tsconfig.json --noEmit",
20
20
  "types:check": "tsc -p tsconfig.types.json --noEmit",
21
21
  "types:build": "tsc -p tsconfig.types.json",
22
- "types:verify": "npm run types:build && test -f dist/types/index.d.ts && test -f dist/types/dendrite_bridge.d.ts",
23
- "types": "npm run types:check && npm run types:verify"
22
+ "types:verify": "npm run types:build && node ./scripts/verify-dist-types.mjs",
23
+ "generated:check": "npm run build && node ./scripts/verify-generated.mjs",
24
+ "types": "npm run check && npm run types:verify",
25
+ "verify": "npm run types && npm run generated:check"
24
26
  },
25
27
  "main": "src/index.js",
26
28
  "types": "src/index.d.ts",
@@ -1,11 +1,11 @@
1
1
  {
2
- "runtime_version": "0.27.14",
2
+ "runtime_version": "0.36.9",
3
3
  "target": "x86_64-unknown-linux-gnu",
4
4
  "os": "linux",
5
5
  "arch": "x86_64",
6
6
  "artifact": {
7
- "path": "runtime/easynet-runtime-rs-0.27.14-x86_64-unknown-linux-gnu.tar.gz",
8
- "sha256": "382b54020ba9caa97e175fdec8e99a45f52209e40d36214f33efc741482e2072",
7
+ "path": "runtime/easynet-runtime-rs-0.36.9-x86_64-unknown-linux-gnu.tar.gz",
8
+ "sha256": "6d4c56411bae31ed11ec94face402c170d7a44d7f0686bac601440d25a9e98b2",
9
9
  "binary_name": "axon-runtime"
10
10
  },
11
11
  "language_bridge_descriptors": [
@@ -16,5 +16,5 @@
16
16
  "java/runtime-bridge.json",
17
17
  "swift/runtime-bridge.json"
18
18
  ],
19
- "generated_at_utc": "2026-03-19T17:45:33Z"
19
+ "generated_at_utc": "2026-03-27T17:24:23Z"
20
20
  }
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "language": "node",
3
- "runtime_version": "0.27.14",
3
+ "runtime_version": "0.36.9",
4
4
  "target": "x86_64-unknown-linux-gnu",
5
- "runtime_artifact_path": "runtime/easynet-runtime-rs-0.27.14-x86_64-unknown-linux-gnu.tar.gz",
5
+ "runtime_artifact_path": "runtime/easynet-runtime-rs-0.36.9-x86_64-unknown-linux-gnu.tar.gz",
6
6
  "binary_relative_path": "axon-runtime",
7
- "recommended_extract": "tar -xzf runtime/easynet-runtime-rs-0.27.14-x86_64-unknown-linux-gnu.tar.gz -C <runtime_dir>",
7
+ "recommended_extract": "tar -xzf runtime/easynet-runtime-rs-0.36.9-x86_64-unknown-linux-gnu.tar.gz -C <runtime_dir>",
8
8
  "recommended_launch": "AXON_ENFORCE_MTLS=false <runtime_dir>/axon-runtime"
9
9
  }
@@ -3,6 +3,7 @@ import { type DeployTrace } from "./receipt.js";
3
3
  import type { ToolSpec } from "./tool_adapter.js";
4
4
  export interface AbilityDescriptor {
5
5
  name: string;
6
+ toolName: string;
6
7
  description: string;
7
8
  commandTemplate: string;
8
9
  inputSchema: Record<string, unknown>;
@@ -1,5 +1,11 @@
1
- // sdk/node/src/ability_lifecycle.ts Ability lifecycle API: create, deploy, and export as Agent Skills.
1
+ // EasyNet Axon for AgentNet
2
+ // =========================
2
3
  //
4
+ // File: sdk/node/src/ability_lifecycle.ts
5
+ // Description: Ability lifecycle API: create, deploy, invoke, and export as Agent Skills.
6
+ //
7
+ // Author: Silan.Hu
8
+ // Email: silan.hu@u.nus.edu
3
9
  // Copyright (c) 2026-2027 easynet. All rights reserved.
4
10
  import { spawn } from "node:child_process";
5
11
  import { webcrypto } from "node:crypto";
@@ -219,6 +225,7 @@ export function createAbility(opts) {
219
225
  const token = normalizeAbilityName(opts.name);
220
226
  return {
221
227
  name: opts.name,
228
+ toolName: token,
222
229
  description: opts.description,
223
230
  commandTemplate: opts.commandTemplate,
224
231
  inputSchema: opts.inputSchema ?? { type: "object", properties: {} },
@@ -232,9 +239,8 @@ export function createAbility(opts) {
232
239
  // toToolSpec
233
240
  // ---------------------------------------------------------------------------
234
241
  export function toToolSpec(descriptor) {
235
- const token = normalizeAbilityName(descriptor.name);
236
242
  return {
237
- name: token,
243
+ name: descriptor.toolName,
238
244
  description: descriptor.description,
239
245
  resourceUri: descriptor.resourceUri,
240
246
  parameters: descriptor.inputSchema,
@@ -244,7 +250,7 @@ export function toToolSpec(descriptor) {
244
250
  // exportAbility
245
251
  // ---------------------------------------------------------------------------
246
252
  export function exportAbility(descriptor, target = "agent_skills", axonEndpoint) {
247
- const token = normalizeAbilityName(descriptor.name);
253
+ const token = descriptor.toolName;
248
254
  const endpoint = axonEndpoint ?? `http://127.0.0.1:${DEFAULT_AXON_PORT}`;
249
255
  const invokeScript = generateInvokeScript(descriptor.resourceUri, endpoint);
250
256
  const abilityMd = generateAbilityMd(descriptor, target, token);
@@ -270,7 +276,7 @@ export function exportAbility(descriptor, target = "agent_skills", axonEndpoint)
270
276
  * ```
271
277
  */
272
278
  export async function deployToNode(bridge, tenant, nodeId, descriptor, signature) {
273
- const token = normalizeAbilityName(descriptor.name);
279
+ const token = descriptor.toolName;
274
280
  const abilityId = token;
275
281
  const pkg = buildDeployPackage({
276
282
  ability_name: descriptor.name,
@@ -308,7 +314,7 @@ export async function listAbilities(bridge, tenant, nodeId) {
308
314
  // invokeAbility
309
315
  // ---------------------------------------------------------------------------
310
316
  export async function invokeAbility(bridge, tenant, nodeId, toolName, args = {}) {
311
- return await bridge.callMcpToolWithArgs(tenant, toolName, nodeId, args);
317
+ return await bridge.callMcpToolWithArgs(tenant, normalizeAbilityName(toolName), nodeId, args);
312
318
  }
313
319
  // ---------------------------------------------------------------------------
314
320
  // uninstallAbility
@@ -23,8 +23,10 @@
23
23
  // Author: Silan.Hu
24
24
  // Email: silan.hu@u.nus.edu
25
25
  // Copyright (c) 2026-2027 easynet. All rights reserved.
26
+ // DendriteError re-exported from errors.ts to break the circular dependency:
27
+ // dendrite_bridge imports validate/build helpers from this module, so we
28
+ // cannot import from dendrite_bridge here. errors.ts has no such dependency.
26
29
  import { AxonConfigError } from "./errors.js";
27
- // DendriteError kept for backward compatibility; validation now uses AxonConfigError.
28
30
  export class DendriteError extends Error {
29
31
  constructor(message) {
30
32
  super(message);
@@ -1,4 +1,4 @@
1
- import type { DendriteBridgeOptions, DendriteUnaryCallOptions, DendriteAbilityCallOptions, DendriteStreamCallOptions, DendriteClientStreamCallOptions, DendriteBidiStreamCallOptions, DendriteListNodesOptions, DendriteRegisterNodeOptions, DendritePublishCapabilityOptions, DendriteInstallCapabilityOptions, DendriteListA2aAgentsOptions, DendriteSendA2aTaskOptions, DendriteDeployMcpListDirOptions, DendriteListMcpToolsOptions, DendriteCallMcpToolOptions, DendriteUninstallCapabilityOptions, DendriteUpdateMcpListDirOptions, DendriteVoiceCreateCallOptions, DendriteVoiceJoinCallOptions, DendriteVoiceUpdateMediaPathOptions, DendriteVoiceMetricsOptions, DendriteVoiceEndCallOptions, DendriteVoiceWatchOptions, DendriteVoiceCreateTransportSessionOptions, DendriteVoiceDescriptionOptions, DendriteVoiceCandidateOptions, DendriteVoiceEndTransportSessionOptions, DendriteProtocolInvokeOptions } from "./types.js";
1
+ import type { DendriteBridgeOptions, DendriteUnaryCallOptions, DendriteAbilityCallOptions, DendriteStreamCallOptions, DendriteClientStreamCallOptions, DendriteBidiStreamCallOptions, DendriteListNodesOptions, DendriteRegisterNodeOptions, DendritePublishCapabilityOptions, DendriteInstallCapabilityOptions, DendriteListA2aAgentsOptions, DendriteSendA2aTaskOptions, DendriteDeployMcpListDirOptions, DendriteListMcpToolsOptions, DendriteCallMcpToolOptions, DendriteCallMcpToolStreamOptions, DendriteUninstallCapabilityOptions, DendriteUpdateMcpListDirOptions, DendriteVoiceCreateCallOptions, DendriteVoiceJoinCallOptions, DendriteVoiceUpdateMediaPathOptions, DendriteVoiceMetricsOptions, DendriteVoiceEndCallOptions, DendriteVoiceWatchOptions, DendriteVoiceCreateTransportSessionOptions, DendriteVoiceDescriptionOptions, DendriteVoiceCandidateOptions, DendriteVoiceEndTransportSessionOptions, DendriteProtocolInvokeOptions } from "./types.js";
2
2
  import type { DendriteBridgeLib } from "./ffi.js";
3
3
  export declare class DendriteBridge {
4
4
  private readonly lib;
@@ -33,6 +33,12 @@ export declare class DendriteBridge {
33
33
  deployMcpListDir(tenantId: string, nodeId: string, options?: DendriteDeployMcpListDirOptions): Record<string, unknown>;
34
34
  listMcpTools(tenantId: string, options?: DendriteListMcpToolsOptions): Record<string, unknown>[];
35
35
  callMcpTool(tenantId: string, toolName: string, options?: DendriteCallMcpToolOptions): Record<string, unknown>;
36
+ /**
37
+ * Open a streaming MCP tool call that returns incremental response chunks.
38
+ * Requires native library support for streaming symbols.
39
+ * Uses DEFAULT_MCP_TOOL_STREAM_TIMEOUT_MS (60s) per chunk if no timeout specified.
40
+ */
41
+ callMcpToolStream(tenantId: string, toolName: string, options?: DendriteCallMcpToolStreamOptions): DendriteServerStream;
36
42
  uninstallCapability(tenantId: string, nodeId: string, installId: string, options?: DendriteUninstallCapabilityOptions): Record<string, unknown>;
37
43
  updateMcpListDir(tenantId: string, nodeId: string, options?: DendriteUpdateMcpListDirOptions): Record<string, unknown>;
38
44
  createVoiceCall(tenantId: string, options?: DendriteVoiceCreateCallOptions): Record<string, unknown>;
@@ -69,9 +75,10 @@ export declare class DendriteBridge {
69
75
  export declare class DendriteServerStream implements AsyncIterable<Uint8Array> {
70
76
  private readonly lib;
71
77
  private readonly streamHandle;
78
+ private readonly defaultChunkTimeoutMs;
72
79
  private done;
73
80
  private nativeClosed;
74
- constructor(lib: DendriteBridgeLib, streamHandle: bigint);
81
+ constructor(lib: DendriteBridgeLib, streamHandle: bigint, defaultChunkTimeoutMs?: number);
75
82
  next(timeoutMs?: number): {
76
83
  chunk: Uint8Array | null;
77
84
  done: boolean;
@@ -23,7 +23,7 @@
23
23
  // Author: Silan.Hu
24
24
  // Email: silan.hu@u.nus.edu
25
25
  // Copyright (c) 2026-2027 easynet. All rights reserved.
26
- import { callJson, loadDendriteLib, resolveLibraryPath, requireBridgeFn, DendriteError, DEFAULT_TIMEOUT_MS, DEFAULT_CONNECT_TIMEOUT_MS, } from "./ffi.js";
26
+ import { callJson, loadDendriteLib, resolveLibraryPath, requireBridgeFn, DendriteError, DEFAULT_TIMEOUT_MS, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_MCP_TOOL_STREAM_TIMEOUT_MS, } from "./ffi.js";
27
27
  const STREAMING_UNSUPPORTED = "incremental streaming not supported by loaded native library";
28
28
  import { validatePublishCapabilityRequest, buildPublishCapabilityPayload, validateInstallCapabilityRequest, buildInstallCapabilityPayload, validateDeployMcpListDirRequest, buildDeployMcpListDirPayload, validateUpdateMcpListDirRequest, buildUpdateMcpListDirPayload, buildUninstallCapabilityPayload, } from "../capability_request.js";
29
29
  export class DendriteBridge {
@@ -304,6 +304,37 @@ export class DendriteBridge {
304
304
  });
305
305
  return callJson(this.lib.axon_dendrite_call_mcp_tool_json, this.handle, req);
306
306
  }
307
+ /**
308
+ * Open a streaming MCP tool call that returns incremental response chunks.
309
+ * Requires native library support for streaming symbols.
310
+ * Uses DEFAULT_MCP_TOOL_STREAM_TIMEOUT_MS (60s) per chunk if no timeout specified.
311
+ */
312
+ callMcpToolStream(tenantId, toolName, options = {}) {
313
+ if (this.handle <= 0n)
314
+ throw new DendriteError("dendrite bridge already closed");
315
+ const streamOpen = requireBridgeFn(this.lib.axon_dendrite_call_mcp_tool_stream_open_json, STREAMING_UNSUPPORTED);
316
+ requireBridgeFn(this.lib.axon_dendrite_stream_next_json, STREAMING_UNSUPPORTED);
317
+ const timeoutMs = typeof options.timeoutMs === "number" && options.timeoutMs > 0
318
+ ? options.timeoutMs
319
+ : DEFAULT_MCP_TOOL_STREAM_TIMEOUT_MS;
320
+ const req = JSON.stringify({
321
+ tenant_id: tenantId,
322
+ tool_name: toolName,
323
+ target_node_id: options.targetNodeId ?? "",
324
+ arguments_json: options.argumentsJson ?? {},
325
+ arguments_base64: options.argumentsBase64,
326
+ timeout_ms: timeoutMs,
327
+ });
328
+ const resp = callJson(streamOpen, this.handle, req);
329
+ if (resp.stream_handle == null || resp.stream_handle === "") {
330
+ throw new DendriteError(`failed to open mcp tool stream: missing stream_handle in ${JSON.stringify(resp)}`);
331
+ }
332
+ const streamHandle = BigInt(resp.stream_handle);
333
+ if (streamHandle <= 0n) {
334
+ throw new DendriteError(`failed to open mcp tool stream: ${JSON.stringify(resp)}`);
335
+ }
336
+ return new DendriteServerStream(this.lib, streamHandle, timeoutMs);
337
+ }
307
338
  uninstallCapability(tenantId, nodeId, installId, options = {}) {
308
339
  if (this.handle <= 0n)
309
340
  throw new DendriteError("dendrite bridge already closed");
@@ -596,16 +627,20 @@ export class DendriteBridge {
596
627
  return new DendriteBidiStream(this.lib, streamHandle);
597
628
  }
598
629
  }
630
+ /** Maximum consecutive timeout results before aborting iteration (matches Python/Go/Java/Swift). */
631
+ const MAX_CONSECUTIVE_TIMEOUT_RETRIES = 3;
599
632
  export class DendriteServerStream {
600
633
  lib;
601
634
  streamHandle;
635
+ defaultChunkTimeoutMs;
602
636
  done = false;
603
637
  nativeClosed = false;
604
- constructor(lib, streamHandle) {
638
+ constructor(lib, streamHandle, defaultChunkTimeoutMs = DEFAULT_TIMEOUT_MS) {
605
639
  this.lib = lib;
606
640
  this.streamHandle = streamHandle;
641
+ this.defaultChunkTimeoutMs = defaultChunkTimeoutMs > 0 ? defaultChunkTimeoutMs : DEFAULT_TIMEOUT_MS;
607
642
  }
608
- next(timeoutMs = DEFAULT_TIMEOUT_MS) {
643
+ next(timeoutMs = this.defaultChunkTimeoutMs) {
609
644
  if (this.done)
610
645
  return { chunk: null, done: true };
611
646
  const streamNext = requireBridgeFn(this.lib.axon_dendrite_stream_next_json, STREAMING_UNSUPPORTED);
@@ -630,13 +665,20 @@ export class DendriteServerStream {
630
665
  }
631
666
  }
632
667
  async *[Symbol.asyncIterator]() {
668
+ let consecutiveTimeouts = 0;
633
669
  try {
634
670
  while (true) {
635
671
  const { chunk, done, timeout } = this.next();
636
672
  if (done)
637
673
  break;
638
- if (timeout)
674
+ if (timeout) {
675
+ consecutiveTimeouts++;
676
+ if (consecutiveTimeouts >= MAX_CONSECUTIVE_TIMEOUT_RETRIES) {
677
+ throw new DendriteError(`stream timed out after ${consecutiveTimeouts} consecutive retries`);
678
+ }
639
679
  continue;
680
+ }
681
+ consecutiveTimeouts = 0;
640
682
  if (chunk)
641
683
  yield chunk;
642
684
  }
@@ -694,13 +736,20 @@ export class DendriteBidiStream {
694
736
  }
695
737
  }
696
738
  async *[Symbol.asyncIterator]() {
739
+ let consecutiveTimeouts = 0;
697
740
  try {
698
741
  while (true) {
699
742
  const { chunk, done, timeout } = this.recv();
700
743
  if (done)
701
744
  break;
702
- if (timeout)
745
+ if (timeout) {
746
+ consecutiveTimeouts++;
747
+ if (consecutiveTimeouts >= MAX_CONSECUTIVE_TIMEOUT_RETRIES) {
748
+ throw new DendriteError(`stream timed out after ${consecutiveTimeouts} consecutive retries`);
749
+ }
703
750
  continue;
751
+ }
752
+ consecutiveTimeouts = 0;
704
753
  if (chunk)
705
754
  yield chunk;
706
755
  }
@@ -1,6 +1,7 @@
1
1
  import { DendriteError } from "../capability_request.js";
2
2
  export declare const DEFAULT_TIMEOUT_MS = 30000;
3
3
  export declare const DEFAULT_CONNECT_TIMEOUT_MS = 5000;
4
+ export declare const DEFAULT_MCP_TOOL_STREAM_TIMEOUT_MS = 60000;
4
5
  interface DendriteBridgeJson {
5
6
  ok: boolean;
6
7
  [key: string]: unknown;
@@ -30,6 +31,8 @@ export type DendriteBridgeLib = {
30
31
  axon_dendrite_deploy_mcp_list_dir_json: DendriteJsonFn<[bigint, string]>;
31
32
  axon_dendrite_list_mcp_tools_json: DendriteJsonFn<[bigint, string]>;
32
33
  axon_dendrite_call_mcp_tool_json: DendriteJsonFn<[bigint, string]>;
34
+ /** FFI binding to open a streaming MCP tool call. Returns a stream_handle. Optional — unavailable in older native libraries. */
35
+ axon_dendrite_call_mcp_tool_stream_open_json?: DendriteJsonFn<[bigint, string]>;
33
36
  axon_dendrite_uninstall_capability_json: DendriteJsonFn<[bigint, string]>;
34
37
  axon_dendrite_update_mcp_list_dir_json: DendriteJsonFn<[bigint, string]>;
35
38
  axon_dendrite_voice_create_call_json: DendriteJsonFn<[bigint, string]>;
@@ -32,6 +32,7 @@ const require = createRequire(import.meta.url);
32
32
  const koffi = require("koffi");
33
33
  export const DEFAULT_TIMEOUT_MS = 30000;
34
34
  export const DEFAULT_CONNECT_TIMEOUT_MS = 5000;
35
+ export const DEFAULT_MCP_TOOL_STREAM_TIMEOUT_MS = 60_000;
35
36
  export { DendriteError };
36
37
  export function resolveLibraryPath(explicitPath) {
37
38
  if (explicitPath)
@@ -88,6 +89,7 @@ export function loadDendriteLib(libraryPath) {
88
89
  axon_dendrite_deploy_mcp_list_dir_json: lib.func("axon_dendrite_deploy_mcp_list_dir_json", bridgeString, ["uint64_t", "str"]),
89
90
  axon_dendrite_list_mcp_tools_json: lib.func("axon_dendrite_list_mcp_tools_json", bridgeString, ["uint64_t", "str"]),
90
91
  axon_dendrite_call_mcp_tool_json: lib.func("axon_dendrite_call_mcp_tool_json", bridgeString, ["uint64_t", "str"]),
92
+ axon_dendrite_call_mcp_tool_stream_open_json: optionalJsonFn("axon_dendrite_call_mcp_tool_stream_open_json", ["uint64_t", "str"]),
91
93
  axon_dendrite_uninstall_capability_json: lib.func("axon_dendrite_uninstall_capability_json", bridgeString, ["uint64_t", "str"]),
92
94
  axon_dendrite_update_mcp_list_dir_json: lib.func("axon_dendrite_update_mcp_list_dir_json", bridgeString, ["uint64_t", "str"]),
93
95
  axon_dendrite_voice_create_call_json: lib.func("axon_dendrite_voice_create_call_json", bridgeString, ["uint64_t", "str"]),
@@ -84,6 +84,10 @@ export interface DendriteCallMcpToolOptions {
84
84
  argumentsJson?: unknown;
85
85
  argumentsBase64?: string;
86
86
  }
87
+ export interface DendriteCallMcpToolStreamOptions extends DendriteCallMcpToolOptions {
88
+ /** Per-chunk timeout in milliseconds for streaming reads. Default: 60,000 ms. */
89
+ timeoutMs?: number;
90
+ }
87
91
  export interface DendriteUninstallCapabilityOptions {
88
92
  deactivateFirst?: boolean;
89
93
  deactivateReason?: string;
package/src/errors.js CHANGED
@@ -1,4 +1,12 @@
1
- // sdk/node/src/errors.ts Canonical error taxonomy for the Axon SDK.
1
+ // EasyNet Axon for AgentNet
2
+ // =========================
3
+ //
4
+ // File: sdk/node/src/errors.ts
5
+ // Description: Canonical error taxonomy for the Axon SDK.
6
+ //
7
+ // Author: Silan.Hu
8
+ // Email: silan.hu@u.nus.edu
9
+ // Copyright (c) 2026-2027 easynet. All rights reserved.
2
10
  //
3
11
  // Terminology:
4
12
  // Ability — network-addressable governed execution contract
@@ -7,8 +15,6 @@
7
15
  //
8
16
  // Each error class exposes a `code` property for cross-SDK mapping.
9
17
  // See the Rust reference: sdk/rust/src/error.rs
10
- //
11
- // Copyright (c) 2026-2027 easynet. All rights reserved.
12
18
  /** Base error for all Axon SDK errors. */
13
19
  export class AxonError extends Error {
14
20
  /** Canonical error code for cross-SDK mapping. */
package/src/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { validateUninstallCapabilityRequest, buildUninstallCapabilityPayload, } from "./capability_request.js";
2
- export type { McpToolProvider, McpToolResult, } from "./mcp/server.js";
2
+ export type { McpToolProvider, McpToolResult, McpToolStreamHandle, } from "./mcp/server.js";
3
3
  export { StdioMcpServer } from "./mcp/server.js";
4
4
  export type JsonValue = string | number | boolean | null | JsonValue[] | {
5
5
  [k: string]: JsonValue;
@@ -52,4 +52,4 @@ export { createAbility, exportAbility, deployToNode, listAbilities, invokeAbilit
52
52
  export { AxonError, AxonConfigError, AxonBridgeError, AxonNotInstalledError, AxonNotActivatedError, AxonInvocationError, AxonStreamError, AxonPolicyDeniedError, AxonMcpError, AxonPartialSuccessError, AxonJsonError, AxonIoError, AxonSymbolNotFoundError, } from "./errors.js";
53
53
  export { beginPhase, skippedReceipt, buildDeployTrace, phaseFromTrace, PhaseReceiptBuilder, type Phase, type PhaseStatus, type PhaseReceipt, type DeployTrace, } from "./receipt.js";
54
54
  export { AbilityToolAdapter, type ToolSpec as AdapterToolSpec, type OpenAITool, type OpenAIChatTool, type AnthropicTool, type ToolParameters, type LocalHandler, type RegisterOptions, type AbilityToolAdapterOptions, } from "./tool_adapter.js";
55
- export { DendriteBridge, DendriteServerStream, DendriteBidiStream, type DendriteBridgeOptions, DendriteError, type DendriteUnaryCallOptions, type DendriteAbilityCallOptions, type DendriteStreamCallOptions, type DendriteClientStreamCallOptions, type DendriteBidiStreamCallOptions, type DendriteListNodesOptions, type DendriteRegisterNodeOptions, type DendritePublishCapabilityOptions, type DendriteInstallCapabilityOptions, type DendriteListA2aAgentsOptions, type DendriteSendA2aTaskOptions, type DendriteDeployMcpListDirOptions, type DendriteListMcpToolsOptions, type DendriteCallMcpToolOptions, type DendriteUninstallCapabilityOptions, type DendriteUpdateMcpListDirOptions, type DendriteVoiceCodecProfileOptions, type DendriteVoiceMetricsOptions, type DendriteVoiceDescriptionOptions, type DendriteVoiceCandidateOptions, type DendriteVoiceCreateCallOptions, type DendriteVoiceJoinCallOptions, type DendriteVoiceUpdateMediaPathOptions, type DendriteVoiceEndCallOptions, type DendriteVoiceWatchOptions, type DendriteVoiceCreateTransportSessionOptions, type DendriteVoiceEndTransportSessionOptions, type DendriteProtocolInvokeOptions, } from "./dendrite_bridge.js";
55
+ export { DendriteBridge, DendriteServerStream, DendriteBidiStream, type DendriteBridgeOptions, DendriteError, type DendriteUnaryCallOptions, type DendriteAbilityCallOptions, type DendriteStreamCallOptions, type DendriteClientStreamCallOptions, type DendriteBidiStreamCallOptions, type DendriteListNodesOptions, type DendriteRegisterNodeOptions, type DendritePublishCapabilityOptions, type DendriteInstallCapabilityOptions, type DendriteListA2aAgentsOptions, type DendriteSendA2aTaskOptions, type DendriteDeployMcpListDirOptions, type DendriteListMcpToolsOptions, type DendriteCallMcpToolOptions, type DendriteCallMcpToolStreamOptions, type DendriteUninstallCapabilityOptions, type DendriteUpdateMcpListDirOptions, type DendriteVoiceCodecProfileOptions, type DendriteVoiceMetricsOptions, type DendriteVoiceDescriptionOptions, type DendriteVoiceCandidateOptions, type DendriteVoiceCreateCallOptions, type DendriteVoiceJoinCallOptions, type DendriteVoiceUpdateMediaPathOptions, type DendriteVoiceEndCallOptions, type DendriteVoiceWatchOptions, type DendriteVoiceCreateTransportSessionOptions, type DendriteVoiceEndTransportSessionOptions, type DendriteProtocolInvokeOptions, } from "./dendrite_bridge.js";
@@ -2,9 +2,20 @@ export interface McpToolResult {
2
2
  payload: Record<string, unknown>;
3
3
  isError: boolean;
4
4
  }
5
+ /** Handle returned by streaming MCP tool providers. Consumers iterate `stream` and must call `close()` when done. */
6
+ export interface McpToolStreamHandle {
7
+ stream: AsyncIterable<Uint8Array>;
8
+ close(): void;
9
+ }
5
10
  export interface McpToolProvider {
6
11
  toolSpecs(): Array<Record<string, unknown>>;
7
12
  handleToolCall(name: string, args: Record<string, unknown>): McpToolResult | Promise<McpToolResult>;
13
+ /**
14
+ * Optional streaming handler. Return a `McpToolStreamHandle` to stream incremental chunks,
15
+ * or `null` to fall back to the unary `handleToolCall` path.
16
+ * When called via a non-streaming transport, the server buffers chunks into a single response.
17
+ */
18
+ handleToolCallStream?(name: string, args: Record<string, unknown>): McpToolStreamHandle | null;
8
19
  }
9
20
  interface McpTransportOptions {
10
21
  protocolVersion?: string;
@@ -14,16 +25,27 @@ interface McpTransportOptions {
14
25
  interface JsonMessage {
15
26
  [key: string]: unknown;
16
27
  }
28
+ /** Callback used by the MCP server to write JSON-RPC messages to the transport. */
29
+ export type McpWriteFn = (payload: JsonMessage) => Promise<void>;
17
30
  export declare class StdioMcpServer {
18
31
  private readonly provider;
19
32
  private readonly protocolVersion;
20
33
  private readonly serverName;
21
34
  private readonly serverVersion;
22
35
  private _closed;
36
+ private _pendingWrite;
23
37
  constructor(provider: McpToolProvider, options?: McpTransportOptions);
24
38
  close(): void;
25
39
  run(input: NodeJS.ReadableStream, output: NodeJS.WritableStream): void;
26
- handleRawLine(raw: string): JsonMessage | Promise<JsonMessage> | null;
27
- handleRequest(request: JsonMessage): JsonMessage | Promise<JsonMessage> | null;
40
+ private dispatchRequest;
41
+ private enqueueWrite;
42
+ handleRawLine(raw: string, writeFn?: McpWriteFn): JsonMessage | Promise<JsonMessage | null> | null;
43
+ handleRequest(request: JsonMessage, writeFn?: McpWriteFn): JsonMessage | Promise<JsonMessage | null> | null;
28
44
  }
45
+ /**
46
+ * Buffer all chunks from a streaming MCP tool handle into a single result.
47
+ * Stops accumulating after `maxBytes` (default 64 MiB) to prevent unbounded
48
+ * memory growth. Always closes the handle when done, even on error.
49
+ */
50
+ export declare function consumeStream(handle: McpToolStreamHandle, maxBytes?: number): Promise<McpToolResult>;
29
51
  export {};