@zackbart/connecta 0.18.3 → 0.19.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/CHANGELOG.md +73 -4
- package/dist/catalog-service.d.ts +16 -13
- package/dist/catalog-service.js +106 -115
- package/dist/catalog.js +29 -46
- package/dist/connector-scope.js +2 -7
- package/dist/connectors/api.d.ts +4 -16
- package/dist/connectors/api.js +19 -46
- package/dist/connectors/guarded-fetch.d.ts +9 -23
- package/dist/connectors/guarded-fetch.js +38 -76
- package/dist/connectors/remote-mcp.js +36 -79
- package/dist/errors.d.ts +6 -27
- package/dist/errors.js +8 -5
- package/dist/execute.d.ts +24 -22
- package/dist/execute.js +98 -145
- package/dist/executor-result.d.ts +1 -0
- package/dist/executor-result.js +4 -11
- package/dist/executors/quickjs-child.js +1 -3
- package/dist/executors/quickjs-runtime.js +1 -3
- package/dist/executors/quickjs.js +1 -3
- package/dist/index.js +27 -57
- package/dist/invocation.js +113 -183
- package/dist/meta-tools.d.ts +15 -28
- package/dist/meta-tools.js +33 -89
- package/dist/providers/cloudflare.d.ts +2 -18
- package/dist/providers/cloudflare.js +1460 -2451
- package/dist/providers/linear.d.ts +4 -41
- package/dist/providers/linear.js +8 -39
- package/dist/providers/mixpanel.d.ts +3 -25
- package/dist/providers/mixpanel.js +7 -22
- package/dist/providers/notion.d.ts +1 -15
- package/dist/providers/notion.js +44 -173
- package/dist/providers/revenuecat.d.ts +4 -57
- package/dist/providers/revenuecat.js +10 -93
- package/dist/providers/stripe.d.ts +1 -12
- package/dist/providers/stripe.js +7 -45
- package/dist/registry.d.ts +9 -34
- package/dist/registry.js +9 -103
- package/dist/routes/mcp.js +1 -1
- package/dist/routes/oauth.js +3 -3
- package/dist/routes/shared.d.ts +15 -15
- package/dist/routes/shared.js +1 -3
- package/dist/timeout.d.ts +8 -7
- package/dist/timeout.js +47 -38
- package/dist/types.d.ts +3 -3
- package/dist/ui.d.ts +1 -25
- package/dist/ui.js +18 -45
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/call-admission.md +1 -1
- package/documentation/cloudflare.md +1 -1
- package/documentation/code-mode.md +7 -7
- package/documentation/connectors.md +24 -1
- package/documentation/linear.md +1 -1
- package/documentation/mixpanel.md +1 -1
- package/documentation/notion.md +1 -1
- package/documentation/operations.md +18 -14
- package/documentation/provider-conventions.md +1 -1
- package/documentation/revenuecat.md +1 -1
- package/documentation/stripe.md +1 -1
- package/documentation/upgrading.md +13 -4
- package/ethos.md +75 -121
- package/package.json +3 -4
- package/templates/node/package.json +1 -1
- package/documentation/code-first-exploration.md +0 -292
- package/documentation/mcp-2026-07-28.md +0 -46
- package/documentation/mcp-ui-design.md +0 -382
- package/documentation/program-ui-read-calls.md +0 -213
- package/documentation/provider-audit.md +0 -198
- package/documentation/rich-output-design.md +0 -211
package/dist/invocation.js
CHANGED
|
@@ -4,7 +4,20 @@ import { classifyCallError, ConnectorCallError, echoedCallArgs, framingError, }
|
|
|
4
4
|
import { unwrapMcpResult } from "./mcp-result.js";
|
|
5
5
|
import { splitAddress } from "./registry.js";
|
|
6
6
|
import { isExplicitlyReadOnly } from "./tool-safety.js";
|
|
7
|
+
import { sleep, withDeadline } from "./timeout.js";
|
|
7
8
|
import { validateToolInput } from "./validate.js";
|
|
9
|
+
function defined(values) {
|
|
10
|
+
return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));
|
|
11
|
+
}
|
|
12
|
+
async function timed(bucket, fn) {
|
|
13
|
+
const started = Date.now();
|
|
14
|
+
try {
|
|
15
|
+
return await fn();
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
bucket(Date.now() - started);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
8
21
|
/**
|
|
9
22
|
* The longest the engine will park a synchronous inbound request in *waiting
|
|
10
23
|
* alone*. The engine already treats ~15 s as the outer bound of one reasonable
|
|
@@ -109,18 +122,16 @@ export class InvocationService {
|
|
|
109
122
|
this.activity = activity;
|
|
110
123
|
}
|
|
111
124
|
async invoke(address, args, context) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
: {}));
|
|
125
|
+
const options = defined({ signal: context.requestSignal });
|
|
126
|
+
return this.invokeWithResolution(address, args, context, () => this.catalog.resolveTool(address, options));
|
|
115
127
|
}
|
|
116
128
|
/**
|
|
117
129
|
* Code-mode namespace dispatch preserves JavaScript-safe tool aliases while
|
|
118
130
|
* still feeding the resolved catalog entry through the one invocation path.
|
|
119
131
|
*/
|
|
120
132
|
async invokeToolAlias(connectorId, toolAlias, aliasFor, args, context) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
: {}));
|
|
133
|
+
const options = defined({ signal: context.requestSignal });
|
|
134
|
+
return this.invokeWithResolution(`${connectorId}.${toolAlias}`, args, context, () => this.catalog.resolveToolAlias(connectorId, toolAlias, aliasFor, options));
|
|
124
135
|
}
|
|
125
136
|
async invokeWithResolution(address, args, context, resolve) {
|
|
126
137
|
const started = Date.now();
|
|
@@ -161,37 +172,35 @@ export class InvocationService {
|
|
|
161
172
|
outcome,
|
|
162
173
|
durationMs: Date.now() - started,
|
|
163
174
|
attempts,
|
|
164
|
-
...(
|
|
165
|
-
|
|
166
|
-
:
|
|
167
|
-
|
|
168
|
-
? { friction: classification.friction }
|
|
169
|
-
: {}),
|
|
175
|
+
...defined({
|
|
176
|
+
errorCode: classification.errorCode,
|
|
177
|
+
friction: classification.friction,
|
|
178
|
+
}),
|
|
170
179
|
});
|
|
171
180
|
};
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
181
|
+
const enrich = (error, target) => {
|
|
182
|
+
if (!target)
|
|
183
|
+
return error;
|
|
184
|
+
switch (error.code) {
|
|
185
|
+
case "destructive_tool_requires_approval": {
|
|
186
|
+
const echoed = echoedCallArgs(args);
|
|
187
|
+
return {
|
|
188
|
+
...error,
|
|
189
|
+
nextAction: {
|
|
190
|
+
tool: "call_destructive_tool",
|
|
191
|
+
arguments: {
|
|
192
|
+
address: `${target.connector.id}.${target.toolName}`,
|
|
193
|
+
...echoed,
|
|
194
|
+
},
|
|
195
|
+
purpose: "Ask the MCP host to approve this consequential call. " +
|
|
196
|
+
("args" in echoed
|
|
197
|
+
? "Re-send these arguments and add a short reason for the human reviewer."
|
|
198
|
+
: "Re-send the arguments you just sent — they are too large to echo back — and add a short reason for the human reviewer."),
|
|
186
199
|
},
|
|
187
|
-
|
|
188
|
-
("args" in echoed
|
|
189
|
-
? "Re-send these arguments and add a short reason for the human reviewer."
|
|
190
|
-
: "Re-send the arguments you just sent — they are too large to echo back — and add a short reason for the human reviewer."),
|
|
191
|
-
},
|
|
200
|
+
};
|
|
192
201
|
}
|
|
193
|
-
|
|
194
|
-
|
|
202
|
+
case "auth_required":
|
|
203
|
+
return {
|
|
195
204
|
...error,
|
|
196
205
|
connector: target.connector.id,
|
|
197
206
|
operation: `${target.connector.id}.${target.toolName}`,
|
|
@@ -203,20 +212,28 @@ export class InvocationService {
|
|
|
203
212
|
},
|
|
204
213
|
retry: `Retry ${target.connector.id}.${target.toolName} after ` +
|
|
205
214
|
"the operator completes recovery.",
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
215
|
+
};
|
|
216
|
+
case "invalid_args":
|
|
217
|
+
if (!error.validation)
|
|
218
|
+
return error;
|
|
219
|
+
return {
|
|
220
|
+
...error,
|
|
221
|
+
connector: target.connector.id,
|
|
222
|
+
operation: `${target.connector.id}.${target.toolName}`,
|
|
223
|
+
nextAction: this.catalog.searchRecovery({
|
|
224
|
+
query: target.toolName,
|
|
210
225
|
connector: target.connector.id,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
226
|
+
}, "Inspect the current input shape if the validation findings are not sufficient."),
|
|
227
|
+
retry: `Correct the listed arguments and retry ` +
|
|
228
|
+
`${target.connector.id}.${target.toolName}.`,
|
|
229
|
+
};
|
|
230
|
+
default:
|
|
231
|
+
return error;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const failed = (error) => {
|
|
235
|
+
const diagnostics = timing();
|
|
236
|
+
const details = enrich(error, resolved ?? activityTarget);
|
|
220
237
|
record(details.code === "timeout"
|
|
221
238
|
? "timeout"
|
|
222
239
|
: details.code === "cancelled"
|
|
@@ -227,7 +244,7 @@ export class InvocationService {
|
|
|
227
244
|
durationMs: Date.now() - started,
|
|
228
245
|
attempts,
|
|
229
246
|
timing: diagnostics,
|
|
230
|
-
...(
|
|
247
|
+
...defined({ resolved }),
|
|
231
248
|
error: details,
|
|
232
249
|
};
|
|
233
250
|
};
|
|
@@ -240,21 +257,8 @@ export class InvocationService {
|
|
|
240
257
|
toolName: resolution.toolName,
|
|
241
258
|
};
|
|
242
259
|
}
|
|
243
|
-
if (resolution.cause &&
|
|
244
|
-
|
|
245
|
-
return failed(callerCancelledDetails());
|
|
246
|
-
}
|
|
247
|
-
// A connector whose catalog cannot be fetched is as unusable as one
|
|
248
|
-
// whose execution fails, so it feeds health accounting the same way the
|
|
249
|
-
// attempt catch below does — otherwise a connector every call fails
|
|
250
|
-
// against (a revoked downstream grant, say) still reads clean in the
|
|
251
|
-
// deployment's health log.
|
|
252
|
-
//
|
|
253
|
-
// Recorded HERE rather than inside the registry's catalog fetch because
|
|
254
|
-
// a cache hit that avoids a live listTools call records nothing — it is
|
|
255
|
-
// not evidence of health. Success stays what it has always been: an
|
|
256
|
-
// actual downstream call that returned.
|
|
257
|
-
this.registry.recordFailure(resolution.connector.id, Date.now() - started, resolution.cause);
|
|
260
|
+
if (resolution.cause && context.requestSignal?.aborted) {
|
|
261
|
+
return failed(callerCancelledDetails());
|
|
258
262
|
}
|
|
259
263
|
return failed(resolution.error);
|
|
260
264
|
}
|
|
@@ -292,95 +296,49 @@ export class InvocationService {
|
|
|
292
296
|
while (true) {
|
|
293
297
|
attempts++;
|
|
294
298
|
let permit;
|
|
295
|
-
const controller = context.timeoutMs || context.requestSignal
|
|
296
|
-
? new AbortController()
|
|
297
|
-
: undefined;
|
|
298
|
-
const forwardAbort = () => controller?.abort(context.requestSignal?.reason);
|
|
299
|
-
if (context.requestSignal?.aborted)
|
|
300
|
-
forwardAbort();
|
|
301
|
-
else {
|
|
302
|
-
context.requestSignal?.addEventListener("abort", forwardAbort, {
|
|
303
|
-
once: true,
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
let timer;
|
|
307
|
-
let onAbort;
|
|
308
299
|
let attemptError;
|
|
309
300
|
let attemptFailed = false;
|
|
310
301
|
try {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
302
|
+
permit = await timed((elapsed) => { admissionMs += elapsed; }, () => this.registry.admitCall(resolved.connector.id, {
|
|
303
|
+
toolName: resolved.toolName,
|
|
304
|
+
args: args ?? {},
|
|
305
|
+
...defined({ signal: context.requestSignal }),
|
|
306
|
+
}));
|
|
307
|
+
const raw = await timed((elapsed) => { connectorMs += elapsed; }, () => {
|
|
308
|
+
const call = (callSignal) => {
|
|
309
|
+
const connectorContext = this.registry.contextFor(resolved.connector.id, this.catalog.baseUrl, this.catalog.requestScope, defined({ signal: callSignal, timeoutMs: context.timeoutMs }));
|
|
310
|
+
if (resolved.connector.credential &&
|
|
311
|
+
!connectorContext.credential) {
|
|
312
|
+
throw new ConnectorCallError("auth_required", "Operator-managed credential storage is not configured. Call " +
|
|
313
|
+
`authorize_connector({ connector: "${resolved.connector.id}" }).`);
|
|
314
|
+
}
|
|
315
|
+
// Cancellation can arrive during admission or context construction.
|
|
316
|
+
if (callSignal?.aborted)
|
|
317
|
+
throw callSignal.reason;
|
|
318
|
+
return resolved.connector.callTool(resolved.toolName, args ?? {}, connectorContext);
|
|
319
|
+
};
|
|
320
|
+
if (!context.timeoutMs && !context.requestSignal)
|
|
321
|
+
return call();
|
|
322
|
+
return withDeadline(call, {
|
|
323
|
+
...defined({
|
|
324
|
+
timeoutMs: context.timeoutMs,
|
|
325
|
+
signal: context.requestSignal,
|
|
326
|
+
}),
|
|
327
|
+
timeoutError: new ConnectorCallError("timeout", `Tool call timed out after ${context.timeoutMs}ms`),
|
|
319
328
|
});
|
|
320
|
-
}
|
|
321
|
-
finally {
|
|
322
|
-
admissionMs += Date.now() - admissionStarted;
|
|
323
|
-
}
|
|
324
|
-
const connectorContext = this.registry.contextFor(resolved.connector.id, this.catalog.baseUrl, this.catalog.requestScope, {
|
|
325
|
-
...(controller?.signal !== undefined
|
|
326
|
-
? { signal: controller.signal }
|
|
327
|
-
: {}),
|
|
328
|
-
...(context.timeoutMs !== undefined
|
|
329
|
-
? { timeoutMs: context.timeoutMs }
|
|
330
|
-
: {}),
|
|
331
329
|
});
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
? new Promise((_, reject) => {
|
|
339
|
-
rejectCancelled = reject;
|
|
340
|
-
})
|
|
341
|
-
: undefined;
|
|
342
|
-
onAbort = () => {
|
|
343
|
-
rejectCancelled(controller?.signal.reason ??
|
|
344
|
-
new ConnectorCallError("timeout", "Tool call was cancelled"));
|
|
345
|
-
};
|
|
346
|
-
controller?.signal.addEventListener("abort", onAbort, { once: true });
|
|
347
|
-
if (controller?.signal.aborted)
|
|
348
|
-
onAbort();
|
|
349
|
-
if (controller?.signal.aborted)
|
|
350
|
-
await cancelled;
|
|
351
|
-
if (context.timeoutMs) {
|
|
352
|
-
timer = setTimeout(() => {
|
|
353
|
-
controller?.abort(new ConnectorCallError("timeout", `Tool call timed out after ${context.timeoutMs}ms`));
|
|
354
|
-
}, context.timeoutMs);
|
|
355
|
-
}
|
|
356
|
-
const connectorStarted = Date.now();
|
|
357
|
-
try {
|
|
358
|
-
const pending = resolved.connector.callTool(resolved.toolName, args ?? {}, connectorContext);
|
|
359
|
-
const raw = cancelled
|
|
360
|
-
? await Promise.race([pending, cancelled])
|
|
361
|
-
: await pending;
|
|
362
|
-
// isError is checked here for BOTH result shapes so every adapter
|
|
363
|
-
// reports the same downstream-failure wording, and the throw lands
|
|
364
|
-
// inside the attempt where it stays retry-eligible and feeds health.
|
|
365
|
-
assertRawMcpSuccess(resolved.connector.kind, raw);
|
|
366
|
-
observedResult = unwrapMcpResult(resolved.connector.kind, raw);
|
|
367
|
-
result = context.unwrapResult ? observedResult : raw;
|
|
368
|
-
}
|
|
369
|
-
finally {
|
|
370
|
-
connectorMs += Date.now() - connectorStarted;
|
|
371
|
-
}
|
|
330
|
+
// isError is checked here for BOTH result shapes so every adapter
|
|
331
|
+
// reports the same downstream-failure wording, and the throw lands
|
|
332
|
+
// inside the attempt where it stays retry-eligible and feeds health.
|
|
333
|
+
assertRawMcpSuccess(resolved.connector.kind, raw);
|
|
334
|
+
observedResult = unwrapMcpResult(resolved.connector.kind, raw);
|
|
335
|
+
result = context.unwrapResult ? observedResult : raw;
|
|
372
336
|
}
|
|
373
337
|
catch (error) {
|
|
374
338
|
attemptFailed = true;
|
|
375
339
|
attemptError = error;
|
|
376
340
|
}
|
|
377
341
|
finally {
|
|
378
|
-
if (timer)
|
|
379
|
-
clearTimeout(timer);
|
|
380
|
-
if (onAbort) {
|
|
381
|
-
controller?.signal.removeEventListener("abort", onAbort);
|
|
382
|
-
}
|
|
383
|
-
context.requestSignal?.removeEventListener("abort", forwardAbort);
|
|
384
342
|
permit?.release();
|
|
385
343
|
}
|
|
386
344
|
if (attemptFailed) {
|
|
@@ -394,59 +352,32 @@ export class InvocationService {
|
|
|
394
352
|
details.retryable) {
|
|
395
353
|
const wait = retryBackoffMs(attempts, details.retryAfterMs);
|
|
396
354
|
if (wait !== undefined) {
|
|
397
|
-
const
|
|
398
|
-
if (
|
|
399
|
-
|
|
400
|
-
let settled = false;
|
|
401
|
-
const finish = (value) => {
|
|
402
|
-
if (settled)
|
|
403
|
-
return;
|
|
404
|
-
settled = true;
|
|
405
|
-
clearTimeout(timer);
|
|
406
|
-
context.requestSignal?.removeEventListener("abort", cancel);
|
|
407
|
-
resolve(value);
|
|
408
|
-
};
|
|
409
|
-
const timer = setTimeout(() => finish(true), wait);
|
|
410
|
-
const cancel = () => finish(false);
|
|
411
|
-
context.requestSignal?.addEventListener("abort", cancel, {
|
|
412
|
-
once: true,
|
|
413
|
-
});
|
|
414
|
-
if (context.requestSignal?.aborted)
|
|
415
|
-
cancel();
|
|
416
|
-
});
|
|
417
|
-
backoffMs += Date.now() - backoffStarted;
|
|
418
|
-
if (!completed)
|
|
419
|
-
return failed(callerCancelledDetails());
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
backoffMs += Date.now() - backoffStarted;
|
|
423
|
-
}
|
|
355
|
+
const completed = await timed((elapsed) => { backoffMs += elapsed; }, () => sleep(wait, context.requestSignal));
|
|
356
|
+
if (!completed)
|
|
357
|
+
return failed(callerCancelledDetails());
|
|
424
358
|
continue;
|
|
425
359
|
}
|
|
426
360
|
// The reported window is longer than the engine will park a
|
|
427
361
|
// synchronous request for. Fall through to failure with
|
|
428
362
|
// retryAfterMs reported verbatim so the agent can re-issue.
|
|
429
363
|
}
|
|
430
|
-
if (!callerCancelled && !isCallAdmissionError(attemptError)) {
|
|
431
|
-
this.registry.recordFailure(resolved.connector.id, Date.now() - started, attemptError);
|
|
432
|
-
}
|
|
433
364
|
return failed(details);
|
|
434
365
|
}
|
|
435
366
|
break;
|
|
436
367
|
}
|
|
437
|
-
this.registry.recordSuccess(resolved.connector.id, Date.now() - started);
|
|
438
|
-
const processingStarted = Date.now();
|
|
439
368
|
try {
|
|
440
|
-
const value =
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
369
|
+
const value = await timed((elapsed) => { resultProcessingMs += elapsed; }, async () => {
|
|
370
|
+
const processed = context.processResult
|
|
371
|
+
? await context.processResult(result, resolved)
|
|
372
|
+
: result;
|
|
373
|
+
try {
|
|
374
|
+
this.registry.observeOutputShape(resolved.connector.id, resolved.definition, observedResult);
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
// Shape learning is advisory. It cannot change a completed call.
|
|
378
|
+
}
|
|
379
|
+
return processed;
|
|
380
|
+
});
|
|
450
381
|
const diagnostics = timing();
|
|
451
382
|
const friction = context.activityFriction?.(value);
|
|
452
383
|
record("success", friction ? { friction } : {});
|
|
@@ -460,7 +391,6 @@ export class InvocationService {
|
|
|
460
391
|
};
|
|
461
392
|
}
|
|
462
393
|
catch (error) {
|
|
463
|
-
resultProcessingMs += Date.now() - processingStarted;
|
|
464
394
|
return failed(framingError("result_processing_failed", error instanceof Error ? error.message : String(error)));
|
|
465
395
|
}
|
|
466
396
|
}
|
package/dist/meta-tools.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface ToolResult {
|
|
|
15
15
|
structuredContent?: Record<string, unknown>;
|
|
16
16
|
[x: string]: unknown;
|
|
17
17
|
}
|
|
18
|
-
export declare function jsonResult(obj: unknown): ToolResult;
|
|
18
|
+
export declare function jsonResult(obj: unknown, text?: string): ToolResult;
|
|
19
19
|
export declare function errorResult(message: string): ToolResult;
|
|
20
20
|
/**
|
|
21
21
|
* Move a byte `offset` back to the nearest UTF-8 codepoint boundary in
|
|
@@ -30,21 +30,8 @@ export declare function errorResult(message: string): ToolResult;
|
|
|
30
30
|
* character there to split.
|
|
31
31
|
*/
|
|
32
32
|
export declare function alignStartToCharBoundary(bytes: Uint8Array, offset: number): number;
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
-
* `(offset, total]`, so decoding `bytes[offset, end)` never splits a codepoint
|
|
36
|
-
* (which would emit U+FFFD and break byte-exact reassembly). If backing up
|
|
37
|
-
* would make no progress — a single codepoint wider than the window — extend
|
|
38
|
-
* forward to the end of that codepoint instead so paging always advances.
|
|
39
|
-
* Assumes `offset` is itself a codepoint boundary (offsets are the prior
|
|
40
|
-
* `nextOffset`, which this function guarantees, and 0 is always a boundary).
|
|
41
|
-
*
|
|
42
|
-
* The return is always `> offset` while `offset < total`, whatever `end` is
|
|
43
|
-
* asked for. That is the belt-and-braces half of issue #32: cap validation
|
|
44
|
-
* keeps an empty window from arising in the first place, and this keeps an
|
|
45
|
-
* empty window from turning into a `nextOffset === offset` paging loop if one
|
|
46
|
-
* ever does. Exported for direct testing of that invariant.
|
|
47
|
-
*/
|
|
33
|
+
/** End boundary for UTF-8-safe, forward-progressing result pages. See
|
|
34
|
+
* documentation/meta-tools.md#result-representation. */
|
|
48
35
|
export declare function alignEndToCharBoundary(bytes: Uint8Array, offset: number, end: number, total: number): number;
|
|
49
36
|
export interface SearchArgs {
|
|
50
37
|
query?: string;
|
|
@@ -105,16 +92,16 @@ export interface SkillArgs {
|
|
|
105
92
|
*/
|
|
106
93
|
export declare function createMetaTools(registry: RegistryView, baseUrl: string, opts?: {
|
|
107
94
|
/** Deadline applied when a call passes no `timeoutMs`. Off when unset. */
|
|
108
|
-
defaultToolTimeoutMs?: number;
|
|
95
|
+
defaultToolTimeoutMs?: number | undefined;
|
|
109
96
|
/** Per-connector deadline for the search/describe probe fan-out. Default 30_000. */
|
|
110
|
-
probeTimeoutMs?: number;
|
|
97
|
+
probeTimeoutMs?: number | undefined;
|
|
111
98
|
/** Maximum simultaneous connector discovery operations. Default 4. */
|
|
112
|
-
discoveryConcurrency?: number;
|
|
113
|
-
activity?: ActivityRequestContext;
|
|
99
|
+
discoveryConcurrency?: number | undefined;
|
|
100
|
+
activity?: ActivityRequestContext | undefined;
|
|
114
101
|
/** Inbound request cancellation shared by every call this request makes. */
|
|
115
|
-
requestSignal?: AbortSignal;
|
|
102
|
+
requestSignal?: AbortSignal | undefined;
|
|
116
103
|
/** Runtime-owned tail for stale catalog refreshes. */
|
|
117
|
-
defer?: DeferredWork;
|
|
104
|
+
defer?: DeferredWork | undefined;
|
|
118
105
|
}): {
|
|
119
106
|
skills(args?: SkillArgs): Promise<ToolResult>;
|
|
120
107
|
searchTools(args: SearchArgs): Promise<ToolResult>;
|
|
@@ -133,10 +120,10 @@ export declare function createMetaTools(registry: RegistryView, baseUrl: string,
|
|
|
133
120
|
*/
|
|
134
121
|
export declare function registerMetaTools(server: McpServer, registry: RegistryView, ctx: {
|
|
135
122
|
baseUrl: string;
|
|
136
|
-
defaultToolTimeoutMs?: number;
|
|
137
|
-
probeTimeoutMs?: number;
|
|
138
|
-
discoveryConcurrency?: number;
|
|
139
|
-
activity?: ActivityRequestContext;
|
|
140
|
-
requestSignal?: AbortSignal;
|
|
141
|
-
defer?: DeferredWork;
|
|
123
|
+
defaultToolTimeoutMs?: number | undefined;
|
|
124
|
+
probeTimeoutMs?: number | undefined;
|
|
125
|
+
discoveryConcurrency?: number | undefined;
|
|
126
|
+
activity?: ActivityRequestContext | undefined;
|
|
127
|
+
requestSignal?: AbortSignal | undefined;
|
|
128
|
+
defer?: DeferredWork | undefined;
|
|
142
129
|
}): void;
|