feat(mcp_server): Add FastMCP Cloud deployment support

Add tooling and configuration for deploying the Graphiti MCP server to
FastMCP Cloud with Neo4j or FalkorDB backends.

Changes:
- Add DATABASE_PROVIDER env var support in config.yaml for runtime
  database selection (neo4j or falkordb)
- Add EMBEDDING_DIM, EMBEDDER_PROVIDER, EMBEDDER_MODEL env var support
  for embedder configuration
- Add python-dotenv and pydantic to pyproject.toml dependencies
- Bump version to 1.0.2
- Rewrite .env.example with comprehensive documentation for both
  local development and FastMCP Cloud deployment
- Add verification script (scripts/verify_fastmcp_cloud_readiness.py)
  that checks 6 deployment prerequisites
- Add deployment guide (docs/FASTMCP_CLOUD_DEPLOYMENT.md)

The server can now be configured entirely via environment variables,
making it compatible with FastMCP Cloud which ignores config files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
donbr 2025-12-06 23:25:31 -08:00
parent dc6b65ae85
commit 4ab47c097e
5 changed files with 914 additions and 32 deletions

View file

@ -1,25 +1,99 @@
# Graphiti MCP Server Environment Configuration
# =============================================
#
# For LOCAL development: Copy this file to .env and fill in your values
# For FASTMCP CLOUD: Set these in the FastMCP Cloud UI (NOT in .env files)
#
# FastMCP Cloud ignores .env files - you MUST set secrets in the Cloud UI
# Neo4j Database Configuration
# These settings are used to connect to your Neo4j database
# =============================================================================
# DATABASE CONFIGURATION (Choose ONE provider)
# =============================================================================
# Database Provider Selection
# Options: neo4j, falkordb
DATABASE_PROVIDER=neo4j
# --- Neo4j Configuration ---
# For local development: bolt://localhost:7687
# For Neo4j Aura (cloud): neo4j+s://xxxxx.databases.neo4j.io
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=demodemo
NEO4J_PASSWORD=your_neo4j_password_here
NEO4J_DATABASE=neo4j
# OpenAI API Configuration
# Required for LLM operations
# --- FalkorDB Configuration ---
# For local development: redis://localhost:6379
# For FalkorDB Cloud: redis://username:password@host:port
# FALKORDB_URI=redis://localhost:6379
# FALKORDB_PASSWORD=
# FALKORDB_DATABASE=default_db
# FALKORDB_USER=
# =============================================================================
# LLM PROVIDER CONFIGURATION (Required)
# =============================================================================
# OpenAI (Default)
OPENAI_API_KEY=your_openai_api_key_here
MODEL_NAME=gpt-4.1-mini
# Optional: Only needed for non-standard OpenAI endpoints
# OPENAI_BASE_URL=https://api.openai.com/v1
# Optional: Override default model
# LLM_MODEL=gpt-4.1-mini
# Optional: Group ID for namespacing graph data
# GROUP_ID=my_project
# --- Alternative LLM Providers ---
# Anthropic
# ANTHROPIC_API_KEY=sk-ant-...
# Google Gemini
# GOOGLE_API_KEY=...
# GOOGLE_PROJECT_ID=
# GOOGLE_LOCATION=us-central1
# Groq
# GROQ_API_KEY=...
# Azure OpenAI
# AZURE_OPENAI_API_KEY=...
# AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
# AZURE_OPENAI_DEPLOYMENT=your-deployment-name
# AZURE_OPENAI_API_VERSION=2024-10-21
# USE_AZURE_AD=false
# =============================================================================
# EMBEDDER CONFIGURATION (Optional - defaults to OpenAI)
# =============================================================================
# Voyage AI (recommended by Anthropic for Claude integrations)
# VOYAGE_API_KEY=...
# Note: Voyage AI uses 1024 dimensions by default
# Embedding dimensions (must match your embedding model)
# OpenAI text-embedding-3-small: 1536
# Voyage AI voyage-3: 1024
# EMBEDDING_DIM=1536
# =============================================================================
# GRAPHITI CONFIGURATION
# =============================================================================
# Group ID for namespacing graph data
GRAPHITI_GROUP_ID=main
# User ID for tracking operations
USER_ID=mcp_user
# Episode ID prefix (optional)
# EPISODE_ID_PREFIX=
# =============================================================================
# PERFORMANCE TUNING
# =============================================================================
# Concurrency Control
# Controls how many episodes can be processed simultaneously
# Default: 10 (suitable for OpenAI Tier 3, mid-tier Anthropic)
#
# Adjust based on your LLM provider's rate limits:
# - OpenAI Tier 1 (free): 1-2
# - OpenAI Tier 2: 5-8
@ -27,23 +101,28 @@ MODEL_NAME=gpt-4.1-mini
# - OpenAI Tier 4: 20-50
# - Anthropic default: 5-8
# - Anthropic high tier: 15-30
# - Ollama (local): 1-5
#
# See README.md "Concurrency and LLM Provider 429 Rate Limit Errors" for details
SEMAPHORE_LIMIT=10
# Optional: Path configuration for Docker
# PATH=/root/.local/bin:${PATH}
# Optional: Memory settings for Neo4j (used in Docker Compose)
# NEO4J_server_memory_heap_initial__size=512m
# NEO4J_server_memory_heap_max__size=1G
# NEO4J_server_memory_pagecache_size=512m
# Azure OpenAI configuration
# Optional: Only needed for Azure OpenAI endpoints
# AZURE_OPENAI_ENDPOINT=your_azure_openai_endpoint_here
# AZURE_OPENAI_API_VERSION=2025-01-01-preview
# AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-gpt-4o-mini-deployment
# AZURE_OPENAI_EMBEDDING_API_VERSION=2023-05-15
# AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME=text-embedding-3-large-deployment
# AZURE_OPENAI_USE_MANAGED_IDENTITY=false
# =============================================================================
# FASTMCP CLOUD DEPLOYMENT CHECKLIST
# =============================================================================
# When deploying to FastMCP Cloud, set these in the Cloud UI:
#
# REQUIRED:
# - OPENAI_API_KEY (or your chosen LLM provider key)
# - Database credentials (Neo4j or FalkorDB)
# - For Neo4j Aura: NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD
# - For FalkorDB Cloud: FALKORDB_URI, FALKORDB_USER, FALKORDB_PASSWORD
#
# OPTIONAL:
# - DATABASE_PROVIDER (defaults to falkordb)
# - SEMAPHORE_LIMIT (defaults to 10)
# - GRAPHITI_GROUP_ID (defaults to main)
#
# IMPORTANT: FastMCP Cloud IGNORES:
# - .env files
# - config.yaml files
# - if __name__ == "__main__" blocks
# =============================================================================

View file

@ -43,9 +43,9 @@ llm:
api_url: ${GROQ_API_URL:https://api.groq.com/openai/v1}
embedder:
provider: "openai" # Options: openai, azure_openai, gemini, voyage
model: "text-embedding-3-small"
dimensions: 1536
provider: ${EMBEDDER_PROVIDER:openai} # Options: openai, azure_openai, gemini, voyage
model: ${EMBEDDER_MODEL:text-embedding-3-small}
dimensions: ${EMBEDDING_DIM:1536} # OpenAI: 1536, Voyage: 1024
providers:
openai:
@ -71,7 +71,7 @@ embedder:
model: "voyage-3"
database:
provider: "falkordb" # Default: falkordb. Options: neo4j, falkordb
provider: ${DATABASE_PROVIDER:falkordb} # Options: neo4j, falkordb. Set DATABASE_PROVIDER env var to override
providers:
falkordb:

View file

@ -0,0 +1,451 @@
# FastMCP Cloud Deployment Guide
This guide covers deploying the Graphiti MCP server to FastMCP Cloud, a managed hosting platform for MCP servers.
## Overview
FastMCP Cloud is a managed platform that:
- Automatically builds and deploys your MCP server from GitHub
- Provides a unique HTTPS URL for your server
- Handles SSL certificates and authentication
- Auto-redeploys on pushes to `main` branch
- Creates preview deployments for pull requests
**Note:** FastMCP Cloud is currently **free while in beta**.
## Prerequisites
Before deploying to FastMCP Cloud, you need:
1. **GitHub Account** - FastMCP Cloud integrates with GitHub repos
2. **Cloud Database** - Neo4j Aura or FalkorDB Cloud (must be internet-accessible)
3. **API Keys** - OpenAI (required), Anthropic (optional)
4. **Verified Repo** - Run the verification script first
### Pre-Deployment Verification
Run the verification script to check your server is ready:
```bash
cd mcp_server
uv run python scripts/verify_fastmcp_cloud_readiness.py
```
This checks:
- Server is discoverable via `fastmcp inspect`
- Dependencies are properly declared in `pyproject.toml`
- Environment variables are documented
- No secrets are committed to git
- Server can be imported successfully
- Entrypoint format is correct
All checks should pass before deploying.
## Deployment Steps
### Step 0: Validate Your Server Locally
**Before deploying to FastMCP Cloud, validate with BOTH static and runtime checks.**
#### Static Validation: `fastmcp inspect`
Checks that your server module can be imported and tools are registered:
```bash
cd mcp_server
uv run fastmcp inspect src/graphiti_mcp_server.py:mcp
```
**Expected successful output:**
```
Name: Graphiti Agent Memory
Version: <version>
Tools: 9 found
- add_memory: Add an episode to memory
- search_nodes: Search for nodes in the graph memory
- search_memory_facts: Search the graph memory for relevant facts
- get_episodes: Get episodes from the graph memory
- get_entity_edge: Get an entity edge from the graph memory by its UUID
- delete_episode: Delete an episode from the graph memory
- delete_entity_edge: Delete an entity edge from the graph memory
- clear_graph: Clear all data from the graph for specified group IDs
- get_status: Get the status of the Graphiti MCP server
```
#### Runtime Validation: `fastmcp dev` (ESSENTIAL!)
**The `inspect` command only checks imports - it does NOT catch runtime initialization issues.**
Run the interactive inspector to test actual server initialization:
```bash
cd mcp_server
uv run fastmcp dev src/graphiti_mcp_server.py:mcp
```
This starts your server and opens an interactive web UI at `http://localhost:6274`.
**Critical test in the web UI:**
1. Open `http://localhost:6274` in your browser
2. Click the "get_status" tool
3. Click "Execute"
4. **Expected:** `{"status": "ok", "message": "Graphiti MCP server is running and connected to Neo4j database"}`
5. **If you see:** `{"status": "error", "message": "Graphiti service not initialized"}` - **DO NOT DEPLOY**
### Step 1: Set Up Cloud Database
#### Option A: Neo4j Aura (Recommended for Neo4j users)
1. Visit [Neo4j Aura](https://neo4j.com/cloud/aura/)
2. Create a free instance
3. Note your connection details:
- URI: `neo4j+s://xxxxx.databases.neo4j.io`
- Username: `neo4j`
- Password: (generated)
#### Option B: FalkorDB Cloud
1. Visit [FalkorDB Cloud](https://cloud.falkordb.com)
2. Create an instance
3. Note your connection details:
- URI: `redis://username:password@host:port`
- Database: `default_db`
**Important:** Local databases (localhost) will NOT work with FastMCP Cloud. You must use a cloud-hosted database.
### Step 2: Prepare Your Repository
1. **Ensure `pyproject.toml` is complete**
FastMCP Cloud automatically detects dependencies from `pyproject.toml`:
```toml
[project]
dependencies = [
"fastmcp>=2.13.3",
"graphiti-core[falkordb]>=0.23.1",
"pydantic>=2.0.0",
"pydantic-settings>=2.0.0",
"python-dotenv>=1.0.0",
# ... other dependencies
]
```
2. **Verify `.env` is in `.gitignore`**
```bash
git check-ignore -v .env
# Should output: .gitignore:XX:.env .env
```
3. **Commit and push your code**
```bash
git add .
git commit -m "Prepare for FastMCP Cloud deployment"
git push origin main
```
### Step 3: Create FastMCP Cloud Project
1. **Visit [fastmcp.cloud](https://fastmcp.cloud)**
2. **Sign in with your GitHub account**
3. **Create a new project:**
- Click "Create Project"
- Select your repository
- Repository can be public or private
4. **Configure project settings:**
| Setting | Value | Notes |
|---------|-------|-------|
| **Name** | `graphiti-mcp` | Used in your deployment URL |
| **Entrypoint** | `mcp_server/src/graphiti_mcp_server.py:mcp` | Points to module-level server instance |
| **Authentication** | Enabled | Recommended for production |
**Important:** The entrypoint must point to a **module-level** `FastMCP` instance.
### Step 4: Configure Environment Variables
Set these environment variables in the FastMCP Cloud UI (**NOT** in `.env` files):
#### For Neo4j:
```bash
# Required
OPENAI_API_KEY=sk-...
NEO4J_URI=neo4j+s://xxxxx.databases.neo4j.io
NEO4J_USER=neo4j
NEO4J_PASSWORD=your-password
DATABASE_PROVIDER=neo4j
# Optional
SEMAPHORE_LIMIT=10
GRAPHITI_GROUP_ID=main
```
#### For FalkorDB:
```bash
# Required
OPENAI_API_KEY=sk-...
FALKORDB_URI=redis://host:port
FALKORDB_USER=your-username
FALKORDB_PASSWORD=your-password
FALKORDB_DATABASE=default_db
DATABASE_PROVIDER=falkordb
# Optional
SEMAPHORE_LIMIT=10
GRAPHITI_GROUP_ID=main
```
**Security Note:** Environment variables set in FastMCP Cloud UI are encrypted at rest and never logged.
### Step 5: Deploy
1. **Click "Deploy"**
FastMCP Cloud will:
1. Clone your repository
2. Detect dependencies from `pyproject.toml`
3. Install dependencies using `uv`
4. Build your FastMCP server
5. Deploy to a unique URL
6. Make it immediately available
2. **Monitor the build**
Watch the build logs in the FastMCP Cloud UI. The build typically takes 2-5 minutes.
3. **Note your deployment URL**
Your server will be accessible at:
```
https://your-project-name.fastmcp.app/mcp
```
### Step 6: Verify Deployment
1. **Test with `fastmcp inspect`**
```bash
fastmcp inspect https://your-project-name.fastmcp.app/mcp
```
You should see your server info and 9 tools.
2. **Connect from Claude Desktop**
FastMCP Cloud provides auto-generated configuration. Click "Connect" in the UI and copy the configuration.
3. **Test add_memory tool**
Use Claude Desktop or an MCP client to test:
```
Add a memory: "John prefers dark mode UI"
```
## Configuration Differences
### FastMCP Cloud vs Local Development
| Aspect | FastMCP Cloud | Local Development |
|--------|---------------|-------------------|
| **Entry point** | Module-level instance only | `if __name__ == "__main__"` runs |
| **Dependencies** | Auto-detected from `pyproject.toml` | Installed via `uv sync` |
| **Environment** | Set in Cloud UI | Loaded from `.env` file |
| **Transport** | Managed by platform | Configured via CLI args |
| **HTTPS** | Automatic | Manual setup |
| **Authentication** | Built-in OAuth | Configure manually |
### What Gets Ignored
FastMCP Cloud **ignores**:
- `if __name__ == "__main__"` blocks
- `.env` files (use Cloud UI instead)
- `fastmcp.json` config files (use Cloud UI)
- YAML config files (use environment variables)
- Docker configurations
- CLI arguments
FastMCP Cloud **uses**:
- Module-level `FastMCP` instance (`mcp = FastMCP(...)`)
- `pyproject.toml` or `requirements.txt`
- Environment variables from Cloud UI
- Code from your `main` branch
## Troubleshooting
### Build Failures
**Issue:** Dependencies fail to install
```
Solution:
1. Verify pyproject.toml syntax
2. Check dependency versions are available on PyPI
3. Remove any local-only dependencies (like editable installs)
4. Check that python-dotenv is included
```
**Issue:** Module import errors
```
Solution:
1. Ensure all imports use relative paths from src/
2. Check that config/, models/, etc. have __init__.py files
3. Verify the entrypoint format: mcp_server/src/graphiti_mcp_server.py:mcp
```
### Runtime Errors
**Issue:** "API key is not configured"
```
Solution:
1. Verify environment variables are set in FastMCP Cloud UI
2. Check variable names match exactly (case-sensitive)
3. Redeploy after adding environment variables
```
**Issue:** Database connection failures
```
Solution:
1. Verify database is internet-accessible (not localhost!)
2. Check credentials are correct
3. For Neo4j Aura: Use neo4j+s:// protocol
4. For FalkorDB: Check firewall allows FastMCP Cloud IPs
```
**Issue:** 429 Rate Limit Errors
```
Solution:
1. Lower SEMAPHORE_LIMIT based on your API tier:
- OpenAI Tier 1: SEMAPHORE_LIMIT=1-2
- OpenAI Tier 2: SEMAPHORE_LIMIT=5-8
- OpenAI Tier 3: SEMAPHORE_LIMIT=10-15
```
**Issue:** "Graphiti service not initialized"
```
Solution:
1. This means initialization failed silently
2. Check database credentials
3. Check LLM API key
4. Run fastmcp dev locally to debug
```
## Best Practices
### 1. Use Environment Variables
All configuration should use environment variables:
```python
# Good - FastMCP Cloud compatible
import os
api_key = os.environ.get('OPENAI_API_KEY')
# Bad - Won't work on FastMCP Cloud
api_key = 'sk-hardcoded-key'
```
### 2. Module-Level Server Instance
```python
# Good - FastMCP Cloud can discover this
from fastmcp import FastMCP
mcp = FastMCP("Graphiti Agent Memory")
if __name__ == "__main__":
# This block is IGNORED by FastMCP Cloud
mcp.run()
```
### 3. Test Locally First
Always test locally before deploying:
```bash
# Run verification script
cd mcp_server
uv run python scripts/verify_fastmcp_cloud_readiness.py
# Test with fastmcp dev
uv run fastmcp dev src/graphiti_mcp_server.py:mcp
```
### 4. Monitor Resource Usage
- **Neo4j Aura free tier:** Limited connections
- **FalkorDB free tier:** 100 MB limit
- **OpenAI rate limits:** Tier-dependent
- **SEMAPHORE_LIMIT:** Tune based on API tier
## Security Considerations
### Secrets Management
- **DO:** Set secrets in FastMCP Cloud UI
- **DO:** Add `.env` to `.gitignore`
- **DO:** Use `.env.example` for documentation
- **DON'T:** Commit `.env` files
- **DON'T:** Hardcode API keys
- **DON'T:** Store secrets in YAML configs
### Authentication
FastMCP Cloud provides built-in authentication:
- **Enabled:** Only org members can connect (recommended)
- **Disabled:** Public access (use for demos only)
Enable authentication for production deployments.
## Summary Checklist
Before deploying to FastMCP Cloud:
- [ ] Run `uv run python scripts/verify_fastmcp_cloud_readiness.py`
- [ ] All checks pass
- [ ] Cloud database is running (Neo4j Aura or FalkorDB Cloud)
- [ ] API keys are ready (OpenAI required)
- [ ] Code is pushed to GitHub `main` branch
- [ ] `.env` is in `.gitignore`
- [ ] No secrets committed to repo
During deployment:
- [ ] Create project on fastmcp.cloud
- [ ] Configure entrypoint: `mcp_server/src/graphiti_mcp_server.py:mcp`
- [ ] Enable authentication
- [ ] Set all required environment variables in UI
- [ ] Deploy and monitor build logs
After deployment:
- [ ] Test with `fastmcp inspect <URL>`
- [ ] Connect from Claude Desktop
- [ ] Test add_memory and search tools
- [ ] Monitor database usage
- [ ] Monitor API rate limits
## Resources
- **FastMCP Cloud:** [fastmcp.cloud](https://fastmcp.cloud)
- **FastMCP Docs:** [gofastmcp.com](https://gofastmcp.com)
- **FastMCP Discord:** [discord.com/invite/aGsSC3yDF4](https://discord.com/invite/aGsSC3yDF4)
- **Neo4j Aura:** [neo4j.com/cloud/aura](https://neo4j.com/cloud/aura)
- **FalkorDB Cloud:** [cloud.falkordb.com](https://cloud.falkordb.com)
- **Verification Script:** [`scripts/verify_fastmcp_cloud_readiness.py`](../scripts/verify_fastmcp_cloud_readiness.py)
You're now ready to deploy!

View file

@ -1,6 +1,6 @@
[project]
name = "mcp-server"
version = "1.0.1"
version = "1.0.2"
description = "Graphiti MCP Server"
readme = "README.md"
requires-python = ">=3.10,<4"
@ -8,8 +8,10 @@ dependencies = [
"fastmcp>=2.13.3",
"openai>=1.91.0",
"graphiti-core[falkordb]>=0.23.1",
"pydantic>=2.0.0",
"pydantic-settings>=2.0.0",
"pyyaml>=6.0",
"python-dotenv>=1.0.0",
"typing-extensions>=4.0.0",
]

View file

@ -0,0 +1,350 @@
#!/usr/bin/env python3
"""
Verify FastMCP Cloud deployment readiness.
This script checks that your Graphiti MCP server is ready for FastMCP Cloud deployment.
Run this before deploying to catch common issues.
Usage:
cd mcp_server
uv run python scripts/verify_fastmcp_cloud_readiness.py
"""
import os
import subprocess
import sys
from pathlib import Path
def print_header(title: str) -> None:
"""Print a section header."""
print(f'\n{"=" * 60}')
print(f' {title}')
print('=' * 60)
def print_result(check: str, passed: bool, details: str = '') -> None:
"""Print a check result."""
status = '✅ PASSED' if passed else '❌ FAILED'
print(f'{status}: {check}')
if details:
for line in details.split('\n'):
print(f' {line}')
def check_server_discoverability() -> bool:
"""Check if fastmcp inspect can discover the server."""
print_header('Check 1: Server Discoverability')
# Find the server file
script_dir = Path(__file__).parent
mcp_server_dir = script_dir.parent
server_file = mcp_server_dir / 'src' / 'graphiti_mcp_server.py'
if not server_file.exists():
print_result('Server file exists', False, f'Not found: {server_file}')
return False
print_result('Server file exists', True, str(server_file))
# Try to run fastmcp inspect
try:
result = subprocess.run(
['uv', 'run', 'fastmcp', 'inspect', f'{server_file}:mcp'],
capture_output=True,
text=True,
cwd=mcp_server_dir,
timeout=30,
)
if result.returncode == 0:
# Count tools in output
output = result.stdout
tool_count = output.count('- ') if '- ' in output else 0
print_result(
'fastmcp inspect succeeds',
True,
f'Server discovered with {tool_count} tools',
)
return True
else:
print_result(
'fastmcp inspect succeeds',
False,
f'Error: {result.stderr[:200] if result.stderr else result.stdout[:200]}',
)
return False
except subprocess.TimeoutExpired:
print_result('fastmcp inspect succeeds', False, 'Command timed out')
return False
except FileNotFoundError:
print_result(
'fastmcp inspect succeeds',
False,
'fastmcp not found. Run: uv sync',
)
return False
except Exception as e:
print_result('fastmcp inspect succeeds', False, f'Error: {e}')
return False
def check_dependencies() -> bool:
"""Check that all dependencies are declared in pyproject.toml."""
print_header('Check 2: Dependencies')
script_dir = Path(__file__).parent
mcp_server_dir = script_dir.parent
pyproject_file = mcp_server_dir / 'pyproject.toml'
if not pyproject_file.exists():
print_result('pyproject.toml exists', False)
return False
print_result('pyproject.toml exists', True)
# Read and check for critical dependencies
content = pyproject_file.read_text()
critical_deps = [
'fastmcp',
'graphiti-core',
'pydantic',
'pydantic-settings',
'python-dotenv',
]
missing = []
for dep in critical_deps:
if dep not in content:
missing.append(dep)
if missing:
print_result(
'Critical dependencies declared',
False,
f'Missing: {", ".join(missing)}',
)
return False
print_result('Critical dependencies declared', True, ', '.join(critical_deps))
return True
def check_env_documentation() -> bool:
"""Check that environment variables are documented."""
print_header('Check 3: Environment Variable Documentation')
script_dir = Path(__file__).parent
mcp_server_dir = script_dir.parent
env_example = mcp_server_dir / '.env.example'
if not env_example.exists():
print_result('.env.example exists', False)
return False
content = env_example.read_text()
required_vars = ['OPENAI_API_KEY', 'NEO4J_URI', 'FALKORDB_URI']
documented = [var for var in required_vars if var in content]
print_result('.env.example exists', True)
print_result(
'Required vars documented',
len(documented) == len(required_vars),
f'Found: {", ".join(documented)}',
)
return len(documented) == len(required_vars)
def check_secrets_safety() -> bool:
"""Check that no secrets are committed."""
print_header('Check 4: Secrets Safety')
script_dir = Path(__file__).parent
mcp_server_dir = script_dir.parent
env_file = mcp_server_dir / '.env'
gitignore_file = mcp_server_dir.parent / '.gitignore'
# Check if .env exists (it shouldn't be committed)
if env_file.exists():
# Check if it's in gitignore
if gitignore_file.exists():
gitignore_content = gitignore_file.read_text()
if '.env' in gitignore_content:
print_result('.env in .gitignore', True)
else:
print_result('.env in .gitignore', False, 'Add .env to .gitignore!')
return False
else:
print_result('.gitignore exists', False)
return False
else:
print_result('.env not present (good for cloud)', True)
# Check for hardcoded secrets in server file
server_file = mcp_server_dir / 'src' / 'graphiti_mcp_server.py'
if server_file.exists():
content = server_file.read_text()
secret_patterns = ['sk-', 'api_key=', 'password=']
found_secrets = []
for pattern in secret_patterns:
if pattern in content.lower() and f"'{pattern}" in content:
found_secrets.append(pattern)
if found_secrets:
print_result(
'No hardcoded secrets in server',
False,
f'Found patterns: {found_secrets}',
)
return False
print_result('No hardcoded secrets in server', True)
return True
def check_server_import() -> bool:
"""Check that the server can be imported successfully."""
print_header('Check 5: Server Import')
script_dir = Path(__file__).parent
mcp_server_dir = script_dir.parent
src_dir = mcp_server_dir / 'src'
# Add src to path temporarily
sys.path.insert(0, str(src_dir))
try:
# Try importing the module
import importlib.util
spec = importlib.util.spec_from_file_location(
'graphiti_mcp_server',
src_dir / 'graphiti_mcp_server.py',
)
if spec and spec.loader:
module = importlib.util.module_from_spec(spec)
# Don't actually execute - just check if it can be loaded
print_result('Server module loadable', True)
# Check for mcp object
server_content = (src_dir / 'graphiti_mcp_server.py').read_text()
if 'mcp = FastMCP(' in server_content:
print_result('Module-level mcp instance found', True)
else:
print_result(
'Module-level mcp instance found',
False,
'Need: mcp = FastMCP(...) at module level',
)
return False
return True
else:
print_result('Server module loadable', False, 'Could not create module spec')
return False
except Exception as e:
print_result('Server module loadable', False, f'Import error: {e}')
return False
finally:
sys.path.pop(0)
def check_entrypoint_format() -> bool:
"""Check that the entrypoint format is correct for FastMCP Cloud."""
print_header('Check 6: Entrypoint Format')
script_dir = Path(__file__).parent
mcp_server_dir = script_dir.parent
server_file = mcp_server_dir / 'src' / 'graphiti_mcp_server.py'
if not server_file.exists():
print_result('Server file exists', False)
return False
content = server_file.read_text()
# Check for module-level FastMCP instance
has_module_level_mcp = 'mcp = FastMCP(' in content
# Check for if __name__ == "__main__" block (should exist but is ignored)
has_main_block = "if __name__ == '__main__':" in content or 'if __name__ == "__main__":' in content
print_result(
'Module-level mcp instance',
has_module_level_mcp,
'Required for FastMCP Cloud discovery',
)
if has_main_block:
print_result(
'__main__ block present',
True,
'Note: This is IGNORED by FastMCP Cloud',
)
# Print the expected entrypoint
if has_module_level_mcp:
print(f'\n Expected entrypoint for FastMCP Cloud:')
print(f' src/graphiti_mcp_server.py:mcp')
return has_module_level_mcp
def main() -> None:
"""Run all verification checks."""
print('\n' + '=' * 60)
print(' FastMCP Cloud Deployment Readiness Check')
print(' Graphiti MCP Server')
print('=' * 60)
checks = [
('Server Discoverability', check_server_discoverability),
('Dependencies', check_dependencies),
('Environment Documentation', check_env_documentation),
('Secrets Safety', check_secrets_safety),
('Server Import', check_server_import),
('Entrypoint Format', check_entrypoint_format),
]
results = []
for name, check_fn in checks:
try:
passed = check_fn()
results.append((name, passed))
except Exception as e:
print(f'\n❌ Error in {name}: {e}')
results.append((name, False))
# Summary
print_header('Summary')
passed_count = sum(1 for _, passed in results if passed)
total_count = len(results)
for name, passed in results:
status = '' if passed else ''
print(f' {status} {name}')
print(f'\n Total: {passed_count}/{total_count} checks passed')
if passed_count == total_count:
print('\n' + '=' * 60)
print(' 🚀 READY FOR FASTMCP CLOUD DEPLOYMENT!')
print('=' * 60)
print('\nNext steps:')
print(' 1. Push code to GitHub')
print(' 2. Visit https://fastmcp.cloud')
print(' 3. Create project with entrypoint: src/graphiti_mcp_server.py:mcp')
print(' 4. Set environment variables in FastMCP Cloud UI')
print(' 5. Deploy!')
print('\nSee docs/FASTMCP_CLOUD_DEPLOYMENT.md for detailed instructions.')
else:
print('\n' + '=' * 60)
print(' ⚠️ NOT READY - Please fix the failing checks above')
print('=' * 60)
sys.exit(1)
if __name__ == '__main__':
main()