@getmarrow/mcp 3.9.85 → 3.9.86
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/package.json
CHANGED
|
@@ -16,14 +16,24 @@ try {
|
|
|
16
16
|
|
|
17
17
|
const CANARY_TOOL_TIMEOUT_MARGIN_MS = 2_000;
|
|
18
18
|
const CANARY_TOOL_TIMEOUT_MAX_MS = 10_000;
|
|
19
|
+
const CANARY_ASYNC_TOOL_TIMEOUT_MAX_MS = 30_000;
|
|
19
20
|
const CANARY_DEFAULT_TOOL_TIMEOUT_MS = Math.min(
|
|
20
21
|
CANARY_TOOL_TIMEOUT_MAX_MS,
|
|
21
22
|
(MARROW_AUTO_RESPONSE_BUDGET_MAX_MS || 8_000) + CANARY_TOOL_TIMEOUT_MARGIN_MS,
|
|
22
23
|
);
|
|
24
|
+
const CANARY_DEFAULT_ASYNC_TOOL_TIMEOUT_MS = Math.min(
|
|
25
|
+
CANARY_ASYNC_TOOL_TIMEOUT_MAX_MS,
|
|
26
|
+
(MARROW_AUTO_RESPONSE_BUDGET_MAX_MS || 8_000) + CANARY_TOOL_TIMEOUT_MARGIN_MS,
|
|
27
|
+
);
|
|
28
|
+
const CANARY_DEFAULT_TOTAL_TIMEOUT_MS = 45_000;
|
|
29
|
+
const TRANSPORT_RETRY_WAIT_MS = 1_000;
|
|
23
30
|
|
|
24
31
|
if (CANARY_DEFAULT_TOOL_TIMEOUT_MS < MARROW_AUTO_RESPONSE_BUDGET_MAX_MS + CANARY_TOOL_TIMEOUT_MARGIN_MS) {
|
|
25
32
|
configurationFailed = true;
|
|
26
33
|
}
|
|
34
|
+
if (CANARY_DEFAULT_ASYNC_TOOL_TIMEOUT_MS < MARROW_AUTO_RESPONSE_BUDGET_MAX_MS + CANARY_TOOL_TIMEOUT_MARGIN_MS) {
|
|
35
|
+
configurationFailed = true;
|
|
36
|
+
}
|
|
27
37
|
|
|
28
38
|
function canaryCases(operationId) {
|
|
29
39
|
return [
|
|
@@ -56,6 +66,15 @@ const HOT_PATH_TOOLS = new Set([
|
|
|
56
66
|
'marrow_agent_runtime',
|
|
57
67
|
'marrow_auto',
|
|
58
68
|
]);
|
|
69
|
+
const ASYNC_PATH_TOOLS = new Set([
|
|
70
|
+
'marrow_auto',
|
|
71
|
+
'marrow_first_value',
|
|
72
|
+
]);
|
|
73
|
+
const TRANSPORT_RETRY_ERROR_CLASSES = new Set(['tool_unavailable', 'unavailable503']);
|
|
74
|
+
const AUTO_TRANSPORT_RETRY_CATEGORIES = new Set([
|
|
75
|
+
'request_failed', 'service_unavailable', 'connection_reset', 'dns_unavailable',
|
|
76
|
+
'tls_failure', 'edge_access_denied', 'rate_limited',
|
|
77
|
+
]);
|
|
59
78
|
const OUTPUT_LIMIT_BYTES = 256 * 1024;
|
|
60
79
|
const TOOL_NAMES = new Set(canaryCases('').map(([name]) => name));
|
|
61
80
|
const errorEvidence = new WeakMap();
|
|
@@ -542,7 +561,14 @@ async function executeCanary(env, options, context) {
|
|
|
542
561
|
CANARY_TOOL_TIMEOUT_MAX_MS,
|
|
543
562
|
env,
|
|
544
563
|
);
|
|
545
|
-
const
|
|
564
|
+
const asyncToolTimeoutMs = boundedMs(
|
|
565
|
+
'MARROW_MCP_CANARY_ASYNC_TOOL_TIMEOUT_MS',
|
|
566
|
+
CANARY_DEFAULT_ASYNC_TOOL_TIMEOUT_MS,
|
|
567
|
+
250,
|
|
568
|
+
CANARY_ASYNC_TOOL_TIMEOUT_MAX_MS,
|
|
569
|
+
env,
|
|
570
|
+
);
|
|
571
|
+
const totalTimeoutMs = boundedMs('MARROW_MCP_CANARY_TOTAL_TIMEOUT_MS', CANARY_DEFAULT_TOTAL_TIMEOUT_MS, 2_000, 60_000, env);
|
|
546
572
|
const expectedVersion = env.MARROW_EXPECTED_MCP_VERSION || packageVersion;
|
|
547
573
|
const childPath = env.MARROW_MCP_CANARY_CHILD || resolve(__dirname, '../dist/cli.js');
|
|
548
574
|
const childEnv = {
|
|
@@ -598,9 +624,11 @@ async function executeCanary(env, options, context) {
|
|
|
598
624
|
const results = context.results;
|
|
599
625
|
for (const [name, args] of cases) {
|
|
600
626
|
const callStarted = performance.now();
|
|
627
|
+
const perToolTimeoutMs = ASYNC_PATH_TOOLS.has(name) ? asyncToolTimeoutMs : toolTimeoutMs;
|
|
601
628
|
let called;
|
|
602
629
|
let payload;
|
|
603
630
|
let attempts = 0;
|
|
631
|
+
let recoveredOnRetry = false;
|
|
604
632
|
context.retryWaitMs = 0;
|
|
605
633
|
context.autoMetrics = undefined;
|
|
606
634
|
context.autoPhase = undefined;
|
|
@@ -613,13 +641,13 @@ async function executeCanary(env, options, context) {
|
|
|
613
641
|
stage('tool_call', name, attempt + 1);
|
|
614
642
|
attempts += 1;
|
|
615
643
|
try {
|
|
616
|
-
called = await client.request('tools/call', { name, arguments: args });
|
|
644
|
+
called = await client.request('tools/call', { name, arguments: args }, perToolTimeoutMs);
|
|
617
645
|
if (client.fatalError) throw client.fatalError;
|
|
618
646
|
stage('tool_validate', name, attempt + 1);
|
|
619
647
|
payload = toolPayload(called, name);
|
|
620
648
|
} catch (error) {
|
|
649
|
+
const evidence = errorEvidence.get(error);
|
|
621
650
|
if (name === 'marrow_auto') {
|
|
622
|
-
const evidence = errorEvidence.get(error);
|
|
623
651
|
autoAttemptRecords.push({
|
|
624
652
|
attempt: attempt + 1,
|
|
625
653
|
duration_ms: safeMs(performance.now() - outerAttemptStarted),
|
|
@@ -631,6 +659,17 @@ async function executeCanary(env, options, context) {
|
|
|
631
659
|
actual_wait_ms: 0,
|
|
632
660
|
});
|
|
633
661
|
}
|
|
662
|
+
// One bounded in-run retry for transport-class failures: a single edge
|
|
663
|
+
// reset or 503 must not fail the canary when the very next call succeeds.
|
|
664
|
+
const remaining = totalTimeoutMs - (performance.now() - context.started);
|
|
665
|
+
const mayRetry = name === 'marrow_auto' ? attempt < 2 : attempt < 1;
|
|
666
|
+
if (evidence && TRANSPORT_RETRY_ERROR_CLASSES.has(evidence.error_class)
|
|
667
|
+
&& mayRetry && remaining > TRANSPORT_RETRY_WAIT_MS + 250) {
|
|
668
|
+
await new Promise((resolveDelay) => setTimeout(resolveDelay, TRANSPORT_RETRY_WAIT_MS));
|
|
669
|
+
context.retryWaitMs += TRANSPORT_RETRY_WAIT_MS;
|
|
670
|
+
recoveredOnRetry = true;
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
634
673
|
throw error;
|
|
635
674
|
}
|
|
636
675
|
if (name !== 'marrow_auto') break;
|
|
@@ -643,11 +682,17 @@ async function executeCanary(env, options, context) {
|
|
|
643
682
|
context.autoMetrics = autoTimings(payload);
|
|
644
683
|
const waitClass = autoWaitClass(payload);
|
|
645
684
|
context.autoPhase = waitClass ? payload.phase : undefined;
|
|
646
|
-
|
|
685
|
+
const autoFailureCategory = payload?.live_delivery?.failure?.error?.category;
|
|
686
|
+
const transportRetryable = waitClass == null
|
|
687
|
+
&& AUTO_TRANSPORT_RETRY_CATEGORIES.has(autoFailureCategory);
|
|
688
|
+
if (waitClass !== 'completion_pending' && !transportRetryable) break;
|
|
647
689
|
if (attempt < 2) {
|
|
648
690
|
const requested = payload.retry_after_ms;
|
|
649
|
-
const
|
|
650
|
-
? Math.ceil(requested) :
|
|
691
|
+
const serverWaitMs = typeof requested === 'number' && Number.isFinite(requested) && requested >= 0
|
|
692
|
+
? Math.ceil(requested) : null;
|
|
693
|
+
const delayMs = transportRetryable
|
|
694
|
+
? Math.max(TRANSPORT_RETRY_WAIT_MS, serverWaitMs ?? 0)
|
|
695
|
+
: serverWaitMs ?? 250;
|
|
651
696
|
const remaining = totalTimeoutMs - (performance.now() - context.started);
|
|
652
697
|
if (delayMs >= remaining) break;
|
|
653
698
|
const waitStarted = performance.now();
|
|
@@ -655,6 +700,7 @@ async function executeCanary(env, options, context) {
|
|
|
655
700
|
const actualWaitMs = Math.round(performance.now() - waitStarted);
|
|
656
701
|
outerAttemptRecord.actual_wait_ms = actualWaitMs;
|
|
657
702
|
context.retryWaitMs += actualWaitMs;
|
|
703
|
+
if (transportRetryable) recoveredOnRetry = true;
|
|
658
704
|
}
|
|
659
705
|
}
|
|
660
706
|
const latencyMs = Math.round(performance.now() - callStarted);
|
|
@@ -662,6 +708,7 @@ async function executeCanary(env, options, context) {
|
|
|
662
708
|
const live = payload.stale !== true && payload.source !== 'last_known' && payload.available !== false;
|
|
663
709
|
if (!live) throw canaryError('contract', `${name} returned cached or unavailable guidance`);
|
|
664
710
|
results.push({ tool: name, ok: true, live: true, latency_ms: latencyMs,
|
|
711
|
+
...(recoveredOnRetry ? { recovered_on_retry: true } : {}),
|
|
665
712
|
...(name === 'marrow_auto' ? {
|
|
666
713
|
...autoTimings(payload),
|
|
667
714
|
attempts,
|
|
@@ -709,7 +756,10 @@ if (require.main === module) {
|
|
|
709
756
|
|
|
710
757
|
module.exports = {
|
|
711
758
|
CANARY_DEFAULT_TOOL_TIMEOUT_MS,
|
|
759
|
+
CANARY_DEFAULT_ASYNC_TOOL_TIMEOUT_MS,
|
|
760
|
+
CANARY_DEFAULT_TOTAL_TIMEOUT_MS,
|
|
712
761
|
CANARY_TOOL_TIMEOUT_MARGIN_MS,
|
|
762
|
+
CANARY_ASYNC_TOOL_TIMEOUT_MAX_MS,
|
|
713
763
|
MARROW_AUTO_RESPONSE_BUDGET_MAX_MS,
|
|
714
764
|
PersistentMcpClient,
|
|
715
765
|
latencyGroup,
|