feat(mongodb): add requested docs and minimal usage example#1127
feat(mongodb): add requested docs and minimal usage example#1127GanpatJangra wants to merge 9 commits intoVoltAgent:mainfrom
Conversation
🦋 Changeset detectedLatest commit: 548468c The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughThis pull request introduces a new MongoDB-based memory storage adapter package ( Changes
Sequence Diagram(s)sequenceDiagram
participant App as Application
participant Adapter as MongoDBMemoryAdapter
participant Mongo as MongoDB
App->>Adapter: new MongoDBMemoryAdapter(options)
Adapter->>Mongo: connect(uri)
Adapter->>Mongo: create indexes<br/>(conversations, messages, etc.)
Adapter-->>App: initialized
Note over App,Mongo: Typical Usage Flow
App->>Adapter: createConversation(input)
Adapter->>Mongo: insert conversation doc
Mongo-->>Adapter: acknowledgment
Adapter-->>App: Conversation object
App->>Adapter: addMessage(message, userId, conversationId)
Adapter->>Mongo: insert message doc
Mongo-->>Adapter: acknowledgment
Adapter-->>App: void
App->>Adapter: getMessages(userId, conversationId)
Adapter->>Mongo: query messages<br/>(with filters/pagination)
Mongo-->>Adapter: message array
Adapter-->>App: UIMessage[]
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
1 issue found across 18 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/mongodb/src/memory-adapter.ts">
<violation number="1" location="packages/mongodb/src/memory-adapter.ts:87">
P2: Initialization failure is sticky: one transient MongoDB startup failure leaves this adapter permanently unusable because `initPromise` is never reset/retried.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Add one-off context when rerunning by tagging
@cubic-dev-aiwith guidance or docs links (includingllms.txt) - Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| this.log("MongoDB Memory adapter initialized"); | ||
|
|
||
| // Start initialization but don't await it | ||
| this.initPromise = this.initialize(); |
There was a problem hiding this comment.
P2: Initialization failure is sticky: one transient MongoDB startup failure leaves this adapter permanently unusable because initPromise is never reset/retried.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/mongodb/src/memory-adapter.ts, line 87:
<comment>Initialization failure is sticky: one transient MongoDB startup failure leaves this adapter permanently unusable because `initPromise` is never reset/retried.</comment>
<file context>
@@ -0,0 +1,1024 @@
+ this.log("MongoDB Memory adapter initialized");
+
+ // Start initialization but don't await it
+ this.initPromise = this.initialize();
+ }
+
</file context>
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (2)
packages/mongodb/package.json (1)
51-51: Replace fixed startup sleep with health-based readiness wait.
Line 51 uses a hardcoded delay, which is prone to flaky startup on slower runners.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mongodb/package.json` at line 51, The package.json script "test:integration:setup" currently uses a fixed "sleep 10" which is flaky; replace that sleep with a health-based readiness wait (either call an existing wait-for utility or add a small script that polls container health/status) so the command becomes: bring up compose with "docker compose -f docker-compose.test.yaml up -d" and then block until the target service(s) report healthy (poll via "docker compose ps" health status or TCP probe) before exiting; update the "test:integration:setup" script to invoke that health-wait mechanism instead of sleep.packages/mongodb/src/index.integration.test.ts (1)
32-45: Ensure MongoClient is always closed in cleanup path.If collection cleanup throws, Line 44 is skipped and the client can leak across tests. Wrap cleanup in
try/finally.Suggested change
const { MongoClient } = await import("mongodb"); const client = new MongoClient(MONGO_URI); - await client.connect(); - const db = client.db(TEST_DATABASE); - - const collections = await db.listCollections().toArray(); - for (const collection of collections) { - if (collection.name.startsWith("test_memory_")) { - await db.collection(collection.name).deleteMany({}); - } - } - - await client.close(); + try { + await client.connect(); + const db = client.db(TEST_DATABASE); + + const collections = await db.listCollections().toArray(); + for (const collection of collections) { + if (collection.name.startsWith("test_memory_")) { + await db.collection(collection.name).deleteMany({}); + } + } + } finally { + await client.close(); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mongodb/src/index.integration.test.ts` around lines 32 - 45, The cleanup loop that lists collections and calls db.collection(...).deleteMany(...) can throw and currently may skip client.close(), so wrap the collection cleanup in a try/finally around the MongoClient lifecycle: after creating and connecting the MongoClient (MongoClient, client.connect(), MONGO_URI, TEST_DATABASE, db.listCollections()), perform the deletion logic inside a try block and call client.close() in the finally block to guarantee the client is always closed even if deleteMany or listCollections throws.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/mongodb/docker-compose.test.yaml`:
- Around line 4-7: The docker-compose test file hardcodes container_name and
host port mapping (container_name: 'voltagent-mongodb-test' and ports: -
'27017:27017'), causing global collisions in concurrent/local CI runs; remove
the fixed container_name and avoid binding the container port to the host by
either deleting the container_name line and changing ports to expose only the
container port (e.g., "27017" or no host mapping) or make the host port
configurable using an env var (e.g., replace '27017:27017' with
"${MONGO_PORT:-27017}:27017" or use project-scoped names like
"${COMPOSE_PROJECT_NAME:-voltagent}-mongodb-test") so tests can run in parallel
without collisions.
In `@packages/mongodb/package.json`:
- Around line 49-53: The integration script currently uses "&&" so
test:integration:teardown only runs on successful tests; change test:integration
to run test:integration:setup, run the vitest command inside a shell that
captures its exit code, always runs npm run test:integration:teardown, then
exits with the original vitest code. Update the "test:integration" script
(referencing test:integration, test:integration:setup, and
test:integration:teardown) to wrap the vitest invocation so teardown always
executes even when tests fail and the process returns the original test exit
status.
In `@packages/mongodb/src/index.integration.test.ts`:
- Around line 26-28: Replace the fixed 1s sleep (the new Promise with
setTimeout) with a deterministic wait for the component to be ready: remove the
setTimeout-based wait and instead await a real readiness check — either call and
await the module's exported initializer (e.g., initializeMongo or connectMongo)
if available, or poll the Mongo client until a successful response (e.g., await
client.connect() or loop until client.db().admin().ping() succeeds) with a short
interval and timeout; update the test that contains the setTimeout promise to
use that deterministic await so the test only proceeds when the DB is actually
initialized.
- Around line 390-404: Tests currently use the unsafe any type for test fixtures
(e.g., the state object and conversation step arrays); import the proper types
from `@voltagent/core` (WorkflowStateEntry and ConversationStepRecord) and
annotate the test variables instead of any (or use the exported parameter types
like Parameters<MongoDBMemoryAdapter["setWorkflowState"]>[1] and
Parameters<MongoDBMemoryAdapter["saveConversationSteps"]>[0][number] if you
prefer). Update the declarations for the state fixture (previously typed as any)
to WorkflowStateEntry and the conversation step fixtures to
ConversationStepRecord, and add the corresponding import statements from
`@voltagent/core` so the integration tests maintain compile-time type safety.
In `@packages/mongodb/src/memory-adapter.spec.ts`:
- Around line 56-58: Remove the unnecessary private method call: delete the
explicit await (adapter as any).initialize() and instead rely on the
constructor-triggered initialization (this.initPromise = this.initialize()) —
either simply assert expect(MongoClient).toHaveBeenCalledTimes(1) after
instantiation, or if you need to wait for completion, await the adapter's
initialization promise (access via adapter.initPromise or
adapter['initPromise']) before asserting; this preserves type safety and avoids
casting to any.
In `@packages/mongodb/src/memory-adapter.ts`:
- Around line 203-207: Ensure we enforce conversation ownership before inserting
messages: after fetching the conversation via
this.getConversation(conversationId) (used in the insert message flow), verify
that conversation.userId === userId and if not, throw a suitable error (e.g.,
ConversationNotFoundError or an AuthorizationError) to prevent cross-user
writes; apply the same ownership check in the other analogous block around the
second getConversation call (the one referenced at 244-248) so both insertion
paths validate userId matches the conversation.userId before proceeding.
- Around line 381-443: The code uses many `any` casts (e.g.,
getCollection<any>("conversations"), insert/find results cast with `as any`,
filter declarations typed `: any`, map callbacks `(item: any)`, and update
result casts) which breaks TypeScript safety; replace these with concrete types
imported from `@voltagent/core` and mongodb: use
getCollection<ConversationRecord>("conversations") (and analogous
ConversationStepRecord, WorkflowStateEntry, etc.), use WithId<T> or Document
where appropriate for query results instead of `as any`, type filter objects as
Partial<ConversationRecord> or the exact filter shape, type map callbacks with
the correct record type, and use mongodb types like UpdateResult<T> for updates;
ensure the necessary types are imported and remove all `any` casts so
insertOne/findOne/countDocuments/updateOne usages and their mappings return
properly typed values (e.g., in createConversation, countConversations,
getConversation, update handlers, and workflow state queries).
In `@website/docs/agents/memory/mongodb.md`:
- Around line 41-57: Update the example to validate the required MONGODB_URI
before constructing MongoDBMemoryAdapter: check process.env.MONGODB_URI (used in
MongoDBMemoryAdapter) and throw or log a clear error if missing, or provide a
brief inline comment noting it is required; ensure the validation happens prior
to new Memory({ storage: new MongoDBMemoryAdapter(...) }) so Agent and Memory
construction use a guaranteed non-null connection string.
---
Nitpick comments:
In `@packages/mongodb/package.json`:
- Line 51: The package.json script "test:integration:setup" currently uses a
fixed "sleep 10" which is flaky; replace that sleep with a health-based
readiness wait (either call an existing wait-for utility or add a small script
that polls container health/status) so the command becomes: bring up compose
with "docker compose -f docker-compose.test.yaml up -d" and then block until the
target service(s) report healthy (poll via "docker compose ps" health status or
TCP probe) before exiting; update the "test:integration:setup" script to invoke
that health-wait mechanism instead of sleep.
In `@packages/mongodb/src/index.integration.test.ts`:
- Around line 32-45: The cleanup loop that lists collections and calls
db.collection(...).deleteMany(...) can throw and currently may skip
client.close(), so wrap the collection cleanup in a try/finally around the
MongoClient lifecycle: after creating and connecting the MongoClient
(MongoClient, client.connect(), MONGO_URI, TEST_DATABASE, db.listCollections()),
perform the deletion logic inside a try block and call client.close() in the
finally block to guarantee the client is always closed even if deleteMany or
listCollections throws.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (17)
.changeset/mongodb-feature-storage.mdpackages/mongodb/README.mdpackages/mongodb/docker-compose.test.yamlpackages/mongodb/package.jsonpackages/mongodb/src/index.integration.test.tspackages/mongodb/src/index.tspackages/mongodb/src/memory-adapter.spec.tspackages/mongodb/src/memory-adapter.tspackages/mongodb/tsconfig.jsonpackages/mongodb/tsup.config.tspackages/mongodb/vitest.config.mtspackages/mongodb/vitest.integration.config.mtswebsite/docs/agents/memory.mdwebsite/docs/agents/memory/mongodb.mdwebsite/docs/agents/memory/overview.mdwebsite/sidebars.tswebsite/static/llms.txt
| container_name: 'voltagent-mongodb-test' | ||
| ports: | ||
| - '27017:27017' | ||
| environment: |
There was a problem hiding this comment.
Avoid global container/port collisions in test compose setup.
Line 4 and Line 6 hardcode a shared container name and host port, which can break local/CI runs when MongoDB is already bound or jobs run concurrently.
Suggested adjustment
- container_name: 'voltagent-mongodb-test'
ports:
- - '27017:27017'
+ - '${MONGODB_TEST_PORT:-27017}:27017'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| container_name: 'voltagent-mongodb-test' | |
| ports: | |
| - '27017:27017' | |
| environment: | |
| ports: | |
| - '${MONGODB_TEST_PORT:-27017}:27017' | |
| environment: |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/mongodb/docker-compose.test.yaml` around lines 4 - 7, The
docker-compose test file hardcodes container_name and host port mapping
(container_name: 'voltagent-mongodb-test' and ports: - '27017:27017'), causing
global collisions in concurrent/local CI runs; remove the fixed container_name
and avoid binding the container port to the host by either deleting the
container_name line and changing ports to expose only the container port (e.g.,
"27017" or no host mapping) or make the host port configurable using an env var
(e.g., replace '27017:27017' with "${MONGO_PORT:-27017}:27017" or use
project-scoped names like "${COMPOSE_PROJECT_NAME:-voltagent}-mongodb-test") so
tests can run in parallel without collisions.
| "test:integration": "npm run test:integration:setup && vitest run --config vitest.integration.config.mts && npm run test:integration:teardown", | ||
| "test:integration:ci": "vitest run --config vitest.integration.config.mts", | ||
| "test:integration:setup": "docker compose -f docker-compose.test.yaml up -d && sleep 10", | ||
| "test:integration:teardown": "docker compose -f docker-compose.test.yaml down -v" | ||
| }, |
There was a problem hiding this comment.
Ensure integration teardown runs even when tests fail.
Line 49 currently skips teardown on test failure, leaving containers/volumes behind.
Suggested fix
- "test:integration": "npm run test:integration:setup && vitest run --config vitest.integration.config.mts && npm run test:integration:teardown",
+ "test:integration": "sh -c 'npm run test:integration:setup; code=0; vitest run --config vitest.integration.config.mts || code=$?; npm run test:integration:teardown; exit $code'",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "test:integration": "npm run test:integration:setup && vitest run --config vitest.integration.config.mts && npm run test:integration:teardown", | |
| "test:integration:ci": "vitest run --config vitest.integration.config.mts", | |
| "test:integration:setup": "docker compose -f docker-compose.test.yaml up -d && sleep 10", | |
| "test:integration:teardown": "docker compose -f docker-compose.test.yaml down -v" | |
| }, | |
| "test:integration": "sh -c 'npm run test:integration:setup; code=0; vitest run --config vitest.integration.config.mts || code=$?; npm run test:integration:teardown; exit $code'", | |
| "test:integration:ci": "vitest run --config vitest.integration.config.mts", | |
| "test:integration:setup": "docker compose -f docker-compose.test.yaml up -d && sleep 10", | |
| "test:integration:teardown": "docker compose -f docker-compose.test.yaml down -v" | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/mongodb/package.json` around lines 49 - 53, The integration script
currently uses "&&" so test:integration:teardown only runs on successful tests;
change test:integration to run test:integration:setup, run the vitest command
inside a shell that captures its exit code, always runs npm run
test:integration:teardown, then exits with the original vitest code. Update the
"test:integration" script (referencing test:integration, test:integration:setup,
and test:integration:teardown) to wrap the vitest invocation so teardown always
executes even when tests fail and the process returns the original test exit
status.
| // Wait for initialization | ||
| await new Promise((resolve) => setTimeout(resolve, 1000)); | ||
| }); |
There was a problem hiding this comment.
Replace fixed sleep with deterministic initialization.
Line 27 relies on timing (setTimeout(1000)), which makes integration tests flaky under slower CI environments.
Suggested change
- // Wait for initialization
- await new Promise((resolve) => setTimeout(resolve, 1000));
+ // Trigger a lightweight call that awaits internal initialization
+ await adapter.getConversation("__init_probe__");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Wait for initialization | |
| await new Promise((resolve) => setTimeout(resolve, 1000)); | |
| }); | |
| // Trigger a lightweight call that awaits internal initialization | |
| await adapter.getConversation("__init_probe__"); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/mongodb/src/index.integration.test.ts` around lines 26 - 28, Replace
the fixed 1s sleep (the new Promise with setTimeout) with a deterministic wait
for the component to be ready: remove the setTimeout-based wait and instead
await a real readiness check — either call and await the module's exported
initializer (e.g., initializeMongo or connectMongo) if available, or poll the
Mongo client until a successful response (e.g., await client.connect() or loop
until client.db().admin().ping() succeeds) with a short interval and timeout;
update the test that contains the setTimeout promise to use that deterministic
await so the test only proceeds when the DB is actually initialized.
| const state: any = { | ||
| id: "exec-1", | ||
| workflowId: "workflow-1", | ||
| workflowName: "Test Workflow", | ||
| status: "running", | ||
| suspension: null, | ||
| events: [], | ||
| output: null, | ||
| cancellation: null, | ||
| userId: "user-1", | ||
| conversationId: "conv-1", | ||
| metadata: {}, | ||
| createdAt: new Date(), | ||
| updatedAt: new Date(), | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if the file exists and get line count
wc -l packages/mongodb/src/index.integration.test.tsRepository: VoltAgent/voltagent
Length of output: 113
🏁 Script executed:
# Read the specific sections with any usage mentioned
sed -n '385,580p' packages/mongodb/src/index.integration.test.tsRepository: VoltAgent/voltagent
Length of output: 5641
🏁 Script executed:
# Find the MongoDBMemoryAdapter class definition
fd -t f "\.ts$" packages/mongodb/src | grep -v test | head -20Repository: VoltAgent/voltagent
Length of output: 175
🏁 Script executed:
# Check the memory-adapter.ts file to see method signatures
wc -l packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 106
🏁 Script executed:
# Search for the method signatures
rg "setWorkflowState|saveConversationSteps" packages/mongodb/src/memory-adapter.ts -A 3 -B 1Repository: VoltAgent/voltagent
Length of output: 417
🏁 Script executed:
# Check the beginning of the test file to see imports
head -n 50 packages/mongodb/src/index.integration.test.tsRepository: VoltAgent/voltagent
Length of output: 1612
🏁 Script executed:
# Check what's exported from memory-adapter.ts
rg "export.*WorkflowStateEntry|export.*ConversationStepRecord" packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 45
🏁 Script executed:
# Search for type definitions in the file
rg "type WorkflowStateEntry|interface WorkflowStateEntry|type ConversationStepRecord|interface ConversationStepRecord" packages/mongodb/src/memory-adapter.ts -B 2Repository: VoltAgent/voltagent
Length of output: 45
🏁 Script executed:
# Check the index.ts file for type exports
cat packages/mongodb/src/index.tsRepository: VoltAgent/voltagent
Length of output: 181
🏁 Script executed:
# Search for WorkflowStateEntry and ConversationStepRecord types more broadly
rg "WorkflowStateEntry|ConversationStepRecord" packages/mongodb/src/memory-adapter.ts | head -20Repository: VoltAgent/voltagent
Length of output: 619
🏁 Script executed:
# Check imports in memory-adapter.ts
head -n 30 packages/mongodb/src/memory-adapter.ts | grep -E "^import|^export"Repository: VoltAgent/voltagent
Length of output: 326
🏁 Script executed:
# Get the first 50 lines to see all imports
sed -n '1,50p' packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 1247
🏁 Script executed:
# Check if the types can be imported from `@voltagent/core`
rg "@voltagent/core" packages/mongodb/src/index.integration.test.tsRepository: VoltAgent/voltagent
Length of output: 155
🏁 Script executed:
# Check if the types are publicly available from `@voltagent/core`
# Let's look for where they're exported
fd -t f "\.ts$" | xargs grep -l "export.*WorkflowStateEntry\|export.*ConversationStepRecord" | head -5Repository: VoltAgent/voltagent
Length of output: 123
🏁 Script executed:
# Check what's exported from `@voltagent/core`
rg "export.*WorkflowStateEntry|export.*ConversationStepRecord" packages/core/src/index.tsRepository: VoltAgent/voltagent
Length of output: 153
🏁 Script executed:
# Check for WorkflowStateEntry export
rg "export.*WorkflowStateEntry" packages/core/src/index.tsRepository: VoltAgent/voltagent
Length of output: 45
Remove any from integration tests to preserve type safety.
Lines 390, 416, 449, 476, 508, 563, and 573 use any, which defeats compile-time type guarantees in a TypeScript-first codebase. Import WorkflowStateEntry and ConversationStepRecord from @voltagent/core and use them directly instead of relying on type assertions or the any type.
The suggested approach using Parameters<MongoDBMemoryAdapter["setWorkflowState"]>[1] and Parameters<MongoDBMemoryAdapter["saveConversationSteps"]>[0][number] is valid, but the types can be imported directly since they're already exported from @voltagent/core.
Suggested change
-import { ConversationAlreadyExistsError, ConversationNotFoundError } from "@voltagent/core";
+import { ConversationAlreadyExistsError, ConversationNotFoundError, WorkflowStateEntry, ConversationStepRecord } from "@voltagent/core";
- const state: any = {
+ const state: WorkflowStateEntry = {
id: "exec-1",
...
};
- const steps: any[] = [
+ const steps: ConversationStepRecord[] = [
...
];
- } as any);
+ } satisfies WorkflowStateEntry);
- } as any,
+ } satisfies ConversationStepRecord,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/mongodb/src/index.integration.test.ts` around lines 390 - 404, Tests
currently use the unsafe any type for test fixtures (e.g., the state object and
conversation step arrays); import the proper types from `@voltagent/core`
(WorkflowStateEntry and ConversationStepRecord) and annotate the test variables
instead of any (or use the exported parameter types like
Parameters<MongoDBMemoryAdapter["setWorkflowState"]>[1] and
Parameters<MongoDBMemoryAdapter["saveConversationSteps"]>[0][number] if you
prefer). Update the declarations for the state fixture (previously typed as any)
to WorkflowStateEntry and the conversation step fixtures to
ConversationStepRecord, and add the corresponding import statements from
`@voltagent/core` so the integration tests maintain compile-time type safety.
| it("should initialize correctly", async () => { | ||
| await (adapter as any).initialize(); | ||
| expect(MongoClient).toHaveBeenCalledTimes(1); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify unsafe `any` usage and internal initialize access patterns in MongoDB tests.
rg -n --type=ts -C2 '\bas any\b|:\s*any\b' packages/mongodb/srcRepository: VoltAgent/voltagent
Length of output: 20865
🏁 Script executed:
#!/bin/bash
rg -n --type=ts -C2 '\binitialize\s*\(' packages/mongodb/src/memory-adapter.spec.ts packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 1090
🏁 Script executed:
#!/bin/bash
# Get full context of the memory-adapter.spec.ts file around the flagged lines
cat -n packages/mongodb/src/memory-adapter.spec.ts | head -80Repository: VoltAgent/voltagent
Length of output: 2305
🏁 Script executed:
#!/bin/bash
# Check the memory-adapter.ts implementation
head -100 packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 2595
Remove unnecessary any cast—initialization is triggered automatically in constructor.
Line 57 unnecessarily casts to any to call the private initialize() method. Since the constructor already calls this.initPromise = this.initialize() (line 87 of memory-adapter.ts), the explicit call is redundant. The test can verify initialization simply by checking that MongoClient was called once after adapter instantiation, without bypassing type safety.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/mongodb/src/memory-adapter.spec.ts` around lines 56 - 58, Remove the
unnecessary private method call: delete the explicit await (adapter as
any).initialize() and instead rely on the constructor-triggered initialization
(this.initPromise = this.initialize()) — either simply assert
expect(MongoClient).toHaveBeenCalledTimes(1) after instantiation, or if you need
to wait for completion, await the adapter's initialization promise (access via
adapter.initPromise or adapter['initPromise']) before asserting; this preserves
type safety and avoids casting to any.
| // Ensure conversation exists | ||
| const conversation = await this.getConversation(conversationId); | ||
| if (!conversation) { | ||
| throw new ConversationNotFoundError(conversationId); | ||
| } |
There was a problem hiding this comment.
Enforce conversation ownership before inserting messages.
Line 204 and Line 245 only check that the conversation exists. They should also verify conversation.userId === userId to prevent cross-user writes into another user’s conversation ID.
Suggested fix
- const conversation = await this.getConversation(conversationId);
- if (!conversation) {
+ const conversation = await this.getConversation(conversationId);
+ if (!conversation || conversation.userId !== userId) {
throw new ConversationNotFoundError(conversationId);
}Also applies to: 244-248
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/mongodb/src/memory-adapter.ts` around lines 203 - 207, Ensure we
enforce conversation ownership before inserting messages: after fetching the
conversation via this.getConversation(conversationId) (used in the insert
message flow), verify that conversation.userId === userId and if not, throw a
suitable error (e.g., ConversationNotFoundError or an AuthorizationError) to
prevent cross-user writes; apply the same ownership check in the other analogous
block around the second getConversation call (the one referenced at 244-248) so
both insertion paths validate userId matches the conversation.userId before
proceeding.
| const conversationsCollection = this.getCollection<any>("conversations"); | ||
|
|
||
| const now = new Date(); | ||
| const conversation = { | ||
| _id: input.id, | ||
| resourceId: input.resourceId, | ||
| userId: input.userId, | ||
| title: input.title, | ||
| metadata: input.metadata || {}, | ||
| createdAt: now, | ||
| updatedAt: now, | ||
| }; | ||
|
|
||
| try { | ||
| await conversationsCollection.insertOne(conversation); | ||
| } catch (error: any) { | ||
| if (error.code === 11000) { | ||
| throw new ConversationAlreadyExistsError(input.id); | ||
| } | ||
| throw error; | ||
| } | ||
|
|
||
| this.log(`Created conversation ${input.id}`); | ||
|
|
||
| return { | ||
| id: conversation._id, | ||
| resourceId: conversation.resourceId, | ||
| userId: conversation.userId, | ||
| title: conversation.title, | ||
| metadata: conversation.metadata, | ||
| createdAt: conversation.createdAt.toISOString(), | ||
| updatedAt: conversation.updatedAt.toISOString(), | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Count conversations based on filters | ||
| */ | ||
| async countConversations(options: ConversationQueryOptions): Promise<number> { | ||
| await this.initPromise; | ||
|
|
||
| const conversationsCollection = this.getCollection("conversations"); | ||
| const filter: any = {}; | ||
|
|
||
| if (options.userId) { | ||
| filter.userId = options.userId; | ||
| } | ||
|
|
||
| if (options.resourceId) { | ||
| filter.resourceId = options.resourceId; | ||
| } | ||
|
|
||
| return conversationsCollection.countDocuments(filter); | ||
| } | ||
|
|
||
| /** | ||
| * Get a conversation by ID | ||
| */ | ||
| async getConversation(id: string): Promise<Conversation | null> { | ||
| await this.initPromise; | ||
|
|
||
| const conversationsCollection = this.getCollection("conversations"); | ||
| const conversation = await conversationsCollection.findOne({ _id: id } as any); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -nP --type=ts '\bas\s+any\b|:\s*any\b|getCollection<any>' packages/mongodb/src/memory-adapter.ts | head -50Repository: VoltAgent/voltagent
Length of output: 2767
🏁 Script executed:
wc -l packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 106
🏁 Script executed:
sed -n '381,443p' packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 1767
🏁 Script executed:
sed -n '545,585p' packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 1111
🏁 Script executed:
sed -n '668,702p' packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 1006
🏁 Script executed:
sed -n '839,915p' packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 2174
🏁 Script executed:
rg -nP 'import.*(?:Conversation|Message|Step|WorkflowState|type)' packages/mongodb/src/memory-adapter.ts | head -20Repository: VoltAgent/voltagent
Length of output: 296
🏁 Script executed:
rg -n 'JSON\.stringify' packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 45
🏁 Script executed:
head -80 packages/mongodb/src/memory-adapter.tsRepository: VoltAgent/voltagent
Length of output: 2176
🏁 Script executed:
grep -n 'interface\|type' packages/mongodb/src/memory-adapter.ts | head -20Repository: VoltAgent/voltagent
Length of output: 306
Remove any type usage to preserve type safety across adapter methods.
The file contains 48+ instances of any usage (via getCollection<any>, as any casts, and : any annotations) that undermine compile-time safety, despite proper type definitions being imported from @voltagent/core (Conversation, ConversationStepRecord, WorkflowStateEntry, etc.) and mongodb library. This violates the coding guideline for TypeScript-first codebases.
Affected locations:
- Line 381:
getCollection<any>("conversations") - Lines 443, 450-456: Query results and object mapping with
as any/(result as any)casts - Line 423, 501, 545: Filter objects with
: anyannotations - Lines 468, 472, 684: Map callbacks with
(item: any)parameters - Lines 566, 578-584: Multiple casts on update results
- Lines 840, 847-848: Workflow state queries with redundant
as anycasts
Replace with properly typed generics and mapped types to ensure schema consistency and catch mismatches at compile time.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/mongodb/src/memory-adapter.ts` around lines 381 - 443, The code uses
many `any` casts (e.g., getCollection<any>("conversations"), insert/find results
cast with `as any`, filter declarations typed `: any`, map callbacks `(item:
any)`, and update result casts) which breaks TypeScript safety; replace these
with concrete types imported from `@voltagent/core` and mongodb: use
getCollection<ConversationRecord>("conversations") (and analogous
ConversationStepRecord, WorkflowStateEntry, etc.), use WithId<T> or Document
where appropriate for query results instead of `as any`, type filter objects as
Partial<ConversationRecord> or the exact filter shape, type map callbacks with
the correct record type, and use mongodb types like UpdateResult<T> for updates;
ensure the necessary types are imported and remove all `any` casts so
insertOne/findOne/countDocuments/updateOne usages and their mappings return
properly typed values (e.g., in createConversation, countConversations,
getConversation, update handlers, and workflow state queries).
| ```ts | ||
| import { Agent, Memory } from "@voltagent/core"; | ||
| import { MongoDBMemoryAdapter } from "@voltagent/mongodb"; | ||
|
|
||
| const memory = new Memory({ | ||
| storage: new MongoDBMemoryAdapter({ | ||
| connection: process.env.MONGODB_URI!, | ||
| database: process.env.MONGODB_DATABASE ?? "voltagent", | ||
| }), | ||
| }); | ||
|
|
||
| const agent = new Agent({ | ||
| name: "Assistant", | ||
| model: "openai/gpt-4o-mini", | ||
| memory, | ||
| }); | ||
| ``` |
There was a problem hiding this comment.
Consider adding validation for required environment variable.
The code example uses process.env.MONGODB_URI! with a non-null assertion (line 47), which will silently pass undefined to the adapter if the environment variable isn't set, potentially causing a confusing runtime error. While this keeps the example concise, consider one of these approaches for better user experience:
- Add inline validation with a clear error message
- Add a comment indicating this variable is required
- Demonstrate basic error handling
This would help users understand the requirement upfront rather than encountering cryptic connection errors.
💡 Example with validation
+if (!process.env.MONGODB_URI) {
+ throw new Error("MONGODB_URI environment variable is required");
+}
+
const memory = new Memory({
storage: new MongoDBMemoryAdapter({
- connection: process.env.MONGODB_URI!,
+ connection: process.env.MONGODB_URI,
database: process.env.MONGODB_DATABASE ?? "voltagent",
}),
});Or at minimum, add a comment:
const memory = new Memory({
storage: new MongoDBMemoryAdapter({
+ // MONGODB_URI must be set in environment variables
connection: process.env.MONGODB_URI!,
database: process.env.MONGODB_DATABASE ?? "voltagent",
}),
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@website/docs/agents/memory/mongodb.md` around lines 41 - 57, Update the
example to validate the required MONGODB_URI before constructing
MongoDBMemoryAdapter: check process.env.MONGODB_URI (used in
MongoDBMemoryAdapter) and throw or log a clear error if missing, or provide a
brief inline comment noting it is required; ensure the validation happens prior
to new Memory({ storage: new MongoDBMemoryAdapter(...) }) so Agent and Memory
construction use a guaranteed non-null connection string.
|
@omeraplak Please can you please take a look and merge it. |
Summary
This branch is based on the current head of #1000 and adds the remaining requested documentation updates so it is easier to review and merge as a complete MongoDB memory adapter contribution.
Base branch for this PR:
UmeshpJadhav:feat/mongodb-memory-storagefrom #1000Additional commit on top of #1000:
548468c6docs: update MongoDB memory documentation and add new MongoDB adapter pageWhat was added
packages/mongodb/README.mdwebsite/static/llms.txtto mention the MongoDB adapterFiles
website/docs/agents/memory/overview.mdwebsite/docs/agents/memory/mongodb.mdwebsite/docs/agents/memory.mdwebsite/sidebars.tspackages/mongodb/README.mdwebsite/static/llms.txtNotes
This PR is intended to help complete the remaining requested docs work from #1000:
If preferred, maintainers can also cherry-pick the docs commit from this branch onto #1000.
Summary by cubic
Adds a new MongoDB memory adapter with full implementation, tests, and docs. This enables persistent conversations, steps, workflow state, and working memory on self‑hosted MongoDB or Atlas.
New Features
Docs
Written for commit 548468c. Summary will update on new commits.
Summary by CodeRabbit
Release Notes
New Features
Documentation