@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.
- package/README.md +12 -0
- package/native/dendrite-bridge-manifest.json +5 -4
- package/native/dendrite-bridge.json +1 -1
- package/native/include/axon_dendrite_bridge.h +18 -4
- package/native/libaxon_dendrite_bridge.so +0 -0
- package/package.json +7 -5
- package/runtime/easynet-runtime-rs-0.36.9-x86_64-unknown-linux-gnu.tar.gz +0 -0
- package/runtime/runtime-bridge-manifest.json +4 -4
- package/runtime/runtime-bridge.json +3 -3
- package/src/ability_lifecycle.d.ts +1 -0
- package/src/ability_lifecycle.js +12 -6
- package/src/capability_request.js +3 -1
- package/src/dendrite_bridge/bridge.d.ts +9 -2
- package/src/dendrite_bridge/bridge.js +54 -5
- package/src/dendrite_bridge/ffi.d.ts +3 -0
- package/src/dendrite_bridge/ffi.js +2 -0
- package/src/dendrite_bridge/types.d.ts +4 -0
- package/src/errors.js +9 -3
- package/src/index.d.ts +2 -2
- package/src/mcp/server.d.ts +24 -2
- package/src/mcp/server.js +218 -18
- package/src/mcp/server.test.js +62 -0
- package/src/presets/remote_control/config.d.ts +3 -0
- package/src/presets/remote_control/config.js +17 -3
- package/src/presets/remote_control/descriptor.js +28 -11
- package/src/presets/remote_control/handlers.d.ts +2 -0
- package/src/presets/remote_control/handlers.js +22 -17
- package/src/presets/remote_control/kit.d.ts +4 -2
- package/src/presets/remote_control/kit.js +70 -1
- package/src/presets/remote_control/kit.test.js +449 -0
- package/src/presets/remote_control/orchestrator.d.ts +5 -0
- package/src/presets/remote_control/orchestrator.js +9 -1
- package/src/presets/remote_control/specs.js +80 -61
- package/src/receipt.js +6 -3
- 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.
|
|
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": "
|
|
8
|
+
"sha256": "e802bd6d15684b1b3f4c7fd2fc7159b591aaa93c58de78c369cac5d0315f7688"
|
|
9
9
|
},
|
|
10
10
|
"header": {
|
|
11
11
|
"path": "native/include/axon_dendrite_bridge.h",
|
|
12
|
-
"sha256": "
|
|
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-
|
|
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.
|
|
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.
|
|
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 &&
|
|
23
|
-
"
|
|
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.
|
|
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.
|
|
8
|
-
"sha256": "
|
|
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-
|
|
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.
|
|
3
|
+
"runtime_version": "0.36.9",
|
|
4
4
|
"target": "x86_64-unknown-linux-gnu",
|
|
5
|
-
"runtime_artifact_path": "runtime/easynet-runtime-rs-0.
|
|
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.
|
|
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
|
}
|
package/src/ability_lifecycle.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
//
|
|
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:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
//
|
|
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";
|
package/src/mcp/server.d.ts
CHANGED
|
@@ -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
|
-
|
|
27
|
-
|
|
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 {};
|