
Research
/Security News
TensorLake npm SDK Compromised in ChainDrop Shai-Hulud Credential-Stealing Attack
Tensorlake npm SDK version 0.5.144 was compromised in a ChainDrop / Shai-Hulud attack, delivering credential-stealing malware.
@contextq/memory-tool
Advanced tools
Anthropic memory tool (memory_20250818) backend adapter that stores Claude's memory files as ContextQ contexts
A backend adapter for Anthropic's memory tool (memory_20250818) that stores
every memory file as a ContextQ
context, wired up entirely through ContextQ's existing /api/contexts*
endpoints -- no schema changes.
Background: docs/product/claude-surfaces-positioning.md (Q1) is the
feasibility study this package implements; this README covers install/use.
Product framing (why an adapter, not a competing surface): the same doc's
"Q3 -- The one-page positioning statement".
npm install @contextq/memory-tool @anthropic-ai/sdk
@anthropic-ai/sdk is a peer dependency -- bring your own version (tested
against ^0.120.0, the version current when this package was built).
import Anthropic from '@anthropic-ai/sdk';
import { contextQMemoryTool } from '@contextq/memory-tool';
const client = new Anthropic(); // reads ANTHROPIC_API_KEY
const memory = contextQMemoryTool({
apiKey: process.env.CONTEXT_API_KEY!, // sk_live_<40 hex>
baseUrl: process.env.CONTEXT_API_URL, // default http://localhost:38200
});
const runner = client.beta.messages.toolRunner({
model: 'claude-opus-5',
max_tokens: 16000,
tools: [memory],
messages: [
{ role: 'user', content: 'Remember that I prefer TypeScript, under /memories/acme/.' },
],
});
for await (const message of runner) {
console.log(message);
}
contextQMemoryTool(options) is a thin factory: new ContextQMemoryTool(options)
handed to Anthropic's betaMemoryTool() helper. Use ContextQMemoryTool
directly if you want the MemoryToolHandlers-shaped object without the
BetaRunnableTool wrapper (e.g. to compose your own tool-loop).
| Option | Default | Notes |
|---|---|---|
apiKey | process.env.CONTEXT_API_KEY | ContextQ API key, sk_live_<40 hex>. Required (one of option or env). |
baseUrl | process.env.CONTEXT_API_URL || http://localhost:38200 | ContextQ API base URL. |
contextType | 'reference' | The ContextQ type every memory-tool row gets. The memory tool has no type taxonomy of its own. |
tag | 'mem-tool' | Reserved tag stamped on every row this adapter creates; also used to scope directory/prefix listings so this adapter never lists a caller's unrelated contexts. |
maxRetries, timeoutMs, fetchImpl | see HttpClientOptions | Passed straight through to the internal HTTP client. |
/memories -> lists workspaces
/memories/<workspace> -> lists that workspace's mem-tool-tagged contexts
/memories/<workspace>/a/b/c.md -> a single context
The first path segment after /memories/ is the ContextQ workspace
slug (lowercased). Everything after it is joined into ContextQ's
external_id for that context, namespaced as mem-tool:<workspace>/<rest>
-- the workspace is included in the key (unlike the bare "remaining path"
sketch in the feasibility doc) because contexts.external_id is unique per
tenant, not per (tenant, workspace); two workspaces both wanting a file
named notes.md would otherwise collide.
| Command | Endpoint(s) | Atomic? |
|---|---|---|
view (file) | GET /api/contexts/by-external/:externalId | yes |
view (directory / /memories root) | GET /api/contexts?workspace=&tag=mem-tool / GET /api/workspaces | yes (read-only) |
create | POST /api/contexts/bulk-upsert (idempotent upsert on externalId) | yes |
delete (single file) | GET .../by-external/:id then DELETE /api/contexts/:id | yes |
delete (workspace root / prefix) | list + loop DELETE /api/contexts/:id | no -- see below |
str_replace | GET .../by-external/:id -> mutate string in-process -> PUT /api/contexts/:id | no -- see below |
insert | same as str_replace | no -- see below |
rename | GET/POST bulk-upsert (create at new path) then DELETE (old path) | no -- see below |
str_replace and insert fetch the whole context, mutate the string in
this process, then PUT the whole content back. ContextQ has no
line-indexed edit primitive and no server-side conflict detection -- a
concurrent writer on the same context can race with this adapter and one
write will silently clobber the other.
delete on a workspace root or a directory-shaped prefix loops individual
DELETE calls (ContextQ has no bulk-delete-by-path-prefix endpoint). A
failure partway through a multi-file delete leaves the remaining files
undeleted, and the thrown error surfaces that rather than swallowing it.
rename is best-effort create-then-delete: it creates a new context at
the destination path, then deletes the source. If the create step fails,
nothing changes (the source is untouched -- a safe failure). If the create
succeeds but the delete step then fails, both rows persist and
ContextQMemoryTool.rename() throws an error that says so explicitly,
naming both paths and the source row's id, rather than hiding the
duplication. There is no server-side transaction spanning both calls.
This matches docs/product/claude-surfaces-positioning.md Q1's verdict:
"buildable thin, with named gaps" -- three commands (view-whole-file,
create, delete-single) map cleanly onto existing endpoints; the other
three degrade to adapter-side string surgery with no atomicity guarantee.
The reference view command on a real filesystem lists up to two directory
levels deep, with true directory objects and byte sizes. ContextQ has no
directory object -- view on /memories/<workspace> or a sub-path lists
every mem-tool-tagged context whose stored path starts with that prefix,
flat, at its full relative path (not a nested tree), and the "size" column
shows each row's updatedAt timestamp rather than a byte count (a
ContextSummary never carries content, so a real byte size would need an
extra fetch per row). Pagination caps at one page of 500 rows.
Anthropic's context-editing feature (clear_tool_uses_20250919) can clear
old tool results -- including this memory tool's own results -- from a long
conversation once a token threshold fires. There is no automatic
exemption for the memory tool; protection requires the calling
application to explicitly set:
context_management: {
edits: [{
type: 'clear_tool_uses_20250919',
trigger: { type: 'input_tokens', value: 30_000 },
exclude_tools: ['memory'], // <- must be set by the caller
}],
}
If a tool named memory is not in exclude_tools, its results (and
therefore whatever the model most recently read back from ContextQ) can be
pruned from context like any other stale tool result. See
docs/product/claude-surfaces-positioning.md Q2 for the full writeup,
including why ContextQ's own brain-state injection has the same exposure
today and how registering as the literal memory tool (this package's
purpose) is the one path that benefits automatically from callers who
follow Anthropic's own documented exclude_tools: ["memory"] pattern.
This package implements MemoryToolHandlers
(@anthropic-ai/sdk/helpers/beta/memory) -- the same interface Anthropic's
own reference implementation, BetaLocalFilesystemMemoryTool, implements.
The TypeScript SDK does not export a class literally named
BetaAbstractMemoryTool to subclass; that shape exists in Anthropic's
Python SDK. TypeScript's idiomatic equivalent -- confirmed against the live
anthropic-sdk-typescript source, not assumed -- is exactly this: implement
MemoryToolHandlers and hand the instance to betaMemoryTool(), which
returns a BetaRunnableTool usable with client.beta.messages.toolRunner(...).
npm install
npm test # unit tests against a local fake HTTP server (node:http) -- no real ContextQ needed
npx tsc --noEmit # typecheck (src + scripts)
npm run build # emit dist/ (src only, tests excluded)
scripts/e2e-live.ts runs a real Claude Messages API conversation against a
real local ContextQ instance: Claude saves a fact via memory.create, a
second, separate turn asks it to recall the fact via memory.view, and the
script independently verifies via GET /api/contexts that the value
actually landed in ContextQ (not just that Claude said it remembered).
Requires:
ANTHROPIC_API_KEY in the environment, valid for api.anthropic.com
directly. If ANTHROPIC_BASE_URL is also set (e.g. by a repo-wide
.env.local pointing dev tooling at a gateway), the SDK honours it, and
a gateway that needs its own separate credential answers a bare
401 Unauthorized that looks exactly like a bad key. The script logs
the resolved host (anthropic base url: ...) on every run so this is
never silent -- unset it for a direct run: env -u ANTHROPIC_BASE_URL.http://localhost:38200).shared-context-postgres container (used to create
a throwaway tenant + owner user and mint an API key for them by direct
INSERT -- see below).( set -a; source path/to/.env.local; set +a
env -u ANTHROPIC_BASE_URL CONTEXT_API_URL=http://localhost:38200 npm run e2e:live )
Earlier versions of this script logged in as the repo's shared
test@local.dev fixture user. That tenant already holds 501+ contexts
against PLANS.free.quotas.contexts = 100 (src/billing/plans.ts), so
create reliably 402s with a quota error there -- not an adapter bug, just
no headroom on shared dev state other sessions have piled onto. The script
now creates a fully throwaway tenant + owner user by direct INSERT
(the same sanctioned local-dev pattern used for the API key -- see the
script's doc comments), mints one API key scoped read+write+admin for
that tenant (the admin scope lets the same key drive workspace
create/archive/delete too, so no separate JWT/cookie session is needed
anywhere in the script), and runs the whole conversation against a tenant
that starts at 0 contexts. The login-as-test@local.dev path has been
removed entirely.
Cleanup runs in the finally block regardless of pass/fail, in order:
delete the created context(s) and the workspace via the API, then the API
key row, then the user row, then -- conditionally -- the tenant row.
The tenant delete is conditional: activity_logs' tamper-evident hash
chain is global (not per-tenant), so a tenant that already produced a
chained audit row (prev_hash IS NOT NULL) during the run cannot be
hard-deleted without forking that chain for every row inserted after it.
The script checks for this (mirroring the guard in
src/test-helpers/tenant-cleanup.ts) and, when it applies, intentionally
leaves the tenant row in place and prints that it did so -- a small, always
uniquely-named, low-cardinality leaked row is the accepted trade-off, the
same one 50+ of this repo's own test files already make.
If ANTHROPIC_API_KEY is unset, the script prints a message and exits 0
rather than fabricating a result.
This package is built and tested to be published, but publishing itself
(npm publish) is not this package's job -- see the repo's task tracker
(T666) for who runs it.
FAQs
Anthropic memory tool (memory_20250818) backend adapter that stores Claude's memory files as ContextQ contexts
The npm package @contextq/memory-tool receives a total of 6 weekly downloads. As such, @contextq/memory-tool popularity was classified as not popular.
We found that @contextq/memory-tool demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Research
/Security News
Tensorlake npm SDK version 0.5.144 was compromised in a ChainDrop / Shai-Hulud attack, delivering credential-stealing malware.

Research
/Security News
Socket found 16 malicious Firefox extensions designed to steal crypto wallet recovery phrases and private keys using cloned Rabby and OKX interfaces.

Product
Socket now scans VS Code extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.