@zackbart/connecta 0.22.1 → 0.22.3
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 +6 -5
- package/CHANGELOG.md +59 -0
- package/README.md +4 -2
- package/dist/auth/downstream-oauth.d.ts +3 -1
- package/dist/auth/downstream-oauth.js +11 -0
- package/dist/catalog-drift.d.ts +3 -3
- package/dist/providers/cloudflare.d.ts +30 -9
- package/dist/providers/cloudflare.js +75 -9
- package/dist/providers/notion.d.ts +28 -7
- package/dist/providers/notion.js +106 -9
- package/dist/providers/revenuecat.js +10 -0
- package/dist/providers/stripe.js +7 -0
- package/dist/providers/vercel.d.ts +29 -10
- package/dist/providers/vercel.js +118 -11
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/cloudflare.md +51 -11
- package/documentation/connectors.md +23 -5
- package/documentation/linear.md +9 -0
- package/documentation/mixpanel.md +13 -3
- package/documentation/notion.md +49 -12
- package/documentation/operations.md +12 -7
- package/documentation/provider-conventions.md +61 -47
- package/documentation/revenuecat.md +14 -10
- package/documentation/stripe.md +7 -0
- package/documentation/upgrading.md +5 -5
- package/documentation/vercel.md +70 -23
- package/package.json +2 -1
- package/templates/node/package.json +1 -1
|
@@ -1,25 +1,44 @@
|
|
|
1
1
|
import type { Connector, ConnectorCallAdmissionPolicy } from "../types.js";
|
|
2
2
|
/** Vercel's public REST origin. Override only for a proxy or test double. */
|
|
3
3
|
export declare const VERCEL_API_BASE_URL = "https://api.vercel.com";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/** Vercel's official hosted MCP endpoint. */
|
|
5
|
+
export declare const VERCEL_MCP_ENDPOINT = "https://mcp.vercel.com";
|
|
6
|
+
interface VercelCommonOptions {
|
|
7
|
+
/** Human-readable display name; defaults identify the selected surface. */
|
|
6
8
|
title?: string;
|
|
7
9
|
/** Downstream auth ownership. Defaults to one shared deployment grant. */
|
|
8
10
|
authScope?: "shared" | "personal";
|
|
9
11
|
/** Which Vercel account or team this connection operates, and for whom. */
|
|
10
12
|
purpose: string;
|
|
11
|
-
/** Default team id for scoped calls. Omit to use the token's personal account. */
|
|
12
|
-
teamId?: string;
|
|
13
13
|
/** Account-specific conventions appended to the maintained provider guide. */
|
|
14
14
|
instructions?: string;
|
|
15
|
-
/** API base override for a proxy or test double. */
|
|
16
|
-
baseUrl?: string;
|
|
17
|
-
/** Default page size for list tools. Defaults to 20; Vercel's local cap is 100. */
|
|
18
|
-
defaultPageSize?: number;
|
|
19
15
|
/** Optional per-runtime downstream call-admission policy. */
|
|
20
16
|
callAdmission?: ConnectorCallAdmissionPolicy;
|
|
21
17
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
22
18
|
maxResultBytes?: number;
|
|
23
19
|
}
|
|
24
|
-
/**
|
|
25
|
-
export
|
|
20
|
+
/** Connecta's maintained hand-written Vercel REST surface. */
|
|
21
|
+
export interface VercelApiOptions extends VercelCommonOptions {
|
|
22
|
+
/** Omit for backward compatibility; the hand-written API surface is the default. */
|
|
23
|
+
surface?: "api";
|
|
24
|
+
/** Default team id for scoped calls. Omit to use the token's personal account. */
|
|
25
|
+
teamId?: string;
|
|
26
|
+
/** API base override for a proxy or test double. */
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
/** Default page size for list tools. Defaults to 20; Vercel's local cap is 100. */
|
|
29
|
+
defaultPageSize?: number;
|
|
30
|
+
}
|
|
31
|
+
/** Vercel's official hosted MCP surface, authenticated through OAuth. */
|
|
32
|
+
export interface VercelMcpOptions extends VercelCommonOptions {
|
|
33
|
+
surface: "mcp";
|
|
34
|
+
}
|
|
35
|
+
/** Backward-compatible API options; existing consumers may extend this interface. */
|
|
36
|
+
export interface VercelOptions extends VercelApiOptions {
|
|
37
|
+
}
|
|
38
|
+
/** Select one Vercel surface when deployment configuration constructs it. */
|
|
39
|
+
export type VercelConnectionOptions = VercelOptions | VercelMcpOptions;
|
|
40
|
+
/** Release-reviewed Vercel MCP inventory and safety verdicts. */
|
|
41
|
+
export declare const VERCEL_MCP_VETTED_CATALOG: import("../catalog-drift.js").VettedCatalog;
|
|
42
|
+
/** A maintained Vercel connection using the selected provider surface. */
|
|
43
|
+
export declare function vercel(id: string, options: VercelConnectionOptions): Connector;
|
|
44
|
+
export {};
|
package/dist/providers/vercel.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/** See documentation/vercel.md#no-sdk-on-purpose. */
|
|
2
|
-
import { api } from "../connectors/api.js";
|
|
2
|
+
import { api, defined } from "../connectors/api.js";
|
|
3
|
+
import { remoteMcp } from "../connectors/remote-mcp.js";
|
|
4
|
+
import { vettedCatalog, withVettedCatalog } from "../catalog-drift.js";
|
|
3
5
|
import { guardedFetch, retryAfterMs, } from "../connectors/guarded-fetch.js";
|
|
4
6
|
import { ConnectorCallError } from "../errors.js";
|
|
5
7
|
import { withDeadline } from "../timeout.js";
|
|
6
8
|
/** Vercel's public REST origin. Override only for a proxy or test double. */
|
|
7
9
|
export const VERCEL_API_BASE_URL = "https://api.vercel.com";
|
|
10
|
+
/** Vercel's official hosted MCP endpoint. */
|
|
11
|
+
export const VERCEL_MCP_ENDPOINT = "https://mcp.vercel.com";
|
|
8
12
|
const MAX_PAGE_SIZE = 100;
|
|
9
13
|
const DEFAULT_PAGE_SIZE = 20;
|
|
10
14
|
const VERCEL_MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
@@ -755,7 +759,7 @@ function tools(send, defaultPageSize, defaultTeamId) {
|
|
|
755
759
|
type: { type: "string" }, createdAt: { type: "number" },
|
|
756
760
|
message: { type: "string" }, payload: { type: "object" },
|
|
757
761
|
},
|
|
758
|
-
required: ["type"
|
|
762
|
+
required: ["type"],
|
|
759
763
|
},
|
|
760
764
|
},
|
|
761
765
|
},
|
|
@@ -776,7 +780,8 @@ function tools(send, defaultPageSize, defaultTeamId) {
|
|
|
776
780
|
const event = asRecord(value);
|
|
777
781
|
const eventPayload = asRecord(event["payload"]);
|
|
778
782
|
return compact({
|
|
779
|
-
type: event["type"]
|
|
783
|
+
type: event["type"] ?? "unknown",
|
|
784
|
+
createdAt: event["created"] ?? event["date"],
|
|
780
785
|
message: eventPayload["text"] ?? eventPayload["message"],
|
|
781
786
|
payload: Object.keys(eventPayload).length === 0 ? undefined : eventPayload,
|
|
782
787
|
});
|
|
@@ -1029,7 +1034,7 @@ function tools(send, defaultPageSize, defaultTeamId) {
|
|
|
1029
1034
|
},
|
|
1030
1035
|
];
|
|
1031
1036
|
}
|
|
1032
|
-
function
|
|
1037
|
+
function apiUsageGuide(purpose, teamId, instructions) {
|
|
1033
1038
|
const accountInstructions = instructions?.trim();
|
|
1034
1039
|
return `# Vercel usage
|
|
1035
1040
|
|
|
@@ -1079,12 +1084,104 @@ ${accountInstructions
|
|
|
1079
1084
|
? `\n## Account instructions\n\n${accountInstructions}\n`
|
|
1080
1085
|
: ""}`;
|
|
1081
1086
|
}
|
|
1082
|
-
/**
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1087
|
+
/** Reads reviewed against Vercel's official MCP tool reference. */
|
|
1088
|
+
const MCP_READ_ONLY_TOOLS = new Set([
|
|
1089
|
+
"search_vercel_documentation",
|
|
1090
|
+
"list_teams",
|
|
1091
|
+
"list_projects",
|
|
1092
|
+
"get_project",
|
|
1093
|
+
"list_deployments",
|
|
1094
|
+
"get_deployment",
|
|
1095
|
+
"get_deployment_build_logs",
|
|
1096
|
+
"get_runtime_logs",
|
|
1097
|
+
"get_runtime_errors",
|
|
1098
|
+
"get_web_analytics",
|
|
1099
|
+
"list_agent_run_projects",
|
|
1100
|
+
"list_agent_runs",
|
|
1101
|
+
"get_agent_run",
|
|
1102
|
+
"get_agent_run_trace",
|
|
1103
|
+
"check_domain_availability_and_price",
|
|
1104
|
+
"get_purchase_quote",
|
|
1105
|
+
"get_domain_order",
|
|
1106
|
+
"list_toolbar_threads",
|
|
1107
|
+
"get_toolbar_thread",
|
|
1108
|
+
// Returns CLI guidance. Any later CLI execution is outside this MCP call.
|
|
1109
|
+
"use_vercel_cli",
|
|
1110
|
+
]);
|
|
1111
|
+
/** Writes reviewed against Vercel's official MCP tool reference. */
|
|
1112
|
+
const MCP_WRITE_TOOLS = new Map([
|
|
1113
|
+
// These only append state.
|
|
1114
|
+
["reply_to_toolbar_thread", "additive"],
|
|
1115
|
+
["add_toolbar_reaction", "additive"],
|
|
1116
|
+
// Deploying to production, billing, access grants, imports, and edits can
|
|
1117
|
+
// all change existing state, even where the provider uses a create verb.
|
|
1118
|
+
["deploy_to_vercel", "destructive"],
|
|
1119
|
+
["buy_pro", "destructive"],
|
|
1120
|
+
["buy_credits", "destructive"],
|
|
1121
|
+
["buy_addon", "destructive"],
|
|
1122
|
+
["buy_domain", "destructive"],
|
|
1123
|
+
["get_access_to_vercel_url", "destructive"],
|
|
1124
|
+
// A GET against application code is not guaranteed to be observational.
|
|
1125
|
+
["web_fetch_vercel_url", "destructive"],
|
|
1126
|
+
["import-claude-design-from-url", "destructive"],
|
|
1127
|
+
["change_toolbar_thread_resolve_status", "destructive"],
|
|
1128
|
+
["edit_toolbar_message", "destructive"],
|
|
1129
|
+
]);
|
|
1130
|
+
/** Release-reviewed Vercel MCP inventory and safety verdicts. */
|
|
1131
|
+
export const VERCEL_MCP_VETTED_CATALOG = vettedCatalog({
|
|
1132
|
+
reads: MCP_READ_ONLY_TOOLS,
|
|
1133
|
+
writes: MCP_WRITE_TOOLS,
|
|
1134
|
+
});
|
|
1135
|
+
function mcpUsageGuide(purpose, instructions) {
|
|
1136
|
+
const accountInstructions = instructions?.trim();
|
|
1137
|
+
return `# Vercel MCP usage
|
|
1138
|
+
|
|
1139
|
+
Official MCP surface: tool names, descriptions, argument schemas, and result
|
|
1140
|
+
schemas come from Vercel's live server. Connecta preserves that catalog and
|
|
1141
|
+
only fills in release-reviewed safety annotations when Vercel leaves them out.
|
|
1142
|
+
|
|
1143
|
+
Account purpose: ${purpose}
|
|
1144
|
+
|
|
1145
|
+
- Discover the live catalog before assuming a tool exists. Vercel can change
|
|
1146
|
+
the surface independently of a Connecta release, and account features may
|
|
1147
|
+
affect what the authorization can reach.
|
|
1148
|
+
- Resolve team, project, deployment, run, thread, and order ids with the list
|
|
1149
|
+
and get tools. Do not guess opaque ids.
|
|
1150
|
+
- Diagnose deployments with \`get_deployment\`, then build logs, runtime error
|
|
1151
|
+
clusters, and runtime logs. Narrow time windows before raising result limits.
|
|
1152
|
+
- Purchase tools change billing. Read a quote first and carry its price,
|
|
1153
|
+
idempotency key, and requested term into the confirmed purchase unchanged.
|
|
1154
|
+
- \`get_access_to_vercel_url\` creates a temporary access grant. Treat the URL
|
|
1155
|
+
it returns as a credential and do not expose it outside the requested task.
|
|
1156
|
+
- \`deploy_to_vercel\` and \`import-claude-design-from-url\` can create or update
|
|
1157
|
+
live projects. Read the target and deployment mode before approving them.
|
|
1158
|
+
- An \`auth_required\` failure means this connector's OAuth grant is missing or
|
|
1159
|
+
expired. Run \`authorize_connector\` for this connector id, then retry.
|
|
1160
|
+
${accountInstructions
|
|
1161
|
+
? `\n## Account instructions\n\n${accountInstructions}\n`
|
|
1162
|
+
: ""}`;
|
|
1163
|
+
}
|
|
1164
|
+
function vercelMcp(id, purpose, options) {
|
|
1165
|
+
const connector = remoteMcp(id, {
|
|
1166
|
+
url: VERCEL_MCP_ENDPOINT,
|
|
1167
|
+
...defined({
|
|
1168
|
+
authScope: options.authScope,
|
|
1169
|
+
callAdmission: options.callAdmission,
|
|
1170
|
+
maxResultBytes: options.maxResultBytes,
|
|
1171
|
+
}),
|
|
1172
|
+
title: options.title ?? "Vercel (MCP)",
|
|
1173
|
+
description: `Vercel's official hosted MCP surface: ${purpose}`,
|
|
1174
|
+
auth: { type: "oauth" },
|
|
1175
|
+
requireHttps: true,
|
|
1176
|
+
usageGuide: {
|
|
1177
|
+
content: mcpUsageGuide(purpose, options.instructions),
|
|
1178
|
+
summary: "Official MCP. Live Vercel schemas, id resolution, deployment diagnosis, purchases, and access grants.",
|
|
1179
|
+
required: true,
|
|
1180
|
+
},
|
|
1181
|
+
});
|
|
1182
|
+
return withVettedCatalog(connector, VERCEL_MCP_VETTED_CATALOG);
|
|
1183
|
+
}
|
|
1184
|
+
function vercelApi(id, purpose, options) {
|
|
1088
1185
|
const defaultPageSize = options.defaultPageSize ?? DEFAULT_PAGE_SIZE;
|
|
1089
1186
|
if (!Number.isInteger(defaultPageSize) ||
|
|
1090
1187
|
defaultPageSize < 1 ||
|
|
@@ -1117,7 +1214,7 @@ export function vercel(id, options) {
|
|
|
1117
1214
|
}
|
|
1118
1215
|
},
|
|
1119
1216
|
usageGuide: {
|
|
1120
|
-
content:
|
|
1217
|
+
content: apiUsageGuide(purpose, teamId, options.instructions),
|
|
1121
1218
|
summary: "Team scoping, deployment diagnosis, value-safe environment variables, REST hatches, and cursor pagination.",
|
|
1122
1219
|
required: true,
|
|
1123
1220
|
},
|
|
@@ -1130,3 +1227,13 @@ export function vercel(id, options) {
|
|
|
1130
1227
|
: {}),
|
|
1131
1228
|
});
|
|
1132
1229
|
}
|
|
1230
|
+
/** A maintained Vercel connection using the selected provider surface. */
|
|
1231
|
+
export function vercel(id, options) {
|
|
1232
|
+
const purpose = options.purpose.trim();
|
|
1233
|
+
if (!purpose) {
|
|
1234
|
+
throw new Error("vercel() requires a non-empty account purpose.");
|
|
1235
|
+
}
|
|
1236
|
+
return options.surface === "mcp"
|
|
1237
|
+
? vercelMcp(id, purpose, options)
|
|
1238
|
+
: vercelApi(id, purpose, options);
|
|
1239
|
+
}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# Cloudflare prebuilt connection
|
|
2
2
|
|
|
3
3
|
Import `cloudflare()` independently from
|
|
4
|
-
`@zackbart/connecta/providers/cloudflare`.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
for
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
from Connecta's root entry.
|
|
4
|
+
`@zackbart/connecta/providers/cloudflare`. The deployment chooses one of two
|
|
5
|
+
interfaces. The default is a deliberate, hand-written surface over
|
|
6
|
+
Cloudflare's v4 REST API. Its fifty-one tools combine ergonomic, fully
|
|
7
|
+
described operations for common work with three guarded escape hatches for the
|
|
8
|
+
rest of Cloudflare's fast-moving control plane. The other choice is
|
|
9
|
+
Cloudflare's official whole-API hosted MCP, which exposes `search` and
|
|
10
|
+
`execute` with live provider-owned schemas. Both are ordinary connectors and
|
|
11
|
+
neither is reachable from Connecta's root entry.
|
|
13
12
|
|
|
14
13
|
```ts
|
|
15
14
|
import { cloudflare } from "@zackbart/connecta/providers/cloudflare";
|
|
16
15
|
|
|
17
16
|
const edge = cloudflare("cloudflare_prod", {
|
|
17
|
+
surface: "api", // optional; this is the backward-compatible default
|
|
18
18
|
title: "Production edge",
|
|
19
19
|
purpose: "DNS and cache administration for the production estate",
|
|
20
20
|
zoneId: "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
|
|
@@ -23,6 +23,15 @@ const edge = cloudflare("cloudflare_prod", {
|
|
|
23
23
|
});
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
Use Cloudflare's hosted code-mode interface instead:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
const wholeApi = cloudflare("cloudflare_mcp", {
|
|
30
|
+
surface: "mcp",
|
|
31
|
+
purpose: "Cloudflare administration outside the curated REST workflows",
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
26
35
|
Use the legacy user-scoped Global API Key when an existing deployment needs it:
|
|
27
36
|
|
|
28
37
|
```ts
|
|
@@ -41,6 +50,29 @@ between a production and a staging instance needs to know which one answers the
|
|
|
41
50
|
question. Account `instructions` are appended to the maintained guide and
|
|
42
51
|
cannot change the connector's safety classification.
|
|
43
52
|
|
|
53
|
+
## Choosing an interface
|
|
54
|
+
|
|
55
|
+
Use the API interface when its projected named tools cover the work. Connecta
|
|
56
|
+
owns those schemas, projections, typed errors, pagination, and the split
|
|
57
|
+
between read-only and mutating escape hatches.
|
|
58
|
+
|
|
59
|
+
Use `surface: "mcp"` when broad product coverage matters more than projected
|
|
60
|
+
results. Cloudflare's official server covers more than 2,500 API endpoints
|
|
61
|
+
through two code-mode tools. `search` reads the OpenAPI document and is
|
|
62
|
+
read-only. `execute` can run a program containing any authorized HTTP method,
|
|
63
|
+
so Connecta always routes it through approval. A program that happens to use
|
|
64
|
+
only GET cannot be proven observational from the tool schema.
|
|
65
|
+
|
|
66
|
+
The MCP catalog and schemas come from the live server. The release manifest
|
|
67
|
+
classifies the two known names but does not replace their schemas. OAuth is the
|
|
68
|
+
default. A headless deployment may instead pass `auth` with a scoped API token.
|
|
69
|
+
The credential remains the provider-side permission boundary either way. The
|
|
70
|
+
MCP interface accepts `callAdmission` when the deployment has a concurrency or
|
|
71
|
+
call-rate requirement; it does not reuse the API interface's REST-wide budget.
|
|
72
|
+
|
|
73
|
+
The remaining sections document the hand-written API interface. MCP tool
|
|
74
|
+
arguments and results are intentionally read from the live server instead.
|
|
75
|
+
|
|
44
76
|
## No SDK, on purpose
|
|
45
77
|
|
|
46
78
|
Cloudflare publishes an official `cloudflare` npm SDK, and this connection does
|
|
@@ -61,7 +93,7 @@ claim: the `cloudflare` package must not appear in `dependencies`,
|
|
|
61
93
|
`peerDependencies`, or `devDependencies`, and every import in the provider
|
|
62
94
|
must be relative.
|
|
63
95
|
|
|
64
|
-
##
|
|
96
|
+
## API credentials
|
|
65
97
|
|
|
66
98
|
The default credential is a scoped Cloudflare API token, sent as
|
|
67
99
|
`Authorization: Bearer <token>`. Create it under My Profile → API Tokens →
|
|
@@ -133,7 +165,7 @@ an empty `accountId` would fall back to the default again. A deployment that
|
|
|
133
165
|
wants zones from one account passes `accountId` explicitly, and the property
|
|
134
166
|
says so.
|
|
135
167
|
|
|
136
|
-
##
|
|
168
|
+
## API tools
|
|
137
169
|
|
|
138
170
|
The named surface covers workflows that benefit most from concise schemas and
|
|
139
171
|
projections:
|
|
@@ -423,6 +455,14 @@ not by Connecta. `maxConcurrency` is the bound that actually protects a shared
|
|
|
423
455
|
credential, because a single `execute_code` program can fan out far faster than
|
|
424
456
|
the window notices.
|
|
425
457
|
|
|
458
|
+
## Contract checks
|
|
459
|
+
|
|
460
|
+
`npm run providers:check` compares the 49 fixed REST endpoints with
|
|
461
|
+
Cloudflare's published OpenAPI document and the two MCP names, endpoint, and
|
|
462
|
+
OAuth support with Cloudflare's official MCP page. It needs no Cloudflare
|
|
463
|
+
credential. The MCP schemas are not vendored or reconstructed: the live
|
|
464
|
+
`tools/list` response remains the contract agents receive.
|
|
465
|
+
|
|
426
466
|
## Conventions
|
|
427
467
|
|
|
428
468
|
This connection is audited against
|
|
@@ -54,11 +54,29 @@ const analytics = mixpanel("product_analytics", {
|
|
|
54
54
|
});
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
The constructor may use `remoteMcp()` or `api()` internally.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
The constructor may use `remoteMcp()` or `api()` internally. When a provider's
|
|
58
|
+
official MCP and HTTP API expose materially different capabilities or schema
|
|
59
|
+
ownership, the constructor may offer an explicit deployment-time surface
|
|
60
|
+
choice. It must document the difference, keep a backward-compatible default,
|
|
61
|
+
and never let an agent switch surfaces during a run. The choice grants no
|
|
62
|
+
different runtime privileges. Two instances of the same provider are isolated
|
|
63
|
+
in exactly the same way as two hand-written connectors with different ids.
|
|
64
|
+
|
|
65
|
+
That choice exists only when the two interfaces are genuinely different:
|
|
66
|
+
|
|
67
|
+
| Provider | Maintained interfaces | Why |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| Cloudflare | API and MCP | The API interface has 48 projected named tools plus three safety-split hatches. The official MCP compresses more than 2,500 endpoints into `search` and approval-gated `execute`. |
|
|
70
|
+
| Notion | API and MCP | The API interface has stable lean projections. The official MCP adds Notion-owned live schemas, workspace search, files, views, agents, and sessions. |
|
|
71
|
+
| Vercel | API and MCP | The API interface has projected deployment operations. The official MCP owns a broader, independently changing catalog. |
|
|
72
|
+
| Linear | MCP | Vendoring its GraphQL API would create a second schema system rather than a distinct maintained interface. |
|
|
73
|
+
| Mixpanel | MCP | Its hosted service already joins several Mixpanel APIs; flattening those APIs would recreate the catalog problem. |
|
|
74
|
+
| RevenueCat | MCP | Its official server is generated from API v2, so a second wrapper would duplicate the same contract. |
|
|
75
|
+
| Stripe | MCP | Its official server already exposes both named workflows and supported API methods. A second raw API interface would duplicate it. |
|
|
76
|
+
|
|
77
|
+
This is not a requirement that every provider have two labels. A second choice
|
|
78
|
+
must change capability, result shape, or schema ownership enough to justify a
|
|
79
|
+
second contract. Otherwise it only gives agents two names for the same thing.
|
|
62
80
|
|
|
63
81
|
A prebuilt connection's vetted annotations fill in downstream silence and
|
|
64
82
|
otherwise preserve explicit annotations. This includes an explicit
|
package/documentation/linear.md
CHANGED
|
@@ -176,6 +176,15 @@ A budget-only rule needs no queue. If you add `maxConcurrency` you are asking
|
|
|
176
176
|
for a queue, and the admission controller then requires the rest of the queue
|
|
177
177
|
settings at construction.
|
|
178
178
|
|
|
179
|
+
## Public contract check
|
|
180
|
+
|
|
181
|
+
`npm run drift:check -- --docs --provider linear` checks Linear's official MCP
|
|
182
|
+
setup page for the read-write endpoint and OAuth support without using a
|
|
183
|
+
credential. Linear does not publish an exact tool inventory there, so the
|
|
184
|
+
command says `setup metadata only` and makes no claim about names or schemas.
|
|
185
|
+
At runtime the live `tools/list` response remains the schema authority and is
|
|
186
|
+
passed through without a vendored replacement.
|
|
187
|
+
|
|
179
188
|
## Conventions
|
|
180
189
|
|
|
181
190
|
This connection is audited against
|
|
@@ -85,9 +85,11 @@ A read-only live audit on 2026-08-13 confirmed all three refusals against the
|
|
|
85
85
|
US hosted endpoint. They are reported upstream as
|
|
86
86
|
[`mixpanel/mixpanel-headless#202`](https://github.com/mixpanel/mixpanel-headless/issues/202).
|
|
87
87
|
The vetted catalog records current schema digests for all 64 tools,
|
|
88
|
-
so a later schema correction or regression
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
so a later schema correction or regression increments runtime drift when an
|
|
89
|
+
ordinary catalog refresh observes it. The live definition is still served
|
|
90
|
+
unchanged. The credential-free provider check does not depend on those digests.
|
|
91
|
+
The guide can shrink when the downstream schema becomes complete; Connecta does
|
|
92
|
+
not absorb the defect permanently.
|
|
91
93
|
|
|
92
94
|
The wrapper classifies the documented observational tools as reads and the
|
|
93
95
|
documented create, update, edit, merge, dismiss, duplicate, and delete tools as
|
|
@@ -142,6 +144,14 @@ for a queue, and the admission controller then requires the rest of the queue
|
|
|
142
144
|
settings at construction. Discovery traffic is outside connector call admission
|
|
143
145
|
either way and still needs restrained use.
|
|
144
146
|
|
|
147
|
+
## Public contract check
|
|
148
|
+
|
|
149
|
+
`npm run drift:check -- --docs --provider mixpanel` compares Mixpanel's
|
|
150
|
+
official Available Tools table with the vetted manifest and checks all three
|
|
151
|
+
regional endpoints plus OAuth support. The current table lists 63 tools. It
|
|
152
|
+
omits `Fill-Event-Metadata`, which remains classified from the last
|
|
153
|
+
authenticated review and is reported as `not documented`, not silently removed.
|
|
154
|
+
|
|
145
155
|
## Conventions
|
|
146
156
|
|
|
147
157
|
This connection is audited against
|
package/documentation/notion.md
CHANGED
|
@@ -1,39 +1,60 @@
|
|
|
1
1
|
# Notion prebuilt connection
|
|
2
2
|
|
|
3
3
|
Import `notion()` independently from `@zackbart/connecta/providers/notion`. It
|
|
4
|
-
is a hand-written `api()`
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
offers two deployment-time interfaces. The default is a hand-written `api()`
|
|
5
|
+
surface over Notion's public REST API: fifteen deliberate tools, lean
|
|
6
|
+
projections of Notion's famously bloated payloads, typed failures, a rate
|
|
7
|
+
budget matched to the documented limit, and a required usage guide. The other
|
|
8
|
+
choice is Notion's official hosted MCP with live provider-owned schemas and a
|
|
9
|
+
broader workspace, files, views, agents, and sessions catalog. Neither is
|
|
8
10
|
reachable from Connecta's root entry.
|
|
9
11
|
|
|
10
12
|
```ts
|
|
11
13
|
import { notion } from "@zackbart/connecta/providers/notion";
|
|
12
14
|
|
|
13
15
|
const wiki = notion("engineering_wiki", {
|
|
16
|
+
surface: "api", // optional; this is the backward-compatible default
|
|
14
17
|
title: "Engineering wiki",
|
|
15
18
|
purpose: "Runbooks, specs, and on-call notes for the platform team",
|
|
16
19
|
instructions: "Prefer the Runbooks database; specs live under Projects.",
|
|
17
20
|
});
|
|
18
21
|
```
|
|
19
22
|
|
|
23
|
+
Use Notion's hosted MCP instead:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const workspace = notion("notion_mcp", {
|
|
27
|
+
surface: "mcp",
|
|
28
|
+
purpose: "Workspace search, files, views, and asynchronous agent sessions",
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
20
32
|
The `id` owns the ordinary connector namespaces; use a different id for every
|
|
21
33
|
Notion workspace. `purpose` is required because an agent choosing between two
|
|
22
34
|
instances needs to know which workspace answers the question. Workspace
|
|
23
35
|
`instructions` are appended to the maintained guide and cannot change the
|
|
24
36
|
connector's safety classification.
|
|
25
37
|
|
|
26
|
-
##
|
|
38
|
+
## Choosing an interface
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
Use the API interface for its compact, stable projections. A single Notion
|
|
41
|
+
page returns every property as a
|
|
30
42
|
discriminated wrapper object, every string as an array of rich-text runs each
|
|
31
43
|
carrying its own annotations block, and every user reference as a nested
|
|
32
44
|
object. A twenty-five row database query is tens of kilobytes of structure
|
|
33
45
|
around a few hundred bytes of meaning. Hand-writing the surface is what makes
|
|
34
46
|
the projections possible, and the projections are the point.
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
Use `surface: "mcp"` for Notion's wider official capabilities, including
|
|
49
|
+
connected-source search, attachments, saved views, Notion Skills, agents, and
|
|
50
|
+
asynchronous sessions. Tool names and schemas come from the live server.
|
|
51
|
+
Connecta preserves them and only fills in release-reviewed safety annotations
|
|
52
|
+
when Notion is silent. OAuth is the hosted server's authentication contract.
|
|
53
|
+
Do not apply the hand-written REST schemas to similarly named MCP tools. The
|
|
54
|
+
MCP interface accepts `callAdmission` for an operator-supplied runtime policy;
|
|
55
|
+
it does not assume the REST interface's endpoint budget describes MCP traffic.
|
|
56
|
+
|
|
57
|
+
## API authentication
|
|
37
58
|
|
|
38
59
|
One operator-managed credential: an internal integration token from
|
|
39
60
|
[notion.so/profile/integrations](https://www.notion.so/profile/integrations).
|
|
@@ -52,6 +73,10 @@ Two Notion-specific facts decide whether a working token is enough:
|
|
|
52
73
|
cheapest call that proves a token is live — and reports the workspace it
|
|
53
74
|
authenticated into.
|
|
54
75
|
|
|
76
|
+
The MCP interface uses Notion OAuth instead of the integration-token form. An
|
|
77
|
+
`auth_required` failure means the grant is absent or expired and must be
|
|
78
|
+
completed again through `authorize_connector`.
|
|
79
|
+
|
|
55
80
|
## The pinned API version
|
|
56
81
|
|
|
57
82
|
The connection pins `Notion-Version: 2026-03-11` and offers no override. That
|
|
@@ -69,7 +94,10 @@ type's payload rather than switching exhaustively. A property type that ships
|
|
|
69
94
|
after this release degrades to its raw value, and a block type that does keeps
|
|
70
95
|
its payload under `raw`; neither vanishes.
|
|
71
96
|
|
|
72
|
-
|
|
97
|
+
The remaining sections document the hand-written API interface. MCP tool
|
|
98
|
+
arguments and results are intentionally read from the live server instead.
|
|
99
|
+
|
|
100
|
+
## API tools
|
|
73
101
|
|
|
74
102
|
Ten reads, all annotated `readOnlyHint: true`:
|
|
75
103
|
|
|
@@ -229,13 +257,14 @@ Cursors are opaque. Notion's own versioning page is explicit that they may
|
|
|
229
257
|
change in length, format, and structure at any time and must be passed back
|
|
230
258
|
verbatim — never parsed, validated, or constructed.
|
|
231
259
|
|
|
232
|
-
## What
|
|
260
|
+
## What the API interface does not do
|
|
233
261
|
|
|
234
262
|
No file uploads, no database or data-source creation, no schema editing, no
|
|
235
263
|
block updates or deletes, no page moves. Those are all real Notion endpoints
|
|
236
264
|
and all deliberately absent: this is a deliberate tool surface, not a mirror of
|
|
237
|
-
the API.
|
|
238
|
-
beside this one, which
|
|
265
|
+
the API. Some are present on Notion's hosted MCP interface. Anything still
|
|
266
|
+
missing is reachable through a custom `api()` connector beside this one, which
|
|
267
|
+
remains a first-class path.
|
|
239
268
|
|
|
240
269
|
The 2026-03-11 contract also offers more fields on create and update. They were
|
|
241
270
|
reviewed after the 0.17.0 drift check and remain deliberately absent:
|
|
@@ -263,6 +292,14 @@ The usage guide says it too, because an agent that assumes a hatch exists
|
|
|
263
292
|
spends a search proving it does not: absent from the tool list means absent
|
|
264
293
|
from this connection, not hidden behind a generic call.
|
|
265
294
|
|
|
295
|
+
## Contract checks
|
|
296
|
+
|
|
297
|
+
`npm run providers:check` compares the 14 fixed REST endpoints with Notion's
|
|
298
|
+
published OpenAPI document and the 34 MCP names, endpoint, and OAuth support
|
|
299
|
+
with Notion's official pages. It needs no Notion credential. The MCP schemas
|
|
300
|
+
are not vendored or reconstructed: the live `tools/list` response remains the
|
|
301
|
+
contract agents receive.
|
|
302
|
+
|
|
266
303
|
## Conventions
|
|
267
304
|
|
|
268
305
|
This connection is audited against
|
|
@@ -194,10 +194,15 @@ Two more runners are deliberately outside `check`:
|
|
|
194
194
|
- `npm run test:browser` — Playwright against a real headless Chromium
|
|
195
195
|
(`npm run test:browser:install` once). It covers the embedded bundle without
|
|
196
196
|
adding a browser download to the CI release check.
|
|
197
|
-
- `npm run drift:check` — the
|
|
198
|
-
|
|
199
|
-
itself; findings are read by a human
|
|
197
|
+
- `npm run drift:check` — the lower-level maintainer provider contract check.
|
|
198
|
+
It reads public MCP references and OpenAPI documents only. No provider
|
|
199
|
+
credential is read and nothing files itself; findings are read by a human
|
|
200
|
+
and become issues
|
|
200
201
|
([provider conventions](./provider-conventions.md#the-maintainer-run-drift-check)).
|
|
202
|
+
- `npm run providers:check` — the normal provider check across every maintained
|
|
203
|
+
provider: official MCP documentation plus the OpenAPI contracts for
|
|
204
|
+
hand-written HTTP connections. It uses the network, so it stays outside the
|
|
205
|
+
deterministic `check` chain.
|
|
201
206
|
- `npm run load:admission` — the opt-in capacity matrix and soak
|
|
202
207
|
([request admission](./request-admission.md#measuring-capacity)).
|
|
203
208
|
|
|
@@ -234,7 +239,7 @@ in.
|
|
|
234
239
|
| `catalog.test.ts` | lexical ranking and the compact schema renderer — `const`, `allOf` beside siblings, `$ref`, the depth limit, per-schema caching, and 2020-12 keyword compatibility |
|
|
235
240
|
| `clerk.test.ts` | protected-resource metadata, the browser sign-in config, OAuth and session tokens, cached best-effort activity labels with their caps, the hand-applied `azp` rejection, and the `allowedDomains` allowlist including every lookalike that must not be repaired into a match |
|
|
236
241
|
| `cloudflare-access-auth.test.ts` | trusted `ctx.access` human and service identities, absent/error fail-closed behavior, service-token MCP admission without operator mutation, human same-origin mutation, and the Clerk-to-ambient shell switch |
|
|
237
|
-
| `cloudflare-provider.test.ts` | `cloudflare()` construction, tool surface, current R2 and KV jurisdictions, useful output declarations, request building, projections including additive provider fields, typed failures, and credential test |
|
|
242
|
+
| `cloudflare-provider.test.ts` | `cloudflare()` API and MCP construction, the code-mode safety manifest, API tool surface, current R2 and KV jurisdictions, useful output declarations, request building, projections including additive provider fields, typed failures, and credential test |
|
|
238
243
|
| `code-first-surface.test.ts` | the seven-tool surface itself — an executor required, every removed option and top-level tool refused, compact always-loaded routing pinned below 1,000 characters, complete on-demand usage served, and `connecta.ui` findable before connector search |
|
|
239
244
|
| `codemode-compat.test.ts` | the `Executor` seam staying structurally compatible with `@cloudflare/codemode`'s `DynamicWorkerExecutor`, enforced by `tsc` |
|
|
240
245
|
| `config.test.ts` | the grouped `ConnectaConfig` boundary — each group forwarding to its internals, malformed admission bounds failing construction, and unknown own-properties rejected by their complete path before construction does work |
|
|
@@ -254,7 +259,7 @@ in.
|
|
|
254
259
|
| `meta-tools-search.test.ts` | registry-backed discovery: bounded search with page and address maxima, compact and JSON schemas with constraints, typed describe recovery and suggestions, and structured-result compatibility |
|
|
255
260
|
| `meta-tools.test.ts` | the remaining registry-backed meta-tools: the complete on-demand usage skill, connector-guide selection and summary bounds, stored-credential drift, catalog health, authorization, probe timeouts, and unavailable or unknown browse recovery |
|
|
256
261
|
| `mixpanel-provider.test.ts` | the Mixpanel proxy, its conditional-input guide, destructive metadata fill, and complete 64-tool schema-digest manifest |
|
|
257
|
-
| `notion-provider.test.ts` | Notion's deliberate
|
|
262
|
+
| `notion-provider.test.ts` | Notion's API and MCP construction, the hosted safety manifest and drift behavior, the deliberate REST surface including declined expanded page inputs, request construction, lean projections, both pagination conventions, error mapping, and writes |
|
|
258
263
|
| `operator-boundary.test.ts` | the operator row of the decisions table, after every mutation route: authentication material managed without moving a declared structure, and the one honest exception — a credential write making a remote catalog appear, which is discovery arriving, not an operator editing the deployment |
|
|
259
264
|
| `operator-store.test.ts` | `src/operator-ui/app/store.ts` against a fake browser: the Clerk listener, ambient Access requests without a browser-readable token, `gate()`, the generation fence, and the request path |
|
|
260
265
|
| `provider-conventions.test.ts` | the conventions a test can hold: hand-written providers refusing schemas they cannot enforce (H5), their compact discovery schemas staying complete (H7), Cloudflare stating its second pagination convention in the schema (H10), and Notion saying it has no escape hatch (H14) |
|
|
@@ -274,7 +279,7 @@ in.
|
|
|
274
279
|
| `ui-credentials.test.ts` | credential-management routes: save, test, delete, validation, authentication, same-origin checks, and multi-field credential shapes |
|
|
275
280
|
| `ui.test.ts` | the server shell and remaining `/ui/*` routes: gated `/ui/data` with broken-connector isolation and registry-owned catalog-observation containment, plus the URL safety gates |
|
|
276
281
|
| `validate.test.ts` | `validateToolInput()` — a returned (not thrown) `invalid_args` naming the path, `additionalProperties: false` enforcement, per-schema validator caching, and an unusable schema passed through with one warning |
|
|
277
|
-
| `vercel-provider.test.ts` | `vercel()` construction, team scoping, project and deployment projections, finite build and runtime logs, value-safe environment variables, domains, lifecycle writes, REST hatches, typed failures, and credential test |
|
|
282
|
+
| `vercel-provider.test.ts` | `vercel()` API and MCP construction, MCP inventory classification, team scoping, project and deployment projections, finite build and runtime logs, value-safe environment variables, domains, lifecycle writes, REST hatches, typed failures, and credential test |
|
|
278
283
|
|
|
279
284
|
### Node-bound (`NODE_ONLY_SUITES`)
|
|
280
285
|
|
|
@@ -286,7 +291,7 @@ justification for *not* re-running it in workerd, so "it was easier" is not one.
|
|
|
286
291
|
| `deployment-shapes.test.ts` | the Worker as the only example with a loader-only sandbox, its agent instructions and setup guide pinning Claude and both ChatGPT Managed OAuth callback forms, one Node template that is also its own container, the same source running locally and in the container, the Node template's pinned esbuild install-script approval, the full operator surface in both, a template that cannot start on its own `.env.example`, a Worker README naming every optional peer its entrypoint imports, and the initializer's `.gitignore` staying in step | walks the template and example trees with Node filesystem APIs |
|
|
287
292
|
| `doc-links.test.ts` | the documentation checker itself — local file and fragment resolution, repository URLs resolved back to the checkout, duplicate heading slugs, fenced-code exclusion, and useful failures | spawns the Node checker against filesystem fixtures |
|
|
288
293
|
| `doctor-cli.test.ts` | `connecta doctor`'s executor line and credentials end to end — the sandbox the deployment reports is the one named, an unidentifiable executor gets an executor-neutral line, a hostile name is bounded, and a complete Cloudflare Access service-token pair is accepted while a partial pair is refused | spawns the CLI against a Node HTTP deployment over real sockets |
|
|
289
|
-
| `drift-check.test.ts` | the maintainer drift checker
|
|
294
|
+
| `drift-check.test.ts` | the credential-free maintainer drift checker: recorded touched endpoints, heading, table, and inline MCP inventories, setup-only providers, live-schema ownership, a quiet revision bump, clear failures for unavailable inputs, `$ref` traversal, and one well-formed row per endpoint | spawns the checker against filesystem fixtures |
|
|
290
295
|
| `file-storage.test.ts` | `fileStorage()` across instances, logical TTL plus physical pruning without clobbering a newer value, and corrupt-file quarantine | exercises the Node filesystem storage adapter |
|
|
291
296
|
| `guest-api-contract-quickjs.test.ts` | the shared guest-contract cases on the real QuickJS executor, including identical caught failure codes and inline describe recovery, its exact absent globals, and blocked runtime imports | runs the contract cases on the Node QuickJS executor |
|
|
292
297
|
| `node.test.ts` | the `listen()` adapter propagating an HTTP client disconnect through the Web `Request` and the MCP handler into a program's connector call, releasing both admission permits | exercises the Node HTTP adapter over real TCP sockets |
|