@integrity-labs/agt-cli 0.28.1112 → 0.28.1114

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 (26) hide show
  1. package/dist/bin/agt.js +5 -5
  2. package/dist/{chunk-5EYINOIW.js → chunk-2XQPQOJX.js} +2 -2
  3. package/dist/chunk-2XQPQOJX.js.map +1 -0
  4. package/dist/{chunk-TJUDOE53.js → chunk-J3L66T65.js} +194 -22
  5. package/dist/{chunk-TJUDOE53.js.map → chunk-J3L66T65.js.map} +1 -1
  6. package/dist/{chunk-NO57I56V.js → chunk-OBZB6MUE.js} +74 -1
  7. package/dist/chunk-OBZB6MUE.js.map +1 -0
  8. package/dist/{claude-pair-runtime-CKWLQMAC.js → claude-pair-runtime-2LVLURUE.js} +2 -2
  9. package/dist/lib/manager-worker.js +17 -16
  10. package/dist/lib/manager-worker.js.map +1 -1
  11. package/dist/mcp/direct-chat-channel.js +107 -4
  12. package/dist/mcp/index.js +106 -3
  13. package/dist/mcp/origami.js +73 -0
  14. package/dist/mcp/slack-channel.js +110 -7
  15. package/dist/mcp/teams-channel.js +34 -4
  16. package/dist/mcp/telegram-channel.js +108 -5
  17. package/dist/{persistent-session-SDYY75X7.js → persistent-session-TDPGTHTG.js} +3 -3
  18. package/dist/{responsiveness-probe-NEBHDSMI.js → responsiveness-probe-6P7TB4QA.js} +3 -3
  19. package/dist/{session-auth-dead-CNV4O7RF.js → session-auth-dead-LMCPZJ4K.js} +2 -2
  20. package/package.json +1 -1
  21. package/dist/chunk-5EYINOIW.js.map +0 -1
  22. package/dist/chunk-NO57I56V.js.map +0 -1
  23. /package/dist/{claude-pair-runtime-CKWLQMAC.js.map → claude-pair-runtime-2LVLURUE.js.map} +0 -0
  24. /package/dist/{persistent-session-SDYY75X7.js.map → persistent-session-TDPGTHTG.js.map} +0 -0
  25. /package/dist/{responsiveness-probe-NEBHDSMI.js.map → responsiveness-probe-6P7TB4QA.js.map} +0 -0
  26. /package/dist/{session-auth-dead-CNV4O7RF.js.map → session-auth-dead-LMCPZJ4K.js.map} +0 -0
@@ -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",
@@ -39777,8 +39850,38 @@ import { join as join10 } from "path";
39777
39850
  import { existsSync as existsSync4, readFileSync as readFileSync8 } from "fs";
39778
39851
  import { homedir as homedir2 } from "os";
39779
39852
  import { join as join9 } from "path";
39780
- function defaultFlagsCachePath() {
39781
- return join9(homedir2(), ".augmented", "flags-cache.json");
39853
+ function hasUsableFlags(path) {
39854
+ try {
39855
+ if (!existsSync4(path)) return false;
39856
+ const parsed = JSON.parse(readFileSync8(path, "utf8"));
39857
+ if (!parsed || typeof parsed !== "object") return false;
39858
+ const flags = parsed.flags;
39859
+ return !!flags && typeof flags === "object" && !Array.isArray(flags);
39860
+ } catch {
39861
+ return false;
39862
+ }
39863
+ }
39864
+ function cacheUpdatedAt(path) {
39865
+ try {
39866
+ if (!existsSync4(path)) return void 0;
39867
+ const parsed = JSON.parse(readFileSync8(path, "utf8"));
39868
+ if (!parsed || typeof parsed !== "object") return void 0;
39869
+ const u = parsed.updated_at;
39870
+ return typeof u === "string" && u.length > 0 ? u : void 0;
39871
+ } catch {
39872
+ return void 0;
39873
+ }
39874
+ }
39875
+ function defaultFlagsCachePath(env2 = process.env) {
39876
+ const hostWide = join9(homedir2(), ".augmented", "flags-cache.json");
39877
+ const agentId = env2["AGT_AGENT_ID"]?.trim();
39878
+ if (!agentId) return hostWide;
39879
+ const perAgent = join9(homedir2(), ".augmented", agentId, "flags-cache.json");
39880
+ if (!hasUsableFlags(perAgent)) return hostWide;
39881
+ const hostWideAt = cacheUpdatedAt(hostWide);
39882
+ const perAgentAt = cacheUpdatedAt(perAgent);
39883
+ if (hostWideAt && (!perAgentAt || perAgentAt < hostWideAt)) return hostWide;
39884
+ return perAgent;
39782
39885
  }
39783
39886
  function envBoolean(raw) {
39784
39887
  if (raw === void 0) return void 0;
@@ -39805,7 +39908,7 @@ function resolveHostBooleanFlag(opts) {
39805
39908
  const env2 = opts.env ?? process.env;
39806
39909
  const envValue = envBoolean(env2[opts.envVar]);
39807
39910
  if (envValue !== void 0) return envValue;
39808
- const cached2 = cachedBoolean(opts.key, opts.cachePath ?? defaultFlagsCachePath());
39911
+ const cached2 = cachedBoolean(opts.key, opts.cachePath ?? defaultFlagsCachePath(env2));
39809
39912
  if (cached2 !== void 0) return cached2;
39810
39913
  return opts.defaultValue;
39811
39914
  }
@@ -39836,7 +39939,7 @@ function cachedMaintenanceMode(path) {
39836
39939
  }
39837
39940
  }
39838
39941
  function resolveMaintenanceMode(opts) {
39839
- const cached2 = cachedMaintenanceMode(opts?.cachePath ?? defaultFlagsCachePath());
39942
+ const cached2 = cachedMaintenanceMode(opts?.cachePath ?? defaultFlagsCachePath(opts?.env));
39840
39943
  if (cached2 !== void 0) return cached2;
39841
39944
  return "off";
39842
39945
  }
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",
@@ -46116,8 +46189,38 @@ var StdioServerTransport = class {
46116
46189
  import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
46117
46190
  import { homedir as homedir2 } from "os";
46118
46191
  import { join as join6 } from "path";
46119
- function defaultFlagsCachePath() {
46120
- return join6(homedir2(), ".augmented", "flags-cache.json");
46192
+ function hasUsableFlags(path) {
46193
+ try {
46194
+ if (!existsSync2(path)) return false;
46195
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
46196
+ if (!parsed || typeof parsed !== "object") return false;
46197
+ const flags = parsed.flags;
46198
+ return !!flags && typeof flags === "object" && !Array.isArray(flags);
46199
+ } catch {
46200
+ return false;
46201
+ }
46202
+ }
46203
+ function cacheUpdatedAt(path) {
46204
+ try {
46205
+ if (!existsSync2(path)) return void 0;
46206
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
46207
+ if (!parsed || typeof parsed !== "object") return void 0;
46208
+ const u = parsed.updated_at;
46209
+ return typeof u === "string" && u.length > 0 ? u : void 0;
46210
+ } catch {
46211
+ return void 0;
46212
+ }
46213
+ }
46214
+ function defaultFlagsCachePath(env2 = process.env) {
46215
+ const hostWide = join6(homedir2(), ".augmented", "flags-cache.json");
46216
+ const agentId = env2["AGT_AGENT_ID"]?.trim();
46217
+ if (!agentId) return hostWide;
46218
+ const perAgent = join6(homedir2(), ".augmented", agentId, "flags-cache.json");
46219
+ if (!hasUsableFlags(perAgent)) return hostWide;
46220
+ const hostWideAt = cacheUpdatedAt(hostWide);
46221
+ const perAgentAt = cacheUpdatedAt(perAgent);
46222
+ if (hostWideAt && (!perAgentAt || perAgentAt < hostWideAt)) return hostWide;
46223
+ return perAgent;
46121
46224
  }
46122
46225
  function envBoolean(raw) {
46123
46226
  if (raw === void 0) return void 0;
@@ -46154,7 +46257,7 @@ function cachedMaintenanceMode(path) {
46154
46257
  }
46155
46258
  }
46156
46259
  function resolveMaintenanceMode(opts) {
46157
- const cached2 = cachedMaintenanceMode(opts?.cachePath ?? defaultFlagsCachePath());
46260
+ const cached2 = cachedMaintenanceMode(opts?.cachePath ?? defaultFlagsCachePath(opts?.env));
46158
46261
  if (cached2 !== void 0) return cached2;
46159
46262
  return "off";
46160
46263
  }
@@ -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",
@@ -30673,8 +30673,38 @@ function isSlackDirectMessage(channelId) {
30673
30673
  import { existsSync, readFileSync } from "fs";
30674
30674
  import { homedir } from "os";
30675
30675
  import { join } from "path";
30676
- function defaultFlagsCachePath() {
30677
- return join(homedir(), ".augmented", "flags-cache.json");
30676
+ function hasUsableFlags(path) {
30677
+ try {
30678
+ if (!existsSync(path)) return false;
30679
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
30680
+ if (!parsed || typeof parsed !== "object") return false;
30681
+ const flags = parsed.flags;
30682
+ return !!flags && typeof flags === "object" && !Array.isArray(flags);
30683
+ } catch {
30684
+ return false;
30685
+ }
30686
+ }
30687
+ function cacheUpdatedAt(path) {
30688
+ try {
30689
+ if (!existsSync(path)) return void 0;
30690
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
30691
+ if (!parsed || typeof parsed !== "object") return void 0;
30692
+ const u = parsed.updated_at;
30693
+ return typeof u === "string" && u.length > 0 ? u : void 0;
30694
+ } catch {
30695
+ return void 0;
30696
+ }
30697
+ }
30698
+ function defaultFlagsCachePath(env2 = process.env) {
30699
+ const hostWide = join(homedir(), ".augmented", "flags-cache.json");
30700
+ const agentId = env2["AGT_AGENT_ID"]?.trim();
30701
+ if (!agentId) return hostWide;
30702
+ const perAgent = join(homedir(), ".augmented", agentId, "flags-cache.json");
30703
+ if (!hasUsableFlags(perAgent)) return hostWide;
30704
+ const hostWideAt = cacheUpdatedAt(hostWide);
30705
+ const perAgentAt = cacheUpdatedAt(perAgent);
30706
+ if (hostWideAt && (!perAgentAt || perAgentAt < hostWideAt)) return hostWide;
30707
+ return perAgent;
30678
30708
  }
30679
30709
  function envBoolean(raw) {
30680
30710
  if (raw === void 0) return void 0;
@@ -30701,7 +30731,7 @@ function resolveHostBooleanFlag(opts) {
30701
30731
  const env2 = opts.env ?? process.env;
30702
30732
  const envValue = envBoolean(env2[opts.envVar]);
30703
30733
  if (envValue !== void 0) return envValue;
30704
- const cached2 = cachedBoolean(opts.key, opts.cachePath ?? defaultFlagsCachePath());
30734
+ const cached2 = cachedBoolean(opts.key, opts.cachePath ?? defaultFlagsCachePath(env2));
30705
30735
  if (cached2 !== void 0) return cached2;
30706
30736
  return opts.defaultValue;
30707
30737
  }
@@ -30737,7 +30767,7 @@ function resolveGhostReplyMode(opts) {
30737
30767
  if (modeOverride !== void 0) return modeOverride;
30738
30768
  const legacyOverride = parseGhostReplyMode(env2["AGT_GHOST_REPLY_INTENT_CLASSIFIER_ENABLED"]);
30739
30769
  if (legacyOverride !== void 0) return legacyOverride;
30740
- const cached2 = cachedGhostReplyMode(opts?.cachePath ?? defaultFlagsCachePath());
30770
+ const cached2 = cachedGhostReplyMode(opts?.cachePath ?? defaultFlagsCachePath(env2));
30741
30771
  if (cached2 !== void 0) return cached2;
30742
30772
  return "off";
30743
30773
  }
@@ -30768,7 +30798,7 @@ function cachedMaintenanceMode(path) {
30768
30798
  }
30769
30799
  }
30770
30800
  function resolveMaintenanceMode(opts) {
30771
- const cached2 = cachedMaintenanceMode(opts?.cachePath ?? defaultFlagsCachePath());
30801
+ const cached2 = cachedMaintenanceMode(opts?.cachePath ?? defaultFlagsCachePath(opts?.env));
30772
30802
  if (cached2 !== void 0) return cached2;
30773
30803
  return "off";
30774
30804
  }
@@ -30802,7 +30832,7 @@ function resolveSlackHotThreadMode(opts) {
30802
30832
  const env2 = opts?.env ?? process.env;
30803
30833
  const modeOverride = parseSlackHotThreadMode(env2["AGT_SLACK_HOT_THREAD_GUARD_MODE"]);
30804
30834
  if (modeOverride !== void 0) return modeOverride;
30805
- const cached2 = cachedSlackHotThreadMode(opts?.cachePath ?? defaultFlagsCachePath());
30835
+ const cached2 = cachedSlackHotThreadMode(opts?.cachePath ?? defaultFlagsCachePath(env2));
30806
30836
  if (cached2 !== void 0) return cached2;
30807
30837
  return "off";
30808
30838
  }
@@ -30838,7 +30868,7 @@ function resolveSlackScheduledChannelGuardMode(opts) {
30838
30868
  env2["AGT_SLACK_SCHEDULED_CHANNEL_GUARD_MODE"]
30839
30869
  );
30840
30870
  if (modeOverride !== void 0) return modeOverride;
30841
- const cached2 = cachedSlackScheduledChannelGuardMode(opts?.cachePath ?? defaultFlagsCachePath());
30871
+ const cached2 = cachedSlackScheduledChannelGuardMode(opts?.cachePath ?? defaultFlagsCachePath(env2));
30842
30872
  if (cached2 !== void 0) return cached2;
30843
30873
  return "shadow";
30844
30874
  }
@@ -34068,6 +34098,51 @@ var OAUTH_PROVIDERS = {
34068
34098
  }
34069
34099
  ]
34070
34100
  },
34101
+ "otter": {
34102
+ // CS-2029: Otter.ai's official MCP — remote streamable-HTTP at
34103
+ // https://mcp.otter.ai/mcp. Same Granola/Kajabi shape: OAuth 2.0
34104
+ // authorization-code with PKCE (S256) and a public client
34105
+ // (token_endpoint_auth_method 'none') issued via Dynamic Client
34106
+ // Registration (RFC 7591). Values below are taken verbatim from the
34107
+ // metadata, not inferred:
34108
+ // - https://mcp.otter.ai/.well-known/oauth-protected-resource names the
34109
+ // AS as https://otter.ai/ and advertises scopes profile:read,
34110
+ // conversations:read;
34111
+ // - https://otter.ai/.well-known/oauth-authorization-server gives
34112
+ // authorize /oauth2/authorize, token /oauth/token, revoke
34113
+ // /oauth/revoke_token, register /oauth/register, PKCE S256, `none`,
34114
+ // and the refresh_token grant.
34115
+ // NOTE the authorize path is /oauth2/authorize while token/revoke live
34116
+ // under /oauth/ — that is Otter's own split, do not "normalise" it.
34117
+ // The prod client was registered 2026-10-10 against /oauth/register with
34118
+ // scope 'profile:read conversations:read' and BOTH API callbacks,
34119
+ // https://api.augmented.team/... and https://api.ninjafy.com/integrations/oauth/callback,
34120
+ // because redirect_uri follows API_PUBLIC_URL and the ENG-11060 flip makes
34121
+ // the ninjafy name canonical; Otter returns no RFC 7592 handle, so a redirect
34122
+ // cannot be added later without a new client and a re-consent per connection.
34123
+ // OAUTH_OTTER_CLIENT_ID is set from its output (docs/runbooks/otter-dogfood.md). Otter advertises ONLY
34124
+ // read scopes, so this integration is read-only by construction: there is
34125
+ // no write scope to request even by mistake.
34126
+ definitionId: "otter",
34127
+ authorizeUrl: "https://otter.ai/oauth2/authorize",
34128
+ tokenUrl: "https://otter.ai/oauth/token",
34129
+ revokeUrl: "https://otter.ai/oauth/revoke_token",
34130
+ defaultScopes: ["profile:read", "conversations:read"],
34131
+ supportsRefresh: true,
34132
+ extraAuthorizeParams: {},
34133
+ clientAuthMethod: "body",
34134
+ pkce: "S256",
34135
+ publicClient: true,
34136
+ mcpUrl: "https://mcp.otter.ai/mcp",
34137
+ // Curated surface (matches the catalog seed's defined_scopes[].tools).
34138
+ // These are the server's three tools, all read-only. The names are
34139
+ // Otter's own, quoted from the tools' descriptions ("use `otter_search`
34140
+ // first", "call otter_get_user_info() first"), not Composio's wrapped
34141
+ // OTTER_AI_MCP_* slugs. An allowlisted name the server does not advertise
34142
+ // is logged and SKIPPED by the proxy, so confirm against the first live
34143
+ // tools/list after a real consent (runbook Step 3).
34144
+ toolAllowlist: ["otter_get_user_info", "otter_search", "otter_fetch"]
34145
+ },
34071
34146
  "notion-cli": {
34072
34147
  // Notion's public OAuth app. Tokens are workspace-scoped and long-lived —
34073
34148
  // Notion does not issue refresh_tokens, so `supportsRefresh: false` and
@@ -34707,6 +34782,34 @@ var INTEGRATION_REGISTRY = [
34707
34782
  docs_url: "https://help.kajabi.com/articles/api-integrations/connect-kajabi-to-claude-or-chatgpt",
34708
34783
  beta: true
34709
34784
  },
34785
+ {
34786
+ id: "otter",
34787
+ name: "Otter.ai",
34788
+ category: "knowledge",
34789
+ description: "Read Otter.ai meetings: search recordings by date, attendee or topic, and read summaries, action items and full speaker-attributed transcripts.",
34790
+ // CS-2029: same remote streamable-HTTP MCP + OAuth pattern as Granola/Kajabi.
34791
+ // Otter's server implements the MCP discovery chain (RFC 9728 protected-
34792
+ // resource metadata at mcp.otter.ai → RFC 8414 AS metadata at otter.ai →
34793
+ // RFC 7591 registration); auth is OAuth 2.0 authorization-code with PKCE
34794
+ // (S256) and a public client registered via one-time Dynamic Client
34795
+ // Registration (scripts/dcr-register.ts against https://otter.ai/oauth/register
34796
+ // → OAUTH_OTTER_CLIENT_ID, --scope 'profile:read conversations:read').
34797
+ // Consent is brokered by the webapp through the shared
34798
+ // /integrations/oauth/authorize → /callback path, and the access_token
34799
+ // reaches the host via the generic remote-oauth-proxy
34800
+ // (OAUTH_PROVIDERS.otter.mcpUrl). Read-only by construction: Otter
34801
+ // advertises no write scope.
34802
+ supported_auth_types: ["oauth2"],
34803
+ capabilities: [
34804
+ { 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" }
34805
+ ],
34806
+ // Otter's own Help Center article for the MCP server (CodeRabbit, #6906). It
34807
+ // exists even though help.otter.ai answers 403 to non-browser clients; the
34808
+ // help-centre API confirmed it (title "Otter MCP Server"). The MCP endpoint
34809
+ // itself is a JSON-RPC URL, not a page (Obi Wan 8305072d5414).
34810
+ docs_url: "https://help.otter.ai/hc/en-us/articles/35287607569687-Otter-MCP-Server",
34811
+ beta: true
34812
+ },
34710
34813
  {
34711
34814
  id: "anchor-browser",
34712
34815
  name: "Anchor Browser",
@@ -14169,8 +14169,38 @@ function emitToolCallMarkupRedactionTelemetry(channel) {
14169
14169
  import { existsSync, readFileSync } from "fs";
14170
14170
  import { homedir } from "os";
14171
14171
  import { join } from "path";
14172
- function defaultFlagsCachePath() {
14173
- return join(homedir(), ".augmented", "flags-cache.json");
14172
+ function hasUsableFlags(path) {
14173
+ try {
14174
+ if (!existsSync(path)) return false;
14175
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
14176
+ if (!parsed || typeof parsed !== "object") return false;
14177
+ const flags = parsed.flags;
14178
+ return !!flags && typeof flags === "object" && !Array.isArray(flags);
14179
+ } catch {
14180
+ return false;
14181
+ }
14182
+ }
14183
+ function cacheUpdatedAt(path) {
14184
+ try {
14185
+ if (!existsSync(path)) return void 0;
14186
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
14187
+ if (!parsed || typeof parsed !== "object") return void 0;
14188
+ const u = parsed.updated_at;
14189
+ return typeof u === "string" && u.length > 0 ? u : void 0;
14190
+ } catch {
14191
+ return void 0;
14192
+ }
14193
+ }
14194
+ function defaultFlagsCachePath(env = process.env) {
14195
+ const hostWide = join(homedir(), ".augmented", "flags-cache.json");
14196
+ const agentId = env["AGT_AGENT_ID"]?.trim();
14197
+ if (!agentId) return hostWide;
14198
+ const perAgent = join(homedir(), ".augmented", agentId, "flags-cache.json");
14199
+ if (!hasUsableFlags(perAgent)) return hostWide;
14200
+ const hostWideAt = cacheUpdatedAt(hostWide);
14201
+ const perAgentAt = cacheUpdatedAt(perAgent);
14202
+ if (hostWideAt && (!perAgentAt || perAgentAt < hostWideAt)) return hostWide;
14203
+ return perAgent;
14174
14204
  }
14175
14205
  function envBoolean(raw) {
14176
14206
  if (raw === void 0) return void 0;
@@ -14197,7 +14227,7 @@ function resolveHostBooleanFlag(opts) {
14197
14227
  const env = opts.env ?? process.env;
14198
14228
  const envValue = envBoolean(env[opts.envVar]);
14199
14229
  if (envValue !== void 0) return envValue;
14200
- const cached2 = cachedBoolean(opts.key, opts.cachePath ?? defaultFlagsCachePath());
14230
+ const cached2 = cachedBoolean(opts.key, opts.cachePath ?? defaultFlagsCachePath(env));
14201
14231
  if (cached2 !== void 0) return cached2;
14202
14232
  return opts.defaultValue;
14203
14233
  }
@@ -14233,7 +14263,7 @@ function resolveGhostReplyMode(opts) {
14233
14263
  if (modeOverride !== void 0) return modeOverride;
14234
14264
  const legacyOverride = parseGhostReplyMode(env["AGT_GHOST_REPLY_INTENT_CLASSIFIER_ENABLED"]);
14235
14265
  if (legacyOverride !== void 0) return legacyOverride;
14236
- const cached2 = cachedGhostReplyMode(opts?.cachePath ?? defaultFlagsCachePath());
14266
+ const cached2 = cachedGhostReplyMode(opts?.cachePath ?? defaultFlagsCachePath(env));
14237
14267
  if (cached2 !== void 0) return cached2;
14238
14268
  return "off";
14239
14269
  }