mcp-scraper 0.66.3 → 0.66.5

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.
@@ -199,7 +199,7 @@ import {
199
199
  resolveDeploymentProfile,
200
200
  resolveLocalSourcebookSchemaType,
201
201
  transcribeMediaUrl
202
- } from "./chunk-LNLMOBP6.js";
202
+ } from "./chunk-OJFWURGD.js";
203
203
  import {
204
204
  auditImageUrls,
205
205
  auditImages,
@@ -256,7 +256,7 @@ import {
256
256
  } from "./chunk-GGZEC22A.js";
257
257
  import {
258
258
  PACKAGE_VERSION
259
- } from "./chunk-JBFLOJGM.js";
259
+ } from "./chunk-RSFMR7O7.js";
260
260
  import {
261
261
  SITE_EXTRACT_ARTIFACT_PREFIX,
262
262
  abandonExtractSettlement,
@@ -13665,12 +13665,39 @@ function localSourcebookPublicUrls(category, state, slug4) {
13665
13665
  };
13666
13666
  }
13667
13667
  function localSourcebookPublicationLinks(submission) {
13668
+ const isLive = submission.publishedRevision !== null && submission.status !== "rejected" && submission.status !== "unpublished";
13668
13669
  return {
13669
13670
  ...localSourcebookPublicUrls(submission.category, submission.state, submission.slug),
13670
13671
  publicationStatus: submission.status,
13671
- isLive: submission.status === "published" && submission.publishedRevision !== null
13672
+ isLive
13672
13673
  };
13673
13674
  }
13675
+ function localSourcebookPublicationMessage(submission) {
13676
+ const { profileUrl } = localSourcebookPublicUrls(submission.category, submission.state, submission.slug);
13677
+ if (submission.status === "published" && submission.publishedRevision !== null) {
13678
+ return `Your Local Sourcebook listing is live: ${profileUrl}`;
13679
+ }
13680
+ if (submission.status === "failed") {
13681
+ if (submission.publishedRevision !== null) {
13682
+ return `We couldn't refresh this Local Sourcebook listing, but the previous version is still live: ${profileUrl}`;
13683
+ }
13684
+ return "We couldn't publish this Local Sourcebook listing because evidence collection failed. Review the reported issue before trying again.";
13685
+ }
13686
+ if (submission.status === "rejected") {
13687
+ return "This Local Sourcebook listing was rejected and is not live.";
13688
+ }
13689
+ if (submission.status === "unpublished") {
13690
+ return "This Local Sourcebook listing has been unpublished and is not live.";
13691
+ }
13692
+ if (submission.publishedRevision !== null) {
13693
+ return `We are collecting and compiling refreshed evidence. The current Local Sourcebook listing remains live: ${profileUrl}`;
13694
+ }
13695
+ return "Submission received. We are collecting and compiling evidence, so this Local Sourcebook listing is not live yet. Check the submission status again in about 10 seconds.";
13696
+ }
13697
+ function localSourcebookStatusCheck(submission) {
13698
+ const required = submission.status === "queued" || submission.status === "enriching" || submission.status === "needs_review";
13699
+ return required ? { required: true, submissionId: submission.id, pollAfterSeconds: 10 } : { required: false, submissionId: submission.id };
13700
+ }
13674
13701
 
13675
13702
  // src/api/local-sourcebook-compiler.ts
13676
13703
  var CATEGORY_LABELS = {
@@ -24864,7 +24891,7 @@ localSourcebookApp.post("/capture", createApiKeyAuth(), async (c) => {
24864
24891
  persisted = true;
24865
24892
  await persistLocalSourcebookTags(submission.id, Number(user.id), validation.normalizedTags, parsed.data.tagDecisions);
24866
24893
  const dispatched = await dispatchLocalSourcebookAcquisition(submission.id).catch(() => false);
24867
- return c.json({ ok: true, valid: true, submission, publicUrls: localSourcebookPublicationLinks(submission), normalizedTags: validation.normalizedTags, acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(identity.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
24894
+ return c.json({ ok: true, valid: true, message: localSourcebookPublicationMessage(submission), statusCheck: localSourcebookStatusCheck(submission), submission, publicUrls: localSourcebookPublicationLinks(submission), normalizedTags: validation.normalizedTags, acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(identity.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
24868
24895
  } catch (error) {
24869
24896
  if (billing && !persisted) await refundListingWrite(billing, "Local Sourcebook creation failed before persistence.").catch(() => null);
24870
24897
  const message = error instanceof Error ? error.message : String(error);
@@ -24913,7 +24940,7 @@ localSourcebookApp.post("/submissions", createApiKeyAuth(), async (c) => {
24913
24940
  });
24914
24941
  persisted = true;
24915
24942
  const dispatched = await dispatchLocalSourcebookAcquisition(submission.id).catch(() => false);
24916
- return c.json({ ok: true, submission, publicUrls: localSourcebookPublicationLinks(submission), acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(input.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
24943
+ return c.json({ ok: true, message: localSourcebookPublicationMessage(submission), statusCheck: localSourcebookStatusCheck(submission), submission, publicUrls: localSourcebookPublicationLinks(submission), acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(input.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
24917
24944
  } catch (error) {
24918
24945
  if (billing && !persisted) await refundListingWrite(billing, "Local Sourcebook creation failed before persistence.").catch(() => null);
24919
24946
  return c.json({ error: "submission_conflict", message: error instanceof Error ? error.message : String(error) }, 409);
@@ -24923,7 +24950,7 @@ localSourcebookApp.get("/submissions/:id", createApiKeyAuth(), async (c) => {
24923
24950
  const userId = Number(c.get("user").id);
24924
24951
  const submission = await getLocalSourcebookSubmission(c.req.param("id"), userId);
24925
24952
  if (!submission) return c.json({ error: "not_found" }, 404);
24926
- return c.json({ ok: true, submission, publicUrls: localSourcebookPublicationLinks(submission), draft: await getLocalSourcebookDraft(submission.id, userId) });
24953
+ return c.json({ ok: true, message: localSourcebookPublicationMessage(submission), statusCheck: localSourcebookStatusCheck(submission), submission, publicUrls: localSourcebookPublicationLinks(submission), draft: await getLocalSourcebookDraft(submission.id, userId) });
24927
24954
  });
24928
24955
  localSourcebookApp.on(["PATCH", "POST"], "/submissions/:id/draft", createApiKeyAuth(), (c) => c.json({
24929
24956
  error: "evidence_refresh_required",
@@ -24958,7 +24985,7 @@ localSourcebookApp.post("/submissions/:id/refresh", createApiKeyAuth(), async (c
24958
24985
  return c.json({ error: "not_found" }, 404);
24959
24986
  }
24960
24987
  const dispatched = await dispatchLocalSourcebookAcquisition(submission.id).catch(() => false);
24961
- return c.json({ ok: true, submission, publicUrls: localSourcebookPublicationLinks(submission), acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(submission.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
24988
+ return c.json({ ok: true, message: localSourcebookPublicationMessage(submission), statusCheck: localSourcebookStatusCheck(submission), submission, publicUrls: localSourcebookPublicationLinks(submission), acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(submission.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
24962
24989
  } catch (error) {
24963
24990
  if (billing) await refundListingWrite(billing, "Local Sourcebook refresh failed before persistence.").catch(() => null);
24964
24991
  return c.json({ error: "moderated_listing", message: error instanceof Error ? error.message : String(error) }, 409);
@@ -41109,6 +41136,12 @@ function ensureServiceConnectionsSchema() {
41109
41136
  transport TEXT NOT NULL,
41110
41137
  upstream_connection_id TEXT,
41111
41138
  label TEXT,
41139
+ user_label TEXT,
41140
+ provider_account_id TEXT,
41141
+ provider_account_email TEXT,
41142
+ provider_account_name TEXT,
41143
+ provider_identity_checked_at TEXT,
41144
+ provider_identity_source_updated_at TEXT,
41112
41145
  lifecycle_status TEXT NOT NULL DEFAULT 'pending',
41113
41146
  operational_status TEXT NOT NULL DEFAULT 'unknown',
41114
41147
  reconnect_required INTEGER NOT NULL DEFAULT 0,
@@ -41130,6 +41163,30 @@ function ensureServiceConnectionsSchema() {
41130
41163
  UNIQUE(user_id, provider_config_key, upstream_connection_id)
41131
41164
  )
41132
41165
  `);
41166
+ try {
41167
+ await db.execute(`ALTER TABLE service_connections ADD COLUMN user_label TEXT`);
41168
+ } catch {
41169
+ }
41170
+ try {
41171
+ await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_id TEXT`);
41172
+ } catch {
41173
+ }
41174
+ try {
41175
+ await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_email TEXT`);
41176
+ } catch {
41177
+ }
41178
+ try {
41179
+ await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_name TEXT`);
41180
+ } catch {
41181
+ }
41182
+ try {
41183
+ await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_checked_at TEXT`);
41184
+ } catch {
41185
+ }
41186
+ try {
41187
+ await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_source_updated_at TEXT`);
41188
+ } catch {
41189
+ }
41133
41190
  await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_user_updated ON service_connections(user_id, updated_at DESC)`);
41134
41191
  await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_upstream ON service_connections(provider_config_key, upstream_connection_id)`);
41135
41192
  await db.execute(`
@@ -41190,6 +41247,12 @@ function rowToRecord(row) {
41190
41247
  transport: row.transport === "remote_mcp" ? "remote_mcp" : "nango",
41191
41248
  upstreamConnectionId: typeof row.upstream_connection_id === "string" ? row.upstream_connection_id : null,
41192
41249
  label: typeof row.label === "string" ? row.label : null,
41250
+ userLabel: typeof row.user_label === "string" ? row.user_label : null,
41251
+ providerAccountId: typeof row.provider_account_id === "string" ? row.provider_account_id : null,
41252
+ providerAccountEmail: typeof row.provider_account_email === "string" ? row.provider_account_email : null,
41253
+ providerAccountName: typeof row.provider_account_name === "string" ? row.provider_account_name : null,
41254
+ providerIdentityCheckedAt: typeof row.provider_identity_checked_at === "string" ? row.provider_identity_checked_at : null,
41255
+ providerIdentitySourceUpdatedAt: typeof row.provider_identity_source_updated_at === "string" ? row.provider_identity_source_updated_at : null,
41193
41256
  lifecycleStatus: String(row.lifecycle_status),
41194
41257
  operationalStatus: String(row.operational_status),
41195
41258
  reconnectRequired: Number(row.reconnect_required) === 1,
@@ -41275,6 +41338,34 @@ async function updateServiceConnectionTools(connectionId, readTools, actionTools
41275
41338
  args: [JSON.stringify(readTools), JSON.stringify(actionTools), toolRevision, connectionId]
41276
41339
  });
41277
41340
  }
41341
+ async function setServiceConnectionProviderIdentity(args) {
41342
+ await ensureServiceConnectionsSchema();
41343
+ await getDb().execute({
41344
+ sql: `
41345
+ UPDATE service_connections SET
41346
+ provider_account_id = ?, provider_account_email = ?, provider_account_name = ?,
41347
+ provider_identity_checked_at = datetime('now'), provider_identity_source_updated_at = ?,
41348
+ updated_at = datetime('now')
41349
+ WHERE id = ?
41350
+ `,
41351
+ args: [
41352
+ args.providerAccountId ?? null,
41353
+ args.providerAccountEmail?.toLowerCase() ?? null,
41354
+ args.providerAccountName ?? null,
41355
+ args.sourceUpdatedAt,
41356
+ args.connectionId
41357
+ ]
41358
+ });
41359
+ }
41360
+ async function setServiceConnectionUserLabel(identity, connectionId, userLabel) {
41361
+ const connection = await getOwnedServiceConnection(identity, connectionId);
41362
+ if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
41363
+ await getDb().execute({
41364
+ sql: `UPDATE service_connections SET user_label = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
41365
+ args: [userLabel, connectionId, connection.userId]
41366
+ });
41367
+ return userLabel;
41368
+ }
41278
41369
  async function recordServiceConnectionHealth(args) {
41279
41370
  await ensureServiceConnectionsSchema();
41280
41371
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -44114,6 +44205,7 @@ var DEFAULT_NANGO_MCP_URL = "https://api.nango.dev/mcp";
44114
44205
  var NANGO_TIMEOUT_MS = 2e4;
44115
44206
  var NANGO_CONNECTION_PAGE_SIZE = 100;
44116
44207
  var NANGO_CONNECTION_MAX_PAGES = 20;
44208
+ var PROVIDER_IDENTITY_RETRY_MS = 6 * 60 * 60 * 1e3;
44117
44209
  var MAIN_INTEGRATION_CONTRACT_VERSION = "2026-07-16.1";
44118
44210
  var MAIN_INTEGRATION_CONTRACT_HASH = "f037859ec80c2ea2fac6e4a67c1a59f3200dd22d3642c97789b9bb3236723d3e";
44119
44211
  var MainNangoTransportError = class extends Error {
@@ -44217,7 +44309,8 @@ async function nangoRequest(path5, options = {}) {
44217
44309
  headers: {
44218
44310
  authorization: `Bearer ${nangoSecret()}`,
44219
44311
  accept: "application/json",
44220
- ...options.body ? { "content-type": "application/json" } : {}
44312
+ ...options.body ? { "content-type": "application/json" } : {},
44313
+ ...options.headers
44221
44314
  },
44222
44315
  body: options.body ? JSON.stringify(options.body) : void 0,
44223
44316
  signal: AbortSignal.timeout(NANGO_TIMEOUT_MS)
@@ -44265,22 +44358,142 @@ function discoveredConnection(value) {
44265
44358
  const upstreamConnectionId = text(row?.connection_id);
44266
44359
  const providerConfigKey = text(row?.provider_config_key);
44267
44360
  if (!upstreamConnectionId || !providerConfigKey) return null;
44268
- const tags = asRecord(row?.tags);
44269
- const endUser = asRecord(row?.end_user);
44270
44361
  const errors = Array.isArray(row?.errors) ? row.errors : [];
44271
44362
  const authError = errors.some((error) => text(asRecord(error)?.type) === "auth");
44272
44363
  return {
44273
44364
  upstreamConnectionId,
44274
44365
  providerConfigKey,
44275
44366
  provider: text(row?.provider) ?? providerConfigKey,
44276
- label: text(tags?.end_user_display_name) ?? text(endUser?.display_name) ?? text(tags?.end_user_email) ?? text(endUser?.email),
44367
+ // Nango's end-user fields identify the MCP Scraper tenant, not the
44368
+ // provider-side principal that authorized this connection.
44369
+ label: null,
44277
44370
  lifecycleStatus: authError ? "needs_reauth" : "connected",
44278
44371
  reconnectRequired: authError,
44279
44372
  createdAt: text(row?.created_at) ?? text(row?.created),
44280
44373
  updatedAt: text(row?.updated_at)
44281
44374
  };
44282
44375
  }
44283
- async function discoverOwnedNangoConnections(identity) {
44376
+ var IDENTITY_SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set([
44377
+ "facebook",
44378
+ "github-getting-started",
44379
+ "google-analytics",
44380
+ "google-calendar",
44381
+ "google-drive",
44382
+ "google-mail",
44383
+ "google-search-console",
44384
+ "linkedin",
44385
+ "meta-marketing-api",
44386
+ "slack",
44387
+ "twitter-v2",
44388
+ "xero",
44389
+ "youtube",
44390
+ "zoom"
44391
+ ]);
44392
+ function scalarId(value) {
44393
+ const valueText = text(value);
44394
+ if (valueText) return valueText;
44395
+ return typeof value === "number" && Number.isFinite(value) ? String(value) : null;
44396
+ }
44397
+ function providerEmail(value) {
44398
+ const email = text(value)?.toLowerCase() ?? null;
44399
+ return email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) ? email : null;
44400
+ }
44401
+ function compactProviderIdentity(identity) {
44402
+ return identity.id || identity.email || identity.name ? identity : null;
44403
+ }
44404
+ async function providerProxyJson(args) {
44405
+ return nangoRequest(`/proxy${args.path}`, {
44406
+ query: args.query,
44407
+ headers: {
44408
+ "connection-id": args.upstreamConnectionId,
44409
+ "provider-config-key": args.providerConfigKey,
44410
+ ...args.baseUrlOverride ? { "base-url-override": args.baseUrlOverride } : {}
44411
+ }
44412
+ });
44413
+ }
44414
+ async function googleProviderAccountIdentity(args) {
44415
+ const request = (path5, query, baseUrlOverride) => providerProxyJson({ ...args, path: path5, query, baseUrlOverride });
44416
+ try {
44417
+ if (args.providerConfigKey === "google-mail") {
44418
+ const payload = asRecord(await request("/gmail/v1/users/me/profile", void 0, "https://gmail.googleapis.com"));
44419
+ const email = providerEmail(payload?.emailAddress);
44420
+ return compactProviderIdentity({ id: email, email, name: null });
44421
+ }
44422
+ if (args.providerConfigKey === "google-drive") {
44423
+ const payload = asRecord(await request("/drive/v3/about", { fields: "user(permissionId,displayName,emailAddress)" }, "https://www.googleapis.com"));
44424
+ const user = asRecord(payload?.user);
44425
+ return compactProviderIdentity({
44426
+ id: text(user?.permissionId),
44427
+ email: providerEmail(user?.emailAddress),
44428
+ name: text(user?.displayName)
44429
+ });
44430
+ }
44431
+ if (args.providerConfigKey === "google-calendar") {
44432
+ const payload = asRecord(await request("/calendar/v3/calendars/primary", void 0, "https://www.googleapis.com"));
44433
+ const id = text(payload?.id);
44434
+ return compactProviderIdentity({ id, email: providerEmail(id), name: text(payload?.summary) });
44435
+ }
44436
+ if (args.providerConfigKey === "youtube") {
44437
+ const payload = asRecord(await request("/youtube/v3/channels", { part: "snippet", mine: "true", maxResults: 1 }, "https://www.googleapis.com"));
44438
+ const first = Array.isArray(payload?.items) ? asRecord(payload.items[0]) : null;
44439
+ return compactProviderIdentity({ id: text(first?.id), email: null, name: text(asRecord(first?.snippet)?.title) });
44440
+ }
44441
+ } catch {
44442
+ }
44443
+ try {
44444
+ const payload = asRecord(await request("/v1/userinfo", void 0, "https://openidconnect.googleapis.com"));
44445
+ return compactProviderIdentity({
44446
+ id: text(payload?.sub),
44447
+ email: payload?.email_verified === false ? null : providerEmail(payload?.email),
44448
+ name: text(payload?.name)
44449
+ });
44450
+ } catch {
44451
+ return null;
44452
+ }
44453
+ }
44454
+ async function getNangoProviderAccountIdentity(args) {
44455
+ if (!IDENTITY_SUPPORTED_PROVIDERS.has(args.providerConfigKey)) return null;
44456
+ if (args.providerConfigKey.startsWith("google-") || args.providerConfigKey === "youtube") {
44457
+ return googleProviderAccountIdentity(args);
44458
+ }
44459
+ try {
44460
+ if (args.providerConfigKey === "github-getting-started") {
44461
+ const payload = asRecord(await providerProxyJson({ ...args, path: "/user" }));
44462
+ return compactProviderIdentity({ id: scalarId(payload?.id), email: providerEmail(payload?.email), name: text(payload?.name) ?? text(payload?.login) });
44463
+ }
44464
+ if (args.providerConfigKey === "slack") {
44465
+ const payload = asRecord(await providerProxyJson({ ...args, path: "/api/auth.test" }));
44466
+ return compactProviderIdentity({ id: text(payload?.user_id), email: null, name: [text(payload?.user), text(payload?.team)].filter(Boolean).join(" \xB7 ") || null });
44467
+ }
44468
+ if (args.providerConfigKey === "zoom") {
44469
+ const payload = asRecord(await providerProxyJson({ ...args, path: "/v2/users/me" }));
44470
+ const fullName = [text(payload?.first_name), text(payload?.last_name)].filter(Boolean).join(" ");
44471
+ return compactProviderIdentity({ id: text(payload?.id), email: providerEmail(payload?.email), name: text(payload?.display_name) ?? (fullName || null) });
44472
+ }
44473
+ if (args.providerConfigKey === "linkedin") {
44474
+ const payload = asRecord(await providerProxyJson({ ...args, path: "/v2/userinfo" }));
44475
+ return compactProviderIdentity({ id: text(payload?.sub), email: payload?.email_verified === false ? null : providerEmail(payload?.email), name: text(payload?.name) });
44476
+ }
44477
+ if (args.providerConfigKey === "meta-marketing-api" || args.providerConfigKey === "facebook") {
44478
+ const payload = asRecord(await providerProxyJson({ ...args, path: "/me", query: { fields: "id,name,email" } }));
44479
+ return compactProviderIdentity({ id: text(payload?.id), email: providerEmail(payload?.email), name: text(payload?.name) });
44480
+ }
44481
+ if (args.providerConfigKey === "twitter-v2") {
44482
+ const payload = asRecord(await providerProxyJson({ ...args, path: "/2/users/me", query: { "user.fields": "id,name,username" } }));
44483
+ const data = asRecord(payload?.data);
44484
+ return compactProviderIdentity({ id: text(data?.id), email: null, name: text(data?.username) ?? text(data?.name) });
44485
+ }
44486
+ if (args.providerConfigKey === "xero") {
44487
+ const payload = await providerProxyJson({ ...args, path: "/connections" });
44488
+ const first = Array.isArray(payload) ? asRecord(payload[0]) : null;
44489
+ return compactProviderIdentity({ id: text(first?.tenantId), email: null, name: text(first?.tenantName) });
44490
+ }
44491
+ } catch {
44492
+ return null;
44493
+ }
44494
+ return null;
44495
+ }
44496
+ async function discoverOwnedNangoConnections(identity, options = {}) {
44284
44497
  const rows = [];
44285
44498
  for (let page = 0; page < NANGO_CONNECTION_MAX_PAGES; page += 1) {
44286
44499
  const payload = await nangoRequest("/connections", {
@@ -44291,6 +44504,35 @@ async function discoverOwnedNangoConnections(identity) {
44291
44504
  if (pageRows.length < NANGO_CONNECTION_PAGE_SIZE) break;
44292
44505
  }
44293
44506
  const discovered = rows.filter((row) => connectionIdentityMatches(row, identity)).map(discoveredConnection).filter((connection) => connection !== null);
44507
+ const reconciled = await reconcileDiscoveredNangoConnections(identity, discovered);
44508
+ if (options.resolveProviderIdentity === false) return reconciled;
44509
+ const remoteByKey = new Map(discovered.map((connection) => [
44510
+ `${connection.providerConfigKey}\0${connection.upstreamConnectionId}`,
44511
+ connection
44512
+ ]));
44513
+ await Promise.all(reconciled.map(async (connection) => {
44514
+ if (connection.lifecycleStatus !== "connected" || !connection.upstreamConnectionId) return;
44515
+ const remote = remoteByKey.get(`${connection.providerConfigKey}\0${connection.upstreamConnectionId}`);
44516
+ if (!remote) return;
44517
+ const sourceUpdatedAt = remote.updatedAt ?? remote.createdAt ?? connection.lifecycleStatus;
44518
+ const hasProviderIdentity = Boolean(
44519
+ connection.providerAccountId || connection.providerAccountEmail || connection.providerAccountName
44520
+ );
44521
+ const checkedAt = connection.providerIdentityCheckedAt ? Date.parse(`${connection.providerIdentityCheckedAt.replace(" ", "T")}Z`) : Number.NaN;
44522
+ const unavailableIdentityIsFresh = !hasProviderIdentity && Number.isFinite(checkedAt) && Date.now() - checkedAt < PROVIDER_IDENTITY_RETRY_MS;
44523
+ if (connection.providerIdentitySourceUpdatedAt === sourceUpdatedAt && (hasProviderIdentity || unavailableIdentityIsFresh)) return;
44524
+ const providerIdentity = await getNangoProviderAccountIdentity({
44525
+ upstreamConnectionId: connection.upstreamConnectionId,
44526
+ providerConfigKey: connection.providerConfigKey
44527
+ });
44528
+ await setServiceConnectionProviderIdentity({
44529
+ connectionId: connection.id,
44530
+ providerAccountId: providerIdentity?.id,
44531
+ providerAccountEmail: providerIdentity?.email,
44532
+ providerAccountName: providerIdentity?.name,
44533
+ sourceUpdatedAt
44534
+ });
44535
+ }));
44294
44536
  return reconcileDiscoveredNangoConnections(identity, discovered);
44295
44537
  }
44296
44538
  async function listNangoCatalogDirect() {
@@ -44965,7 +45207,7 @@ async function getNangoConnections(identity, options = {}) {
44965
45207
  if (mainOwnsIntegrations()) {
44966
45208
  let connections;
44967
45209
  try {
44968
- connections = await discoverOwnedNangoConnections(identity);
45210
+ connections = await discoverOwnedNangoConnections(identity, { resolveProviderIdentity: !options.summaryOnly });
44969
45211
  } catch (error) {
44970
45212
  try {
44971
45213
  connections = await listServiceConnections(identity, "nango");
@@ -44986,11 +45228,12 @@ async function getNangoConnections(identity, options = {}) {
44986
45228
  connectionId: connection.id,
44987
45229
  providerConfigKey: connection.providerConfigKey,
44988
45230
  provider: connection.provider,
44989
- label: connection.label || connection.providerConfigKey,
44990
- providerAccountId: null,
44991
- providerAccountEmail: null,
44992
- providerAccountName: null,
44993
- providerIdentityStatus: "unavailable",
45231
+ label: connection.userLabel || connection.providerAccountEmail || connection.providerAccountName || (connection.label && connection.label.toLowerCase() !== identity.trim().toLowerCase() ? connection.label : null) || connection.providerConfigKey,
45232
+ userLabel: connection.userLabel,
45233
+ providerAccountId: connection.providerAccountId,
45234
+ providerAccountEmail: connection.providerAccountEmail,
45235
+ providerAccountName: connection.providerAccountName,
45236
+ providerIdentityStatus: connection.providerIdentityCheckedAt ? connection.providerAccountId || connection.providerAccountEmail || connection.providerAccountName ? "verified" : "unavailable" : "pending",
44994
45237
  status: connection.reconnectRequired ? "needs_reauth" : "connected",
44995
45238
  lifecycleStatus: connection.lifecycleStatus,
44996
45239
  operationalStatus: connection.operationalStatus,
@@ -45091,6 +45334,7 @@ async function getNangoConnections(identity, options = {}) {
45091
45334
  providerConfigKey,
45092
45335
  provider,
45093
45336
  label,
45337
+ userLabel: null,
45094
45338
  providerAccountId,
45095
45339
  providerAccountEmail,
45096
45340
  providerAccountName,
@@ -46932,6 +47176,26 @@ app.get("/schedule-connections", auth4, async (c) => {
46932
47176
  return scheduleConnectionError(c, err, "Unable to load service connections.");
46933
47177
  }
46934
47178
  });
47179
+ app.patch("/schedule-connections/:id/label", auth4, async (c) => {
47180
+ const user = c.get("user");
47181
+ const body = await c.req.json().catch(() => ({}));
47182
+ if (body.label !== null && typeof body.label !== "string") {
47183
+ return c.json({ ok: false, error: "label must be a string or null." }, 400);
47184
+ }
47185
+ const label = typeof body.label === "string" ? body.label.normalize("NFKC").trim().replace(/\s+/g, " ") : null;
47186
+ if (label && label.length > 80) {
47187
+ return c.json({ ok: false, error: "label must contain at most 80 characters." }, 400);
47188
+ }
47189
+ try {
47190
+ const userLabel = await setServiceConnectionUserLabel(user.email, c.req.param("id"), label || null);
47191
+ return c.json({ ok: true, userLabel });
47192
+ } catch (err) {
47193
+ if (err instanceof Error && err.message === "service_connection_not_found") {
47194
+ return c.json({ ok: false, error: "Service connection not found." }, 404);
47195
+ }
47196
+ return scheduleConnectionError(c, err, "Unable to rename this service connection.");
47197
+ }
47198
+ });
46935
47199
  app.post("/schedule-connections/billing/reconcile", auth4, requireIntegrationsTier, async (c) => {
46936
47200
  const user = c.get("user");
46937
47201
  try {
@@ -47518,6 +47782,7 @@ app.post("/integrations/billing/reconcile", (c) => forwardIntegrationAlias(c, "/
47518
47782
  app.post("/integrations/:id/reconnect-session", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}/reconnect-session`));
47519
47783
  app.post("/integrations/:id/enable-actions", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}/enable-actions`));
47520
47784
  app.post("/integrations/:id/test", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}/test`));
47785
+ app.patch("/integrations/:id/label", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}/label`));
47521
47786
  app.delete("/integrations/:id", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}`));
47522
47787
  app.post("/integrations/actions/read", (c) => forwardIntegrationAlias(c, "/schedule-connections/actions/read"));
47523
47788
  app.post("/integrations/actions/describe", (c) => forwardIntegrationAlias(c, "/schedule-connections/actions/describe"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-scraper",
3
- "version": "0.66.3",
3
+ "version": "0.66.5",
4
4
  "description": "MCP server for MCP Scraper web intelligence tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",