Skip to content

Commit aae3b48

Browse files
committed
feat: MongoMemory — drop-in persistent Memory backend for mcp-agent + Atlas Vector Search recall
0 parents  commit aae3b48

19 files changed

Lines changed: 1676 additions & 0 deletions

File tree

.claude/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"enabledPlugins": {
3+
"mongodb@claude-plugins-official": true
4+
}
5+
}

.github/workflows/ci.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
workflow_dispatch:
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
python-version: ["3.10", "3.11", "3.12"]
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
24+
- name: Install package + dev deps
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -e ".[dev]"
28+
29+
- name: Run acceptance suite (offline; Atlas handshake test auto-skips)
30+
env:
31+
# ATLAS_URI is intentionally unset in CI → the live handshake test skips.
32+
# Set it as a repo secret to exercise the Atlas path.
33+
ATLAS_URI: ${{ secrets.ATLAS_URI }}
34+
run: pytest tests/ -v
35+
36+
build:
37+
runs-on: ubuntu-latest
38+
steps:
39+
- uses: actions/checkout@v4
40+
- uses: actions/setup-python@v5
41+
with:
42+
python-version: "3.12"
43+
- name: Build sdist + wheel
44+
run: |
45+
python -m pip install --upgrade pip build twine
46+
python -m build
47+
python -m twine check dist/*

.github/workflows/release.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Release
2+
3+
# Publishes to PyPI via Trusted Publishing (OIDC) — no API tokens stored.
4+
# Trigger: push a version tag (e.g. v0.1.0) OR run manually for a TestPyPI dry-run.
5+
#
6+
# One-time setup on PyPI (and TestPyPI) → Project → Publishing → "Add a pending publisher":
7+
# Owner: mongodb-developer
8+
# Repository: mcp-agent-mongodb
9+
# Workflow: release.yml
10+
# Environment: pypi (and testpypi for the test index)
11+
12+
on:
13+
push:
14+
tags: ["v*"]
15+
workflow_dispatch:
16+
inputs:
17+
target:
18+
description: "Where to publish"
19+
type: choice
20+
default: testpypi
21+
options: [testpypi, pypi]
22+
23+
permissions:
24+
contents: read
25+
26+
jobs:
27+
build:
28+
runs-on: ubuntu-latest
29+
steps:
30+
- uses: actions/checkout@v4
31+
- uses: actions/setup-python@v5
32+
with:
33+
python-version: "3.12"
34+
- name: Build distributions
35+
run: |
36+
python -m pip install --upgrade pip build twine
37+
python -m build
38+
python -m twine check dist/*
39+
- uses: actions/upload-artifact@v4
40+
with:
41+
name: dist
42+
path: dist/
43+
44+
publish-testpypi:
45+
# Only on manual dry-runs; tag pushes go straight to PyPI (publish-pypi).
46+
if: github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'testpypi'
47+
48+
needs: build
49+
runs-on: ubuntu-latest
50+
environment: testpypi
51+
permissions:
52+
id-token: write # required for Trusted Publishing (OIDC)
53+
steps:
54+
- uses: actions/download-artifact@v4
55+
with:
56+
name: dist
57+
path: dist/
58+
- name: Publish to TestPyPI
59+
uses: pypa/gh-action-pypi-publish@release/v1
60+
with:
61+
repository-url: https://test.pypi.org/legacy/
62+
63+
publish-pypi:
64+
if: startsWith(github.ref, 'refs/tags/v') || github.event.inputs.target == 'pypi'
65+
needs: build
66+
runs-on: ubuntu-latest
67+
environment: pypi
68+
permissions:
69+
id-token: write # required for Trusted Publishing (OIDC)
70+
steps:
71+
- uses: actions/download-artifact@v4
72+
with:
73+
name: dist
74+
path: dist/
75+
- name: Publish to PyPI
76+
uses: pypa/gh-action-pypi-publish@release/v1

.gitignore

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Internal build plan & helper artifacts — not shipped with the package/repo
2+
PLAN.md
3+
4+
# Internal outreach drafts — not part of the public package/repo
5+
outreach/
6+
7+
# Secrets / local env
8+
.env
9+
demo/.env
10+
11+
# Virtual environments
12+
.venv/
13+
venv/
14+
env/
15+
16+
# Python build / packaging artifacts
17+
build/
18+
dist/
19+
*.egg-info/
20+
*.egg
21+
pip-wheel-metadata/
22+
23+
# Caches & bytecode
24+
__pycache__/
25+
*.py[cod]
26+
.pytest_cache/
27+
.mypy_cache/
28+
.ruff_cache/
29+
.coverage
30+
htmlcov/
31+
32+
# OS / editor cruft
33+
.DS_Store

AGENTS.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# AGENTS.md — guide for AI coding agents
2+
3+
A structured guide for AI agents working in `mcp-agent-mongodb`: how to build and test,
4+
where key files live, and the MongoDB-specific rules to follow.
5+
6+
## Build and test commands
7+
8+
```bash
9+
# Install (editable) + dev deps
10+
pip install -e ".[dev]"
11+
12+
# Run the test suite (mongomock — no infra required)
13+
pytest -q # 17 tests
14+
15+
# Demos (need Atlas + keys; see demo/.env: ATLAS_URI, VOYAGE_API_KEY, GEMINI_API_KEY)
16+
pip install -e ".[dev]" "mcp-agent" "google-genai" voyageai
17+
python demo/memory_demo.py # bring-your-own Voyage vectors
18+
MEMORY_MODE=auto python demo/memory_demo.py # Atlas Automated Embedding
19+
python demo/agent_demo.py # Gemini agent, cross-session memory
20+
```
21+
22+
## Project structure
23+
24+
- `src/mcp_agent_mongodb/memory.py``MongoMemory` (the `Memory` adapter + vector recall).
25+
- `src/mcp_agent_mongodb/__init__.py` — public exports + `__version__`.
26+
- `tests/test_acceptance.py` — acceptance tests (mongomock; Atlas handshake test auto-skips).
27+
- `demo/` — runnable demos over Atlas (`memory_demo.py`, `agent_demo.py`).
28+
- `EDD.md` — the MongoDB data model (source of truth for schema).
29+
- `PLAN.md` — the integration plan (phases + acceptance criteria).
30+
31+
## Upstream extension point
32+
33+
mcp-agent's `Memory[MessageParamT]` base class
34+
(`mcp_agent/workflows/llm/augmented_llm.py`) defines `extend` / `set` / `append` / `get` /
35+
`clear`. `AugmentedLLM.__init__` assigns `self.history = SimpleMemory()`, so `MongoMemory`
36+
is a **drop-in**: just set `llm.history = MongoMemory(...)`. Messages are arbitrary
37+
`MessageParamT` (provider message objects such as `google.genai.types.Content`, or dicts).
38+
39+
## Environment variables and configuration
40+
41+
| Name | Required | Description |
42+
|---|---|---|
43+
| `ATLAS_URI` | demos / vector tests | Atlas connection string |
44+
| `VOYAGE_API_KEY` | bring-your-own embedding demos | Voyage AI key for `voyage-3.5` |
45+
| `GEMINI_API_KEY` | agent demo | Gemini (Google AI) key |
46+
| `MEMORY_MODE` | optional | `auto` to use Atlas Automated Embedding in `memory_demo.py` |
47+
48+
## Conventions (do not break)
49+
50+
- The package **owns its `MongoClient`** — built from a connection string. `appName`
51+
(`devrel-integ-mcp-agent-python`) and the `mcp-agent-mongodb` driver-info handshake are
52+
always set and **non-overridable** (caller `appname`/`appName`/`driver` are stripped).
53+
- Embeddings use **Voyage AI 3.5** (`voyage-3.5`, 1024-dim) on the bring-your-own path.
54+
- No silent embedding fallback: `recall_semantic` raises if neither `query_vector` nor
55+
`auto_embed` is provided.
56+
- `get()` preserves insertion order via the per-session `seq` counter.
57+
58+
## MongoDB Skills
59+
60+
Use the official MongoDB agent skills from https://github.com/mongodb/agent-skills
61+
whenever the task is MongoDB-specific and a matching skill exists.
62+
63+
## When To Use EDD.md
64+
65+
Use [EDD.md](./EDD.md) as the source of truth for the MongoDB data model in this repository.
66+
67+
Consult [EDD.md](./EDD.md) before making changes that touch:
68+
69+
- The `memory` collection, document structure, or field names
70+
- Code paths that read or write database records (`memory.py`)
71+
- Index definitions (the `(session_id, seq)` index, optional TTL on `ts`, the Atlas Vector
72+
Search index)
73+
- Validation, payloads, or anything that depends on persisted data
74+
- Schema documentation, Mermaid diagrams, or entity modeling discussions

EDD.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# EDD.md — Entity Document Diagram
2+
3+
The MongoDB data model for `mcp-agent-mongodb`. A single collection, `memory`, backs
4+
`MongoMemory`. Keep this in sync with `src/mcp_agent_mongodb/memory.py`.
5+
6+
## Entities
7+
8+
### `memory` (database `mcp_agent`)
9+
10+
One document per message in an agent's conversation history. Documents are scoped by
11+
`session_id` and ordered by a monotonic `seq` (0-based, contiguous per session).
12+
13+
| Field | Type | Required | Description |
14+
|---|---|---|---|
15+
| `_id` | ObjectId | yes | Primary key (auto) |
16+
| `session_id` | string | yes | Conversation / agent scope key |
17+
| `seq` | int | yes | Monotonic order within the session (insertion order) |
18+
| `role` | string \| null | no | Best-effort role extracted from the message (`user`/`assistant`/…) |
19+
| `message` | object | yes | The serialized message, returned verbatim by `get()` |
20+
| `content` | string \| null | no | Best-effort text for embedding / recall |
21+
| `embedding` | double[1024] | bring-your-own path only | Voyage 3.5 vector |
22+
| `ts` | date | yes | Insert time (UTC); TTL anchor when enabled |
23+
24+
```json
25+
{
26+
"_id": { "$oid": "" },
27+
"session_id": "user-123",
28+
"seq": 7,
29+
"role": "user",
30+
"message": { "role": "user", "parts": [ { "text": "summarize the Q3 report" } ] },
31+
"content": "summarize the Q3 report",
32+
"embedding": [0.01, "… 1024 dims …"],
33+
"ts": { "$date": "2025-01-01T00:00:00Z" }
34+
}
35+
```
36+
37+
## Indexes
38+
39+
- Order index: `{ session_id: 1, seq: 1 }` (`session_seq`) — ordered retrieval + append cursor.
40+
- Optional TTL: `{ ts: 1 }, expireAfterSeconds=ttl_seconds` (`ttl_ts`).
41+
- Atlas Vector Search index `idx_agent_memory` over `embedding`
42+
(`numDimensions: 1024`, cosine, with a `session_id` filter) — or an `autoEmbed` index
43+
over `content` when `auto_embed=True` (Atlas Automated Embedding, recall by query text).
44+
45+
## Surface contract → MongoDB ops
46+
47+
| `Memory` method | MongoDB operation |
48+
|---|---|
49+
| `append(message)` | `insert_one` with next `seq` for `session_id` |
50+
| `extend(messages)` | `insert_many` with contiguous `seq` values |
51+
| `set(messages)` | `delete_many({session_id})` then `insert_many` |
52+
| `get()` | `find({session_id}).sort(seq)` → original messages |
53+
| `clear()` | `delete_many({session_id})` |
54+
| `recall_semantic(...)` | `$vectorSearch` over `embedding`, `session_id` prefilter |
55+
56+
## Relationships
57+
58+
```mermaid
59+
erDiagram
60+
SESSION ||--o{ MESSAGE : "owns ordered history"
61+
MESSAGE {
62+
objectId _id
63+
string session_id
64+
int seq
65+
string role
66+
object message
67+
string content
68+
double[] embedding
69+
date ts
70+
}
71+
```
72+
73+
## Notes
74+
75+
- `MongoMemory` is a drop-in for mcp-agent's `SimpleMemory` (`llm.history = MongoMemory(...)`).
76+
- `message` stores arbitrary provider message objects (e.g. `google.genai.types.Content`);
77+
pass `message_model=` to rehydrate them into model instances on `get()`.
78+
- Demo seed data uses the conversation turns created at runtime; the shared
79+
`data/embeddings.json` corpus is available for larger semantic-recall experiments.
80+
- appName: `devrel-integ-mcp-agent-python`; driver-info: `mcp-agent-mongodb`.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 MongoDB, Inc.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)