@zackbart/connecta 0.37.1 → 0.39.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.
Files changed (265) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +5 -4
  3. package/bin/connecta.mjs +1 -39
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.js +2 -4
  6. package/dist/agent-identity.js +1 -1
  7. package/dist/agent-routes.js +231 -0
  8. package/dist/auth/clerk-sdk/generated.js +2 -1
  9. package/dist/auth/downstream-oauth/consent.js +519 -0
  10. package/dist/auth/downstream-oauth/grant-store.js +439 -0
  11. package/dist/auth/downstream-oauth/refresh-coordinator.js +562 -0
  12. package/dist/auth/downstream-oauth/refresh-records.js +447 -0
  13. package/dist/auth/downstream-oauth/sdk-hooks.js +242 -0
  14. package/dist/auth/downstream-oauth.js +66 -2516
  15. package/dist/auth/downstream-revocation.js +1 -1
  16. package/dist/auth/native.js +27 -0
  17. package/dist/auth/oauth-grant.js +24 -0
  18. package/dist/auth/oauth-token-response.js +1 -1
  19. package/dist/auth/static-oauth.js +153 -131
  20. package/dist/auth/vercel-agent.js +2 -1
  21. package/dist/auth-elicitation.js +9 -3
  22. package/dist/authorization-handoff.js +11 -5
  23. package/dist/branding.d.ts +1 -15
  24. package/dist/branding.js +2 -2
  25. package/dist/bytes.js +42 -0
  26. package/dist/call-admission-context.js +2 -5
  27. package/dist/call-recovery.js +0 -3
  28. package/dist/catalog-cache.js +130 -43
  29. package/dist/catalog-fingerprint.js +2 -18
  30. package/dist/catalog-policy.js +18 -0
  31. package/dist/catalog-service.js +285 -48
  32. package/dist/catalog.js +18 -2
  33. package/dist/config-defaults.d.ts +1 -0
  34. package/dist/config-defaults.js +1 -0
  35. package/dist/config-schema.d.ts +1 -1
  36. package/dist/config-schema.js +6 -2
  37. package/dist/config.d.ts +57 -25
  38. package/dist/config.js +30 -77
  39. package/dist/connector-caller.d.ts +1 -1
  40. package/dist/connector-health.js +59 -0
  41. package/dist/connector-status.js +117 -0
  42. package/dist/connectors/api-connector.d.ts +7 -2
  43. package/dist/connectors/api-connector.js +11 -7
  44. package/dist/connectors/guarded-fetch.d.ts +0 -6
  45. package/dist/connectors/guarded-fetch.js +1 -11
  46. package/dist/connectors/negotiation-cache.js +22 -4
  47. package/dist/connectors/remote-mcp/call.js +164 -0
  48. package/dist/connectors/remote-mcp/catalog.js +278 -0
  49. package/dist/connectors/remote-mcp/config.js +263 -0
  50. package/dist/connectors/remote-mcp/connection.js +671 -0
  51. package/dist/connectors/remote-mcp/errors.js +488 -0
  52. package/dist/connectors/remote-mcp/oauth.js +469 -0
  53. package/dist/connectors/remote-mcp/resources.js +473 -0
  54. package/dist/connectors/remote-mcp/runtime.js +1 -0
  55. package/dist/connectors/remote-mcp/transport.js +544 -0
  56. package/dist/connectors/remote-mcp.js +61 -2898
  57. package/dist/connectors/resource-uri.js +0 -4
  58. package/dist/credential-contract.d.ts +23 -0
  59. package/dist/credential-reads.js +17 -0
  60. package/dist/credential-verification.js +53 -0
  61. package/dist/credentials.d.ts +7 -0
  62. package/dist/credentials.js +60 -25
  63. package/dist/d1.d.ts +0 -89
  64. package/dist/d1.js +0 -34
  65. package/dist/describe-config.d.ts +12 -2
  66. package/dist/describe-config.js +2 -0
  67. package/dist/discovery-health.js +199 -0
  68. package/dist/downstream-input.js +4 -2
  69. package/dist/errors.d.ts +55 -17
  70. package/dist/errors.js +95 -41
  71. package/dist/execute.js +172 -125
  72. package/dist/executor-result.js +5 -2
  73. package/dist/executors/quickjs-runtime.js +2 -25
  74. package/dist/executors/quickjs.js +1 -1
  75. package/dist/guest-functions.js +83 -0
  76. package/dist/guest-runtime.js +63 -20
  77. package/dist/guest-types.d.ts +24 -10
  78. package/dist/http/argument-problems.js +35 -0
  79. package/dist/http/client.js +1 -1
  80. package/dist/http/errors.js +7 -6
  81. package/dist/http/fields.js +192 -13
  82. package/dist/http/hatch-response.js +73 -0
  83. package/dist/http/hatch.js +88 -23
  84. package/dist/http/json-schema.js +1 -1
  85. package/dist/http/node-transport.js +0 -1
  86. package/dist/http/oauth.js +4 -3
  87. package/dist/http/paging.js +6 -9
  88. package/dist/http/provider.js +33 -5
  89. package/dist/http/safety.js +9 -1
  90. package/dist/http/services.js +1 -8
  91. package/dist/http/tools.js +45 -30
  92. package/dist/http/vendor.js +7 -7
  93. package/dist/identity.js +2 -6
  94. package/dist/index.d.ts +1 -1
  95. package/dist/index.js +9 -2
  96. package/dist/invocation-auth.js +2 -1
  97. package/dist/invocation.js +21 -6
  98. package/dist/membership.d.ts +13 -5
  99. package/dist/membership.js +34 -2
  100. package/dist/meta-tools.js +50 -35
  101. package/dist/native-recovery.js +4 -16
  102. package/dist/native-tools.js +6 -2
  103. package/dist/oauth-handoff.js +2 -2
  104. package/dist/oauth-sealing.js +10 -0
  105. package/dist/operation-vocabulary.js +136 -14
  106. package/dist/operator-record.d.ts +2 -2
  107. package/dist/operator-record.js +2 -49
  108. package/dist/operator-ui/contract.d.ts +12 -2
  109. package/dist/operator-ui/generated.js +3 -3
  110. package/dist/operator-ui/model.d.ts +22 -1
  111. package/dist/program-writes.js +3 -8
  112. package/dist/provider-call.js +0 -7
  113. package/dist/provider.d.ts +1 -1
  114. package/dist/providers/_shared/google/kit.js +1 -6
  115. package/dist/providers/_shared/google/service-account.js +1 -3
  116. package/dist/providers/_shared/google/workspace.d.ts +1 -1
  117. package/dist/providers/_shared/guide.js +3 -0
  118. package/dist/providers/_shared/json.js +12 -0
  119. package/dist/providers/_shared/rest/compact-index.js +1 -1
  120. package/dist/providers/_shared/rest/kit.js +1 -3
  121. package/dist/providers/_shared/rest/operation-index.js +49 -53
  122. package/dist/providers/_shared/rest/value-safety.js +106 -28
  123. package/dist/providers/breeze/api.js +9 -8
  124. package/dist/providers/breeze/index.js +4 -2
  125. package/dist/providers/breeze/skill.generated.js +1 -1
  126. package/dist/providers/breeze/tools.js +7 -7
  127. package/dist/providers/ccb/api.js +3 -7
  128. package/dist/providers/ccb/index.js +7 -1
  129. package/dist/providers/ccb/skill.generated.js +1 -1
  130. package/dist/providers/ccb/tools.js +1 -6
  131. package/dist/providers/cloudflare/api.js +10 -3
  132. package/dist/providers/cloudflare/index.js +2 -1
  133. package/dist/providers/cloudflare/safety.js +3 -3
  134. package/dist/providers/cloudflare/skill.generated.js +1 -1
  135. package/dist/providers/docs/lib/schemas.js +24 -22
  136. package/dist/providers/docs/safety.js +2 -0
  137. package/dist/providers/docs/skill.generated.js +1 -1
  138. package/dist/providers/docs/tools.js +4 -14
  139. package/dist/providers/drive/api.js +1 -0
  140. package/dist/providers/drive/lib/contracts.js +8 -5
  141. package/dist/providers/drive/skill.generated.js +1 -1
  142. package/dist/providers/drive/tools.js +23 -41
  143. package/dist/providers/forms/api.js +1 -0
  144. package/dist/providers/forms/skill.generated.js +1 -1
  145. package/dist/providers/forms/tools.js +11 -16
  146. package/dist/providers/github/api.js +1 -0
  147. package/dist/providers/github/lib/discovery.js +1 -1
  148. package/dist/providers/github/skill.generated.js +1 -1
  149. package/dist/providers/github/tools.js +61 -17
  150. package/dist/providers/gmail/lib/schemas.js +60 -50
  151. package/dist/providers/gmail/skill.generated.js +1 -1
  152. package/dist/providers/gmail/tools.js +12 -26
  153. package/dist/providers/granola/index.js +2 -0
  154. package/dist/providers/granola/skill.generated.js +1 -1
  155. package/dist/providers/overflow/api.js +8 -4
  156. package/dist/providers/overflow/index.js +2 -1
  157. package/dist/providers/overflow/skill.generated.js +1 -1
  158. package/dist/providers/overflow/tools.js +3 -7
  159. package/dist/providers/planning-center/api.js +5 -2
  160. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  161. package/dist/providers/planning-center/lib/json-api.js +1 -9
  162. package/dist/providers/planning-center/skill.generated.js +1 -1
  163. package/dist/providers/planning-center/tools.js +2 -1
  164. package/dist/providers/quickbooks/safety.js +80 -39
  165. package/dist/providers/quickbooks/skill.generated.js +1 -1
  166. package/dist/providers/sheets/api.js +1 -0
  167. package/dist/providers/sheets/skill.generated.js +1 -1
  168. package/dist/providers/sheets/tools.js +14 -23
  169. package/dist/providers/slides/skill.generated.js +1 -1
  170. package/dist/providers/slides/tools.js +19 -26
  171. package/dist/providers/stripe/index.js +2 -1
  172. package/dist/providers/stripe/safety.js +2 -2
  173. package/dist/providers/stripe/skill.generated.js +1 -1
  174. package/dist/providers/tithely/api.js +5 -8
  175. package/dist/providers/tithely/index.js +3 -2
  176. package/dist/providers/tithely/skill.generated.js +1 -1
  177. package/dist/providers/tithely/tools.js +7 -5
  178. package/dist/providers/vercel/api.js +2 -7
  179. package/dist/providers/vercel/lib/pin.js +1 -1
  180. package/dist/providers/vercel/safety.js +1 -1
  181. package/dist/providers/vercel/skill.generated.js +1 -1
  182. package/dist/providers/vercel/tools.js +6 -4
  183. package/dist/redaction-marker.js +2 -2
  184. package/dist/registry.d.ts +11 -5
  185. package/dist/registry.js +46 -20
  186. package/dist/request-principal.js +1 -1
  187. package/dist/request-state.js +2 -2
  188. package/dist/routes/access-tokens.js +1 -1
  189. package/dist/routes/activity-disclosure.js +1 -1
  190. package/dist/routes/connect.js +2 -2
  191. package/dist/routes/credentials.js +130 -61
  192. package/dist/routes/dispatch.js +5 -1
  193. package/dist/routes/mcp.js +34 -17
  194. package/dist/routes/oauth-management.js +1 -1
  195. package/dist/routes/operator.js +6 -5
  196. package/dist/routes/shared.d.ts +13 -8
  197. package/dist/routes/shared.js +15 -9
  198. package/dist/routes/ui-config.js +3 -3
  199. package/dist/routes/ui-data.js +126 -20
  200. package/dist/routes/ui.js +17 -1
  201. package/dist/runtime/call-admission.js +1 -1
  202. package/dist/runtime/call.js +8 -3
  203. package/dist/runtime/native-storage.js +9 -0
  204. package/dist/runtime/request-reads.js +182 -0
  205. package/dist/runtime/request-scope.js +16 -0
  206. package/dist/runtime/settlement.js +1 -1
  207. package/dist/runtime/shared-read.js +2 -15
  208. package/dist/runtime/storage.js +12 -0
  209. package/dist/sent-secrets.d.ts +1 -2
  210. package/dist/sent-secrets.js +3 -8
  211. package/dist/server.js +4 -4
  212. package/dist/skills.js +44 -54
  213. package/dist/sqlite.d.ts +0 -19
  214. package/dist/sqlite.js +2 -83
  215. package/dist/storage/keys.js +69 -128
  216. package/dist/storage/memory.js +8 -2
  217. package/dist/storage/sql-native.js +27 -101
  218. package/dist/storage/sql.d.ts +1 -24
  219. package/dist/storage/sql.js +13 -14
  220. package/dist/storage-digest.js +32 -0
  221. package/dist/timeout.js +8 -0
  222. package/dist/tool-error.js +4 -0
  223. package/dist/types.d.ts +33 -0
  224. package/dist/url-safety.js +0 -4
  225. package/dist/usage-guide.js +18 -37
  226. package/dist/validate.d.ts +3 -0
  227. package/dist/validate.js +220 -158
  228. package/dist/version.d.ts +1 -1
  229. package/dist/version.js +1 -1
  230. package/dist/worker.js +1 -1
  231. package/dist/write-outcome.js +0 -3
  232. package/documentation/README.md +19 -55
  233. package/documentation/architecture.md +436 -515
  234. package/documentation/auth.md +229 -88
  235. package/documentation/code-mode.md +201 -94
  236. package/documentation/deploying.md +25 -43
  237. package/documentation/integrating.md +18 -19
  238. package/documentation/meta-tools.md +225 -51
  239. package/documentation/migration-0.33.md +10 -6
  240. package/documentation/migration-0.34.md +5 -5
  241. package/documentation/migration-retired-layouts.md +74 -0
  242. package/documentation/operator-tests.md +20 -120
  243. package/documentation/operator-ui.md +108 -16
  244. package/documentation/pool-membership-upgrade.md +44 -0
  245. package/documentation/provider-auth-migration.md +4 -10
  246. package/documentation/provider-migration-0.29.md +4 -15
  247. package/documentation/providers.md +105 -0
  248. package/examples/worker/AGENTS.md +14 -16
  249. package/examples/worker/README.md +24 -118
  250. package/examples/worker/src/connecta.config.ts +1 -0
  251. package/guides/connecting-agents.md +9 -31
  252. package/guides/how-it-works.md +6 -4
  253. package/guides/safety-and-trust.md +4 -1
  254. package/package.json +19 -19
  255. package/templates/node/AGENTS.md +13 -12
  256. package/templates/node/README.md +9 -26
  257. package/templates/node/package.json +1 -1
  258. package/templates/node/src/connecta.config.ts +1 -0
  259. package/dist/auth/oauth-v2-migration.js +0 -135
  260. package/dist/provider-call-facts.d.ts +0 -115
  261. package/dist/provider-call.d.ts +0 -112
  262. package/dist/storage/d1-copy.js +0 -255
  263. package/dist/storage/sql-schema.d.ts +0 -6
  264. package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
  265. package/examples/worker/scripts/copy-kv-to-d1.mjs +0 -162
@@ -0,0 +1,263 @@
1
+ // remoteMcp() options and validation: the closed option set, resource and
2
+ // credential header rules, and every construction-time refusal, in order.
3
+ import { authorizationParamsFor } from "../../auth/oauth-authorization-params.js";
4
+ import { assertRemoteOAuthClient } from "../../auth/downstream-client-metadata.js";
5
+ import { assertOAuthScope } from "../../auth/downstream-oauth.js";
6
+ import { shortSecretWarning } from "../../sent-secrets.js";
7
+ import { assertKnownOptions, keys, optionsOf, strings } from "../../config-schema.js";
8
+ import { READ_ONLY, CALL_ADMISSION, REMOTE_MCP_AUTH, USAGE_GUIDE } from "../option-shapes.js";
9
+ /** The closed options remoteMcp() accepts; see `assertKnownOptions`. */
10
+ const REMOTE_MCP_OPTIONS = optionsOf()({
11
+ ...keys("url", "title", "description", "authScope", "maxResultBytes", "versionNegotiation", "redirects", "requireHttps", "logger", "fetch", "_transportFactory", "skills"),
12
+ readOnly: READ_ONLY,
13
+ callAdmission: CALL_ADMISSION,
14
+ usageGuide: USAGE_GUIDE,
15
+ auth: REMOTE_MCP_AUTH,
16
+ headers: strings(),
17
+ });
18
+ /** Extra headers are immutable code configuration, not another auth channel. */
19
+ function extraResourceHeaders(id, opts) {
20
+ if (opts.headers === undefined)
21
+ return undefined;
22
+ const refuse = () => new Error(`remoteMcp(${JSON.stringify(id)}).headers requires non-secret resource headers without reserved or auth-owned names; use auth for credentials.`);
23
+ if (opts.auth?.type === "headers")
24
+ throw refuse();
25
+ const authNames = new Set([
26
+ "authorization",
27
+ ...(opts.auth?.type === "credential" ? [opts.auth.header?.trim().toLowerCase() || "authorization"] : []),
28
+ ...(opts.auth?.type === "request" ? Object.keys(opts.auth.headers ?? {}).map((name) => name.toLowerCase()) : []),
29
+ ]);
30
+ const normalized = Object.create(null);
31
+ for (const [name, value] of Object.entries(opts.headers)) {
32
+ const lower = name.toLowerCase();
33
+ if (!HEADER_NAME_TOKEN.test(name) ||
34
+ typeof value !== "string" ||
35
+ /[\r\n]/.test(value) ||
36
+ Object.hasOwn(normalized, lower) ||
37
+ authNames.has(lower) ||
38
+ /^(?:accept|cookie|host|origin|connection|keep-alive|proxy-authenticate|proxy-authorization|te|trailer|transfer-encoding|upgrade|last-event-id|dpop|www-authenticate)$/.test(lower) ||
39
+ /^(?:content-|mcp-)/.test(lower) ||
40
+ /(?:api[-_]?key|token|secret|password|credential|authorization|signature|session)/.test(lower))
41
+ throw refuse();
42
+ try {
43
+ normalized[lower] = new Headers({ [lower]: value }).get(lower);
44
+ }
45
+ catch {
46
+ throw refuse();
47
+ }
48
+ }
49
+ return Object.freeze(Object.fromEntries(Object.entries(normalized).sort(([a], [b]) => a.localeCompare(b))));
50
+ }
51
+ const encoder = new TextEncoder();
52
+ /** Base64 of a UTF-8 string, Web-API only so the core still runs on workerd. */
53
+ function base64Utf8(value) {
54
+ let binary = "";
55
+ for (const byte of encoder.encode(value)) {
56
+ binary += String.fromCharCode(byte);
57
+ }
58
+ return btoa(binary);
59
+ }
60
+ /**
61
+ * Assemble the one header a credential-auth connector sends.
62
+ *
63
+ * A scheme ending in `Basic` names HTTP Basic credentials wherever a provider
64
+ * nests it, so the `user:secret` it frames is base64-encoded — that is what
65
+ * makes plain `Basic` and nested `Bearer Basic` one rule instead of two.
66
+ */
67
+ export function credentialHeaderValue(scheme, value) {
68
+ if (scheme === null)
69
+ return value;
70
+ return /(?:^|\s)basic$/i.test(scheme) ? `${scheme} ${base64Utf8(value)}` : `${scheme} ${value}`;
71
+ }
72
+ /**
73
+ * True when a stored credential carries a character a header cannot.
74
+ *
75
+ * The fetch specification refuses NUL, CR, and LF outright, and the runtime
76
+ * that refuses them says so in a `TypeError` that quotes the whole offending
77
+ * value — a message that would otherwise travel to the agent. The remaining C0
78
+ * controls and DEL are refused here too: no real API key contains one, and a
79
+ * paste that picked one up is a paste to redo rather than a request to send.
80
+ * Leading and trailing whitespace is already gone by the time this runs.
81
+ *
82
+ * A scan rather than a regular expression, because a character class over the
83
+ * control range is exactly what `no-control-regex` exists to flag, and the
84
+ * suppression would be less readable than the loop it suppressed.
85
+ */
86
+ export function carriesIllegalHeaderChar(value) {
87
+ for (let index = 0; index < value.length; index++) {
88
+ const code = value.charCodeAt(index);
89
+ if (code <= 0x1f || code === 0x7f)
90
+ return true;
91
+ }
92
+ return false;
93
+ }
94
+ /** RFC 9110 field-name token, checked once at construction. */
95
+ const HEADER_NAME_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
96
+ function isLoopbackHost(hostname) {
97
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname === "::1";
98
+ }
99
+ /** Validate `opts` once, at construction, and derive what every seam reads. */
100
+ export function remoteMcpConfig(id, opts) {
101
+ opts = assertKnownOptions(opts, `remoteMcp(${JSON.stringify(id)})`, REMOTE_MCP_OPTIONS);
102
+ if (opts.readOnly !== undefined)
103
+ READ_ONLY.parse(opts.readOnly, `Connector(${JSON.stringify(id)}).readOnly`);
104
+ if (opts.callAdmission !== undefined)
105
+ CALL_ADMISSION.parse(opts.callAdmission, `remoteMcp(${JSON.stringify(id)}).callAdmission`);
106
+ const extraHeaders = extraResourceHeaders(id, opts);
107
+ const skillsEnabled = opts.skills;
108
+ if (skillsEnabled !== undefined && typeof skillsEnabled !== "boolean") {
109
+ throw new Error(`[connecta] connector "${id}" skills must be a boolean.`);
110
+ }
111
+ const clientMetadataUrl = opts.auth?.type === "oauth" ? opts.auth.clientMetadataUrl : undefined;
112
+ const oauthScope = opts.auth?.type === "oauth" ? opts.auth.scope : undefined;
113
+ assertOAuthScope(id, oauthScope);
114
+ const authorizationParams = authorizationParamsFor(opts.auth?.type === "oauth" ? opts.auth.authorizationParams : undefined, `remoteMcp(${JSON.stringify(id)}).auth.authorizationParams`);
115
+ const staticClient = opts.auth?.type === "oauth" && opts.auth.client ? { ...opts.auth.client } : undefined;
116
+ assertRemoteOAuthClient(id, staticClient);
117
+ if (staticClient && clientMetadataUrl !== undefined)
118
+ throw new Error(`[connecta] connector "${id}" OAuth client and clientMetadataUrl are mutually exclusive.`);
119
+ if (clientMetadataUrl !== undefined) {
120
+ let valid = false;
121
+ try {
122
+ const url = new URL(clientMetadataUrl);
123
+ valid = url.protocol === "https:" && url.pathname !== "/" && !url.username && !url.password && !url.hash;
124
+ }
125
+ catch {
126
+ /* Invalid configuration is rejected below without echoing its value. */
127
+ }
128
+ if (!valid)
129
+ throw new Error(`[connecta] connector "${id}" clientMetadataUrl must be an HTTPS URL with a non-root path, without credentials or a fragment.`);
130
+ }
131
+ if (opts.authScope === "personal" && opts.auth?.type === "headers") {
132
+ throw new Error(`[connecta] connector "${id}" cannot combine authScope "personal" ` +
133
+ "with static headers. Use credential or OAuth auth so each principal " +
134
+ "can own a different grant.");
135
+ }
136
+ const isOauth = opts.auth?.type === "oauth";
137
+ const logger = opts.logger ?? console;
138
+ const warnShortSecret = shortSecretWarning();
139
+ warnShortSecret(staticClient?.clientSecret, logger);
140
+ if (opts.auth?.type === "headers") {
141
+ for (const [name, value] of Object.entries(opts.auth.headers)) {
142
+ if (!/key|token|secret|password|auth|signature|session/i.test(name))
143
+ continue;
144
+ const basic = /^Basic\s+(.+)$/i.exec(value);
145
+ if (basic) {
146
+ try {
147
+ const decoded = atob(basic[1]);
148
+ const colon = decoded.indexOf(":");
149
+ if (colon !== -1)
150
+ warnShortSecret(decoded.slice(colon + 1), logger);
151
+ }
152
+ catch {
153
+ /* Invalid Basic configuration is not a short password. */
154
+ }
155
+ }
156
+ else
157
+ warnShortSecret(value.replace(/^(?:Bearer|token)\s+/i, ""), logger);
158
+ }
159
+ }
160
+ const credentialAuth = opts.auth?.type === "credential" ? opts.auth : undefined;
161
+ if (credentialAuth?.credential?.fields?.length) {
162
+ throw new Error(`[connecta] connector "${id}" credential auth declares named fields; ` +
163
+ "this shape sends one secret in one header and reads the reserved " +
164
+ "`value` field only.");
165
+ }
166
+ // Both request shapes, read field by field; a mixture is refused below.
167
+ const requestAuth = opts.auth?.type === "request" ? opts.auth : undefined;
168
+ // Opt-in only: without it the resolver keeps receiving the context alone.
169
+ // `principal` replaces `token`; a malformed or mixed shape has no resolver.
170
+ const forwarding = requestAuth?.principal;
171
+ const requestToken = forwarding
172
+ ? requestAuth?.token === undefined && (forwarding.accept === "human" || forwarding.accept === "human-or-agent")
173
+ ? forwarding.token
174
+ : undefined
175
+ : requestAuth?.token;
176
+ if (requestAuth && (typeof requestToken !== "function" || opts.authScope === "personal")) {
177
+ throw new Error(`remoteMcp(${JSON.stringify(id)}) requires a request token callback and shared authScope.`);
178
+ }
179
+ const principalPolicy = forwarding?.accept;
180
+ const customFetch = opts.fetch;
181
+ // OAuth discovery, registration and token requests reach authorization
182
+ // servers the downstream names, through the owned credential sender. A
183
+ // custom fetch would split that traffic between two transports.
184
+ if (customFetch !== undefined && (typeof customFetch !== "function" || opts.auth?.type === "oauth")) {
185
+ throw new Error(`remoteMcp(${JSON.stringify(id)}).fetch must be a function, without OAuth auth.`);
186
+ }
187
+ // No delayed dispatch: a write counts as sent once it is handed to the function.
188
+ const customSender = customFetch && { send: customFetch };
189
+ if (requestAuth?.headers) {
190
+ try {
191
+ if (Object.keys(requestAuth.headers).some((name) => name.toLowerCase() === "authorization"))
192
+ throw new Error();
193
+ new Headers(requestAuth.headers);
194
+ }
195
+ catch {
196
+ throw new Error(`remoteMcp(${JSON.stringify(id)}) requires valid request auth headers without Authorization.`);
197
+ }
198
+ }
199
+ const credentialConfig = credentialAuth?.credential ?? {
200
+ label: "API key",
201
+ };
202
+ const credentialHeader = credentialAuth?.header?.trim() || "Authorization";
203
+ // A structural mistake in the deployment file, beside the `fields` refusal
204
+ // above: an unsendable header name is not worth discovering on the first
205
+ // request, where only a runtime error can report it.
206
+ if (credentialAuth && !HEADER_NAME_TOKEN.test(credentialHeader)) {
207
+ throw new Error(`[connecta] connector "${id}" credential auth declares header ` +
208
+ `"${credentialHeader}", which is not a valid HTTP field name.`);
209
+ }
210
+ // `undefined` means "not stated" and takes the bearer default; `null` and
211
+ // `""` both mean "send the stored value verbatim", which some providers
212
+ // require for a bare API key.
213
+ const credentialScheme = credentialAuth === undefined || credentialAuth.scheme === undefined
214
+ ? "Bearer"
215
+ : (credentialAuth.scheme?.trim() ?? "") || null;
216
+ // Check the destination scheme once at construction: buildTransport (and the
217
+ // SDK's fetch) attach any static credentials to every request, so an http://
218
+ // endpoint sends bearer tokens / API keys in cleartext. Loopback is exempt
219
+ // for local development.
220
+ const destination = new URL(opts.url);
221
+ // Only a web URL reaches a remote MCP server. Another scheme cannot, and a
222
+ // wrapping one (`blob:https://…`) would also carry its inner URL, userinfo
223
+ // and all, into every place the endpoint is reported; the value is not echoed.
224
+ if (destination.protocol !== "https:" && destination.protocol !== "http:") {
225
+ throw new Error(`[connecta] connector "${id}" url must be an http(s) URL.`);
226
+ }
227
+ const insecureDestination = destination.protocol !== "https:" && !isLoopbackHost(destination.hostname);
228
+ if (insecureDestination) {
229
+ if (opts.requireHttps || requestAuth) {
230
+ throw new Error(`[connecta] connector "${id}" url ${opts.url} is not https:// (and not loopback) — refusing to connect (requireHttps).`);
231
+ }
232
+ // Both static shapes send a secret on every request; that an operator
233
+ // typed one into the operator UI rather than a deployment file changes
234
+ // who owns it, not whether the wire carries it in the clear.
235
+ if (opts.auth?.type === "headers" || credentialAuth) {
236
+ logger.warn(`[connecta] connector "${id}" sends static credentials to ${opts.url} over a non-https:// connection — those tokens will be transmitted in cleartext.`);
237
+ }
238
+ }
239
+ const endpointOrigin = new URL(opts.url).origin;
240
+ return {
241
+ id,
242
+ opts,
243
+ requestAuth,
244
+ forwarding,
245
+ staticClient,
246
+ clientMetadataUrl,
247
+ oauthScope,
248
+ authorizationParams,
249
+ destination,
250
+ isOauth,
251
+ extraHeaders,
252
+ credentialHeader,
253
+ credentialScheme,
254
+ skillsEnabled,
255
+ principalPolicy,
256
+ credentialAuth,
257
+ customFetch,
258
+ customSender,
259
+ requestToken,
260
+ endpointOrigin,
261
+ credentialConfig,
262
+ };
263
+ }