Move profile deduplication into the SDK middleware. Facts are normalized
(strip leading date, trim, collapse whitespace, casefold) and deduplicated
in static > dynamic > search priority within each request, then injected as
one owned <supermemory> block that replaces the previous block instead of
accumulating. Dedup is mode-aware so query-mode search results are not
dropped against an omitted profile. No global/browser Set: request-local
only, safe for concurrent requests and Cloudflare Workers.
Covers AI SDK, OpenAI Chat/Responses, Mastra, and VoltAgent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update tool descriptions, proactive search guidance, and v4 API examples
across SKILL.md and sdk-guide reference.
Co-authored-by: Cursor <cursoragent@cursor.com>
<!-- VORFLUX_AGENT_PR_BODY_BEGIN -->
Adds the full 654-entry MCP directory without bundling records into client JavaScript, with explicit capability status and connector branding that degrades safely when no authoritative logo is available.
## Changes
- Lazy-load and validate the searchable, filterable, progressively rendered MCP catalog.
- Render same-origin proxied provider icons for 543 entries, with a reviewed domain allowlist and deterministic fallback marks for 111 unresolved or unbranded entries.
- Record OAuth discovery capability separately from end-to-end support; all directory setup actions remain suppressed until their authentication flow is verified.
- Add a reproducible OAuth metadata probe with HTTPS/private-network protections, stable URL keys, authorization-server scanning, and catalog fingerprint validation.
- Add Google Drive branding for the curated built-in connector.
## Testing
- **Passed:** Deterministic generation and catalog assertions.
```bash
PATH="$HOME/.bun/bin:$PATH" python3 apps/web/scripts/generate-mcp-directory.py --output
cmp apps/web/public/mcp-directory.json
```
Verified 654 entries, 254 DCR discoveries, 27 preregistered OAuth discoveries, 373 unclassified entries, and zero directory setup actions.
- **Passed:** Stale OAuth metadata fingerprint is rejected by the generator.
- **Passed:** Touched-file Biome checks and `git diff --check`.
- **Passed:** Icon proxy returned 200 for an allowlisted domain and 400 for an unknown valid-looking domain.
- **Passed:** Authenticated desktop/mobile browser inspection and conservative capability labels.
- **Passed:** Public preview returned HTTP 200 and rendered the real app. Authentication cookies do not transfer to the public hostname, so the public screenshot shows login.
- **Partial:** Repository-wide TypeScript checks remain blocked by unrelated existing errors outside the touched MCP files.
- **Partial:** 111 entries intentionally retain deterministic fallback marks; endpoint-derived domains may not always be the canonical brand logo.
- **Blocked:** Google rejected the local HTTP OAuth callback, so live Google Drive consent, callback, persistence, tool discovery, disconnect, and reconnect were not completed.
Public preview: https://ar8ruchhbi65.preview.us1.vorflux.com/configure/tools
---
**Attached Images**
*[288.csv]*
*[mcp-directory-final.json]*



<!-- VORFLUX_AGENT_PR_BODY_END -->
---
**Session Details**
- Session: [View Session](https://supermemory.us1.vorflux.com/agent-sessions/1cd0aab9-2a45-4818-aa13-f9bfe032ddba)
- Requested by: Dhravya Shah (dhravya@supermemory.com)
- Address comments on this PR. Add `(aside)` to your comment to have me ignore it.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes how users pick MCP URLs and auth (OAuth vs API key) before hitting existing connect endpoints; no new backend auth logic in this diff, but misconfiguration or trusting bad URLs remains a user-risk surface.
>
> **Overview**
> Adds a **browseable MCP directory** on the Company Brain connectors page: the catalog is **not bundled in JS**—it loads from static **`/mcp-directory.json`** only after the user opens the directory (with validation, caching, and abort handling).
>
> The new **`McpDirectoryBrowser`** supports search, category/availability filters, and progressive “show more” rendering. Supported remote entries route into the existing custom MCP flow via **Set up**, which pre-fills name/URL and opens the connector dialog with context-specific copy.
>
> The custom connector dialog now uses an explicit **OAuth vs API key** toggle; API key fields only appear for API-key mode, and directory-backed connections get **stable slugs** (`-dir-` suffix) so names display cleanly on connected cards. **Middleware** excludes `mcp-directory.json` from the auth matcher so the asset can be fetched publicly.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 8b59bae84a. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Stack Context
Single-auth story for the Claude Code supermemory plugin rework: the plugin's hooks and its MCP surface share one credential (`sm_` API key from the existing browser connect flow). That requires `mcp.supermemory.ai` to accept plain API keys, which it currently rejects (OAuth JWT only).
## What?
- `validateApiKey()` in `server/auth`: `sm_`-prefixed Bearer tokens validate via the existing `fetchSession()` (`GET /v3/session`) and map to the same `AuthUser` shape as OAuth tokens (`userId` ← `user.id`, `organizationId` ← `org.id`, the key itself as `bearerToken` for downstream API calls). Successful lookups cached per isolate for 60s.
- `handleMcpRequest` routes by token shape: `sm_` keys → session validation, everything else → OAuth JWT verification (unchanged).
- `sessionInfoSchema` now types the `org.id` field the session endpoint already returns.
## Why?
MCP clients that already hold an API key (Claude Code plugin hooks, CLI, scripts) can connect without an OAuth dance or a second consent. OAuth behavior is untouched — the existing "rejects opaque API keys" test on the OAuth validator still passes; keys just get their own path. Malformed keys are rejected without an API round-trip.
Tests: 4 new cases (valid key → AuthUser, cache hit → single fetch, 401 → null, malformed → no request). `vitest run src/server/auth` 13/13, `tsc --noEmit` clean.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Adds a new authentication path on the MCP entrypoint with in-memory key caching (60s TTL), so revoked keys may remain valid briefly within an isolate; OAuth behavior is unchanged.
>
> **Overview**
> MCP Bearer auth now accepts **`sm_` Supermemory API keys** in addition to OAuth JWTs, so clients that already hold an API key can connect without OAuth.
>
> **`validateApiKey`** treats keys matching `sm_` plus at least 17 non-space characters as API keys: it calls **`GET /v3/session`** with the key as Bearer, maps **`user.id`** and **`org.id`** into the same **`AuthUser`** shape as OAuth (key kept as **`bearerToken`** for downstream API calls), and caches successful results per isolate for **60s** (up to 1000 entries, full clear on overflow). Malformed keys are rejected locally with no HTTP call; session **401** yields unauthenticated.
>
> **`handleMcpRequest`** branches on token shape: API keys go through session validation; other tokens still use JWT verification unchanged.
>
> **`sessionInfoSchema`** now includes optional **`org.id`** typing for session responses used when resolving organization context from API keys.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e54fb11bf1598d07a807eb2b0b63a347aaa58fb6. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Returning from Stripe remounts onboarding and reseeds the domain from the user's email, so the header showed the wrong company and a research retry would re-run on the wrong domain. Past the confirm step, read the org's stored brainWorkspaceDomain instead.
Two hand-rolled copies of the add-on/brainMode rule replaced by a single shared helper, and the one-line isCompanyBrainOrganization wrapper dropped. No behaviour change.
Resolve pyproject.toml conflict by keeping supermemory>=3.50.0
(required for v4 API migration) and main's package version 1.0.5.
Co-authored-by: Dhravya Shah <dhravya@supermemory.com>
## Summary
- Reorder Apps & extensions so Import X bookmarks appears in the top row and Apple Shortcuts uses the open space below.
- Keep both Apple Shortcut actions inline on larger screens while allowing the card to grow only as much as needed.
- Rework the Company Brain promo on phones so its logo, copy, close control, and CTA remain readable and aligned.
## What?
Adds a "Continue with AgentID" button to the web app's login page, matching the existing Google/GitHub buttons (same `ExternalAuthButton` pattern, PostHog `login_attempt` capture, last-used badge).
- `packages/lib/auth.ts`: adds the `genericOAuthClient` plugin — generic OAuth providers sign in via `signIn.oauth2({ providerId })`, not `signIn.social`.
- `apps/web/app/(auth)/login/page.tsx`: the button, gated the same way as the other social buttons — always shown on cloud (`NEXT_PUBLIC_HOST_ID === "supermemory"`), opt-in elsewhere via `NEXT_PUBLIC_AGENTID_AUTH_ENABLED` (added to `.env.example`).
## Why?
Companion to supermemoryai/mono#2908, which registers an `agentid` generic OAuth provider (OIDC against auth.agentid.com) on the API so agents can authenticate with their AgentID identity. The consumer app talks to the same better-auth server, so it gets the same sign-in option. mono#2916 additionally auto-invites the agent's verified human owner to the agent's workspace.
Requires mono#2908 to be deployed for the button to work; until then the API rejects the unknown provider and the page shows its normal error state.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches authentication entry points and OAuth client configuration; risk is moderate because it extends login surface area but follows existing social sign-in patterns and is feature-flagged.
>
> **Overview**
> Adds **Continue with AgentID** on the web login page, using the same `ExternalAuthButton` flow as Google/GitHub (PostHog `login_attempt`, last-used badge, loading/error handling).
>
> The button calls **`signIn.oauth2({ providerId: "agentid" })`** instead of `signIn.social`, enabled by registering **`genericOAuthClient`** on the shared better-auth client in `packages/lib/auth.ts`.
>
> Visibility matches other social providers: shown on cloud when `NEXT_PUBLIC_HOST_ID === "supermemory"`, or elsewhere when **`NEXT_PUBLIC_AGENTID_AUTH_ENABLED`** is set (documented in `.env.example`). Depends on the API registering the `agentid` generic OAuth provider.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 90a32786a3. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->