@zackbart/connecta 0.34.0 → 0.34.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 +30 -0
- package/README.md +3 -2
- package/bin/version.mjs +1 -1
- package/dist/catalog-drift.d.ts +30 -26
- package/dist/catalog-drift.js +169 -84
- package/dist/catalog-service.js +1 -1
- package/dist/connectors/api-connector.d.ts +3 -0
- package/dist/connectors/api-connector.js +5 -1
- package/dist/connectors/option-shapes.js +1 -1
- package/dist/connectors/remote-mcp.d.ts +2 -0
- package/dist/connectors/remote-mcp.js +5 -1
- package/dist/describe-config.d.ts +2 -0
- package/dist/describe-config.js +1 -0
- package/dist/invocation.js +3 -2
- package/dist/meta-tools.js +3 -2
- package/dist/operator-ui/config-summary.js +3 -0
- package/dist/operator-ui/connection-map.js +2 -1
- package/dist/operator-ui/endpoints.js +1 -1
- package/dist/operator-ui/generated.js +3 -3
- package/dist/operator-ui/health.js +52 -0
- package/dist/operator-ui/overview.js +6 -17
- package/dist/operator-ui/view.d.ts +3 -4
- package/dist/operator-ui/view.js +29 -11
- package/dist/provider.d.ts +6 -1
- package/dist/provider.js +18 -1
- package/dist/providers/_shared/google/workspace.d.ts +3 -0
- package/dist/providers/_shared/google/workspace.js +1 -1
- package/dist/providers/basecamp/index.d.ts +2 -0
- package/dist/providers/breeze/index.d.ts +2 -0
- package/dist/providers/ccb/index.d.ts +2 -0
- package/dist/providers/ccb/index.js +1 -1
- package/dist/providers/cloudflare/index.d.ts +2 -0
- package/dist/providers/cloudflare/index.js +5 -1
- package/dist/providers/cloudflare/openapi.generated.js +3296 -3206
- package/dist/providers/cloudflare/rest.js +19 -0
- package/dist/providers/cloudflare/skill.generated.js +2 -2
- package/dist/providers/cloudflare/value-safety.js +51 -18
- package/dist/providers/intercom/index.d.ts +24 -0
- package/dist/providers/intercom/index.js +121 -0
- package/dist/providers/intercom/skill.generated.js +8 -0
- package/dist/providers/linear/index.js +204 -19
- package/dist/providers/linear/skill.generated.js +2 -2
- package/dist/providers/mixpanel/index.d.ts +2 -0
- package/dist/providers/mixpanel/index.js +21 -17
- package/dist/providers/mixpanel/skill.generated.js +1 -1
- package/dist/providers/notion/index.d.ts +2 -0
- package/dist/providers/notion/index.js +1 -1
- package/dist/providers/overflow/index.d.ts +2 -0
- package/dist/providers/planning-center/index.d.ts +2 -0
- package/dist/providers/revenuecat/index.d.ts +5 -3
- package/dist/providers/revenuecat/index.js +147 -7
- package/dist/providers/revenuecat/skill.generated.js +3 -3
- package/dist/providers/stripe/index.d.ts +2 -0
- package/dist/providers/stripe/index.js +50 -30
- package/dist/providers/stripe/skill.generated.js +2 -1
- package/dist/providers/tithely/index.d.ts +2 -0
- package/dist/providers/vercel/index.d.ts +2 -0
- package/dist/providers/vercel/index.js +1 -1
- package/dist/providers/vercel/openapi.generated.js +391 -374
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/providers/vercel/value-safety.js +14 -4
- package/dist/registry.d.ts +10 -14
- package/dist/registry.js +62 -62
- package/dist/routes/ui-data.js +8 -2
- package/dist/skills.js +8 -2
- package/dist/types.d.ts +18 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +48 -5
- package/documentation/code-mode.md +7 -5
- package/documentation/integrating.md +55 -1
- package/documentation/meta-tools.md +2 -2
- package/documentation/operating.md +1 -1
- package/documentation/operator-tests.md +8 -2
- package/documentation/operator-ui.md +58 -11
- package/documentation/provider-auth-migration.md +22 -0
- package/package.json +5 -1
- package/templates/node/package.json +1 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The next steady evidence. A settled connector takes its current row; one
|
|
3
|
+
* being read again keeps its previous evidence when this identity has any.
|
|
4
|
+
* Returns `previous` itself when nothing changed, so it can live in React
|
|
5
|
+
* state. A new identity starts empty.
|
|
6
|
+
*/
|
|
7
|
+
export function steadyHealth(state, previous) {
|
|
8
|
+
const same = previous?.generation === state.generation ? previous : null;
|
|
9
|
+
const contract = state.contract?.role === "maintainer" && state.data ? state.contract : null;
|
|
10
|
+
// No contract on screen: keep what was shown until the next one arrives.
|
|
11
|
+
if (!contract || !state.data)
|
|
12
|
+
return same ?? { generation: state.generation, rows: Object.create(null) };
|
|
13
|
+
const reading = new Set(state.data.connectors.filter((c) => c.status === "loading").map((c) => c.id));
|
|
14
|
+
const held = same?.rows ?? Object.create(null);
|
|
15
|
+
const rows = Object.create(null);
|
|
16
|
+
let changed = !same || Object.keys(held).length !== contract.live.connectors.length;
|
|
17
|
+
for (const live of contract.live.connectors) {
|
|
18
|
+
const before = Object.hasOwn(held, live.id) ? held[live.id] : undefined;
|
|
19
|
+
if (before && reading.has(live.id)) {
|
|
20
|
+
rows[live.id] = before;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const failed = Object.hasOwn(state.connectorFailures, live.id) && Boolean(state.connectorFailures[live.id]);
|
|
24
|
+
if (before?.live === live && before.failed === failed)
|
|
25
|
+
rows[live.id] = before;
|
|
26
|
+
else {
|
|
27
|
+
rows[live.id] = { live, failed };
|
|
28
|
+
changed = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return changed ? { generation: state.generation, rows } : same;
|
|
32
|
+
}
|
|
33
|
+
/** The contract each view renders health from, with every live row replaced by its steady evidence. */
|
|
34
|
+
export function shownHealth(contract, health) {
|
|
35
|
+
const signals = Object.create(null);
|
|
36
|
+
if (!contract)
|
|
37
|
+
return { contract, signals };
|
|
38
|
+
let replaced = false;
|
|
39
|
+
const connectors = contract.live.connectors.map((live) => {
|
|
40
|
+
const row = Object.hasOwn(health.rows, live.id) ? health.rows[live.id] : undefined;
|
|
41
|
+
if (row?.failed)
|
|
42
|
+
signals[live.id] = { failed: true };
|
|
43
|
+
if (!row || row.live === live)
|
|
44
|
+
return live;
|
|
45
|
+
replaced = true;
|
|
46
|
+
return row.live;
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
contract: replaced ? { ...contract, live: { ...contract.live, connectors } } : contract,
|
|
50
|
+
signals,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -13,7 +13,7 @@ function affects(paths) {
|
|
|
13
13
|
return paths.length ? `Affects ${joinList(paths)}` : "No endpoint serves it";
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* One row per connector that is not healthy,
|
|
16
|
+
* One row per connector that is not healthy, in config order, then one per
|
|
17
17
|
* agent that sits in an endpoint without a working token. Each row carries
|
|
18
18
|
* the fix the rest of the UI already offers for it.
|
|
19
19
|
*/
|
|
@@ -26,7 +26,7 @@ export function attentionItems(contract, options = {}) {
|
|
|
26
26
|
const live = contract.live.connectors.find((c) => c.id === config.id);
|
|
27
27
|
if (!live)
|
|
28
28
|
continue;
|
|
29
|
-
const signals = options.signals
|
|
29
|
+
const signals = options.signals && Object.hasOwn(options.signals, config.id) ? options.signals[config.id] : {};
|
|
30
30
|
const verdict = connectorVerdict(live, signals);
|
|
31
31
|
if ((verdict.tone !== "warn" && verdict.tone !== "danger") || !verdict.action)
|
|
32
32
|
continue;
|
|
@@ -71,15 +71,6 @@ export function attentionItems(contract, options = {}) {
|
|
|
71
71
|
fix: null,
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
else {
|
|
75
|
-
// Healthy but drifted: the live tool list differs from the reviewed one.
|
|
76
|
-
connectors.push({
|
|
77
|
-
...base,
|
|
78
|
-
problem: "has a changed tool list",
|
|
79
|
-
body: affects(paths),
|
|
80
|
-
fix: { kind: "catalog_drift", connectorId: config.id },
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
74
|
}
|
|
84
75
|
const agents = contract.agents
|
|
85
76
|
.filter((agent) => agent.pools.length && agent.token.state !== "issued")
|
|
@@ -101,11 +92,9 @@ export function attentionItems(contract, options = {}) {
|
|
|
101
92
|
fix: null,
|
|
102
93
|
};
|
|
103
94
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
...agents,
|
|
108
|
-
];
|
|
95
|
+
// Severity changes as individual details settle. Config order keeps surviving
|
|
96
|
+
// rows in place without waiting for the slowest connector's read.
|
|
97
|
+
return [...connectors, ...agents];
|
|
109
98
|
}
|
|
110
99
|
/** The numbers above the list, counted from the same map the page draws. */
|
|
111
100
|
export function overviewHeadline(contract, map, items, signals = {}) {
|
|
@@ -113,7 +102,7 @@ export function overviewHeadline(contract, map, items, signals = {}) {
|
|
|
113
102
|
const live = contract.live.connectors.find((c) => c.id === config.id);
|
|
114
103
|
if (!live)
|
|
115
104
|
return [];
|
|
116
|
-
const health = connectorVerdict(live, signals[config.id]);
|
|
105
|
+
const health = connectorVerdict(live, Object.hasOwn(signals, config.id) ? signals[config.id] : undefined);
|
|
117
106
|
return [{ id: config.id, title: config.title ?? config.id, tone: health.tone, label: health.label }];
|
|
118
107
|
});
|
|
119
108
|
const healthy = bar.filter((entry) => entry.tone === "ok").length;
|
|
@@ -318,9 +318,8 @@ export declare function problemCopy(problem: UiProblem | undefined): string | nu
|
|
|
318
318
|
* error. The two used to share the red box, so the row contradicted itself.
|
|
319
319
|
*/
|
|
320
320
|
export declare function problemTone(problem: UiProblem): "warn" | "danger";
|
|
321
|
-
/** What the page knows about a connector beyond its live status:
|
|
321
|
+
/** What the page knows about a connector beyond its live status: a failed detail read. */
|
|
322
322
|
export interface ConnectorSignals {
|
|
323
|
-
drift?: boolean;
|
|
324
323
|
failed?: boolean;
|
|
325
324
|
}
|
|
326
325
|
/** One connector's health everywhere it is shown: badge tone, status word, and the one action beside it. */
|
|
@@ -328,12 +327,12 @@ export interface ConnectorVerdict {
|
|
|
328
327
|
tone: "ok" | "warn" | "danger" | "neutral";
|
|
329
328
|
label: string;
|
|
330
329
|
/** The link to the Connection tab, or null when the connector is fine. */
|
|
331
|
-
action: "Connect" | "Replace key" | "Inspect" |
|
|
330
|
+
action: "Connect" | "Replace key" | "Inspect" | null;
|
|
332
331
|
}
|
|
333
332
|
/**
|
|
334
333
|
* The verdict the Connectors list, the Overview, Endpoints and a connector's
|
|
335
334
|
* page all show. The server's problem code comes first, then a failed detail
|
|
336
|
-
* read on this page, then a non-ok status
|
|
335
|
+
* read on this page, then a non-ok status. Catalog drift is informational. A problem
|
|
337
336
|
* outranks the status, so a connector whose catalog failed is never
|
|
338
337
|
* "Connected". A failed read marks its connector `connector_unavailable` in
|
|
339
338
|
* the store, so that one code beside a failed read is the page's own and
|
package/dist/operator-ui/view.js
CHANGED
|
@@ -425,7 +425,7 @@ function problemAction(problem) {
|
|
|
425
425
|
/**
|
|
426
426
|
* The verdict the Connectors list, the Overview, Endpoints and a connector's
|
|
427
427
|
* page all show. The server's problem code comes first, then a failed detail
|
|
428
|
-
* read on this page, then a non-ok status
|
|
428
|
+
* read on this page, then a non-ok status. Catalog drift is informational. A problem
|
|
429
429
|
* outranks the status, so a connector whose catalog failed is never
|
|
430
430
|
* "Connected". A failed read marks its connector `connector_unavailable` in
|
|
431
431
|
* the store, so that one code beside a failed read is the page's own and
|
|
@@ -443,8 +443,6 @@ export function connectorVerdict(live, signals = {}) {
|
|
|
443
443
|
const tone = connectorStatusTone(live.status) === "warn" ? "warn" : "danger";
|
|
444
444
|
return { tone, label: connectorStatusLabel(live.status), action: tone === "warn" ? "Connect" : "Inspect" };
|
|
445
445
|
}
|
|
446
|
-
if (signals.drift)
|
|
447
|
-
return { tone: "warn", label: "Catalog drift", action: "Review" };
|
|
448
446
|
return { tone: "ok", label: "Connected", action: null };
|
|
449
447
|
}
|
|
450
448
|
/**
|
|
@@ -469,7 +467,15 @@ function driftTotal(drift) {
|
|
|
469
467
|
export function driftState(drift) {
|
|
470
468
|
if (!drift)
|
|
471
469
|
return "unavailable";
|
|
472
|
-
|
|
470
|
+
if (driftTotal(drift) > 0)
|
|
471
|
+
return "warning";
|
|
472
|
+
if (drift.reviewedBaseline !== true ||
|
|
473
|
+
drift.verification !== "complete" ||
|
|
474
|
+
!drift.schemaCoverage ||
|
|
475
|
+
drift.schemaCoverage.unpinned > 0 ||
|
|
476
|
+
drift.schemaCoverage.pinned === 0)
|
|
477
|
+
return "unavailable";
|
|
478
|
+
return "clean";
|
|
473
479
|
}
|
|
474
480
|
/** Every category with its count, so a clean report still shows its zeros. */
|
|
475
481
|
export function driftCounts(drift) {
|
|
@@ -483,16 +489,28 @@ export function driftCounts(drift) {
|
|
|
483
489
|
}
|
|
484
490
|
/** One line naming the state and when it was observed. Never what drifted. */
|
|
485
491
|
export function driftSummary(drift) {
|
|
486
|
-
|
|
487
|
-
if (state === "unavailable") {
|
|
492
|
+
if (!drift)
|
|
488
493
|
return "No catalog refresh yet.";
|
|
489
|
-
|
|
490
|
-
const observed = formatDate(drift?.observedAt);
|
|
494
|
+
const observed = formatDate(drift.observedAt);
|
|
491
495
|
const when = observed ? ` · observed ${observed}` : "";
|
|
492
|
-
if (state === "clean")
|
|
493
|
-
return `Matches the reviewed manifest${when}`;
|
|
494
496
|
const total = driftTotal(drift);
|
|
495
|
-
|
|
497
|
+
const difference = total
|
|
498
|
+
? `${total} difference${total === 1 ? "" : "s"}${drift.reviewedBaseline === false ? "" : " from the reviewed manifest"} · `
|
|
499
|
+
: "";
|
|
500
|
+
if (drift.reviewedBaseline === false)
|
|
501
|
+
return `${difference}No reviewed baseline${when}`;
|
|
502
|
+
if (drift.verification === "unavailable")
|
|
503
|
+
return `${difference}Schema verification unavailable${when}`;
|
|
504
|
+
const coverage = drift.schemaCoverage;
|
|
505
|
+
if (!coverage || coverage.pinned === 0)
|
|
506
|
+
return `${difference}Schemas not pinned${when}`;
|
|
507
|
+
if (coverage.unpinned > 0)
|
|
508
|
+
return `${difference}Partial schema coverage (${coverage.pinned} pinned, ${coverage.unpinned} not pinned)${when}`;
|
|
509
|
+
if (driftState(drift) === "clean")
|
|
510
|
+
return `Matches the reviewed manifest${when}`;
|
|
511
|
+
if (!total)
|
|
512
|
+
return `Schema verification unavailable${when}`;
|
|
513
|
+
return `${difference.replace(/ · $/, "")}${when}`;
|
|
496
514
|
}
|
|
497
515
|
/**
|
|
498
516
|
* Only http/https may become a clickable href — the browser half of the gate
|
package/dist/provider.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface ProviderOptions {
|
|
|
19
19
|
authScope?: "shared" | "personal";
|
|
20
20
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
21
21
|
maxResultBytes?: number;
|
|
22
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
23
|
+
readOnly?: boolean;
|
|
22
24
|
/** Optional per-runtime downstream call-admission policy. */
|
|
23
25
|
callAdmission?: ConnectorCallAdmissionPolicy;
|
|
24
26
|
}
|
|
@@ -82,8 +84,11 @@ export declare const CALL_ADMISSION: Omit<Field<unknown, ConnectorCallAdmissionP
|
|
|
82
84
|
parse(value: unknown, path: string): ConnectorCallAdmissionPolicy;
|
|
83
85
|
readonly shape: typeof admissionPolicy.shape;
|
|
84
86
|
};
|
|
87
|
+
/** Validate the common connector policy before a deployment can serve. */
|
|
88
|
+
export declare const READ_ONLY: Field<boolean, boolean | undefined, true>;
|
|
85
89
|
/** Options every maintained provider shares. */
|
|
86
90
|
export declare const PROVIDER_COMMON: {
|
|
91
|
+
readOnly: Field<boolean, boolean | undefined, true>;
|
|
87
92
|
callAdmission: Omit<Field<unknown, ConnectorCallAdmissionPolicy | undefined, true>, "parse"> & {
|
|
88
93
|
parse(value: unknown, path: string): ConnectorCallAdmissionPolicy;
|
|
89
94
|
readonly shape: typeof admissionPolicy.shape;
|
|
@@ -129,7 +134,7 @@ export interface ProviderContext {
|
|
|
129
134
|
* Common connector options the deployment set, and only those, ready to
|
|
130
135
|
* spread into `remoteMcp()` or `api()` options.
|
|
131
136
|
*/
|
|
132
|
-
readonly connectorOptions: Readonly<Pick<ProviderOptions, "authScope" | "maxResultBytes" | "callAdmission">>;
|
|
137
|
+
readonly connectorOptions: Readonly<Pick<ProviderOptions, "authScope" | "maxResultBytes" | "callAdmission" | "readOnly">>;
|
|
133
138
|
/**
|
|
134
139
|
* Render the maintained skill with this connection's context and the
|
|
135
140
|
* deployment's instructions. Instructions are appended; they never replace
|
package/dist/provider.js
CHANGED
|
@@ -50,9 +50,17 @@ export const CALL_ADMISSION = {
|
|
|
50
50
|
return policy;
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
|
+
/** Validate the common connector policy before a deployment can serve. */
|
|
54
|
+
export const READ_ONLY = opaque({
|
|
55
|
+
check(value, path) {
|
|
56
|
+
if (typeof value !== "boolean")
|
|
57
|
+
throw new ConfigError(`${path} must be a boolean.`);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
53
60
|
/** Options every maintained provider shares. */
|
|
54
61
|
export const PROVIDER_COMMON = {
|
|
55
62
|
...keys("title", "authScope", "purpose", "instructions", "maxResultBytes"),
|
|
63
|
+
readOnly: READ_ONLY,
|
|
56
64
|
callAdmission: CALL_ADMISSION,
|
|
57
65
|
};
|
|
58
66
|
const PROVIDER_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
@@ -127,6 +135,7 @@ export function defineProvider(definition) {
|
|
|
127
135
|
const connector = frozen.create(id, resolved, {
|
|
128
136
|
...(frozen.classify !== undefined ? { classify: frozen.classify } : {}),
|
|
129
137
|
connectorOptions: {
|
|
138
|
+
...(options.readOnly !== undefined ? { readOnly: options.readOnly } : {}),
|
|
130
139
|
...(options.authScope !== undefined ? { authScope: options.authScope } : {}),
|
|
131
140
|
...(options.maxResultBytes !== undefined ? { maxResultBytes: options.maxResultBytes } : {}),
|
|
132
141
|
...(options.callAdmission !== undefined ? { callAdmission: options.callAdmission } : {}),
|
|
@@ -178,6 +187,8 @@ function asProvider(provider, shape, id, options, build) {
|
|
|
178
187
|
let connector;
|
|
179
188
|
try {
|
|
180
189
|
options = assertKnownOptions(options, `${factory}(${JSON.stringify(id)})`, shape);
|
|
190
|
+
if (options?.readOnly !== undefined)
|
|
191
|
+
READ_ONLY.parse(options.readOnly, `${factory}(${JSON.stringify(id)}).readOnly`);
|
|
181
192
|
const admission = options == null ? undefined : options.callAdmission;
|
|
182
193
|
if (admission !== undefined)
|
|
183
194
|
CALL_ADMISSION.parse(admission, `${factory}(${JSON.stringify(id)}).callAdmission`);
|
|
@@ -188,12 +199,18 @@ function asProvider(provider, shape, id, options, build) {
|
|
|
188
199
|
}
|
|
189
200
|
const describe = connector.describe?.bind(connector);
|
|
190
201
|
const ownDescribe = Object.getOwnPropertyDescriptor(connector, "describe");
|
|
202
|
+
const ownReadOnly = Object.getOwnPropertyDescriptor(connector, "readOnly");
|
|
191
203
|
// Keep the receiver of prototype methods (including private fields). When
|
|
192
204
|
// the description cannot be replaced, inherit the whole connector instead
|
|
193
205
|
// of copying it: decorators may inherit their id, methods, and review.
|
|
194
|
-
const stamped = Object.isExtensible(connector) &&
|
|
206
|
+
const stamped = Object.isExtensible(connector) &&
|
|
207
|
+
(!ownDescribe || ownDescribe.configurable) &&
|
|
208
|
+
(options.readOnly === undefined || !ownReadOnly || ownReadOnly.configurable)
|
|
195
209
|
? connector
|
|
196
210
|
: Object.create(connector);
|
|
211
|
+
if (options.readOnly !== undefined) {
|
|
212
|
+
Object.defineProperty(stamped, "readOnly", { value: options.readOnly, enumerable: true, configurable: true });
|
|
213
|
+
}
|
|
197
214
|
Object.defineProperty(stamped, "describe", {
|
|
198
215
|
configurable: true,
|
|
199
216
|
enumerable: false,
|
|
@@ -100,6 +100,8 @@ export interface GoogleWorkspaceOptions {
|
|
|
100
100
|
callAdmission?: ConnectorCallAdmissionPolicy;
|
|
101
101
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
102
102
|
maxResultBytes?: number;
|
|
103
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
104
|
+
readOnly?: boolean;
|
|
103
105
|
/** API base override for a proxy or test double. */
|
|
104
106
|
baseUrl?: string;
|
|
105
107
|
}
|
|
@@ -144,6 +146,7 @@ export declare const GOOGLE_WORKSPACE_OPTIONS: import("../../../config-schema.js
|
|
|
144
146
|
};
|
|
145
147
|
instructions: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
146
148
|
title: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
149
|
+
readOnly: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
147
150
|
maxResultBytes: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
148
151
|
purpose: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
149
152
|
subject: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
@@ -61,7 +61,7 @@ import { tagWorkspaceSubject } from "../../../workspace-subject.js";
|
|
|
61
61
|
import { delegatedToken, forgetDelegatedToken, parseServiceAccount, } from "./service-account.js";
|
|
62
62
|
/** The closed options every Workspace provider accepts; see `assertKnownOptions`. */
|
|
63
63
|
export const GOOGLE_WORKSPACE_OPTIONS = optionsOf()({
|
|
64
|
-
...keys("purpose", "subject", "title", "instructions", "maxResultBytes", "baseUrl"),
|
|
64
|
+
...keys("purpose", "subject", "title", "instructions", "maxResultBytes", "baseUrl", "readOnly"),
|
|
65
65
|
// A string is the key's JSON; only the object form has keys to check.
|
|
66
66
|
serviceAccount: optionsOf()(keys("clientEmail", "privateKey", "clientId")),
|
|
67
67
|
callAdmission: CALL_ADMISSION,
|
|
@@ -27,6 +27,8 @@ export interface BasecampOptions {
|
|
|
27
27
|
instructions?: string;
|
|
28
28
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
29
29
|
maxResultBytes?: number;
|
|
30
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
31
|
+
readOnly?: boolean;
|
|
30
32
|
/**
|
|
31
33
|
* Optional per-runtime policy. There is no default: Basecamp's API documents
|
|
32
34
|
* several dynamically adjusted limits (GET versus POST, per second, hour,
|
|
@@ -27,6 +27,8 @@ export interface BreezeOptions {
|
|
|
27
27
|
callAdmission?: ConnectorCallAdmissionPolicy;
|
|
28
28
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
29
29
|
maxResultBytes?: number;
|
|
30
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
31
|
+
readOnly?: boolean;
|
|
30
32
|
/** Default `list_people` page size. Defaults to 100; connecta's cap is 1,000. */
|
|
31
33
|
defaultPageSize?: number;
|
|
32
34
|
}
|
|
@@ -98,6 +98,8 @@ export interface CcbOptions {
|
|
|
98
98
|
callAdmission?: ConnectorCallAdmissionPolicy;
|
|
99
99
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
100
100
|
maxResultBytes?: number;
|
|
101
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
102
|
+
readOnly?: boolean;
|
|
101
103
|
}
|
|
102
104
|
/** A maintained Church Community Builder (Pushpay ChMS) v2 connection. */
|
|
103
105
|
export declare const ccb: import("../../provider.js").ProviderFactory<CcbOptions>;
|
|
@@ -1651,7 +1651,7 @@ function defaultTitle(environment, mode, writable) {
|
|
|
1651
1651
|
}
|
|
1652
1652
|
/** The closed options ccb() accepts; see `assertKnownOptions`. */
|
|
1653
1653
|
const CCB_OPTIONS = optionsOf()({
|
|
1654
|
-
...keys("purpose", "title", "instructions", "environment", "mode", "clientId", "clientSecret", "access", "scopes", "subdomain", "defaultPerPage", "maxResultBytes"),
|
|
1654
|
+
...keys("purpose", "title", "instructions", "environment", "mode", "clientId", "clientSecret", "access", "scopes", "subdomain", "defaultPerPage", "maxResultBytes", "readOnly"),
|
|
1655
1655
|
callAdmission: CALL_ADMISSION,
|
|
1656
1656
|
});
|
|
1657
1657
|
/** A maintained Church Community Builder (Pushpay ChMS) v2 connection. */
|
|
@@ -15,6 +15,8 @@ interface CloudflareCommonOptions {
|
|
|
15
15
|
instructions?: string;
|
|
16
16
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
17
17
|
maxResultBytes?: number;
|
|
18
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
19
|
+
readOnly?: boolean;
|
|
18
20
|
}
|
|
19
21
|
/** Cloudflare's hosted MCP server (`search` and `execute`) over OAuth. */
|
|
20
22
|
export interface CloudflareOAuthOptions extends CloudflareCommonOptions {
|
|
@@ -68,6 +68,10 @@ const GLOBAL_API_KEY_CREDENTIAL = {
|
|
|
68
68
|
*/
|
|
69
69
|
const CLOUDFLARE_MCP_CLASSIFICATION = {
|
|
70
70
|
tools: {
|
|
71
|
+
docs: {
|
|
72
|
+
verdict: "read",
|
|
73
|
+
reason: "Searches Cloudflare documentation without executing API methods.",
|
|
74
|
+
},
|
|
71
75
|
search: {
|
|
72
76
|
verdict: "read",
|
|
73
77
|
reason: "Searches the Cloudflare OpenAPI contract without executing API methods.",
|
|
@@ -116,7 +120,7 @@ function keyUsageGuide(auth, options) {
|
|
|
116
120
|
|
|
117
121
|
REST with ${scheme}. Connector purpose: ${options.purpose}${auth === "globalApiKey" ? global_warning.trimEnd() : ""}${pin}${defaultLine}${key}${shared}${key_tail}${instructionsSection(options.instructions)}`;
|
|
118
122
|
}
|
|
119
|
-
const COMMON = keys("title", "authScope", "purpose", "instructions", "maxResultBytes");
|
|
123
|
+
const COMMON = keys("title", "authScope", "purpose", "instructions", "maxResultBytes", "readOnly");
|
|
120
124
|
const KEY_COMMON = keys("accountId", "zoneId", "baseUrl", "maxConcurrency");
|
|
121
125
|
const PIN = optionsOf()(keys("accountIds", "zoneIds"));
|
|
122
126
|
/** The closed options cloudflare() accepts, selected by `auth.type`; see `assertKnownOptions`. */
|