@zackbart/connecta 0.5.0 → 0.6.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 (70) hide show
  1. package/CHANGELOG.md +358 -0
  2. package/README.md +53 -12
  3. package/dist/auth/bearer.d.ts +10 -3
  4. package/dist/auth/bearer.d.ts.map +1 -1
  5. package/dist/auth/bearer.js +21 -0
  6. package/dist/auth/bearer.js.map +1 -1
  7. package/dist/auth/clerk.d.ts +26 -1
  8. package/dist/auth/clerk.d.ts.map +1 -1
  9. package/dist/auth/clerk.js +161 -4
  10. package/dist/auth/clerk.js.map +1 -1
  11. package/dist/connectors/remote-mcp.d.ts.map +1 -1
  12. package/dist/connectors/remote-mcp.js +8 -0
  13. package/dist/connectors/remote-mcp.js.map +1 -1
  14. package/dist/credential-health.d.ts +212 -0
  15. package/dist/credential-health.d.ts.map +1 -0
  16. package/dist/credential-health.js +535 -0
  17. package/dist/credential-health.js.map +1 -0
  18. package/dist/execute.d.ts.map +1 -1
  19. package/dist/execute.js +16 -4
  20. package/dist/execute.js.map +1 -1
  21. package/dist/index.d.ts +46 -5
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +96 -13
  24. package/dist/index.js.map +1 -1
  25. package/dist/meta-tools.d.ts +56 -5
  26. package/dist/meta-tools.d.ts.map +1 -1
  27. package/dist/meta-tools.js +249 -92
  28. package/dist/meta-tools.js.map +1 -1
  29. package/dist/registry.d.ts +62 -0
  30. package/dist/registry.d.ts.map +1 -1
  31. package/dist/registry.js +85 -1
  32. package/dist/registry.js.map +1 -1
  33. package/dist/server.d.ts.map +1 -1
  34. package/dist/server.js +292 -37
  35. package/dist/server.js.map +1 -1
  36. package/dist/skills.d.ts +1 -1
  37. package/dist/skills.d.ts.map +1 -1
  38. package/dist/skills.js +1 -1
  39. package/dist/timeout.d.ts +16 -0
  40. package/dist/timeout.d.ts.map +1 -0
  41. package/dist/timeout.js +38 -0
  42. package/dist/timeout.js.map +1 -0
  43. package/dist/toolkits.d.ts +95 -1
  44. package/dist/toolkits.d.ts.map +1 -1
  45. package/dist/toolkits.js +190 -5
  46. package/dist/toolkits.js.map +1 -1
  47. package/dist/types.d.ts +70 -0
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/ui.d.ts +35 -0
  50. package/dist/ui.d.ts.map +1 -1
  51. package/dist/ui.js +87 -3
  52. package/dist/ui.js.map +1 -1
  53. package/dist/version.d.ts +1 -1
  54. package/dist/version.js +1 -1
  55. package/package.json +1 -1
  56. package/src/auth/bearer.ts +35 -1
  57. package/src/auth/clerk.ts +202 -5
  58. package/src/connectors/remote-mcp.ts +9 -0
  59. package/src/credential-health.ts +736 -0
  60. package/src/execute.ts +28 -4
  61. package/src/index.ts +176 -20
  62. package/src/meta-tools.ts +286 -109
  63. package/src/registry.ts +125 -1
  64. package/src/server.ts +349 -34
  65. package/src/skills.ts +1 -1
  66. package/src/timeout.ts +49 -0
  67. package/src/toolkits.ts +241 -6
  68. package/src/types.ts +76 -1
  69. package/src/ui.ts +98 -3
  70. package/src/version.ts +1 -1
package/dist/ui.js CHANGED
@@ -132,6 +132,45 @@ export function isSafeIconHref(href) {
132
132
  return false;
133
133
  }
134
134
  }
135
+ /**
136
+ * True only for an absolute `https:` URL — the gate for `uiAuth.frontendApiUrl`,
137
+ * the last operator-config value that lands in a URL-valued HTML position (the
138
+ * `<script src>` of `/ui`'s sign-in loader). `javascript:` in a `src` does not
139
+ * execute, so this closes a hole in the *invariant* rather than a live vector:
140
+ * every operator value reaching an `href`/`src` is validated, with no exception
141
+ * left to remember.
142
+ *
143
+ * Stricter than `isSafeHttpUrl` on purpose. There is no `http:` carve-out and no
144
+ * loopback carve-out, because nobody types this value: the shipped Clerk adapter
145
+ * derives it from the publishable key and Clerk's Frontend API is always https.
146
+ * A cleartext script source on the dashboard would be a downgrade even where a
147
+ * browser's mixed-content rules had not already blocked it.
148
+ */
149
+ export function isSafeScriptSrcUrl(url) {
150
+ if (typeof url !== "string")
151
+ return false;
152
+ try {
153
+ return new URL(url).protocol === "https:";
154
+ }
155
+ catch {
156
+ return false;
157
+ }
158
+ }
159
+ /**
160
+ * Names of the `uiAuth` URLs an inbound-auth provider supplied that failed their
161
+ * gate. Lives beside the gate for the same reason `droppedBrandingUrls` does: the
162
+ * startup warning cannot then drift from what rendering actually drops. Every
163
+ * field is read defensively rather than trusted, because a custom `InboundAuth`
164
+ * is untyped at a JS call site — `isSafeScriptSrcUrl` takes `unknown`, and a
165
+ * `uiAuth` that is not the clerk shape is reported as nothing to warn about.
166
+ */
167
+ export function droppedUiAuthUrls(uiAuth) {
168
+ if (!uiAuth || uiAuth.kind !== "clerk")
169
+ return [];
170
+ return isSafeScriptSrcUrl(uiAuth.frontendApiUrl)
171
+ ? []
172
+ : ["uiAuth.frontendApiUrl"];
173
+ }
135
174
  /**
136
175
  * Filter by connector identity/description or tool name/description. A
137
176
  * connector-level match stays visible even when it currently exposes no tools
@@ -168,6 +207,7 @@ export async function buildUiData(registry, baseUrl, serverInfo, credentialVault
168
207
  const requestScope = {};
169
208
  const connectors = await Promise.all(registry.listConnectors().map(async (c) => {
170
209
  const status = await registry.statusFor(c.id, baseUrl, requestScope);
210
+ const credentialCheck = await registry.credentialHealthFor(c.id);
171
211
  let tools = [];
172
212
  // `status()` on an unauthenticated remote connector starts OAuth and
173
213
  // stores its state + PKCE verifier. Probing listTools immediately
@@ -263,6 +303,17 @@ export async function buildUiData(registry, baseUrl, serverInfo, credentialVault
263
303
  : {}),
264
304
  toolCount: tools.length,
265
305
  tools,
306
+ ...(credentialCheck
307
+ ? {
308
+ credentialCheck: {
309
+ state: credentialCheck.state,
310
+ checkedAt: credentialCheck.checkedAt,
311
+ ...(credentialCheck.message
312
+ ? { message: credentialCheck.message }
313
+ : {}),
314
+ },
315
+ }
316
+ : {}),
266
317
  ...(credential ? { credential } : {}),
267
318
  };
268
319
  }));
@@ -288,7 +339,27 @@ function jsonForInlineScript(value) {
288
339
  * retain the manual-token fallback.
289
340
  */
290
341
  export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce) {
291
- const auth = uiAuth ?? { kind: "bearer" };
342
+ const clerk = uiAuth?.kind === "clerk" ? uiAuth : undefined;
343
+ // The Clerk loader's origin. A value that fails the gate is dropped rather
344
+ // than escaped into the page: the loader tag is simply not emitted, the gate
345
+ // reports that Clerk could not load, and the rest of the shell still renders —
346
+ // the same fallback-and-warn posture the branding URLs take, with the drop
347
+ // named in a startup warning (see `droppedUiAuthUrls`).
348
+ const clerkScriptOrigin = clerk && isSafeScriptSrcUrl(clerk.frontendApiUrl)
349
+ ? clerk.frontendApiUrl
350
+ : undefined;
351
+ // Enumerated field by field, because this object is serialized into the page's
352
+ // inline script: a rejected frontendApiUrl must not reach the document through
353
+ // `AUTH` after being kept out of the `<script src>`.
354
+ const auth = clerk
355
+ ? {
356
+ kind: clerk.kind,
357
+ publishableKey: clerk.publishableKey,
358
+ ...(clerkScriptOrigin ? { frontendApiUrl: clerkScriptOrigin } : {}),
359
+ ...(clerk.signInUrl ? { signInUrl: clerk.signInUrl } : {}),
360
+ ...(clerk.signUpUrl ? { signUpUrl: clerk.signUpUrl } : {}),
361
+ }
362
+ : (uiAuth ?? { kind: "bearer" });
292
363
  const brand = resolveBranding(branding);
293
364
  const title = brand.pageTitle;
294
365
  // When the /ui response ships a nonce-based CSP, every <script> it emits must
@@ -309,8 +380,8 @@ export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce) {
309
380
  ? `<a class="product navlink" href="${escapeHtmlAttr(brand.productUrl)}">${escapeHtmlAttr(brand.productName)}</a>`
310
381
  : `<span class="product">${escapeHtmlAttr(brand.productName)}</span>`
311
382
  : "";
312
- const clerkScript = uiAuth?.kind === "clerk"
313
- ? `<script${nonceAttr} defer crossorigin="anonymous" data-clerk-publishable-key="${escapeHtmlAttr(uiAuth.publishableKey)}" src="${escapeHtmlAttr(uiAuth.frontendApiUrl)}/npm/@clerk/clerk-js@6/dist/clerk.browser.js"></script>`
383
+ const clerkScript = clerk && clerkScriptOrigin
384
+ ? `<script${nonceAttr} defer crossorigin="anonymous" data-clerk-publishable-key="${escapeHtmlAttr(clerk.publishableKey)}" src="${escapeHtmlAttr(clerkScriptOrigin)}/npm/@clerk/clerk-js@6/dist/clerk.browser.js"></script>`
314
385
  : "";
315
386
  return `<!doctype html>
316
387
  <html lang="en">
@@ -888,6 +959,19 @@ function render() {
888
959
  if (c.message) {
889
960
  head += '<p class="connector-message msg">' + esc(c.message) + "</p>";
890
961
  }
962
+ if (c.credentialCheck) {
963
+ const check = c.credentialCheck;
964
+ const verdict = check.state === "ok"
965
+ ? "credential verified"
966
+ : check.state === "auth_required"
967
+ ? "credential needs authorization"
968
+ : "credential check failed";
969
+ head += '<p class="connector-check meta">Credential check: ' +
970
+ esc(verdict) + " · " + esc(formatDate(check.checkedAt)) +
971
+ (check.message && check.message !== c.message
972
+ ? " — " + esc(check.message)
973
+ : "") + "</p>";
974
+ }
891
975
  if (c.authorizationUrl) {
892
976
  const safe = safeHttp(c.authorizationUrl);
893
977
  head += safe
package/dist/ui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -4,5 +4,5 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export declare const CONNECTA_VERSION = "0.5.0";
7
+ export declare const CONNECTA_VERSION = "0.6.0";
8
8
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -4,5 +4,5 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export const CONNECTA_VERSION = "0.5.0";
7
+ export const CONNECTA_VERSION = "0.6.0";
8
8
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind nine meta-tools.",
@@ -1,3 +1,7 @@
1
+ import {
2
+ resolveToolkitBinding,
3
+ type ToolkitBindingOptions,
4
+ } from "../toolkits.js";
1
5
  import type { AuthResult, InboundAuth } from "../types.js";
2
6
 
3
7
  const encoder = new TextEncoder();
@@ -15,18 +19,48 @@ function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {
15
19
  return r === 0;
16
20
  }
17
21
 
22
+ export interface BearerTokenOptions extends ToolkitBindingOptions {
23
+ /** Stable identity for this credential, used on activity events. */
24
+ subjectId?: string;
25
+ }
26
+
18
27
  /**
19
28
  * Static bearer-token inbound auth. Constant-time compares the Bearer token
20
29
  * against `secret`. Checked BEFORE the Clerk gate in the server; a mismatch
21
30
  * falls through so a co-configured Clerk provider can still admit the request.
31
+ *
32
+ * `toolkits` binds the token to named toolkits (§16): one `bearerToken(...)` per
33
+ * team credential, each naming the view that team may open. Omit it and the
34
+ * token stays unbound — every declared toolkit plus the full registry.
22
35
  */
23
36
  export function bearerToken(
24
37
  secret: string,
25
- options: { subjectId?: string } = {},
38
+ options: BearerTokenOptions = {},
26
39
  ): InboundAuth {
27
40
  const secretBytes = encoder.encode(secret);
41
+ const toolkitBinding = resolveToolkitBinding(
42
+ options.subjectId
43
+ ? `bearerToken (subjectId "${options.subjectId}")`
44
+ : "bearerToken",
45
+ options,
46
+ );
47
+ if (toolkitBinding && !options.subjectId) {
48
+ // A bound token stands for one team, and both surfaces that report on it —
49
+ // the 403 refusal log (§16) and activity events (§15) — can only say
50
+ // "bearer" without a subjectId. With several bound tokens that makes an
51
+ // operator unable to tell which credential was refused, or whose call
52
+ // succeeded. console.warn (as the Clerk adapter does) because an adapter is
53
+ // constructed before `createConnecta` has a logger to hand it.
54
+ console.warn(
55
+ "[connecta] bearerToken is bound to toolkits " +
56
+ `(${toolkitBinding.toolkits.join(", ") || "unscoped only"}) but has no ` +
57
+ "subjectId: refusal logs and activity events cannot say which " +
58
+ "credential they came from. Pass { subjectId: \"<team>\" }.",
59
+ );
60
+ }
28
61
  return {
29
62
  kind: "bearer",
63
+ ...(toolkitBinding ? { toolkitBinding } : {}),
30
64
  authorize(request): AuthResult {
31
65
  const header = request.headers.get("authorization") ?? "";
32
66
  const match = /^Bearer\s+(.+)$/i.exec(header);
package/src/auth/clerk.ts CHANGED
@@ -1,17 +1,33 @@
1
1
  // Clerk as the OAuth 2.1 authorization server; connecta is the resource server.
2
- // Single tenant, no tenant-tag requirement, optional gate() with ~60s identity
3
- // caching.
2
+ // Single tenant, no tenant-tag requirement, optional allowedDomains/gate() with
3
+ // ~60s identity caching.
4
4
 
5
5
  import { createClerkClient } from "@clerk/backend";
6
+ import {
7
+ resolveToolkitBinding,
8
+ type ToolkitBindingOptions,
9
+ } from "../toolkits.js";
6
10
  import type { AuthResult, InboundAuth } from "../types.js";
7
11
 
8
12
  type ClerkClient = ReturnType<typeof createClerkClient>;
9
13
 
10
- export interface ClerkAuthOptions {
14
+ export interface ClerkAuthOptions extends ToolkitBindingOptions {
11
15
  publishableKey: string;
12
16
  secretKey: string;
13
17
  /** Public base URL of this deployment. Defaults to the request origin. */
14
18
  publicUrl?: string;
19
+ /**
20
+ * Email domains this deployment admits, e.g. `["acme.com"]`. An
21
+ * authenticated user whose verified primary email is not on one of them is
22
+ * rejected exactly like a `gate` rejection. Matching is exact on the whole
23
+ * domain and case-insensitive: `acme.com` admits neither `evil-acme.com` nor
24
+ * `mail.acme.com` — spell a subdomain out to allow it. Entries must be ASCII
25
+ * (punycode for an internationalized domain) and are validated at
26
+ * construction. Absent ⇒ every authenticated user passes this check, as
27
+ * before the option existed. Governs Clerk sign-in only: a co-configured
28
+ * `bearerToken` has no email to read and is admitted without a domain check.
29
+ */
30
+ allowedDomains?: readonly string[];
15
31
  /** Optional allow-list hook. Return false to reject an authenticated user. */
16
32
  gate?: (userId: string, clerk: ClerkClient) => boolean | Promise<boolean>;
17
33
  /** Advertised scopes in protected-resource metadata. */
@@ -49,11 +65,134 @@ function fapiUrl(publishableKey: string): string {
49
65
  const GATE_ALLOWED_TTL_MS = 60 * 1000;
50
66
  const GATE_FORBIDDEN_TTL_MS = 30 * 1000;
51
67
 
68
+ /**
69
+ * One label of a domain: ASCII letters/digits, interior hyphens only, 63
70
+ * characters at most. ASCII-only is deliberate — an internationalized domain
71
+ * must be in its punycode (`xn--…`) form, so a Unicode confusable can neither be
72
+ * typed into the allowlist nor arrive in an email address and pass for a domain
73
+ * the operator cannot tell from theirs by eye.
74
+ */
75
+ const DOMAIN_LABEL_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
76
+
77
+ /**
78
+ * Is this string a domain, before any case folding? Both sides of the
79
+ * comparison — the operator's allowlist entries and the domain read off a
80
+ * user's email — are checked against this one grammar, so neither side can be
81
+ * *repaired* into a match by the normalization that follows: `"acme.com\n"` and
82
+ * `" acme.com"` are malformed, not `acme.com`, and an `akme.com` spelled with a
83
+ * U+212A KELVIN SIGN is rejected here rather than folded to plain ASCII `k` by
84
+ * `toLowerCase`.
85
+ */
86
+ function isDomain(domain: string): boolean {
87
+ return (
88
+ domain.length > 0 &&
89
+ domain.length <= 253 &&
90
+ domain.includes(".") &&
91
+ domain.split(".").every((label) => DOMAIN_LABEL_RE.test(label))
92
+ );
93
+ }
94
+
95
+ /**
96
+ * Validate and lowercase `allowedDomains` at construction. Everything here
97
+ * throws rather than dropping the entry: an allowlist that does not say what
98
+ * its author meant is invisible until the day it admits the wrong caller.
99
+ */
100
+ function normalizeAllowedDomains(
101
+ value: readonly string[] | undefined,
102
+ ): ReadonlySet<string> | undefined {
103
+ if (value === undefined) return undefined;
104
+ if (!Array.isArray(value)) {
105
+ throw new Error("clerkAuth: `allowedDomains` must be an array of domains.");
106
+ }
107
+ if (value.length === 0) {
108
+ // Fail-closed, an empty list admits nobody and the deployment is dead on
109
+ // arrival; read as "no restriction", it is the one shape here that fails
110
+ // OPEN. Neither is what anyone meant to write.
111
+ throw new Error(
112
+ "clerkAuth: `allowedDomains` is empty. List at least one domain, or " +
113
+ "drop the option to admit every authenticated user.",
114
+ );
115
+ }
116
+ const domains = new Set<string>();
117
+ for (const entry of value) {
118
+ if (typeof entry !== "string") {
119
+ throw new Error(
120
+ `clerkAuth: \`allowedDomains\` entry ${JSON.stringify(entry)} is not a string.`,
121
+ );
122
+ }
123
+ // Surrounding whitespace is the one thing forgiven, and only here: this is
124
+ // operator config read at construction, where a stray space is a typo the
125
+ // operator can see in the throw. Nothing is forgiven on the email side.
126
+ const domain = entry.trim();
127
+ if (!isDomain(domain)) {
128
+ const hint = domain.includes("@")
129
+ ? " Write the domain alone, with no `@` and no local part."
130
+ : "";
131
+ throw new Error(
132
+ `clerkAuth: \`allowedDomains\` entry ${JSON.stringify(entry)} is not a ` +
133
+ `domain (expected something like "acme.com").${hint}`,
134
+ );
135
+ }
136
+ domains.add(domain.toLowerCase());
137
+ }
138
+ return domains;
139
+ }
140
+
141
+ /**
142
+ * Bounded, escaped form of the denied domain for the operator log — the same
143
+ * treatment `src/server.ts` gives a rejected toolkit name. An email domain is
144
+ * caller-influenced (anyone who controls a mailbox controls its domain): the
145
+ * bound is what a 253-byte domain needs, and the escaping — JSON.stringify plus
146
+ * the hand-rolled U+2028/U+2029 pass it leaves raw — is defense in depth behind
147
+ * `isDomain`, which has already ruled out the newline that would forge a line.
148
+ */
149
+ function loggableDomain(domain: string): string {
150
+ const bounded = domain.slice(0, 100);
151
+ const escaped = JSON.stringify(bounded).replace(
152
+ /[\u2028\u2029]/g,
153
+ (ch) => `\\u${ch.charCodeAt(0).toString(16)}`,
154
+ );
155
+ return escaped + (bounded.length < domain.length ? " (truncated)" : "");
156
+ }
157
+
158
+ /**
159
+ * The domain of an email address, lowercased for comparison, or null when the
160
+ * address does not have exactly one well-formed domain to read.
161
+ *
162
+ * Nothing here repairs the input. The domain is validated as it arrived and
163
+ * only then lowercased, so `dev@ acme.com`, `dev@acme.com\n` and `dev@acme.com.`
164
+ * are malformed addresses that DENY, rather than whitespace-trimmed or
165
+ * dot-stripped into a match for `acme.com`. The split is on the last `@`, the
166
+ * part a mail system routes on, so this reads the same domain that would
167
+ * receive the mail. (Under exact set matching a first-`@` split could not fail
168
+ * open either — it would just read a domain nobody delivers to.)
169
+ */
170
+ function emailDomain(email: string): string | null {
171
+ const at = email.lastIndexOf("@");
172
+ if (at <= 0 || at === email.length - 1) return null;
173
+ const domain = email.slice(at + 1);
174
+ return isDomain(domain) ? domain.toLowerCase() : null;
175
+ }
176
+
177
+ /**
178
+ * Clerk inbound auth.
179
+ *
180
+ * `allowedDomains` and `gate` decide WHO is admitted (both must pass);
181
+ * `toolkits` decides WHICH view the admitted user gets.
182
+ *
183
+ * `toolkits` binds every user this provider admits to those toolkits (§16). For
184
+ * a per-team split, configure one `clerkAuth(...)` per team — the same keys, a
185
+ * `gate` naming that team's users, and that team's `toolkits`. The server tries
186
+ * providers in order and the first that admits the user supplies the binding, so
187
+ * a user one gate rejects falls through to the next.
188
+ */
52
189
  export function clerkAuth(opts: ClerkAuthOptions): InboundAuth {
53
190
  const clerk = createClerkClient({
54
191
  secretKey: opts.secretKey,
55
192
  publishableKey: opts.publishableKey,
56
193
  });
194
+ const toolkitBinding = resolveToolkitBinding("clerkAuth", opts);
195
+ const allowedDomains = normalizeAllowedDomains(opts.allowedDomains);
57
196
  const scopes = opts.scopes ?? ["openid", "profile", "email"];
58
197
  const gateCache = new Map<string, { allowed: boolean; exp: number }>();
59
198
 
@@ -80,13 +219,70 @@ export function clerkAuth(opts: ClerkAuthOptions): InboundAuth {
80
219
  { status: 403, headers: { "Content-Type": "application/json" } },
81
220
  );
82
221
 
222
+ /**
223
+ * The domain half of admission. Fails CLOSED on every uncertainty — no
224
+ * primary email, an unverified one, a malformed address, or the lookup
225
+ * itself failing — because "we could not tell" and "they belong here" must
226
+ * not be the same answer for a membership rule.
227
+ */
228
+ const checkDomain = async (userId: string): Promise<boolean> => {
229
+ if (!allowedDomains) return true;
230
+ let email: string | undefined;
231
+ try {
232
+ const user = await clerk.users.getUser(userId);
233
+ const primary = user.emailAddresses?.find(
234
+ (address) => address.id === user.primaryEmailAddressId,
235
+ );
236
+ if (primary?.verification?.status === "verified") {
237
+ email = primary.emailAddress;
238
+ }
239
+ } catch (error) {
240
+ console.warn(
241
+ `[connecta] clerk email lookup failed for ${userId}: ${
242
+ error instanceof Error ? error.message : String(error)
243
+ } — denying`,
244
+ );
245
+ return false;
246
+ }
247
+ const domain = email ? emailDomain(email) : null;
248
+ if (!domain) {
249
+ // One line for three cases (no primary email, unverified, or an address
250
+ // with no readable domain) because the caller must not be able to tell
251
+ // them apart — but it must not claim the email is missing when it is
252
+ // there and malformed.
253
+ console.warn(
254
+ `[connecta] clerk user ${userId} has no verified primary email with a ` +
255
+ "well-formed domain — denying",
256
+ );
257
+ return false;
258
+ }
259
+ if (!allowedDomains.has(domain)) {
260
+ // The domain, never the address: this is an operator log, not a place to
261
+ // spill the local part of someone's email on every denied request.
262
+ console.warn(
263
+ `[connecta] clerk user ${userId} denied: email domain ` +
264
+ `${loggableDomain(domain)} is not on allowedDomains`,
265
+ );
266
+ return false;
267
+ }
268
+ return true;
269
+ };
270
+
271
+ /**
272
+ * Is this authenticated user admitted? The domain allowlist and `gate` both
273
+ * have to say yes, and the allowlist runs first so an outsider never reaches
274
+ * operator gate code. One cached verdict covers both, so composing them costs
275
+ * no more Clerk calls than `gate` alone did.
276
+ */
83
277
  const checkGate = async (userId: string): Promise<boolean> => {
84
- if (!opts.gate) return true;
278
+ if (!opts.gate && !allowedDomains) return true;
85
279
  const hit = gateCache.get(userId);
86
280
  if (hit && Date.now() < hit.exp) return hit.allowed;
87
281
  let allowed = false;
88
282
  try {
89
- allowed = await opts.gate(userId, clerk);
283
+ allowed =
284
+ (await checkDomain(userId)) &&
285
+ (opts.gate ? await opts.gate(userId, clerk) : true);
90
286
  } catch {
91
287
  allowed = false;
92
288
  }
@@ -101,6 +297,7 @@ export function clerkAuth(opts: ClerkAuthOptions): InboundAuth {
101
297
 
102
298
  return {
103
299
  kind: "clerk",
300
+ ...(toolkitBinding ? { toolkitBinding } : {}),
104
301
  uiAuth: {
105
302
  kind: "clerk",
106
303
  publishableKey: opts.publishableKey,
@@ -345,6 +345,15 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
345
345
  };
346
346
 
347
347
  if (opts.auth?.type === "oauth") {
348
+ // The credential liveness checks (issue #24) probe only connectors that
349
+ // actually hold a stored grant: with no tokens there is nothing whose
350
+ // liveness could have lapsed, and a `status()` probe would kick off DCR +
351
+ // consent on a timer for a connector nobody has authorized yet.
352
+ connector.hasStoredCredential = async (ctx) => {
353
+ const state = stateFor(ctx);
354
+ return (await getProvider(ctx, state).tokens()) !== undefined;
355
+ };
356
+
348
357
  connector.verifyState = async (oauthState, ctx) => {
349
358
  const state = stateFor(ctx);
350
359
  return getProvider(ctx, state).verifyState(oauthState);