Skip to main content
Glama
sasajib

Unified Dynamic MCP Server

by sasajib
README.md
# Unified Dynamic MCP Server

**Single MCP server with progressive discovery for 96-160x token reduction**

Consolidates code understanding (Codanna), documentation (Context7), browser automation (Playwright), memory (Claude-mem), and knowledge graph (Graphiti+LadybugDB) into one unified server.

> ✨ **NEW**: Graphiti knowledge graph now supports Google Gemini! Use free Gemini API with embedded LadybugDB (no Docker required).

## Features

✨ **Progressive Discovery** - 3-step pattern reduces tokens from 10,000+ to 50-200
šŸ”§ **Dynamic Tool Loading** - Lazy load capabilities only when needed
šŸŽÆ **5 Integrated Capabilities** - Code, docs, browser, memory, knowledge graph
šŸš€ **Git Submodules** - Auto-update with `git submodule update --remote`
⚔ **Fast** - Sub-10ms symbol lookup (Codanna), embedded LadybugDB (no Docker)

## Quick Start

```bash
# 1. Clone with submodules
git clone --recursive https://github.com/yourusername/unified-mcp.git
cd unified-mcp

# 2. Install dependencies (using uv - recommended)
uv pip install -r requirements.txt

# 3. Install Claude-mem plugin (required for memory capability)
# In Claude Code terminal:
/plugin marketplace add thedotmack/claude-mem
/plugin install claude-mem
# Then restart Claude Code

# 4. Add to Claude Code (replace /path/to/unified-mcp with actual path)
claude mcp add --transport stdio \
  unified-mcp \
  -- uv --directory /absolute/path/to/unified-mcp run server.py

# 5. For Graphiti + Google Gemini support (optional)
claude mcp add --transport stdio unified-mcp \
  -e GRAPHITI_ENABLED=true \
  -e GRAPHITI_LLM_PROVIDER=google_ai \
  -e GRAPHITI_EMBEDDER_PROVIDER=google_ai \
  -e GOOGLE_API_KEY=your-gemini-api-key-here \
  -e GRAPHITI_LLM_MODEL=gemini-1.5-pro \
  -e GRAPHITI_EMBEDDER_MODEL=text-embedding-004 \
  -- uv --directory /absolute/path/to/unified-mcp run server.py

# 6. Restart Claude Code and verify
# Ask Claude: "What tools do you have available?"
```

**Manual Testing (optional):**
```bash
# Run tests
pytest tests/ -v

# Start server directly
uv run server.py
```

## Progressive Discovery Pattern

Traditional approach: Load all 20 tools upfront = **10,000 tokens**

Our approach:
1. **Search** (`search_tools`) → Find relevant tools → **~50 tokens**
2. **Describe** (`describe_tools`) → Get full schemas → **~200 tokens/tool**
3. **Execute** (`execute_tool`) → Run the tool → **Variable**

**Result: 98% token reduction** šŸŽ‰

## Architecture

```
Unified MCP Server
ā”œā”€ā”€ Progressive Discovery Engine
ā”œā”€ā”€ Dynamic Tool Registry
ā”œā”€ā”€ 5 Capability Modules
│   ā”œā”€ā”€ Codanna (code understanding)
│   ā”œā”€ā”€ Context7 (documentation)
│   ā”œā”€ā”€ Playwright (browser automation)
│   ā”œā”€ā”€ Claude-mem (memory)
│   └── Graphiti+LadybugDB (knowledge graph)
```

## Installation

### Requirements

- **Python 3.12+** (for LadybugDB)
- **Rust/Cargo** (for Codanna) - [Install Rust](https://rustup.rs/)
- **Node.js 18+** (for Context7/Playwright) - [Install Node](https://nodejs.org/)
- **Claude Code** (for Claude-mem plugin) - [Install Claude Code](https://claude.ai/download)

### Claude-Mem Setup (Memory Capability)

**Important:** Claude-mem is a separate MCP server that runs as a Claude Code plugin. It must be installed before the unified-mcp memory capability will work.

**Installation Steps:**

1. **Install via Claude Code Plugin Marketplace:**
   ```bash
   # In Claude Code terminal
   /plugin marketplace add thedotmack/claude-mem
   /plugin install claude-mem
   ```

2. **Restart Claude Code** - The plugin auto-starts the HTTP API on `http://localhost:37777`

3. **Verify Installation:**
   ```bash
   # Check the service is running
   curl http://localhost:37777

   # Or visit the web UI
   open http://localhost:37777
   ```

4. **Troubleshooting:**
   ```bash
   # Use the built-in troubleshooting skill
   /claude-mem:troubleshoot
   ```

**What Gets Installed:**
- HTTP API service on port 37777 (auto-managed by Bun)
- SQLite database at `~/.claude-mem/claude-mem.db`
- Web UI for browsing stored memories
- 5 lifecycle hooks for automatic memory capture
- Vector search via Chroma for semantic queries

**Architecture:**
- The unified-mcp server connects to claude-mem's HTTP API
- No direct npm installation needed - plugin handles all dependencies
- Auto-starts when Claude Code is running
- Stores observations across sessions with semantic search

For more details, see the [claude-mem documentation](https://github.com/thedotmack/claude-mem).

### Install Dependencies

```bash
# Python dependencies
pip install -r requirements.txt

# Codanna (code understanding)
cargo install codanna --all-features

# Git submodules (for Context7 and Playwright only - claude-mem is a plugin)
git submodule update --init --recursive
cd capabilities/context7 && npm install
cd ../playwright-mcp && npm install
# Note: claude-mem is installed as a Claude Code plugin (see above), not via npm
```

**Codanna Auto-Indexing:**

The server automatically creates and manages Codanna indexes:
- āœ… **Automatic indexing** on first run (indexes `src`, `lib`, `handlers`, `core` directories)
- āœ… **No manual setup required** - just install Codanna and start the server
- šŸ”„ **Optional file watching** - enable `watch_changes: true` in `config/catalog.yaml` to auto-reindex on file changes

To customize auto-indexing, edit `config/catalog.yaml`:

```yaml
code_understanding:
  auto_index: true  # Auto-create index if missing
  watch_changes: false  # Watch files and re-index (requires watchdog)
  index_dirs:  # Directories to index
    - src
    - lib
    - your-custom-dir
```

## Usage

### Start Server

```bash
python server.py
```

Server runs on stdio (MCP protocol).

### Configure with Claude Code

**Option 1: Using `claude mcp add` (Recommended)**

Replace `/absolute/path/to/unified-mcp` with your actual installation path.

**Basic Configuration (No Graphiti):**
```bash
claude mcp add --transport stdio \
  unified-mcp \
  -- uv --directory /absolute/path/to/unified-mcp run server.py
```

**With Graphiti + Google Gemini:**
```bash
claude mcp add --transport stdio unified-mcp \
  -e GRAPHITI_ENABLED=true \
  -e GRAPHITI_LLM_PROVIDER=google_ai \
  -e GRAPHITI_EMBEDDER_PROVIDER=google_ai \
  -e GOOGLE_API_KEY=your-gemini-api-key-here \
  -e GRAPHITI_LLM_MODEL=gemini-1.5-pro \
  -e GRAPHITI_EMBEDDER_MODEL=text-embedding-004 \
  -- uv --directory /absolute/path/to/unified-mcp run server.py
```

**With Graphiti + OpenAI:**
```bash
claude mcp add --transport stdio unified-mcp \
  -e GRAPHITI_ENABLED=true \
  -e GRAPHITI_LLM_PROVIDER=openai \
  -e GRAPHITI_EMBEDDER_PROVIDER=openai \
  -e OPENAI_API_KEY=sk-your-openai-key-here \
  -e GRAPHITI_LLM_MODEL=gpt-4o \
  -e GRAPHITI_EMBEDDER_MODEL=text-embedding-3-small \
  -- uv --directory /absolute/path/to/unified-mcp run server.py
```

**With Graphiti + Anthropic Claude + Voyage Embeddings:**
```bash
claude mcp add --transport stdio unified-mcp \
  -e GRAPHITI_ENABLED=true \
  -e GRAPHITI_LLM_PROVIDER=anthropic \
  -e GRAPHITI_EMBEDDER_PROVIDER=voyage_ai \
  -e ANTHROPIC_API_KEY=sk-ant-your-key-here \
  -e VOYAGE_API_KEY=pa-your-voyage-key-here \
  -e GRAPHITI_LLM_MODEL=claude-3-5-sonnet-20241022 \
  -e GRAPHITI_EMBEDDER_MODEL=voyage-3 \
  -- uv --directory /absolute/path/to/unified-mcp run server.py
```

**With Graphiti + Local Ollama (Free):**
```bash
claude mcp add --transport stdio unified-mcp \
  -e GRAPHITI_ENABLED=true \
  -e GRAPHITI_LLM_PROVIDER=ollama \
  -e GRAPHITI_EMBEDDER_PROVIDER=ollama \
  -e OLLAMA_BASE_URL=http://localhost:11434 \
  -e GRAPHITI_LLM_MODEL=llama3.1 \
  -e GRAPHITI_EMBEDDER_MODEL=nomic-embed-text \
  -- uv --directory /absolute/path/to/unified-mcp run server.py
```

**Full Configuration Example (All Options):**
```bash
claude mcp add --transport stdio unified-mcp \
  -e CODANNA_INDEX_DIR=.codanna \
  -e CLAUDE_MEM_API_URL=http://localhost:37777 \
  -e GRAPHITI_ENABLED=true \
  -e GRAPHITI_LLM_PROVIDER=google_ai \
  -e GRAPHITI_EMBEDDER_PROVIDER=google_ai \
  -e GOOGLE_API_KEY=your-gemini-api-key-here \
  -e GRAPHITI_LLM_MODEL=gemini-1.5-pro \
  -e GRAPHITI_EMBEDDER_MODEL=text-embedding-004 \
  -e GRAPHITI_DB_PATH=.graphiti/ladybug.db \
  -- uv --directory /absolute/path/to/unified-mcp run server.py
```

**Option 2: Manual Configuration**

Add to your MCP settings file (`~/.config/claude/mcp_settings.json`):

```json
{
  "mcpServers": {
    "unified-mcp": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/unified-mcp",
        "run",
        "server.py"
      ],
      "env": {
        "CODANNA_INDEX_DIR": "${workspaceFolder}/.codanna",
        "CLAUDE_MEM_API_URL": "http://localhost:37777",
        "GRAPHITI_ENABLED": "true",
        "GOOGLE_API_KEY": "your-gemini-api-key-here"
      }
    }
  }
}
```

**With Graphiti + Google Gemini (Full Configuration):**

```json
{
  "mcpServers": {
    "unified-mcp": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/unified-mcp",
        "run",
        "server.py"
      ],
      "env": {
        "CODANNA_INDEX_DIR": "${workspaceFolder}/.codanna",
        "CLAUDE_MEM_API_URL": "http://localhost:37777",
        "GRAPHITI_ENABLED": "true",
        "GRAPHITI_LLM_PROVIDER": "google_ai",
        "GRAPHITI_EMBEDDER_PROVIDER": "google_ai",
        "GOOGLE_API_KEY": "your-gemini-api-key-here",
        "GRAPHITI_LLM_MODEL": "gemini-1.5-pro",
        "GRAPHITI_EMBEDDER_MODEL": "text-embedding-004"
      }
    }
  }
}
```

**Environment Variables:**

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `CODANNA_INDEX_DIR` | No | `.codanna` | Codanna index directory |
| `CLAUDE_MEM_API_URL` | No | `http://localhost:37777` | Claude-mem API endpoint |
| `GRAPHITI_ENABLED` | No | `false` | Enable Graphiti knowledge graph |
| `GRAPHITI_LLM_PROVIDER` | No | `openai` | LLM provider: `openai`, `anthropic`, `azure_openai`, `ollama`, `google_ai` |
| `GRAPHITI_EMBEDDER_PROVIDER` | No | `openai` | Embedder: `openai`, `voyage_ai`, `azure_openai`, `ollama`, `google_ai` |
| `GRAPHITI_LLM_MODEL` | No | (varies) | Model name (e.g., `gemini-2.5-flash`, `gpt-4o`, `claude-3-5-sonnet-20241022`) |
| `GRAPHITI_EMBEDDER_MODEL` | No | (varies) | Embedder model (e.g., `text-embedding-004`, `text-embedding-3-small`) |
| `GRAPHITI_RERANKER_MODEL` | No | `gemini-2.5-flash-lite` | Reranker model (Gemini only) |
| `GOOGLE_API_KEY` | If using Gemini | - | Google AI API key for Gemini |
| `OPENAI_API_KEY` | If using OpenAI | - | OpenAI API key |
| `ANTHROPIC_API_KEY` | If using Claude | - | Anthropic API key |

**Playwright Browser Configuration:**

Browser automation runs with **headless=false** by default (shows browser window). Configure in `config/catalog.yaml`:

```yaml
browser_automation:
  headless: false  # Set to true to run browser in background
```

**Restart Claude Code** to load the server.

**Verify it's working:**
- Ask Claude: "What tools do you have available?"
- You should see tools like `search_tools`, `describe_tools`, `execute_tool`, etc.

### Configuration

Edit `config/catalog.yaml`:

```yaml
capabilities:
  code_understanding:
    enabled: true  # Toggle capabilities
    tools: [search_code, get_call_graph, ...]
```

### Tools Available

**Progressive Discovery** (Meta-tools):
- `search_tools(query)` - Find relevant tools
- `describe_tools([names])` - Get full schemas
- `execute_tool(name, args)` - Run a tool

**Capability Management**:
- `list_capabilities()` - See all capabilities
- `enable_capability(name)` - Enable at runtime
- `disable_capability(name)` - Disable at runtime

**Code Understanding** (Codanna - Phase 2):
- `search_code` - Semantic code search
- `get_call_graph` - Function relationships
- `find_symbol` - Symbol lookup (sub-10ms)
- `find_implementations` - Find implementations

**Documentation** (Context7 - Phase 3):
- `resolve_library_id` - Resolve library name
- `get_library_docs` - Fetch documentation

**Browser Automation** (Playwright - Phase 3):
- `playwright_navigate` - Navigate to URL
- `playwright_screenshot` - Take screenshot
- `playwright_click` - Click element
- `playwright_fill` - Fill form field
- `playwright_evaluate` - Execute JavaScript

**Memory** (Claude-mem - Phase 4):
- `mem_search` - Search observations
- `mem_get_observation` - Get by ID
- `mem_recent_context` - Recent sessions
- `mem_timeline` - Timeline view

**Knowledge Graph** (Graphiti - Phase 4):
- `store_insight` - Store knowledge
- `search_insights` - Search insights
- `query_graph` - Cypher queries
- `add_episode` - Add episode

## Development

### Run Tests

```bash
# All tests
pytest tests/ -v

# Unit tests only
pytest tests/unit/ -v

# With coverage
pytest tests/ --cov=. --cov-report=term-missing

# Skip slow tests
pytest tests/ -m "not slow"
```

### Project Structure

```
unified-mcp/
ā”œā”€ā”€ server.py              # Main MCP server
ā”œā”€ā”€ config/
│   └── catalog.yaml       # Capability configuration
ā”œā”€ā”€ core/
│   ā”œā”€ā”€ dynamic_registry.py
│   ā”œā”€ā”€ progressive_discovery.py
│   └── capability_loader.py
ā”œā”€ā”€ handlers/             # Capability handlers
ā”œā”€ā”€ capabilities/         # Git submodules
ā”œā”€ā”€ tests/               # Comprehensive test suite
└── docs/                # Documentation
```

## Implementation Status

- āœ… **Phase 1**: Foundation (Dynamic registry, progressive discovery)
- āœ… **Phase 2**: Codanna integration (4 code understanding tools)
- āœ… **Phase 3**: Context7 + Playwright (7 tools: docs + browser automation)
- āœ… **Phase 4**: Claude-mem + Graphiti (8 tools: memory + knowledge graph)
- āœ… **Phase 5**: Comprehensive testing (Unit + Integration + E2E, 80%+ coverage, CI/CD)
- āœ… **Phase 6**: Documentation (Complete)

## License

Apache 2.0

## References

- [MCP Specification](https://modelcontextprotocol.io/)
- [Docker MCP Gateway](https://github.com/docker/mcp-gateway)
- [Codanna](https://github.com/bartolli/codanna)
- [Context7](https://github.com/upstash/context7)
- [Playwright MCP](https://github.com/microsoft/playwright-mcp)
- [Claude-mem](https://github.com/thedotmack/claude-mem)