Three places where ClaudeMemoryTool diverges from the documented
memory_20250818 wire format:
- rename sends old_path/new_path, not path. handleCommand validated
command.path, so every rename coming from a real model died with
"Cannot read properties of undefined (reading 'startsWith')".
path is still accepted as the source for existing callers.
- insert_line means "insert after this line" (0 = top of file), but we
spliced at insertLine - 1 and rejected 0, so every insert landed one
line above where Claude asked and inserting at the top was impossible.
- str_replace with new_str omitted is a deletion per the spec; we
rejected it.
The new tests mock the supermemory client so they run without an API
key. Also fixed the rename example in the docs, which showed the same
path shape the code expected.
Cherry-picks two cleanup PRs and finishes the job. Net 262 deletions.
- #1473 (@abhay-codes07): drops a `console.log` in the fullscreen note editor that printed the whole note body on every keystroke, which PostHog session replay can capture.
- #1563 (@ishaanxgupta): removes `/api/onboarding/research` and `/api/onboarding/extract-content`. Neither has a caller anywhere in the repo, and both spent metered Exa and xAI quota. This reverts the guards added for them in #1589, which only existed to make unreachable code safe.
- On top: `EXA_API_KEY`, `XAI_API_KEY` and the `@ai-sdk/xai` dependency are removed, since deleting those routes left them with no consumer.
Co-Authored-By: abhay-codes07 <182421137+abhay-codes07@users.noreply.github.com>
Co-Authored-By: ishaanxgupta <124028055+ishaanxgupta@users.noreply.github.com>
Cherry-picks #1582, #1583, #1584 and #1585 from @Sravanjangam (security audit #1578) onto one branch.
- MCP: `get_document` scopes to the active space like its sibling read tools, `fetch-graph-data` bounds page/limit, `guided-save` caps prefill at 200k, and `whoAmI` no longer returns the transport session id.
- ai-sdk: search limit clamped to 1-50 with a 30s client timeout.
- validation: caps on `DocumentsWithMemoriesQuerySchema.limit` and `BulkDeleteMemoriesSchema.containerTags`.
- Raycast: `metadata.url` is parsed and only http(s) is offered to the OS opener.
Dropped his `add_memory` permission gate: it checked the target against the list of existing spaces, so writes to a new space failed and the no-active-space path surfaced `No write access to space "undefined"`. Write permission stays enforced in the API via `containerTagGate`.
Hardening and consistency rather than a security fix, since the API already enforces every permission boundary here.
Co-Authored-By: Sravanjangam <163002695+Sravanjangam@users.noreply.github.com>
Cherry-picks #1587 from @Sravanjangam, plus the OAuth half on top.
When the auth backend is slow or returns a 5xx, the MCP server currently answers `invalid_token`. That is the protocol's signal to discard the credential and re-authenticate, so a brief upstream blip logs every connected client out, and `sm_` API key users have no automatic way back. These requests now return 503 with `Retry-After: 5` so clients retry instead.
His change covered the API key path only. This shares one `transientAuthErrorFor` helper between `validateApiKey` and `validateOAuthToken`, so a JWKS timeout or a 5xx also returns 503 on the OAuth path that Claude, Cursor and browser clients use.
Genuinely bad tokens are unaffected: bad signature, expired, and no-matching-key still resolve to 401. Verified across all seven cases.
Co-Authored-By: Sravanjangam <163002695+Sravanjangam@users.noreply.github.com>
Cherry-picks #1579 and #1580 from @Sravanjangam (security audit #1578), plus improvements on top.
- `/api/og`, `/api/onboarding/extract-content` and `/api/onboarding/research` now verify the session against the auth backend; the middleware only checked that a cookie was present, so a forged cookie reached handlers that spend metered Exa/xAI quota.
- Bounds those routes: 2MB cap on fetched HTML, max 10 http(s) URLs per request, name/email length limits and a 60s timeout on the LLM call.
- De-duplicates URLs before calling Exa, and collapses whitespace in `name`/`email` so a newline can't forge extra prompt lines. Both adapted from @SEPURI-SAI-KRISHNA's #1528 and #1530.
- Deletes the unused, unauthenticated `account-status` route.
Verified locally: pre-fix `/api/og` returned 200 for a forged cookie, post-fix it returns 401. Five duplicate URLs collapse to two before reaching Exa, and a newline-laden `name` arrives as a single prompt line.
Supersedes #1528 and #1530.
<!-- 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.
## 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 -->
## Why?
Plain **T-1554**: a user with a **read-only** MCP OAuth grant got 403s on memory listing, and the client rendered them as *"Access forbidden. Your account may be restricted or blocked."* The API's actual error body said `{"error": "This API key has read-only access"}` — but `handleError` discarded it, so the user (and support) chased a nonexistent account ban.
Two masking layers:
1. `handleError` used the raw error `message`, which for our raw-fetch endpoints was a hardcoded string ("Failed to fetch documents") or unparsed JSON, and fell back to the scary "restricted or blocked" text when empty.
2. `getDocuments` didn't read the response body at all.
## What?
- New `extractApiErrorMessage()` unwraps JSON error bodies (`{"error": ...}` / `{"message": ...}`) so the API's real reason reaches the user.
- `getDocuments` and `listMemoryEntries` now pass the (unwrapped) response body through with the status, letting `handleError` apply status-aware fallbacks when the body is empty.
- Reworded the empty-body 403 fallback to point at the common cause first: *"Access forbidden. This connection may be read-only or scoped to specific spaces — reconnect with broader access, or check your account status."*
Companion API-side fix (read-only grants couldn't call semantically-read POST list endpoints at all): supermemoryai/mono#2772.
## Testing
- Added tests: a 403 with a JSON error body surfaces the API's message; an empty-body 403 gets the scope-aware fallback. `vitest run src/server/client/index.test.ts` — 3 passed.
- `tsc --noEmit -p tsconfig.json` clean. (The `check-types` script also runs `tsconfig.widget.json`, which fails on origin/main with a pre-existing `UseAppOptions.strict` error, unrelated.)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> User-facing error text only in the MCP client; no auth or API behavior changes.
>
> **Overview**
> **MCP client errors now show what the API actually returned** instead of hardcoded strings or misleading “restricted or blocked” text.
>
> Adds `extractApiErrorMessage()` to parse JSON bodies (`error` / `message` fields) from failed responses. **`getDocuments`** and **`listMemoryEntries`** read the response body on non-OK status and attach the unwrapped message (with status) for **`handleError`**, which also uses the helper on error messages. When a 403 has no body message, the fallback now points users toward **read-only or scoped OAuth** rather than an account ban.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 1f492470cf4b619e58eea6d45af1dfa0b8cad0c4. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Adds a focused guide for backfilling dated documents with `documentDate` and the batch ingestion API.
- includes TypeScript and Python batch examples plus optional completion polling
- links the guide from the docs navigation and ingestion entry points
Validated with `bunx mintlify@latest validate` and `bunx mintlify@latest broken-links`.
Onboarding now opens a trial step that collects a card through Stripe checkout before the brain is enabled, with a timeline showing today's $0, the day-12 reminder, and the day-14 charge.
- Only leaves the card step once the API confirms the trial is live
- Brain home shows a setup banner and dims what the trial unlocks
- Recovers orgs that abandoned checkout instead of stranding them
- Adds the organization ID to account settings, copyable from the label
Company Brain workspaces could only buy Scale at $399/mo, which is roughly eight times what the median team uses. Adds the $100/mo Max card to the Company Brain plan picker, notes what a Scale trial loses on the way down, and flags that Scale is cheaper above about $400/mo of credits.
Inline notice with app icons when a channel automation can't use personal-only connections (footer, next to Save), post-save warning toast from the API, templates capped to 3 connection-relevant ideas with a show-all toggle, and New automation promoted to a primary button on the heading row. Pairs with mono #2724; degrades gracefully without it.
Fixes ENG-1151
Adds one FAQ to the ChatGPT Web setup guide for making Supermemory the default memory.
- shows how to disable ChatGPT memory and optionally move the existing summary
- adds light and dark screenshots plus the custom instruction to paste
Tested with `mintlify validate` and `mintlify broken-links`.
### TL;DR
Documents the new `ids` parameter for the `forget-matching` endpoint, allowing exact memory deletion without semantic search.
### What changed?
The `forget-matching` endpoint now accepts either a `query` (semantic search-based forgetting) or an explicit `ids` list (direct deletion by memory ID) — one or the other must be provided. The docs have been updated to reflect this:
- The `query` parameter is now marked as `one of*` rather than required, and `ids` is introduced as an alternative with the same mutual-exclusivity constraint.
- `threshold` is clarified as applying to `query` mode only.
- Code examples for both JavaScript and cURL now show the recommended two-step pattern: run a `dryRun` with `query`, then apply using the `ids` returned from the preview — avoiding drift if the container changes between steps.
- A new `<Tip>` block explains why passing `ids` on the apply step produces a more deterministic delete than re-running the `query`.
### How to test?
1. Call `forget-matching` with `dryRun: true` and a `query` to retrieve candidate `id`s.
2. Re-call with `dryRun: false` and the `ids` from step 1 to confirm only those exact memories are forgotten.
3. Verify that passing `ids` belonging to a different `containerTag` are ignored.
4. Confirm that providing both `query` and `ids`, or neither, returns an appropriate validation error.
### Why make this change?
Re-running a `query` on the apply step can produce different results if the container was modified between the preview and the apply. Exposing `ids` as a first-class parameter lets callers pin the delete to exactly the set they reviewed, making bulk forgetting safer and more predictable.
Header-auth MCP servers like Plane need an API key plus a second header,
which the custom connector dialog had no way to collect.
- Restructured on the Claude connector pattern: name + URL up front,
credentials behind collapsed Advanced settings
- Adds API key, header name, and repeatable extra header rows
## What and why
Make skills created through Company Brain visible in the existing Settings catalog without a manual reload. The UI labels Slack-originated skills and refreshes the catalog while the Settings page is open.
```mermaid
flowchart LR
A[Slack approval] --> B[Company Brain skill catalog]
B --> C[Skills API query]
C --> D[Settings Skills list]
D --> E[Created by Company Brain label]
```
## Validation
- `bunx biome check apps/web/components/settings/company-brain-skills.tsx apps/web/components/settings/company-brain-skills/domain.ts apps/web/components/settings/company-brain-skills/skill-row.tsx apps/web/hooks/use-brain-skills.impl.ts` — passed
## Impact
Settings polling refreshes every 15 seconds and on window focus. It does not change the Nova/browser chat workflow or create skills from that surface.
Updates the Supermemory MCP docs for the revamped tool, space, widget, and OAuth flows.
- adds a screenshot-backed ChatGPT Web setup guide with light and dark variants
- refreshes the overview, setup, tools, spaces, and widget docs
- keeps manual JSON configuration in a dropdown
Tested all four local MCP docs routes and image references.
for mcp image light mode -> dark mode images and dark mode has light mode (since the background was blending in i made this way to refocus user attention on the screenshots)
Fixes cross-host graph rendering and moves widget uploads off the JSON/base64 tool transport.
- render graph data from the launcher result without a second tool call
- stream multipart uploads through one-time, short-lived upload sessions
- remove temporary widget diagnostics and redundant unit tests
Tested with Biome, TypeScript, 17 unit tests, a Wrangler deployment dry-run, and live graph rendering in ChatGPT and Claude. Authenticated E2E setup is currently blocked by the saved OAuth refresh session returning `invalid_grant: session not found`.
Keep docs asset URLs repo-root-relative and pass images as literal MDX children so Mintlify can compile them through OptimizedImage under the /docs mount.
Mintlify's official guidance says: “Image paths are root-relative from your docs repository.” It also says relative paths such as `./screenshot.png` are unsupported. See [Image embeds](https://www.mintlify.com/docs/create/image-embeds).
The existing `/images/...` paths were correct. The failure came from passing them through custom-component string props, which kept Mintlify from seeing those images during its MDX transform.
- fix hero, building-block, and Slack avatar images
- preserve Slack avatar clipping
- normalize Hermes and Company Brain icon sizing
Tested with Mintlify validation, broken-link checks, and browser checks across every changed route.
Fixes broken docs navigation and asset paths from the site audit.
- serves docs images from `/images` and adds the missing Cartesia icon
- corrects homepage, console, and LinkedIn destinations
- removes agent-only comparison headings from the web TOC
Tested with Mintlify validate and Mintlify broken-links.
- Header pill with trial days left (Autumn-first, org metadata fallback)
- Brain home: Your Company Brain timeline card (trial strip, milestones from /brain/overview) promoted to top-right
- Trial copy in CB onboarding Slack step and docked header
- Brain home now reads the new /brain/overview endpoint (drops the dead /brain/connections fetch)
Fixes ENG-1142
## Stack Context
This is the frontend half of the Company Brain Skills feature. The harness is implemented in supermemoryai/mono#2611.
## What?
Add Skills settings with separate Org-wide and Personal sections, Markdown upload autofill, scoped creation and editing, approval controls, and server-driven permissions.
## Why?
Members need a focused way to manage their private playbooks while admins create and approve organization-wide guidance.
## Related
- Harness: https://github.com/supermemoryai/mono/pull/2611
---
**Session Details**
- Session: [View Session](https://supermemory.us1.vorflux.com/agent-sessions/d8c77451-8f55-47ab-a182-5c98da616263)
- Requested by: Sreeram Sreedhar (sreeram@supermemory.com)
- Address comments on this PR. Add `(aside)` to your comment to have me ignore it.
Fixes MCP app submission metadata and makes widget delivery consistent across hosts.
- content-hash widget resources and set the production widget domain
- add typed structured outputs to direct tools and scope default-space calls
- keep graph rendering compatible with both initial results and app-side loading
Tested with `bun run check-types` and `bun run test:unit` (42 tests).
Adjusts login handling for a limited credential-validation flow. Normal Google and magic-link sign-ins are unchanged for other accounts.
Tested with direct login validation and a production build.
ChatGPT can omit nullable nested fields from structured tool results and does not reliably expose result metadata to MCP Apps. Keep graph data in `structuredContent`, tolerate missing document titles, and bundle React inside the widget so the graph renders consistently across hosts.
- use the deployable `app-v4` resource URI
- keep the graph response and widget schema aligned
- remove the `esm.sh` runtime dependency
Tested with typecheck, 42 unit tests, production widget build, 36 branch-local authenticated E2E tests, and a live ChatGPT graph render.