@zackbart/connecta 0.24.4 → 0.25.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 +40 -10
- package/CHANGELOG.md +253 -0
- package/dist/advertised-schema.js +53 -0
- package/dist/auth/downstream-oauth.js +490 -106
- package/dist/call-admission.js +139 -113
- package/dist/catalog-service.js +141 -105
- package/dist/catalog.js +258 -33
- package/dist/concurrency.js +24 -23
- package/dist/connector-scope.js +6 -37
- package/dist/connectors/api.d.ts +2 -2
- package/dist/connectors/remote-mcp.d.ts +6 -5
- package/dist/connectors/remote-mcp.js +317 -241
- package/dist/credential-contract.d.ts +9 -0
- package/dist/credential-rules.js +2 -2
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +45 -3
- package/dist/execute.js +363 -257
- package/dist/executor-admission.js +67 -50
- package/dist/executors/quickjs-runtime.js +4 -2
- package/dist/executors/quickjs.js +251 -210
- package/dist/fix-prompt.js +37 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +51 -20
- package/dist/invocation.js +324 -285
- package/dist/meta-tools.js +206 -92
- package/dist/oauth-callback-outcome.js +94 -0
- package/dist/oauth-sealing.js +29 -0
- package/dist/operator-ui/fix-prompts.js +87 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +46 -1
- package/dist/operator-ui/setup-commands.js +53 -0
- package/dist/operator-ui/view.js +38 -2
- package/dist/program-source.js +205 -0
- package/dist/providers/notion.js +2 -2
- package/dist/providers/revenuecat.js +32 -5
- package/dist/providers/stripe.d.ts +3 -2
- package/dist/providers/vercel.js +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/registry.js +305 -260
- package/dist/routes/activity.js +120 -137
- package/dist/routes/credentials.js +174 -163
- package/dist/routes/mcp.js +129 -70
- package/dist/routes/oauth-management.js +60 -71
- package/dist/routes/oauth.js +44 -16
- package/dist/routes/operator.js +82 -0
- package/dist/routes/shared.d.ts +2 -0
- package/dist/routes/ui-data.js +210 -0
- package/dist/routes/ui.js +64 -32
- package/dist/runtime/admission.js +43 -0
- package/dist/runtime/call-admission.js +67 -0
- package/dist/runtime/connector-scope.js +72 -0
- package/dist/runtime/run.js +174 -0
- package/dist/runtime/services.js +130 -0
- package/dist/runtime/storage.js +66 -0
- package/dist/server.js +111 -89
- package/dist/skills.js +1 -1
- package/dist/storage/file.js +39 -2
- package/dist/storage/memory.js +28 -16
- package/dist/timeout.js +17 -47
- package/dist/types.d.ts +27 -7
- package/dist/ui.d.ts +16 -2
- package/dist/ui.js +50 -166
- package/dist/url-safety.js +157 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +184 -10
- package/documentation/auth.md +80 -0
- package/documentation/code-mode.md +69 -24
- package/documentation/meta-tools.md +164 -27
- package/ethos.md +44 -47
- package/examples/worker/README.md +51 -5
- package/examples/worker/src/d1-storage.ts +116 -0
- package/examples/worker/src/index.ts +6 -6
- package/examples/worker/wrangler.jsonc +16 -4
- package/package.json +11 -3
- package/templates/node/package.json +1 -1
- package/dist/auth/downstream-oauth.d.ts +0 -200
- package/dist/catalog-fingerprint.d.ts +0 -19
- package/dist/catalog-limits.d.ts +0 -9
- package/dist/catalog-service.d.ts +0 -263
- package/dist/catalog.d.ts +0 -87
- package/dist/concurrency.d.ts +0 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -96
- package/dist/credential-rules.d.ts +0 -85
- package/dist/execute.d.ts +0 -154
- package/dist/executor-result.d.ts +0 -10
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -67
- package/dist/favicon.d.ts +0 -1
- package/dist/identity.d.ts +0 -4
- package/dist/invocation.d.ts +0 -74
- package/dist/mcp-result.d.ts +0 -8
- package/dist/meta-tools.d.ts +0 -129
- package/dist/operator-ui/generated.d.ts +0 -2
- package/dist/operator-ui/view.d.ts +0 -188
- package/dist/result-shapes.d.ts +0 -13
- package/dist/retired-toolkits.d.ts +0 -2
- package/dist/routes/activity.d.ts +0 -2
- package/dist/routes/credentials.d.ts +0 -2
- package/dist/routes/mcp.d.ts +0 -10
- package/dist/routes/oauth-management.d.ts +0 -2
- package/dist/routes/oauth.d.ts +0 -2
- package/dist/routes/ui.d.ts +0 -2
- package/dist/server.d.ts +0 -9
- package/dist/skills.d.ts +0 -44
- package/dist/timeout.d.ts +0 -21
- package/dist/tool-safety.d.ts +0 -9
|
@@ -1,9 +1,41 @@
|
|
|
1
1
|
import type { CatalogAccessObservation, CatalogDriftReport } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Which call path a tool takes, decided on the server by the same
|
|
4
|
+
* `isExplicitlyReadOnly` predicate discovery and invocation use
|
|
5
|
+
* (`src/tool-safety.ts`) and never re-derived in the browser: a second copy of
|
|
6
|
+
* the rule is a second place for it to be wrong.
|
|
7
|
+
*
|
|
8
|
+
* - `runs_in_programs` — explicitly read-only, so `execute_code` may call it.
|
|
9
|
+
* - `needs_approval` — everything else, which crosses `call_destructive_tool`.
|
|
10
|
+
*
|
|
11
|
+
* Planned resumable writes add a third value for a config `approval: "never"`
|
|
12
|
+
* exemption; the badge renders from a table keyed by this union so that value
|
|
13
|
+
* is one entry, not a new branch.
|
|
14
|
+
*/
|
|
15
|
+
export type UiToolSafety = "runs_in_programs" | "needs_approval";
|
|
2
16
|
export interface UiTool {
|
|
3
17
|
name: string;
|
|
4
18
|
address: string;
|
|
5
19
|
description?: string;
|
|
20
|
+
/** Absent only in payloads older than the field; the page then shows no badge. */
|
|
21
|
+
safety?: UiToolSafety;
|
|
6
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* What went wrong with a connector, as a closed set the server chooses from.
|
|
25
|
+
* The browser keys both its on-screen description and a fixed fix prompt off
|
|
26
|
+
* this. There is deliberately no message beside it: a connector's status
|
|
27
|
+
* message can quote a downstream error body, so it goes to the deployment's
|
|
28
|
+
* log and never into the operator payload.
|
|
29
|
+
*
|
|
30
|
+
* `auth_required` splits by who has to act: a downstream OAuth grant
|
|
31
|
+
* (`oauth_required`, which also covers an expired or revoked grant — the
|
|
32
|
+
* status seam cannot tell those apart), an operator-managed credential slot
|
|
33
|
+
* (`credential_required`), or a secret that lives in deployment configuration
|
|
34
|
+
* (`auth_required`).
|
|
35
|
+
*/
|
|
36
|
+
export type UiProblem = "connector_unavailable" | "oauth_required" | "credential_required" | "auth_required" | "credential_mismatch" | "catalog_failed";
|
|
37
|
+
/** A stored credential that cannot be used, by cause. */
|
|
38
|
+
export type UiCredentialProblem = "credential_mismatch" | "credential_unreadable";
|
|
7
39
|
interface UiCredentialField {
|
|
8
40
|
name: string;
|
|
9
41
|
label: string;
|
|
@@ -26,6 +58,8 @@ interface UiCredential {
|
|
|
26
58
|
updatedAt?: string;
|
|
27
59
|
testable: boolean;
|
|
28
60
|
error?: string;
|
|
61
|
+
/** Present exactly when `error` is: the cause, for the fix prompt. */
|
|
62
|
+
problem?: UiCredentialProblem;
|
|
29
63
|
/**
|
|
30
64
|
* Something true and non-blocking about a working credential — today, that
|
|
31
65
|
* the vault still holds fields the connector has stopped declaring.
|
|
@@ -45,7 +79,12 @@ export interface UiConnector {
|
|
|
45
79
|
manageSharedAuth: boolean;
|
|
46
80
|
connectPersonal: boolean;
|
|
47
81
|
};
|
|
48
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Why this connector is not usable, when it is not. See `UiProblem`. The
|
|
84
|
+
* only failure detail the payload carries; the status message behind it is
|
|
85
|
+
* logged on the server, not shipped.
|
|
86
|
+
*/
|
|
87
|
+
problem?: UiProblem;
|
|
49
88
|
authorizationUrl?: string;
|
|
50
89
|
toolCount: number;
|
|
51
90
|
tools: UiTool[];
|
|
@@ -77,6 +116,12 @@ export interface UiData {
|
|
|
77
116
|
credentialManagement: CredentialManagementCapability;
|
|
78
117
|
/** True when this interactive human may manage any visible OAuth connector. */
|
|
79
118
|
oauthManagement: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Names of the `/mcp/<name>` pools this identity's grant admits. A pool
|
|
121
|
+
* whose grant refuses or throws is absent, exactly as the endpoint answers
|
|
122
|
+
* it with a 404, so the page never enumerates a pool its reader cannot open.
|
|
123
|
+
*/
|
|
124
|
+
pools?: string[];
|
|
80
125
|
}
|
|
81
126
|
export interface FilteredUiConnector {
|
|
82
127
|
connector: UiConnector;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client setup snippets for one MCP endpoint: the deployment's `/mcp` or a
|
|
3
|
+
* `/mcp/<pool>`. Three formats and no more — Claude Code's CLI, Codex's CLI,
|
|
4
|
+
* and the `mcpServers` JSON most other clients read — because a longer list
|
|
5
|
+
* is a list to keep current.
|
|
6
|
+
*
|
|
7
|
+
* None of them carries a credential. A client authenticates through the
|
|
8
|
+
* deployment's inbound auth — OAuth discovery where that is configured — and
|
|
9
|
+
* a token pasted into a snippet on this page would be one rendered by it.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The name a client files this server under. Derived from the deployment's
|
|
13
|
+
* configured server name, reduced to the characters every client CLI accepts
|
|
14
|
+
* unquoted, with the pool appended so two endpoints never collide.
|
|
15
|
+
*/
|
|
16
|
+
export function clientServerName(serverName, pool) {
|
|
17
|
+
const base = (serverName ?? "")
|
|
18
|
+
.toLowerCase()
|
|
19
|
+
.replace(/[^a-z0-9_-]+/g, "-")
|
|
20
|
+
.replace(/-{2,}/g, "-")
|
|
21
|
+
.replace(/^-+|-+$/g, "")
|
|
22
|
+
.slice(0, 48) || "connecta";
|
|
23
|
+
return pool ? `${base}-${pool}` : base;
|
|
24
|
+
}
|
|
25
|
+
/** The URL for a pool: `/mcp` plus its name, which config limits to `[a-z0-9_-]`. */
|
|
26
|
+
export function poolEndpointUrl(mcpUrl, pool) {
|
|
27
|
+
return `${mcpUrl.replace(/\/+$/, "")}/${encodeURIComponent(pool)}`;
|
|
28
|
+
}
|
|
29
|
+
/** POSIX single-quoting, only when the value needs it. */
|
|
30
|
+
function shellWord(value) {
|
|
31
|
+
return /^[A-Za-z0-9_./:@%+=,~-]+$/.test(value)
|
|
32
|
+
? value
|
|
33
|
+
: `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
34
|
+
}
|
|
35
|
+
export function clientSetupCommands(name, url) {
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
id: "claude",
|
|
39
|
+
label: "Claude Code",
|
|
40
|
+
text: `claude mcp add --transport http ${shellWord(name)} ${shellWord(url)}`,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "codex",
|
|
44
|
+
label: "Codex",
|
|
45
|
+
text: `codex mcp add ${shellWord(name)} --url ${shellWord(url)}`,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "json",
|
|
49
|
+
label: "JSON config",
|
|
50
|
+
text: JSON.stringify({ mcpServers: { [name]: { type: "http", url } } }, null, 2),
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
}
|
package/dist/operator-ui/view.js
CHANGED
|
@@ -13,8 +13,8 @@ export function pageForPath(path) {
|
|
|
13
13
|
export function info(message) {
|
|
14
14
|
return { message, tone: "info" };
|
|
15
15
|
}
|
|
16
|
-
export function failure(message) {
|
|
17
|
-
return { message, tone: "error" };
|
|
16
|
+
export function failure(message, fix) {
|
|
17
|
+
return fix ? { message, tone: "error", fix } : { message, tone: "error" };
|
|
18
18
|
}
|
|
19
19
|
export function initialState(page) {
|
|
20
20
|
return {
|
|
@@ -150,6 +150,42 @@ export function connectorSummaryParts(summary) {
|
|
|
150
150
|
{ text: toolCountLabel(summary.tools), tone: "neutral" },
|
|
151
151
|
];
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* The badge for each call path, keyed by the server's classification so the
|
|
155
|
+
* planned third state is a row here rather than a new branch in a component.
|
|
156
|
+
*/
|
|
157
|
+
export const TOOL_SAFETY_BADGE = {
|
|
158
|
+
runs_in_programs: {
|
|
159
|
+
label: "runs in programs",
|
|
160
|
+
tone: "ok",
|
|
161
|
+
title: "Explicitly read-only: execute_code programs may call it without asking.",
|
|
162
|
+
},
|
|
163
|
+
needs_approval: {
|
|
164
|
+
label: "needs approval",
|
|
165
|
+
tone: "warn",
|
|
166
|
+
title: "Not explicitly read-only: calls cross call_destructive_tool, where the host asks first.",
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* What the page says about a connector that is not usable, one fixed sentence
|
|
171
|
+
* per problem kind the server classified. This is the only account of a
|
|
172
|
+
* failure the Connections page renders: a connector's status message can quote
|
|
173
|
+
* a downstream error body, and a downstream error body can quote the secret
|
|
174
|
+
* that was just rejected, so the raw text stays in the deployment's log and
|
|
175
|
+
* never reaches the payload or the DOM. The fix prompt beside it is keyed off
|
|
176
|
+
* the same kind.
|
|
177
|
+
*/
|
|
178
|
+
const PROBLEM_COPY = {
|
|
179
|
+
connector_unavailable: "Unavailable: its status check or catalog load failed, or did not finish in time. The deployment's log has the downstream error.",
|
|
180
|
+
oauth_required: "Needs OAuth authorization: no grant is stored, or the stored grant expired or was revoked.",
|
|
181
|
+
credential_required: "Needs a credential: nothing usable is stored in its credential slot.",
|
|
182
|
+
auth_required: "Needs authorization. Its secret lives in deployment configuration, not on this page.",
|
|
183
|
+
credential_mismatch: "The stored credential does not match the fields this connector declares, so it cannot be used.",
|
|
184
|
+
catalog_failed: "Connected, but its tool catalog could not be loaded, so none of its tools are served.",
|
|
185
|
+
};
|
|
186
|
+
export function problemCopy(problem) {
|
|
187
|
+
return problem ? PROBLEM_COPY[problem] ?? null : null;
|
|
188
|
+
}
|
|
153
189
|
/** Who owns this connector's downstream credentials, in two words. */
|
|
154
190
|
export function authScopeLabel(scope) {
|
|
155
191
|
return scope === "personal" ? "personal auth" : "shared auth";
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// Program source handling shared by every executor. Web-API only: execute_code
|
|
2
|
+
// runs it before handing a program to QuickJS or a Dynamic Worker, and the
|
|
3
|
+
// QuickJS normalizer runs it too for callers that drive that executor directly.
|
|
4
|
+
/** A whole-program markdown fence, as QuickJS's normalizer recognizes one. */
|
|
5
|
+
const FENCE = /^```[\w-]*\s*\n([\s\S]*?)\n?```$/;
|
|
6
|
+
/** Leading whitespace and comments, which may precede the arrow expression. */
|
|
7
|
+
const LEADING_TRIVIA = /^(?:\s+|\/\/[^\n]*|\/\*[\s\S]*?\*\/)+/;
|
|
8
|
+
/** One character of an identifier, keyword, or number; `\\` for escapes. */
|
|
9
|
+
const WORD = /[\w$\\\u0080-\uffff]/;
|
|
10
|
+
/**
|
|
11
|
+
* Words after which a `/` begins a regular expression rather than dividing.
|
|
12
|
+
* Any other word is an identifier or a literal, which a `/` divides.
|
|
13
|
+
*/
|
|
14
|
+
const REGEX_AFTER_WORD = new Set([
|
|
15
|
+
"await", "case", "delete", "do", "else", "in", "instanceof", "new", "of",
|
|
16
|
+
"return", "throw", "typeof", "void", "yield",
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Remove the statement terminators after a program's arrow expression:
|
|
20
|
+
* `async () => { … };`, with any whitespace or comments around the trailing
|
|
21
|
+
* semicolons, becomes `async () => { … }` with those comments kept. Both
|
|
22
|
+
* executors evaluate a program as a parenthesized expression, where a
|
|
23
|
+
* terminator is a syntax error on QuickJS and, on a Dynamic Worker, either the
|
|
24
|
+
* same error or a program that silently returns `undefined`. Models often end
|
|
25
|
+
* a program with `};`, so P1 accepts it on both.
|
|
26
|
+
*
|
|
27
|
+
* Deliberately narrow, so it cannot broaden what a program is: only a program
|
|
28
|
+
* whose first token is `async` or `(` — what QuickJS treats as a function
|
|
29
|
+
* expression — and only when every semicolon at bracket depth zero trails it,
|
|
30
|
+
* with nothing but whitespace and comments after. `async () => 1; 2();` keeps
|
|
31
|
+
* both its semicolons, and a bare statement body is never touched, since its
|
|
32
|
+
* trailing `;` can be an empty loop body. A whole-program markdown fence is
|
|
33
|
+
* looked through, and dropped when a terminator came out from under it.
|
|
34
|
+
*
|
|
35
|
+
* A small lexer, not a parser: it skips strings, template literals (with
|
|
36
|
+
* `${}` nesting), comments, and regular expressions, telling a regex from a
|
|
37
|
+
* division by the token before it. Any source it cannot close — an
|
|
38
|
+
* unterminated string or comment, unbalanced brackets — comes back unchanged,
|
|
39
|
+
* so the executor reports the same error it always did.
|
|
40
|
+
*/
|
|
41
|
+
export function withoutProgramTerminator(code) {
|
|
42
|
+
const fenced = FENCE.exec(code.trim())?.[1];
|
|
43
|
+
const source = fenced === undefined ? code : fenced.trim();
|
|
44
|
+
const stripped = stripTerminators(source);
|
|
45
|
+
return stripped === source ? code : stripped;
|
|
46
|
+
}
|
|
47
|
+
function stripTerminators(source) {
|
|
48
|
+
const head = source.replace(LEADING_TRIVIA, "");
|
|
49
|
+
if (!/^async\b/.test(head) && !head.startsWith("("))
|
|
50
|
+
return source;
|
|
51
|
+
const n = source.length;
|
|
52
|
+
let depth = 0;
|
|
53
|
+
/** Bracket depth at which each open template substitution resumes its template. */
|
|
54
|
+
const substitutions = [];
|
|
55
|
+
/** Positions of the top-level semicolons, all of which must trail. */
|
|
56
|
+
const terminators = [];
|
|
57
|
+
/**
|
|
58
|
+
* The previous significant token: a punctuator character or a word, with a
|
|
59
|
+
* string, template, or regex literal recorded as `0` — a value, like a number.
|
|
60
|
+
*/
|
|
61
|
+
let previous = "";
|
|
62
|
+
let i = 0;
|
|
63
|
+
/** Scan template text from `from` to its closing backtick or next `${`. */
|
|
64
|
+
const template = (from) => {
|
|
65
|
+
for (let j = from; j < n; j++) {
|
|
66
|
+
const c = source[j];
|
|
67
|
+
if (c === "\\") {
|
|
68
|
+
j++;
|
|
69
|
+
}
|
|
70
|
+
else if (c === "`") {
|
|
71
|
+
return j + 1;
|
|
72
|
+
}
|
|
73
|
+
else if (c === "$" && source[j + 1] === "{") {
|
|
74
|
+
substitutions.push(depth);
|
|
75
|
+
depth++;
|
|
76
|
+
return j + 2;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
};
|
|
81
|
+
while (i < n) {
|
|
82
|
+
const c = source[i];
|
|
83
|
+
if (/\s/.test(c)) {
|
|
84
|
+
i++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (c === "/" && source[i + 1] === "/") {
|
|
88
|
+
const end = source.indexOf("\n", i);
|
|
89
|
+
i = end === -1 ? n : end;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (c === "/" && source[i + 1] === "*") {
|
|
93
|
+
const end = source.indexOf("*/", i + 2);
|
|
94
|
+
if (end === -1)
|
|
95
|
+
return source;
|
|
96
|
+
i = end + 2;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (c === ";") {
|
|
100
|
+
if (depth === 0)
|
|
101
|
+
terminators.push(i);
|
|
102
|
+
previous = ";";
|
|
103
|
+
i++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
// Code after a top-level semicolon means the program is more than one
|
|
107
|
+
// expression; that is for the executor to refuse, not for us to repair.
|
|
108
|
+
if (terminators.length > 0)
|
|
109
|
+
return source;
|
|
110
|
+
if (c === '"' || c === "'") {
|
|
111
|
+
let j = i + 1;
|
|
112
|
+
while (j < n && source[j] !== c) {
|
|
113
|
+
if (source[j] === "\n")
|
|
114
|
+
return source;
|
|
115
|
+
j += source[j] === "\\" ? 2 : 1;
|
|
116
|
+
}
|
|
117
|
+
if (j >= n)
|
|
118
|
+
return source;
|
|
119
|
+
i = j + 1;
|
|
120
|
+
previous = "0";
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (c === "`") {
|
|
124
|
+
const end = template(i + 1);
|
|
125
|
+
if (end === undefined)
|
|
126
|
+
return source;
|
|
127
|
+
i = end;
|
|
128
|
+
previous = source[end - 1] === "`" ? "0" : "{";
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (c === "}" && substitutions.at(-1) === depth - 1) {
|
|
132
|
+
substitutions.pop();
|
|
133
|
+
depth--;
|
|
134
|
+
const end = template(i + 1);
|
|
135
|
+
if (end === undefined)
|
|
136
|
+
return source;
|
|
137
|
+
i = end;
|
|
138
|
+
previous = source[end - 1] === "`" ? "0" : "{";
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (c === "/" && startsRegex(previous)) {
|
|
142
|
+
let j = i + 1;
|
|
143
|
+
let inClass = false;
|
|
144
|
+
for (; j < n; j++) {
|
|
145
|
+
const r = source[j];
|
|
146
|
+
if (r === "\n")
|
|
147
|
+
return source;
|
|
148
|
+
if (r === "\\")
|
|
149
|
+
j++;
|
|
150
|
+
else if (r === "[")
|
|
151
|
+
inClass = true;
|
|
152
|
+
else if (r === "]")
|
|
153
|
+
inClass = false;
|
|
154
|
+
else if (r === "/" && !inClass)
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if (j >= n)
|
|
158
|
+
return source;
|
|
159
|
+
i = j + 1;
|
|
160
|
+
while (i < n && /[\w$]/.test(source[i]))
|
|
161
|
+
i++;
|
|
162
|
+
previous = "0";
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (WORD.test(c)) {
|
|
166
|
+
let j = i + 1;
|
|
167
|
+
while (j < n && WORD.test(source[j]))
|
|
168
|
+
j++;
|
|
169
|
+
previous = source.slice(i, j);
|
|
170
|
+
i = j;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (c === "(" || c === "[" || c === "{")
|
|
174
|
+
depth++;
|
|
175
|
+
else if (c === ")" || c === "]" || c === "}") {
|
|
176
|
+
if (depth === 0)
|
|
177
|
+
return source;
|
|
178
|
+
depth--;
|
|
179
|
+
}
|
|
180
|
+
previous = c;
|
|
181
|
+
i++;
|
|
182
|
+
}
|
|
183
|
+
if (depth !== 0 || substitutions.length > 0 || terminators.length === 0) {
|
|
184
|
+
return source;
|
|
185
|
+
}
|
|
186
|
+
let out = "";
|
|
187
|
+
let from = 0;
|
|
188
|
+
for (const at of terminators) {
|
|
189
|
+
out += source.slice(from, at);
|
|
190
|
+
from = at + 1;
|
|
191
|
+
}
|
|
192
|
+
return out + source.slice(from);
|
|
193
|
+
}
|
|
194
|
+
/** Whether a `/` after `previous` opens a regular expression. */
|
|
195
|
+
function startsRegex(previous) {
|
|
196
|
+
if (previous === "")
|
|
197
|
+
return true;
|
|
198
|
+
if (WORD.test(previous[0])) {
|
|
199
|
+
// A number or an identifier divides; a keyword introduces an operand.
|
|
200
|
+
return REGEX_AFTER_WORD.has(previous);
|
|
201
|
+
}
|
|
202
|
+
// After a closing bracket a value precedes; after any other punctuator
|
|
203
|
+
// an operand is expected.
|
|
204
|
+
return previous !== ")" && previous !== "]" && previous !== "}";
|
|
205
|
+
}
|
package/dist/providers/notion.js
CHANGED
|
@@ -89,7 +89,7 @@ function notionFailure(status, body, headers) {
|
|
|
89
89
|
return new ConnectorCallError("unavailable", `${labelled} Notion is overloaded; retry after the reported window.`, { retryAfterMs: retryAfter ?? 5_000 });
|
|
90
90
|
}
|
|
91
91
|
if (status === 401) {
|
|
92
|
-
return new ConnectorCallError("auth_required", `${labelled} The Notion integration token is missing or invalid
|
|
92
|
+
return new ConnectorCallError("auth_required", `${labelled} The Notion integration token is missing or invalid. Call authorize_connector for recovery options. When available, an operator can set a valid token in this connection in the operator UI.`);
|
|
93
93
|
}
|
|
94
94
|
if (status === 403) {
|
|
95
95
|
return new ConnectorCallError("connector_call_failed", `${labelled} The token is valid but this integration is not allowed to perform this operation. An operator must enable the matching capability on the Notion integration (comment capabilities are off by default) or share the object with it. Re-authorizing will not help.`, { retryable: false });
|
|
@@ -133,7 +133,7 @@ const send = guardedFetch({
|
|
|
133
133
|
authenticate: async (ctx) => {
|
|
134
134
|
const token = (await ctx.credential?.get())?.trim();
|
|
135
135
|
if (!token) {
|
|
136
|
-
throw new ConnectorCallError("auth_required", "No Notion integration token is configured for this connector
|
|
136
|
+
throw new ConnectorCallError("auth_required", "No Notion integration token is configured for this connector. Call authorize_connector for recovery options. When available, an operator can add the token in this connection in the operator UI.");
|
|
137
137
|
}
|
|
138
138
|
return { Authorization: `Bearer ${token}` };
|
|
139
139
|
},
|
|
@@ -5,7 +5,7 @@ import { defined } from "../connectors/api.js";
|
|
|
5
5
|
export const REVENUECAT_MCP_ENDPOINT = "https://mcp.revenuecat.ai/mcp";
|
|
6
6
|
/**
|
|
7
7
|
* Reviewed reads, every `Read` row of RevenueCat's tool reference as read on
|
|
8
|
-
* 2026-
|
|
8
|
+
* 2026-09-21 (P5). The list is a superset: plan, platform, and beta enrollment
|
|
9
9
|
* gate parts of the catalog, so a name this account never returns costs
|
|
10
10
|
* nothing while an unlisted new one fails closed.
|
|
11
11
|
*/
|
|
@@ -23,7 +23,11 @@ const READ_ONLY_TOOLS = new Set([
|
|
|
23
23
|
// Products and prices
|
|
24
24
|
"get-product",
|
|
25
25
|
"get-product-store-state",
|
|
26
|
+
// Deprecated in RevenueCat's reference — superseded by the plan family's
|
|
27
|
+
// own read-back below — but still served, so the classification stands.
|
|
26
28
|
"get-product-store-state-operation",
|
|
29
|
+
"get-product-store-state-plan",
|
|
30
|
+
"list-product-store-state-plans",
|
|
27
31
|
"list-products",
|
|
28
32
|
// Entitlements
|
|
29
33
|
"get-entitlement",
|
|
@@ -100,19 +104,36 @@ const WRITE_TOOLS = new Map([
|
|
|
100
104
|
["archive-product", "destructive"],
|
|
101
105
|
["create-product", "additive"],
|
|
102
106
|
// Named `create-`, but "Configure prices for a product": the price set
|
|
103
|
-
// already exists, so this overwrites it, and it is money-facing.
|
|
107
|
+
// already exists, so this overwrites it, and it is money-facing. Deprecated
|
|
108
|
+
// in RevenueCat's reference; products are priced through the plan family
|
|
109
|
+
// below, but the tool is still served, so the classification stands.
|
|
104
110
|
["create-product-prices", "destructive"],
|
|
105
111
|
// "Fills *missing* App Store subscription territory prices" — by
|
|
106
|
-
// RevenueCat's own word it writes only where nothing is set.
|
|
112
|
+
// RevenueCat's own word it writes only where nothing is set. Deprecated in
|
|
113
|
+
// RevenueCat's reference; still served, so the classification stands.
|
|
107
114
|
["equalize-subscription-prices", "additive"],
|
|
108
|
-
// An upsert.
|
|
115
|
+
// An upsert. Deprecated in RevenueCat's reference — the store-state plan
|
|
116
|
+
// family below replaces it — but still served, so the classification stands.
|
|
109
117
|
["set-product-store-state", "destructive"],
|
|
110
118
|
// Sends products to Apple for review.
|
|
111
119
|
["submit-products-to-store", "destructive"],
|
|
112
120
|
["unarchive-product", "destructive"],
|
|
113
121
|
["update-product", "destructive"],
|
|
114
122
|
// Reserves a new App Store Connect review screenshot slot; replaces nothing.
|
|
123
|
+
// Deprecated in RevenueCat's reference; still served, so the classification
|
|
124
|
+
// stands.
|
|
115
125
|
["upload-product-store-state-screenshot", "additive"],
|
|
126
|
+
// Product store state plans, the workflow RevenueCat deprecated
|
|
127
|
+
// `set-product-store-state` for. Create brings a draft into being and
|
|
128
|
+
// touches nothing else; plan recomputes an existing plan's proposed change,
|
|
129
|
+
// overwriting its previous planning; apply pushes the whole plan into
|
|
130
|
+
// RevenueCat and the app stores; update and discard act on an existing plan
|
|
131
|
+
// by name. Every step past create is asynchronous.
|
|
132
|
+
["create-product-store-state-plan", "additive"],
|
|
133
|
+
["plan-product-store-state-plan", "destructive"],
|
|
134
|
+
["apply-product-store-state-plan", "destructive"],
|
|
135
|
+
["update-product-store-state-plan", "destructive"],
|
|
136
|
+
["discard-product-store-state-plan", "destructive"],
|
|
116
137
|
// Entitlements
|
|
117
138
|
["archive-entitlement", "destructive"],
|
|
118
139
|
// Attach adds membership and removes nothing; detach is the destructive half.
|
|
@@ -135,6 +156,11 @@ const WRITE_TOOLS = new Map([
|
|
|
135
156
|
// Targeting and audiences
|
|
136
157
|
["create-audience", "additive"],
|
|
137
158
|
["update-audience", "destructive"],
|
|
159
|
+
// Targeting rules follow the verb: create brings one into being, update
|
|
160
|
+
// and delete act on a rule the project already has.
|
|
161
|
+
["create-targeting-rule", "additive"],
|
|
162
|
+
["update-targeting-rule", "destructive"],
|
|
163
|
+
["delete-targeting-rule", "destructive"],
|
|
138
164
|
// Paywalls
|
|
139
165
|
["attach-offering-to-paywall", "destructive"],
|
|
140
166
|
["detach-offering-from-paywall", "destructive"],
|
|
@@ -204,7 +230,8 @@ function sharedUsageGuide() {
|
|
|
204
230
|
- Customer and subscription objects are large, and a customer's history is larger. Page with the cursor the list returned rather than raising the page size, and reduce inside \`execute_code\` — select the fields the question needs and return those, not the whole object.
|
|
205
231
|
- Whether a customer should have access is \`gives_access\` on each subscription from \`list-subscriptions\`, which RevenueCat calls the authoritative flag. \`status\` and \`expires_date\` describe the store-side state and disagree with it during grace periods, billing retries, and promotional grants — answer access questions from \`gives_access\` and say which subscription it came from.
|
|
206
232
|
- \`get-chart-data\` is the metrics path: read \`get-chart-options-schema\` for the chart you want before calling it, rather than guessing an option name. \`get-overview-metrics\` and \`get-revenue-metric\` answer the summary questions in one call.
|
|
207
|
-
- \`create-paywall-ai
|
|
233
|
+
- \`create-paywall-ai\` and \`edit-paywall-ai\` are asynchronous. Poll the task id they return with \`get-paywall-ai-task\` rather than assuming the work finished when the call returned.
|
|
234
|
+
- Store changes ride the plan workflow, not the deprecated direct tools: \`create-product-store-state-plan\`, then \`plan-product-store-state-plan\`, then \`apply-product-store-state-plan\`. Every step past the create is asynchronous — read the plan back with \`get-product-store-state-plan\` between steps instead of assuming the last one finished.
|
|
208
235
|
- This connection's tool list is not a fixed set. RevenueCat gates parts of its MCP catalog by plan, platform, and beta enrollment — paywall AI editing, benchmarks, experiments, virtual currencies, and the account-billing tools are the usual absentees — so search this connector for what it actually exposes rather than assuming a documented tool is here.
|
|
209
236
|
- \`render-paywall-screenshot\` is unclassified on purpose because RevenueCat's reference gives it no access column. The current server marks it read-only, which Connecta preserves; without that annotation it fails closed onto \`call_destructive_tool\`.
|
|
210
237
|
- RevenueCat meters API v2 per minute and per domain, and the domains differ: 480 requests per minute for customer information and virtual currencies, 60 for project configuration and audiences, 25 for charts and metrics. It answers a breach with \`429\`, a \`Retry-After\` header, and a \`backoff_ms\` field. Back off on that rather than retrying immediately, and expect chart sweeps to hit the ceiling long before customer reads do.
|
|
@@ -28,8 +28,9 @@ export interface StripeOAuthOptions extends StripeCommonOptions {
|
|
|
28
28
|
* Static credentials have one fixed mode, including Stripe Connect calls.
|
|
29
29
|
*
|
|
30
30
|
* Both static shapes belong here: a key the deployment supplies as a literal
|
|
31
|
-
* header, and one the operator pastes
|
|
32
|
-
* its own mode
|
|
31
|
+
* header, and one the operator pastes into the connection in the operator UI.
|
|
32
|
+
* Neither can discover its own mode, since a restricted key answers for
|
|
33
|
+
* exactly one, so both declare it.
|
|
33
34
|
*/
|
|
34
35
|
export interface StripeHeaderOptions extends StripeCommonOptions {
|
|
35
36
|
auth: Exclude<RemoteMcpAuth, {
|
package/dist/providers/vercel.js
CHANGED
|
@@ -138,7 +138,7 @@ function vercelTransport(baseUrl) {
|
|
|
138
138
|
authenticate: async (ctx) => {
|
|
139
139
|
const token = (await ctx.credential?.get())?.trim();
|
|
140
140
|
if (!token) {
|
|
141
|
-
throw new ConnectorCallError("auth_required", "No Vercel access token is configured for this connector.
|
|
141
|
+
throw new ConnectorCallError("auth_required", "No Vercel access token is configured for this connector. Call authorize_connector for recovery options. When available, an operator can add the token in this connection in the operator UI.");
|
|
142
142
|
}
|
|
143
143
|
return { Authorization: `Bearer ${token}` };
|
|
144
144
|
},
|
package/dist/registry.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export interface RegistryOptions {
|
|
|
58
58
|
/**
|
|
59
59
|
* Cap on inline result size before truncation + get_result paging. Must be a
|
|
60
60
|
* whole number of bytes >= 1; anything else warns at startup and falls back
|
|
61
|
-
* to the default
|
|
61
|
+
* to the default 24_000.
|
|
62
62
|
*/
|
|
63
63
|
maxResultBytes?: number | undefined;
|
|
64
64
|
results?: {
|