Add the Atlaso Memory plugin#157
Conversation
Automatic long-term memory for Cursor: recall + capture hooks, the self-contained atlaso MCP server (recall/remember/forget/recent/status), a usage rule, and a curation skill. Client-side secret scrub, per-tool credentials, fails open. Passes scripts/validate-plugins.mjs.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 15 potential issues.
Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| const results = Array.isArray(data.results) ? data.results : []; | ||
| const row = results.find((r: any) => r?.client_id === client_id) ?? results[0]; | ||
| return row?.id ?? client_id; // durable server id when settled, else the idempotency key | ||
| } |
There was a problem hiding this comment.
Remember skips client scrub
High Severity · Security Issue
The remember function sends raw text to the brain without client-side scrubbing, unlike auto-capture. This allows secrets like API keys and tokens to be exfiltrated, violating the plugin's on-device scrubbing guarantee.
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| return { results: results.map((r) => ({ id: r.id, content: r.content })) }; | ||
| } | ||
| case "recent": | ||
| return { memories: await recent(auth, Number(args?.limit ?? 10)) }; |
There was a problem hiding this comment.
MCP leaks cross-project memory
High Severity · Logic Bug
The recall and recent MCP tools omit project-specific filtering and visibleInProject checks. This can expose project-scoped memories from one repository within another, a behavior that differs from how these memories are handled elsewhere.
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| token: string, | ||
| user_id: string, | ||
| device_id: string | null, | ||
| ): string { |
There was a problem hiding this comment.
Ambiguous auth write arguments
Medium Severity · Bugbot Rules
writeAuth takes multiple consecutive string arguments (server, token, user_id, and nullable device_id). Call sites can swap token and user_id without a TypeScript error, which would persist a malformed credential. This violates the review rule that same-type parameters must use a named-args object.
Additional Locations (1)
Triggered by team rule: No ambiguous args at callsite in typescript
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| return ( | ||
| !!a.server && !!a.user_id && !!a.device_id && | ||
| a.server === b.server && a.user_id === b.user_id && a.device_id === b.device_id | ||
| ); |
There was a problem hiding this comment.
Null device blocks credential reuse
High Severity · Logic Bug
The sameIdentity function requires device_id to be truthy for both credentials, but the connect process can persist device_id: null. This causes sameIdentity to incorrectly fail, treating valid tool credentials as foreign. Consequently, resolveCredential repeatedly clears and re-mints tool tokens, leading to increased latency and unnecessary token churn.
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| detached: true, | ||
| env: { ...process.env, ATLASO_TOOL: tool }, | ||
| }).unref(); | ||
| return true; |
There was a problem hiding this comment.
Spawn error leaves connect lock
Medium Severity · Logic Bug
maybeAutoconnect and openBrowser spawn child processes without handling asynchronous error events. If spawn fails (e.g., command not found), the try/catch block is bypassed, leaving the .connecting lock unreleased and blocking subsequent autoconnect attempts until its TTL expires. This can also lead to uncaught errors during authorization.
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| case "status": { | ||
| const h = await health(auth); | ||
| return { connected: true, fmi: h?.fmi ?? null, total: h?.deposit_count ?? null }; | ||
| } |
There was a problem hiding this comment.
Status ignores failed health
Medium Severity · Logic Bug
The MCP status tool always returns connected: true even when health() returns null after a network or API failure. There is no check of the health result, so the agent is told memory is connected while fmi and total are null and the brain is actually unreachable.
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| if (!name.endsWith(".json")) continue; | ||
| const full = join(dir, name); | ||
| try { | ||
| if (now - statSync(full).mtimeMs > STALE_MS) rmSync(full, { force: true }); |
There was a problem hiding this comment.
Prune drops active turn stash
Medium Severity · Logic Bug
takePending runs prune() before reading the current conversation’s stash, and prune deletes any *.json older than one hour by mtime—including the file about to be read. Long turns that only stashed via beforeSubmitPrompt (no later afterAgentResponse refresh) can lose the pending user text and workspace before deposit, so capture falls back or skips entirely.
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| if (scope === "project" && pk) tags.push(`project:${pk}`); | ||
|
|
||
| const item: DepositItem = { | ||
| client_id: turnKey(scrubbedUser, scope, scope === "project" ? pk : null), |
There was a problem hiding this comment.
Divergent project keys duplicate deposits
Medium Severity · Logic Bug
stop often deposits with pending.ws from prompt time, while a later sessionEnd with an empty stash falls back to workspaceRoot(payload) (and possibly transcript text). turnKey includes the project key, so when those workspace resolutions disagree the same turn gets two different client_ids and server-side dedupe does not collapse them.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| return false; | ||
| } | ||
| } | ||
| return false; |
There was a problem hiding this comment.
Connect lock expires mid-authorize
Medium Severity · Potential Edge Case
The .connecting lock is written once and never refreshed, while waitForCode can wait for the server’s expires_in (default 10 minutes, but not capped to the 15-minute lock TTL). After the TTL, another sessionStart can reclaim the lock and spawn a second browser authorize flow concurrently with the first.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
| { | ||
| "command": "bun run ${CURSOR_PLUGIN_ROOT}/hooks/capture.ts", | ||
| "timeout": 30 | ||
| } |
There was a problem hiding this comment.
Hook timeouts undercut network budget
Medium Severity · Logic Bug
sessionStart is capped at 20s and stop at 30s, but a cold path can run entitlement, tool claim, credential exchange, then recall+recent or deposit sequentially—each already bounded at 8–15s. Cursor can kill the hook before atlaso-recall.mdc is written or before a deposit finishes, so first-session recall and slow-link capture fail silently even though the client’s own fetches would still be in flight.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 19e8f32. Configure here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>


Add the Atlaso Memory plugin
Adds Atlaso (
atlaso/) — automatic long-term memory for Cursor. It recalls whatyou've decided and remembers what matters across sessions, projects, and tools, so the
agent starts each session with the relevant context instead of a blank slate.
Homepage: atlaso.ai · Source: atlaso-labs/cursor
What it adds
sessionStartrecalls relevant notes into a rules file;stop/sessionEndcapture the exchange. The automatic loop, zero model involvement.atlaso)recall,remember,forget,recent,status— for deliberate moves.alwaysApply, with frontmatter)Self-contained — no external binary
The MCP server is
lib/mcp.ts, which ships inside the plugin and runs on thebunCursor already provides (
bun run ${CURSOR_PLUGIN_ROOT}/lib/mcp.ts). There is nounpublished CLI or npm package to install — the whole plugin is self-contained and
works on a fresh machine. Rule has valid
description+alwaysApplyfrontmatter; theskill has
name+description; rule and skill are distinct (orientation vs judgment).Privacy (a memory plugin should be explicit here)
tokens, and credentialed URLs are redacted client-side (regex + entropy), and again
server-side.
Cursor — nothing else on the machine.
recall never block or break a turn.
app.atlaso.ai/dashboard.
Validation
node scripts/validate-plugins.mjs(marketplace + plugin manifests).source repo.
Maintained by Atlaso Labs (we ship the same memory for Claude Code, Codex, and
Antigravity). Happy to make any changes the team would like. Contact: hello@atlaso.ai
Note
Medium Risk
New third-party plugin runs hooks on every session/turn and can send scrubbed conversation excerpts to an external service; mitigations (fail-open, client redaction, per-tool creds) are explicit but warrant privacy/security review.
Overview
Adds a new Atlaso Memory marketplace plugin (
atlaso/) plus a listing in.cursor-plugin/marketplace.json, giving Cursor automatic long-term memory backed by Atlaso’s cloud API.Automatic loop (hooks): On
sessionStart,recall.tsfetches memories and writes.cursor/rules/atlaso-recall.mdc(workaround for brokenadditional_context). OnbeforeSubmitPrompt/afterAgentResponseit stashes the turn; onstop/sessionEnd,capture.tsgates, scrubs secrets, scopes personal vs project, and deposits to the brain. First run can spawn a detached PKCE device-auth flow (connect.ts). Hooks always exit 0 so memory never blocks a session.Deliberate control: Bundled stdio MCP server (
lib/mcp.tsviamcp.json) exposesrecall,remember,forget,recent, andstatus, sharing the same per-tool credential as the hooks. AnalwaysApplyrule and memory skill orient the agent and curation judgment;AGENTS.mdmirrors the rule.Client stack: Bun/TypeScript thin client (
lib/atlaso.ts, credential mint underflock, entitlement cache, project keys from git origin, transcript fallbacks). Per-tool tokens at~/.atlaso/tools/cursor.json; revokes only on verifiedx-atlaso-responsefrom Atlaso’s server. v1 is online-first (no offline outbox).Reviewed by Cursor Bugbot for commit cfe62fe. Bugbot is set up for automated code reviews on this repo. Configure here.