@zackbart/connecta 0.22.3 → 0.24.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/AGENTS.md +5 -0
- package/CHANGELOG.md +82 -0
- package/README.md +19 -11
- package/dist/activity-friction.d.ts +3 -0
- package/dist/activity-friction.js +19 -0
- package/dist/activity.d.ts +11 -2
- package/dist/activity.js +15 -19
- package/dist/auth/downstream-oauth.d.ts +2 -1
- package/dist/auth/downstream-oauth.js +10 -1
- package/dist/branding.d.ts +67 -0
- package/dist/branding.js +176 -0
- package/dist/catalog-service.d.ts +1 -6
- package/dist/catalog-service.js +3 -53
- package/dist/connectors/remote-mcp.js +3 -5
- package/dist/credential-contract.d.ts +24 -0
- package/dist/credential-contract.js +1 -0
- package/dist/credential-rules.d.ts +85 -0
- package/dist/credential-rules.js +107 -0
- package/dist/credentials.d.ts +4 -100
- package/dist/credentials.js +3 -107
- package/dist/errors.d.ts +1 -1
- package/dist/execute.d.ts +5 -52
- package/dist/execute.js +40 -347
- package/dist/executors/quickjs-protocol.d.ts +0 -7
- package/dist/executors/quickjs-protocol.js +2 -10
- package/dist/executors/quickjs.js +1 -1
- package/dist/index.d.ts +23 -56
- package/dist/index.js +30 -58
- package/dist/invocation.d.ts +0 -33
- package/dist/invocation.js +56 -124
- package/dist/meta-tools.d.ts +7 -6
- package/dist/meta-tools.js +14 -21
- package/dist/module-contracts.d.ts +19 -0
- package/dist/module-contracts.js +1 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +6 -3
- package/dist/operator-ui/view.d.ts +2 -18
- package/dist/operator-ui/view.js +3 -20
- package/dist/registry.d.ts +4 -1
- package/dist/registry.js +8 -6
- package/dist/routes/activity.js +1 -1
- package/dist/routes/credentials.js +5 -2
- package/dist/routes/mcp.js +7 -51
- package/dist/routes/oauth-management.d.ts +2 -0
- package/dist/routes/oauth-management.js +108 -0
- package/dist/routes/oauth.d.ts +0 -1
- package/dist/routes/oauth.js +21 -121
- package/dist/routes/shared.d.ts +19 -17
- package/dist/routes/shared.js +48 -44
- package/dist/routes/ui.js +36 -33
- package/dist/server.d.ts +1 -2
- package/dist/server.js +7 -45
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +55 -19
- package/dist/types.d.ts +3 -15
- package/dist/ui.d.ts +15 -70
- package/dist/ui.js +176 -317
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +31 -26
- package/documentation/auth.md +65 -111
- package/documentation/call-admission.md +10 -11
- package/documentation/cloudflare.md +1 -1
- package/documentation/code-mode.md +65 -260
- package/documentation/connectors.md +8 -6
- package/documentation/linear.md +1 -1
- package/documentation/meta-tools.md +37 -18
- package/documentation/mixpanel.md +1 -1
- package/documentation/notion.md +2 -2
- package/documentation/operations.md +19 -23
- package/documentation/operator-ui.md +82 -104
- package/documentation/optional-modules-upgrade.md +243 -0
- package/documentation/provider-conventions.md +7 -5
- package/documentation/revenuecat.md +1 -1
- package/documentation/storage-and-credentials.md +59 -40
- package/documentation/stripe.md +1 -1
- package/documentation/upgrading.md +83 -8
- package/ethos.md +36 -43
- package/examples/worker/AGENTS.md +3 -1
- package/examples/worker/README.md +68 -84
- package/examples/worker/src/d1-activity.ts +1 -1
- package/examples/worker/src/index.ts +11 -6
- package/package.json +18 -2
- package/templates/node/AGENTS.md +8 -6
- package/templates/node/README.md +56 -67
- package/templates/node/package.json +1 -1
- package/templates/node/src/file-activity.ts +1 -1
- package/templates/node/src/index.ts +11 -12
- package/dist/access-tokens.d.ts +0 -31
- package/dist/access-tokens.js +0 -236
- package/dist/apps-shell.d.ts +0 -37
- package/dist/apps-shell.js +0 -174
- package/dist/routes/access-tokens.d.ts +0 -6
- package/dist/routes/access-tokens.js +0 -83
package/dist/invocation.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { recordToolActivity, } from "./activity.js";
|
|
2
1
|
import { isCallAdmissionError } from "./call-admission.js";
|
|
3
2
|
import { classifyCallError, ConnectorCallError, echoedCallArgs, framingError, } from "./errors.js";
|
|
4
3
|
import { unwrapMcpResult } from "./mcp-result.js";
|
|
5
4
|
import { splitAddress } from "./registry.js";
|
|
6
5
|
import { isExplicitlyReadOnly } from "./tool-safety.js";
|
|
7
|
-
import {
|
|
6
|
+
import { withDeadline } from "./timeout.js";
|
|
8
7
|
import { validateToolInput } from "./validate.js";
|
|
9
8
|
function defined(values) {
|
|
10
9
|
return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));
|
|
@@ -18,40 +17,6 @@ async function timed(bucket, fn) {
|
|
|
18
17
|
bucket(Date.now() - started);
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
|
-
/**
|
|
22
|
-
* The longest the engine will park a synchronous inbound request in *waiting
|
|
23
|
-
* alone*. The engine already treats ~15 s as the outer bound of one reasonable
|
|
24
|
-
* connector call (EXECUTE_HOST_CALL_TIMEOUT_MS), so sleeping for minutes trades
|
|
25
|
-
* a fast, informative failure for a hung one. A connector-reported window this
|
|
26
|
-
* long isn't truncated — it's declined (see `retryBackoffMs`) and reported
|
|
27
|
-
* verbatim as `error.retryAfterMs`, so the agent, which can afford to wait,
|
|
28
|
-
* decides when to re-issue.
|
|
29
|
-
*/
|
|
30
|
-
export const MAX_RETRY_BACKOFF_MS = 10_000;
|
|
31
|
-
/**
|
|
32
|
-
* How long to wait before the next attempt, or `undefined` for "don't retry".
|
|
33
|
-
*
|
|
34
|
-
* A connector that read a `Retry-After` header knows the window exactly, so it
|
|
35
|
-
* is honoured **exactly or not at all**: truncating an exponential *guess* is
|
|
36
|
-
* harmless, but truncating a *known* window means deliberately retrying inside
|
|
37
|
-
* a rate limit — the harm this channel exists to prevent. A window longer than
|
|
38
|
-
* `MAX_RETRY_BACKOFF_MS` therefore declines the retry rather than shortening
|
|
39
|
-
* it. (`retryAfterMs` is normalized non-negative, so `0` means "retry now".)
|
|
40
|
-
* Connectors that report no window keep the historical exponential guess.
|
|
41
|
-
*
|
|
42
|
-
* Waits are per attempt, matching the per-attempt `timeoutMs` race in
|
|
43
|
-
* `InvocationService.invoke`. Exported for direct testing.
|
|
44
|
-
*/
|
|
45
|
-
export function retryBackoffMs(attempt, retryAfterMs) {
|
|
46
|
-
if (retryAfterMs === undefined) {
|
|
47
|
-
return Math.min(250 * 2 ** (attempt - 1), 1_000);
|
|
48
|
-
}
|
|
49
|
-
return retryAfterMs <= MAX_RETRY_BACKOFF_MS ? retryAfterMs : undefined;
|
|
50
|
-
}
|
|
51
|
-
function retrySafe(definition) {
|
|
52
|
-
return (definition.annotations?.readOnlyHint === true ||
|
|
53
|
-
definition.annotations?.idempotentHint === true);
|
|
54
|
-
}
|
|
55
20
|
function callerCancelledDetails() {
|
|
56
21
|
return {
|
|
57
22
|
code: "cancelled",
|
|
@@ -62,7 +27,7 @@ function callerCancelledDetails() {
|
|
|
62
27
|
function recoveryMode(registry, connector, baseUrl) {
|
|
63
28
|
if (connector.startAuth)
|
|
64
29
|
return "oauth";
|
|
65
|
-
if (connector.credential &&
|
|
30
|
+
if (registry.credentialUiAvailable() && connector.credential &&
|
|
66
31
|
registry.contextFor(connector.id, baseUrl).credential) {
|
|
67
32
|
return "operator_config";
|
|
68
33
|
}
|
|
@@ -122,23 +87,10 @@ export class InvocationService {
|
|
|
122
87
|
this.activity = activity;
|
|
123
88
|
}
|
|
124
89
|
async invoke(address, args, context) {
|
|
125
|
-
const options = defined({ signal: context.requestSignal });
|
|
126
|
-
return this.invokeWithResolution(address, args, context, () => this.catalog.resolveTool(address, options));
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Code-mode namespace dispatch preserves JavaScript-safe tool aliases while
|
|
130
|
-
* still feeding the resolved catalog entry through the one invocation path.
|
|
131
|
-
*/
|
|
132
|
-
async invokeToolAlias(connectorId, toolAlias, aliasFor, args, context) {
|
|
133
|
-
const options = defined({ signal: context.requestSignal });
|
|
134
|
-
return this.invokeWithResolution(`${connectorId}.${toolAlias}`, args, context, () => this.catalog.resolveToolAlias(connectorId, toolAlias, aliasFor, options));
|
|
135
|
-
}
|
|
136
|
-
async invokeWithResolution(address, args, context, resolve) {
|
|
137
90
|
const started = Date.now();
|
|
138
91
|
let catalogMs = 0;
|
|
139
92
|
let admissionMs = 0;
|
|
140
93
|
let connectorMs = 0;
|
|
141
|
-
let backoffMs = 0;
|
|
142
94
|
let resultProcessingMs = 0;
|
|
143
95
|
let attempts = 0;
|
|
144
96
|
let resolved;
|
|
@@ -151,7 +103,6 @@ export class InvocationService {
|
|
|
151
103
|
catalogMs,
|
|
152
104
|
admissionMs,
|
|
153
105
|
connectorMs,
|
|
154
|
-
backoffMs,
|
|
155
106
|
resultProcessingMs,
|
|
156
107
|
totalMs: Date.now() - started,
|
|
157
108
|
});
|
|
@@ -164,7 +115,7 @@ export class InvocationService {
|
|
|
164
115
|
: attempted;
|
|
165
116
|
if (!identity)
|
|
166
117
|
return;
|
|
167
|
-
|
|
118
|
+
this.activity?.recordTool?.(this.activity, {
|
|
168
119
|
connectorId: identity.connectorId,
|
|
169
120
|
toolName: identity.toolName,
|
|
170
121
|
address: `${identity.connectorId}.${identity.toolName}`,
|
|
@@ -248,7 +199,7 @@ export class InvocationService {
|
|
|
248
199
|
error: details,
|
|
249
200
|
};
|
|
250
201
|
};
|
|
251
|
-
const resolution = await
|
|
202
|
+
const resolution = await this.catalog.resolveTool(address, defined({ signal: context.requestSignal }));
|
|
252
203
|
catalogMs += resolution.catalogMs;
|
|
253
204
|
if (!resolution.ok) {
|
|
254
205
|
if (resolution.connector && resolution.toolName) {
|
|
@@ -290,80 +241,61 @@ export class InvocationService {
|
|
|
290
241
|
? error.details
|
|
291
242
|
: classifyCallError(error));
|
|
292
243
|
}
|
|
293
|
-
const maxRetries = Math.min(2, Math.max(0, Math.trunc(context.maxRetries ?? 0)));
|
|
294
244
|
let result;
|
|
295
245
|
let observedResult;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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`),
|
|
328
|
-
});
|
|
329
|
-
});
|
|
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;
|
|
336
|
-
}
|
|
337
|
-
catch (error) {
|
|
338
|
-
attemptFailed = true;
|
|
339
|
-
attemptError = error;
|
|
340
|
-
}
|
|
341
|
-
finally {
|
|
342
|
-
permit?.release();
|
|
343
|
-
}
|
|
344
|
-
if (attemptFailed) {
|
|
345
|
-
const callerCancelled = isCallerCancellation(attemptError, context.requestSignal);
|
|
346
|
-
const details = callerCancelled
|
|
347
|
-
? callerCancelledDetails()
|
|
348
|
-
: classifyCallError(attemptError);
|
|
349
|
-
if (!callerCancelled &&
|
|
350
|
-
attempts <= maxRetries &&
|
|
351
|
-
retrySafe(resolved.definition) &&
|
|
352
|
-
details.retryable) {
|
|
353
|
-
const wait = retryBackoffMs(attempts, details.retryAfterMs);
|
|
354
|
-
if (wait !== undefined) {
|
|
355
|
-
const completed = await timed((elapsed) => { backoffMs += elapsed; }, () => sleep(wait, context.requestSignal));
|
|
356
|
-
if (!completed)
|
|
357
|
-
return failed(callerCancelledDetails());
|
|
358
|
-
continue;
|
|
246
|
+
attempts = 1;
|
|
247
|
+
let permit;
|
|
248
|
+
let attemptError;
|
|
249
|
+
let attemptFailed = false;
|
|
250
|
+
try {
|
|
251
|
+
permit = await timed((elapsed) => { admissionMs += elapsed; }, () => this.registry.admitCall(resolved.connector.id, {
|
|
252
|
+
toolName: resolved.toolName,
|
|
253
|
+
args: args ?? {},
|
|
254
|
+
...defined({ signal: context.requestSignal }),
|
|
255
|
+
}));
|
|
256
|
+
const raw = await timed((elapsed) => { connectorMs += elapsed; }, () => {
|
|
257
|
+
const call = (callSignal) => {
|
|
258
|
+
const connectorContext = this.registry.contextFor(resolved.connector.id, this.catalog.baseUrl, this.catalog.requestScope, defined({ signal: callSignal, timeoutMs: context.timeoutMs }));
|
|
259
|
+
if (resolved.connector.credential &&
|
|
260
|
+
!connectorContext.credential) {
|
|
261
|
+
throw new ConnectorCallError("auth_required", "Operator-managed credential storage is not configured. Call " +
|
|
262
|
+
`authorize_connector({ connector: "${resolved.connector.id}" }).`);
|
|
359
263
|
}
|
|
360
|
-
//
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
264
|
+
// Cancellation can arrive during admission or context construction.
|
|
265
|
+
if (callSignal?.aborted)
|
|
266
|
+
throw callSignal.reason;
|
|
267
|
+
return resolved.connector.callTool(resolved.toolName, args ?? {}, connectorContext);
|
|
268
|
+
};
|
|
269
|
+
if (!context.timeoutMs && !context.requestSignal)
|
|
270
|
+
return call();
|
|
271
|
+
return withDeadline(call, {
|
|
272
|
+
...defined({
|
|
273
|
+
timeoutMs: context.timeoutMs,
|
|
274
|
+
signal: context.requestSignal,
|
|
275
|
+
}),
|
|
276
|
+
timeoutError: new ConnectorCallError("timeout", `Tool call timed out after ${context.timeoutMs}ms`),
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
// isError is checked here for BOTH result shapes so every adapter
|
|
280
|
+
// reports the same downstream-failure wording, and the throw lands
|
|
281
|
+
// inside the attempt where it feeds health.
|
|
282
|
+
assertRawMcpSuccess(resolved.connector.kind, raw);
|
|
283
|
+
observedResult = unwrapMcpResult(resolved.connector.kind, raw);
|
|
284
|
+
result = context.unwrapResult ? observedResult : raw;
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
attemptFailed = true;
|
|
288
|
+
attemptError = error;
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
permit?.release();
|
|
292
|
+
}
|
|
293
|
+
if (attemptFailed) {
|
|
294
|
+
const callerCancelled = isCallerCancellation(attemptError, context.requestSignal);
|
|
295
|
+
const details = callerCancelled
|
|
296
|
+
? callerCancelledDetails()
|
|
297
|
+
: classifyCallError(attemptError);
|
|
298
|
+
return failed(details);
|
|
367
299
|
}
|
|
368
300
|
try {
|
|
369
301
|
const value = await timed((elapsed) => { resultProcessingMs += elapsed; }, async () => {
|
package/dist/meta-tools.d.ts
CHANGED
|
@@ -2,9 +2,8 @@ import type { McpServer } from "@modelcontextprotocol/server";
|
|
|
2
2
|
import type { ActivityRequestContext } from "./activity.js";
|
|
3
3
|
import { MAX_DESCRIBE_ADDRESSES, MAX_DISCOVERY_RESULT_BYTES, MAX_SEARCH_LIMIT } from "./catalog-service.js";
|
|
4
4
|
import type { DeferredWork } from "./connector-scope.js";
|
|
5
|
-
import { MAX_RETRY_BACKOFF_MS, retryBackoffMs } from "./invocation.js";
|
|
6
5
|
import { type RegistryView } from "./registry.js";
|
|
7
|
-
export { MAX_DESCRIBE_ADDRESSES, MAX_DISCOVERY_RESULT_BYTES,
|
|
6
|
+
export { MAX_DESCRIBE_ADDRESSES, MAX_DISCOVERY_RESULT_BYTES, MAX_SEARCH_LIMIT, };
|
|
8
7
|
interface TextContent {
|
|
9
8
|
type: "text";
|
|
10
9
|
text: string;
|
|
@@ -48,8 +47,6 @@ export interface CallArgs {
|
|
|
48
47
|
args?: Record<string, unknown>;
|
|
49
48
|
resultMode?: ResultMode;
|
|
50
49
|
timeoutMs?: number;
|
|
51
|
-
/** Retries after the first attempt; honored only for safely annotated tools. */
|
|
52
|
-
maxRetries?: number;
|
|
53
50
|
/** Include connector/catalog/result-processing timing segments. */
|
|
54
51
|
diagnostics?: boolean;
|
|
55
52
|
}
|
|
@@ -97,6 +94,8 @@ export declare function createMetaTools(registry: RegistryView, baseUrl: string,
|
|
|
97
94
|
/** Maximum simultaneous connector discovery operations. Default 4. */
|
|
98
95
|
discoveryConcurrency?: number | undefined;
|
|
99
96
|
activity?: ActivityRequestContext | undefined;
|
|
97
|
+
canManageAuth?: ((id: string) => boolean) | undefined;
|
|
98
|
+
credentialHandoffUrl?: string | undefined;
|
|
100
99
|
/** Inbound request cancellation shared by every call this request makes. */
|
|
101
100
|
requestSignal?: AbortSignal | undefined;
|
|
102
101
|
/** Runtime-owned tail for stale catalog refreshes. */
|
|
@@ -112,8 +111,8 @@ export declare function createMetaTools(registry: RegistryView, baseUrl: string,
|
|
|
112
111
|
/**
|
|
113
112
|
* Register the six explicit meta-tools onto an McpServer instance.
|
|
114
113
|
* `registerExecuteTool` adds the seventh, `execute_code`. Broad discovery and
|
|
115
|
-
* multi-call work
|
|
116
|
-
*
|
|
114
|
+
* multi-call work uses discovery and ordinary JavaScript promises inside a
|
|
115
|
+
* program, which `execute_code` builds over the same
|
|
117
116
|
* `CatalogService` and `InvocationService` these handlers use — one shared
|
|
118
117
|
* services layer, two adapters above it.
|
|
119
118
|
*/
|
|
@@ -123,6 +122,8 @@ export declare function registerMetaTools(server: McpServer, registry: RegistryV
|
|
|
123
122
|
probeTimeoutMs?: number | undefined;
|
|
124
123
|
discoveryConcurrency?: number | undefined;
|
|
125
124
|
activity?: ActivityRequestContext | undefined;
|
|
125
|
+
canManageAuth?: ((id: string) => boolean) | undefined;
|
|
126
|
+
credentialHandoffUrl?: string | undefined;
|
|
126
127
|
requestSignal?: AbortSignal | undefined;
|
|
127
128
|
defer?: DeferredWork | undefined;
|
|
128
129
|
}): void;
|
package/dist/meta-tools.js
CHANGED
|
@@ -3,11 +3,11 @@ import { boundedDiscoveryText, CatalogService, DEFAULT_SEARCH_LIMIT, DiscoveryPo
|
|
|
3
3
|
import { resolveDiscoveryConcurrency } from "./concurrency.js";
|
|
4
4
|
import { msg } from "./errors.js";
|
|
5
5
|
import { serializeResultText } from "./executor-result.js";
|
|
6
|
-
import { InvocationService,
|
|
6
|
+
import { InvocationService, } from "./invocation.js";
|
|
7
7
|
import { isValidMaxResultBytes, MIN_MAX_RESULT_BYTES, resolveMaxResultBytes, } from "./registry.js";
|
|
8
8
|
import { hasConnectorGuides, listSkills, resolveSkill, } from "./skills.js";
|
|
9
9
|
import { DEFAULT_PROBE_TIMEOUT_MS, normalizeTimeoutMs, } from "./timeout.js";
|
|
10
|
-
export { MAX_DESCRIBE_ADDRESSES, MAX_DISCOVERY_RESULT_BYTES,
|
|
10
|
+
export { MAX_DESCRIBE_ADDRESSES, MAX_DISCOVERY_RESULT_BYTES, MAX_SEARCH_LIMIT, };
|
|
11
11
|
const RESULT_TTL_SECONDS = 900;
|
|
12
12
|
const enc = new TextEncoder();
|
|
13
13
|
const dec = new TextDecoder();
|
|
@@ -267,9 +267,6 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
267
267
|
? { allowDestructive: options.allowDestructive }
|
|
268
268
|
: {}),
|
|
269
269
|
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
270
|
-
...(call.maxRetries !== undefined
|
|
271
|
-
? { maxRetries: call.maxRetries }
|
|
272
|
-
: {}),
|
|
273
270
|
...(opts.requestSignal !== undefined
|
|
274
271
|
? { requestSignal: opts.requestSignal }
|
|
275
272
|
: {}),
|
|
@@ -451,12 +448,12 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
451
448
|
});
|
|
452
449
|
}
|
|
453
450
|
const ctx = registry.contextFor(connector.id, baseUrl, requestScope);
|
|
454
|
-
if (!ctx.credential) {
|
|
451
|
+
if (!ctx.credential || !opts.credentialHandoffUrl) {
|
|
455
452
|
return jsonResult({
|
|
456
453
|
connector: connector.id,
|
|
457
454
|
recovery: "unavailable",
|
|
458
|
-
message: "Credential
|
|
459
|
-
"
|
|
455
|
+
message: "Credential recovery needs both a vault and the optional UI. Configure " +
|
|
456
|
+
"vault and ui in deployment code, then call " +
|
|
460
457
|
"authorize_connector again.",
|
|
461
458
|
});
|
|
462
459
|
}
|
|
@@ -477,7 +474,7 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
477
474
|
label: connector.credential.label,
|
|
478
475
|
fields,
|
|
479
476
|
},
|
|
480
|
-
operatorUrl:
|
|
477
|
+
operatorUrl: opts.credentialHandoffUrl,
|
|
481
478
|
instructions: "Have the operator open operatorUrl, set and test the credential, " +
|
|
482
479
|
"then retry the original call. No redeploy is needed. " +
|
|
483
480
|
(connector.authScope === "personal"
|
|
@@ -485,6 +482,8 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
485
482
|
: "Shared credential mutation requires a signed-in human with access to this connector."),
|
|
486
483
|
});
|
|
487
484
|
}
|
|
485
|
+
if (!opts.canManageAuth?.(connector.id))
|
|
486
|
+
return jsonResult({ connector: connector.id, recovery: "unavailable", message: "Your identity is not permitted to manage authentication for this connection." });
|
|
488
487
|
const ctx = registry.contextFor(connector.id, baseUrl, requestScope);
|
|
489
488
|
try {
|
|
490
489
|
const status = await connector.startAuth(ctx, args.force !== undefined ? { force: args.force } : {});
|
|
@@ -568,14 +567,13 @@ const CALL_INPUT_SCHEMA = {
|
|
|
568
567
|
args: z.record(z.string(), z.unknown()).optional(),
|
|
569
568
|
resultMode: z.enum(["mcp", "value"]).optional(),
|
|
570
569
|
timeoutMs: z.number().int().positive().optional(),
|
|
571
|
-
maxRetries: z.number().int().min(0).max(2).optional(),
|
|
572
570
|
diagnostics: z.boolean().optional(),
|
|
573
571
|
};
|
|
574
572
|
/**
|
|
575
573
|
* Register the six explicit meta-tools onto an McpServer instance.
|
|
576
574
|
* `registerExecuteTool` adds the seventh, `execute_code`. Broad discovery and
|
|
577
|
-
* multi-call work
|
|
578
|
-
*
|
|
575
|
+
* multi-call work uses discovery and ordinary JavaScript promises inside a
|
|
576
|
+
* program, which `execute_code` builds over the same
|
|
579
577
|
* `CatalogService` and `InvocationService` these handlers use — one shared
|
|
580
578
|
* services layer, two adapters above it.
|
|
581
579
|
*/
|
|
@@ -585,6 +583,8 @@ export function registerMetaTools(server, registry, ctx) {
|
|
|
585
583
|
probeTimeoutMs: ctx.probeTimeoutMs,
|
|
586
584
|
discoveryConcurrency: ctx.discoveryConcurrency,
|
|
587
585
|
activity: ctx.activity,
|
|
586
|
+
canManageAuth: ctx.canManageAuth,
|
|
587
|
+
credentialHandoffUrl: ctx.credentialHandoffUrl,
|
|
588
588
|
requestSignal: ctx.requestSignal,
|
|
589
589
|
defer: ctx.defer,
|
|
590
590
|
});
|
|
@@ -592,7 +592,6 @@ export function registerMetaTools(server, registry, ctx) {
|
|
|
592
592
|
description: describedFor(registry, SKILLS_DESC, "skills"),
|
|
593
593
|
inputSchema: z.object({ name: z.string().optional() }),
|
|
594
594
|
annotations: READ_ONLY_LOCAL,
|
|
595
|
-
_meta: { ui: { visibility: ["model"] } },
|
|
596
595
|
}, async (args) => mt.skills(args));
|
|
597
596
|
server.registerTool("search_tools", {
|
|
598
597
|
description: describedFor(registry, SEARCH_DESC, "search"),
|
|
@@ -608,20 +607,17 @@ export function registerMetaTools(server, registry, ctx) {
|
|
|
608
607
|
includeSchemas: z.enum(["compact", "json"]).optional(),
|
|
609
608
|
}),
|
|
610
609
|
annotations: READ_ONLY_REMOTE,
|
|
611
|
-
_meta: { ui: { visibility: ["model"] } },
|
|
612
610
|
}, async (args) => mt.searchTools(args));
|
|
613
611
|
server.registerTool("call_tool", {
|
|
614
612
|
description: CALL_DESC,
|
|
615
|
-
inputSchema: z.
|
|
613
|
+
inputSchema: z.strictObject(CALL_INPUT_SCHEMA),
|
|
616
614
|
// call_tool admits only tools that are themselves explicitly read-only;
|
|
617
615
|
// anything else is refused and routed to call_destructive_tool.
|
|
618
616
|
annotations: READ_ONLY_REMOTE,
|
|
619
|
-
// Omission defaults to model + app. Display-only views may call no tool.
|
|
620
|
-
_meta: { ui: { visibility: ["model"] } },
|
|
621
617
|
}, async (args) => mt.callTool(args));
|
|
622
618
|
server.registerTool("call_destructive_tool", {
|
|
623
619
|
description: describedFor(registry, CALL_DESTRUCTIVE_DESC, "destructive"),
|
|
624
|
-
inputSchema: z.
|
|
620
|
+
inputSchema: z.strictObject({
|
|
625
621
|
...CALL_INPUT_SCHEMA,
|
|
626
622
|
// Bounded above, but with no lower bound: a model that sends `""` or
|
|
627
623
|
// whitespace has written no reason, and failing an entire consequential
|
|
@@ -634,7 +630,6 @@ export function registerMetaTools(server, registry, ctx) {
|
|
|
634
630
|
readOnlyHint: false,
|
|
635
631
|
openWorldHint: true,
|
|
636
632
|
},
|
|
637
|
-
_meta: { ui: { visibility: ["model"] } },
|
|
638
633
|
}, async (args) => {
|
|
639
634
|
// `reason` is the host's to display and connecta's to keep out of the
|
|
640
635
|
// downstream call, so this destructuring is the whole of its handling:
|
|
@@ -657,7 +652,6 @@ export function registerMetaTools(server, registry, ctx) {
|
|
|
657
652
|
destructiveHint: false,
|
|
658
653
|
openWorldHint: true,
|
|
659
654
|
},
|
|
660
|
-
_meta: { ui: { visibility: ["model"] } },
|
|
661
655
|
}, async (args) => mt.authorizeConnector(args));
|
|
662
656
|
server.registerTool("get_result", {
|
|
663
657
|
description: GET_RESULT_DESC,
|
|
@@ -671,6 +665,5 @@ export function registerMetaTools(server, registry, ctx) {
|
|
|
671
665
|
maxBytes: z.number().int().min(MIN_MAX_RESULT_BYTES).optional(),
|
|
672
666
|
}),
|
|
673
667
|
annotations: READ_ONLY_LOCAL,
|
|
674
|
-
_meta: { ui: { visibility: ["model"] } },
|
|
675
668
|
}, async (args) => mt.getResult(args));
|
|
676
669
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ConnectaBranding } from "./types.js";
|
|
2
|
+
import type { ActivityStore, ActivityReadGate, recordToolActivity, recordCatalogDriftActivity } from "./activity.js";
|
|
3
|
+
import type { RouteContext } from "./routes/shared.js";
|
|
4
|
+
/** Construction-time UI contract. Core never imports the implementation. */
|
|
5
|
+
export interface OperatorSurface {
|
|
6
|
+
readonly branding?: ConnectaBranding;
|
|
7
|
+
readonly reservedPaths: readonly string[];
|
|
8
|
+
credentialHandoffUrl(baseUrl: string): string;
|
|
9
|
+
handle(context: RouteContext): Promise<Response | null>;
|
|
10
|
+
}
|
|
11
|
+
/** Optional recording and reading callbacks supplied by /activity. */
|
|
12
|
+
export interface ActivityModule {
|
|
13
|
+
readonly store: ActivityStore;
|
|
14
|
+
readonly deploymentId?: string;
|
|
15
|
+
readonly readGate?: ActivityReadGate;
|
|
16
|
+
handle(context: RouteContext): Promise<Response | null>;
|
|
17
|
+
readonly recordTool: typeof recordToolActivity;
|
|
18
|
+
readonly recordDrift: typeof recordCatalogDriftActivity;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|