@zackbart/connecta 0.7.9 → 0.8.1
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 +135 -0
- package/README.md +47 -148
- package/dist/activity.d.ts +0 -8
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +0 -1
- package/dist/activity.js.map +1 -1
- package/dist/auth/bearer.d.ts +1 -7
- package/dist/auth/bearer.d.ts.map +1 -1
- package/dist/auth/bearer.js +3 -24
- package/dist/auth/bearer.js.map +1 -1
- package/dist/auth/clerk.d.ts +2 -11
- package/dist/auth/clerk.d.ts.map +1 -1
- package/dist/auth/clerk.js +31 -46
- package/dist/auth/clerk.js.map +1 -1
- package/dist/call-admission.d.ts.map +1 -1
- package/dist/call-admission.js +9 -4
- package/dist/call-admission.js.map +1 -1
- package/dist/catalog-fingerprint.d.ts +20 -0
- package/dist/catalog-fingerprint.d.ts.map +1 -0
- package/dist/catalog-fingerprint.js +34 -0
- package/dist/catalog-fingerprint.js.map +1 -0
- package/dist/catalog-limits.d.ts +10 -0
- package/dist/catalog-limits.d.ts.map +1 -0
- package/dist/catalog-limits.js +10 -0
- package/dist/catalog-limits.js.map +1 -0
- package/dist/catalog-service.d.ts +137 -0
- package/dist/catalog-service.d.ts.map +1 -0
- package/dist/catalog-service.js +401 -0
- package/dist/catalog-service.js.map +1 -0
- package/dist/catalog.d.ts +6 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +53 -2
- package/dist/catalog.js.map +1 -1
- package/dist/concurrency.d.ts +4 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +31 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/connectors/api.d.ts.map +1 -1
- package/dist/connectors/api.js +22 -12
- package/dist/connectors/api.js.map +1 -1
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js +53 -41
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/credentials.d.ts +3 -2
- package/dist/credentials.d.ts.map +1 -1
- package/dist/errors.d.ts +19 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/execute.d.ts +10 -16
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +143 -330
- package/dist/execute.js.map +1 -1
- package/dist/executor-result.d.ts +0 -3
- package/dist/executor-result.d.ts.map +1 -1
- package/dist/executor-result.js +3 -3
- package/dist/executor-result.js.map +1 -1
- package/dist/executors/quickjs-child.js +3 -3
- package/dist/executors/quickjs-child.js.map +1 -1
- package/dist/executors/quickjs-protocol.d.ts +4 -1
- package/dist/executors/quickjs-protocol.d.ts.map +1 -1
- package/dist/executors/quickjs-runtime.d.ts.map +1 -1
- package/dist/executors/quickjs-runtime.js +7 -2
- package/dist/executors/quickjs-runtime.js.map +1 -1
- package/dist/executors/quickjs.d.ts.map +1 -1
- package/dist/executors/quickjs.js +4 -1
- package/dist/executors/quickjs.js.map +1 -1
- package/dist/favicon.js +2 -2
- package/dist/favicon.js.map +1 -1
- package/dist/index.d.ts +8 -82
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -104
- package/dist/index.js.map +1 -1
- package/dist/invocation.d.ts +101 -0
- package/dist/invocation.d.ts.map +1 -0
- package/dist/invocation.js +405 -0
- package/dist/invocation.js.map +1 -0
- package/dist/meta-tools.d.ts +9 -95
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +235 -725
- package/dist/meta-tools.js.map +1 -1
- package/dist/operator-ui/generated.d.ts +3 -0
- package/dist/operator-ui/generated.d.ts.map +1 -0
- package/dist/operator-ui/generated.js +5 -0
- package/dist/operator-ui/generated.js.map +1 -0
- package/dist/operator-ui/model.d.ts +73 -0
- package/dist/operator-ui/model.d.ts.map +1 -0
- package/dist/operator-ui/model.js +28 -0
- package/dist/operator-ui/model.js.map +1 -0
- package/dist/registry.d.ts +33 -184
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +345 -330
- package/dist/registry.js.map +1 -1
- package/dist/retired-toolkits.d.ts +3 -0
- package/dist/retired-toolkits.d.ts.map +1 -0
- package/dist/retired-toolkits.js +9 -0
- package/dist/retired-toolkits.js.map +1 -0
- package/dist/routes/activity.d.ts +3 -0
- package/dist/routes/activity.d.ts.map +1 -0
- package/dist/routes/activity.js +170 -0
- package/dist/routes/activity.js.map +1 -0
- package/dist/routes/credentials.d.ts +3 -0
- package/dist/routes/credentials.d.ts.map +1 -0
- package/dist/routes/credentials.js +170 -0
- package/dist/routes/credentials.js.map +1 -0
- package/dist/routes/mcp.d.ts +8 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +270 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/oauth.d.ts +4 -0
- package/dist/routes/oauth.d.ts.map +1 -0
- package/dist/routes/oauth.js +263 -0
- package/dist/routes/oauth.js.map +1 -0
- package/dist/routes/shared.d.ts +76 -0
- package/dist/routes/shared.d.ts.map +1 -0
- package/dist/routes/shared.js +140 -0
- package/dist/routes/shared.js.map +1 -0
- package/dist/routes/ui.d.ts +3 -0
- package/dist/routes/ui.d.ts.map +1 -0
- package/dist/routes/ui.js +116 -0
- package/dist/routes/ui.js.map +1 -0
- package/dist/server.d.ts +9 -40
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +62 -1322
- package/dist/server.js.map +1 -1
- package/dist/skills.d.ts +3 -21
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +15 -43
- package/dist/skills.js.map +1 -1
- package/dist/storage/file.d.ts.map +1 -1
- package/dist/storage/file.js +3 -1
- package/dist/storage/file.js.map +1 -1
- package/dist/storage/memory.d.ts.map +1 -1
- package/dist/storage/memory.js +3 -1
- package/dist/storage/memory.js.map +1 -1
- package/dist/timeout.d.ts.map +1 -1
- package/dist/timeout.js +2 -4
- package/dist/timeout.js.map +1 -1
- package/dist/types.d.ts +9 -59
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +3 -94
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +30 -1427
- package/dist/ui.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +15 -8
- package/src/activity.ts +0 -9
- package/src/auth/bearer.ts +4 -33
- package/src/auth/clerk.ts +35 -48
- package/src/call-admission.ts +8 -6
- package/src/catalog-fingerprint.ts +52 -0
- package/src/catalog-limits.ts +11 -0
- package/src/catalog-service.ts +601 -0
- package/src/catalog.ts +57 -2
- package/src/concurrency.ts +39 -0
- package/src/connectors/api.ts +22 -12
- package/src/connectors/remote-mcp.ts +63 -45
- package/src/credentials.ts +2 -2
- package/src/errors.ts +21 -1
- package/src/execute.ts +193 -423
- package/src/executor-result.ts +3 -3
- package/src/executors/quickjs-child.ts +8 -4
- package/src/executors/quickjs-protocol.ts +1 -1
- package/src/executors/quickjs-runtime.ts +5 -1
- package/src/executors/quickjs.ts +11 -8
- package/src/favicon.ts +2 -2
- package/src/index.ts +70 -222
- package/src/invocation.ts +580 -0
- package/src/meta-tools.ts +393 -985
- package/src/operator-ui/browser.css +360 -0
- package/src/operator-ui/browser.ts +987 -0
- package/src/operator-ui/generated.ts +4 -0
- package/src/operator-ui/model.ts +105 -0
- package/src/registry.ts +459 -439
- package/src/retired-toolkits.ts +14 -0
- package/src/routes/activity.ts +215 -0
- package/src/routes/credentials.ts +254 -0
- package/src/routes/mcp.ts +339 -0
- package/src/routes/oauth.ts +322 -0
- package/src/routes/shared.ts +240 -0
- package/src/routes/ui.ts +143 -0
- package/src/server.ts +64 -1731
- package/src/skills.ts +15 -43
- package/src/storage/file.ts +3 -1
- package/src/storage/memory.ts +3 -1
- package/src/timeout.ts +2 -4
- package/src/types.ts +8 -60
- package/src/ui.ts +60 -1538
- package/src/version.ts +1 -1
- package/dist/credential-health.d.ts +0 -227
- package/dist/credential-health.d.ts.map +0 -1
- package/dist/credential-health.js +0 -606
- package/dist/credential-health.js.map +0 -1
- package/dist/toolkits.d.ts +0 -146
- package/dist/toolkits.d.ts.map +0 -1
- package/dist/toolkits.js +0 -322
- package/dist/toolkits.js.map +0 -1
- package/src/credential-health.ts +0 -832
- package/src/toolkits.ts +0 -461
package/src/connectors/api.ts
CHANGED
|
@@ -92,10 +92,10 @@ export interface ApiOptions {
|
|
|
92
92
|
export function api(id: string, opts: ApiOptions): Connector {
|
|
93
93
|
const defs: ToolDef[] = opts.tools.map((t) => ({
|
|
94
94
|
name: t.name,
|
|
95
|
-
description: t.description,
|
|
96
|
-
inputSchema: t.inputSchema,
|
|
97
|
-
outputSchema: t.outputSchema,
|
|
98
|
-
annotations: t.annotations,
|
|
95
|
+
...(t.description !== undefined ? { description: t.description } : {}),
|
|
96
|
+
...(t.inputSchema !== undefined ? { inputSchema: t.inputSchema } : {}),
|
|
97
|
+
...(t.outputSchema !== undefined ? { outputSchema: t.outputSchema } : {}),
|
|
98
|
+
...(t.annotations !== undefined ? { annotations: t.annotations } : {}),
|
|
99
99
|
}));
|
|
100
100
|
const byName = new Map(opts.tools.map((t) => [t.name, t]));
|
|
101
101
|
const validateArgs = opts.validateArgs ?? true;
|
|
@@ -110,15 +110,25 @@ export function api(id: string, opts: ApiOptions): Connector {
|
|
|
110
110
|
}
|
|
111
111
|
return {
|
|
112
112
|
id,
|
|
113
|
-
title: opts.title,
|
|
113
|
+
...(opts.title !== undefined ? { title: opts.title } : {}),
|
|
114
114
|
kind: "api",
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
...(opts.description !== undefined
|
|
116
|
+
? { description: opts.description }
|
|
117
|
+
: {}),
|
|
118
|
+
...(opts.maxResultBytes !== undefined
|
|
119
|
+
? { maxResultBytes: opts.maxResultBytes }
|
|
120
|
+
: {}),
|
|
121
|
+
...(opts.callAdmission !== undefined
|
|
122
|
+
? { callAdmission: opts.callAdmission }
|
|
123
|
+
: {}),
|
|
124
|
+
...(opts.usageGuide !== undefined ? { usageGuide: opts.usageGuide } : {}),
|
|
125
|
+
...(opts.credential !== undefined ? { credential: opts.credential } : {}),
|
|
126
|
+
...(opts.testCredential !== undefined
|
|
127
|
+
? { testCredential: opts.testCredential }
|
|
128
|
+
: {}),
|
|
129
|
+
...(opts.testCredentials !== undefined
|
|
130
|
+
? { testCredentials: opts.testCredentials }
|
|
131
|
+
: {}),
|
|
122
132
|
staticTools: defs,
|
|
123
133
|
async listTools() {
|
|
124
134
|
return defs;
|
|
@@ -9,6 +9,7 @@ import { ListToolsResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
|
9
9
|
import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import { KvOAuthProvider } from "../auth/downstream-oauth.js";
|
|
12
|
+
import { MAX_CATALOG_TOOLS } from "../catalog-limits.js";
|
|
12
13
|
import { ConnectorCallError } from "../errors.js";
|
|
13
14
|
import { CONNECTA_VERSION } from "../version.js";
|
|
14
15
|
import type {
|
|
@@ -109,18 +110,16 @@ const TERMINATE_SESSION_BUDGET_MS = 1_000;
|
|
|
109
110
|
* cap the bytes a caller can be made to hold, not the number of round trips it
|
|
110
111
|
* took to get them.
|
|
111
112
|
*/
|
|
112
|
-
const MAX_TOOLS = 100_000;
|
|
113
|
-
|
|
114
113
|
/**
|
|
115
114
|
* Absolute backstop on `tools/list` pages in one refresh — a runaway guard, not
|
|
116
115
|
* the primary defense.
|
|
117
116
|
*
|
|
118
117
|
* The walk terminates on its own well before this: a cursor handed back twice
|
|
119
118
|
* is a definite loop, two consecutive pages that add no new tools are a server
|
|
120
|
-
* going nowhere, and
|
|
121
|
-
* only so the loop is finite even if a downstream somehow
|
|
122
|
-
* forever on a path with no discovery deadline. Set high
|
|
123
|
-
* server reaches it.
|
|
119
|
+
* going nowhere, and MAX_CATALOG_TOOLS caps what any of it can accumulate.
|
|
120
|
+
* This exists only so the loop is finite even if a downstream somehow
|
|
121
|
+
* satisfies all three forever on a path with no discovery deadline. Set high
|
|
122
|
+
* enough that no honest server reaches it.
|
|
124
123
|
*/
|
|
125
124
|
const MAX_TOOL_PAGES = 10_000;
|
|
126
125
|
|
|
@@ -191,8 +190,7 @@ function msg(err: unknown): string {
|
|
|
191
190
|
* `Client.close()` only unwinds our side — it aborts the transport's controller
|
|
192
191
|
* and fires `onclose`. Spec session termination is a separate DELETE carrying
|
|
193
192
|
* `Mcp-Session-Id`, and without it a stateful provider keeps the session alive
|
|
194
|
-
* until its own (often hour-long) timeout,
|
|
195
|
-
* accumulate several of per connector.
|
|
193
|
+
* until its own (often hour-long) timeout, accumulating abandoned sessions.
|
|
196
194
|
*
|
|
197
195
|
* Ordering is load-bearing: the SDK sends that DELETE on the transport's
|
|
198
196
|
* AbortSignal, so calling this *after* close would abort the request on issue
|
|
@@ -298,7 +296,9 @@ function redirectedInit(init: RequestInit, status: number): RequestInit {
|
|
|
298
296
|
if (!becomesGet) return init;
|
|
299
297
|
const headers = new Headers(init.headers);
|
|
300
298
|
for (const name of BODY_HEADERS) headers.delete(name);
|
|
301
|
-
|
|
299
|
+
const redirected = { ...init, method: "GET", headers };
|
|
300
|
+
delete redirected.body;
|
|
301
|
+
return redirected;
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
/**
|
|
@@ -467,6 +467,14 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
467
467
|
const scopeEndedError = () =>
|
|
468
468
|
new Error(`Connector "${id}" scope ended during connection.`);
|
|
469
469
|
|
|
470
|
+
const requestOptions = (ctx: ConnectorContext) =>
|
|
471
|
+
ctx.timeoutMs || ctx.signal
|
|
472
|
+
? {
|
|
473
|
+
...(ctx.timeoutMs ? { timeout: ctx.timeoutMs } : {}),
|
|
474
|
+
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
475
|
+
}
|
|
476
|
+
: undefined;
|
|
477
|
+
|
|
470
478
|
/**
|
|
471
479
|
* One `tools/list` request. The SDK schema is retained wholesale except for
|
|
472
480
|
* accepting `null` as the common, unambiguous end-of-chain spelling. Other
|
|
@@ -484,12 +492,7 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
484
492
|
...(cursor === undefined ? {} : { params: { cursor } }),
|
|
485
493
|
},
|
|
486
494
|
CompatibleListToolsResultSchema,
|
|
487
|
-
ctx
|
|
488
|
-
? {
|
|
489
|
-
...(ctx.timeoutMs ? { timeout: ctx.timeoutMs } : {}),
|
|
490
|
-
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
491
|
-
}
|
|
492
|
-
: undefined,
|
|
495
|
+
requestOptions(ctx),
|
|
493
496
|
);
|
|
494
497
|
} catch (err) {
|
|
495
498
|
if (!isCursorShapeError(err)) throw err;
|
|
@@ -546,10 +549,14 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
546
549
|
const url = new URL(opts.url);
|
|
547
550
|
const guardedFetch = redirectSafeFetch(id, opts.redirects);
|
|
548
551
|
if (opts.auth?.type === "oauth") {
|
|
552
|
+
// The SDK class declares `sessionId` as an own `string | undefined`
|
|
553
|
+
// property while its Transport interface declares it optional. They are
|
|
554
|
+
// runtime-compatible; exact optional types only exposes that declaration
|
|
555
|
+
// mismatch at this boundary.
|
|
549
556
|
return new StreamableHTTPClientTransport(url, {
|
|
550
557
|
authProvider: provider ?? newProvider(ctx),
|
|
551
558
|
fetch: guardedFetch,
|
|
552
|
-
});
|
|
559
|
+
}) as unknown as Transport;
|
|
553
560
|
}
|
|
554
561
|
const headers =
|
|
555
562
|
opts.auth?.type === "headers" ? opts.auth.headers : undefined;
|
|
@@ -559,7 +566,7 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
559
566
|
...(headers ? { requestInit: { headers } } : {}),
|
|
560
567
|
fetch: guardedFetch,
|
|
561
568
|
},
|
|
562
|
-
);
|
|
569
|
+
) as unknown as Transport;
|
|
563
570
|
};
|
|
564
571
|
|
|
565
572
|
const reset = (state: ConnectionState) => {
|
|
@@ -742,12 +749,18 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
742
749
|
|
|
743
750
|
const connector: Connector = {
|
|
744
751
|
id,
|
|
745
|
-
title: opts.title,
|
|
752
|
+
...(opts.title !== undefined ? { title: opts.title } : {}),
|
|
746
753
|
kind: "mcp",
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
754
|
+
...(opts.description !== undefined
|
|
755
|
+
? { description: opts.description }
|
|
756
|
+
: {}),
|
|
757
|
+
...(opts.maxResultBytes !== undefined
|
|
758
|
+
? { maxResultBytes: opts.maxResultBytes }
|
|
759
|
+
: {}),
|
|
760
|
+
...(opts.callAdmission !== undefined
|
|
761
|
+
? { callAdmission: opts.callAdmission }
|
|
762
|
+
: {}),
|
|
763
|
+
...(opts.usageGuide !== undefined ? { usageGuide: opts.usageGuide } : {}),
|
|
751
764
|
|
|
752
765
|
// `tools/list` is cursor-paginated: the server chooses the page size and
|
|
753
766
|
// signals "there is more" with a `nextCursor`, which the SDK's
|
|
@@ -830,9 +843,9 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
830
843
|
}
|
|
831
844
|
// Checked here rather than on arrival: this bounds what a *walk* may
|
|
832
845
|
// accumulate; a one-page server was always free to send its page.
|
|
833
|
-
if (listed.length >
|
|
846
|
+
if (listed.length > MAX_CATALOG_TOOLS) {
|
|
834
847
|
throw new Error(
|
|
835
|
-
`Connector "${id}" advertised further tools/list pages past ${listed.length} tools, over the ${
|
|
848
|
+
`Connector "${id}" advertised further tools/list pages past ${listed.length} tools, over the ${MAX_CATALOG_TOOLS}-tool ceiling one catalog refresh will collect.`,
|
|
836
849
|
);
|
|
837
850
|
}
|
|
838
851
|
// Opaque by contract: handed straight back, never parsed, rewritten,
|
|
@@ -863,10 +876,28 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
863
876
|
primeToolMetadata(client, listed);
|
|
864
877
|
return listed.map((t) => ({
|
|
865
878
|
name: t.name,
|
|
866
|
-
description: t.description,
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
879
|
+
...(t.description !== undefined ? { description: t.description } : {}),
|
|
880
|
+
...(t.inputSchema !== undefined
|
|
881
|
+
? {
|
|
882
|
+
inputSchema: t.inputSchema as NonNullable<
|
|
883
|
+
ToolDef["inputSchema"]
|
|
884
|
+
>,
|
|
885
|
+
}
|
|
886
|
+
: {}),
|
|
887
|
+
...(t.outputSchema !== undefined
|
|
888
|
+
? {
|
|
889
|
+
outputSchema: t.outputSchema as NonNullable<
|
|
890
|
+
ToolDef["outputSchema"]
|
|
891
|
+
>,
|
|
892
|
+
}
|
|
893
|
+
: {}),
|
|
894
|
+
...(t.annotations !== undefined
|
|
895
|
+
? {
|
|
896
|
+
annotations: t.annotations as NonNullable<
|
|
897
|
+
ToolDef["annotations"]
|
|
898
|
+
>,
|
|
899
|
+
}
|
|
900
|
+
: {}),
|
|
870
901
|
}));
|
|
871
902
|
},
|
|
872
903
|
|
|
@@ -881,12 +912,7 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
881
912
|
arguments: (args ?? {}) as Record<string, unknown>,
|
|
882
913
|
},
|
|
883
914
|
undefined,
|
|
884
|
-
ctx
|
|
885
|
-
? {
|
|
886
|
-
...(ctx.timeoutMs ? { timeout: ctx.timeoutMs } : {}),
|
|
887
|
-
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
888
|
-
}
|
|
889
|
-
: undefined,
|
|
915
|
+
requestOptions(ctx),
|
|
890
916
|
);
|
|
891
917
|
} catch (err) {
|
|
892
918
|
// A grant revoked after connect surfaces here, not in ensureConnected.
|
|
@@ -942,7 +968,7 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
942
968
|
const url = await getProvider(ctx, state).pendingAuthorizationUrl();
|
|
943
969
|
return {
|
|
944
970
|
state: "auth_required",
|
|
945
|
-
authorizationUrl: url,
|
|
971
|
+
...(url !== undefined ? { authorizationUrl: url } : {}),
|
|
946
972
|
message: "Authorization required — open the URL to connect.",
|
|
947
973
|
};
|
|
948
974
|
}
|
|
@@ -969,15 +995,6 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
969
995
|
};
|
|
970
996
|
|
|
971
997
|
if (opts.auth?.type === "oauth") {
|
|
972
|
-
// The credential liveness checks (issue #24) probe only connectors that
|
|
973
|
-
// actually hold a stored grant: with no tokens there is nothing whose
|
|
974
|
-
// liveness could have lapsed, and a `status()` probe would kick off DCR +
|
|
975
|
-
// consent on a timer for a connector nobody has authorized yet.
|
|
976
|
-
connector.hasStoredCredential = async (ctx) => {
|
|
977
|
-
const state = stateFor(ctx);
|
|
978
|
-
return (await getProvider(ctx, state).tokens()) !== undefined;
|
|
979
|
-
};
|
|
980
|
-
|
|
981
998
|
connector.verifyState = async (oauthState, ctx) => {
|
|
982
999
|
const state = stateFor(ctx);
|
|
983
1000
|
return getProvider(ctx, state).verifyState(oauthState);
|
|
@@ -1013,9 +1030,10 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
1013
1030
|
};
|
|
1014
1031
|
} catch (err) {
|
|
1015
1032
|
if (state.authRequired) {
|
|
1033
|
+
const authorizationUrl = await p.pendingAuthorizationUrl();
|
|
1016
1034
|
return {
|
|
1017
1035
|
state: "auth_required",
|
|
1018
|
-
authorizationUrl:
|
|
1036
|
+
...(authorizationUrl !== undefined ? { authorizationUrl } : {}),
|
|
1019
1037
|
message: "Authorization required — open the URL to connect.",
|
|
1020
1038
|
};
|
|
1021
1039
|
}
|
package/src/credentials.ts
CHANGED
|
@@ -24,7 +24,7 @@ interface CredentialPlaintext {
|
|
|
24
24
|
updatedBy: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
interface CredentialFieldMetadata {
|
|
28
28
|
configured: true;
|
|
29
29
|
/** Only emitted when the value is long enough that four chars don't leak much. */
|
|
30
30
|
lastFour?: string;
|
|
@@ -41,7 +41,7 @@ export interface CredentialMetadata {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/** Which hook a testable credential is checked with. */
|
|
44
|
-
|
|
44
|
+
type CredentialTestMode = "single" | "multiple";
|
|
45
45
|
|
|
46
46
|
/** Operator-safe explanation shared by every surface that detects shape drift. */
|
|
47
47
|
export const STORED_CREDENTIAL_SHAPE_MISMATCH_ERROR =
|
package/src/errors.ts
CHANGED
|
@@ -10,6 +10,12 @@ export type ConnectorCallErrorCode =
|
|
|
10
10
|
| "invalid_args"
|
|
11
11
|
| "connector_call_failed";
|
|
12
12
|
|
|
13
|
+
/** Agent-visible recovery class attached only to `auth_required` failures. */
|
|
14
|
+
export type AuthRecoveryMode =
|
|
15
|
+
| "oauth"
|
|
16
|
+
| "operator_config"
|
|
17
|
+
| "unavailable";
|
|
18
|
+
|
|
13
19
|
const RETRYABLE_BY_CODE: Record<ConnectorCallErrorCode, boolean> = {
|
|
14
20
|
timeout: true,
|
|
15
21
|
rate_limited: true,
|
|
@@ -49,7 +55,7 @@ export class ConnectorCallError extends Error {
|
|
|
49
55
|
* the assignment would not keep it off the instance. Keeping the window out
|
|
50
56
|
* of the wire format is `classifyCallError`'s job, not this constructor's.
|
|
51
57
|
*/
|
|
52
|
-
readonly retryAfterMs
|
|
58
|
+
readonly retryAfterMs: number | undefined;
|
|
53
59
|
|
|
54
60
|
constructor(
|
|
55
61
|
code: ConnectorCallErrorCode,
|
|
@@ -78,6 +84,20 @@ export interface CallErrorDetails {
|
|
|
78
84
|
* window so it can schedule a re-issue.
|
|
79
85
|
*/
|
|
80
86
|
retryAfterMs?: number;
|
|
87
|
+
/** Connector whose failed operation needs recovery. */
|
|
88
|
+
connector?: string;
|
|
89
|
+
/** Canonical downstream address the agent may retry after recovery. */
|
|
90
|
+
operation?: string;
|
|
91
|
+
/** Which safe recovery path `authorize_connector` will return. */
|
|
92
|
+
recovery?: AuthRecoveryMode;
|
|
93
|
+
/** The single model-facing entry point for every credential class. */
|
|
94
|
+
nextAction?: {
|
|
95
|
+
tool: "authorize_connector";
|
|
96
|
+
arguments: { connector: string };
|
|
97
|
+
operatorHandoff: string;
|
|
98
|
+
};
|
|
99
|
+
/** Explicit retry guidance; recovery never retries or mutates by itself. */
|
|
100
|
+
retry?: string;
|
|
81
101
|
}
|
|
82
102
|
|
|
83
103
|
const RETRYABLE_MESSAGE_RE =
|