Merge branch 'main' into graphid-isolation
This commit is contained in:
commit
445ea2e45c
14 changed files with 810 additions and 453 deletions
|
|
@ -69,6 +69,14 @@ COPY ./server/graph_service ./graph_service
|
||||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
uv sync --frozen --no-dev
|
uv sync --frozen --no-dev
|
||||||
|
|
||||||
|
# Install falkordb if requested
|
||||||
|
ARG INSTALL_FALKORDB=false
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
|
if [ "$INSTALL_FALKORDB" = "true" ]; then \
|
||||||
|
WHEEL=$(ls /tmp/*.whl | head -n 1); \
|
||||||
|
uv pip install "$WHEEL[falkordb]"; \
|
||||||
|
fi
|
||||||
|
|
||||||
# Change ownership to app user
|
# Change ownership to app user
|
||||||
RUN chown -R app:app /app
|
RUN chown -R app:app /app
|
||||||
|
|
||||||
|
|
|
||||||
33
README.md
33
README.md
|
|
@ -77,6 +77,23 @@ We're excited to open-source Graphiti, believing its potential reaches far beyon
|
||||||
<a href="https://arxiv.org/abs/2501.13956"><img src="images/arxiv-screenshot.png" alt="Zep: A Temporal Knowledge Graph Architecture for Agent Memory" width="700px"></a>
|
<a href="https://arxiv.org/abs/2501.13956"><img src="images/arxiv-screenshot.png" alt="Zep: A Temporal Knowledge Graph Architecture for Agent Memory" width="700px"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
## Zep vs Graphiti
|
||||||
|
|
||||||
|
| Aspect | Zep | Graphiti |
|
||||||
|
|--------|-----|----------|
|
||||||
|
| **What they are** | Complete managed platform for AI memory | Open-source graph framework |
|
||||||
|
| **User & conversation management** | Built-in users, threads, and message storage | Build your own |
|
||||||
|
| **Retrieval & performance** | Pre-configured, production-ready retrieval with sub-200ms performance at scale | Custom implementation required; performance depends on your setup |
|
||||||
|
| **Developer tools** | Dashboard with graph visualization, debug logs, API logs; SDKs for Python, TypeScript, and Go | Build your own tools |
|
||||||
|
| **Enterprise features** | SLAs, support, security guarantees | Self-managed |
|
||||||
|
| **Deployment** | Fully managed or in your cloud | Self-hosted only |
|
||||||
|
|
||||||
|
### When to choose which
|
||||||
|
|
||||||
|
**Choose Zep** if you want a turnkey, enterprise-grade platform with security, performance, and support baked in.
|
||||||
|
|
||||||
|
**Choose Graphiti** if you want a flexible OSS core and you're comfortable building/operating the surrounding system.
|
||||||
|
|
||||||
## Why Graphiti?
|
## Why Graphiti?
|
||||||
|
|
||||||
Traditional RAG approaches often rely on batch processing and static data summarization, making them inefficient for
|
Traditional RAG approaches often rely on batch processing and static data summarization, making them inefficient for
|
||||||
|
|
@ -239,6 +256,22 @@ The quickstart demonstrates:
|
||||||
The example is fully documented with clear explanations of each functionality and includes a comprehensive README with
|
The example is fully documented with clear explanations of each functionality and includes a comprehensive README with
|
||||||
setup instructions and next steps.
|
setup instructions and next steps.
|
||||||
|
|
||||||
|
### Running with Docker Compose
|
||||||
|
|
||||||
|
You can use Docker Compose to quickly start the required services:
|
||||||
|
|
||||||
|
- **Neo4j Docker:**
|
||||||
|
```sh
|
||||||
|
docker compose up
|
||||||
|
```
|
||||||
|
This will start the Neo4j Docker service and related components.
|
||||||
|
|
||||||
|
- **FalkorDB Docker:**
|
||||||
|
```sh
|
||||||
|
docker compose --profile falkordb up
|
||||||
|
```
|
||||||
|
This will start the FalkorDB Docker service and related components.
|
||||||
|
|
||||||
## MCP Server
|
## MCP Server
|
||||||
|
|
||||||
The `mcp_server` directory contains a Model Context Protocol (MCP) server implementation for Graphiti. This server
|
The `mcp_server` directory contains a Model Context Protocol (MCP) server implementation for Graphiti. This server
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
services:
|
services:
|
||||||
graph:
|
graph:
|
||||||
|
profiles: [""]
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
ports:
|
ports:
|
||||||
|
|
@ -24,8 +25,10 @@ services:
|
||||||
- NEO4J_USER=${NEO4J_USER}
|
- NEO4J_USER=${NEO4J_USER}
|
||||||
- NEO4J_PASSWORD=${NEO4J_PASSWORD}
|
- NEO4J_PASSWORD=${NEO4J_PASSWORD}
|
||||||
- PORT=8000
|
- PORT=8000
|
||||||
|
- db_backend=neo4j
|
||||||
neo4j:
|
neo4j:
|
||||||
image: neo4j:5.26.2
|
image: neo4j:5.26.2
|
||||||
|
profiles: [""]
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test:
|
test:
|
||||||
[
|
[
|
||||||
|
|
@ -44,5 +47,46 @@ services:
|
||||||
environment:
|
environment:
|
||||||
- NEO4J_AUTH=${NEO4J_USER}/${NEO4J_PASSWORD}
|
- NEO4J_AUTH=${NEO4J_USER}/${NEO4J_PASSWORD}
|
||||||
|
|
||||||
|
falkordb:
|
||||||
|
image: falkordb/falkordb:latest
|
||||||
|
profiles: ["falkordb"]
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- falkordb_data:/data
|
||||||
|
environment:
|
||||||
|
- FALKORDB_ARGS=--port 6379 --cluster-enabled no
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "-p", "6379", "ping"]
|
||||||
|
interval: 1s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 10
|
||||||
|
start_period: 3s
|
||||||
|
graph-falkordb:
|
||||||
|
build:
|
||||||
|
args:
|
||||||
|
INSTALL_FALKORDB: "true"
|
||||||
|
context: .
|
||||||
|
profiles: ["falkordb"]
|
||||||
|
ports:
|
||||||
|
- "8001:8001"
|
||||||
|
depends_on:
|
||||||
|
falkordb:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8001/healthcheck')"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
environment:
|
||||||
|
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||||
|
- FALKORDB_HOST=falkordb
|
||||||
|
- FALKORDB_PORT=6379
|
||||||
|
- FALKORDB_DATABASE=default_db
|
||||||
|
- GRAPHITI_BACKEND=falkordb
|
||||||
|
- PORT=8001
|
||||||
|
- db_backend=falkordb
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
neo4j_data:
|
neo4j_data:
|
||||||
|
falkordb_data:
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
@ -263,6 +264,28 @@ class FalkorDriver(GraphDriver):
|
||||||
|
|
||||||
return cloned
|
return cloned
|
||||||
|
|
||||||
|
async def health_check(self) -> None:
|
||||||
|
"""Check FalkorDB connectivity by running a simple query."""
|
||||||
|
try:
|
||||||
|
await self.execute_query("MATCH (n) RETURN 1 LIMIT 1")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"FalkorDB health check failed: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def convert_datetimes_to_strings(obj):
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
return {k: FalkorDriver.convert_datetimes_to_strings(v) for k, v in obj.items()}
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
return [FalkorDriver.convert_datetimes_to_strings(item) for item in obj]
|
||||||
|
elif isinstance(obj, tuple):
|
||||||
|
return tuple(FalkorDriver.convert_datetimes_to_strings(item) for item in obj)
|
||||||
|
elif isinstance(obj, datetime):
|
||||||
|
return obj.isoformat()
|
||||||
|
else:
|
||||||
|
return obj
|
||||||
|
|
||||||
def sanitize(self, query: str) -> str:
|
def sanitize(self, query: str) -> str:
|
||||||
"""
|
"""
|
||||||
Replace FalkorDB special characters with whitespace.
|
Replace FalkorDB special characters with whitespace.
|
||||||
|
|
|
||||||
|
|
@ -106,3 +106,12 @@ class Neo4jDriver(GraphDriver):
|
||||||
for query in index_queries
|
for query in index_queries
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def health_check(self) -> None:
|
||||||
|
"""Check Neo4j connectivity by running the driver's verify_connectivity method."""
|
||||||
|
try:
|
||||||
|
await self.client.verify_connectivity()
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Neo4j health check failed: {e}")
|
||||||
|
raise
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,20 @@
|
||||||
# Graphiti MCP Server Environment Configuration
|
# Graphiti MCP Server Environment Configuration
|
||||||
|
|
||||||
# Neo4j Database Configuration
|
# Database Configuration
|
||||||
|
# Choose between 'neo4j' or 'falkordb'
|
||||||
|
DATABASE_TYPE=falkordb
|
||||||
|
|
||||||
# These settings are used to connect to your Neo4j database
|
# These settings are used to connect to your Neo4j database
|
||||||
NEO4J_URI=bolt://localhost:7687
|
NEO4J_URI=bolt://localhost:7687
|
||||||
NEO4J_USER=neo4j
|
NEO4J_USER=neo4j
|
||||||
NEO4J_PASSWORD=demodemo
|
NEO4J_PASSWORD=demodemo
|
||||||
|
|
||||||
|
# These settings are used to connect to your FalkorDB database
|
||||||
|
FALKORDB_PORT=6379
|
||||||
|
FALKORDB_HOST=localhost
|
||||||
|
FALKORDB_USER=
|
||||||
|
FALKORDB_PASSWORD=
|
||||||
|
|
||||||
# OpenAI API Configuration
|
# OpenAI API Configuration
|
||||||
# Required for LLM operations
|
# Required for LLM operations
|
||||||
OPENAI_API_KEY=your_openai_api_key_here
|
OPENAI_API_KEY=your_openai_api_key_here
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,13 @@ RUN groupadd -r app && useradd -r -d /app -g app app
|
||||||
COPY pyproject.toml uv.lock ./
|
COPY pyproject.toml uv.lock ./
|
||||||
|
|
||||||
# Install dependencies first (better layer caching)
|
# Install dependencies first (better layer caching)
|
||||||
|
ARG INSTALL_FALKORDB=false
|
||||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
uv sync --frozen --no-dev
|
if [ "$INSTALL_FALKORDB" = "true" ]; then \
|
||||||
|
uv sync --frozen --no-dev --extra falkordb; \
|
||||||
|
else \
|
||||||
|
uv sync --frozen --no-dev; \
|
||||||
|
fi
|
||||||
|
|
||||||
# Copy application code
|
# Copy application code
|
||||||
COPY graphiti_mcp_server.py ./
|
COPY graphiti_mcp_server.py ./
|
||||||
|
|
@ -43,7 +48,7 @@ RUN chown -Rv app:app /app
|
||||||
USER app
|
USER app
|
||||||
|
|
||||||
# Expose port
|
# Expose port
|
||||||
EXPOSE 8000
|
EXPOSE $PORT
|
||||||
|
|
||||||
# Command to run the application
|
# Command to run the application
|
||||||
CMD ["uv", "run", "graphiti_mcp_server.py"]
|
CMD ["uv", "run", "graphiti_mcp_server.py"]
|
||||||
|
|
|
||||||
|
|
@ -82,11 +82,21 @@ uv sync
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The server uses the following environment variables:
|
The server supports both Neo4j and FalkorDB as database backends. Use the `DATABASE_TYPE` environment variable to choose between them.
|
||||||
|
|
||||||
|
#### Neo4j Configuration (default)
|
||||||
|
|
||||||
- `NEO4J_URI`: URI for the Neo4j database (default: `bolt://localhost:7687`)
|
- `NEO4J_URI`: URI for the Neo4j database (default: `bolt://localhost:7687`)
|
||||||
- `NEO4J_USER`: Neo4j username (default: `neo4j`)
|
- `NEO4J_USER`: Neo4j username (default: `neo4j`)
|
||||||
- `NEO4J_PASSWORD`: Neo4j password (default: `demodemo`)
|
- `NEO4J_PASSWORD`: Neo4j password (default: `demodemo`)
|
||||||
|
|
||||||
|
#### FalkorDB Configuration
|
||||||
|
- `DATABASE_TYPE`: Set to `falkordb`
|
||||||
|
- `FALKORDB_HOST`: FalkorDB host (default: `localhost`)
|
||||||
|
- `FALKORDB_PORT`: FalkorDB port (default: `6379`)
|
||||||
|
- `FALKORDB_USERNAME`: FalkorDB username (optional)
|
||||||
|
- `FALKORDB_PASSWORD`: FalkorDB password (optional)
|
||||||
|
|
||||||
- `OPENAI_API_KEY`: OpenAI API key (required for LLM operations)
|
- `OPENAI_API_KEY`: OpenAI API key (required for LLM operations)
|
||||||
- `OPENAI_BASE_URL`: Optional base URL for OpenAI API
|
- `OPENAI_BASE_URL`: Optional base URL for OpenAI API
|
||||||
- `MODEL_NAME`: OpenAI model name to use for LLM operations.
|
- `MODEL_NAME`: OpenAI model name to use for LLM operations.
|
||||||
|
|
@ -115,7 +125,7 @@ uv run graphiti_mcp_server.py
|
||||||
With options:
|
With options:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv run graphiti_mcp_server.py --model gpt-4.1-mini --transport sse
|
uv run graphiti_mcp_server.py --model gpt-4.1-mini --transport sse --database-type falkordb --port 8001
|
||||||
```
|
```
|
||||||
|
|
||||||
Available arguments:
|
Available arguments:
|
||||||
|
|
@ -124,6 +134,7 @@ Available arguments:
|
||||||
- `--small-model`: Overrides the `SMALL_MODEL_NAME` environment variable.
|
- `--small-model`: Overrides the `SMALL_MODEL_NAME` environment variable.
|
||||||
- `--temperature`: Overrides the `LLM_TEMPERATURE` environment variable.
|
- `--temperature`: Overrides the `LLM_TEMPERATURE` environment variable.
|
||||||
- `--transport`: Choose the transport method (sse or stdio, default: sse)
|
- `--transport`: Choose the transport method (sse or stdio, default: sse)
|
||||||
|
- `--database-type`: Choose database backend (neo4j or falkordb, default: neo4j)
|
||||||
- `--group-id`: Set a namespace for the graph (optional). If not provided, defaults to "default".
|
- `--group-id`: Set a namespace for the graph (optional). If not provided, defaults to "default".
|
||||||
- `--destroy-graph`: If set, destroys all Graphiti graphs on startup.
|
- `--destroy-graph`: If set, destroys all Graphiti graphs on startup.
|
||||||
- `--use-custom-entities`: Enable entity extraction using the predefined ENTITY_TYPES
|
- `--use-custom-entities`: Enable entity extraction using the predefined ENTITY_TYPES
|
||||||
|
|
@ -175,11 +186,30 @@ The Docker Compose setup includes a Neo4j container with the following default c
|
||||||
- URI: `bolt://neo4j:7687` (from within the Docker network)
|
- URI: `bolt://neo4j:7687` (from within the Docker network)
|
||||||
- Memory settings optimized for development use
|
- Memory settings optimized for development use
|
||||||
|
|
||||||
|
To run only Neo4j with its MCP server:
|
||||||
|
```bash
|
||||||
|
docker compose up
|
||||||
|
```
|
||||||
|
- Neo4j MCP server on port 8000
|
||||||
|
|
||||||
|
#### FalkorDB Configuration
|
||||||
|
|
||||||
|
The Docker Compose setup includes a FalkorDB container with the following default configuration:
|
||||||
|
- Host: `falkordb`
|
||||||
|
- Port: `6379`
|
||||||
|
- No authentication by default
|
||||||
|
|
||||||
|
To run only FalkorDB with its MCP server:
|
||||||
|
```bash
|
||||||
|
docker compose --profile falkordb up
|
||||||
|
```
|
||||||
|
- FalkorDB MCP server on port 8001
|
||||||
|
|
||||||
#### Running with Docker Compose
|
#### Running with Docker Compose
|
||||||
|
|
||||||
A Graphiti MCP container is available at: `zepai/knowledge-graph-mcp`. The latest build of this container is used by the Compose setup below.
|
A Graphiti MCP container is available at: `zepai/knowledge-graph-mcp`. The latest build of this container is used by the Compose setup below.
|
||||||
|
|
||||||
Start the services using Docker Compose:
|
Start the services using Docker Compose For Neo4j:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up
|
docker compose up
|
||||||
|
|
@ -191,13 +221,25 @@ Or if you're using an older version of Docker Compose:
|
||||||
docker-compose up
|
docker-compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
This will start both the Neo4j database and the Graphiti MCP server. The Docker setup:
|
For FalkorDB:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile falkordb up
|
||||||
|
```
|
||||||
|
|
||||||
|
Or if you're using an older version of Docker Compose:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose --profile falkordb up
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start the database(s) and the Graphiti MCP server(s). The Docker setup:
|
||||||
|
|
||||||
- Uses `uv` for package management and running the server
|
- Uses `uv` for package management and running the server
|
||||||
- Installs dependencies from the `pyproject.toml` file
|
- Installs dependencies from the `pyproject.toml` file
|
||||||
- Connects to the Neo4j container using the environment variables
|
- Connects to the database container using the environment variables
|
||||||
- Exposes the server on port 8000 for HTTP-based SSE transport
|
- Exposes the server on port 8000 (Neo4j) or 8001 (FalkorDB) for HTTP-based SSE transport
|
||||||
- Includes a healthcheck for Neo4j to ensure it's fully operational before starting the MCP server
|
- Includes healthchecks to ensure databases are fully operational before starting the MCP server
|
||||||
|
|
||||||
## Integrating with MCP Clients
|
## Integrating with MCP Clients
|
||||||
|
|
||||||
|
|
|
||||||
8
mcp_server/claude_desktop_config.json
Normal file
8
mcp_server/claude_desktop_config.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"graphiti": {
|
||||||
|
"transport": "sse",
|
||||||
|
"url": "http://localhost:8001/sse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
services:
|
services:
|
||||||
neo4j:
|
neo4j:
|
||||||
|
profiles: [""]
|
||||||
image: neo4j:5.26.0
|
image: neo4j:5.26.0
|
||||||
ports:
|
ports:
|
||||||
- "7474:7474" # HTTP
|
- "7474:7474" # HTTP
|
||||||
|
|
@ -19,7 +20,8 @@ services:
|
||||||
retries: 5
|
retries: 5
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
||||||
graphiti-mcp:
|
graphiti-mcp-neo4j:
|
||||||
|
profiles: [""]
|
||||||
image: zepai/knowledge-graph-mcp:latest
|
image: zepai/knowledge-graph-mcp:latest
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
|
|
@ -31,6 +33,8 @@ services:
|
||||||
neo4j:
|
neo4j:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
environment:
|
environment:
|
||||||
|
- PORT=8000
|
||||||
|
- DATABASE_TYPE=neo4j
|
||||||
- NEO4J_URI=${NEO4J_URI:-bolt://neo4j:7687}
|
- NEO4J_URI=${NEO4J_URI:-bolt://neo4j:7687}
|
||||||
- NEO4J_USER=${NEO4J_USER:-neo4j}
|
- NEO4J_USER=${NEO4J_USER:-neo4j}
|
||||||
- NEO4J_PASSWORD=${NEO4J_PASSWORD:-demodemo}
|
- NEO4J_PASSWORD=${NEO4J_PASSWORD:-demodemo}
|
||||||
|
|
@ -42,6 +46,50 @@ services:
|
||||||
- "8000:8000" # Expose the MCP server via HTTP for SSE transport
|
- "8000:8000" # Expose the MCP server via HTTP for SSE transport
|
||||||
command: ["uv", "run", "graphiti_mcp_server.py", "--transport", "sse"]
|
command: ["uv", "run", "graphiti_mcp_server.py", "--transport", "sse"]
|
||||||
|
|
||||||
|
falkordb:
|
||||||
|
profiles: ["falkordb"]
|
||||||
|
image: falkordb/falkordb:latest
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
command: ["falkordb-server", "--loadmodule", "/FalkorDB/bin/src/falkordb.so"]
|
||||||
|
volumes:
|
||||||
|
- falkordb_data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
graphiti-mcp-falkordb:
|
||||||
|
profiles: ["falkordb"]
|
||||||
|
build:
|
||||||
|
args:
|
||||||
|
INSTALL_FALKORDB: "true"
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
env_file:
|
||||||
|
- path: .env
|
||||||
|
required: false
|
||||||
|
depends_on:
|
||||||
|
falkordb:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
- PORT=8001
|
||||||
|
- DATABASE_TYPE=falkordb
|
||||||
|
- FALKORDB_HOST=falkordb
|
||||||
|
- FALKORDB_PORT=6379
|
||||||
|
- FALKORDB_USER=${FALKORDB_USER:-}
|
||||||
|
- FALKORDB_PASSWORD=${FALKORDB_PASSWORD:-}
|
||||||
|
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||||
|
- MODEL_NAME=${MODEL_NAME}
|
||||||
|
- PATH=/root/.local/bin:${PATH}
|
||||||
|
- SEMAPHORE_LIMIT=${SEMAPHORE_LIMIT:-10}
|
||||||
|
ports:
|
||||||
|
- "8001:8001" # Expose the MCP server via HTTP for SSE transport
|
||||||
|
command: ["uv", "run", "graphiti_mcp_server.py", "--transport", "sse"]
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
neo4j_data:
|
neo4j_data:
|
||||||
neo4j_logs:
|
neo4j_logs:
|
||||||
|
falkordb_data:
|
||||||
|
|
@ -10,15 +10,17 @@ import os
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Any, TypedDict, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
|
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
from openai import AsyncAzureOpenAI
|
from openai import AsyncAzureOpenAI
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
from typing_extensions import TypedDict
|
||||||
|
|
||||||
from graphiti_core import Graphiti
|
from graphiti_core import Graphiti
|
||||||
|
from graphiti_core.driver.neo4j_driver import Neo4jDriver
|
||||||
from graphiti_core.edges import EntityEdge
|
from graphiti_core.edges import EntityEdge
|
||||||
from graphiti_core.embedder.azure_openai import AzureOpenAIEmbedderClient
|
from graphiti_core.embedder.azure_openai import AzureOpenAIEmbedderClient
|
||||||
from graphiti_core.embedder.client import EmbedderClient
|
from graphiti_core.embedder.client import EmbedderClient
|
||||||
|
|
@ -465,6 +467,21 @@ class Neo4jConfig(BaseModel):
|
||||||
password=os.environ.get('NEO4J_PASSWORD', 'password'),
|
password=os.environ.get('NEO4J_PASSWORD', 'password'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class FalkorConfig(BaseModel):
|
||||||
|
"""Configuration for FalkorDB database connection."""
|
||||||
|
|
||||||
|
host: str = 'localhost'
|
||||||
|
port: int = 6379
|
||||||
|
user: str = ''
|
||||||
|
password: str = ''
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_env(cls) -> 'FalkorConfig':
|
||||||
|
host = os.environ.get('FALKORDB_HOST', 'localhost')
|
||||||
|
port = int(os.environ.get('FALKORDB_PORT', 6379))
|
||||||
|
user = os.environ.get('FALKORDB_USER', '')
|
||||||
|
password = os.environ.get('FALKORDB_PASSWORD', '')
|
||||||
|
return cls(host=host, port=port, user=user, password=password)
|
||||||
|
|
||||||
class GraphitiConfig(BaseModel):
|
class GraphitiConfig(BaseModel):
|
||||||
"""Configuration for Graphiti client.
|
"""Configuration for Graphiti client.
|
||||||
|
|
@ -475,18 +492,35 @@ class GraphitiConfig(BaseModel):
|
||||||
llm: GraphitiLLMConfig = Field(default_factory=GraphitiLLMConfig)
|
llm: GraphitiLLMConfig = Field(default_factory=GraphitiLLMConfig)
|
||||||
embedder: GraphitiEmbedderConfig = Field(default_factory=GraphitiEmbedderConfig)
|
embedder: GraphitiEmbedderConfig = Field(default_factory=GraphitiEmbedderConfig)
|
||||||
neo4j: Neo4jConfig = Field(default_factory=Neo4jConfig)
|
neo4j: Neo4jConfig = Field(default_factory=Neo4jConfig)
|
||||||
|
falkordb: FalkorConfig = Field(default_factory=FalkorConfig)
|
||||||
|
|
||||||
group_id: str | None = None
|
group_id: str | None = None
|
||||||
use_custom_entities: bool = False
|
use_custom_entities: bool = False
|
||||||
destroy_graph: bool = False
|
destroy_graph: bool = False
|
||||||
|
database_type: str = 'neo4j'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_env(cls) -> 'GraphitiConfig':
|
def from_env(cls) -> 'GraphitiConfig':
|
||||||
"""Create a configuration instance from environment variables."""
|
"""Create a configuration instance from environment variables."""
|
||||||
return cls(
|
db_type = os.environ.get('DATABASE_TYPE')
|
||||||
llm=GraphitiLLMConfig.from_env(),
|
if not db_type:
|
||||||
embedder=GraphitiEmbedderConfig.from_env(),
|
raise ValueError('DATABASE_TYPE environment variable must be set (e.g., "neo4j" or "falkordb")')
|
||||||
neo4j=Neo4jConfig.from_env(),
|
if db_type == 'neo4j':
|
||||||
)
|
return cls(
|
||||||
|
llm=GraphitiLLMConfig.from_env(),
|
||||||
|
embedder=GraphitiEmbedderConfig.from_env(),
|
||||||
|
neo4j=Neo4jConfig.from_env(),
|
||||||
|
database_type=db_type,
|
||||||
|
)
|
||||||
|
elif db_type == 'falkordb':
|
||||||
|
return cls(
|
||||||
|
llm=GraphitiLLMConfig.from_env(),
|
||||||
|
embedder=GraphitiEmbedderConfig.from_env(),
|
||||||
|
falkordb=FalkorConfig.from_env(),
|
||||||
|
database_type=db_type,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Unsupported DATABASE_TYPE: {db_type}')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_cli_and_env(cls, args: argparse.Namespace) -> 'GraphitiConfig':
|
def from_cli_and_env(cls, args: argparse.Namespace) -> 'GraphitiConfig':
|
||||||
|
|
@ -587,13 +621,38 @@ async def initialize_graphiti():
|
||||||
if not config.neo4j.uri or not config.neo4j.user or not config.neo4j.password:
|
if not config.neo4j.uri or not config.neo4j.user or not config.neo4j.password:
|
||||||
raise ValueError('NEO4J_URI, NEO4J_USER, and NEO4J_PASSWORD must be set')
|
raise ValueError('NEO4J_URI, NEO4J_USER, and NEO4J_PASSWORD must be set')
|
||||||
|
|
||||||
|
# Validate FalkorDB configuration
|
||||||
|
if config.database_type == 'falkordb' and (not config.falkordb.host or not config.falkordb.port):
|
||||||
|
raise ValueError('FALKORDB_HOST and FALKORDB_PORT must be set for FalkorDB')
|
||||||
|
|
||||||
embedder_client = config.embedder.create_client()
|
embedder_client = config.embedder.create_client()
|
||||||
|
|
||||||
|
# Construct the driver based on the database_type
|
||||||
|
driver = None
|
||||||
|
if config.database_type == 'neo4j':
|
||||||
|
driver = Neo4jDriver(
|
||||||
|
uri=config.neo4j.uri,
|
||||||
|
user=config.neo4j.user,
|
||||||
|
password=config.neo4j.password,
|
||||||
|
)
|
||||||
|
elif config.database_type == 'falkordb':
|
||||||
|
from graphiti_core.driver.falkordb_driver import FalkorDriver
|
||||||
|
host = config.falkordb.host if hasattr(config.falkordb, 'host') else 'localhost'
|
||||||
|
port = int(config.falkordb.port) if hasattr(config.falkordb, 'port') else 6379
|
||||||
|
username = config.falkordb.user or None
|
||||||
|
password = config.falkordb.password or None
|
||||||
|
driver = FalkorDriver(
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Unsupported database type: {config.database_type}')
|
||||||
|
|
||||||
# Initialize Graphiti client
|
# Initialize Graphiti client
|
||||||
graphiti_client = Graphiti(
|
graphiti_client = Graphiti(
|
||||||
uri=config.neo4j.uri,
|
graph_driver=driver,
|
||||||
user=config.neo4j.user,
|
|
||||||
password=config.neo4j.password,
|
|
||||||
llm_client=llm_client,
|
llm_client=llm_client,
|
||||||
embedder=embedder_client,
|
embedder=embedder_client,
|
||||||
max_coroutines=SEMAPHORE_LIMIT,
|
max_coroutines=SEMAPHORE_LIMIT,
|
||||||
|
|
@ -606,7 +665,7 @@ async def initialize_graphiti():
|
||||||
|
|
||||||
# Initialize the graph database with Graphiti's indices
|
# Initialize the graph database with Graphiti's indices
|
||||||
await graphiti_client.build_indices_and_constraints()
|
await graphiti_client.build_indices_and_constraints()
|
||||||
logger.info('Graphiti client initialized successfully')
|
logger.info(f'Graphiti client initialized successfully with {config.database_type}')
|
||||||
|
|
||||||
# Log configuration details for transparency
|
# Log configuration details for transparency
|
||||||
if llm_client:
|
if llm_client:
|
||||||
|
|
@ -616,6 +675,7 @@ async def initialize_graphiti():
|
||||||
logger.info('No LLM client configured - entity extraction will be limited')
|
logger.info('No LLM client configured - entity extraction will be limited')
|
||||||
|
|
||||||
logger.info(f'Using group_id: {config.group_id}')
|
logger.info(f'Using group_id: {config.group_id}')
|
||||||
|
logger.info(f'Using database type: {config.database_type}')
|
||||||
logger.info(
|
logger.info(
|
||||||
f'Custom entity extraction: {"enabled" if config.use_custom_entities else "disabled"}'
|
f'Custom entity extraction: {"enabled" if config.use_custom_entities else "disabled"}'
|
||||||
)
|
)
|
||||||
|
|
@ -1131,7 +1191,7 @@ async def clear_graph() -> SuccessResponse | ErrorResponse:
|
||||||
|
|
||||||
@mcp.resource('http://graphiti/status')
|
@mcp.resource('http://graphiti/status')
|
||||||
async def get_status() -> StatusResponse:
|
async def get_status() -> StatusResponse:
|
||||||
"""Get the status of the Graphiti MCP server and Neo4j connection."""
|
"""Get the status of the Graphiti MCP server and database connection."""
|
||||||
global graphiti_client
|
global graphiti_client
|
||||||
|
|
||||||
if graphiti_client is None:
|
if graphiti_client is None:
|
||||||
|
|
@ -1145,14 +1205,14 @@ async def get_status() -> StatusResponse:
|
||||||
client = cast(Graphiti, graphiti_client)
|
client = cast(Graphiti, graphiti_client)
|
||||||
|
|
||||||
# Test database connection
|
# Test database connection
|
||||||
await client.driver.client.verify_connectivity() # type: ignore
|
await client.driver.health_check() # type: ignore # type: ignore
|
||||||
|
|
||||||
return StatusResponse(
|
return StatusResponse(
|
||||||
status='ok', message='Graphiti MCP server is running and connected to Neo4j'
|
status='ok', message=f'Graphiti MCP server is running and connected to {config.database_type}'
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
logger.error(f'Error checking Neo4j connection: {error_msg}')
|
logger.error(f'Error checking {config.database_type} connection: {error_msg}')
|
||||||
return StatusResponse(
|
return StatusResponse(
|
||||||
status='error',
|
status='error',
|
||||||
message=f'Graphiti MCP server is running but Neo4j connection failed: {error_msg}',
|
message=f'Graphiti MCP server is running but Neo4j connection failed: {error_msg}',
|
||||||
|
|
@ -1200,6 +1260,17 @@ async def initialize_server() -> MCPConfig:
|
||||||
default=os.environ.get('MCP_SERVER_HOST'),
|
default=os.environ.get('MCP_SERVER_HOST'),
|
||||||
help='Host to bind the MCP server to (default: MCP_SERVER_HOST environment variable)',
|
help='Host to bind the MCP server to (default: MCP_SERVER_HOST environment variable)',
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--port',
|
||||||
|
type=int,
|
||||||
|
default=int(os.environ.get('PORT', 8000)),
|
||||||
|
help='Port to run the MCP server on (default: 8000 or value of PORT env variable)',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--database-type',
|
||||||
|
choices=['neo4j', 'falkordb'],
|
||||||
|
help='Type of database to use (default: neo4j)',
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
@ -1226,6 +1297,11 @@ async def initialize_server() -> MCPConfig:
|
||||||
# Set MCP server host from CLI or env
|
# Set MCP server host from CLI or env
|
||||||
mcp.settings.host = args.host
|
mcp.settings.host = args.host
|
||||||
|
|
||||||
|
if args.port:
|
||||||
|
logger.info(f'Setting MCP server port to: {args.port}')
|
||||||
|
# Set MCP server port from CLI or env
|
||||||
|
mcp.settings.port = args.port
|
||||||
|
|
||||||
# Return MCP configuration
|
# Return MCP configuration
|
||||||
return MCPConfig.from_cli(args)
|
return MCPConfig.from_cli(args)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ requires-python = ">=3.10,<4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mcp>=1.5.0",
|
"mcp>=1.5.0",
|
||||||
"openai>=1.68.2",
|
"openai>=1.68.2",
|
||||||
"graphiti-core>=0.14.0",
|
"graphiti-core==0.18.8",
|
||||||
"azure-identity>=1.21.0",
|
"azure-identity>=1.21.0",
|
||||||
"graphiti-core",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
falkordb = ["falkordb>=1.1.2,<2.0.0"]
|
||||||
892
mcp_server/uv.lock
generated
892
mcp_server/uv.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -423,6 +423,14 @@
|
||||||
"created_at": "2025-10-15T16:29:33Z",
|
"created_at": "2025-10-15T16:29:33Z",
|
||||||
"repoId": 840056306,
|
"repoId": 840056306,
|
||||||
"pullRequestNo": 1005
|
"pullRequestNo": 1005
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dontang97",
|
||||||
|
"id": 88384441,
|
||||||
|
"comment_id": 3431443627,
|
||||||
|
"created_at": "2025-10-22T09:52:01Z",
|
||||||
|
"repoId": 840056306,
|
||||||
|
"pullRequestNo": 1020
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue