@integrity-labs/agt-cli 0.28.1112 → 0.28.1113

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.
@@ -16,7 +16,7 @@ import {
16
16
  parseEnvFileEntries,
17
17
  parseEnvIntegrations,
18
18
  shellQuote
19
- } from "./chunk-5EYINOIW.js";
19
+ } from "./chunk-JX7ZXCNZ.js";
20
20
  import {
21
21
  BIND_FAILURE_QUARANTINE_THRESHOLD,
22
22
  INTEGRATIONS_SECTION_END,
@@ -79,7 +79,7 @@ import {
79
79
  sessionTranscriptDir,
80
80
  worseConnectivityOutcome,
81
81
  wrapScheduledTaskPrompt
82
- } from "./chunk-NO57I56V.js";
82
+ } from "./chunk-OBZB6MUE.js";
83
83
  import {
84
84
  classifyUnanswerablePane,
85
85
  isConsumerTermsDialogVisible,
@@ -7518,7 +7518,7 @@ function exchangeFailureKind(err) {
7518
7518
  }
7519
7519
 
7520
7520
  // src/lib/api-client.ts
7521
- var agtCliVersion = true ? "0.28.1112" : "dev";
7521
+ var agtCliVersion = true ? "0.28.1113" : "dev";
7522
7522
  var lastConfigHash = null;
7523
7523
  function setConfigHash(hash) {
7524
7524
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -12492,4 +12492,4 @@ export {
12492
12492
  managerInstallSystemUnitCommand,
12493
12493
  managerUninstallSystemUnitCommand
12494
12494
  };
12495
- //# sourceMappingURL=chunk-TJUDOE53.js.map
12495
+ //# sourceMappingURL=chunk-TXNVB3DN.js.map
@@ -266,7 +266,7 @@ async function spawnPairSession(session) {
266
266
  return { ok: true };
267
267
  } catch {
268
268
  }
269
- const { resolveClaudeBinary } = await import("./persistent-session-SDYY75X7.js");
269
+ const { resolveClaudeBinary } = await import("./persistent-session-6KIQF4O7.js");
270
270
  const claudeBin = resolveClaudeBinary();
271
271
  const pairEnv = {
272
272
  ...process.env,
@@ -734,4 +734,4 @@ export {
734
734
  startClaudePair,
735
735
  submitClaudePairCode
736
736
  };
737
- //# sourceMappingURL=claude-pair-runtime-CKWLQMAC.js.map
737
+ //# sourceMappingURL=claude-pair-runtime-GL67TYXC.js.map
@@ -68,7 +68,7 @@ import {
68
68
  safeWriteJsonAtomic,
69
69
  setConfigHash,
70
70
  tripClass
71
- } from "../chunk-TJUDOE53.js";
71
+ } from "../chunk-TXNVB3DN.js";
72
72
  import {
73
73
  getProjectDir as getProjectDir2,
74
74
  getReadyTasks,
@@ -155,7 +155,7 @@ import {
155
155
  webFetchBaselineValue,
156
156
  webFetchRestartNeeded,
157
157
  writeEgressAllowlist
158
- } from "../chunk-5EYINOIW.js";
158
+ } from "../chunk-JX7ZXCNZ.js";
159
159
  import {
160
160
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
161
161
  AnchorSessionClient,
@@ -265,7 +265,7 @@ import {
265
265
  subagentActivityAgeSeconds,
266
266
  sumTranscriptUsageInWindow,
267
267
  transcriptActivityAgeSeconds
268
- } from "../chunk-NO57I56V.js";
268
+ } from "../chunk-OBZB6MUE.js";
269
269
  import {
270
270
  classifyUnanswerablePane,
271
271
  paneRemedy
@@ -14143,7 +14143,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
14143
14143
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
14144
14144
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
14145
14145
  async function channelInboundActivityAgeSecondsFor(codeName) {
14146
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-NEBHDSMI.js");
14146
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-GGJNITME.js");
14147
14147
  const newest = newestPendingInboundActivityMtimeMs(dirname11(paneLogPath(codeName)));
14148
14148
  if (newest === null) return null;
14149
14149
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -14901,7 +14901,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
14901
14901
  var lastVersionCheckAt = 0;
14902
14902
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
14903
14903
  var lastResponsivenessProbeAt = 0;
14904
- var agtCliVersion = true ? "0.28.1112" : "dev";
14904
+ var agtCliVersion = true ? "0.28.1113" : "dev";
14905
14905
  function resolveBrewPath(execFileSync3) {
14906
14906
  try {
14907
14907
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -16518,7 +16518,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
16518
16518
  if (codeNames.length === 0) return;
16519
16519
  void (async () => {
16520
16520
  try {
16521
- const { collectDiagnostics } = await import("../persistent-session-SDYY75X7.js");
16521
+ const { collectDiagnostics } = await import("../persistent-session-6KIQF4O7.js");
16522
16522
  await api.post("/host/heartbeat", {
16523
16523
  host_id: hostId,
16524
16524
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -16660,7 +16660,7 @@ async function pollCycleInner() {
16660
16660
  }
16661
16661
  try {
16662
16662
  const { detectHostSecurity } = await import("../host-security-KQYYZZVW.js");
16663
- const { collectDiagnostics } = await import("../persistent-session-SDYY75X7.js");
16663
+ const { collectDiagnostics } = await import("../persistent-session-6KIQF4O7.js");
16664
16664
  const diagCodeNames = [...agentState.persistentSessionAgents];
16665
16665
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor) : void 0;
16666
16666
  let tailscaleHostname;
@@ -16868,7 +16868,7 @@ async function pollCycleInner() {
16868
16868
  collectPanelessActivityProbes,
16869
16869
  getResponsivenessIntervalMs,
16870
16870
  occupancyQualificationClassifications
16871
- } = await import("../responsiveness-probe-NEBHDSMI.js");
16871
+ } = await import("../responsiveness-probe-GGJNITME.js");
16872
16872
  const probeIntervalMs = getResponsivenessIntervalMs();
16873
16873
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
16874
16874
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -16985,7 +16985,7 @@ async function pollCycleInner() {
16985
16985
  collectResponsivenessProbes,
16986
16986
  livePendingInboundOldestAgeSeconds,
16987
16987
  parkPendingInbound
16988
- } = await import("../responsiveness-probe-NEBHDSMI.js");
16988
+ } = await import("../responsiveness-probe-GGJNITME.js");
16989
16989
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-M6AFZWNU.js");
16990
16990
  const wedgeNow = /* @__PURE__ */ new Date();
16991
16991
  const liveAgents = agentState.persistentSessionAgents;
@@ -17271,7 +17271,7 @@ async function pollCycleInner() {
17271
17271
  }
17272
17272
  }
17273
17273
  );
17274
- const { parkPendingInbound } = await import("../responsiveness-probe-NEBHDSMI.js");
17274
+ const { parkPendingInbound } = await import("../responsiveness-probe-GGJNITME.js");
17275
17275
  const { parked, deadLettered } = parkPendingInbound(codeName, new Date(fatalNowMs));
17276
17276
  paneFatalStreak.delete(codeName);
17277
17277
  paneFatalRestarts.set(codeName, [...history, fatalNowMs]);
@@ -17302,7 +17302,7 @@ async function pollCycleInner() {
17302
17302
  TRANSCRIPT_MOUNT_ESCALATION_REFRESH_MS,
17303
17303
  bindSourceEscalations
17304
17304
  } = await import("../transcript-mount-health-OHE6IVJV.js");
17305
- const { transcriptBindSourcesFor, ensureTranscriptBindSources } = await import("../persistent-session-SDYY75X7.js");
17305
+ const { transcriptBindSourcesFor, ensureTranscriptBindSources } = await import("../persistent-session-6KIQF4O7.js");
17306
17306
  const mountConfig = resolveTranscriptMountConfig();
17307
17307
  const liveAgents = agentState.persistentSessionAgents;
17308
17308
  for (const tracked of [
@@ -17467,7 +17467,7 @@ async function pollCycleInner() {
17467
17467
  }
17468
17468
  try {
17469
17469
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-VR5QGP3J.js");
17470
- const { probeSessionAuth } = await import("../session-auth-dead-CNV4O7RF.js");
17470
+ const { probeSessionAuth } = await import("../session-auth-dead-LMCPZJ4K.js");
17471
17471
  const observations = [];
17472
17472
  const pendingCacheCommits = [];
17473
17473
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -22054,7 +22054,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
22054
22054
  try {
22055
22055
  const freshId = rotateSessionForWedge(codeName);
22056
22056
  consecutiveWedgeCycles.delete(codeName);
22057
- const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-NEBHDSMI.js");
22057
+ const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-GGJNITME.js");
22058
22058
  const { parked, deadLettered } = parkForRestart(codeName, /* @__PURE__ */ new Date());
22059
22059
  const parkNote = parked > 0 ? `, ${parked} inbound parked` : "";
22060
22060
  const deadNote = deadLettered > 0 ? `, ${deadLettered} undeliverable dead-lettered` : "";
@@ -22097,7 +22097,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
22097
22097
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
22098
22098
  void (async () => {
22099
22099
  try {
22100
- const { collectDiagnostics } = await import("../persistent-session-SDYY75X7.js");
22100
+ const { collectDiagnostics } = await import("../persistent-session-6KIQF4O7.js");
22101
22101
  await api.post("/host/heartbeat", {
22102
22102
  host_id: hostId,
22103
22103
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -22148,7 +22148,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
22148
22148
  }
22149
22149
  try {
22150
22150
  const hostId = await getHostId();
22151
- const { collectDiagnostics } = await import("../persistent-session-SDYY75X7.js");
22151
+ const { collectDiagnostics } = await import("../persistent-session-6KIQF4O7.js");
22152
22152
  await api.post("/host/heartbeat", {
22153
22153
  host_id: hostId,
22154
22154
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -22821,7 +22821,7 @@ async function processClaudePairSessions(agents) {
22821
22821
  // cannot take it for itself — by the time it runs, the write it is looking
22822
22822
  // for may already have happened.
22823
22823
  snapshotPairConfig
22824
- } = await import("../claude-pair-runtime-CKWLQMAC.js");
22824
+ } = await import("../claude-pair-runtime-GL67TYXC.js");
22825
22825
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
22826
22826
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
22827
22827
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -33059,6 +33059,51 @@ var OAUTH_PROVIDERS = {
33059
33059
  }
33060
33060
  ]
33061
33061
  },
33062
+ "otter": {
33063
+ // CS-2029: Otter.ai's official MCP — remote streamable-HTTP at
33064
+ // https://mcp.otter.ai/mcp. Same Granola/Kajabi shape: OAuth 2.0
33065
+ // authorization-code with PKCE (S256) and a public client
33066
+ // (token_endpoint_auth_method 'none') issued via Dynamic Client
33067
+ // Registration (RFC 7591). Values below are taken verbatim from the
33068
+ // metadata, not inferred:
33069
+ // - https://mcp.otter.ai/.well-known/oauth-protected-resource names the
33070
+ // AS as https://otter.ai/ and advertises scopes profile:read,
33071
+ // conversations:read;
33072
+ // - https://otter.ai/.well-known/oauth-authorization-server gives
33073
+ // authorize /oauth2/authorize, token /oauth/token, revoke
33074
+ // /oauth/revoke_token, register /oauth/register, PKCE S256, `none`,
33075
+ // and the refresh_token grant.
33076
+ // NOTE the authorize path is /oauth2/authorize while token/revoke live
33077
+ // under /oauth/ — that is Otter's own split, do not "normalise" it.
33078
+ // The prod client was registered 2026-10-10 against /oauth/register with
33079
+ // scope 'profile:read conversations:read' and BOTH API callbacks,
33080
+ // https://api.augmented.team/... and https://api.ninjafy.com/integrations/oauth/callback,
33081
+ // because redirect_uri follows API_PUBLIC_URL and the ENG-11060 flip makes
33082
+ // the ninjafy name canonical; Otter returns no RFC 7592 handle, so a redirect
33083
+ // cannot be added later without a new client and a re-consent per connection.
33084
+ // OAUTH_OTTER_CLIENT_ID is set from its output (docs/runbooks/otter-dogfood.md). Otter advertises ONLY
33085
+ // read scopes, so this integration is read-only by construction: there is
33086
+ // no write scope to request even by mistake.
33087
+ definitionId: "otter",
33088
+ authorizeUrl: "https://otter.ai/oauth2/authorize",
33089
+ tokenUrl: "https://otter.ai/oauth/token",
33090
+ revokeUrl: "https://otter.ai/oauth/revoke_token",
33091
+ defaultScopes: ["profile:read", "conversations:read"],
33092
+ supportsRefresh: true,
33093
+ extraAuthorizeParams: {},
33094
+ clientAuthMethod: "body",
33095
+ pkce: "S256",
33096
+ publicClient: true,
33097
+ mcpUrl: "https://mcp.otter.ai/mcp",
33098
+ // Curated surface (matches the catalog seed's defined_scopes[].tools).
33099
+ // These are the server's three tools, all read-only. The names are
33100
+ // Otter's own, quoted from the tools' descriptions ("use `otter_search`
33101
+ // first", "call otter_get_user_info() first"), not Composio's wrapped
33102
+ // OTTER_AI_MCP_* slugs. An allowlisted name the server does not advertise
33103
+ // is logged and SKIPPED by the proxy, so confirm against the first live
33104
+ // tools/list after a real consent (runbook Step 3).
33105
+ toolAllowlist: ["otter_get_user_info", "otter_search", "otter_fetch"]
33106
+ },
33062
33107
  "notion-cli": {
33063
33108
  // Notion's public OAuth app. Tokens are workspace-scoped and long-lived —
33064
33109
  // Notion does not issue refresh_tokens, so `supportsRefresh: false` and
@@ -33692,6 +33737,34 @@ var INTEGRATION_REGISTRY = [
33692
33737
  docs_url: "https://help.kajabi.com/articles/api-integrations/connect-kajabi-to-claude-or-chatgpt",
33693
33738
  beta: true
33694
33739
  },
33740
+ {
33741
+ id: "otter",
33742
+ name: "Otter.ai",
33743
+ category: "knowledge",
33744
+ description: "Read Otter.ai meetings: search recordings by date, attendee or topic, and read summaries, action items and full speaker-attributed transcripts.",
33745
+ // CS-2029: same remote streamable-HTTP MCP + OAuth pattern as Granola/Kajabi.
33746
+ // Otter's server implements the MCP discovery chain (RFC 9728 protected-
33747
+ // resource metadata at mcp.otter.ai → RFC 8414 AS metadata at otter.ai →
33748
+ // RFC 7591 registration); auth is OAuth 2.0 authorization-code with PKCE
33749
+ // (S256) and a public client registered via one-time Dynamic Client
33750
+ // Registration (scripts/dcr-register.ts against https://otter.ai/oauth/register
33751
+ // → OAUTH_OTTER_CLIENT_ID, --scope 'profile:read conversations:read').
33752
+ // Consent is brokered by the webapp through the shared
33753
+ // /integrations/oauth/authorize → /callback path, and the access_token
33754
+ // reaches the host via the generic remote-oauth-proxy
33755
+ // (OAUTH_PROVIDERS.otter.mcpUrl). Read-only by construction: Otter
33756
+ // advertises no write scope.
33757
+ supported_auth_types: ["oauth2"],
33758
+ capabilities: [
33759
+ { id: "otter:read", name: "Read Meetings & Transcripts", description: "Find recorded meetings by date, attendee, topic or title, and read their summaries, action items, outlines and full transcripts (otter_get_user_info, otter_search, otter_fetch)", access: "read" }
33760
+ ],
33761
+ // Otter's own Help Center article for the MCP server (CodeRabbit, #6906). It
33762
+ // exists even though help.otter.ai answers 403 to non-browser clients; the
33763
+ // help-centre API confirmed it (title "Otter MCP Server"). The MCP endpoint
33764
+ // itself is a JSON-RPC URL, not a page (Obi Wan 8305072d5414).
33765
+ docs_url: "https://help.otter.ai/hc/en-us/articles/35287607569687-Otter-MCP-Server",
33766
+ beta: true
33767
+ },
33695
33768
  {
33696
33769
  id: "anchor-browser",
33697
33770
  name: "Anchor Browser",
package/dist/mcp/index.js CHANGED
@@ -25562,6 +25562,51 @@ var OAUTH_PROVIDERS = {
25562
25562
  }
25563
25563
  ]
25564
25564
  },
25565
+ "otter": {
25566
+ // CS-2029: Otter.ai's official MCP — remote streamable-HTTP at
25567
+ // https://mcp.otter.ai/mcp. Same Granola/Kajabi shape: OAuth 2.0
25568
+ // authorization-code with PKCE (S256) and a public client
25569
+ // (token_endpoint_auth_method 'none') issued via Dynamic Client
25570
+ // Registration (RFC 7591). Values below are taken verbatim from the
25571
+ // metadata, not inferred:
25572
+ // - https://mcp.otter.ai/.well-known/oauth-protected-resource names the
25573
+ // AS as https://otter.ai/ and advertises scopes profile:read,
25574
+ // conversations:read;
25575
+ // - https://otter.ai/.well-known/oauth-authorization-server gives
25576
+ // authorize /oauth2/authorize, token /oauth/token, revoke
25577
+ // /oauth/revoke_token, register /oauth/register, PKCE S256, `none`,
25578
+ // and the refresh_token grant.
25579
+ // NOTE the authorize path is /oauth2/authorize while token/revoke live
25580
+ // under /oauth/ — that is Otter's own split, do not "normalise" it.
25581
+ // The prod client was registered 2026-10-10 against /oauth/register with
25582
+ // scope 'profile:read conversations:read' and BOTH API callbacks,
25583
+ // https://api.augmented.team/... and https://api.ninjafy.com/integrations/oauth/callback,
25584
+ // because redirect_uri follows API_PUBLIC_URL and the ENG-11060 flip makes
25585
+ // the ninjafy name canonical; Otter returns no RFC 7592 handle, so a redirect
25586
+ // cannot be added later without a new client and a re-consent per connection.
25587
+ // OAUTH_OTTER_CLIENT_ID is set from its output (docs/runbooks/otter-dogfood.md). Otter advertises ONLY
25588
+ // read scopes, so this integration is read-only by construction: there is
25589
+ // no write scope to request even by mistake.
25590
+ definitionId: "otter",
25591
+ authorizeUrl: "https://otter.ai/oauth2/authorize",
25592
+ tokenUrl: "https://otter.ai/oauth/token",
25593
+ revokeUrl: "https://otter.ai/oauth/revoke_token",
25594
+ defaultScopes: ["profile:read", "conversations:read"],
25595
+ supportsRefresh: true,
25596
+ extraAuthorizeParams: {},
25597
+ clientAuthMethod: "body",
25598
+ pkce: "S256",
25599
+ publicClient: true,
25600
+ mcpUrl: "https://mcp.otter.ai/mcp",
25601
+ // Curated surface (matches the catalog seed's defined_scopes[].tools).
25602
+ // These are the server's three tools, all read-only. The names are
25603
+ // Otter's own, quoted from the tools' descriptions ("use `otter_search`
25604
+ // first", "call otter_get_user_info() first"), not Composio's wrapped
25605
+ // OTTER_AI_MCP_* slugs. An allowlisted name the server does not advertise
25606
+ // is logged and SKIPPED by the proxy, so confirm against the first live
25607
+ // tools/list after a real consent (runbook Step 3).
25608
+ toolAllowlist: ["otter_get_user_info", "otter_search", "otter_fetch"]
25609
+ },
25565
25610
  "notion-cli": {
25566
25611
  // Notion's public OAuth app. Tokens are workspace-scoped and long-lived —
25567
25612
  // Notion does not issue refresh_tokens, so `supportsRefresh: false` and
@@ -26195,6 +26240,34 @@ var INTEGRATION_REGISTRY = [
26195
26240
  docs_url: "https://help.kajabi.com/articles/api-integrations/connect-kajabi-to-claude-or-chatgpt",
26196
26241
  beta: true
26197
26242
  },
26243
+ {
26244
+ id: "otter",
26245
+ name: "Otter.ai",
26246
+ category: "knowledge",
26247
+ description: "Read Otter.ai meetings: search recordings by date, attendee or topic, and read summaries, action items and full speaker-attributed transcripts.",
26248
+ // CS-2029: same remote streamable-HTTP MCP + OAuth pattern as Granola/Kajabi.
26249
+ // Otter's server implements the MCP discovery chain (RFC 9728 protected-
26250
+ // resource metadata at mcp.otter.ai → RFC 8414 AS metadata at otter.ai →
26251
+ // RFC 7591 registration); auth is OAuth 2.0 authorization-code with PKCE
26252
+ // (S256) and a public client registered via one-time Dynamic Client
26253
+ // Registration (scripts/dcr-register.ts against https://otter.ai/oauth/register
26254
+ // → OAUTH_OTTER_CLIENT_ID, --scope 'profile:read conversations:read').
26255
+ // Consent is brokered by the webapp through the shared
26256
+ // /integrations/oauth/authorize → /callback path, and the access_token
26257
+ // reaches the host via the generic remote-oauth-proxy
26258
+ // (OAUTH_PROVIDERS.otter.mcpUrl). Read-only by construction: Otter
26259
+ // advertises no write scope.
26260
+ supported_auth_types: ["oauth2"],
26261
+ capabilities: [
26262
+ { id: "otter:read", name: "Read Meetings & Transcripts", description: "Find recorded meetings by date, attendee, topic or title, and read their summaries, action items, outlines and full transcripts (otter_get_user_info, otter_search, otter_fetch)", access: "read" }
26263
+ ],
26264
+ // Otter's own Help Center article for the MCP server (CodeRabbit, #6906). It
26265
+ // exists even though help.otter.ai answers 403 to non-browser clients; the
26266
+ // help-centre API confirmed it (title "Otter MCP Server"). The MCP endpoint
26267
+ // itself is a JSON-RPC URL, not a page (Obi Wan 8305072d5414).
26268
+ docs_url: "https://help.otter.ai/hc/en-us/articles/35287607569687-Otter-MCP-Server",
26269
+ beta: true
26270
+ },
26198
26271
  {
26199
26272
  id: "anchor-browser",
26200
26273
  name: "Anchor Browser",
@@ -39711,6 +39711,51 @@ var OAUTH_PROVIDERS = {
39711
39711
  }
39712
39712
  ]
39713
39713
  },
39714
+ "otter": {
39715
+ // CS-2029: Otter.ai's official MCP — remote streamable-HTTP at
39716
+ // https://mcp.otter.ai/mcp. Same Granola/Kajabi shape: OAuth 2.0
39717
+ // authorization-code with PKCE (S256) and a public client
39718
+ // (token_endpoint_auth_method 'none') issued via Dynamic Client
39719
+ // Registration (RFC 7591). Values below are taken verbatim from the
39720
+ // metadata, not inferred:
39721
+ // - https://mcp.otter.ai/.well-known/oauth-protected-resource names the
39722
+ // AS as https://otter.ai/ and advertises scopes profile:read,
39723
+ // conversations:read;
39724
+ // - https://otter.ai/.well-known/oauth-authorization-server gives
39725
+ // authorize /oauth2/authorize, token /oauth/token, revoke
39726
+ // /oauth/revoke_token, register /oauth/register, PKCE S256, `none`,
39727
+ // and the refresh_token grant.
39728
+ // NOTE the authorize path is /oauth2/authorize while token/revoke live
39729
+ // under /oauth/ — that is Otter's own split, do not "normalise" it.
39730
+ // The prod client was registered 2026-10-10 against /oauth/register with
39731
+ // scope 'profile:read conversations:read' and BOTH API callbacks,
39732
+ // https://api.augmented.team/... and https://api.ninjafy.com/integrations/oauth/callback,
39733
+ // because redirect_uri follows API_PUBLIC_URL and the ENG-11060 flip makes
39734
+ // the ninjafy name canonical; Otter returns no RFC 7592 handle, so a redirect
39735
+ // cannot be added later without a new client and a re-consent per connection.
39736
+ // OAUTH_OTTER_CLIENT_ID is set from its output (docs/runbooks/otter-dogfood.md). Otter advertises ONLY
39737
+ // read scopes, so this integration is read-only by construction: there is
39738
+ // no write scope to request even by mistake.
39739
+ definitionId: "otter",
39740
+ authorizeUrl: "https://otter.ai/oauth2/authorize",
39741
+ tokenUrl: "https://otter.ai/oauth/token",
39742
+ revokeUrl: "https://otter.ai/oauth/revoke_token",
39743
+ defaultScopes: ["profile:read", "conversations:read"],
39744
+ supportsRefresh: true,
39745
+ extraAuthorizeParams: {},
39746
+ clientAuthMethod: "body",
39747
+ pkce: "S256",
39748
+ publicClient: true,
39749
+ mcpUrl: "https://mcp.otter.ai/mcp",
39750
+ // Curated surface (matches the catalog seed's defined_scopes[].tools).
39751
+ // These are the server's three tools, all read-only. The names are
39752
+ // Otter's own, quoted from the tools' descriptions ("use `otter_search`
39753
+ // first", "call otter_get_user_info() first"), not Composio's wrapped
39754
+ // OTTER_AI_MCP_* slugs. An allowlisted name the server does not advertise
39755
+ // is logged and SKIPPED by the proxy, so confirm against the first live
39756
+ // tools/list after a real consent (runbook Step 3).
39757
+ toolAllowlist: ["otter_get_user_info", "otter_search", "otter_fetch"]
39758
+ },
39714
39759
  "notion-cli": {
39715
39760
  // Notion's public OAuth app. Tokens are workspace-scoped and long-lived —
39716
39761
  // Notion does not issue refresh_tokens, so `supportsRefresh: false` and
@@ -40344,6 +40389,34 @@ var INTEGRATION_REGISTRY = [
40344
40389
  docs_url: "https://help.kajabi.com/articles/api-integrations/connect-kajabi-to-claude-or-chatgpt",
40345
40390
  beta: true
40346
40391
  },
40392
+ {
40393
+ id: "otter",
40394
+ name: "Otter.ai",
40395
+ category: "knowledge",
40396
+ description: "Read Otter.ai meetings: search recordings by date, attendee or topic, and read summaries, action items and full speaker-attributed transcripts.",
40397
+ // CS-2029: same remote streamable-HTTP MCP + OAuth pattern as Granola/Kajabi.
40398
+ // Otter's server implements the MCP discovery chain (RFC 9728 protected-
40399
+ // resource metadata at mcp.otter.ai → RFC 8414 AS metadata at otter.ai →
40400
+ // RFC 7591 registration); auth is OAuth 2.0 authorization-code with PKCE
40401
+ // (S256) and a public client registered via one-time Dynamic Client
40402
+ // Registration (scripts/dcr-register.ts against https://otter.ai/oauth/register
40403
+ // → OAUTH_OTTER_CLIENT_ID, --scope 'profile:read conversations:read').
40404
+ // Consent is brokered by the webapp through the shared
40405
+ // /integrations/oauth/authorize → /callback path, and the access_token
40406
+ // reaches the host via the generic remote-oauth-proxy
40407
+ // (OAUTH_PROVIDERS.otter.mcpUrl). Read-only by construction: Otter
40408
+ // advertises no write scope.
40409
+ supported_auth_types: ["oauth2"],
40410
+ capabilities: [
40411
+ { id: "otter:read", name: "Read Meetings & Transcripts", description: "Find recorded meetings by date, attendee, topic or title, and read their summaries, action items, outlines and full transcripts (otter_get_user_info, otter_search, otter_fetch)", access: "read" }
40412
+ ],
40413
+ // Otter's own Help Center article for the MCP server (CodeRabbit, #6906). It
40414
+ // exists even though help.otter.ai answers 403 to non-browser clients; the
40415
+ // help-centre API confirmed it (title "Otter MCP Server"). The MCP endpoint
40416
+ // itself is a JSON-RPC URL, not a page (Obi Wan 8305072d5414).
40417
+ docs_url: "https://help.otter.ai/hc/en-us/articles/35287607569687-Otter-MCP-Server",
40418
+ beta: true
40419
+ },
40347
40420
  {
40348
40421
  id: "anchor-browser",
40349
40422
  name: "Anchor Browser",
@@ -34068,6 +34068,51 @@ var OAUTH_PROVIDERS = {
34068
34068
  }
34069
34069
  ]
34070
34070
  },
34071
+ "otter": {
34072
+ // CS-2029: Otter.ai's official MCP — remote streamable-HTTP at
34073
+ // https://mcp.otter.ai/mcp. Same Granola/Kajabi shape: OAuth 2.0
34074
+ // authorization-code with PKCE (S256) and a public client
34075
+ // (token_endpoint_auth_method 'none') issued via Dynamic Client
34076
+ // Registration (RFC 7591). Values below are taken verbatim from the
34077
+ // metadata, not inferred:
34078
+ // - https://mcp.otter.ai/.well-known/oauth-protected-resource names the
34079
+ // AS as https://otter.ai/ and advertises scopes profile:read,
34080
+ // conversations:read;
34081
+ // - https://otter.ai/.well-known/oauth-authorization-server gives
34082
+ // authorize /oauth2/authorize, token /oauth/token, revoke
34083
+ // /oauth/revoke_token, register /oauth/register, PKCE S256, `none`,
34084
+ // and the refresh_token grant.
34085
+ // NOTE the authorize path is /oauth2/authorize while token/revoke live
34086
+ // under /oauth/ — that is Otter's own split, do not "normalise" it.
34087
+ // The prod client was registered 2026-10-10 against /oauth/register with
34088
+ // scope 'profile:read conversations:read' and BOTH API callbacks,
34089
+ // https://api.augmented.team/... and https://api.ninjafy.com/integrations/oauth/callback,
34090
+ // because redirect_uri follows API_PUBLIC_URL and the ENG-11060 flip makes
34091
+ // the ninjafy name canonical; Otter returns no RFC 7592 handle, so a redirect
34092
+ // cannot be added later without a new client and a re-consent per connection.
34093
+ // OAUTH_OTTER_CLIENT_ID is set from its output (docs/runbooks/otter-dogfood.md). Otter advertises ONLY
34094
+ // read scopes, so this integration is read-only by construction: there is
34095
+ // no write scope to request even by mistake.
34096
+ definitionId: "otter",
34097
+ authorizeUrl: "https://otter.ai/oauth2/authorize",
34098
+ tokenUrl: "https://otter.ai/oauth/token",
34099
+ revokeUrl: "https://otter.ai/oauth/revoke_token",
34100
+ defaultScopes: ["profile:read", "conversations:read"],
34101
+ supportsRefresh: true,
34102
+ extraAuthorizeParams: {},
34103
+ clientAuthMethod: "body",
34104
+ pkce: "S256",
34105
+ publicClient: true,
34106
+ mcpUrl: "https://mcp.otter.ai/mcp",
34107
+ // Curated surface (matches the catalog seed's defined_scopes[].tools).
34108
+ // These are the server's three tools, all read-only. The names are
34109
+ // Otter's own, quoted from the tools' descriptions ("use `otter_search`
34110
+ // first", "call otter_get_user_info() first"), not Composio's wrapped
34111
+ // OTTER_AI_MCP_* slugs. An allowlisted name the server does not advertise
34112
+ // is logged and SKIPPED by the proxy, so confirm against the first live
34113
+ // tools/list after a real consent (runbook Step 3).
34114
+ toolAllowlist: ["otter_get_user_info", "otter_search", "otter_fetch"]
34115
+ },
34071
34116
  "notion-cli": {
34072
34117
  // Notion's public OAuth app. Tokens are workspace-scoped and long-lived —
34073
34118
  // Notion does not issue refresh_tokens, so `supportsRefresh: false` and
@@ -34707,6 +34752,34 @@ var INTEGRATION_REGISTRY = [
34707
34752
  docs_url: "https://help.kajabi.com/articles/api-integrations/connect-kajabi-to-claude-or-chatgpt",
34708
34753
  beta: true
34709
34754
  },
34755
+ {
34756
+ id: "otter",
34757
+ name: "Otter.ai",
34758
+ category: "knowledge",
34759
+ description: "Read Otter.ai meetings: search recordings by date, attendee or topic, and read summaries, action items and full speaker-attributed transcripts.",
34760
+ // CS-2029: same remote streamable-HTTP MCP + OAuth pattern as Granola/Kajabi.
34761
+ // Otter's server implements the MCP discovery chain (RFC 9728 protected-
34762
+ // resource metadata at mcp.otter.ai → RFC 8414 AS metadata at otter.ai →
34763
+ // RFC 7591 registration); auth is OAuth 2.0 authorization-code with PKCE
34764
+ // (S256) and a public client registered via one-time Dynamic Client
34765
+ // Registration (scripts/dcr-register.ts against https://otter.ai/oauth/register
34766
+ // → OAUTH_OTTER_CLIENT_ID, --scope 'profile:read conversations:read').
34767
+ // Consent is brokered by the webapp through the shared
34768
+ // /integrations/oauth/authorize → /callback path, and the access_token
34769
+ // reaches the host via the generic remote-oauth-proxy
34770
+ // (OAUTH_PROVIDERS.otter.mcpUrl). Read-only by construction: Otter
34771
+ // advertises no write scope.
34772
+ supported_auth_types: ["oauth2"],
34773
+ capabilities: [
34774
+ { id: "otter:read", name: "Read Meetings & Transcripts", description: "Find recorded meetings by date, attendee, topic or title, and read their summaries, action items, outlines and full transcripts (otter_get_user_info, otter_search, otter_fetch)", access: "read" }
34775
+ ],
34776
+ // Otter's own Help Center article for the MCP server (CodeRabbit, #6906). It
34777
+ // exists even though help.otter.ai answers 403 to non-browser clients; the
34778
+ // help-centre API confirmed it (title "Otter MCP Server"). The MCP endpoint
34779
+ // itself is a JSON-RPC URL, not a page (Obi Wan 8305072d5414).
34780
+ docs_url: "https://help.otter.ai/hc/en-us/articles/35287607569687-Otter-MCP-Server",
34781
+ beta: true
34782
+ },
34710
34783
  {
34711
34784
  id: "anchor-browser",
34712
34785
  name: "Anchor Browser",
@@ -34260,6 +34260,51 @@ var OAUTH_PROVIDERS = {
34260
34260
  }
34261
34261
  ]
34262
34262
  },
34263
+ "otter": {
34264
+ // CS-2029: Otter.ai's official MCP — remote streamable-HTTP at
34265
+ // https://mcp.otter.ai/mcp. Same Granola/Kajabi shape: OAuth 2.0
34266
+ // authorization-code with PKCE (S256) and a public client
34267
+ // (token_endpoint_auth_method 'none') issued via Dynamic Client
34268
+ // Registration (RFC 7591). Values below are taken verbatim from the
34269
+ // metadata, not inferred:
34270
+ // - https://mcp.otter.ai/.well-known/oauth-protected-resource names the
34271
+ // AS as https://otter.ai/ and advertises scopes profile:read,
34272
+ // conversations:read;
34273
+ // - https://otter.ai/.well-known/oauth-authorization-server gives
34274
+ // authorize /oauth2/authorize, token /oauth/token, revoke
34275
+ // /oauth/revoke_token, register /oauth/register, PKCE S256, `none`,
34276
+ // and the refresh_token grant.
34277
+ // NOTE the authorize path is /oauth2/authorize while token/revoke live
34278
+ // under /oauth/ — that is Otter's own split, do not "normalise" it.
34279
+ // The prod client was registered 2026-10-10 against /oauth/register with
34280
+ // scope 'profile:read conversations:read' and BOTH API callbacks,
34281
+ // https://api.augmented.team/... and https://api.ninjafy.com/integrations/oauth/callback,
34282
+ // because redirect_uri follows API_PUBLIC_URL and the ENG-11060 flip makes
34283
+ // the ninjafy name canonical; Otter returns no RFC 7592 handle, so a redirect
34284
+ // cannot be added later without a new client and a re-consent per connection.
34285
+ // OAUTH_OTTER_CLIENT_ID is set from its output (docs/runbooks/otter-dogfood.md). Otter advertises ONLY
34286
+ // read scopes, so this integration is read-only by construction: there is
34287
+ // no write scope to request even by mistake.
34288
+ definitionId: "otter",
34289
+ authorizeUrl: "https://otter.ai/oauth2/authorize",
34290
+ tokenUrl: "https://otter.ai/oauth/token",
34291
+ revokeUrl: "https://otter.ai/oauth/revoke_token",
34292
+ defaultScopes: ["profile:read", "conversations:read"],
34293
+ supportsRefresh: true,
34294
+ extraAuthorizeParams: {},
34295
+ clientAuthMethod: "body",
34296
+ pkce: "S256",
34297
+ publicClient: true,
34298
+ mcpUrl: "https://mcp.otter.ai/mcp",
34299
+ // Curated surface (matches the catalog seed's defined_scopes[].tools).
34300
+ // These are the server's three tools, all read-only. The names are
34301
+ // Otter's own, quoted from the tools' descriptions ("use `otter_search`
34302
+ // first", "call otter_get_user_info() first"), not Composio's wrapped
34303
+ // OTTER_AI_MCP_* slugs. An allowlisted name the server does not advertise
34304
+ // is logged and SKIPPED by the proxy, so confirm against the first live
34305
+ // tools/list after a real consent (runbook Step 3).
34306
+ toolAllowlist: ["otter_get_user_info", "otter_search", "otter_fetch"]
34307
+ },
34263
34308
  "notion-cli": {
34264
34309
  // Notion's public OAuth app. Tokens are workspace-scoped and long-lived —
34265
34310
  // Notion does not issue refresh_tokens, so `supportsRefresh: false` and
@@ -34893,6 +34938,34 @@ var INTEGRATION_REGISTRY = [
34893
34938
  docs_url: "https://help.kajabi.com/articles/api-integrations/connect-kajabi-to-claude-or-chatgpt",
34894
34939
  beta: true
34895
34940
  },
34941
+ {
34942
+ id: "otter",
34943
+ name: "Otter.ai",
34944
+ category: "knowledge",
34945
+ description: "Read Otter.ai meetings: search recordings by date, attendee or topic, and read summaries, action items and full speaker-attributed transcripts.",
34946
+ // CS-2029: same remote streamable-HTTP MCP + OAuth pattern as Granola/Kajabi.
34947
+ // Otter's server implements the MCP discovery chain (RFC 9728 protected-
34948
+ // resource metadata at mcp.otter.ai → RFC 8414 AS metadata at otter.ai →
34949
+ // RFC 7591 registration); auth is OAuth 2.0 authorization-code with PKCE
34950
+ // (S256) and a public client registered via one-time Dynamic Client
34951
+ // Registration (scripts/dcr-register.ts against https://otter.ai/oauth/register
34952
+ // → OAUTH_OTTER_CLIENT_ID, --scope 'profile:read conversations:read').
34953
+ // Consent is brokered by the webapp through the shared
34954
+ // /integrations/oauth/authorize → /callback path, and the access_token
34955
+ // reaches the host via the generic remote-oauth-proxy
34956
+ // (OAUTH_PROVIDERS.otter.mcpUrl). Read-only by construction: Otter
34957
+ // advertises no write scope.
34958
+ supported_auth_types: ["oauth2"],
34959
+ capabilities: [
34960
+ { id: "otter:read", name: "Read Meetings & Transcripts", description: "Find recorded meetings by date, attendee, topic or title, and read their summaries, action items, outlines and full transcripts (otter_get_user_info, otter_search, otter_fetch)", access: "read" }
34961
+ ],
34962
+ // Otter's own Help Center article for the MCP server (CodeRabbit, #6906). It
34963
+ // exists even though help.otter.ai answers 403 to non-browser clients; the
34964
+ // help-centre API confirmed it (title "Otter MCP Server"). The MCP endpoint
34965
+ // itself is a JSON-RPC URL, not a page (Obi Wan 8305072d5414).
34966
+ docs_url: "https://help.otter.ai/hc/en-us/articles/35287607569687-Otter-MCP-Server",
34967
+ beta: true
34968
+ },
34896
34969
  {
34897
34970
  id: "anchor-browser",
34898
34971
  name: "Anchor Browser",
@@ -68,8 +68,8 @@ import {
68
68
  writeDirectChatSessionState,
69
69
  writeEgressAllowlist,
70
70
  writePersistentClaudeWrapper
71
- } from "./chunk-5EYINOIW.js";
72
- import "./chunk-NO57I56V.js";
71
+ } from "./chunk-JX7ZXCNZ.js";
72
+ import "./chunk-OBZB6MUE.js";
73
73
  import "./chunk-OCMJ5E6J.js";
74
74
  import "./chunk-XWVM4KPK.js";
75
75
  import "./chunk-EL5MWGGH.js";
@@ -145,4 +145,4 @@ export {
145
145
  writeEgressAllowlist,
146
146
  writePersistentClaudeWrapper
147
147
  };
148
- //# sourceMappingURL=persistent-session-SDYY75X7.js.map
148
+ //# sourceMappingURL=persistent-session-6KIQF4O7.js.map