Skip to Content
ProvidersMongoDB

MongoDB

Category: Infrastructure
Integration type: Platform-level (sidecar service or Atlas)
External SDK: mongoose or mongodb (native driver)


Purpose

MongoDB is the document store for all data that doesn’t fit cleanly in relational tables — large blobs, free-form agent outputs, versioned content, and high-cardinality log entries:

CollectionWhat it stores
agent_outputsFull text content from agent runs (blog posts, briefs, strategies)
skillsPer-tenant Markdown skill files for Claude skill injection
client_context_filesThe assembled client context file per tenant
contract_pdfsContract PDF binary metadata (actual PDF in S3)
activity_logsVerbose activity execution logs (not the audit trail — that’s in PostgreSQL)
llm_responsesRaw NDJSON streams from Claude (for debugging and replay)

PostgreSQL holds the structured references (IDs, status, foreign keys). MongoDB holds the blobs those references point to.


Config Structure

Platform config (env vars)

MONGODB_URI=mongodb://mongo:27017/leadmetrics # Docker service MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/leadmetrics # Atlas

Integration Pattern

Connection (packages/db-mongo/src/client.ts)

import { MongoClient } from 'mongodb'; let client: MongoClient; export async function getMongoClient(): Promise<MongoClient> { if (!client) { client = new MongoClient(config.MONGODB_URI, { serverSelectionTimeoutMS: 5000, maxPoolSize: 20, }); await client.connect(); } return client; } export async function getDb(): Promise<Db> { const c = await getMongoClient(); return c.db(); // Uses DB name from URI }

Key collection patterns

Storing agent output:

// After Blog Writer agent completes const db = await getDb(); await db.collection('agent_outputs').insertOne({ _id: new ObjectId(), tenantId: activity.tenantId, activityId: activity.id, agentType: 'blog_writer', content: { title: output.title, html: output.html, markdown: output.markdown, wordCount: output.wordCount, }, version: 1, createdAt: new Date(), });

Storing and retrieving skills:

// Skills for a tenant (per agent type) await db.collection('skills').updateOne( { tenantId, agentType: 'blog_writer' }, { $set: { files: [ { name: 'client-context.md', content: clientContext }, { name: 'blog-sop.md', content: blogSop }, { name: 'brand-voice.md', content: brandVoice }, ], updatedAt: new Date(), }, $setOnInsert: { createdAt: new Date() }, }, { upsert: true }, );

Multi-tenant isolation

Every document includes tenantId as a string field. Indexes on tenantId ensure queries remain fast even at scale:

// Indexes created on startup await db.collection('agent_outputs').createIndex({ tenantId: 1, activityId: 1 }); await db.collection('skills').createIndex({ tenantId: 1, agentType: 1 }, { unique: true }); await db.collection('activity_logs').createIndex({ tenantId: 1, createdAt: -1 }); // TTL index for activity logs — auto-delete after 90 days await db.collection('activity_logs').createIndex( { createdAt: 1 }, { expireAfterSeconds: 60 * 60 * 24 * 90 }, );

Docker Compose Setup

# docker-compose.yml services: mongo: image: mongo:7.0 volumes: - mongo_data:/data/db ports: - "27017:27017" environment: MONGO_INITDB_ROOT_USERNAME: admin MONGO_INITDB_ROOT_PASSWORD: password volumes: mongo_data:

Test Cases

Integration tests (real MongoDB)

MongoDB tests use mongodb-memory-server for in-process testing (unlike PostgreSQL which uses a real instance):

import { MongoMemoryServer } from 'mongodb-memory-server'; let mongod: MongoMemoryServer; beforeAll(async () => { mongod = await MongoMemoryServer.create(); process.env.MONGODB_URI = mongod.getUri(); await connect(); }); afterAll(async () => { await mongod.stop(); });
TestApproach
Upsert skill for tenantInsert twice with same tenantId + agentType; assert single doc
Agent output stored and retrieved by activityIdInsert; query by activityId; assert content matches
TTL index on activity_logsVerify index exists with expireAfterSeconds: 7776000
Tenant isolationInsert docs for two tenants; query with tenantId = A; assert B’s docs not returned

© 2026 Leadmetrics — Internal use only