Store and recall a message from Claude Desktop
We will connect Claude Desktop on macOS to a local stdio MCP server backed by AuraDB, explicitly request a message-storage tool call, and verify that record in Aura before retrieving it. The server does not automatically capture all chat turns merely because it is connected.
This lesson selects the core tool profile, a local embedding model, and disabled entity/preference extraction. The MCP process and embedding computation run on your Mac; the database runs in Aura. Hosting the MCP process remotely and selecting other profiles are separate how-to tasks.
This lesson uses neo4j-agent-memory 0.7.0 from PyPI and the complete programs on this page in a POSIX shell. Create the local files shown below; the checks below verify the results in your environment.
Before you begin
-
macOS with Claude Desktop installed and support for local MCP servers enabled.
-
Python 3.10 or newer and a POSIX shell.
-
A Neo4j Aura account for a dedicated lesson instance.
-
Space and network access for the initial sentence-transformer model download.
Claude Desktop’s interface can vary by release. The verification target is the visible tool invocation and stored database record, not a particular icon or generated sentence.
1. Install the server and connect to Aura
Create a local folder and virtual environment for the examples on this page. The commands reuse an existing environment without changing its files:
mkdir -p ~/agent-memory-tutorials
cd ~/agent-memory-tutorials
if [ -e .venv ]; then
printf '%s\n' 'Using the existing virtual environment.'
else
python3 -m venv .venv
fi
source .venv/bin/activate
Expected: ~/agent-memory-tutorials is your working directory and its virtual environment is active. Install the published SDK with the command below.
Each complete code block labelled Save as names a file to create in this folder using your editor. Copy the entire block, including imports and the entry point. Expand each helper disclosure and use Copy code to copy its full source. Keep all files together so their imports resolve.
When continuing from another tutorial or guide, retain the existing environment, configuration, session files, and .tutorial-state/. Reuse unchanged helper files; compare an existing file before replacing it, and finish any pending cleanup or recovery before changing the code that owns its state.
python -m pip install 'neo4j-agent-memory[mcp,sentence-transformers]==0.7.0'
python -c "from importlib.metadata import version; \
import neo4j_agent_memory; \
assert version('neo4j-agent-memory') == '0.7.0'; \
print('SDK 0.7.0 import verified')"
Expected: SDK 0.7.0 import verified. Keep agent-memory-tutorials as your working directory for every command below.
Create and check the local files
Create each file below in ~/agent-memory-tutorials. All complete sources follow this manifest. On a first visit every file is new; when returning, reuse unchanged helpers and compare their contents before replacing them.
| File | Purpose | Returning reader |
|---|---|---|
|
Read explicit Aura credentials; the database defaults to |
Reuse unchanged |
|
Probe the selected database and close the driver. |
Reuse unchanged |
|
Prepare embeddings, validate Desktop configuration, or run the local MCP server. This is the lesson entry point. |
New for this lesson; retain it with unfinished state |
The manifest above names this lesson’s entry point and its helpers. Save every file it lists, then run the offline assembly checks that follow the sources before the lesson commands.
Open the helper below and save its complete source as aura_connection.py in ~/agent-memory-tutorials.
Show aura_connection.py
aura_connection.py"""Read the Aura connection exported by the documentation setup commands."""
import os
class AuraConfigurationError(ValueError):
"""Missing or invalid tutorial settings, with no credential values in errors."""
def aura_config():
required = ("NEO4J_URI", "NEO4J_USERNAME", "NEO4J_PASSWORD")
missing = [name for name in required if not os.environ.get(name, "").strip()]
if missing:
raise AuraConfigurationError("Export the Aura connection settings: " + ", ".join(missing))
uri = os.environ["NEO4J_URI"]
if not uri.startswith("neo4j+s://"):
raise AuraConfigurationError("NEO4J_URI must use the Aura neo4j+s:// connection scheme")
database = os.environ.get("NEO4J_DATABASE", "neo4j")
if not database.strip():
raise AuraConfigurationError("NEO4J_DATABASE must not be empty")
return {
"uri": uri,
"username": os.environ["NEO4J_USERNAME"],
"password": os.environ["NEO4J_PASSWORD"],
"database": database,
}
Open the helper below and save its complete source as wait_for_tutorial_neo4j.py in ~/agent-memory-tutorials.
Show wait_for_tutorial_neo4j.py
wait_for_tutorial_neo4j.py"""Check the exported Aura connection, waiting at most three minutes."""
import asyncio
import sys
from aura_connection import AuraConfigurationError, aura_config
from neo4j import AsyncGraphDatabase
from neo4j.exceptions import DriverError, Neo4jError, ServiceUnavailable, SessionExpired
async def wait_until_ready(timeout=180):
if timeout <= 0:
raise ValueError("Readiness timeout must be positive")
config = aura_config()
async def probe(driver):
while True:
try:
await driver.verify_connectivity()
records, _, _ = await driver.execute_query(
"RETURN 1 AS ready", database_=config["database"], routing_="r"
)
if len(records) != 1 or records[0]["ready"] != 1:
raise RuntimeError("Unexpected readiness query result")
return
except (ServiceUnavailable, SessionExpired):
await asyncio.sleep(2)
async with AsyncGraphDatabase.driver(
config["uri"],
auth=(config["username"], config["password"]),
connection_timeout=min(10, timeout),
connection_acquisition_timeout=min(10, timeout),
) as driver:
await asyncio.wait_for(probe(driver), timeout=timeout)
def main():
try:
asyncio.run(wait_until_ready())
except AuraConfigurationError as error:
print(str(error), file=sys.stderr)
raise SystemExit(1) from None
except (asyncio.TimeoutError, DriverError, Neo4jError) as error:
# Driver exceptions can include connection details. Report the category
# and next action without echoing credentials or a raw server message.
print(
f"Neo4j Aura readiness failed ({type(error).__name__}). "
"Check that the instance is Running, the exported NEO4J_* values "
"match its credentials, and your network allows the connection.",
file=sys.stderr,
)
raise SystemExit(1) from None
print("Verified: Neo4j Aura answered the readiness query")
if __name__ == "__main__":
main()
mcp_local_tutorial.py"""Local stdio server backed by AuraDB for the macOS Claude Desktop tutorial.
Claude must invoke a storage tool; merely chatting does not store every turn.
"""
import argparse
import asyncio
import json
import sys
from pathlib import Path
from aura_connection import aura_config
def build_server():
from neo4j_agent_memory import MemorySettings
from neo4j_agent_memory.mcp.server import create_mcp_server
settings = MemorySettings(
backend="bolt",
neo4j=aura_config(),
embedding="BAAI/bge-small-en-v1.5",
extraction={"extractor_type": "none"},
)
return create_mcp_server(settings, profile="core", auto_preferences=False)
def check_desktop_config(path):
"""Validate the final merged file locally, without printing its contents."""
def unique_keys(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError(
"Duplicate JSON object key; merge entries without duplicating keys"
)
result[key] = value
return result
try:
config = json.loads(Path(path).read_text(), object_pairs_hook=unique_keys)
except json.JSONDecodeError as error:
raise ValueError(f"Invalid JSON at line {error.lineno}, column {error.colno}") from None
except (OSError, UnicodeError):
raise ValueError("Cannot read the Desktop configuration as UTF-8 text") from None
try:
entry = config["mcpServers"]["neo4j-docs"]
env = entry["env"]
required = ("NEO4J_URI", "NEO4J_USERNAME", "NEO4J_PASSWORD", "NEO4J_DATABASE")
if any(not isinstance(env.get(key), str) or not env[key].strip() for key in required):
raise ValueError("The neo4j-docs env must contain all four nonempty NEO4J_* settings")
if not env["NEO4J_URI"].startswith("neo4j+s://"):
raise ValueError("The neo4j-docs NEO4J_URI must use neo4j+s://")
if entry["command"] != sys.executable or entry["args"] != [str(Path(__file__).resolve())]:
raise ValueError(
"Regenerate the entry using this lesson's interpreter and script paths"
)
except (KeyError, TypeError, AttributeError):
raise ValueError("Expected mcpServers -> neo4j-docs -> command, args and env") from None
print("Verified: merged Desktop JSON, lesson paths and explicit Aura variables")
def main():
parser = argparse.ArgumentParser()
action = parser.add_mutually_exclusive_group()
action.add_argument("--prepare", action="store_true")
action.add_argument("--config", action="store_true")
action.add_argument("--check-config", type=Path, metavar="PATH")
args = parser.parse_args()
if args.check_config:
try:
check_desktop_config(args.check_config)
except ValueError as error:
print(f"Desktop configuration check failed: {error}", file=sys.stderr)
raise SystemExit(1) from None
elif args.prepare:
from neo4j_agent_memory.llm import from_provider
embedding = from_provider("BAAI/bge-small-en-v1.5", kind="embedding")
vector = asyncio.run(embedding.embed_one("MCP tutorial readiness"))
if len(vector) != 384:
raise RuntimeError("Unexpected local embedding dimension")
print("Verified: local embedding model returned 384 dimensions")
elif args.config:
neo4j = aura_config()
print(
json.dumps(
{
"mcpServers": {
"neo4j-docs": {
"command": sys.executable,
"args": [str(Path(__file__).resolve())],
"env": {
"NEO4J_URI": neo4j["uri"],
"NEO4J_USERNAME": neo4j["username"],
"NEO4J_PASSWORD": neo4j["password"],
"NEO4J_DATABASE": neo4j["database"],
},
}
}
},
indent=2,
)
)
else:
build_server().run(transport="stdio", show_banner=False)
if __name__ == "__main__":
main()
Check the copied files before exporting credentials or making a service request:
python -m py_compile aura_connection.py wait_for_tutorial_neo4j.py mcp_local_tutorial.py
python -c "import aura_connection, wait_for_tutorial_neo4j, mcp_local_tutorial; assert all(callable(f) for f in [aura_connection.aura_config, wait_for_tutorial_neo4j.wait_until_ready, mcp_local_tutorial.main]); print('Lesson imports verified')"
python mcp_local_tutorial.py --help
Expected: compilation exits without errors, then Lesson imports verified and the command help. These checks import the local files without calling their entry points, downloading models, or contacting Aura/providers. Compilation alone does not check imports. If a file or function is missing, recopy its entire source and repeat the checks; leave existing session/state files intact. Provider/framework calls are checked when the lesson runs.
This starts a new lesson with a new empty database. To return to an unfinished lesson, use Resume this lesson later instead.
-
Sign in to the Neo4j Aura console and create an AuraDB Free instance named
agent-memory-tutorial. Use an empty instance dedicated to this lesson; do not load a sample dataset. -
Download the generated credentials and keep them outside the tutorial folder. Wait until the instance shows Running.
-
Copy its connection URI, username, password and database name into the exports below. Keep the
neo4j+s://scheme supplied by Aura.
Aura permits one Free instance per account. This lesson needs that slot for a dedicated tutorial instance; do not delete an existing database containing other work to make room. See Aura instance creation for account and tier requirements.
The files from the preceding assembly checkpoint read these exports and verify the selected database.
export NEO4J_URI='neo4j+s://<instance-id>.databases.neo4j.io'
export NEO4J_USERNAME='neo4j'
export NEO4J_PASSWORD='replace-with-the-generated-password'
export NEO4J_DATABASE='neo4j'
python wait_for_tutorial_neo4j.py
Expected: Verified: Neo4j Aura answered the readiness query. The helper checks connectivity and executes RETURN 1 in the selected database, retrying temporary connection failures for up to three minutes. Authentication failures stop immediately. If the check fails, confirm the instance is Running and recopy its connection settings.
The examples read these exported variables through aura_connection.py. They require the Aura URI, username and password and never fall back to a local instance. When NEO4J_DATABASE is unset, they use the neo4j database, which is the Aura default. Keep this shell and virtual environment active for the remaining commands. See Aura connection instructions for help locating the settings.
In the Aura console, open Query, select this instance and database in the connection bar, and connect using the same credentials. Run:
MATCH (n) RETURN count(n) AS node_count
Expected: 0. This confirms that the lesson starts with an empty database. The Python SDK still uses backend="bolt": Aura hosts Neo4j and accepts encrypted Bolt connections.
The server program copied above uses the current virtual environment and the shared aura_config() helper for its explicit Bolt settings.
python mcp_local_tutorial.py --prepare
Expected: Verified: local embedding model returned 384 dimensions. This prepares the model before Claude Desktop starts the server, avoiding an unobserved first-start download.
2. Configure the local stdio process
Claude Desktop does not inherit the Aura variables exported in your terminal. Generate configuration containing absolute interpreter/script paths and an explicit env mapping for those Aura variables:
umask 077
MCP_TUTORIAL_CONFIG=$(mktemp "${TMPDIR:-/tmp}/neo4j-docs-mcp.XXXXXX")
python mcp_local_tutorial.py --config > "$MCP_TUTORIAL_CONFIG"
python -c "import json, sys; \
config = json.load(open(sys.argv[1])); \
uri = config['mcpServers']['neo4j-docs']['env']['NEO4J_URI']; \
assert uri.startswith('neo4j+s://'); \
print('Aura MCP configuration verified')" "$MCP_TUTORIAL_CONFIG"
printf 'Private MCP configuration: %s\n' "$MCP_TUTORIAL_CONFIG"
Expected: Aura MCP configuration verified, followed by the private temporary file’s path. Open that path in your editor. The neo4j-docs entry contains absolute interpreter/script paths and NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD, and NEO4J_DATABASE. The generated JSON contains database credentials; keep it outside the tutorial folder and do not share it. Merge that one entry into ~/Library/Application Support/Claude/claude_desktop_config.json, preserving any existing servers and keeping that file private too. Place neo4j-docs directly inside the existing top-level mcpServers object, beside other server entries. Keep this terminal open for cleanup. Validate the final destination file after saving the merge:
python mcp_local_tutorial.py --check-config \
"$HOME/Library/Application Support/Claude/claude_desktop_config.json"
Expected: Verified: merged Desktop JSON, lesson paths and explicit Aura variables. The check reads only the local file, rejects malformed/duplicate JSON keys, checks this interpreter/script and required child-process variables, and never prints credential values or modifies other entries. It does not verify the password, server startup, or model access. Fix a reported line/column or nesting error before continuing. Then completely quit and restart Claude Desktop; it launches the stdio process.
3. Verify the tool connection
In a new Claude Desktop conversation, inspect the tools provided by neo4j-docs. Confirm that memory_store_message and memory_search are available.
Ask Claude: "Use the neo4j-docs memory_search tool to search for cedar-lantern-47. Show whether the tool found a stored result."
Expected: a visible tool invocation and a result or an empty result. A chat response without a tool call does not verify the connection.
If the server is absent, rerun the final JSON check, confirm the virtual environment and script still exist at the generated absolute paths, and inspect the local logs. The official MCP Desktop troubleshooting guide documents macOS logs under ~/Library/Logs/Claude: mcp.log records connection failures and mcp-server-neo4j-docs.log contains this server’s stderr. Open those files locally in your editor; logs can contain sensitive data, so do not paste their raw contents or the configuration into a support message. Record the error category and redact details first. These documented log locations are separate from a live verification of your installed Desktop release.
A listed server with a failed tool invocation needs its server/Aura/provider error investigated. A successful search returning no records is an ordinary empty result at this point; it is not a startup failure. Do not test connection repair by repeatedly storing the passphrase.
4. Store one exact message
Ask Claude:
Use neo4j-docs memory_store_message with session_id "claude-docs-tutorial", role "user", and content "The workshop passphrase is cedar-lantern-47." Report the returned storage result.
Expected: a visible memory_store_message invocation with those arguments and a successful tool result. The supplied Bolt session ID organizes this exercise; it is not an authorization boundary. No extracted entity or preference is required for this lesson.
5. Verify the stored record
In the Aura console, open Query, select this lesson’s instance, connect with its credentials, and run:
MATCH (conversation:Conversation {session_id: 'claude-docs-tutorial'})
-[:HAS_MESSAGE]->(message:Message)
WHERE message.content = 'The workshop passphrase is cedar-lantern-47.'
RETURN message.id, message.content
Expected: at least one row with the exact passphrase message and its stored ID. Repeating the storage request may create another message, so inspect the result before retrying.
6. Retrieve through the tool
Start a fresh Claude Desktop conversation and ask:
Use neo4j-docs memory_search for "workshop passphrase" with session_id "claude-docs-tutorial". Quote the stored passphrase only if the tool returns it.
Expected: a visible search invocation whose result contains cedar-lantern-47. An answer inferred from earlier chat text is not proof of retrieval; compare the tool result with the database row from the preceding step.
Resume this lesson later
A new Python process keeps the same shell exports; a new terminal does not. If this lesson’s Aura instance still exists, resume it without creating another instance or asserting that its database is empty:
-
Return to the existing folder and activate its environment:
cd ~/agent-memory-tutorials source .venv/bin/activate -
Re-export the original instance’s
NEO4J_URI,NEO4J_USERNAME,NEO4J_PASSWORD, andNEO4J_DATABASEfrom its saved credentials. Restore this page’s provider key/model/region settings too. Keep secrets out of the Python files. -
Check the same database:
python wait_for_tutorial_neo4j.py -
Use the readback or inspection step described below. Do not repeat the seed/record/write command just to recover context. Preserve existing IDs and state until you know which operations completed.
If the instance was already destroyed, its stored results cannot be resumed; complete local cleanup and start a new empty lesson instance. A different lesson also starts with a fresh instance, while keeping the folder and environment.
Keep the existing Desktop server entry and run the final configuration check again. In Aura Query, inspect the exact message before asking for another memory_search call. Do not repeat memory_store_message just because you opened a new chat. If the private temporary configuration path was lost, locate and delete only that lesson-generated file during cleanup; do not delete other temporary files.
Cleanup and further tasks
Remove the neo4j-docs entry from Claude Desktop’s configuration and restart the app. Delete the generated credential-bearing file:
rm "$MCP_TUTORIAL_CONFIG"
Cleanup ends this lesson and removes its stored results. To keep working with those results later, follow Resume this lesson later before cleanup.
In the Aura console, select only the agent-memory-tutorial instance created for this lesson. Use its trashcan action, enter its exact name and confirm Destroy.
Expected: the tutorial instance disappears from the instance list. This removes its data and snapshots, so verify the instance name before confirming. See Aura instance deletion for the console procedure.
Remove the downloaded credentials for that deleted instance and clear its connection variables:
unset NEO4J_URI NEO4J_USERNAME NEO4J_PASSWORD NEO4J_DATABASE
Keep the tutorial folder and virtual environment. Start each Aura tutorial with a new empty lesson instance and its new credentials; this also avoids carrying over vector indexes from a lesson using a different embedding dimension.
The remote transport, extended profile, and team setup belong in Create a context-graph MCP integration, MCP tool reference, and Team memory. They are not additional branches required to finish this desktop lesson.