@animalabs/agent-framework 0.32.0 → 0.34.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 +340 -22
- package/dist/src/admission/admit.d.ts +18 -0
- package/dist/src/admission/admit.d.ts.map +1 -1
- package/dist/src/admission/admit.js +16 -3
- package/dist/src/admission/admit.js.map +1 -1
- package/dist/src/agent.d.ts +208 -80
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +345 -113
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/mcp-server-core.d.ts.map +1 -1
- package/dist/src/api/mcp-server-core.js +50 -12
- package/dist/src/api/mcp-server-core.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +4 -20
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/append-all.d.ts +5 -0
- package/dist/src/append-all.d.ts.map +1 -0
- package/dist/src/append-all.js +8 -0
- package/dist/src/append-all.js.map +1 -0
- package/dist/src/code-execution/isolate/scripts.d.ts +12 -8
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/scripts.js +24 -0
- package/dist/src/code-execution/isolate/scripts.js.map +1 -1
- package/dist/src/config-checks.d.ts +7 -0
- package/dist/src/config-checks.d.ts.map +1 -1
- package/dist/src/config-checks.js +14 -0
- package/dist/src/config-checks.js.map +1 -1
- package/dist/src/context-manager-errors.d.ts +25 -0
- package/dist/src/context-manager-errors.d.ts.map +1 -0
- package/dist/src/context-manager-errors.js +31 -0
- package/dist/src/context-manager-errors.js.map +1 -0
- package/dist/src/context-options.d.ts +70 -0
- package/dist/src/context-options.d.ts.map +1 -0
- package/dist/src/context-options.js +90 -0
- package/dist/src/context-options.js.map +1 -0
- package/dist/src/cut-off-round.js +4 -4
- package/dist/src/deferred-import.d.ts +93 -0
- package/dist/src/deferred-import.d.ts.map +1 -0
- package/dist/src/deferred-import.js +177 -0
- package/dist/src/deferred-import.js.map +1 -0
- package/dist/src/dendrite/audience.d.ts +223 -0
- package/dist/src/dendrite/audience.d.ts.map +1 -0
- package/dist/src/dendrite/audience.js +202 -0
- package/dist/src/dendrite/audience.js.map +1 -0
- package/dist/src/dendrite/index.d.ts +5 -3
- package/dist/src/dendrite/index.d.ts.map +1 -1
- package/dist/src/dendrite/index.js +2 -1
- package/dist/src/dendrite/index.js.map +1 -1
- package/dist/src/dendrite/presets.d.ts +10 -5
- package/dist/src/dendrite/presets.d.ts.map +1 -1
- package/dist/src/dendrite/presets.js +17 -5
- package/dist/src/dendrite/presets.js.map +1 -1
- package/dist/src/dendrite/registry.d.ts +62 -7
- package/dist/src/dendrite/registry.d.ts.map +1 -1
- package/dist/src/dendrite/registry.js +183 -12
- package/dist/src/dendrite/registry.js.map +1 -1
- package/dist/src/dendrite/types.d.ts +91 -8
- package/dist/src/dendrite/types.d.ts.map +1 -1
- package/dist/src/failure-text.d.ts +36 -0
- package/dist/src/failure-text.d.ts.map +1 -0
- package/dist/src/failure-text.js +89 -0
- package/dist/src/failure-text.js.map +1 -0
- package/dist/src/foreign-rounds.d.ts +69 -0
- package/dist/src/foreign-rounds.d.ts.map +1 -0
- package/dist/src/foreign-rounds.js +152 -0
- package/dist/src/foreign-rounds.js.map +1 -0
- package/dist/src/framework.d.ts +2064 -842
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +8586 -3739
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +125 -21
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +487 -72
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/index.d.ts +1 -1
- package/dist/src/gate/index.d.ts.map +1 -1
- package/dist/src/gate/index.js.map +1 -1
- package/dist/src/gate/reply-judge.d.ts +4 -0
- package/dist/src/gate/reply-judge.d.ts.map +1 -1
- package/dist/src/gate/reply-judge.js +12 -3
- package/dist/src/gate/reply-judge.js.map +1 -1
- package/dist/src/gate/rule-scope.d.ts +30 -0
- package/dist/src/gate/rule-scope.d.ts.map +1 -0
- package/dist/src/gate/rule-scope.js +41 -0
- package/dist/src/gate/rule-scope.js.map +1 -0
- package/dist/src/gate/suppress-script-worker.d.ts +2 -0
- package/dist/src/gate/suppress-script-worker.d.ts.map +1 -0
- package/dist/src/gate/suppress-script-worker.js +103 -0
- package/dist/src/gate/suppress-script-worker.js.map +1 -0
- package/dist/src/gate/suppress-scripts.d.ts +105 -0
- package/dist/src/gate/suppress-scripts.d.ts.map +1 -0
- package/dist/src/gate/suppress-scripts.js +403 -0
- package/dist/src/gate/suppress-scripts.js.map +1 -0
- package/dist/src/gate/suppression-review.d.ts +143 -0
- package/dist/src/gate/suppression-review.d.ts.map +1 -0
- package/dist/src/gate/suppression-review.js +463 -0
- package/dist/src/gate/suppression-review.js.map +1 -0
- package/dist/src/gate/suppression-stats.d.ts +125 -0
- package/dist/src/gate/suppression-stats.d.ts.map +1 -0
- package/dist/src/gate/suppression-stats.js +214 -0
- package/dist/src/gate/suppression-stats.js.map +1 -0
- package/dist/src/gate/types.d.ts +142 -7
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/gate/wake-onboarding.d.ts +119 -0
- package/dist/src/gate/wake-onboarding.d.ts.map +1 -0
- package/dist/src/gate/wake-onboarding.js +277 -0
- package/dist/src/gate/wake-onboarding.js.map +1 -0
- package/dist/src/heartbeat/heartbeat.js +1 -1
- package/dist/src/heartbeat/heartbeat.js.map +1 -1
- package/dist/src/held-writes.d.ts +31 -23
- package/dist/src/held-writes.d.ts.map +1 -1
- package/dist/src/held-writes.js +44 -59
- package/dist/src/held-writes.js.map +1 -1
- package/dist/src/index.d.ts +6 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/inference-bounds.d.ts +2 -1
- package/dist/src/inference-bounds.d.ts.map +1 -1
- package/dist/src/inference-bounds.js +9 -6
- package/dist/src/inference-bounds.js.map +1 -1
- package/dist/src/jsonrpc-envelope.d.ts +69 -0
- package/dist/src/jsonrpc-envelope.d.ts.map +1 -0
- package/dist/src/jsonrpc-envelope.js +66 -0
- package/dist/src/jsonrpc-envelope.js.map +1 -0
- package/dist/src/mcpl/channel-registry.d.ts +188 -28
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +426 -70
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts +2 -4
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/feature-set-manager.d.ts +2 -4
- package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
- package/dist/src/mcpl/feature-set-manager.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +2 -1
- package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
- package/dist/src/mcpl/inbound-source.js +2 -1
- package/dist/src/mcpl/inbound-source.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts +6 -6
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/modern-connection.d.ts +109 -15
- package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
- package/dist/src/mcpl/modern-connection.js +593 -86
- package/dist/src/mcpl/modern-connection.js.map +1 -1
- package/dist/src/mcpl/protocol-family.d.ts +3 -1
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
- package/dist/src/mcpl/protocol-family.js +15 -9
- package/dist/src/mcpl/protocol-family.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +132 -22
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +105 -15
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +9 -7
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +50 -28
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +165 -72
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tool-classes.d.ts.map +1 -1
- package/dist/src/mcpl/tool-classes.js +3 -0
- package/dist/src/mcpl/tool-classes.js.map +1 -1
- package/dist/src/mcpl/tool-presence.d.ts.map +1 -1
- package/dist/src/mcpl/tool-presence.js +10 -6
- package/dist/src/mcpl/tool-presence.js.map +1 -1
- package/dist/src/mcpl/toolset-host.d.ts.map +1 -1
- package/dist/src/mcpl/toolset-host.js +7 -6
- package/dist/src/mcpl/toolset-host.js.map +1 -1
- package/dist/src/mcpl/toolsets-module.d.ts.map +1 -1
- package/dist/src/mcpl/toolsets-module.js +2 -1
- package/dist/src/mcpl/toolsets-module.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +33 -6
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +24 -1
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +68 -18
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/discord/index.d.ts +45 -8
- package/dist/src/modules/discord/index.d.ts.map +1 -1
- package/dist/src/modules/discord/index.js +238 -87
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/health/index.js +2 -2
- package/dist/src/modules/health/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts +59 -0
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +432 -67
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +6 -5
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +18 -25
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.d.ts +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +1 -1
- package/dist/src/modules/workspace/index.d.ts +28 -7
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +80 -25
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +2 -1
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +3 -2
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/notice-tags.d.ts +20 -9
- package/dist/src/notice-tags.d.ts.map +1 -1
- package/dist/src/notice-tags.js +20 -9
- package/dist/src/notice-tags.js.map +1 -1
- package/dist/src/operator-log.d.ts +36 -3
- package/dist/src/operator-log.d.ts.map +1 -1
- package/dist/src/operator-log.js +2 -2
- package/dist/src/operator-log.js.map +1 -1
- package/dist/src/presentation.d.ts +747 -0
- package/dist/src/presentation.d.ts.map +1 -0
- package/dist/src/presentation.js +1437 -0
- package/dist/src/presentation.js.map +1 -0
- package/dist/src/prose-drafts.d.ts +15 -11
- package/dist/src/prose-drafts.d.ts.map +1 -1
- package/dist/src/prose-drafts.js +16 -12
- package/dist/src/prose-drafts.js.map +1 -1
- package/dist/src/read-marks.d.ts +192 -0
- package/dist/src/read-marks.d.ts.map +1 -0
- package/dist/src/read-marks.js +242 -0
- package/dist/src/read-marks.js.map +1 -0
- package/dist/src/reasoning-loss.d.ts +5 -0
- package/dist/src/reasoning-loss.d.ts.map +1 -0
- package/dist/src/reasoning-loss.js +8 -0
- package/dist/src/reasoning-loss.js.map +1 -0
- package/dist/src/recovery/offline-branch.d.ts +91 -6
- package/dist/src/recovery/offline-branch.d.ts.map +1 -1
- package/dist/src/recovery/offline-branch.js +277 -32
- package/dist/src/recovery/offline-branch.js.map +1 -1
- package/dist/src/recovery/recover-cli.js +24 -4
- package/dist/src/recovery/recover-cli.js.map +1 -1
- package/dist/src/send-tools.d.ts +5 -9
- package/dist/src/send-tools.d.ts.map +1 -1
- package/dist/src/send-tools.js +40 -7
- package/dist/src/send-tools.js.map +1 -1
- package/dist/src/sent-rows.js +1 -1
- package/dist/src/stored-injections.d.ts +2 -2
- package/dist/src/stored-injections.js +2 -2
- package/dist/src/stored-removal.d.ts +17 -4
- package/dist/src/stored-removal.d.ts.map +1 -1
- package/dist/src/stored-removal.js +29 -5
- package/dist/src/stored-removal.js.map +1 -1
- package/dist/src/supplied-text.d.ts +1 -1
- package/dist/src/supplied-text.js +3 -3
- package/dist/src/supplied-text.js.map +1 -1
- package/dist/src/thinking-drops.d.ts +2 -0
- package/dist/src/thinking-drops.d.ts.map +1 -1
- package/dist/src/thinking-drops.js.map +1 -1
- package/dist/src/tool-presentation.d.ts.map +1 -1
- package/dist/src/tool-presentation.js +38 -9
- package/dist/src/tool-presentation.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +12 -1
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +42 -8
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +1170 -81
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +3732 -369
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/tune-out/tools.d.ts +114 -17
- package/dist/src/tune-out/tools.d.ts.map +1 -1
- package/dist/src/tune-out/tools.js +89 -28
- package/dist/src/tune-out/tools.js.map +1 -1
- package/dist/src/tune-out/window-anchor.d.ts +143 -0
- package/dist/src/tune-out/window-anchor.d.ts.map +1 -0
- package/dist/src/tune-out/window-anchor.js +202 -0
- package/dist/src/tune-out/window-anchor.js.map +1 -0
- package/dist/src/types/agent.d.ts +121 -25
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +2 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +72 -17
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/inference-log.d.ts +1 -0
- package/dist/src/types/inference-log.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +180 -69
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +573 -39
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/admission/admit.ts +32 -3
- package/src/agent.ts +392 -132
- package/src/api/mcp-server-core.ts +54 -17
- package/src/api/server.ts +4 -23
- package/src/append-all.ts +6 -0
- package/src/code-execution/isolate/scripts.ts +28 -3
- package/src/config-checks.ts +14 -0
- package/src/context-manager-errors.ts +37 -0
- package/src/context-options.ts +106 -0
- package/src/cut-off-round.ts +4 -4
- package/src/deferred-import.ts +239 -0
- package/src/dendrite/audience.ts +315 -0
- package/src/dendrite/index.ts +14 -2
- package/src/dendrite/presets.ts +18 -6
- package/src/dendrite/registry.ts +213 -14
- package/src/dendrite/types.ts +92 -8
- package/src/failure-text.ts +88 -0
- package/src/foreign-rounds.ts +199 -0
- package/src/framework.ts +8796 -3813
- package/src/gate/event-gate.ts +518 -91
- package/src/gate/index.ts +3 -0
- package/src/gate/reply-judge.ts +12 -3
- package/src/gate/rule-scope.ts +45 -0
- package/src/gate/suppress-script-worker.ts +89 -0
- package/src/gate/suppress-scripts.ts +414 -0
- package/src/gate/suppression-review.ts +517 -0
- package/src/gate/suppression-stats.ts +314 -0
- package/src/gate/types.ts +148 -7
- package/src/gate/wake-onboarding.ts +309 -0
- package/src/heartbeat/heartbeat.ts +1 -1
- package/src/held-writes.ts +49 -60
- package/src/index.ts +10 -2
- package/src/inference-bounds.ts +11 -6
- package/src/jsonrpc-envelope.ts +81 -0
- package/src/mcpl/channel-registry.ts +513 -116
- package/src/mcpl/checkpoint-manager.ts +3 -2
- package/src/mcpl/feature-set-manager.ts +2 -1
- package/src/mcpl/inbound-source.ts +2 -1
- package/src/mcpl/inference-router.ts +7 -4
- package/src/mcpl/modern-connection.ts +629 -91
- package/src/mcpl/protocol-family.ts +15 -9
- package/src/mcpl/push-coalescer.ts +193 -34
- package/src/mcpl/push-handler.ts +10 -5
- package/src/mcpl/server-connection.ts +169 -75
- package/src/mcpl/tool-classes.ts +3 -0
- package/src/mcpl/tool-presence.ts +10 -5
- package/src/mcpl/toolset-host.ts +7 -6
- package/src/mcpl/toolsets-module.ts +2 -1
- package/src/mcpl/types.ts +33 -6
- package/src/module-registry.ts +73 -17
- package/src/modules/discord/index.ts +230 -82
- package/src/modules/health/index.ts +2 -2
- package/src/modules/history/index.ts +439 -70
- package/src/modules/history/semantic.ts +18 -24
- package/src/modules/workspace/disk-agreement.ts +1 -1
- package/src/modules/workspace/index.ts +89 -26
- package/src/modules/workspace/observe.ts +2 -1
- package/src/modules/workspace/reconcile.ts +3 -2
- package/src/notice-tags.ts +21 -9
- package/src/operator-log.ts +36 -3
- package/src/presentation.ts +1678 -0
- package/src/prose-drafts.ts +16 -12
- package/src/read-marks.ts +328 -0
- package/src/reasoning-loss.ts +7 -0
- package/src/recovery/offline-branch.ts +350 -35
- package/src/recovery/recover-cli.ts +23 -4
- package/src/send-tools.ts +40 -7
- package/src/sent-rows.ts +1 -1
- package/src/stored-injections.ts +2 -2
- package/src/stored-removal.ts +31 -7
- package/src/supplied-text.ts +3 -3
- package/src/thinking-drops.ts +2 -0
- package/src/tool-presentation.ts +34 -9
- package/src/tool-result-guard.ts +41 -7
- package/src/tune-out/coordinator.ts +4121 -472
- package/src/tune-out/tools.ts +162 -44
- package/src/tune-out/window-anchor.ts +277 -0
- package/src/types/agent.ts +122 -25
- package/src/types/events.ts +2 -0
- package/src/types/framework.ts +73 -17
- package/src/types/inference-log.ts +1 -0
- package/src/types/module.ts +181 -68
- package/src/types/trace.ts +607 -43
|
@@ -16,6 +16,7 @@ import { maskNegotiatedCapabilities } from './capability-mask.js';
|
|
|
16
16
|
import { CapabilityGrant, expandAdvertisementShorthand } from './capability-grant.js';
|
|
17
17
|
import { CAPABILITY_DISABLED, boundedForLog, describeCause } from './errors.js';
|
|
18
18
|
import { logDetail, logValue, logValueRedacted, loggedCode } from './log-value.js';
|
|
19
|
+
import { jsonKind, readEnvelope } from '../jsonrpc-envelope.js';
|
|
19
20
|
import {
|
|
20
21
|
LEGACY_MCP_PROTOCOL_VERSION,
|
|
21
22
|
MODERN_MCP_PROTOCOL_VERSION,
|
|
@@ -156,20 +157,27 @@ const UNSUPPORTED_PROTOCOL_VERSION_CODE = -32022;
|
|
|
156
157
|
const MAX_TOOL_LIST_PAGES = 100;
|
|
157
158
|
|
|
158
159
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* revision
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
160
|
+
* This engine and the server could not negotiate a protocol revision, so the
|
|
161
|
+
* connect that throws it established no connection. Usually they share none. Over HTTP it
|
|
162
|
+
* can also be the SDK's own refusal: after a -32022 whose list names a
|
|
163
|
+
* revision from 2026-07-28 on that this client doesn't speak, the SDK
|
|
164
|
+
* negotiates no further, even when the list also names a 2025 revision both
|
|
165
|
+
* speak. This is a protocol-negotiation fact, not a request outcome.
|
|
166
|
+
* Retrying cannot change it: each engine offers the same revisions every
|
|
167
|
+
* time (`offeredRevisions`), and only configuration, or the server's
|
|
168
|
+
* operator, can fix the mismatch. A connection that gets this error does not
|
|
169
|
+
* schedule reconnects. The legacy engine and the modern one
|
|
170
|
+
* (ModernMcpConnection) both throw it, so its readers treat them alike.
|
|
166
171
|
*
|
|
167
|
-
* - `rejected`: the server refused the offered
|
|
168
|
-
* `initialize` answered with error -32022
|
|
169
|
-
* or error
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
172
|
+
* - `rejected`: the server refused the revision offered. The legacy
|
|
173
|
+
* engine's `initialize` was answered with error -32022. A modern server's
|
|
174
|
+
* discovery or -32022 error named no revision in common (over HTTP, a
|
|
175
|
+
* discovery's list counts once the server has refused the `initialize`
|
|
176
|
+
* tried after it too), or, over HTTP, named one this client doesn't speak,
|
|
177
|
+
* as above. `supported` is the server's own list, when it gave one.
|
|
178
|
+
* - `mismatch`: the server answered `initialize` with a revision that
|
|
179
|
+
* handshake doesn't accept, or with none: the legacy engine's, or a modern
|
|
180
|
+
* connection's 2025 handshake over HTTP.
|
|
173
181
|
*/
|
|
174
182
|
export class McplProtocolVersionError extends Error {
|
|
175
183
|
constructor(
|
|
@@ -184,9 +192,15 @@ export class McplProtocolVersionError extends Error {
|
|
|
184
192
|
* redacted (`rejected`). */
|
|
185
193
|
readonly code?: number,
|
|
186
194
|
readonly data?: unknown,
|
|
187
|
-
/** The revision this client offered
|
|
188
|
-
* modern connection's
|
|
195
|
+
/** The revision this client offered first, in its order of preference:
|
|
196
|
+
* the legacy engine's (2024-11-05), a modern connection's over stdio
|
|
197
|
+
* (2026-07-28), or over HTTP 2026-07-28, the first of
|
|
198
|
+
* `offeredRevisions`. */
|
|
189
199
|
readonly offered: string = MCP_PROTOCOL_VERSION,
|
|
200
|
+
/** Every revision this client offered, in its order of preference: the
|
|
201
|
+
* legacy engine's one, a modern connection's one over stdio, or its
|
|
202
|
+
* list over HTTP. */
|
|
203
|
+
readonly offeredRevisions: readonly string[] = [offered],
|
|
190
204
|
) {
|
|
191
205
|
super(message);
|
|
192
206
|
this.name = 'McplProtocolVersionError';
|
|
@@ -367,11 +381,32 @@ interface HeldItem {
|
|
|
367
381
|
kept?: true;
|
|
368
382
|
}
|
|
369
383
|
|
|
370
|
-
/**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
384
|
+
/** Whether a response that can't be matched to a request of ours, its id
|
|
385
|
+
* being neither a string nor a number or missing, is logged: only a
|
|
386
|
+
* JSON-RPC 2.0 error is, since that's how a server says it couldn't read a
|
|
387
|
+
* message of ours (JSON-RPC 2.0 §5). Anything else id-less, such as a
|
|
388
|
+
* server's own JSON log line with `"id": null`, is ignored silently, as it
|
|
389
|
+
* was before #475, so a server's log output can't fill the host's. */
|
|
390
|
+
function isJsonRpcError(response: object): boolean {
|
|
391
|
+
return (response as { jsonrpc?: unknown }).jsonrpc === '2.0' && Object.hasOwn(response, 'error');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** The log line for such an error. The host's own request ids are numbers,
|
|
395
|
+
* so it can't be matched to one. Once connected it may be the answer to a
|
|
396
|
+
* request of ours, which gets nothing from it and ends as any unanswered
|
|
397
|
+
* request does, by its timeout where it has one. (During the handshake an
|
|
398
|
+
* error with id null or no id answers initialize.) Its error member is the
|
|
399
|
+
* server's text, whatever JSON value it is, so it's printed redacted with
|
|
400
|
+
* `redact`, the server's redaction (logValueRedacted), as handleResponse
|
|
401
|
+
* prints a matched error's code and message. */
|
|
402
|
+
function unmatchedResponse(
|
|
403
|
+
serverId: string,
|
|
404
|
+
response: { id?: unknown; error?: unknown },
|
|
405
|
+
redact: (text: string) => string,
|
|
406
|
+
): string {
|
|
407
|
+
const which = Object.hasOwn(response, 'id') ? `whose id is ${jsonKind(response.id)}` : 'with no id';
|
|
408
|
+
return `[mcpl] ${serverId}: ignored a response ${which}, which can't be matched to a request of ours; ` +
|
|
409
|
+
`its error: ${logValueRedacted(response.error, redact)}`;
|
|
375
410
|
}
|
|
376
411
|
|
|
377
412
|
/**
|
|
@@ -700,13 +735,14 @@ export class McplServerConnection extends EventEmitter {
|
|
|
700
735
|
// Reconnect state (adapted from Anarchid/agent-framework@mcpl-module-proto)
|
|
701
736
|
private config: McplServerConfig | null = null;
|
|
702
737
|
/** The redactor of the server's text for `config`, made at its first error
|
|
703
|
-
* answer, by when `config`
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
* something new
|
|
738
|
+
* answer or response it can't match (handleResponse), by when `config`
|
|
739
|
+
* is set, and kept: a framework-launched child's `env` is a getter that
|
|
740
|
+
* reads the awareness ledger, so a redactor built per answer would read
|
|
741
|
+
* it per answer (#540's review, R1-2). An answer takes one build of it
|
|
742
|
+
* for its words, data and log line (`now()`), so it reads the ledger at
|
|
743
|
+
* most once. It builds again only after a build that failed, which
|
|
744
|
+
* withholds that answer's text, or once a dial notes something new
|
|
745
|
+
* (serverTextRedactor). */
|
|
710
746
|
private redactor: ServerTextRedactor | null = null;
|
|
711
747
|
private hostCapabilities: McplHostCapabilities | null = null;
|
|
712
748
|
private reconnectEnabled = false;
|
|
@@ -1358,15 +1394,35 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1358
1394
|
// initialize response, whatever its id: a server's own ids can start
|
|
1359
1395
|
// at 0, this handshake's. MCP lets a server ping before
|
|
1360
1396
|
// initialization completes, and the ping gets its empty result, as
|
|
1361
|
-
// on a connected transport
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1397
|
+
// on a connected transport, `"id": null` included
|
|
1398
|
+
// (src/jsonrpc-envelope.ts). Anything else it sends now is ignored,
|
|
1399
|
+
// an invalid request included. Of its responses, the one with
|
|
1400
|
+
// initialize's id answers it, and so does a JSON-RPC 2.0 error with
|
|
1401
|
+
// id null or with no id: a peer answers a request it couldn't read
|
|
1402
|
+
// that way (JSON-RPC 2.0 §5; MCP 2025-11-25, Basic), initialize is
|
|
1403
|
+
// the only request this host has out, and a peer never answers a
|
|
1404
|
+
// response. Such an error means the server can't read this host's
|
|
1405
|
+
// messages, so the handshake fails at once, with the error's message.
|
|
1406
|
+
// `"jsonrpc": "2.0"` keeps a server's own JSON log line with an
|
|
1407
|
+
// error key and id null, or no id, from failing it; one with
|
|
1408
|
+
// initialize's id is taken for the answer, whatever its jsonrpc. Of
|
|
1409
|
+
// the other responses, a JSON-RPC 2.0 error whose id is neither a
|
|
1410
|
+
// string nor a number is logged, as on a connected transport, and
|
|
1411
|
+
// any other is ignored.
|
|
1412
|
+
const envelope = readEnvelope(msg as unknown as Record<string, unknown>);
|
|
1413
|
+
if (envelope.kind === 'request' && envelope.method === 'ping') {
|
|
1414
|
+
transport.writeLine(JSON.stringify({ jsonrpc: '2.0', id: envelope.id, result: {} }));
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
if (envelope.kind !== 'response') return;
|
|
1418
|
+
const namesNoRequest = msg.id === null || !Object.hasOwn(msg, 'id');
|
|
1419
|
+
const answersInitialize = msg.id === initId || (namesNoRequest && msg.jsonrpc === '2.0' && Boolean(msg.error));
|
|
1420
|
+
if (!answersInitialize) {
|
|
1421
|
+
if (typeof msg.id !== 'string' && typeof msg.id !== 'number' && isJsonRpcError(msg)) {
|
|
1422
|
+
console.error(unmatchedResponse(config.id, msg, (text) => redactServerText(text, config)));
|
|
1366
1423
|
}
|
|
1367
1424
|
return;
|
|
1368
1425
|
}
|
|
1369
|
-
if (msg.id !== initId) return;
|
|
1370
1426
|
cleanup();
|
|
1371
1427
|
if (msg.error) {
|
|
1372
1428
|
// describeCause rather than the template's own conversion: the
|
|
@@ -1739,8 +1795,9 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1739
1795
|
* following `nextCursor` across pages (2024-11-05 pagination). Cursors are
|
|
1740
1796
|
* opaque strings, `''` included: only an absent or null `nextCursor` ends
|
|
1741
1797
|
* the inventory (a null one is how some servers say "no more", and main
|
|
1742
|
-
* read it as a complete page). A page that isn't well formed (no `tools` array,
|
|
1743
|
-
* that isn't a string
|
|
1798
|
+
* read it as a complete page). A page that isn't well formed (no `tools` array, an entry
|
|
1799
|
+
* that isn't an object with a string `name`, a cursor that isn't a string), or cursors
|
|
1800
|
+
* that don't terminate (a repeat, or more
|
|
1744
1801
|
* than {@link MAX_TOOL_LIST_PAGES} pages), are errors, never a partial or
|
|
1745
1802
|
* empty inventory published as complete. The read tap (RFC-014) is handed
|
|
1746
1803
|
* the complete inventory once, with the last page's `_meta`, as the last
|
|
@@ -1755,9 +1812,12 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1755
1812
|
let first: Record<string, unknown> | undefined;
|
|
1756
1813
|
const read: McpToolDefinition[] = [];
|
|
1757
1814
|
const revs = new Set<unknown>();
|
|
1815
|
+
const isTool = (entry: unknown): boolean =>
|
|
1816
|
+
!!entry && typeof entry === 'object' && typeof (entry as { name?: unknown }).name === 'string';
|
|
1758
1817
|
const onRead = (result: unknown) => {
|
|
1759
1818
|
if (!result || typeof result !== 'object' || !Array.isArray((result as { tools?: unknown }).tools)) return;
|
|
1760
1819
|
const r = result as { tools: McpToolDefinition[]; nextCursor?: unknown; _meta?: unknown };
|
|
1820
|
+
if (!r.tools.every(isTool)) return;
|
|
1761
1821
|
for (const t of r.tools) read.push(t);
|
|
1762
1822
|
const rev = (r._meta as Record<string, unknown> | undefined)?.['mcpl/rev'];
|
|
1763
1823
|
if (typeof rev === 'number') revs.add(rev);
|
|
@@ -1774,6 +1834,8 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1774
1834
|
if (!result || typeof result !== 'object' || !Array.isArray(result.tools)) {
|
|
1775
1835
|
throw malformed('no tools array');
|
|
1776
1836
|
}
|
|
1837
|
+
const unreadable = (result.tools as unknown[]).findIndex((entry) => !isTool(entry));
|
|
1838
|
+
if (unreadable !== -1) throw malformed(`entry ${unreadable + 1} isn't an object with a string name`);
|
|
1777
1839
|
first ??= result;
|
|
1778
1840
|
for (const t of result.tools as McpToolDefinition[]) tools.push(t);
|
|
1779
1841
|
if (result.nextCursor === undefined || result.nextCursor === null) {
|
|
@@ -2389,10 +2451,10 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2389
2451
|
// passed is the dead process's (afterBoundary).
|
|
2390
2452
|
const epoch = this.transportEpoch;
|
|
2391
2453
|
transport.on('line', (line: string) => {
|
|
2392
|
-
const msg = messageFromLine(this.id, line, transport)
|
|
2454
|
+
const msg = messageFromLine(this.id, line, transport);
|
|
2393
2455
|
if (!msg) return;
|
|
2394
2456
|
try {
|
|
2395
|
-
this.dispatchMessage(msg, epoch);
|
|
2457
|
+
this.dispatchMessage(msg as Record<string, unknown>, epoch);
|
|
2396
2458
|
} catch (error) {
|
|
2397
2459
|
const request = msg as { method?: unknown; id?: unknown };
|
|
2398
2460
|
this.reportInboundFailure(error, {
|
|
@@ -2404,25 +2466,47 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2404
2466
|
}
|
|
2405
2467
|
|
|
2406
2468
|
/** Contain synchronous dispatch failures; async RPC handlers own their promises. */
|
|
2407
|
-
private dispatchMessage(msg:
|
|
2408
|
-
//
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2469
|
+
private dispatchMessage(msg: Record<string, unknown>, epoch: number): void {
|
|
2470
|
+
// The envelope is read once, here (src/jsonrpc-envelope.ts says how).
|
|
2471
|
+
const envelope = readEnvelope(msg);
|
|
2472
|
+
switch (envelope.kind) {
|
|
2473
|
+
case 'response': {
|
|
2474
|
+
// Never answered, so a server that answers what it can't read, with
|
|
2475
|
+
// id null, can't start an exchange of errors with us. One read after
|
|
2476
|
+
// its transport's boundary is late (handleResponse).
|
|
2477
|
+
const late = epoch !== this.transportEpoch;
|
|
2478
|
+
// RFC-014: its hints apply as it's read, whether or not anyone still
|
|
2479
|
+
// waits for it. A dead transport's revs are its own, so a response
|
|
2480
|
+
// read after its boundary is never tapped.
|
|
2481
|
+
if (!late) this.tapResponse(msg as unknown as JsonRpcResponse);
|
|
2482
|
+
this.handleResponse(msg as unknown as JsonRpcResponse, late);
|
|
2483
|
+
return;
|
|
2484
|
+
}
|
|
2485
|
+
case 'neither':
|
|
2486
|
+
// Not JSON-RPC: ignored like the server's other unrelated output.
|
|
2487
|
+
return;
|
|
2488
|
+
case 'invalid': {
|
|
2489
|
+
// Answered on the transport it came on, as every request is. One
|
|
2490
|
+
// read after that transport's boundary is the dead process's and
|
|
2491
|
+
// isn't answered: sendErrorResponse logs the answer it didn't send.
|
|
2492
|
+
this.sendErrorResponse(envelope.id, envelope.error.code, envelope.error.message, undefined, epoch);
|
|
2493
|
+
if (epoch !== this.transportEpoch) return;
|
|
2494
|
+
const forMethod = envelope.method !== undefined ? ` for method ${logValue(envelope.method)}` : '';
|
|
2495
|
+
console.error(`[mcpl] ${this.id}: answered -32600 to an invalid request${forMethod}: ${envelope.problems}`);
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2417
2498
|
}
|
|
2418
2499
|
|
|
2419
|
-
// It is an inbound request or notification from the server
|
|
2420
|
-
|
|
2500
|
+
// It is an inbound request or notification from the server, its method
|
|
2501
|
+
// a string. A request's id is a string, a number or null, and `"id": null`
|
|
2502
|
+
// is answered with id null; a notification has none.
|
|
2503
|
+
const request = msg as unknown as JsonRpcRequest;
|
|
2504
|
+
const requestId = envelope.kind === 'request' ? envelope.id : undefined;
|
|
2421
2505
|
|
|
2422
2506
|
// Read after its transport's boundary, it is the dead process's: the
|
|
2423
2507
|
// boundary's rule applies (afterBoundary).
|
|
2424
2508
|
if (epoch !== this.transportEpoch) {
|
|
2425
|
-
this.afterBoundary(request, epoch);
|
|
2509
|
+
this.afterBoundary(request, requestId, epoch);
|
|
2426
2510
|
return;
|
|
2427
2511
|
}
|
|
2428
2512
|
|
|
@@ -2432,7 +2516,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2432
2516
|
// paused plane mustn't hold that answer. A ping sent as a notification
|
|
2433
2517
|
// has nothing to answer. The handshake answers one too.
|
|
2434
2518
|
if (request.method === 'ping') {
|
|
2435
|
-
if (
|
|
2519
|
+
if (requestId !== undefined) this.sendResponse(requestId, {});
|
|
2436
2520
|
return;
|
|
2437
2521
|
}
|
|
2438
2522
|
|
|
@@ -2443,11 +2527,11 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2443
2527
|
if (request.method === 'push/event') this.tap({ kind: 'push', params: request.params });
|
|
2444
2528
|
else if (request.method === 'tools/hints' && this.readTap) {
|
|
2445
2529
|
this.tap({ kind: 'hints', params: request.params });
|
|
2446
|
-
if (
|
|
2530
|
+
if (requestId !== undefined) this.sendResponse(requestId, {});
|
|
2447
2531
|
return;
|
|
2448
2532
|
}
|
|
2449
2533
|
|
|
2450
|
-
const eventName =
|
|
2534
|
+
const eventName = Object.hasOwn(McplServerConnection.METHOD_TO_EVENT, request.method)
|
|
2451
2535
|
? McplServerConnection.METHOD_TO_EVENT[request.method] : undefined;
|
|
2452
2536
|
|
|
2453
2537
|
// §7 is removed in 0.5.0: scope/elevate is no longer a method. Answer
|
|
@@ -2459,8 +2543,8 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2459
2543
|
// routed, control-plane classified, and mutating declarations — a
|
|
2460
2544
|
// live ungated self-attestation path (PR #79 review blocker 4).
|
|
2461
2545
|
if (request.method === McplMethod.FeatureSetsChanged) {
|
|
2462
|
-
if (
|
|
2463
|
-
this.sendErrorResponse(
|
|
2546
|
+
if (requestId !== undefined) {
|
|
2547
|
+
this.sendErrorResponse(requestId, -32601, 'featureSets/changed removed in MCPL 0.5.0 (superseded by mcpl/manifestChanged, SPEC §17)');
|
|
2464
2548
|
} else {
|
|
2465
2549
|
console.error(`[mcpl] ${this.id}: discarded featureSets/changed — removed in 0.5.0 (§6.7/§17)`);
|
|
2466
2550
|
}
|
|
@@ -2468,8 +2552,8 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2468
2552
|
}
|
|
2469
2553
|
|
|
2470
2554
|
if (request.method === McplMethod.ScopeElevate) {
|
|
2471
|
-
if (
|
|
2472
|
-
this.sendErrorResponse(
|
|
2555
|
+
if (requestId !== undefined) {
|
|
2556
|
+
this.sendErrorResponse(requestId, -32601, 'scope/elevate removed in MCPL 0.5.0 (SPEC §7)');
|
|
2473
2557
|
}
|
|
2474
2558
|
return;
|
|
2475
2559
|
}
|
|
@@ -2481,20 +2565,19 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2481
2565
|
|
|
2482
2566
|
if (!eventName) {
|
|
2483
2567
|
// §6.6: a request that will never be answered MUST get an error, so a
|
|
2484
|
-
// request
|
|
2485
|
-
//
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
console.error(`[mcpl] ${this.id}: answered -32601 to a request for ${describeMethod(request.method)}, which this host doesn't handle`);
|
|
2568
|
+
// request whose method isn't one this host handles is answered -32601.
|
|
2569
|
+
// A notification nobody handles is ignored, as JSON-RPC asks.
|
|
2570
|
+
if (requestId !== undefined) {
|
|
2571
|
+
this.sendErrorResponse(requestId, -32601, 'Method not found');
|
|
2572
|
+
console.error(`[mcpl] ${this.id}: answered -32601 to a request for method ${logValue(request.method)}, which this host doesn't handle`);
|
|
2490
2573
|
}
|
|
2491
2574
|
return;
|
|
2492
2575
|
}
|
|
2493
2576
|
|
|
2494
2577
|
// Emit the typed event with params and (for requests) a respond callback
|
|
2495
|
-
if (
|
|
2578
|
+
if (requestId !== undefined) {
|
|
2496
2579
|
// Server expects a response — provide a respond helper
|
|
2497
|
-
this.dispatchInboundEvent(eventName, request.params, this.responderFor(
|
|
2580
|
+
this.dispatchInboundEvent(eventName, request.params, this.responderFor(requestId, epoch));
|
|
2498
2581
|
} else {
|
|
2499
2582
|
// Notification — no response expected
|
|
2500
2583
|
this.dispatchInboundEvent(eventName, request.params);
|
|
@@ -2504,9 +2587,10 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2504
2587
|
/**
|
|
2505
2588
|
* A request's responder, bound to the transport it came on (`epoch`): an
|
|
2506
2589
|
* answer, or a streamed inference's chunk, sent after that transport has
|
|
2507
|
-
* closed reaches nothing, never a server that didn't ask it.
|
|
2590
|
+
* closed reaches nothing, never a server that didn't ask it. A request
|
|
2591
|
+
* sent with id null is answered with id null.
|
|
2508
2592
|
*/
|
|
2509
|
-
private responderFor(id: string | number, epoch: number) {
|
|
2593
|
+
private responderFor(id: string | number | null, epoch: number) {
|
|
2510
2594
|
return {
|
|
2511
2595
|
id,
|
|
2512
2596
|
signal: epoch === this.transportEpoch ? this.transportEnded.signal : AbortSignal.abort(),
|
|
@@ -2531,17 +2615,17 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2531
2615
|
* it while its writer lives, with no grant to come, so it drops and logs
|
|
2532
2616
|
* all of it; hold's bounds cap what any other keeps.
|
|
2533
2617
|
*/
|
|
2534
|
-
private afterBoundary(request: JsonRpcRequest, epoch: number): void {
|
|
2535
|
-
const event =
|
|
2618
|
+
private afterBoundary(request: JsonRpcRequest, requestId: string | number | null | undefined, epoch: number): void {
|
|
2619
|
+
const event = Object.hasOwn(McplServerConnection.METHOD_TO_EVENT, request.method)
|
|
2536
2620
|
? McplServerConnection.METHOD_TO_EVENT[request.method] : undefined;
|
|
2537
2621
|
if (this.closedForGood || event === undefined || !McplServerConnection.WORLD_EVENTS.has(event)) {
|
|
2538
2622
|
console.error(
|
|
2539
2623
|
`[mcpl] ${this.id}: dropped a message read after its transport closed` +
|
|
2540
|
-
`${this.closedForGood ? ', with no transport to come' : ''}: ${
|
|
2624
|
+
`${this.closedForGood ? ', with no transport to come' : ''}: method ${logValue(request.method)}`,
|
|
2541
2625
|
);
|
|
2542
2626
|
return;
|
|
2543
2627
|
}
|
|
2544
|
-
this.hold(event,
|
|
2628
|
+
this.hold(event, requestId !== undefined ? [request.params, this.responderFor(requestId, epoch)] : [request.params], true);
|
|
2545
2629
|
}
|
|
2546
2630
|
|
|
2547
2631
|
/** Live delivery and delayed plane flushes share the same containment boundary. */
|
|
@@ -2596,7 +2680,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2596
2680
|
/** Tap a response to a hinted request (RFC-014): its result's hints, or a
|
|
2597
2681
|
* list's statement, apply now, awaited or not. */
|
|
2598
2682
|
private tapResponse(response: JsonRpcResponse): void {
|
|
2599
|
-
if (!this.readTap) return;
|
|
2683
|
+
if (!this.readTap || response.id === null) return;
|
|
2600
2684
|
const sent = this.hintedRequests.get(response.id);
|
|
2601
2685
|
if (!sent) return;
|
|
2602
2686
|
this.hintedRequests.delete(response.id);
|
|
@@ -2609,6 +2693,16 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2609
2693
|
* Handle a JSON-RPC response by resolving/rejecting the corresponding pending request.
|
|
2610
2694
|
*/
|
|
2611
2695
|
private handleResponse(response: JsonRpcResponse, late = false): void {
|
|
2696
|
+
// A response whose id is neither a string nor a number, or that has
|
|
2697
|
+
// none, can't be matched to a request of ours, on this transport or an
|
|
2698
|
+
// earlier one. It isn't an orphan, which is a late answer to one, so
|
|
2699
|
+
// it's ignored, and logged when it's a JSON-RPC 2.0 error.
|
|
2700
|
+
if (typeof response.id !== 'string' && typeof response.id !== 'number') {
|
|
2701
|
+
if (isJsonRpcError(response)) {
|
|
2702
|
+
console.error(unmatchedResponse(this.id, response, (this.redactor ??= serverTextRedactor(this.config ?? {})).now()));
|
|
2703
|
+
}
|
|
2704
|
+
return;
|
|
2705
|
+
}
|
|
2612
2706
|
// One read after its transport's boundary answers a request the close
|
|
2613
2707
|
// ended. It's matched against none of this connection's request records,
|
|
2614
2708
|
// since the next transport numbers its requests afresh, and goes the way
|
|
@@ -2706,7 +2800,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2706
2800
|
/**
|
|
2707
2801
|
* Send a successful JSON-RPC response back to the server.
|
|
2708
2802
|
*/
|
|
2709
|
-
private sendResponse(id: string | number, result: unknown, epoch = this.transportEpoch): void {
|
|
2803
|
+
private sendResponse(id: string | number | null, result: unknown, epoch = this.transportEpoch): void {
|
|
2710
2804
|
// A closed connection's epoch is past every request's, so a refusal
|
|
2711
2805
|
// while it is closed is logged too.
|
|
2712
2806
|
if (!this.sameTransport(id, epoch) || this.closed) return;
|
|
@@ -2717,7 +2811,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2717
2811
|
/** Whether a response to a request received on `epoch` may go out now.
|
|
2718
2812
|
* The id is the server's, so it's logged as a field; `answer`, which the
|
|
2719
2813
|
* caller has formatted for the log, ends the line. */
|
|
2720
|
-
private sameTransport(id: string | number, epoch: number, answer?: string): boolean {
|
|
2814
|
+
private sameTransport(id: string | number | null, epoch: number, answer?: string): boolean {
|
|
2721
2815
|
if (epoch === this.transportEpoch) return true;
|
|
2722
2816
|
console.error(
|
|
2723
2817
|
`[mcpl] ${this.id}: not answering request ${logValue(id)}: the transport it came on has closed${answer ? `, with ${answer}` : ''}`,
|
|
@@ -2729,7 +2823,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2729
2823
|
* Send a JSON-RPC error response back to the server.
|
|
2730
2824
|
*/
|
|
2731
2825
|
private sendErrorResponse(
|
|
2732
|
-
id: string | number,
|
|
2826
|
+
id: string | number | null,
|
|
2733
2827
|
code: number,
|
|
2734
2828
|
message: string,
|
|
2735
2829
|
data?: unknown,
|
package/src/mcpl/tool-classes.ts
CHANGED
|
@@ -87,8 +87,11 @@ export const BUILTIN_TOOL_CLASSES: Readonly<Record<string, readonly ToolClass[]>
|
|
|
87
87
|
wake: ['control'],
|
|
88
88
|
wake_add_rule: ['control'],
|
|
89
89
|
wake_remove_rule: ['control'],
|
|
90
|
+
wake_accept_suggestions: ['control'],
|
|
91
|
+
wake_decline: ['control'],
|
|
90
92
|
event_tags: ['control'],
|
|
91
93
|
prose_help: ['control'],
|
|
94
|
+
prose_locus: ['control'],
|
|
92
95
|
// Files and blobs in the agent's own stores.
|
|
93
96
|
save_recent_image: ['files'],
|
|
94
97
|
read_image: ['files'],
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { objectSignature, renderToolLines, toolDigest, type HintedTool, type Shown } from './tool-signature.js';
|
|
29
29
|
import { nameToTypeBack, quotedValue, suppliedValue } from '../supplied-text.js';
|
|
30
|
+
import { appendAll } from '../append-all.js';
|
|
30
31
|
|
|
31
32
|
export interface ToolsetDecl {
|
|
32
33
|
title?: string;
|
|
@@ -632,7 +633,7 @@ export class ToolPresence {
|
|
|
632
633
|
continue;
|
|
633
634
|
}
|
|
634
635
|
const title = (c.opened || c.retitled) && !bare ? c.title : undefined;
|
|
635
|
-
if (showsBlock(c)) lines
|
|
636
|
+
if (showsBlock(c)) appendAll(lines, this.toolBlock(c.key, c.now, { headed: !bare, title, seen, describeAll: opts.describeAll }));
|
|
636
637
|
else if (title) lines.push(`${this.label(c.key)} ${suppliedValue(title)}`);
|
|
637
638
|
if (c.gone.length) lines.push(`now unavailable: ${c.gone.map(suppliedValue).join(', ')}`);
|
|
638
639
|
if (c.opened && !bare) {
|
|
@@ -641,7 +642,8 @@ export class ToolPresence {
|
|
|
641
642
|
}
|
|
642
643
|
}
|
|
643
644
|
for (const s of cs.suggest) lines.push(this.renderSuggestion(s));
|
|
644
|
-
lines
|
|
645
|
+
appendAll(lines, this.untoldNotes(cs));
|
|
646
|
+
appendAll(lines, cs.notes);
|
|
645
647
|
return lines.join('\n');
|
|
646
648
|
}
|
|
647
649
|
|
|
@@ -663,7 +665,7 @@ export class ToolPresence {
|
|
|
663
665
|
if (!v.size) return null;
|
|
664
666
|
const lines: string[] = [];
|
|
665
667
|
for (const [key, e] of v) {
|
|
666
|
-
lines
|
|
668
|
+
appendAll(lines, this.toolBlock(key, [...e.tools.values()], { describeAll: true, title: e.title }));
|
|
667
669
|
const close = this.closeHint(key);
|
|
668
670
|
if (close) lines.push(` ${close} closes it.`);
|
|
669
671
|
}
|
|
@@ -679,7 +681,7 @@ export class ToolPresence {
|
|
|
679
681
|
const v = this.view();
|
|
680
682
|
if (v.size) {
|
|
681
683
|
for (const [key, e] of v) {
|
|
682
|
-
lines
|
|
684
|
+
appendAll(lines, this.toolBlock(key, [...e.tools.values()], { describeAll: true, title: e.title, seen: opts.mark === false ? new Set<string>() : undefined }).map((l) => ` ${l}`));
|
|
683
685
|
const close = this.closeHint(key);
|
|
684
686
|
if (close) lines.push(` ${close} closes it.`);
|
|
685
687
|
}
|
|
@@ -700,7 +702,10 @@ export class ToolPresence {
|
|
|
700
702
|
const names = this.allTools.filter((t) => this.toolsetOf(t.name) === g).map((t) => t.name);
|
|
701
703
|
openable.push(` ${nameToTypeBack(g)}: ${names.map(nameToTypeBack).join(', ')}`);
|
|
702
704
|
}
|
|
703
|
-
if (openable.length)
|
|
705
|
+
if (openable.length) {
|
|
706
|
+
lines.push(`can open — ${this.names.open} {server:"${this.server}", toolset, instance?, options?}:`);
|
|
707
|
+
appendAll(lines, openable);
|
|
708
|
+
}
|
|
704
709
|
return lines.join('\n');
|
|
705
710
|
}
|
|
706
711
|
|
package/src/mcpl/toolset-host.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
type ChangeSet, type MetaToolNames, type Presence,
|
|
36
36
|
} from './tool-presence.js';
|
|
37
37
|
import type { HintedTool } from './tool-signature.js';
|
|
38
|
+
import { appendAll } from '../append-all.js';
|
|
38
39
|
|
|
39
40
|
/** The meta-tools' module, whose tools are `toolsets--open` and so on. */
|
|
40
41
|
export const TOOLSETS_MODULE = 'toolsets';
|
|
@@ -246,7 +247,7 @@ export class ToolsetController {
|
|
|
246
247
|
const toolset = typeof res.toolset === 'string' ? res.toolset : String(params.toolset ?? '');
|
|
247
248
|
const instance = typeof res.instance === 'string' && res.instance ? res.instance : typeof params.instance === 'string' ? params.instance : undefined;
|
|
248
249
|
this.presence.noteOpened(toolset, instance, typeof res.title === 'string' && res.title ? res.title : undefined);
|
|
249
|
-
cs.changes
|
|
250
|
+
appendAll(cs.changes, this.presence.diff(before, this.presence.view()).changes);
|
|
250
251
|
break;
|
|
251
252
|
}
|
|
252
253
|
case 'toolsets/close': {
|
|
@@ -255,7 +256,7 @@ export class ToolsetController {
|
|
|
255
256
|
cs = this.applyHints(metaHints(res), 'toolsets/close');
|
|
256
257
|
const before = this.presence.view();
|
|
257
258
|
this.presence.noteClosed(String(params.toolset ?? ''), typeof params.instance === 'string' ? params.instance : undefined);
|
|
258
|
-
cs.changes
|
|
259
|
+
appendAll(cs.changes, this.presence.diff(before, this.presence.view()).changes);
|
|
259
260
|
break;
|
|
260
261
|
}
|
|
261
262
|
default:
|
|
@@ -298,7 +299,7 @@ export class ToolsetController {
|
|
|
298
299
|
this.scheduleRecovery();
|
|
299
300
|
}
|
|
300
301
|
const cs = this.presence.diff(before, this.presence.view());
|
|
301
|
-
cs.suggest
|
|
302
|
+
appendAll(cs.suggest, r.suggest);
|
|
302
303
|
return cs;
|
|
303
304
|
}
|
|
304
305
|
|
|
@@ -332,11 +333,11 @@ export class ToolsetController {
|
|
|
332
333
|
this.scheduleRecovery();
|
|
333
334
|
}
|
|
334
335
|
const cs = this.presence.diff(before, this.presence.view());
|
|
335
|
-
cs.suggest
|
|
336
|
+
appendAll(cs.suggest, suggest);
|
|
336
337
|
// A lost toolset gets its note, not also a "closed" line.
|
|
337
338
|
cs.changes = cs.changes.filter((c) => !lost.some((o) => keyOf(o.toolset, o.instance) === c.key));
|
|
338
339
|
const told = this.presence.adoptionNotes(lost, ended);
|
|
339
|
-
cs.notes
|
|
340
|
+
appendAll(cs.notes, told.notes);
|
|
340
341
|
if (told.ended.length) cs.ended = [...(cs.ended ?? []), ...told.ended];
|
|
341
342
|
return cs;
|
|
342
343
|
}
|
|
@@ -625,7 +626,7 @@ export class ToolsetController {
|
|
|
625
626
|
const moved = this.presence.applyMoves(moves);
|
|
626
627
|
if (!moved.length) return;
|
|
627
628
|
const cs = emptyChange();
|
|
628
|
-
cs.notes
|
|
629
|
+
appendAll(cs.notes, moved.map(({ name, to }) => to === 'dynamic'
|
|
629
630
|
? `${nameToTypeBack(name)} left your tool list: it's announced when available, and called with ${META_TOOL_NAMES.act} {server:"${this.serverId}", tool:${quotedValue(name)}, args}.`
|
|
630
631
|
: `${nameToTypeBack(name)} is in your tool list now, as ${nameToTypeBack(this.deps.listedName(this.serverId, name))}.`));
|
|
631
632
|
this.deliver(cs);
|
|
@@ -32,6 +32,7 @@ import { keyOf, type ChangeSet, type ToolPresence } from './tool-presence.js';
|
|
|
32
32
|
import { checkArgs, jsonText, renderToolDefinition, type HintedTool } from './tool-signature.js';
|
|
33
33
|
import { McplRequestError } from './server-connection.js';
|
|
34
34
|
import { BUILTIN_TOOLSET, type HostPresentation } from '../host-presentation.js';
|
|
35
|
+
import { appendAll } from '../append-all.js';
|
|
35
36
|
|
|
36
37
|
/** The namespace a reminder's stored injection carries. */
|
|
37
38
|
export const TOOLSETS_REMINDER_NAMESPACE = 'mcpl-toolsets';
|
|
@@ -516,7 +517,7 @@ export class ToolsetsModule implements Module {
|
|
|
516
517
|
return said;
|
|
517
518
|
})
|
|
518
519
|
: [];
|
|
519
|
-
lines
|
|
520
|
+
appendAll(lines, unread);
|
|
520
521
|
return lines.length ? textResult(`${result.data}\n${lines.join('\n')}`) : result;
|
|
521
522
|
}
|
|
522
523
|
|