Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0d77c8f
fix(tools): isolate OpenAI middleware clients
abhinav7x94 Aug 16, 2026
7647061
fix(tools): align claude-memory insert with memory_20250818 line sema…
rohitsux Jul 18, 2026
b8b9511
fix(tools): reject parent-directory segments in Claude memory paths
aniruddhaadak80 Sep 8, 2026
db64d13
fix(tools): prevent customId collisions in claude memory tool (#1547)
Adityakk9031 Sep 17, 2026
d110df8
fix(tools): handle legacy customIds during document verification
Adityakk9031 Sep 17, 2026
4929603
fix(tools): clean up legacy customId documents during mutations to pr…
Adityakk9031 Oct 1, 2026
0d90a15
fix(tools): handle the memory tool commands Claude actually sends
Rikinshah787 Aug 25, 2026
444b4f1
fix(tools): keep the forget-memory timeout when a caller passes a signal
Agnik47 Aug 25, 2026
8233cfb
fix(mcp): keep the getDocuments timeout when a caller passes a signal
Agnik47 Oct 2, 2026
274e6b6
fix(tools): bound and harden the shared /v4/profile request
Sep 14, 2026
17ced72
fix(tools): escape delimiters in makeTurnKey to prevent cache collision
Adityakk9031 Sep 17, 2026
977b2fc
fix(tools): enforce configured container scope
shamAnimates Aug 16, 2026
8a1cf31
test(tools): type scoped operation results
shamAnimates Aug 16, 2026
4486950
test(tools): remove redundant result assertions
abhinav7x94 Aug 16, 2026
b89ed82
fix(tools): accept zero-argument OpenAI tool calls and reject non-obj…
Agnik47 Aug 28, 2026
f7e5bf2
fix(tools): handle trailing slashes and whitespace in normalizeBaseUr…
Adityakk9031 Sep 17, 2026
81f1f5c
fix(tools): clear every type error under packages/tools/test
rajarshidattapy Aug 19, 2026
b5db9a7
refactor(tools): narrow memory tool input with a type guard
rajarshidattapy Aug 19, 2026
e3d32b7
fix(tools): resolve openai-middleware and voltagent test type errors …
Adityakk9031 Oct 3, 2026
45fe24e
fix(tools): adapt community fixes to current main
MaheshtheDev Oct 3, 2026
9fd2e98
chore(tools): bump to 2.4.0
MaheshtheDev Oct 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/docs/integrations/claude-memory.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ Paths are normalized for storage: `/memories/preferences` is stored as `--memori
```typescript
{
command: "rename",
path: "/memories/old-name.txt",
old_path: "/memories/old-name.txt",
new_path: "/memories/new-name.txt"
}
```
Expand Down
66 changes: 66 additions & 0 deletions apps/mcp/src/server/client/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { afterEach, describe, expect, it, vi } from "vitest"
import { SupermemoryClient } from "./index"

const API_URL = "https://api.example.com"

describe("SupermemoryClient.getDocuments", () => {
afterEach(() => {
vi.restoreAllMocks()
vi.unstubAllGlobals()
})

function stubFetch() {
const fetchMock = vi.fn().mockResolvedValue(
Response.json({
documents: [],
pagination: {
currentPage: 1,
limit: 200,
totalItems: 0,
totalPages: 0,
},
}),
)
vi.stubGlobal("fetch", fetchMock)
return fetchMock
}

it("cancels through a caller-provided signal", async () => {
const fetchMock = stubFetch()
const controller = new AbortController()

await new SupermemoryClient("sm_test_key", "user_1", API_URL).getDocuments(
["user_1"],
1,
200,
{ signal: controller.signal },
)

const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]
controller.abort()
expect(init.signal?.aborted).toBe(true)
})

it("keeps the timeout when a caller-provided signal is present", async () => {
const timeoutController = new AbortController()
const timeoutSpy = vi
.spyOn(AbortSignal, "timeout")
.mockReturnValue(timeoutController.signal)
const fetchMock = stubFetch()

await new SupermemoryClient("sm_test_key", "user_1", API_URL).getDocuments(
["user_1"],
1,
200,
{ signal: new AbortController().signal },
)

expect(timeoutSpy).toHaveBeenCalledWith(30_000)

const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]
// Firing only the timeout leg aborts the request: a caller signal adds
// cancellation, it does not remove the 30s bound.
timeoutController.abort()
expect(init.signal?.aborted).toBe(true)
})
})
9 changes: 8 additions & 1 deletion apps/mcp/src/server/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,14 @@ export class SupermemoryClient {
options?: { signal?: AbortSignal },
): Promise<DocumentsApiResponse> {
try {
const signal = options?.signal ?? AbortSignal.timeout(FETCH_TIMEOUT_MS)
// Compose rather than choose: a caller-supplied signal must add
// cancellation on top of the timeout, not replace it.
const signal = options?.signal
? AbortSignal.any([
options.signal,
AbortSignal.timeout(FETCH_TIMEOUT_MS),
])
: AbortSignal.timeout(FETCH_TIMEOUT_MS)
const response = await fetch(`${this.apiUrl}/v3/documents/documents`, {
method: "POST",
headers: {
Expand Down
2 changes: 1 addition & 1 deletion packages/tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ The `MemoryPromptData` object provides:

#### OpenAI Middleware with Supermemory

The `withSupermemory` function creates an OpenAI client with SuperMemory middleware automatically injected:
The `withSupermemory` function creates an isolated middleware facade around an OpenAI client. It does not mutate the supplied client, so a shared base client can safely be wrapped with different user or conversation options:

```typescript
import { withSupermemory } from "@supermemory/tools/openai"
Expand Down
4 changes: 2 additions & 2 deletions packages/tools/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "@supermemory/tools",
"type": "module",
"version": "2.3.0",
"version": "2.4.0",
"description": "Memory tools for AI SDK, OpenAI, Voltagent and Mastra with supermemory",
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch --ignore-watch .turbo",
"check-types": "tsc --noEmit",
"test": "vitest --testTimeout 100000",
"test:unit": "vitest run --testTimeout 100000 src/tools-shared.test.ts src/tool-operations.test.ts src/claude-memory.test.ts test/with-supermemory/unit.test.ts test/with-supermemory/conversation-conversion.test.ts test/openai-middleware.unit.test.ts test/mastra/unit.test.ts test/voltagent.unit.test.ts",
"test:unit": "vitest run --testTimeout 100000 src/tools-shared.test.ts src/tool-operations.test.ts src/claude-memory.test.ts src/shared/memory-client.test.ts test/with-supermemory/unit.test.ts test/with-supermemory/conversation-conversion.test.ts test/openai-middleware.unit.test.ts test/mastra/unit.test.ts test/voltagent.unit.test.ts",
"test:watch": "vitest --watch --testTimeout 100000"
},
"dependencies": {
Expand Down
9 changes: 5 additions & 4 deletions packages/tools/src/ai-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
clampSearchLimit,
deleteDocumentByIdentifier,
getContainerTags,
resolveConfiguredContainerTag,
} from "./tools-shared"
import { forgetMemoryRequest } from "./shared/forget-memory"
import type { SupermemoryToolsConfig } from "./types"
Expand Down Expand Up @@ -146,7 +147,7 @@ export const getProfileTool = (
}),
execute: async ({ containerTag, query }) => {
try {
const tag = containerTag || containerTags[0]
const tag = resolveConfiguredContainerTag(containerTags, containerTag)

const response = await client.profile({
containerTag: tag,
Expand Down Expand Up @@ -199,7 +200,7 @@ export const documentListTool = (
execute: async ({ containerTag, limit, page }) => {
try {
const scopeTags: [string, ...string[]] = containerTag
? [containerTag]
? [resolveConfiguredContainerTag(containerTags, containerTag)]
: containerTags

const response = await client.documents.list({
Expand Down Expand Up @@ -339,12 +340,12 @@ export const memoryForgetTool = (
}
}

const tag = containerTag || containerTags[0]
const tag = resolveConfiguredContainerTag(containerTags, containerTag)

await forgetMemoryRequest(
apiKey,
{
containerTag: tag as string,
containerTag: tag,
...(memoryId && { id: memoryId }),
...(memoryContent && { content: memoryContent }),
...(reason && { reason }),
Expand Down
Loading
Loading