@zackbart/connecta 0.8.1 → 0.9.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 +90 -0
- package/README.md +9 -8
- package/SECURITY.md +5 -11
- package/dist/auth/downstream-oauth.d.ts +15 -6
- package/dist/auth/downstream-oauth.d.ts.map +1 -1
- package/dist/auth/downstream-oauth.js +60 -11
- package/dist/auth/downstream-oauth.js.map +1 -1
- package/dist/catalog-service.d.ts +8 -0
- package/dist/catalog-service.d.ts.map +1 -1
- package/dist/catalog-service.js +24 -2
- package/dist/catalog-service.js.map +1 -1
- package/dist/catalog.d.ts +34 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +264 -40
- package/dist/catalog.js.map +1 -1
- package/dist/connectors/remote-mcp.d.ts +1 -1
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js +65 -50
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -0
- package/dist/errors.js.map +1 -1
- package/dist/execute.d.ts +3 -1
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +50 -13
- package/dist/execute.js.map +1 -1
- package/dist/meta-tools.d.ts +1 -1
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +19 -17
- package/dist/meta-tools.js.map +1 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +63 -44
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/oauth.js +1 -1
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/shared.d.ts +2 -2
- package/dist/routes/shared.d.ts.map +1 -1
- package/dist/skills.d.ts +2 -2
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +7 -7
- package/dist/skills.js.map +1 -1
- package/dist/types.d.ts +6 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
- package/src/auth/downstream-oauth.ts +106 -24
- package/src/catalog-service.ts +43 -0
- package/src/catalog.ts +327 -33
- package/src/connectors/remote-mcp.ts +96 -64
- package/src/errors.ts +2 -0
- package/src/execute.ts +55 -12
- package/src/meta-tools.ts +22 -18
- package/src/routes/mcp.ts +70 -44
- package/src/routes/oauth.ts +1 -1
- package/src/routes/shared.ts +2 -2
- package/src/skills.ts +7 -7
- package/src/types.ts +10 -2
- package/src/version.ts +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
2
1
|
import type {
|
|
2
|
+
OAuthClientInformationContext,
|
|
3
3
|
OAuthClientInformationMixed,
|
|
4
4
|
OAuthClientMetadata,
|
|
5
|
+
OAuthClientProvider,
|
|
5
6
|
OAuthTokens,
|
|
6
|
-
} from "@modelcontextprotocol/
|
|
7
|
+
} from "@modelcontextprotocol/client";
|
|
7
8
|
import type { KVStorage } from "../types.js";
|
|
8
9
|
|
|
9
10
|
/** A 256-bit random opaque value, hex-encoded — used for the OAuth `state`. */
|
|
@@ -25,7 +26,7 @@ const LEGACY_GENERATION = "legacy";
|
|
|
25
26
|
const ACTIVE_GENERATION_PREFIX = "v2:";
|
|
26
27
|
const RESETTING_GENERATION_PREFIX = "reset:";
|
|
27
28
|
const DISCONNECTED_GENERATION_PREFIX = "disconnected:";
|
|
28
|
-
const STORED_VALUE_VERSION =
|
|
29
|
+
const STORED_VALUE_VERSION = 2;
|
|
29
30
|
const OAUTH_VALUE_KEYS = [
|
|
30
31
|
"oauth:client",
|
|
31
32
|
"oauth:tokens",
|
|
@@ -38,6 +39,13 @@ const MAX_CLEANUP_BACKLOG = 1_000;
|
|
|
38
39
|
interface StoredOAuthValue<T> {
|
|
39
40
|
connectaOAuthVersion: typeof STORED_VALUE_VERSION;
|
|
40
41
|
generation: string;
|
|
42
|
+
issuer?: string;
|
|
43
|
+
value: T;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface LegacyStoredOAuthValue<T> {
|
|
47
|
+
connectaOAuthVersion: 1;
|
|
48
|
+
generation: string;
|
|
41
49
|
value: T;
|
|
42
50
|
}
|
|
43
51
|
|
|
@@ -49,6 +57,19 @@ function storedOAuthValue<T>(
|
|
|
49
57
|
return (
|
|
50
58
|
candidate.connectaOAuthVersion === STORED_VALUE_VERSION &&
|
|
51
59
|
typeof candidate.generation === "string" &&
|
|
60
|
+
(candidate.issuer === undefined || typeof candidate.issuer === "string") &&
|
|
61
|
+
"value" in candidate
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function legacyStoredOAuthValue<T>(
|
|
66
|
+
value: unknown,
|
|
67
|
+
): value is LegacyStoredOAuthValue<T> {
|
|
68
|
+
if (!value || typeof value !== "object") return false;
|
|
69
|
+
const candidate = value as Partial<LegacyStoredOAuthValue<T>>;
|
|
70
|
+
return (
|
|
71
|
+
candidate.connectaOAuthVersion === 1 &&
|
|
72
|
+
typeof candidate.generation === "string" &&
|
|
52
73
|
"value" in candidate
|
|
53
74
|
);
|
|
54
75
|
}
|
|
@@ -132,6 +153,7 @@ export class KvOAuthProvider implements OAuthClientProvider {
|
|
|
132
153
|
key: string,
|
|
133
154
|
value: T,
|
|
134
155
|
serializeLegacy: (value: T) => string,
|
|
156
|
+
issuer?: string,
|
|
135
157
|
): Promise<void> {
|
|
136
158
|
const generation = await this.writeGeneration();
|
|
137
159
|
if (
|
|
@@ -144,12 +166,13 @@ export class KvOAuthProvider implements OAuthClientProvider {
|
|
|
144
166
|
const stored: StoredOAuthValue<T> = {
|
|
145
167
|
connectaOAuthVersion: STORED_VALUE_VERSION,
|
|
146
168
|
generation,
|
|
169
|
+
...(issuer !== undefined ? { issuer } : {}),
|
|
147
170
|
value,
|
|
148
171
|
};
|
|
149
172
|
const physicalKey = oauthValueStorageKey(key, generation);
|
|
150
173
|
await this.storage.set(
|
|
151
174
|
physicalKey,
|
|
152
|
-
isModernGeneration(generation)
|
|
175
|
+
isModernGeneration(generation) || issuer !== undefined
|
|
153
176
|
? JSON.stringify(stored)
|
|
154
177
|
: serializeLegacy(value),
|
|
155
178
|
);
|
|
@@ -182,7 +205,9 @@ export class KvOAuthProvider implements OAuthClientProvider {
|
|
|
182
205
|
private async readValue<T>(
|
|
183
206
|
key: string,
|
|
184
207
|
parseLegacy: (raw: string) => T,
|
|
185
|
-
): Promise<
|
|
208
|
+
): Promise<
|
|
209
|
+
{ value: T; generation: string; issuer?: string } | undefined
|
|
210
|
+
> {
|
|
186
211
|
const generation = await this.generation();
|
|
187
212
|
const raw = await this.storage.get(
|
|
188
213
|
oauthValueStorageKey(key, generation),
|
|
@@ -202,6 +227,15 @@ export class KvOAuthProvider implements OAuthClientProvider {
|
|
|
202
227
|
// Raw string state from a pre-envelope deployment is handled below.
|
|
203
228
|
}
|
|
204
229
|
if (storedOAuthValue<T>(parsed)) {
|
|
230
|
+
return parsed.generation === generation
|
|
231
|
+
? {
|
|
232
|
+
value: parsed.value,
|
|
233
|
+
generation,
|
|
234
|
+
...(parsed.issuer !== undefined ? { issuer: parsed.issuer } : {}),
|
|
235
|
+
}
|
|
236
|
+
: undefined;
|
|
237
|
+
}
|
|
238
|
+
if (legacyStoredOAuthValue<T>(parsed)) {
|
|
205
239
|
return parsed.generation === generation
|
|
206
240
|
? { value: parsed.value, generation }
|
|
207
241
|
: undefined;
|
|
@@ -210,6 +244,40 @@ export class KvOAuthProvider implements OAuthClientProvider {
|
|
|
210
244
|
return { value: parseLegacy(raw), generation };
|
|
211
245
|
}
|
|
212
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Read credentials only for the authorization server that issued them.
|
|
249
|
+
*
|
|
250
|
+
* Values written before issuer binding are bound on their first read with a
|
|
251
|
+
* validated SDK issuer, preserving an existing grant across this upgrade.
|
|
252
|
+
* A later issuer change is different: publish a new generation before
|
|
253
|
+
* returning no credentials, so every isolate drops the old registration and
|
|
254
|
+
* token set and the SDK starts authorization from scratch.
|
|
255
|
+
*/
|
|
256
|
+
private async readIssuerBoundValue<T>(
|
|
257
|
+
key: "oauth:client" | "oauth:tokens",
|
|
258
|
+
parseLegacy: (raw: string) => T,
|
|
259
|
+
serializeLegacy: (value: T) => string,
|
|
260
|
+
ctx?: OAuthClientInformationContext,
|
|
261
|
+
): Promise<T | undefined> {
|
|
262
|
+
const stored = await this.readValue(key, parseLegacy);
|
|
263
|
+
if (!stored || !ctx) return stored?.value;
|
|
264
|
+
if (stored.issuer === undefined) {
|
|
265
|
+
await this.writeValue(key, stored.value, serializeLegacy, ctx.issuer);
|
|
266
|
+
return stored.value;
|
|
267
|
+
}
|
|
268
|
+
if (stored.issuer === ctx.issuer) return stored.value;
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
await this.resetAuthorization();
|
|
272
|
+
} finally {
|
|
273
|
+
// The issuer-aware flow may continue after the reset. Stamp any later
|
|
274
|
+
// writes into the replacement epoch; the connector's generation check
|
|
275
|
+
// still discards this connection attempt before caching it.
|
|
276
|
+
this.captureGeneration(await this.generation());
|
|
277
|
+
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
213
281
|
/** Attempt every key deletion, then report the first backend failure. */
|
|
214
282
|
private async deleteAll(keys: readonly string[]): Promise<void> {
|
|
215
283
|
let firstError: unknown;
|
|
@@ -276,35 +344,49 @@ export class KvOAuthProvider implements OAuthClientProvider {
|
|
|
276
344
|
};
|
|
277
345
|
}
|
|
278
346
|
|
|
279
|
-
async clientInformation(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
)
|
|
285
|
-
|
|
347
|
+
async clientInformation(
|
|
348
|
+
ctx?: OAuthClientInformationContext,
|
|
349
|
+
): Promise<OAuthClientInformationMixed | undefined> {
|
|
350
|
+
return this.readIssuerBoundValue(
|
|
351
|
+
"oauth:client",
|
|
352
|
+
(raw) => JSON.parse(raw) as OAuthClientInformationMixed,
|
|
353
|
+
(value) => JSON.stringify(value),
|
|
354
|
+
ctx,
|
|
355
|
+
);
|
|
286
356
|
}
|
|
287
357
|
|
|
288
358
|
async saveClientInformation(
|
|
289
359
|
info: OAuthClientInformationMixed,
|
|
360
|
+
ctx?: OAuthClientInformationContext,
|
|
290
361
|
): Promise<void> {
|
|
291
|
-
await this.writeValue(
|
|
292
|
-
|
|
362
|
+
await this.writeValue(
|
|
363
|
+
"oauth:client",
|
|
364
|
+
info,
|
|
365
|
+
(value) => JSON.stringify(value),
|
|
366
|
+
ctx?.issuer,
|
|
293
367
|
);
|
|
294
368
|
}
|
|
295
369
|
|
|
296
|
-
async tokens(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
)
|
|
302
|
-
|
|
370
|
+
async tokens(
|
|
371
|
+
ctx?: OAuthClientInformationContext,
|
|
372
|
+
): Promise<OAuthTokens | undefined> {
|
|
373
|
+
return this.readIssuerBoundValue(
|
|
374
|
+
"oauth:tokens",
|
|
375
|
+
(raw) => JSON.parse(raw) as OAuthTokens,
|
|
376
|
+
(value) => JSON.stringify(value),
|
|
377
|
+
ctx,
|
|
378
|
+
);
|
|
303
379
|
}
|
|
304
380
|
|
|
305
|
-
async saveTokens(
|
|
306
|
-
|
|
307
|
-
|
|
381
|
+
async saveTokens(
|
|
382
|
+
tokens: OAuthTokens,
|
|
383
|
+
ctx?: OAuthClientInformationContext,
|
|
384
|
+
): Promise<void> {
|
|
385
|
+
await this.writeValue(
|
|
386
|
+
"oauth:tokens",
|
|
387
|
+
tokens,
|
|
388
|
+
(value) => JSON.stringify(value),
|
|
389
|
+
ctx?.issuer,
|
|
308
390
|
);
|
|
309
391
|
}
|
|
310
392
|
|
package/src/catalog-service.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
compactSchema,
|
|
3
|
+
lexicalCorpusStatistics,
|
|
3
4
|
lexicalSearchQuery,
|
|
4
5
|
rankTools,
|
|
6
|
+
schemaObjectKeys,
|
|
5
7
|
summarizeDescription,
|
|
6
8
|
} from "./catalog.js";
|
|
7
9
|
import {
|
|
@@ -103,6 +105,8 @@ export interface CatalogSearchArgs {
|
|
|
103
105
|
offset?: number;
|
|
104
106
|
fullDescriptions?: boolean;
|
|
105
107
|
includeSchemas?: "compact" | "json";
|
|
108
|
+
/** Code-mode helper metadata; never exposed by the public search_tools schema. */
|
|
109
|
+
includeSchemaKeys?: boolean;
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
export interface CatalogDescribeArgs {
|
|
@@ -120,10 +124,39 @@ interface CatalogSearchEntry {
|
|
|
120
124
|
description?: string;
|
|
121
125
|
inputSchema?: unknown;
|
|
122
126
|
outputSchema?: unknown;
|
|
127
|
+
inputKeys?: string[];
|
|
128
|
+
requiredInputKeys?: string[];
|
|
129
|
+
outputKeys?: string[];
|
|
123
130
|
annotations?: ToolDef["annotations"];
|
|
124
131
|
};
|
|
125
132
|
}
|
|
126
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Code-mode key metadata for one match. Each half is omitted when its schema
|
|
136
|
+
* does not resolve to an object shape, so a program reads "no metadata, use the
|
|
137
|
+
* rendered schema" rather than "this tool has no fields".
|
|
138
|
+
*/
|
|
139
|
+
function schemaKeyMetadata(
|
|
140
|
+
input: JsonSchema,
|
|
141
|
+
output: JsonSchema | undefined,
|
|
142
|
+
): {
|
|
143
|
+
inputKeys?: string[];
|
|
144
|
+
requiredInputKeys?: string[];
|
|
145
|
+
outputKeys?: string[];
|
|
146
|
+
} {
|
|
147
|
+
const inputKeys = schemaObjectKeys(input);
|
|
148
|
+
const outputKeys = schemaObjectKeys(output);
|
|
149
|
+
return {
|
|
150
|
+
...(inputKeys
|
|
151
|
+
? {
|
|
152
|
+
inputKeys: inputKeys.properties,
|
|
153
|
+
requiredInputKeys: inputKeys.required,
|
|
154
|
+
}
|
|
155
|
+
: {}),
|
|
156
|
+
...(outputKeys ? { outputKeys: outputKeys.properties } : {}),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
127
160
|
export interface CatalogSearchPage {
|
|
128
161
|
entries: CatalogSearchEntry[];
|
|
129
162
|
total: number;
|
|
@@ -391,6 +424,12 @@ export class CatalogService {
|
|
|
391
424
|
order: number;
|
|
392
425
|
}> = [];
|
|
393
426
|
let matchMode: "all" | "partial" = "all";
|
|
427
|
+
const statistics = lexicalCorpusStatistics(
|
|
428
|
+
catalogs.flatMap((catalog) =>
|
|
429
|
+
catalog.status === "fulfilled" ? [catalog.value] : [],
|
|
430
|
+
),
|
|
431
|
+
retrievalQuery,
|
|
432
|
+
);
|
|
394
433
|
const collectMatches = (mode: "all" | "partial") => {
|
|
395
434
|
matches.length = 0;
|
|
396
435
|
let orderBase = 0;
|
|
@@ -404,6 +443,7 @@ export class CatalogService {
|
|
|
404
443
|
catalog.value,
|
|
405
444
|
retrievalQuery,
|
|
406
445
|
mode,
|
|
446
|
+
statistics,
|
|
407
447
|
)) {
|
|
408
448
|
matches.push({
|
|
409
449
|
connector,
|
|
@@ -450,6 +490,9 @@ export class CatalogService {
|
|
|
450
490
|
renderSchema(match.tool.outputSchema, args.includeSchemas),
|
|
451
491
|
}
|
|
452
492
|
: {}),
|
|
493
|
+
...(args.includeSchemas && args.includeSchemaKeys
|
|
494
|
+
? schemaKeyMetadata(input, match.tool.outputSchema)
|
|
495
|
+
: {}),
|
|
453
496
|
...(match.tool.annotations
|
|
454
497
|
? { annotations: match.tool.annotations }
|
|
455
498
|
: {}),
|