@zackbart/connecta 0.7.7 → 0.7.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/CHANGELOG.md +88 -0
- package/dist/activity.d.ts +1 -1
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js.map +1 -1
- package/dist/call-admission.d.ts +81 -0
- package/dist/call-admission.d.ts.map +1 -0
- package/dist/call-admission.js +339 -0
- package/dist/call-admission.js.map +1 -0
- package/dist/catalog.d.ts +5 -3
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +13 -4
- package/dist/catalog.js.map +1 -1
- package/dist/connectors/api.d.ts +3 -1
- package/dist/connectors/api.d.ts.map +1 -1
- package/dist/connectors/api.js +1 -0
- package/dist/connectors/api.js.map +1 -1
- package/dist/connectors/remote-mcp.d.ts +3 -1
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js +1 -0
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +72 -30
- package/dist/execute.js.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/meta-tools.d.ts +7 -4
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +212 -54
- package/dist/meta-tools.js.map +1 -1
- package/dist/registry.d.ts +37 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +45 -2
- package/dist/registry.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +5 -0
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +55 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +8 -4
- package/dist/ui.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/activity.ts +5 -1
- package/src/call-admission.ts +519 -0
- package/src/catalog.ts +24 -4
- package/src/connectors/api.ts +4 -0
- package/src/connectors/remote-mcp.ts +4 -0
- package/src/execute.ts +83 -35
- package/src/index.ts +14 -1
- package/src/meta-tools.ts +246 -70
- package/src/registry.ts +91 -1
- package/src/server.ts +5 -0
- package/src/types.ts +61 -0
- package/src/ui.ts +8 -4
- package/src/version.ts +1 -1
package/dist/meta-tools.d.ts
CHANGED
|
@@ -194,10 +194,10 @@ export interface SkillArgs {
|
|
|
194
194
|
* supplies a deadline for calls that don't carry one. (execute_code, the
|
|
195
195
|
* optional tenth tool, is registered separately by registerExecuteTool.)
|
|
196
196
|
*
|
|
197
|
-
*
|
|
198
|
-
* passed in: `ConnectaConfig.calls.maxResultBytes
|
|
199
|
-
*
|
|
200
|
-
*
|
|
197
|
+
* Deployment-wide result-size caps are read off the registry view rather than
|
|
198
|
+
* passed in: `ConnectaConfig.calls.maxResultBytes`, its per-connector override,
|
|
199
|
+
* and the independent `calls.maxBatchResultBytes` final-envelope boundary each
|
|
200
|
+
* have one runtime source of truth.
|
|
201
201
|
*/
|
|
202
202
|
export declare function createMetaTools(registry: RegistryView, baseUrl: string, opts?: {
|
|
203
203
|
/** Deadline applied when a call passes no `timeoutMs`. Off when unset. */
|
|
@@ -205,6 +205,8 @@ export declare function createMetaTools(registry: RegistryView, baseUrl: string,
|
|
|
205
205
|
/** Per-connector deadline for the list/search/describe probe fan-out. Default 30_000. */
|
|
206
206
|
probeTimeoutMs?: number;
|
|
207
207
|
activity?: ActivityRequestContext;
|
|
208
|
+
/** Inbound request cancellation shared by direct and batch child calls. */
|
|
209
|
+
requestSignal?: AbortSignal;
|
|
208
210
|
/** Runtime continuation for the bounded tail of probe-owned teardown. */
|
|
209
211
|
defer?: DeferredWork;
|
|
210
212
|
}): {
|
|
@@ -224,6 +226,7 @@ export declare function registerMetaTools(server: McpServer, registry: RegistryV
|
|
|
224
226
|
defaultToolTimeoutMs?: number;
|
|
225
227
|
probeTimeoutMs?: number;
|
|
226
228
|
activity?: ActivityRequestContext;
|
|
229
|
+
requestSignal?: AbortSignal;
|
|
227
230
|
defer?: DeferredWork;
|
|
228
231
|
}): void;
|
|
229
232
|
export {};
|
package/dist/meta-tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meta-tools.d.ts","sourceRoot":"","sources":["../src/meta-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,eAAe,CAAC;AACvB,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"meta-tools.d.ts","sourceRoot":"","sources":["../src/meta-tools.ts"],"names":[],"mappings":"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"}
|
package/dist/meta-tools.js
CHANGED
|
@@ -4,6 +4,7 @@ import { recordToolActivity, } from "./activity.js";
|
|
|
4
4
|
import { closeConnectorScope, } from "./connector-scope.js";
|
|
5
5
|
import { unwrapMcpResult } from "./mcp-result.js";
|
|
6
6
|
import { classifyCallError, ConnectorCallError, messageLooksRetryable, } from "./errors.js";
|
|
7
|
+
import { isCallAdmissionError } from "./call-admission.js";
|
|
7
8
|
import { isValidMaxResultBytes, MIN_MAX_RESULT_BYTES, resolveMaxResultBytes, } from "./registry.js";
|
|
8
9
|
import { connectorGuide, connectorSkillName, hasConnectorGuides, listSkills, resolveSkill, } from "./skills.js";
|
|
9
10
|
import { DEFAULT_PROBE_TIMEOUT_MS, normalizeTimeoutMs, withAbortableTimeout, } from "./timeout.js";
|
|
@@ -148,6 +149,17 @@ export function retryBackoffMs(attempt, retryAfterMs) {
|
|
|
148
149
|
function errorDetails(code, message) {
|
|
149
150
|
return { code, message, retryable: messageLooksRetryable(message) };
|
|
150
151
|
}
|
|
152
|
+
function callerCancelledDetails() {
|
|
153
|
+
return {
|
|
154
|
+
code: "cancelled",
|
|
155
|
+
message: "Tool call was cancelled by the caller.",
|
|
156
|
+
retryable: false,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function isCallerCancellation(error, signal) {
|
|
160
|
+
return (signal?.aborted === true ||
|
|
161
|
+
(isCallAdmissionError(error) && error.admissionKind === "cancelled"));
|
|
162
|
+
}
|
|
151
163
|
/** True if `b` is a UTF-8 continuation byte (0b10xxxxxx). */
|
|
152
164
|
function isContinuationByte(b) {
|
|
153
165
|
return (b & 0xc0) === 0x80;
|
|
@@ -301,6 +313,15 @@ async function stashResult(text, results, totalBytes) {
|
|
|
301
313
|
hint: "use get_result {id, offset} to page, or re-call with fields to select less",
|
|
302
314
|
};
|
|
303
315
|
}
|
|
316
|
+
/** Keep an oversized batch's inline outcome summary at fixed string overhead. */
|
|
317
|
+
function batchSummaryString(value) {
|
|
318
|
+
const bytes = enc.encode(value);
|
|
319
|
+
const maxBytes = 512;
|
|
320
|
+
if (bytes.length <= maxBytes)
|
|
321
|
+
return value;
|
|
322
|
+
const end = alignEndToCharBoundary(bytes, 0, maxBytes, bytes.length);
|
|
323
|
+
return `${dec.decode(bytes.slice(0, end))}…`;
|
|
324
|
+
}
|
|
304
325
|
/**
|
|
305
326
|
* Return `text` as a single content block; if it exceeds `cap` bytes, stash the
|
|
306
327
|
* full text and return the first `cap` bytes followed by a JSON truncation
|
|
@@ -382,14 +403,15 @@ async function guardContent(content, results, cap) {
|
|
|
382
403
|
* supplies a deadline for calls that don't carry one. (execute_code, the
|
|
383
404
|
* optional tenth tool, is registered separately by registerExecuteTool.)
|
|
384
405
|
*
|
|
385
|
-
*
|
|
386
|
-
* passed in: `ConnectaConfig.calls.maxResultBytes
|
|
387
|
-
*
|
|
388
|
-
*
|
|
406
|
+
* Deployment-wide result-size caps are read off the registry view rather than
|
|
407
|
+
* passed in: `ConnectaConfig.calls.maxResultBytes`, its per-connector override,
|
|
408
|
+
* and the independent `calls.maxBatchResultBytes` final-envelope boundary each
|
|
409
|
+
* have one runtime source of truth.
|
|
389
410
|
*/
|
|
390
411
|
export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
391
412
|
// Already normalized and warned about at registry construction.
|
|
392
413
|
const globalCap = registry.maxResultBytes;
|
|
414
|
+
const batchCap = registry.maxBatchResultBytes;
|
|
393
415
|
const defaultToolTimeoutMs = normalizeTimeoutMs(opts.defaultToolTimeoutMs);
|
|
394
416
|
const probeTimeoutMs = normalizeTimeoutMs(opts.probeTimeoutMs) ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
395
417
|
// createMetaTools() is called once per inbound MCP request. Sharing this
|
|
@@ -401,12 +423,14 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
401
423
|
async function runCall(call, source, options = {}) {
|
|
402
424
|
const started = Date.now();
|
|
403
425
|
let catalogMs = 0;
|
|
426
|
+
let admissionMs = 0;
|
|
404
427
|
let connectorMs = 0;
|
|
405
428
|
let backoffMs = 0;
|
|
406
429
|
let resultProcessingMs = 0;
|
|
407
430
|
let attempts = 0;
|
|
408
431
|
const timing = () => ({
|
|
409
432
|
catalogMs,
|
|
433
|
+
admissionMs,
|
|
410
434
|
connectorMs,
|
|
411
435
|
backoffMs,
|
|
412
436
|
resultProcessingMs,
|
|
@@ -430,7 +454,11 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
430
454
|
const failed = (error) => {
|
|
431
455
|
const durationMs = Date.now() - started;
|
|
432
456
|
const diagnostics = timing();
|
|
433
|
-
record(error.code === "timeout"
|
|
457
|
+
record(error.code === "timeout"
|
|
458
|
+
? "timeout"
|
|
459
|
+
: error.code === "cancelled"
|
|
460
|
+
? "cancelled"
|
|
461
|
+
: "error", error.code);
|
|
434
462
|
return {
|
|
435
463
|
toolResult: call.resultMode === "value"
|
|
436
464
|
? jsonResult({
|
|
@@ -470,6 +498,9 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
470
498
|
}
|
|
471
499
|
catch (err) {
|
|
472
500
|
catalogMs += Date.now() - catalogStarted;
|
|
501
|
+
if (opts.requestSignal?.aborted) {
|
|
502
|
+
return failed(callerCancelledDetails());
|
|
503
|
+
}
|
|
473
504
|
// A connector whose catalog cannot be fetched is as unusable as one whose
|
|
474
505
|
// execution fails, so it feeds health accounting the same way the
|
|
475
506
|
// execution catch below does — otherwise a connector every call_tool
|
|
@@ -502,56 +533,132 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
502
533
|
let result;
|
|
503
534
|
while (true) {
|
|
504
535
|
attempts++;
|
|
505
|
-
|
|
536
|
+
let permit;
|
|
537
|
+
const controller = timeoutMs || opts.requestSignal ? new AbortController() : undefined;
|
|
538
|
+
const forwardAbort = () => controller?.abort(opts.requestSignal?.reason);
|
|
539
|
+
if (opts.requestSignal?.aborted)
|
|
540
|
+
forwardAbort();
|
|
541
|
+
else {
|
|
542
|
+
opts.requestSignal?.addEventListener("abort", forwardAbort, {
|
|
543
|
+
once: true,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
506
546
|
let timer;
|
|
507
|
-
|
|
508
|
-
|
|
547
|
+
let onAbort;
|
|
548
|
+
let attemptFailed = false;
|
|
549
|
+
let attemptError;
|
|
509
550
|
try {
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
551
|
+
const admissionStarted = Date.now();
|
|
552
|
+
try {
|
|
553
|
+
permit = await registry.admitCall(resolved.connector.id, {
|
|
554
|
+
toolName: resolved.toolName,
|
|
555
|
+
args: call.args ?? {},
|
|
556
|
+
signal: opts.requestSignal,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
finally {
|
|
560
|
+
admissionMs += Date.now() - admissionStarted;
|
|
561
|
+
}
|
|
562
|
+
const ctx = registry.contextFor(resolved.connector.id, baseUrl, requestScope, { signal: controller?.signal, timeoutMs });
|
|
563
|
+
let rejectCancelled;
|
|
564
|
+
const cancelled = controller
|
|
565
|
+
? new Promise((_, reject) => {
|
|
566
|
+
rejectCancelled = reject;
|
|
567
|
+
})
|
|
568
|
+
: undefined;
|
|
569
|
+
onAbort = () => {
|
|
570
|
+
rejectCancelled(controller?.signal.reason ??
|
|
571
|
+
new ConnectorCallError("timeout", "Tool call was cancelled"));
|
|
572
|
+
};
|
|
573
|
+
controller?.signal.addEventListener("abort", onAbort, { once: true });
|
|
574
|
+
if (controller?.signal.aborted)
|
|
575
|
+
onAbort();
|
|
576
|
+
if (controller?.signal.aborted)
|
|
577
|
+
await cancelled;
|
|
578
|
+
if (timeoutMs) {
|
|
579
|
+
timer = setTimeout(() => {
|
|
580
|
+
controller?.abort(new ConnectorCallError("timeout", `Tool call timed out after ${timeoutMs}ms`));
|
|
581
|
+
}, timeoutMs);
|
|
582
|
+
}
|
|
583
|
+
const connectorStarted = Date.now();
|
|
584
|
+
try {
|
|
585
|
+
const pending = resolved.connector.callTool(resolved.toolName, call.args ?? {}, ctx);
|
|
586
|
+
result = cancelled
|
|
587
|
+
? await Promise.race([pending, cancelled])
|
|
588
|
+
: await pending;
|
|
589
|
+
}
|
|
590
|
+
finally {
|
|
591
|
+
connectorMs += Date.now() - connectorStarted;
|
|
592
|
+
}
|
|
523
593
|
const mcpResult = result;
|
|
524
594
|
if (resolved.connector.kind === "mcp" && mcpResult?.isError) {
|
|
525
595
|
throw new Error(mcpResult.content?.map((block) => block.text).join("") ||
|
|
526
596
|
"Downstream tool call failed");
|
|
527
597
|
}
|
|
528
|
-
break;
|
|
529
598
|
}
|
|
530
599
|
catch (err) {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
600
|
+
attemptFailed = true;
|
|
601
|
+
attemptError = err;
|
|
602
|
+
}
|
|
603
|
+
finally {
|
|
604
|
+
if (timer)
|
|
605
|
+
clearTimeout(timer);
|
|
606
|
+
if (onAbort) {
|
|
607
|
+
controller?.signal.removeEventListener("abort", onAbort);
|
|
608
|
+
}
|
|
609
|
+
opts.requestSignal?.removeEventListener("abort", forwardAbort);
|
|
610
|
+
permit?.release();
|
|
611
|
+
}
|
|
612
|
+
if (attemptFailed) {
|
|
613
|
+
const callerCancelled = isCallerCancellation(attemptError, opts.requestSignal);
|
|
614
|
+
const details = callerCancelled
|
|
615
|
+
? callerCancelledDetails()
|
|
616
|
+
: classifyCallError(attemptError);
|
|
617
|
+
if (!callerCancelled &&
|
|
618
|
+
attempts <= maxRetries &&
|
|
619
|
+
retrySafe &&
|
|
620
|
+
details.retryable) {
|
|
535
621
|
const wait = retryBackoffMs(attempts, details.retryAfterMs);
|
|
536
622
|
if (wait !== undefined) {
|
|
537
623
|
const backoffStarted = Date.now();
|
|
538
624
|
if (wait > 0) {
|
|
539
|
-
await new Promise((resolve) =>
|
|
625
|
+
const completed = await new Promise((resolve) => {
|
|
626
|
+
let settled = false;
|
|
627
|
+
const finish = (value) => {
|
|
628
|
+
if (settled)
|
|
629
|
+
return;
|
|
630
|
+
settled = true;
|
|
631
|
+
clearTimeout(timer);
|
|
632
|
+
opts.requestSignal?.removeEventListener("abort", cancel);
|
|
633
|
+
resolve(value);
|
|
634
|
+
};
|
|
635
|
+
const timer = setTimeout(() => finish(true), wait);
|
|
636
|
+
const cancel = () => finish(false);
|
|
637
|
+
opts.requestSignal?.addEventListener("abort", cancel, {
|
|
638
|
+
once: true,
|
|
639
|
+
});
|
|
640
|
+
if (opts.requestSignal?.aborted)
|
|
641
|
+
cancel();
|
|
642
|
+
});
|
|
643
|
+
backoffMs += Date.now() - backoffStarted;
|
|
644
|
+
if (!completed)
|
|
645
|
+
return failed(callerCancelledDetails());
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
backoffMs += Date.now() - backoffStarted;
|
|
540
649
|
}
|
|
541
|
-
backoffMs += Date.now() - backoffStarted;
|
|
542
650
|
continue;
|
|
543
651
|
}
|
|
544
652
|
// The reported window is longer than the engine will park a
|
|
545
653
|
// synchronous request for. Fall through to failure with
|
|
546
654
|
// retryAfterMs reported verbatim so the agent can re-issue.
|
|
547
655
|
}
|
|
548
|
-
|
|
656
|
+
if (!callerCancelled && !isCallAdmissionError(attemptError)) {
|
|
657
|
+
registry.recordFailure(resolved.connector.id, Date.now() - started, attemptError);
|
|
658
|
+
}
|
|
549
659
|
return failed(details);
|
|
550
660
|
}
|
|
551
|
-
|
|
552
|
-
if (timer)
|
|
553
|
-
clearTimeout(timer);
|
|
554
|
-
}
|
|
661
|
+
break;
|
|
555
662
|
}
|
|
556
663
|
registry.recordSuccess(resolved.connector.id, Date.now() - started);
|
|
557
664
|
const processingStarted = Date.now();
|
|
@@ -816,26 +923,36 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
816
923
|
: registry.listConnectors();
|
|
817
924
|
const matches = [];
|
|
818
925
|
const catalogs = await Promise.allSettled(conns.map((c) => withProbeDeadline(`search_tools probe of "${c.id}"`, (options) => registry.getTools(c.id, baseUrl, requestScope, options))));
|
|
819
|
-
let
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
:
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
926
|
+
let matchMode = "all";
|
|
927
|
+
const collectMatches = (mode) => {
|
|
928
|
+
matches.length = 0;
|
|
929
|
+
let orderBase = 0;
|
|
930
|
+
catalogs.forEach((catalog, connectorIndex) => {
|
|
931
|
+
const c = conns[connectorIndex];
|
|
932
|
+
if (catalog.status === "fulfilled") {
|
|
933
|
+
for (const ranked of rankTools(catalog.value, q, mode)) {
|
|
934
|
+
matches.push({
|
|
935
|
+
connectorId: c.id,
|
|
936
|
+
connectorTitle: c.title,
|
|
937
|
+
connectorDescription: c.description,
|
|
938
|
+
...(connectorGuide(c)
|
|
939
|
+
? { connectorGuideSkill: connectorSkillName(c.id) }
|
|
940
|
+
: {}),
|
|
941
|
+
tool: ranked.tool,
|
|
942
|
+
score: ranked.score,
|
|
943
|
+
order: orderBase + ranked.order,
|
|
944
|
+
});
|
|
945
|
+
}
|
|
835
946
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
947
|
+
orderBase +=
|
|
948
|
+
catalog.status === "fulfilled" ? catalog.value.length : 1;
|
|
949
|
+
});
|
|
950
|
+
};
|
|
951
|
+
collectMatches("all");
|
|
952
|
+
if (q.trim() && matches.length === 0) {
|
|
953
|
+
matchMode = "partial";
|
|
954
|
+
collectMatches(matchMode);
|
|
955
|
+
}
|
|
839
956
|
matches.sort((a, b) => b.score - a.score || a.order - b.order);
|
|
840
957
|
const page = matches.slice(offset, offset + limit);
|
|
841
958
|
const groups = [];
|
|
@@ -889,6 +1006,9 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
889
1006
|
limit,
|
|
890
1007
|
hasMore: nextOffset !== undefined,
|
|
891
1008
|
...(nextOffset !== undefined ? { nextOffset } : {}),
|
|
1009
|
+
...(matchMode === "partial" && matches.length > 0
|
|
1010
|
+
? { matchMode }
|
|
1011
|
+
: {}),
|
|
892
1012
|
}, "Request a smaller limit, omit fullDescriptions, or use compact schemas.");
|
|
893
1013
|
},
|
|
894
1014
|
async describeTools(args) {
|
|
@@ -1065,9 +1185,46 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
1065
1185
|
: {}),
|
|
1066
1186
|
};
|
|
1067
1187
|
});
|
|
1068
|
-
|
|
1188
|
+
const envelope = {
|
|
1069
1189
|
results,
|
|
1070
1190
|
durationMs: Date.now() - batchStarted,
|
|
1191
|
+
};
|
|
1192
|
+
const text = serializeResultText(envelope);
|
|
1193
|
+
const bytes = enc.encode(text);
|
|
1194
|
+
if (bytes.length <= batchCap)
|
|
1195
|
+
return jsonResult(envelope);
|
|
1196
|
+
const notice = await stashResult(text, registry.resultsStorage(), bytes.length);
|
|
1197
|
+
return jsonResult({
|
|
1198
|
+
results: results.map((result) => {
|
|
1199
|
+
const common = {
|
|
1200
|
+
address: batchSummaryString(result.address),
|
|
1201
|
+
ok: !("error" in result),
|
|
1202
|
+
...("durationMs" in result
|
|
1203
|
+
? { durationMs: result.durationMs }
|
|
1204
|
+
: {}),
|
|
1205
|
+
...("attempts" in result ? { attempts: result.attempts } : {}),
|
|
1206
|
+
...("timing" in result ? { timing: result.timing } : {}),
|
|
1207
|
+
};
|
|
1208
|
+
if (!("error" in result))
|
|
1209
|
+
return common;
|
|
1210
|
+
const error = result.error ?? "Batch call failed";
|
|
1211
|
+
const details = result.errorDetails ??
|
|
1212
|
+
errorDetails("batch_call_failed", error);
|
|
1213
|
+
return {
|
|
1214
|
+
...common,
|
|
1215
|
+
error: batchSummaryString(error),
|
|
1216
|
+
errorDetails: {
|
|
1217
|
+
code: batchSummaryString(details.code),
|
|
1218
|
+
message: batchSummaryString(details.message),
|
|
1219
|
+
retryable: details.retryable,
|
|
1220
|
+
...(details.retryAfterMs !== undefined
|
|
1221
|
+
? { retryAfterMs: details.retryAfterMs }
|
|
1222
|
+
: {}),
|
|
1223
|
+
},
|
|
1224
|
+
};
|
|
1225
|
+
}),
|
|
1226
|
+
durationMs: envelope.durationMs,
|
|
1227
|
+
...notice,
|
|
1071
1228
|
});
|
|
1072
1229
|
},
|
|
1073
1230
|
async authorizeConnector(args) {
|
|
@@ -1125,7 +1282,7 @@ const DESCRIBE_DESC = `Inspect up to ${MAX_DESCRIBE_ADDRESSES} known tool addres
|
|
|
1125
1282
|
const CALL_DESC = 'Use for one tool explicitly annotated readOnlyHint: true. For 2–10 independent read-only calls use batch_call; for dependent steps or data reduction use execute_code when available. Unannotated, write-capable, and destructive tools are refused and require call_destructive_tool. fields selects JSON dot-paths, resultMode "value" unwraps results, timeoutMs sets a deadline, safe maxRetries are annotation-gated, diagnostics adds timing, and large results page through get_result.';
|
|
1126
1283
|
const CALL_DESTRUCTIVE_DESC = "Invoke any tool that is not explicitly annotated readOnlyHint: true, including unannotated, write-capable, or destructive tools. The MCP destructiveHint on this meta-tool lets the host request human approval before execution. Use only after reviewing the downstream tool schema and consequences.";
|
|
1127
1284
|
const GET_RESULT_DESC = "Page a truncated result stashed by call_tool/batch_call. Input { id, offset?, maxBytes? } → { text, offset, nextOffset?, totalBytes } sliced by byte offset. maxBytes is a whole number of bytes >= 1 (omit for the deployment default) and offset a whole number of bytes >= 0; an offset inside a multi-byte character is moved back to that character's first byte and the offset served is returned. Unknown/expired id is an error.";
|
|
1128
|
-
const BATCH_DESC = "Use for 2–10 independent tools explicitly annotated readOnlyHint: true. Calls run in parallel with shared request-scoped clients; use execute_code when available instead for dependencies or in-sandbox reduction. Unannotated, write-capable, and destructive tools are refused. Batch timeout, safe retry, result mode, and diagnostics defaults may be overridden per call.";
|
|
1285
|
+
const BATCH_DESC = "Use for 2–10 independent tools explicitly annotated readOnlyHint: true. Calls run in parallel with shared request-scoped clients; use execute_code when available instead for dependencies or in-sandbox reduction. Unannotated, write-capable, and destructive tools are refused. Batch timeout, safe retry, result mode, and diagnostics defaults may be overridden per call. An oversized final envelope returns ordered outcome summaries plus a get_result page handle.";
|
|
1129
1286
|
const AUTHORIZE_DESC = "Use after a connector reports auth_required. Starts downstream OAuth and returns an authorizationUrl for the operator to open. force=true wipes stored credentials first and restarts consent.";
|
|
1130
1287
|
const SKILLS_DESC = 'List or fetch concise guidance for choosing among Connecta meta-tools. Call skills({ name: "usage" }) once when the routing workflow is unfamiliar; do not refetch it in the same task.';
|
|
1131
1288
|
/**
|
|
@@ -1174,6 +1331,7 @@ export function registerMetaTools(server, registry, ctx) {
|
|
|
1174
1331
|
defaultToolTimeoutMs: ctx.defaultToolTimeoutMs,
|
|
1175
1332
|
probeTimeoutMs: ctx.probeTimeoutMs,
|
|
1176
1333
|
activity: ctx.activity,
|
|
1334
|
+
requestSignal: ctx.requestSignal,
|
|
1177
1335
|
defer: ctx.defer,
|
|
1178
1336
|
});
|
|
1179
1337
|
server.registerTool("skills", {
|