#
tokens: 6024/50000 9/9 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .dockerignore
├── .env.example
├── .gitattributes
├── .gitignore
├── Dockerfile
├── LICENSE
├── public
│   └── Mem0AndMCP.png
├── pyproject.toml
├── README.md
├── src
│   ├── main.py
│   └── utils.py
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------

```
1 | .env
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | .env
2 | venv
3 | .venv
4 | __pycache__
5 | mem0_mcp.egg-info
6 | .python-version
```

--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------

```
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 | 
```

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
 1 | # The transport for the MCP server - either 'sse' or 'stdio' (defaults to SSE if left empty)
 2 | TRANSPORT=
 3 | 
 4 | # Host to bind to if using sse as the transport (leave empty if using stdio)
 5 | HOST=
 6 | 
 7 | # Port to listen on if using sse as the transport (leave empty if using stdio)
 8 | PORT=
 9 | 
10 | # The provider for your LLM
11 | # Set this to either openai, openrouter, or ollama
12 | # This is needed on top of the base URL for Mem0 (long term memory)
13 | LLM_PROVIDER=
14 | 
15 | # Base URL for the OpenAI compatible instance (default is https://api.openai.com/v1)
16 | # OpenAI: https://api.openai.com/v1
17 | # Ollama (example): http://localhost:11434/v1
18 | # OpenRouter: https://openrouter.ai/api/v1
19 | LLM_BASE_URL=
20 | 
21 | # OpenAI: https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key
22 | # Open Router: Get your API Key here after registering: https://openrouter.ai/keys
23 | # Ollama: No need to set this unless you specifically configured an API key
24 | LLM_API_KEY=
25 | 
26 | # The LLM you want to use for processing memories.
27 | # OpenAI example: gpt-4o-mini
28 | # OpenRouter example: anthropic/claude-3.7-sonnet
29 | # Ollama example: qwen2.5:14b-instruct-8k
30 | LLM_CHOICE=
31 | 
32 | # The embedding model you want to use to store memories - this needs to be from the same provider as set above.
33 | # OpenAI example: text-embedding-3-small
34 | # Ollama example: nomic-embed-text
35 | EMBEDDING_MODEL_CHOICE=
36 | 
37 | # Postgres DB URL used for mem0
38 | # Format: postgresql://[user]:[password]@[host]:[port]/[database_name]
39 | # Example: postgresql://postgres:mypassword@localhost:5432/mydb
40 | # For Supabase Postgres connection, you can find this in "Connect" (top middle of Supabase dashboard) -> Transaction pooler
41 | DATABASE_URL=
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | <h1 align="center">MCP-Mem0: Long-Term Memory for AI Agents</h1>
  2 | 
  3 | <p align="center">
  4 |   <img src="public/Mem0AndMCP.png" alt="Mem0 and MCP Integration" width="600">
  5 | </p>
  6 | 
  7 | A template implementation of the [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server integrated with [Mem0](https://mem0.ai) for providing AI agents with persistent memory capabilities.
  8 | 
  9 | Use this as a reference point to build your MCP servers yourself, or give this as an example to an AI coding assistant and tell it to follow this example for structure and code correctness!
 10 | 
 11 | ## Overview
 12 | 
 13 | This project demonstrates how to build an MCP server that enables AI agents to store, retrieve, and search memories using semantic search. It serves as a practical template for creating your own MCP servers, simply using Mem0 and a practical example.
 14 | 
 15 | The implementation follows the best practices laid out by Anthropic for building MCP servers, allowing seamless integration with any MCP-compatible client.
 16 | 
 17 | ## Features
 18 | 
 19 | The server provides three essential memory management tools:
 20 | 
 21 | 1. **`save_memory`**: Store any information in long-term memory with semantic indexing
 22 | 2. **`get_all_memories`**: Retrieve all stored memories for comprehensive context
 23 | 3. **`search_memories`**: Find relevant memories using semantic search
 24 | 
 25 | ## Prerequisites
 26 | 
 27 | - Python 3.12+
 28 | - Supabase or any PostgreSQL database (for vector storage of memories)
 29 | - API keys for your chosen LLM provider (OpenAI, OpenRouter, or Ollama)
 30 | - Docker if running the MCP server as a container (recommended)
 31 | 
 32 | ## Installation
 33 | 
 34 | ### Using uv
 35 | 
 36 | 1. Install uv if you don't have it:
 37 |    ```bash
 38 |    pip install uv
 39 |    ```
 40 | 
 41 | 2. Clone this repository:
 42 |    ```bash
 43 |    git clone https://github.com/coleam00/mcp-mem0.git
 44 |    cd mcp-mem0
 45 |    ```
 46 | 
 47 | 3. Install dependencies:
 48 |    ```bash
 49 |    uv pip install -e .
 50 |    ```
 51 | 
 52 | 4. Create a `.env` file based on `.env.example`:
 53 |    ```bash
 54 |    cp .env.example .env
 55 |    ```
 56 | 
 57 | 5. Configure your environment variables in the `.env` file (see Configuration section)
 58 | 
 59 | ### Using Docker (Recommended)
 60 | 
 61 | 1. Build the Docker image:
 62 |    ```bash
 63 |    docker build -t mcp/mem0 --build-arg PORT=8050 .
 64 |    ```
 65 | 
 66 | 2. Create a `.env` file based on `.env.example` and configure your environment variables
 67 | 
 68 | ## Configuration
 69 | 
 70 | The following environment variables can be configured in your `.env` file:
 71 | 
 72 | | Variable | Description | Example |
 73 | |----------|-------------|----------|
 74 | | `TRANSPORT` | Transport protocol (sse or stdio) | `sse` |
 75 | | `HOST` | Host to bind to when using SSE transport | `0.0.0.0` |
 76 | | `PORT` | Port to listen on when using SSE transport | `8050` |
 77 | | `LLM_PROVIDER` | LLM provider (openai, openrouter, or ollama) | `openai` |
 78 | | `LLM_BASE_URL` | Base URL for the LLM API | `https://api.openai.com/v1` |
 79 | | `LLM_API_KEY` | API key for the LLM provider | `sk-...` |
 80 | | `LLM_CHOICE` | LLM model to use | `gpt-4o-mini` |
 81 | | `EMBEDDING_MODEL_CHOICE` | Embedding model to use | `text-embedding-3-small` |
 82 | | `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:port/db` |
 83 | 
 84 | ## Running the Server
 85 | 
 86 | ### Using uv
 87 | 
 88 | #### SSE Transport
 89 | 
 90 | ```bash
 91 | # Set TRANSPORT=sse in .env then:
 92 | uv run src/main.py
 93 | ```
 94 | 
 95 | The MCP server will essentially be run as an API endpoint that you can then connect to with config shown below.
 96 | 
 97 | #### Stdio Transport
 98 | 
 99 | With stdio, the MCP client iself can spin up the MCP server, so nothing to run at this point.
100 | 
101 | ### Using Docker
102 | 
103 | #### SSE Transport
104 | 
105 | ```bash
106 | docker run --env-file .env -p:8050:8050 mcp/mem0
107 | ```
108 | 
109 | The MCP server will essentially be run as an API endpoint within the container that you can then connect to with config shown below.
110 | 
111 | #### Stdio Transport
112 | 
113 | With stdio, the MCP client iself can spin up the MCP server container, so nothing to run at this point.
114 | 
115 | ## Integration with MCP Clients
116 | 
117 | ### SSE Configuration
118 | 
119 | Once you have the server running with SSE transport, you can connect to it using this configuration:
120 | 
121 | ```json
122 | {
123 |   "mcpServers": {
124 |     "mem0": {
125 |       "transport": "sse",
126 |       "url": "http://localhost:8050/sse"
127 |     }
128 |   }
129 | }
130 | ```
131 | 
132 | > **Note for Windsurf users**: Use `serverUrl` instead of `url` in your configuration:
133 | > ```json
134 | > {
135 | >   "mcpServers": {
136 | >     "mem0": {
137 | >       "transport": "sse",
138 | >       "serverUrl": "http://localhost:8050/sse"
139 | >     }
140 | >   }
141 | > }
142 | > ```
143 | 
144 | > **Note for n8n users**: Use host.docker.internal instead of localhost since n8n has to reach outside of it's own container to the host machine:
145 | > 
146 | > So the full URL in the MCP node would be: http://host.docker.internal:8050/sse
147 | 
148 | Make sure to update the port if you are using a value other than the default 8050.
149 | 
150 | ### Python with Stdio Configuration
151 | 
152 | Add this server to your MCP configuration for Claude Desktop, Windsurf, or any other MCP client:
153 | 
154 | ```json
155 | {
156 |   "mcpServers": {
157 |     "mem0": {
158 |       "command": "your/path/to/mcp-mem0/.venv/Scripts/python.exe",
159 |       "args": ["your/path/to/mcp-mem0/src/main.py"],
160 |       "env": {
161 |         "TRANSPORT": "stdio",
162 |         "LLM_PROVIDER": "openai",
163 |         "LLM_BASE_URL": "https://api.openai.com/v1",
164 |         "LLM_API_KEY": "YOUR-API-KEY",
165 |         "LLM_CHOICE": "gpt-4o-mini",
166 |         "EMBEDDING_MODEL_CHOICE": "text-embedding-3-small",
167 |         "DATABASE_URL": "YOUR-DATABASE-URL"
168 |       }
169 |     }
170 |   }
171 | }
172 | ```
173 | 
174 | ### Docker with Stdio Configuration
175 | 
176 | ```json
177 | {
178 |   "mcpServers": {
179 |     "mem0": {
180 |       "command": "docker",
181 |       "args": ["run", "--rm", "-i", 
182 |                "-e", "TRANSPORT", 
183 |                "-e", "LLM_PROVIDER", 
184 |                "-e", "LLM_BASE_URL", 
185 |                "-e", "LLM_API_KEY", 
186 |                "-e", "LLM_CHOICE", 
187 |                "-e", "EMBEDDING_MODEL_CHOICE", 
188 |                "-e", "DATABASE_URL", 
189 |                "mcp/mem0"],
190 |       "env": {
191 |         "TRANSPORT": "stdio",
192 |         "LLM_PROVIDER": "openai",
193 |         "LLM_BASE_URL": "https://api.openai.com/v1",
194 |         "LLM_API_KEY": "YOUR-API-KEY",
195 |         "LLM_CHOICE": "gpt-4o-mini",
196 |         "EMBEDDING_MODEL_CHOICE": "text-embedding-3-small",
197 |         "DATABASE_URL": "YOUR-DATABASE-URL"
198 |       }
199 |     }
200 |   }
201 | }
202 | ```
203 | 
204 | ## Building Your Own Server
205 | 
206 | This template provides a foundation for building more complex MCP servers. To build your own:
207 | 
208 | 1. Add your own tools by creating methods with the `@mcp.tool()` decorator
209 | 2. Create your own lifespan function to add your own dependencies (clients, database connections, etc.)
210 | 3. Modify the `utils.py` file for any helper functions you need for your MCP server
211 | 4. Feel free to add prompts and resources as well  with `@mcp.resource()` and `@mcp.prompt()`
212 | 
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | FROM python:3.12-slim
 2 | 
 3 | ARG PORT=8050
 4 | 
 5 | WORKDIR /app
 6 | 
 7 | # Install uv
 8 | RUN pip install uv
 9 | 
10 | # Copy the MCP server files
11 | COPY . .
12 | 
13 | # Install packages
14 | RUN python -m venv .venv
15 | RUN uv pip install -e .
16 | 
17 | EXPOSE ${PORT}
18 | 
19 | # Command to run the MCP server
20 | CMD ["uv", "run", "src/main.py"]
```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
 1 | [project]
 2 | name = "mem0-mcp"
 3 | version = "0.1.0"
 4 | description = "MCP server for integrating long term memory into AI agents with Mem0"
 5 | readme = "README.md"
 6 | requires-python = ">=3.12"
 7 | dependencies = [
 8 |     "httpx>=0.28.1",
 9 |     "mcp[cli]>=1.3.0",
10 |     "mem0ai>=0.1.88",
11 |     "vecs>=0.4.5"
12 | ]
13 | 
```

--------------------------------------------------------------------------------
/src/utils.py:
--------------------------------------------------------------------------------

```python
  1 | from mem0 import Memory
  2 | import os
  3 | 
  4 | # Custom instructions for memory processing
  5 | # These aren't being used right now but Mem0 does support adding custom prompting
  6 | # for handling memory retrieval and processing.
  7 | CUSTOM_INSTRUCTIONS = """
  8 | Extract the Following Information:  
  9 | 
 10 | - Key Information: Identify and save the most important details.
 11 | - Context: Capture the surrounding context to understand the memory's relevance.
 12 | - Connections: Note any relationships to other topics or memories.
 13 | - Importance: Highlight why this information might be valuable in the future.
 14 | - Source: Record where this information came from when applicable.
 15 | """
 16 | 
 17 | def get_mem0_client():
 18 |     # Get LLM provider and configuration
 19 |     llm_provider = os.getenv('LLM_PROVIDER')
 20 |     llm_api_key = os.getenv('LLM_API_KEY')
 21 |     llm_model = os.getenv('LLM_CHOICE')
 22 |     embedding_model = os.getenv('EMBEDDING_MODEL_CHOICE')
 23 |     
 24 |     # Initialize config dictionary
 25 |     config = {}
 26 |     
 27 |     # Configure LLM based on provider
 28 |     if llm_provider == 'openai' or llm_provider == 'openrouter':
 29 |         config["llm"] = {
 30 |             "provider": "openai",
 31 |             "config": {
 32 |                 "model": llm_model,
 33 |                 "temperature": 0.2,
 34 |                 "max_tokens": 2000,
 35 |             }
 36 |         }
 37 |         
 38 |         # Set API key in environment if not already set
 39 |         if llm_api_key and not os.environ.get("OPENAI_API_KEY"):
 40 |             os.environ["OPENAI_API_KEY"] = llm_api_key
 41 |             
 42 |         # For OpenRouter, set the specific API key
 43 |         if llm_provider == 'openrouter' and llm_api_key:
 44 |             os.environ["OPENROUTER_API_KEY"] = llm_api_key
 45 |     
 46 |     elif llm_provider == 'ollama':
 47 |         config["llm"] = {
 48 |             "provider": "ollama",
 49 |             "config": {
 50 |                 "model": llm_model,
 51 |                 "temperature": 0.2,
 52 |                 "max_tokens": 2000,
 53 |             }
 54 |         }
 55 |         
 56 |         # Set base URL for Ollama if provided
 57 |         llm_base_url = os.getenv('LLM_BASE_URL')
 58 |         if llm_base_url:
 59 |             config["llm"]["config"]["ollama_base_url"] = llm_base_url
 60 |     
 61 |     # Configure embedder based on provider
 62 |     if llm_provider == 'openai':
 63 |         config["embedder"] = {
 64 |             "provider": "openai",
 65 |             "config": {
 66 |                 "model": embedding_model or "text-embedding-3-small",
 67 |                 "embedding_dims": 1536  # Default for text-embedding-3-small
 68 |             }
 69 |         }
 70 |         
 71 |         # Set API key in environment if not already set
 72 |         if llm_api_key and not os.environ.get("OPENAI_API_KEY"):
 73 |             os.environ["OPENAI_API_KEY"] = llm_api_key
 74 |     
 75 |     elif llm_provider == 'ollama':
 76 |         config["embedder"] = {
 77 |             "provider": "ollama",
 78 |             "config": {
 79 |                 "model": embedding_model or "nomic-embed-text",
 80 |                 "embedding_dims": 768  # Default for nomic-embed-text
 81 |             }
 82 |         }
 83 |         
 84 |         # Set base URL for Ollama if provided
 85 |         embedding_base_url = os.getenv('LLM_BASE_URL')
 86 |         if embedding_base_url:
 87 |             config["embedder"]["config"]["ollama_base_url"] = embedding_base_url
 88 |     
 89 |     # Configure Supabase vector store
 90 |     config["vector_store"] = {
 91 |         "provider": "supabase",
 92 |         "config": {
 93 |             "connection_string": os.environ.get('DATABASE_URL', ''),
 94 |             "collection_name": "mem0_memories",
 95 |             "embedding_model_dims": 1536 if llm_provider == "openai" else 768
 96 |         }
 97 |     }
 98 | 
 99 |     # config["custom_fact_extraction_prompt"] = CUSTOM_INSTRUCTIONS
100 |     
101 |     # Create and return the Memory client
102 |     return Memory.from_config(config)
```

--------------------------------------------------------------------------------
/src/main.py:
--------------------------------------------------------------------------------

```python
  1 | from mcp.server.fastmcp import FastMCP, Context
  2 | from contextlib import asynccontextmanager
  3 | from collections.abc import AsyncIterator
  4 | from dataclasses import dataclass
  5 | from dotenv import load_dotenv
  6 | from mem0 import Memory
  7 | import asyncio
  8 | import json
  9 | import os
 10 | 
 11 | from utils import get_mem0_client
 12 | 
 13 | load_dotenv()
 14 | 
 15 | # Default user ID for memory operations
 16 | DEFAULT_USER_ID = "user"
 17 | 
 18 | # Create a dataclass for our application context
 19 | @dataclass
 20 | class Mem0Context:
 21 |     """Context for the Mem0 MCP server."""
 22 |     mem0_client: Memory
 23 | 
 24 | @asynccontextmanager
 25 | async def mem0_lifespan(server: FastMCP) -> AsyncIterator[Mem0Context]:
 26 |     """
 27 |     Manages the Mem0 client lifecycle.
 28 |     
 29 |     Args:
 30 |         server: The FastMCP server instance
 31 |         
 32 |     Yields:
 33 |         Mem0Context: The context containing the Mem0 client
 34 |     """
 35 |     # Create and return the Memory client with the helper function in utils.py
 36 |     mem0_client = get_mem0_client()
 37 |     
 38 |     try:
 39 |         yield Mem0Context(mem0_client=mem0_client)
 40 |     finally:
 41 |         # No explicit cleanup needed for the Mem0 client
 42 |         pass
 43 | 
 44 | # Initialize FastMCP server with the Mem0 client as context
 45 | mcp = FastMCP(
 46 |     "mcp-mem0",
 47 |     description="MCP server for long term memory storage and retrieval with Mem0",
 48 |     lifespan=mem0_lifespan,
 49 |     host=os.getenv("HOST", "0.0.0.0"),
 50 |     port=os.getenv("PORT", "8050")
 51 | )        
 52 | 
 53 | @mcp.tool()
 54 | async def save_memory(ctx: Context, text: str) -> str:
 55 |     """Save information to your long-term memory.
 56 | 
 57 |     This tool is designed to store any type of information that might be useful in the future.
 58 |     The content will be processed and indexed for later retrieval through semantic search.
 59 | 
 60 |     Args:
 61 |         ctx: The MCP server provided context which includes the Mem0 client
 62 |         text: The content to store in memory, including any relevant details and context
 63 |     """
 64 |     try:
 65 |         mem0_client = ctx.request_context.lifespan_context.mem0_client
 66 |         messages = [{"role": "user", "content": text}]
 67 |         mem0_client.add(messages, user_id=DEFAULT_USER_ID)
 68 |         return f"Successfully saved memory: {text[:100]}..." if len(text) > 100 else f"Successfully saved memory: {text}"
 69 |     except Exception as e:
 70 |         return f"Error saving memory: {str(e)}"
 71 | 
 72 | @mcp.tool()
 73 | async def get_all_memories(ctx: Context) -> str:
 74 |     """Get all stored memories for the user.
 75 |     
 76 |     Call this tool when you need complete context of all previously memories.
 77 | 
 78 |     Args:
 79 |         ctx: The MCP server provided context which includes the Mem0 client
 80 | 
 81 |     Returns a JSON formatted list of all stored memories, including when they were created
 82 |     and their content. Results are paginated with a default of 50 items per page.
 83 |     """
 84 |     try:
 85 |         mem0_client = ctx.request_context.lifespan_context.mem0_client
 86 |         memories = mem0_client.get_all(user_id=DEFAULT_USER_ID)
 87 |         if isinstance(memories, dict) and "results" in memories:
 88 |             flattened_memories = [memory["memory"] for memory in memories["results"]]
 89 |         else:
 90 |             flattened_memories = memories
 91 |         return json.dumps(flattened_memories, indent=2)
 92 |     except Exception as e:
 93 |         return f"Error retrieving memories: {str(e)}"
 94 | 
 95 | @mcp.tool()
 96 | async def search_memories(ctx: Context, query: str, limit: int = 3) -> str:
 97 |     """Search memories using semantic search.
 98 | 
 99 |     This tool should be called to find relevant information from your memory. Results are ranked by relevance.
100 |     Always search your memories before making decisions to ensure you leverage your existing knowledge.
101 | 
102 |     Args:
103 |         ctx: The MCP server provided context which includes the Mem0 client
104 |         query: Search query string describing what you're looking for. Can be natural language.
105 |         limit: Maximum number of results to return (default: 3)
106 |     """
107 |     try:
108 |         mem0_client = ctx.request_context.lifespan_context.mem0_client
109 |         memories = mem0_client.search(query, user_id=DEFAULT_USER_ID, limit=limit)
110 |         if isinstance(memories, dict) and "results" in memories:
111 |             flattened_memories = [memory["memory"] for memory in memories["results"]]
112 |         else:
113 |             flattened_memories = memories
114 |         return json.dumps(flattened_memories, indent=2)
115 |     except Exception as e:
116 |         return f"Error searching memories: {str(e)}"
117 | 
118 | async def main():
119 |     transport = os.getenv("TRANSPORT", "sse")
120 |     if transport == 'sse':
121 |         # Run the MCP server with sse transport
122 |         await mcp.run_sse_async()
123 |     else:
124 |         # Run the MCP server with stdio transport
125 |         await mcp.run_stdio_async()
126 | 
127 | if __name__ == "__main__":
128 |     asyncio.run(main())
129 | 
```