Use official Dockerfile.standalone for custom MCP server build

This commit is contained in:
Lars Varming 2025-11-08 21:07:28 +01:00
parent 66df6ce7df
commit aab233496c
16 changed files with 2574 additions and 85 deletions

View file

@ -92,8 +92,8 @@ jobs:
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./mcp_server/docker/Dockerfile.custom
context: ./mcp_server
file: ./mcp_server/docker/Dockerfile.standalone
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.docker_tags.outputs.tags }}

1
.serena/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/cache

View file

@ -0,0 +1,76 @@
# Graphiti Codebase Structure
## Root Directory Layout
```
graphiti/
├── graphiti_core/ # Core library (main Python package)
├── server/ # FastAPI REST API service
├── mcp_server/ # Model Context Protocol server for AI assistants
├── tests/ # Test suite (unit and integration tests)
├── examples/ # Example implementations and use cases
├── images/ # Documentation images and assets
├── signatures/ # CLA signatures
├── .github/ # GitHub Actions workflows
├── pyproject.toml # Project configuration and dependencies
├── Makefile # Development commands
├── README.md # Main documentation
├── CLAUDE.md # Claude Code assistant instructions
├── CONTRIBUTING.md # Contribution guidelines
└── docker-compose.yml # Docker configuration
```
## Core Library (`graphiti_core/`)
### Main Components
- **`graphiti.py`**: Main entry point containing the `Graphiti` class that orchestrates all functionality
- **`nodes.py`**: Core node/entity data structures
- **`edges.py`**: Core edge/relationship data structures
- **`graphiti_types.py`**: Type definitions
- **`errors.py`**: Custom exception classes
- **`helpers.py`**: Utility helper functions
- **`graph_queries.py`**: Graph query definitions
- **`decorators.py`**: Function decorators
- **`tracer.py`**: OpenTelemetry tracing support
### Subdirectories
- **`driver/`**: Database drivers for Neo4j, FalkorDB, Kuzu, Neptune
- **`llm_client/`**: LLM clients for OpenAI, Anthropic, Gemini, Groq
- **`embedder/`**: Embedding clients for various providers (OpenAI, Voyage, local models)
- **`cross_encoder/`**: Cross-encoder models for reranking
- **`search/`**: Hybrid search implementation with configurable strategies
- **`prompts/`**: LLM prompts for entity extraction, deduplication, summarization
- **`utils/`**: Maintenance operations, bulk processing, datetime handling
- **`models/`**: Pydantic models for data structures
- **`migrations/`**: Database migration scripts
- **`telemetry/`**: Analytics and telemetry code
## Server (`server/`)
- **`graph_service/main.py`**: FastAPI application entry point
- **`routers/`**: API endpoint definitions (ingestion, retrieval)
- **`dto/`**: Data Transfer Objects for API contracts
- Has its own `Makefile` for server-specific commands
## MCP Server (`mcp_server/`)
- **`graphiti_mcp_server.py`**: Model Context Protocol server implementation
- **`docker-compose.yml`**: Containerized deployment with Neo4j
- Has its own `pyproject.toml` and dependencies
## Tests (`tests/`)
- **Unit tests**: Standard pytest tests
- **Integration tests**: Files with `_int` suffix (require database connections)
- **`evals/`**: End-to-end evaluation scripts
- **`conftest.py`**: Pytest configuration and fixtures (at root level)
## Key Classes
From `graphiti_core/graphiti.py`:
- `Graphiti`: Main orchestrator class
- `AddEpisodeResults`: Results from adding episodes
- `AddBulkEpisodeResults`: Results from bulk episode operations
- `AddTripletResults`: Results from adding triplets
## Configuration Files
- **`pyproject.toml`**: Main project configuration (dependencies, build system, tool configs)
- **`pytest.ini`**: Pytest configuration
- **`.env.example`**: Example environment variables
- **`docker-compose.yml`**: Docker setup for development
- **`docker-compose.test.yml`**: Docker setup for testing

View file

@ -0,0 +1,85 @@
# Code Style and Conventions
## Formatting and Linting
### Ruff Configuration
- **Tool**: Ruff (handles both linting and formatting)
- **Line length**: 100 characters
- **Quote style**: Single quotes (`'`)
- **Indentation**: Spaces (not tabs)
- **Docstring code format**: Enabled (formats code in docstrings)
### Linting Rules (Ruff)
Enabled rule sets:
- `E` - pycodestyle errors
- `F` - Pyflakes
- `UP` - pyupgrade (Python version upgrades)
- `B` - flake8-bugbear (common bugs)
- `SIM` - flake8-simplify (simplification suggestions)
- `I` - isort (import sorting)
Ignored rules:
- `E501` - Line too long (handled by line-length setting)
### Type Checking
- **Tool**: Pyright
- **Python version**: 3.10+
- **Type checking mode**: `basic` for main project, `standard` for server
- **Scope**: Main type checking focuses on `graphiti_core/` directory
- **Type hints**: Required and enforced
## Code Conventions
### General Guidelines
- Use type hints for all function parameters and return values
- Follow PEP 8 style guide (enforced by Ruff)
- Use Pydantic models for data validation and structure
- Prefer async/await for I/O operations
- Use descriptive variable and function names
### Python Version
- Minimum supported: Python 3.10
- Maximum supported: Python 3.x (< 4.0)
### Import Organization
Imports are automatically organized by Ruff using isort rules:
1. Standard library imports
2. Third-party imports
3. Local application imports
### Documentation
- Use docstrings for classes and public methods
- Keep README.md and CLAUDE.md up to date
- Add examples to `examples/` folder for new features
- Document breaking changes and migrations
### Testing Conventions
- Use pytest for all tests
- Async tests use `pytest-asyncio`
- Integration tests must have `_int` suffix in filename or test name
- Unit tests should not require external services
- Use fixtures from `conftest.py`
- Parallel execution supported via `pytest-xdist`
### Naming Conventions
- Classes: PascalCase (e.g., `Graphiti`, `AddEpisodeResults`)
- Functions/methods: snake_case (e.g., `add_episode`, `build_indices`)
- Constants: UPPER_SNAKE_CASE
- Private methods/attributes: prefix with underscore (e.g., `_internal_method`)
### Error Handling
- Use custom exceptions from `graphiti_core/errors.py`
- Provide meaningful error messages
- Use `tenacity` for retry logic on external service calls
### LLM Provider Support
- The codebase supports multiple LLM providers
- Best compatibility with services supporting structured output (OpenAI, Gemini)
- Smaller models may cause schema validation issues
- Always validate LLM outputs against expected schemas
## Configuration and Dependencies
- Use `pyproject.toml` for all project configuration
- Pin minimum versions in dependencies
- Optional features go in `[project.optional-dependencies]`
- Development dependencies go in `dev` extra

View file

@ -0,0 +1,169 @@
# Development Commands
## Package Manager
This project uses **uv** (https://docs.astral.sh/uv/) instead of pip or poetry.
## Main Project Commands (from project root)
### Installation
```bash
# Install all dependencies including dev tools
make install
# OR
uv sync --extra dev
```
### Code Formatting
```bash
# Format code (runs ruff import sorting + code formatting)
make format
# Equivalent to:
# uv run ruff check --select I --fix
# uv run ruff format
```
### Linting
```bash
# Lint code (runs ruff checks + pyright type checking)
make lint
# Equivalent to:
# uv run ruff check
# uv run pyright ./graphiti_core
```
### Testing
```bash
# Run unit tests only (excludes integration tests)
make test
# Equivalent to:
# DISABLE_FALKORDB=1 DISABLE_KUZU=1 DISABLE_NEPTUNE=1 uv run pytest -m "not integration"
# Run all tests including integration tests
uv run pytest
# Run only integration tests
uv run pytest -k "_int"
# Run specific test file
uv run pytest tests/test_specific_file.py
# Run specific test method
uv run pytest tests/test_file.py::test_method_name
# Run tests in parallel (faster)
uv run pytest -n auto
```
### Combined Checks
```bash
# Run format, lint, and test in sequence
make check
# OR
make all
```
## Server Commands (from server/ directory)
```bash
cd server/
# Install server dependencies
uv sync --extra dev
# Run server in development mode with auto-reload
uvicorn graph_service.main:app --reload
# Format server code
make format
# Lint server code
make lint
# Test server code
make test
```
## MCP Server Commands (from mcp_server/ directory)
```bash
cd mcp_server/
# Install MCP server dependencies
uv sync
# Run with Docker Compose
docker-compose up
# Stop Docker Compose
docker-compose down
```
## Environment Variables for Testing
### Required for Integration Tests
```bash
export TEST_OPENAI_API_KEY=...
export TEST_OPENAI_MODEL=...
export TEST_ANTHROPIC_API_KEY=...
# For Neo4j
export TEST_URI=neo4j://...
export TEST_USER=...
export TEST_PASSWORD=...
```
### Optional Runtime Variables
```bash
export OPENAI_API_KEY=... # For LLM inference
export USE_PARALLEL_RUNTIME=true # Neo4j parallel runtime (enterprise only)
export ANTHROPIC_API_KEY=... # For Claude models
export GOOGLE_API_KEY=... # For Gemini models
export GROQ_API_KEY=... # For Groq models
export VOYAGE_API_KEY=... # For VoyageAI embeddings
```
## Git Workflow
```bash
# Create a new branch
git checkout -b feature/your-feature-name
# After making changes, run checks
make check
# Commit changes (ensure all checks pass first)
git add .
git commit -m "Your commit message"
# Push to your fork
git push origin feature/your-feature-name
```
## Common Development Tasks
### Before Submitting PR
1. `make check` - Ensures code is formatted, linted, and tested
2. Verify all tests pass including integration tests if applicable
3. Update documentation if needed
### Adding New Dependencies
Edit `pyproject.toml`:
- Core dependencies → `[project.dependencies]`
- Optional features → `[project.optional-dependencies]`
- Dev dependencies → `[project.optional-dependencies.dev]`
Then run:
```bash
uv sync --extra dev
```
### Database Setup
- **Neo4j**: Version 5.26+ required, use Neo4j Desktop
- **FalkorDB**: Version 1.1.2+ as alternative backend
## Tool Versions
- Python: 3.10+
- UV: Latest stable
- Pytest: 8.3.3+
- Ruff: 0.7.1+
- Pyright: 1.1.404+

View file

@ -0,0 +1,104 @@
# Git Workflow for Graphiti Fork
## Repository Setup
This repository is a fork of the official Graphiti project with custom MCP server enhancements.
### Remote Configuration
```bash
origin https://github.com/Varming73/graphiti.git (your fork)
upstream https://github.com/getzep/graphiti.git (official Graphiti)
```
**Best Practice Convention:**
- `origin` = Your fork (where you push your changes)
- `upstream` = Official project (where you pull updates from)
## Common Workflows
### Push Your Changes
```bash
git add <files>
git commit -m "Your message"
git push origin main
```
### Pull Upstream Updates
```bash
# Fetch latest from upstream
git fetch upstream
# Merge upstream changes into your main branch
git merge upstream/main
# Or rebase if you prefer
git rebase upstream/main
# Push to your fork
git push origin main
```
### Check for Upstream Updates
```bash
git fetch upstream
git log HEAD..upstream/main --oneline # See what's new
```
### Sync with Upstream (Full Update)
```bash
# Fetch upstream
git fetch upstream
# Switch to main
git checkout main
# Merge or rebase
git merge upstream/main # or git rebase upstream/main
# Push to your fork
git push origin main
```
## Current Status
### Last Repository Replacement
- **Date**: 2025-11-08
- **Action**: Force-pushed clean code to replace "messed" project
- **Commit**: Added get_entities_by_type and compare_facts_over_time MCP tools
- **Result**: Successfully replaced entire fork history with clean implementation
### Upstream Tracking
- Upstream connection verified and working
- Can freely pull updates from official Graphiti project
- Your customizations remain in your fork
## MCP Server Customizations
Your fork contains these custom MCP tools (not in upstream):
1. `get_entities_by_type` - Retrieve entities by type classification
2. `compare_facts_over_time` - Compare facts between time periods
3. Enhanced `add_memory` UUID documentation
**Important**: When pulling upstream updates, these customizations are ONLY in `mcp_server/src/graphiti_mcp_server.py`. You may need to manually merge if upstream changes that file.
## Safety Notes
- **Never push to upstream** - You don't have permission and shouldn't try
- **Always test locally** before pushing to origin
- **Pull upstream regularly** to stay current with bug fixes and features
- **Document custom changes** in commit messages for future reference
## If You Need to Reset to Upstream
```bash
# Backup your current work first!
git checkout -b backup-branch
# Reset main to match upstream exactly
git checkout main
git reset --hard upstream/main
git push origin main --force
# Then cherry-pick your custom commits from backup-branch
```

View file

@ -0,0 +1,193 @@
# MCP Server Tools Documentation
## Overview
The Graphiti MCP Server exposes Graphiti functionality through the Model Context Protocol (MCP) for AI assistants (like those in LibreChat). Each tool is decorated with `@mcp.tool()` and provides a specific capability.
## Tool Naming Convention
All tools follow MCP best practices:
- **snake_case naming**: All lowercase with underscores
- **Action-oriented**: Start with verbs (add, search, get, compare, delete)
- **Concise descriptions**: First line describes core action
- **Clear parameters**: Descriptions specify format and provide examples
Reference: https://modelcontextprotocol.io/specification/2025-06-18/server/tools
## Recent Changes
### 2025-11-08 - UUID Parameter Documentation Enhanced
**Problem**: LLMs were attempting to generate and provide UUIDs when adding NEW memories, which should never happen - UUIDs must be auto-generated for new episodes.
**Solution**: Enhanced the `uuid` parameter documentation in `add_memory` to be very explicit: "NEVER provide a UUID for new episodes - UUIDs are auto-generated. This parameter can ONLY be used for updating an existing episode by providing its existing UUID."
**Impact**: Clear guidance for LLMs to prevent them from trying to generate UUIDs for new memories while preserving the ability to update existing episodes.
## Tool List
### Core Memory Management
1. **add_memory** - Add episodes to the knowledge graph ✨ IMPROVED DOCS
2. **clear_graph** - Clear all data for specified group IDs
3. **get_status** - Get server and database connection status
### Search and Retrieval Tools
4. **search_nodes** - Search for nodes/entities using semantic search
5. **search_memory_facts** - Search for facts/relationships using semantic search
6. **get_entities_by_type** ⭐ NEW - Retrieve entities by their type classification
7. **compare_facts_over_time** ⭐ NEW - Compare facts between two time periods
### Entity and Episode Management
8. **get_entity_edge** - Retrieve a specific entity edge by UUID
9. **delete_entity_edge** - Delete an entity edge from the graph
10. **get_episodes** - Retrieve episodes from the graph
11. **delete_episode** - Delete an episode from the graph
## Tool Details
### add_memory (Updated Documentation)
**Purpose**: Add episodes to the knowledge graph
**MCP-Compliant Description**: "Add an episode to memory. This is the primary way to add information to the graph."
**Parameters**:
- `name`: str - Name of the episode
- `episode_body`: str - Content to persist (JSON string for source='json')
- `group_id`: Optional[str] - Group ID for this graph (uses default if not provided)
- `source`: str = 'text' - Source type ('text', 'json', or 'message')
- `source_description`: str = '' - Optional description of the source
- `uuid`: Optional[str] = None - **NEVER provide for NEW episodes**. Can ONLY be used to update an existing episode by providing its UUID.
**UUID Parameter Behavior**:
- **For NEW episodes**: Do NOT provide - auto-generated
- **For UPDATING episodes**: Provide the existing episode's UUID to replace/update it
- **Other uses**: Idempotent operations or external system integration (advanced)
**Implementation Notes**:
- Returns immediately, processes in background
- Episodes for same group_id processed sequentially
- Providing a UUID updates the episode with that UUID if it exists
### get_entities_by_type
**Added**: 2025-11-08
**Purpose**: Essential for PKM (Personal Knowledge Management) - enables browsing entities by their type classification
**MCP-Compliant Description**: "Retrieve entities by their type classification."
**Parameters**:
- `entity_types`: List[str] - Entity types to retrieve (e.g., ["Pattern", "Insight", "Preference"])
- `group_ids`: Optional[List[str]] - Filter by group IDs
- `max_entities`: int = 20 - Maximum entities to return
- `query`: Optional[str] - Optional search query to filter entities
**Implementation Notes**:
- Uses `SearchFilters(node_labels=entity_types)` from graphiti_core
- Uses `NODE_HYBRID_SEARCH_RRF` search config
- When query is provided: semantic search with type filter
- When query is empty: uses space (' ') as generic query to retrieve all of the type
- Returns `NodeSearchResponse` (same format as search_nodes)
**Use Cases**:
- "Show me all my Preferences"
- "List Patterns I've identified"
- "Get Insights about productivity"
- "Find all documented Procedures"
**Example**:
```python
# Get all preferences
get_entities_by_type(entity_types=["Preference"])
# Get patterns and insights about productivity
get_entities_by_type(
entity_types=["Pattern", "Insight"],
query="productivity"
)
```
### compare_facts_over_time
**Added**: 2025-11-08
**Purpose**: Track how knowledge/understanding evolved over time - critical for seeing how Patterns, Insights, and understanding changed
**MCP-Compliant Description**: "Compare facts between two time periods."
**Parameters**:
- `query`: str - Search query for facts to compare
- `start_time`: str - ISO 8601 timestamp (e.g., "2024-01-01" or "2024-01-01T10:30:00Z")
- `end_time`: str - ISO 8601 timestamp
- `group_ids`: Optional[List[str]] - Filter by group IDs
- `max_facts_per_period`: int = 10 - Max facts per time category
**Returns**: Dictionary with:
- `facts_from_start`: Facts valid at start_time
- `facts_at_end`: Facts valid at end_time
- `facts_invalidated`: Facts that were invalidated between start and end
- `facts_added`: Facts that became valid between start and end
- `summary`: Count statistics
**Implementation Notes**:
- Uses `DateFilter` and `ComparisonOperator` from graphiti_core.search.search_filters
- Uses `EDGE_HYBRID_SEARCH_RRF` search config
- Makes 4 separate searches with temporal filters:
1. Facts valid at start (valid_at <= start AND (invalid_at > start OR invalid_at IS NULL))
2. Facts valid at end (valid_at <= end AND (invalid_at > end OR invalid_at IS NULL))
3. Facts invalidated (invalid_at > start AND invalid_at <= end)
4. Facts added (created_at > start AND created_at <= end)
- Uses `format_fact_result()` helper for consistent formatting
**Use Cases**:
- "How did my understanding of sleep patterns change this month?"
- "What productivity insights were replaced?"
- "Show me how my procedures evolved"
- "Track changes in my preferences over time"
**Example**:
```python
compare_facts_over_time(
query="productivity patterns",
start_time="2024-01-01",
end_time="2024-03-01"
)
```
## Implementation Constraints
### Safe Design Principles
All tools follow strict constraints to maintain upstream compatibility:
1. **Only use public Graphiti APIs** - No custom Cypher queries, no internal methods
2. **MCP server only changes** - No modifications to graphiti_core/
3. **Existing patterns** - Follow same structure as existing tools
4. **Standard imports** - Only use imports already in the file or from stable public APIs
5. **MCP compliance** - Follow MCP specification for tool naming and descriptions
6. **LLM-friendly documentation** - Clear guidance to prevent LLM confusion (e.g., UUID usage)
### Dependencies
All required imports are either:
- Already present in the file (SearchFilters, format_fact_result)
- From stable public APIs (DateFilter, ComparisonOperator, search configs)
No new dependencies added to pyproject.toml.
## Testing Notes
### Validation Tests Passed
- ✅ Python syntax check (py_compile)
- ✅ Ruff formatting (auto-formatted)
- ✅ Ruff linting (all checks passed)
- ✅ No custom Cypher or internal APIs used
- ✅ Follows project code style conventions
- ✅ MCP specification compliance verified
- ✅ UUID documentation enhanced to prevent LLM misuse
### Manual Testing Required
Before production use, test:
1. add_memory without LLM trying to provide UUIDs for NEW episodes
2. add_memory with UUID for UPDATING existing episodes
3. get_entities_by_type with various entity type combinations
4. get_entities_by_type with and without query parameter
5. compare_facts_over_time with various date ranges
6. Error handling for invalid inputs (empty types, bad dates, etc.)
## File Location
`mcp_server/src/graphiti_mcp_server.py`
- `add_memory`: Updated documentation (lines 320-403)
- `get_entities_by_type`: Inserted after `search_nodes` function (lines 486-583)
- `compare_facts_over_time`: Inserted after `search_memory_facts` function (lines 585-766)

View file

@ -0,0 +1,52 @@
# Graphiti Project Overview
## Purpose
Graphiti is a Python framework for building and querying temporally-aware knowledge graphs, specifically designed for AI agents operating in dynamic environments. It continuously integrates user interactions, structured/unstructured data, and external information into a coherent, queryable graph with incremental updates and efficient retrieval.
## Key Features
- **Bi-temporal data model**: Explicit tracking of event occurrence times
- **Hybrid retrieval**: Combining semantic embeddings, keyword search (BM25), and graph traversal
- **Custom entity definitions**: Support via Pydantic models
- **Real-time incremental updates**: No batch recomputation required
- **Multiple graph backends**: Neo4j and FalkorDB support
- **Optional OpenTelemetry tracing**: For distributed systems
## Use Cases
- Integrate and maintain dynamic user interactions and business data
- Facilitate state-based reasoning and task automation for agents
- Query complex, evolving data with semantic, keyword, and graph-based search methods
## Relationship to Zep
Graphiti powers the core of Zep, a turn-key context engineering platform for AI Agents. This is the open-source version that provides flexibility for custom implementations.
## Tech Stack
- **Language**: Python 3.10+
- **Package Manager**: uv (modern, fast Python package installer)
- **Core Dependencies**:
- Pydantic 2.11.5+ (data validation and models)
- Neo4j 5.26.0+ (primary graph database)
- OpenAI 1.91.0+ (LLM inference and embeddings)
- Tenacity 9.0.0+ (retry logic)
- DiskCache 5.6.3+ (caching)
- **Optional Integrations**:
- Anthropic (Claude models)
- Google Gemini
- Groq
- FalkorDB (alternative graph database)
- Kuzu (graph database)
- Neptune (AWS graph database)
- VoyageAI (embeddings)
- Sentence Transformers (local embeddings)
- OpenTelemetry (tracing)
- **Development Tools**:
- Ruff (linting and formatting)
- Pyright (type checking)
- Pytest (testing framework with pytest-asyncio and pytest-xdist)
## Project Version
Current version: 0.22.1pre2 (pre-release)
## Repository
https://github.com/getzep/graphiti

View file

@ -0,0 +1,183 @@
# System Commands (Darwin/macOS)
This project is being developed on **Darwin** (macOS). Here are the relevant system commands:
## File System Navigation
### Basic Commands
```bash
ls # List directory contents
ls -la # List all files including hidden, with details
cd <dir> # Change directory
pwd # Print working directory
mkdir <dir> # Create directory
rm <file> # Remove file
rm -rf <dir> # Remove directory recursively
```
### macOS-Specific Notes
- Case-insensitive filesystem by default (though case-preserving)
- Hidden files start with `.` (like `.env`, `.gitignore`)
- Use `open .` to open current directory in Finder
- Use `open <file>` to open file with default application
## File Operations
### Reading Files
```bash
cat <file> # Display entire file
head -n 20 <file> # First 20 lines
tail -n 20 <file> # Last 20 lines
less <file> # Page through file
```
### Searching Files
```bash
find . -name "*.py" # Find Python files
find . -type f -name "test_*.py" # Find test files
grep -r "pattern" . # Search for pattern recursively
grep -r "pattern" --include="*.py" . # Search only in Python files
```
## Git Commands
### Basic Git Operations
```bash
git status # Check status
git branch # List branches
git checkout -b <branch> # Create and switch to new branch
git add <file> # Stage file
git add . # Stage all changes
git commit -m "message" # Commit changes
git push origin <branch> # Push to remote
git pull # Pull latest changes
git diff # Show unstaged changes
git diff --staged # Show staged changes
git log # View commit history
git log --oneline # Compact commit history
```
### Current Repository Info
- Current branch: `main`
- Main branch for PRs: `main`
## Process Management
```bash
ps aux # List all running processes
ps aux | grep python # Find Python processes
kill <PID> # Terminate process
kill -9 <PID> # Force kill process
```
## Environment Variables
### View Environment
```bash
env # List all environment variables
echo $PATH # Show PATH variable
echo $OPENAI_API_KEY # Show specific variable
```
### Set Environment Variables
```bash
export VAR_NAME=value # Set for current session
export OPENAI_API_KEY="sk-..." # Example
```
### Permanent Environment Variables
For permanent variables, add to `~/.zshrc` or `~/.bash_profile`:
```bash
echo 'export VAR_NAME=value' >> ~/.zshrc
source ~/.zshrc
```
## Docker Commands (if applicable)
```bash
docker ps # List running containers
docker ps -a # List all containers
docker-compose up # Start services
docker-compose up -d # Start in background
docker-compose down # Stop services
docker-compose logs -f # Follow logs
docker-compose ps # List compose services
```
## Network Commands
```bash
curl <url> # Make HTTP request
curl -I <url> # Get headers only
ping <host> # Check connectivity
netstat -an | grep LISTEN # Show listening ports
lsof -i :<port> # See what's using a port
```
## Permissions
```bash
chmod +x <file> # Make file executable
chmod 644 <file> # Set file permissions (rw-r--r--)
chmod 755 <file> # Set file permissions (rwxr-xr-x)
chown <user> <file> # Change file owner
```
## Useful Utilities
### Text Processing
```bash
wc -l <file> # Count lines
wc -w <file> # Count words
sort <file> # Sort lines
uniq <file> # Remove duplicates
awk '{print $1}' <file> # Print first column
sed 's/old/new/g' <file> # Replace text
```
### Archives
```bash
tar -czf archive.tar.gz <dir> # Create tar.gz archive
tar -xzf archive.tar.gz # Extract tar.gz archive
zip -r archive.zip <dir> # Create zip archive
unzip archive.zip # Extract zip archive
```
## macOS-Specific Commands
```bash
pbcopy < <file> # Copy file contents to clipboard
pbpaste > <file> # Paste clipboard to file
caffeinate # Prevent system sleep
say "text" # Text-to-speech
```
## Python/UV Specific
```bash
which python3 # Find Python executable location
python3 --version # Check Python version
uv --version # Check UV version
uv run python # Run Python with UV
uv pip list # List installed packages
```
## Development Workflow Integration
For this project, you'll commonly use:
```bash
# Navigate to project
cd /Users/lvarming/it-setup/projects/graphiti
# Check git status
git status
# Run development checks
make check
# Search for code patterns
grep -r "def add_episode" graphiti_core/
# Find specific files
find . -name "graphiti.py"
```

View file

@ -0,0 +1,128 @@
# Task Completion Checklist
When you complete a coding task, follow these steps to ensure quality:
## 1. Format Code
```bash
make format
```
This will:
- Sort imports using ruff (isort rules)
- Format code to 100-character line length
- Apply single-quote style
- Format code in docstrings
## 2. Lint Code
```bash
make lint
```
This will:
- Run ruff checks for code quality issues
- Run pyright type checking on `graphiti_core/`
- Identify any style violations or type errors
Fix any issues reported by the linter.
## 3. Run Tests
```bash
# Run unit tests (default)
make test
# OR run all tests including integration tests
uv run pytest
```
Ensure all tests pass. If you:
- Modified existing functionality: Verify related tests still pass
- Added new functionality: Consider adding new tests
- Fixed a bug: Consider adding a regression test
## 4. Integration Testing (if applicable)
If your changes affect:
- Database interactions
- LLM integrations
- External service calls
Run integration tests:
```bash
# Ensure environment variables are set
export TEST_OPENAI_API_KEY=...
export TEST_URI=neo4j://...
export TEST_USER=...
export TEST_PASSWORD=...
# Run integration tests
uv run pytest -k "_int"
```
## 5. Type Checking
Pyright should have passed during `make lint`, but if you added new code, verify:
- All function parameters have type hints
- Return types are specified
- No `Any` types unless necessary
- Pydantic models are properly defined
## 6. Run Complete Check
Run the comprehensive check command:
```bash
make check
```
This runs format, lint, and test in sequence. All should pass.
## 7. Documentation Updates (if needed)
Consider if your changes require:
- README.md updates (for user-facing features)
- CLAUDE.md updates (for development patterns)
- Docstring additions/updates
- Example code in `examples/` folder
- Comments for complex logic
## 8. Git Commit
Only commit if all checks pass:
```bash
git add <files>
git commit -m "Descriptive commit message"
```
## 9. PR Preparation (if submitting changes)
Before creating a PR:
- Ensure `make check` passes completely
- Review your changes for any debug code or comments
- Check for any TODO items you added
- Verify no sensitive data (API keys, passwords) in code
- Consider if changes need an issue/RFC (>500 LOC changes require discussion)
## Quick Reference
Most common workflow:
```bash
# After making changes
make check
# If all passes, commit
git add .
git commit -m "Your message"
```
## Special Cases
### Server Changes
If you modified `server/` code:
```bash
cd server/
make format
make lint
make test
```
### MCP Server Changes
If you modified `mcp_server/` code:
```bash
cd mcp_server/
# Test with Docker
docker-compose up
```
### Large Architectural Changes
- Create GitHub issue (RFC) first
- Discuss technical design and justification
- Get feedback before implementing >500 LOC changes

84
.serena/project.yml Normal file
View file

@ -0,0 +1,84 @@
# list of languages for which language servers are started; choose from:
# al bash clojure cpp csharp csharp_omnisharp
# dart elixir elm erlang fortran go
# haskell java julia kotlin lua markdown
# nix perl php python python_jedi r
# rego ruby ruby_solargraph rust scala swift
# terraform typescript typescript_vts zig
# Note:
# - For C, use cpp
# - For JavaScript, use typescript
# Special requirements:
# - csharp: Requires the presence of a .sln file in the project folder.
# When using multiple languages, the first language server that supports a given file will be used for that file.
# The first language is the default language and the respective language server will be used as a fallback.
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
languages:
- python
# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"
# whether to use the project's gitignore file to ignore files
# Added on 2025-04-07
ignore_all_files_in_gitignore: true
# list of additional paths to ignore
# same syntax as gitignore, so you can use * and **
# Was previously called `ignored_dirs`, please update your config if you are using that.
# Added (renamed) on 2025-04-07
ignored_paths: []
# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
#
# * `activate_project`: Activates a project by name.
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
# * `create_text_file`: Creates/overwrites a file in the project directory.
# * `delete_lines`: Deletes a range of lines within a file.
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
# * `execute_shell_command`: Executes a shell command.
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
# * `initial_instructions`: Gets the initial instructions for the current project.
# Should only be used in settings where the system prompt cannot be set,
# e.g. in clients you have no control over, like Claude Desktop.
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
# * `insert_at_line`: Inserts content at a given line in a file.
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
# * `list_memories`: Lists memories in Serena's project-specific memory store.
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
# * `read_file`: Reads a file within the project directory.
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
# * `remove_project`: Removes a project from the Serena configuration.
# * `replace_lines`: Replaces a range of lines within a file with new content.
# * `replace_symbol_body`: Replaces the full definition of a symbol.
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
# * `search_for_pattern`: Performs a search for a pattern in the project.
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
# * `switch_modes`: Activates modes by providing a list of their names
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
excluded_tools: []
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""
project_name: "graphiti"
included_optional_tools: []

View file

@ -0,0 +1,379 @@
# GitHub Actions → Docker Hub Automated Build Setup
This guide explains how to automatically build your custom Graphiti MCP Docker image with your local changes and push it to Docker Hub using GitHub Actions.
## Why This Approach?
**Automatic builds** - Every push to main triggers a new build
**Reproducible** - Anyone can see exactly what was built
**Multi-platform** - Builds for both AMD64 and ARM64
**No local building** - GitHub does all the work
**Version tracking** - Tied to git commits
**Clean workflow** - Professional CI/CD pipeline
## Prerequisites
1. **GitHub account** with a fork of the graphiti repository
2. **Docker Hub account** (username: `lvarming`)
3. **Docker Hub Access Token** (for GitHub Actions to push images)
---
## Step 1: Create Docker Hub Access Token
1. Go to [Docker Hub](https://hub.docker.com/)
2. Click your username → **Account Settings**
3. Click **Security** → **New Access Token**
4. Give it a description: "GitHub Actions - Graphiti MCP"
5. **Copy the token** (you'll only see it once!)
---
## Step 2: Add Token to GitHub Repository Secrets
1. Go to your forked repository on GitHub
2. Click **Settings****Secrets and variables** → **Actions**
3. Click **New repository secret**
4. Name: `DOCKERHUB_TOKEN`
5. Value: Paste the access token from Step 1
6. Click **Add secret**
---
## Step 3: Verify Workflow Files
The repository already includes the necessary workflow file at:
```
.github/workflows/build-custom-mcp.yml
```
And the custom Dockerfile at:
```
mcp_server/docker/Dockerfile.custom
```
These files are configured to:
- Build using YOUR local `graphiti-core` changes (not PyPI)
- Push to `lvarming/graphiti-mcp` on Docker Hub
- Tag with version numbers and `latest`
---
## Step 4: Trigger a Build
### Option A: Automatic Build (On Push)
The workflow automatically triggers when you:
- Push to the `main` branch
- Modify files in `graphiti_core/` or `mcp_server/`
Simply commit and push your changes:
```bash
git add .
git commit -m "Update graphiti-core with custom changes"
git push origin main
```
### Option B: Manual Build
1. Go to your repository on GitHub
2. Click **Actions** tab
3. Select **Build Custom MCP Server** workflow
4. Click **Run workflow** dropdown
5. (Optional) Specify a custom tag, or leave as `latest`
6. Click **Run workflow**
---
## Step 5: Monitor the Build
1. Click on the running workflow to see progress
2. The build takes about 5-10 minutes
3. You'll see:
- Version extraction
- Docker image build (for AMD64 and ARM64)
- Push to Docker Hub
- Build summary with tags
---
## Step 6: Verify Image on Docker Hub
1. Go to [Docker Hub](https://hub.docker.com/)
2. Navigate to your repository: `lvarming/graphiti-mcp`
3. Check the **Tags** tab
4. You should see tags like:
- `latest`
- `mcp-1.0.0`
- `mcp-1.0.0-core-0.23.0`
- `sha-abc1234`
---
## Step 7: Use Your Custom Image
### In Unraid
Update your Docker container to use:
```
Repository: lvarming/graphiti-mcp:latest
```
### In Docker Compose
```yaml
services:
graphiti-mcp:
image: lvarming/graphiti-mcp:latest
container_name: graphiti-mcp
restart: unless-stopped
# ... rest of your config
```
### Pull Manually
```bash
docker pull lvarming/graphiti-mcp:latest
```
---
## Understanding the Build Process
### What Gets Built
The custom Dockerfile (`Dockerfile.custom`) does the following:
1. **Copies entire project** - Both `graphiti_core/` and `mcp_server/`
2. **Builds graphiti-core from local source** - Not from PyPI
3. **Installs MCP server** - Using the local graphiti-core
4. **Creates multi-platform image** - AMD64 and ARM64
### Version Tagging
Each build creates multiple tags:
| Tag | Description | Example |
|-----|-------------|---------|
| `latest` | Always points to most recent build | `lvarming/graphiti-mcp:latest` |
| `mcp-X.Y.Z` | MCP server version | `lvarming/graphiti-mcp:mcp-1.0.0` |
| `mcp-X.Y.Z-core-A.B.C` | Full version info | `lvarming/graphiti-mcp:mcp-1.0.0-core-0.23.0` |
| `sha-xxxxxxx` | Git commit SHA | `lvarming/graphiti-mcp:sha-abc1234` |
### Build Arguments
The workflow passes these build arguments:
```dockerfile
GRAPHITI_CORE_VERSION=0.23.0 # From pyproject.toml
MCP_SERVER_VERSION=1.0.0 # From mcp_server/pyproject.toml
BUILD_DATE=2025-11-08T12:00:00Z # UTC timestamp
VCS_REF=abc1234 # Git commit hash
```
---
## Workflow Customization
### Change Docker Hub Username
If you want to use a different Docker Hub account, edit `.github/workflows/build-custom-mcp.yml`:
```yaml
env:
DOCKERHUB_USERNAME: your-username # Change this
IMAGE_NAME: graphiti-mcp
```
### Change Trigger Conditions
To only build on tags instead of every push:
```yaml
on:
push:
tags:
- 'v*.*.*'
```
### Add Slack/Discord Notifications
Add a notification step at the end of the workflow:
```yaml
- name: Notify on Success
uses: slackapi/slack-github-action@v1
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
payload: |
{
"text": "✅ New Graphiti MCP image built: lvarming/graphiti-mcp:latest"
}
```
---
## Troubleshooting
### Build Fails - "Error: buildx failed"
**Cause**: Docker Buildx issue
**Solution**: Re-run the workflow (transient issue)
### Build Fails - "unauthorized: incorrect username or password"
**Cause**: Invalid Docker Hub credentials
**Solution**:
1. Verify `DOCKERHUB_TOKEN` secret is correct
2. Regenerate access token on Docker Hub
3. Update the secret in GitHub
### Build Fails - "No space left on device"
**Cause**: GitHub runner out of disk space
**Solution**: Add cleanup step before build:
```yaml
- name: Free up disk space
run: |
docker system prune -af
df -h
```
### Image Not Found on Docker Hub
**Cause**: Image is private
**Solution**:
1. Go to Docker Hub → lvarming/graphiti-mcp
2. Click **Settings**
3. Make repository **Public**
### Workflow Doesn't Trigger
**Cause**: Branch protection or incorrect path filters
**Solution**:
1. Check you're pushing to `main` branch
2. Verify changes are in `graphiti_core/` or `mcp_server/`
3. Manually trigger from Actions tab
---
## Advanced: Multi-Repository Setup
If you want separate images for development and production:
### Development Image
Create `.github/workflows/build-dev-mcp.yml`:
```yaml
name: Build Dev MCP Server
on:
push:
branches:
- dev
- feature/*
env:
DOCKERHUB_USERNAME: lvarming
IMAGE_NAME: graphiti-mcp-dev # Different image name
```
### Production Image
Keep the main workflow for production builds on `main` branch.
---
## Comparing with Official Builds
| Feature | Official (zepai) | Your Custom Build |
|---------|-----------------|-------------------|
| Source | PyPI graphiti-core | Local graphiti-core |
| Trigger | Manual tags only | Auto on push + manual |
| Docker Hub | zepai/knowledge-graph-mcp | lvarming/graphiti-mcp |
| Build Platform | Depot (paid) | GitHub Actions (free) |
| Customization | Limited | Full control |
---
## Best Practices
### 1. **Pin Versions for Production**
Instead of `latest`, use specific versions:
```yaml
image: lvarming/graphiti-mcp:mcp-1.0.0-core-0.23.0
```
### 2. **Test Before Deploying**
Add a test step in the workflow:
```yaml
- name: Test image
run: |
docker run --rm lvarming/graphiti-mcp:latest --version
```
### 3. **Keep Workflows Updated**
GitHub Actions updates frequently. Use Dependabot:
Create `.github/dependabot.yml`:
```yaml
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
```
### 4. **Monitor Build Times**
If builds are slow, enable caching:
```yaml
cache-from: type=gha
cache-to: type=gha,mode=max
```
(Already enabled in the workflow!)
### 5. **Security Scanning**
Add Trivy security scanner:
```yaml
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: lvarming/graphiti-mcp:latest
format: 'table'
exit-code: '1'
severity: 'CRITICAL,HIGH'
```
---
## Next Steps
1. ✅ Set up Docker Hub access token
2. ✅ Add secret to GitHub repository
3. ✅ Push changes to trigger first build
4. ✅ Verify image appears on Docker Hub
5. ✅ Update your Unraid/LibreChat config to use new image
6. 📝 Document any custom changes in DOCS/
---
## Questions?
- **GitHub Actions Issues**: Check the Actions tab for detailed logs
- **Docker Hub Issues**: Verify your account and access token
- **Build Failures**: Review the workflow logs for specific errors
## Related Documentation
- [LibreChat Setup Guide](./Librechat.setup.md)
- [OpenAI Compatible Endpoints](./OpenAI-Compatible-Endpoints.md)
- [GitHub Actions Documentation](https://docs.github.com/en/actions)
- [Docker Hub Documentation](https://docs.docker.com/docker-hub/)

371
DOCS/Librechat.setup.md Normal file
View file

@ -0,0 +1,371 @@
Complete Setup Guide: Graphiti MCP + LibreChat + Neo4j on Unraid
Prerequisites
- LibreChat running in Docker on Unraid
- Neo4j Docker container running on Unraid
- OpenAI API key (or other LLM provider)
- Access to your Unraid Docker network
---
Step 1: Prepare Graphiti MCP Configuration
1.1 Create a directory on Unraid for Graphiti MCP
mkdir -p /mnt/user/appdata/graphiti-mcp/config
1.2 Create .env file
Create /mnt/user/appdata/graphiti-mcp/.env with your settings:
# Neo4j Connection - IMPORTANT: Use your existing Neo4j container details
# If your Neo4j container is named "neo4j", use: bolt://neo4j:7687
# Replace with your actual container name if different
NEO4J_URI=bolt://YOUR_NEO4J_CONTAINER_NAME:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=YOUR_NEO4J_PASSWORD
NEO4J_DATABASE=neo4j
# OpenAI Configuration (Required)
OPENAI_API_KEY=sk-your-openai-api-key-here
# LLM Model (default: gpt-5-mini)
MODEL_NAME=gpt-5-mini
# Concurrency Control - adjust based on your OpenAI tier
# Tier 1 (free): 1-2, Tier 2: 5-8, Tier 3: 10-15
SEMAPHORE_LIMIT=10
# Group ID for namespacing (optional)
GRAPHITI_GROUP_ID=main
# Disable telemetry (optional)
GRAPHITI_TELEMETRY_ENABLED=false
1.3 Create config file
Create /mnt/user/appdata/graphiti-mcp/config/config.yaml:
server:
transport: "http"
host: "0.0.0.0"
port: 8000
llm:
provider: "openai"
model: "gpt-5-mini"
max_tokens: 4096
providers:
openai:
api_key: ${OPENAI_API_KEY}
api_url: ${OPENAI_API_URL:https://api.openai.com/v1}
embedder:
provider: "openai"
model: "text-embedding-3-small"
dimensions: 1536
providers:
openai:
api_key: ${OPENAI_API_KEY}
database:
provider: "neo4j"
providers:
neo4j:
uri: ${NEO4J_URI}
username: ${NEO4J_USER}
password: ${NEO4J_PASSWORD}
database: ${NEO4J_DATABASE:neo4j}
use_parallel_runtime: false
graphiti:
group_id: ${GRAPHITI_GROUP_ID:main}
user_id: ${USER_ID:mcp_user}
entity_types:
- name: "Preference"
description: "User preferences, choices, opinions, or selections"
- name: "Requirement"
description: "Specific needs, features, or functionality that must be fulfilled"
- name: "Procedure"
description: "Standard operating procedures and sequential instructions"
- name: "Location"
description: "Physical or virtual places where activities occur"
- name: "Event"
description: "Time-bound activities, occurrences, or experiences"
- name: "Organization"
description: "Companies, institutions, groups, or formal entities"
- name: "Document"
description: "Information content in various forms"
- name: "Topic"
description: "Subject of conversation, interest, or knowledge domain"
- name: "Object"
description: "Physical items, tools, devices, or possessions"
---
Step 2: Deploy Graphiti MCP on Unraid
You have two options for deploying on Unraid:
Option A: Using Unraid Docker Template (Recommended)
1. Go to Docker tab in Unraid
2. Click Add Container
3. Fill in the following settings:
Basic Settings:
- Name: graphiti-mcp
- Repository: lvarming/graphiti-mcp:latest # Custom build with your changes
- Network Type: bridge (or custom: br0 if you have a custom network)
Port Mappings:
- Container Port: 8000 → Host Port: 8000
Path Mappings:
- Config Path:
- Container Path: /app/mcp/config/config.yaml
- Host Path: /mnt/user/appdata/graphiti-mcp/config/config.yaml
- Access Mode: Read Only
Environment Variables:
Add each variable from your .env file:
NEO4J_URI=bolt://YOUR_NEO4J_CONTAINER_NAME:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your_password
NEO4J_DATABASE=neo4j
OPENAI_API_KEY=sk-your-key-here
GRAPHITI_GROUP_ID=main
SEMAPHORE_LIMIT=10
CONFIG_PATH=/app/mcp/config/config.yaml
PATH=/root/.local/bin:${PATH}
Extra Parameters:
--env-file=/mnt/user/appdata/graphiti-mcp/.env
Network: Ensure this container is on the same Docker network as your Neo4j and LibreChat containers.
Option B: Using Docker Compose
Create /mnt/user/appdata/graphiti-mcp/docker-compose.yml:
version: '3.8'
services:
graphiti-mcp:
image: lvarming/graphiti-mcp:latest # Custom build with your changes
container_name: graphiti-mcp
restart: unless-stopped
env_file:
- .env
environment:
- NEO4J_URI=${NEO4J_URI}
- NEO4J_USER=${NEO4J_USER}
- NEO4J_PASSWORD=${NEO4J_PASSWORD}
- NEO4J_DATABASE=${NEO4J_DATABASE:-neo4j}
- GRAPHITI_GROUP_ID=${GRAPHITI_GROUP_ID:-main}
- SEMAPHORE_LIMIT=${SEMAPHORE_LIMIT:-10}
- CONFIG_PATH=/app/mcp/config/config.yaml
- PATH=/root/.local/bin:${PATH}
volumes:
- ./config/config.yaml:/app/mcp/config/config.yaml:ro
ports:
- "8000:8000"
networks:
- unraid_network # Replace with your network name
networks:
unraid_network:
external: true # Use existing Unraid network
Then run:
cd /mnt/user/appdata/graphiti-mcp
docker-compose up -d
---
Step 3: Configure Docker Networking
Find Your Neo4j Container Name
docker ps | grep neo4j
The container name will be something like neo4j or neo4j-community. Use this exact name in your NEO4J_URI.
Ensure Same Network
All three containers (Neo4j, Graphiti MCP, LibreChat) should be on the same Docker network.
Check which network your Neo4j is on:
docker inspect YOUR_NEO4J_CONTAINER_NAME | grep NetworkMode
Connect Graphiti MCP to the same network:
docker network connect NETWORK_NAME graphiti-mcp
---
Step 4: Configure LibreChat
4.1 Add Graphiti MCP to LibreChat's librechat.yaml
Edit your LibreChat configuration file (usually /mnt/user/appdata/librechat/librechat.yaml):
# ... existing LibreChat config ...
# Add MCP server configuration
mcpServers:
graphiti-memory:
url: "http://graphiti-mcp:8000/mcp/"
# For multi-user support with user-specific graphs
server_instructions: |
You have access to a knowledge graph memory system through Graphiti.
IMPORTANT USAGE GUIDELINES:
1. Always search existing knowledge before adding new information
2. Use entity type filters: Preference, Procedure, Requirement
3. Store new information immediately using add_memory
4. Follow discovered procedures and respect preferences
Available tools:
- add_episode: Store new conversations/information
- search_nodes: Find entities and summaries
- search_facts: Find relationships between entities
- get_episodes: Retrieve recent conversations
# Optional: Hide from chat menu (agent-only access)
# chatMenu: false
# Optional: User-specific group IDs for isolation
# This requires configuring Graphiti to accept dynamic group_id
# user_headers:
# X-User-ID: "{{LIBRECHAT_USER_ID}}"
# X-User-Email: "{{LIBRECHAT_USER_EMAIL}}"
4.2 For Production: Use Streamable HTTP
According to LibreChat docs, for multi-user deployments, ensure the transport is HTTP (which is already default for Graphiti MCP).
4.3 Restart LibreChat
docker restart YOUR_LIBRECHAT_CONTAINER_NAME
---
Step 5: Test the Setup
5.1 Verify Graphiti MCP is Running
curl http://YOUR_UNRAID_IP:8000/health
You should see a health status response.
5.2 Test Neo4j Connection
Check Graphiti MCP logs:
docker logs graphiti-mcp
Look for successful Neo4j connection messages.
5.3 Test in LibreChat
1. Open LibreChat in your browser
2. Start a new chat
3. In an agent configuration, you should see graphiti-memory available
4. Try asking the agent to remember something:
Please remember that I prefer dark mode for all interfaces
5. Then later ask:
What do you know about my preferences?
---
Step 6: Advanced Configuration (Optional)
Per-User Graph Isolation
To give each LibreChat user their own knowledge graph, you need to:
1. Modify Graphiti MCP to accept dynamic group_id from headers
2. Update LibreChat config to send user info:
mcpServers:
graphiti-memory:
url: "http://graphiti-mcp:8000/mcp/"
user_headers:
X-User-ID: "{{LIBRECHAT_USER_ID}}"
X-User-Email: "{{LIBRECHAT_USER_EMAIL}}"
This requires custom modification of Graphiti MCP to read the X-User-ID header and use it as the group_id.
Using Different LLM Providers
If you want to use Claude or other providers instead of OpenAI, update config.yaml:
llm:
provider: "anthropic"
model: "claude-sonnet-4-5-latest"
providers:
anthropic:
api_key: ${ANTHROPIC_API_KEY}
And add ANTHROPIC_API_KEY to your .env file.
---
Troubleshooting
Graphiti MCP Can't Connect to Neo4j
- Issue: Connection refused or timeout
- Solution:
- Verify Neo4j container name: docker ps | grep neo4j
- Use exact container name in NEO4J_URI: bolt://container_name:7687
- Ensure both containers are on same Docker network
- Check Neo4j is listening on port 7687: docker exec NEO4J_CONTAINER netstat -tlnp | grep 7687
LibreChat Can't See Graphiti Tools
- Issue: Tools not appearing in agent builder
- Solution:
- Check Graphiti MCP is running: curl http://localhost:8000/health
- Verify librechat.yaml syntax is correct
- Restart LibreChat: docker restart librechat
- Check LibreChat logs: docker logs librechat
Rate Limit Errors
- Issue: 429 errors from OpenAI
- Solution: Lower SEMAPHORE_LIMIT in .env (try 5 or lower)
Memory/Performance Issues
- Issue: Slow responses or high memory usage
- Solution:
- Adjust Neo4j memory in your Neo4j container settings
- Reduce SEMAPHORE_LIMIT to lower concurrent processing
---
⏺ Quick Start Summary
Building Your Custom Docker Image:
This setup uses a custom Docker image built from YOUR fork with YOUR changes.
The image is automatically built by GitHub Actions and pushed to Docker Hub.
Setup Steps:
1. Fork the graphiti repository to your GitHub account
2. Add Docker Hub credentials to your repository secrets:
- Go to Settings → Secrets and variables → Actions
- Add secret: DOCKERHUB_TOKEN (your Docker Hub access token)
3. Push changes to trigger automatic build, or manually trigger from Actions tab
4. Image will be available at: lvarming/graphiti-mcp:latest
Key Points:
1. Docker Image: Use lvarming/graphiti-mcp:latest (your custom build)
2. Port: Expose 8000 for HTTP transport
3. Neo4j Connection: Use bolt://YOUR_NEO4J_CONTAINER_NAME:7687 (container name, not localhost)
4. Network: All 3 containers (Neo4j, Graphiti MCP, LibreChat) must be on same Docker network
5. LibreChat Config: Add to librechat.yaml under mcpServers with URL: http://graphiti-mcp:8000/mcp/
6. Required Env Vars: OPENAI_API_KEY, NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD
The setup gives LibreChat powerful knowledge graph memory capabilities, allowing it to remember user preferences, procedures, and facts across conversations!
Let me know if you need help with any specific step or run into issues during setup.

View file

@ -0,0 +1,569 @@
# OpenAI-Compatible Custom Endpoint Support in Graphiti
## Overview
This document analyzes how Graphiti handles OpenAI-compatible custom endpoints (like OpenRouter, NagaAI, Together.ai, etc.) and provides recommendations for improving support.
## Current Architecture
Graphiti has **three main OpenAI-compatible client implementations**:
### 1. OpenAIClient (Default)
**File**: `graphiti_core/llm_client/openai_client.py`
- Extends `BaseOpenAIClient`
- Uses the **new OpenAI Responses API** (`/v1/responses` endpoint)
- Uses `client.responses.parse()` for structured outputs (OpenAI SDK v1.91+)
- This is the **default client** exported in the public API
```python
response = await self.client.responses.parse(
model=model,
input=messages,
temperature=temperature,
max_output_tokens=max_tokens,
text_format=response_model,
reasoning={'effort': reasoning},
text={'verbosity': verbosity},
)
```
### 2. OpenAIGenericClient (Legacy)
**File**: `graphiti_core/llm_client/openai_generic_client.py`
- Uses the **standard Chat Completions API** (`/v1/chat/completions`)
- Uses `client.chat.completions.create()`
- **Only supports unstructured JSON responses** (not Pydantic schemas)
- Currently **not exported** in `__init__.py` (hidden from public API)
```python
response = await self.client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
max_tokens=max_tokens,
response_format={'type': 'json_object'},
)
```
### 3. AzureOpenAILLMClient
**File**: `graphiti_core/llm_client/azure_openai_client.py`
- Azure-specific implementation
- Also uses `responses.parse()` like `OpenAIClient`
- Handles Azure-specific authentication and endpoints
## The Root Problem
### Issue Description
When users configure Graphiti with custom OpenAI-compatible endpoints, they encounter errors because:
1. **`OpenAIClient` uses the new `/v1/responses` endpoint** via `client.responses.parse()`
- This is a **new OpenAI API** (introduced in OpenAI SDK v1.91.0) for structured outputs
- This endpoint is **proprietary to OpenAI** and **not part of the standard OpenAI-compatible API specification**
2. **Most OpenAI-compatible services** (OpenRouter, NagaAI, Ollama, Together.ai, etc.) **only implement** the standard `/v1/chat/completions` endpoint
- They do **NOT** implement `/v1/responses`
3. When you configure a `base_url` pointing to these services, Graphiti tries to call:
```
https://your-custom-endpoint.com/v1/responses
```
Instead of the expected:
```
https://your-custom-endpoint.com/v1/chat/completions
```
### Example Error Scenario
```python
from graphiti_core import Graphiti
from graphiti_core.llm_client import OpenAIClient, LLMConfig
config = LLMConfig(
api_key="sk-or-v1-...",
model="meta-llama/llama-3-8b-instruct",
base_url="https://openrouter.ai/api/v1"
)
llm_client = OpenAIClient(config=config)
graphiti = Graphiti(uri, user, password, llm_client=llm_client)
# This will fail because OpenRouter doesn't have /v1/responses endpoint
# Error: 404 Not Found - https://openrouter.ai/api/v1/responses
```
## Current Workaround (Documented)
The README documents using `OpenAIGenericClient` with Ollama:
```python
from graphiti_core.llm_client.openai_generic_client import OpenAIGenericClient
from graphiti_core.llm_client.config import LLMConfig
llm_config = LLMConfig(
api_key="ollama",
model="deepseek-r1:7b",
base_url="http://localhost:11434/v1"
)
llm_client = OpenAIGenericClient(config=llm_config)
```
### Limitations of Current Workaround
- `OpenAIGenericClient` **doesn't support structured outputs with Pydantic models**
- It only returns raw JSON and manually validates schemas
- It's not the recommended/default client
- It's **not exported** in the public API (`graphiti_core.llm_client`)
- Users must know to import from the internal module path
## Recommended Solutions
### Priority 1: Quick Wins (High Priority)
#### 1.1 Export `OpenAIGenericClient` in Public API
**File**: `graphiti_core/llm_client/__init__.py`
**Current**:
```python
from .client import LLMClient
from .config import LLMConfig
from .errors import RateLimitError
from .openai_client import OpenAIClient
__all__ = ['LLMClient', 'OpenAIClient', 'LLMConfig', 'RateLimitError']
```
**Proposed**:
```python
from .client import LLMClient
from .config import LLMConfig
from .errors import RateLimitError
from .openai_client import OpenAIClient
from .openai_generic_client import OpenAIGenericClient
__all__ = ['LLMClient', 'OpenAIClient', 'OpenAIGenericClient', 'LLMConfig', 'RateLimitError']
```
#### 1.2 Add Clear Documentation
**File**: `README.md`
Add a dedicated section:
```markdown
### Using OpenAI-Compatible Endpoints (OpenRouter, NagaAI, Together.ai, etc.)
Most OpenAI-compatible services only support the standard Chat Completions API,
not OpenAI's newer Responses API. Use `OpenAIGenericClient` for these services:
**OpenRouter Example**:
```python
from graphiti_core import Graphiti
from graphiti_core.llm_client import OpenAIGenericClient, LLMConfig
config = LLMConfig(
api_key="sk-or-v1-...",
model="meta-llama/llama-3-8b-instruct",
base_url="https://openrouter.ai/api/v1"
)
llm_client = OpenAIGenericClient(config=config)
graphiti = Graphiti(uri, user, password, llm_client=llm_client)
```
**Together.ai Example**:
```python
config = LLMConfig(
api_key="your-together-api-key",
model="meta-llama/Llama-3-70b-chat-hf",
base_url="https://api.together.xyz/v1"
)
llm_client = OpenAIGenericClient(config=config)
```
**Note**: `OpenAIGenericClient` has limited structured output support compared to
the default `OpenAIClient`. It uses JSON mode instead of Pydantic schema validation.
```
#### 1.3 Add Better Error Messages
**File**: `graphiti_core/llm_client/openai_client.py`
Add error handling that detects the issue:
```python
async def _create_structured_completion(self, ...):
try:
response = await self.client.responses.parse(...)
return response
except openai.NotFoundError as e:
if self.config.base_url and "api.openai.com" not in self.config.base_url:
raise Exception(
f"The OpenAI Responses API (/v1/responses) is not available at {self.config.base_url}. "
f"Most OpenAI-compatible services only support /v1/chat/completions. "
f"Please use OpenAIGenericClient instead of OpenAIClient for custom endpoints. "
f"See: https://help.getzep.com/graphiti/guides/custom-endpoints"
) from e
raise
```
### Priority 2: Better UX (Medium Priority)
#### 2.1 Add Auto-Detection Logic
**File**: `graphiti_core/llm_client/config.py`
```python
class LLMConfig:
def __init__(
self,
api_key: str | None = None,
model: str | None = None,
base_url: str | None = None,
temperature: float = DEFAULT_TEMPERATURE,
max_tokens: int = DEFAULT_MAX_TOKENS,
small_model: str | None = None,
use_responses_api: bool | None = None, # NEW: Auto-detect if None
):
self.base_url = base_url
self.api_key = api_key
self.model = model
self.small_model = small_model
self.temperature = temperature
self.max_tokens = max_tokens
# Auto-detect API style based on base_url
if use_responses_api is None:
self.use_responses_api = self._should_use_responses_api()
else:
self.use_responses_api = use_responses_api
def _should_use_responses_api(self) -> bool:
"""Determine if we should use the Responses API based on base_url."""
if self.base_url is None:
return True # Default OpenAI
# Known services that support Responses API
supported_services = ["api.openai.com", "azure.com"]
return any(service in self.base_url for service in supported_services)
```
#### 2.2 Create a Unified Smart Client
**Option A**: Modify `OpenAIClient` to Fall Back
```python
class OpenAIClient(BaseOpenAIClient):
def __init__(self, config: LLMConfig | None = None, ...):
super().__init__(config, ...)
if config is None:
config = LLMConfig()
self.use_responses_api = config.use_responses_api
self.client = AsyncOpenAI(api_key=config.api_key, base_url=config.base_url)
async def _create_structured_completion(self, ...):
if self.use_responses_api:
# Use responses.parse() for OpenAI native
return await self.client.responses.parse(...)
else:
# Fall back to chat.completions with JSON schema for compatibility
return await self.client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
max_tokens=max_tokens,
response_format={
"type": "json_schema",
"json_schema": {
"name": response_model.__name__,
"schema": response_model.model_json_schema(),
"strict": False
}
}
)
```
**Option B**: Create a Factory Function
```python
# graphiti_core/llm_client/__init__.py
def create_openai_client(
config: LLMConfig | None = None,
cache: bool = False,
**kwargs
) -> LLMClient:
"""
Factory to create the appropriate OpenAI-compatible client.
Automatically selects between OpenAIClient (for native OpenAI)
and OpenAIGenericClient (for OpenAI-compatible services).
Args:
config: LLM configuration including base_url
cache: Whether to enable caching
**kwargs: Additional arguments passed to the client
Returns:
LLMClient: Either OpenAIClient or OpenAIGenericClient
Example:
>>> # Automatically uses OpenAIGenericClient for OpenRouter
>>> config = LLMConfig(
... api_key="sk-or-v1-...",
... model="meta-llama/llama-3-8b-instruct",
... base_url="https://openrouter.ai/api/v1"
... )
>>> client = create_openai_client(config)
"""
if config is None:
config = LLMConfig()
# Auto-detect based on base_url
if config.base_url is None or "api.openai.com" in config.base_url:
return OpenAIClient(config, cache, **kwargs)
else:
return OpenAIGenericClient(config, cache, **kwargs)
```
#### 2.3 Enhance `OpenAIGenericClient` with Better Structured Output Support
**File**: `graphiti_core/llm_client/openai_generic_client.py`
```python
async def _generate_response(
self,
messages: list[Message],
response_model: type[BaseModel] | None = None,
max_tokens: int = DEFAULT_MAX_TOKENS,
model_size: ModelSize = ModelSize.medium,
) -> dict[str, typing.Any]:
openai_messages: list[ChatCompletionMessageParam] = []
for m in messages:
m.content = self._clean_input(m.content)
if m.role == 'user':
openai_messages.append({'role': 'user', 'content': m.content})
elif m.role == 'system':
openai_messages.append({'role': 'system', 'content': m.content})
try:
# Try to use json_schema format (supported by more providers)
if response_model:
response = await self.client.chat.completions.create(
model=self.model or DEFAULT_MODEL,
messages=openai_messages,
temperature=self.temperature,
max_tokens=max_tokens or self.max_tokens,
response_format={
"type": "json_schema",
"json_schema": {
"name": response_model.__name__,
"schema": response_model.model_json_schema(),
"strict": False # Most providers don't support strict mode
}
}
)
else:
response = await self.client.chat.completions.create(
model=self.model or DEFAULT_MODEL,
messages=openai_messages,
temperature=self.temperature,
max_tokens=max_tokens or self.max_tokens,
response_format={'type': 'json_object'},
)
result = response.choices[0].message.content or '{}'
return json.loads(result)
except Exception as e:
logger.error(f'Error in generating LLM response: {e}')
raise
```
### Priority 3: Nice to Have (Low Priority)
#### 3.1 Provider-Specific Clients
Create convenience clients for popular providers:
```python
# graphiti_core/llm_client/openrouter_client.py
class OpenRouterClient(OpenAIGenericClient):
"""Pre-configured client for OpenRouter.
Example:
>>> client = OpenRouterClient(
... api_key="sk-or-v1-...",
... model="meta-llama/llama-3-8b-instruct"
... )
"""
def __init__(
self,
api_key: str,
model: str,
temperature: float = DEFAULT_TEMPERATURE,
max_tokens: int = DEFAULT_MAX_TOKENS,
**kwargs
):
config = LLMConfig(
api_key=api_key,
model=model,
base_url="https://openrouter.ai/api/v1",
temperature=temperature,
max_tokens=max_tokens
)
super().__init__(config=config, **kwargs)
```
```python
# graphiti_core/llm_client/together_client.py
class TogetherClient(OpenAIGenericClient):
"""Pre-configured client for Together.ai.
Example:
>>> client = TogetherClient(
... api_key="your-together-key",
... model="meta-llama/Llama-3-70b-chat-hf"
... )
"""
def __init__(
self,
api_key: str,
model: str,
temperature: float = DEFAULT_TEMPERATURE,
max_tokens: int = DEFAULT_MAX_TOKENS,
**kwargs
):
config = LLMConfig(
api_key=api_key,
model=model,
base_url="https://api.together.xyz/v1",
temperature=temperature,
max_tokens=max_tokens
)
super().__init__(config=config, **kwargs)
```
#### 3.2 Provider Compatibility Matrix
Add to documentation:
| Provider | Standard Client | Generic Client | Structured Outputs | Notes |
|----------|----------------|----------------|-------------------|-------|
| OpenAI | ✅ `OpenAIClient` | ✅ | ✅ Full (Responses API) | Recommended: Use `OpenAIClient` |
| Azure OpenAI | ✅ `AzureOpenAILLMClient` | ✅ | ✅ Full (Responses API) | Requires API version 2024-08-01-preview+ |
| OpenRouter | ❌ | ✅ `OpenAIGenericClient` | ⚠️ Limited (JSON Schema) | Use `OpenAIGenericClient` |
| Together.ai | ❌ | ✅ `OpenAIGenericClient` | ⚠️ Limited (JSON Schema) | Use `OpenAIGenericClient` |
| Ollama | ❌ | ✅ `OpenAIGenericClient` | ⚠️ Limited (JSON mode) | Local deployment |
| Groq | ❌ | ✅ `OpenAIGenericClient` | ⚠️ Limited (JSON Schema) | Very fast inference |
| Perplexity | ❌ | ✅ `OpenAIGenericClient` | ⚠️ Limited (JSON mode) | Primarily for search |
## Testing Recommendations
### Unit Tests
1. **Endpoint detection logic**
```python
def test_should_use_responses_api():
# OpenAI native should use Responses API
config = LLMConfig(base_url="https://api.openai.com/v1")
assert config.use_responses_api is True
# Custom endpoints should not
config = LLMConfig(base_url="https://openrouter.ai/api/v1")
assert config.use_responses_api is False
```
2. **Client selection**
```python
def test_create_openai_client_auto_selection():
# Should return OpenAIClient for OpenAI
config = LLMConfig(api_key="test")
client = create_openai_client(config)
assert isinstance(client, OpenAIClient)
# Should return OpenAIGenericClient for others
config = LLMConfig(api_key="test", base_url="https://openrouter.ai/api/v1")
client = create_openai_client(config)
assert isinstance(client, OpenAIGenericClient)
```
### Integration Tests
1. **Mock server tests** with responses for both endpoints
2. **Real provider tests** (optional, may require API keys):
- OpenRouter
- Together.ai
- Ollama (local)
### Manual Testing Checklist
- [ ] OpenRouter with Llama models
- [ ] Together.ai with various models
- [ ] Ollama with local models
- [ ] Groq with fast models
- [ ] Verify error messages are helpful
- [ ] Test both structured and unstructured outputs
## Summary of Issues
| Issue | Current State | Impact | Priority |
|-------|---------------|--------|----------|
| `/v1/responses` endpoint usage | Used by default `OpenAIClient` | **BREAKS** all non-OpenAI providers | High |
| `OpenAIGenericClient` not exported | Hidden from public API | Users can't easily use it | High |
| Poor error messages | Generic 404 errors | Confusing for users | High |
| No auto-detection | Must manually choose client | Poor DX | Medium |
| Limited docs | Only Ollama example | Users don't know how to configure other providers | High |
| No structured output in Generic client | Only supports loose JSON | Reduced type safety for custom endpoints | Medium |
| No provider-specific helpers | Generic configuration only | More setup required | Low |
## Implementation Roadmap
### Phase 1: Quick Fixes (1-2 days)
1. Export `OpenAIGenericClient` in public API
2. Add documentation section for custom endpoints
3. Improve error messages in `OpenAIClient`
4. Add examples for OpenRouter, Together.ai
### Phase 2: Enhanced Support (3-5 days)
1. Add auto-detection logic to `LLMConfig`
2. Create factory function for client selection
3. Enhance `OpenAIGenericClient` with better JSON schema support
4. Add comprehensive tests
### Phase 3: Polish (2-3 days)
1. Create provider-specific client classes
2. Build compatibility matrix documentation
3. Add integration tests with real providers
4. Update all examples and guides
## References
- OpenAI SDK v1.91.0+ Responses API: https://platform.openai.com/docs/api-reference/responses
- OpenAI Chat Completions API: https://platform.openai.com/docs/api-reference/chat
- OpenRouter API: https://openrouter.ai/docs
- Together.ai API: https://docs.together.ai/docs/openai-api-compatibility
- Ollama OpenAI compatibility: https://github.com/ollama/ollama/blob/main/docs/openai.md
## Contributing
If you're implementing these changes, please ensure:
1. All changes follow the repository guidelines in `AGENTS.md`
2. Run `make format` before committing
3. Run `make lint` and `make test` to verify changes
4. Update documentation for any new public APIs
5. Add examples demonstrating the new functionality
## Questions or Issues?
- Open an issue: https://github.com/getzep/graphiti/issues
- Discussion: https://github.com/getzep/graphiti/discussions
- Documentation: https://help.getzep.com/graphiti

178
DOCS/README.md Normal file
View file

@ -0,0 +1,178 @@
# Graphiti Custom Build Documentation
This directory contains documentation for building and deploying a custom Graphiti MCP server with your local changes.
## Quick Navigation
### 🐳 Docker Build Setup
**[GitHub-DockerHub-Setup.md](./GitHub-DockerHub-Setup.md)**
- Complete guide for automated Docker builds via GitHub Actions
- Builds with YOUR local graphiti-core changes (not PyPI)
- Pushes to Docker Hub (`lvarming/graphiti-mcp`)
- **Start here** if you want to build custom Docker images
### 🖥️ LibreChat Integration
**[Librechat.setup.md](./Librechat.setup.md)**
- Complete setup guide for Graphiti MCP + LibreChat + Neo4j on Unraid
- Uses your custom Docker image from Docker Hub
- Step-by-step deployment instructions
### 🔌 OpenAI API Compatibility
**[OpenAI-Compatible-Endpoints.md](./OpenAI-Compatible-Endpoints.md)**
- Analysis of OpenAI-compatible endpoint support
- Explains `/v1/responses` vs `/v1/chat/completions` issue
- Recommendations for supporting OpenRouter, Together.ai, Ollama, etc.
---
## Quick Start for Custom Builds
### 1. Setup GitHub → Docker Hub Pipeline
Follow **[GitHub-DockerHub-Setup.md](./GitHub-DockerHub-Setup.md)** to:
1. Create Docker Hub access token
2. Add token to GitHub repository secrets
3. Push changes to trigger automatic build
### 2. Deploy on Unraid
Follow **[Librechat.setup.md](./Librechat.setup.md)** to:
1. Configure Neo4j connection
2. Deploy Graphiti MCP container using `lvarming/graphiti-mcp:latest`
3. Integrate with LibreChat
---
## What's Different in This Setup?
### Standard Graphiti Deployment
```yaml
# Uses official image from PyPI
image: zepai/knowledge-graph-mcp:standalone
```
### Your Custom Deployment
```yaml
# Uses YOUR image with YOUR changes
image: lvarming/graphiti-mcp:latest
```
The custom image includes:
- ✅ Your local `graphiti-core` changes
- ✅ Your MCP server modifications
- ✅ Both Neo4j and FalkorDB drivers
- ✅ Built automatically on every push
---
## Files in This Repository
### Workflow Files
- `.github/workflows/build-custom-mcp.yml` - GitHub Actions workflow for automated builds
### Docker Files
- `mcp_server/docker/Dockerfile.custom` - Custom Dockerfile that uses local graphiti-core
### Documentation
- `DOCS/GitHub-DockerHub-Setup.md` - Docker build setup guide
- `DOCS/Librechat.setup.md` - LibreChat integration guide
- `DOCS/OpenAI-Compatible-Endpoints.md` - API compatibility analysis
- `DOCS/README.md` - This file
---
## Workflow Overview
```mermaid
graph LR
A[Make Changes] --> B[Git Push]
B --> C[GitHub Actions]
C --> D[Build Docker Image]
D --> E[Push to Docker Hub]
E --> F[Deploy on Unraid]
F --> G[Use in LibreChat]
```
1. **Make Changes** - Modify `graphiti_core/` or `mcp_server/`
2. **Git Push** - Push to `main` branch on GitHub
3. **GitHub Actions** - Automatically triggered
4. **Build Image** - Using `Dockerfile.custom` with local code
5. **Push to Docker Hub** - Tagged as `lvarming/graphiti-mcp:latest`
6. **Deploy on Unraid** - Pull latest image
7. **Use in LibreChat** - Configure MCP server URL
---
## Version Information
Your builds include comprehensive version tracking:
```bash
docker inspect lvarming/graphiti-mcp:latest | jq '.[0].Config.Labels'
```
Returns:
```json
{
"org.opencontainers.image.title": "Graphiti MCP Server (Custom Build)",
"org.opencontainers.image.version": "1.0.0",
"graphiti.core.version": "0.23.0",
"graphiti.core.source": "local",
"org.opencontainers.image.revision": "abc1234",
"org.opencontainers.image.created": "2025-11-08T12:00:00Z"
}
```
---
## Key Benefits
### 🚀 Automated
- No manual Docker builds
- No need to push images yourself
- Triggered automatically on code changes
### 🔄 Reproducible
- Every build is traced to a git commit
- Anyone can see exactly what was built
- Version labels include all metadata
### 🏗️ Multi-Platform
- Builds for AMD64 and ARM64
- Works on Intel, AMD, and Apple Silicon
- Single command works everywhere
### 🎯 Clean Workflow
- Professional CI/CD pipeline
- Follows industry best practices
- Easy to maintain and extend
---
## Support
### Issues with Docker Builds?
See [GitHub-DockerHub-Setup.md - Troubleshooting](./GitHub-DockerHub-Setup.md#troubleshooting)
### Issues with Deployment?
See [Librechat.setup.md - Troubleshooting](./Librechat.setup.md#troubleshooting)
### Issues with API Compatibility?
See [OpenAI-Compatible-Endpoints.md](./OpenAI-Compatible-Endpoints.md)
---
## Contributing
If you make improvements to these docs or workflows:
1. Update the relevant documentation file
2. Test the changes
3. Commit and push
4. (Optional) Share with the community via PR to upstream
---
## License
This documentation follows the same license as the Graphiti project.

View file

@ -1,83 +0,0 @@
# syntax=docker/dockerfile:1
# Custom Graphiti MCP Server Image with Local graphiti-core Changes
# This Dockerfile builds the MCP server using the LOCAL graphiti-core code
# instead of pulling from PyPI
FROM python:3.11-slim-bookworm
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
git \
&& rm -rf /var/lib/apt/lists/*
# Install uv for Python package management
ADD https://astral.sh/uv/install.sh /uv-installer.sh
RUN sh /uv-installer.sh && rm /uv-installer.sh
# Add uv to PATH
ENV PATH="/root/.local/bin:${PATH}"
# Configure uv for optimal Docker usage
ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_PYTHON_DOWNLOADS=never \
MCP_SERVER_HOST="0.0.0.0" \
PYTHONUNBUFFERED=1
# Set up application directory
WORKDIR /app
# Copy the ENTIRE graphiti project (both core and mcp_server)
# This allows us to use the local graphiti-core
COPY . /app
# Build and install graphiti-core from local source first
WORKDIR /app
RUN uv pip install --system "./[neo4j,falkordb]"
# Now set up MCP server
WORKDIR /app/mcp_server
# Remove the local path override for graphiti-core in Docker builds
# and remove graphiti-core from dependencies since we already installed it
RUN sed -i '/\[tool\.uv\.sources\]/,/graphiti-core/d' pyproject.toml && \
sed -i '/graphiti-core\[falkordb\]/d' pyproject.toml
# Install remaining MCP server dependencies (graphiti-core already installed from local)
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --no-dev
# Accept version build arguments
ARG GRAPHITI_CORE_VERSION=local
ARG MCP_SERVER_VERSION=1.0.0
ARG BUILD_DATE
ARG VCS_REF
# Store version info
RUN echo "${GRAPHITI_CORE_VERSION}" > /app/mcp_server/.graphiti-core-version
# Create log directory
RUN mkdir -p /var/log/graphiti
# Add Docker labels with version information
LABEL org.opencontainers.image.title="Graphiti MCP Server (Custom Build)" \
org.opencontainers.image.description="Custom Graphiti MCP server with local graphiti-core changes" \
org.opencontainers.image.version="${MCP_SERVER_VERSION}" \
org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.revision="${VCS_REF}" \
org.opencontainers.image.vendor="Custom Build" \
org.opencontainers.image.source="https://github.com/lvarming/graphiti" \
graphiti.core.version="${GRAPHITI_CORE_VERSION}" \
graphiti.core.source="local"
# Expose MCP server port
EXPOSE 8000
# Health check - verify MCP server is responding
HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Run the MCP server
CMD ["uv", "run", "main.py"]