Remove MCP-only deployment - using export approach instead
Removed deployment/mcp-only/* because it conflicts with our export-based architecture for ServiceBot integration. Why removed: - MCP-only requires Claude OS MCP server running in production - Export approach is simpler: SQLite file → ServiceBot (standalone) - No runtime dependency on Claude OS - Better aligns with "ServiceBot is standalone" design goal What we're keeping (export approach): - app/core/knowledge_exporter.py - templates/commands/claude-os-export.md - docs/EXPORT_FORMAT_SPEC.md - docs/CLAUDE_OS_FAMILY.md - docs/PISTN_API_INTEGRATION_PLAN.md - docs/VECTOR_SEARCH_OPTIMIZATION.md ServiceBot integration is export-based: Claude OS → Export SQLite → ServiceBot reads directly (no API calls)
This commit is contained in:
parent
ea779fba43
commit
43ad8605fe
14 changed files with 0 additions and 2455 deletions
|
|
@ -1,48 +0,0 @@
|
|||
# Claude OS MCP-Only Deployment Configuration
|
||||
|
||||
# ======================
|
||||
# Database Configuration
|
||||
# ======================
|
||||
SQLITE_DB_PATH=/app/data/claude-os.db
|
||||
|
||||
# ======================
|
||||
# MCP Server Configuration
|
||||
# ======================
|
||||
MCP_SERVER_HOST=0.0.0.0
|
||||
MCP_SERVER_PORT=8051
|
||||
|
||||
# ======================
|
||||
# Ollama Configuration
|
||||
# ======================
|
||||
OLLAMA_HOST=http://ollama:11434
|
||||
OLLAMA_EMBED_MODEL=nomic-embed-text
|
||||
OLLAMA_MODEL=llama3.1:latest
|
||||
|
||||
# ======================
|
||||
# CORS Configuration
|
||||
# ======================
|
||||
# Comma-separated list of allowed origins
|
||||
# Use "*" for development, specific domains for production
|
||||
CORS_ORIGINS=*
|
||||
|
||||
# ======================
|
||||
# Authentication (Optional)
|
||||
# ======================
|
||||
# Uncomment to enable JWT authentication
|
||||
# JWT_SECRET_KEY=your-super-secret-key-change-this-in-production
|
||||
# JWT_ALGORITHM=HS256
|
||||
# API_USERNAME=admin
|
||||
# API_PASSWORD=changeme
|
||||
|
||||
# ======================
|
||||
# RAG Configuration
|
||||
# ======================
|
||||
CHUNK_SIZE=512
|
||||
CHUNK_OVERLAP=128
|
||||
TOP_K_RETRIEVAL=20
|
||||
SIMILARITY_TOP_K=20
|
||||
|
||||
# ======================
|
||||
# Logging
|
||||
# ======================
|
||||
LOG_LEVEL=INFO
|
||||
|
|
@ -1,382 +0,0 @@
|
|||
# Deployment Options: Your Server vs Cloudflare Workers
|
||||
|
||||
You have two main options for deploying your MCP server. Here's a detailed comparison to help you choose.
|
||||
|
||||
## Option 1: Docker on Your Server (This Repo)
|
||||
|
||||
### What It Is
|
||||
- Run the MCP server as a Docker container on your own server
|
||||
- Sits alongside your other applications
|
||||
- Uses your existing SQLite database directly
|
||||
|
||||
### Pros ✅
|
||||
|
||||
1. **Dead Simple Setup**
|
||||
- Your database is already there
|
||||
- Just mount it as a volume
|
||||
- No data migration needed
|
||||
|
||||
2. **Zero Code Changes**
|
||||
- Use the exact Python code from Claude OS
|
||||
- No rewriting required
|
||||
- Full feature parity
|
||||
|
||||
3. **Fast Local Access**
|
||||
- Your other apps can access via `http://mcp-server:8051`
|
||||
- No network latency
|
||||
- Can use Docker networks
|
||||
|
||||
4. **Full Control**
|
||||
- Choose your own models (Ollama)
|
||||
- Adjust configuration freely
|
||||
- Access to all RAG features (hybrid, rerank, agentic)
|
||||
|
||||
5. **Cost**
|
||||
- Free (you're already paying for the server)
|
||||
- No additional services needed
|
||||
|
||||
6. **Debugging**
|
||||
- Direct access to logs
|
||||
- Can inspect database
|
||||
- Easy to troubleshoot
|
||||
|
||||
### Cons ❌
|
||||
|
||||
1. **Server Requirements**
|
||||
- Needs ~500MB disk space
|
||||
- ~200-500MB RAM
|
||||
- Must manage Docker containers
|
||||
|
||||
2. **Scaling**
|
||||
- Limited to your server's resources
|
||||
- Need load balancer for multiple instances
|
||||
|
||||
3. **Availability**
|
||||
- Single point of failure
|
||||
- Need to manage uptime yourself
|
||||
|
||||
### When to Choose This
|
||||
|
||||
✅ **Use Docker on your server if:**
|
||||
- Your apps are already on that server
|
||||
- You want the simplest setup
|
||||
- You have a single server or small deployment
|
||||
- You want to use your existing database directly
|
||||
- You need all RAG features (hybrid search, reranking)
|
||||
- You don't need global distribution
|
||||
|
||||
### Example Architecture
|
||||
|
||||
```
|
||||
┌──────────────────────────────────┐
|
||||
│ Your Server │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Chat App │───▶│MCP Server│ │
|
||||
│ └──────────┘ └─────┬────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────┐ │ │
|
||||
│ │ Docs App │─────────┤ │
|
||||
│ └──────────┘ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────┐ │
|
||||
│ │SQLite DB │ │
|
||||
│ └──────────┘ │
|
||||
│ │
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Option 2: Cloudflare Workers (Like cloudflare_mcp)
|
||||
|
||||
### What It Is
|
||||
- Serverless functions running on Cloudflare's edge network
|
||||
- JavaScript/TypeScript only
|
||||
- Uses Cloudflare D1 (SQLite) and Vectorize (vectors)
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Export your SQLite data to Cloudflare D1**
|
||||
```bash
|
||||
# Export schema and data
|
||||
sqlite3 data/claude-os.db .dump > dump.sql
|
||||
|
||||
# Import to D1
|
||||
wrangler d1 execute claude-os-db --file=dump.sql
|
||||
```
|
||||
|
||||
2. **Rewrite MCP endpoints in JavaScript**
|
||||
- Translate Python → TypeScript
|
||||
- Use Cloudflare APIs instead of SQLiteManager
|
||||
- Use Cloudflare AI for embeddings
|
||||
|
||||
3. **Deploy to Workers**
|
||||
```bash
|
||||
wrangler deploy
|
||||
```
|
||||
|
||||
### Pros ✅
|
||||
|
||||
1. **Global Distribution**
|
||||
- Runs in 300+ cities worldwide
|
||||
- Ultra-low latency for users anywhere
|
||||
- Automatic geo-routing
|
||||
|
||||
2. **Auto-Scaling**
|
||||
- Handles 0 to millions of requests
|
||||
- No capacity planning
|
||||
- Pay per use
|
||||
|
||||
3. **High Availability**
|
||||
- Built-in redundancy
|
||||
- 100% uptime SLA (on paid plans)
|
||||
- No maintenance windows
|
||||
|
||||
4. **No Server Management**
|
||||
- Completely serverless
|
||||
- Automatic updates
|
||||
- No Docker, no VMs
|
||||
|
||||
5. **Free Tier**
|
||||
- 100,000 requests/day free
|
||||
- 10GB D1 storage free
|
||||
- Good for most use cases
|
||||
|
||||
### Cons ❌
|
||||
|
||||
1. **Major Rewrite Required**
|
||||
- Must port Python → JavaScript/TypeScript
|
||||
- Reimplement all RAG logic
|
||||
- Could take days/weeks
|
||||
|
||||
2. **Cloudflare Limitations**
|
||||
- D1: 500MB database limit (free tier)
|
||||
- Workers: 10ms CPU time per request (free tier)
|
||||
- Must use Cloudflare AI models (different from Ollama)
|
||||
|
||||
3. **Data Migration**
|
||||
- Must export SQLite → D1
|
||||
- Must export vectors → Vectorize
|
||||
- Complex migration process
|
||||
|
||||
4. **Limited RAG Features**
|
||||
- Cloudflare AI has fewer models
|
||||
- Harder to implement advanced features
|
||||
- No LlamaIndex integration
|
||||
|
||||
5. **Debugging**
|
||||
- Harder to debug serverless
|
||||
- Limited logging (on free tier)
|
||||
- Can't inspect database easily
|
||||
|
||||
6. **Vendor Lock-in**
|
||||
- Tied to Cloudflare ecosystem
|
||||
- Migration away is difficult
|
||||
|
||||
### When to Choose This
|
||||
|
||||
✅ **Use Cloudflare Workers if:**
|
||||
- Your apps are distributed globally
|
||||
- You need extreme scalability
|
||||
- You want zero server management
|
||||
- You're comfortable rewriting in JavaScript
|
||||
- Your database is < 500MB (or you can split it)
|
||||
- You don't need advanced RAG features
|
||||
|
||||
### Example Architecture
|
||||
|
||||
```
|
||||
Global Users
|
||||
│
|
||||
▼
|
||||
┌───────────────────────┐
|
||||
│ Cloudflare Edge │
|
||||
│ (300+ locations) │
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ MCP Worker │ │
|
||||
│ │ (TypeScript) │ │
|
||||
│ └────┬────────────┘ │
|
||||
│ │ │
|
||||
│ ├──▶ D1 (SQLite)│
|
||||
│ └──▶ Vectorize │
|
||||
│ │
|
||||
└───────────────────────┘
|
||||
▲
|
||||
│
|
||||
Your Chat/Docs Apps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Side-by-Side Comparison
|
||||
|
||||
| Feature | Your Server (Docker) | Cloudflare Workers |
|
||||
|---------|---------------------|-------------------|
|
||||
| **Setup Time** | 5 minutes | Days (rewrite code) |
|
||||
| **Code Changes** | None | Full rewrite |
|
||||
| **Data Migration** | None | Complex |
|
||||
| **Server Management** | Yes | No |
|
||||
| **Scaling** | Manual | Automatic |
|
||||
| **Global Latency** | Higher | Ultra-low |
|
||||
| **Cost (small)** | $0 (existing server) | $0 (free tier) |
|
||||
| **Cost (large)** | Fixed | Pay-per-use |
|
||||
| **Database Size** | Unlimited | 500MB (free tier) |
|
||||
| **RAG Features** | Full | Limited |
|
||||
| **Model Choice** | Any (Ollama) | Cloudflare AI only |
|
||||
| **Debugging** | Easy | Harder |
|
||||
| **Vendor Lock-in** | None | Cloudflare |
|
||||
|
||||
---
|
||||
|
||||
## Recommended Approach
|
||||
|
||||
### For Your Use Case
|
||||
|
||||
Based on what you described:
|
||||
- ✅ You have MCPs populated with data locally
|
||||
- ✅ You have other apps that need this data
|
||||
- ✅ One needs it as a knowledge base
|
||||
- ✅ Another needs it for live chat
|
||||
|
||||
**I recommend: Start with Docker on your server (Option 1)**
|
||||
|
||||
### Why?
|
||||
|
||||
1. **Fastest to deploy** - 5 minutes vs days
|
||||
2. **No code changes** - Use existing Python code
|
||||
3. **No data migration** - Mount your existing DB
|
||||
4. **Full features** - All RAG capabilities work
|
||||
5. **Same server** - Your apps can access locally
|
||||
|
||||
### Migration Path (If Needed Later)
|
||||
|
||||
```
|
||||
Phase 1: Docker on your server (NOW)
|
||||
↓
|
||||
Get your apps working
|
||||
Test performance
|
||||
Measure traffic
|
||||
↓
|
||||
Phase 2: Evaluate needs (LATER)
|
||||
↓
|
||||
If scaling is needed → Consider Cloudflare
|
||||
If working well → Stay with Docker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start: Docker on Your Server
|
||||
|
||||
```bash
|
||||
# 1. Navigate to deployment directory
|
||||
cd deployment/mcp-only/
|
||||
|
||||
# 2. Run setup script
|
||||
./setup.sh
|
||||
|
||||
# 3. Done! MCP server is running at http://localhost:8051
|
||||
|
||||
# 4. Update your apps to use:
|
||||
# http://mcp-server:8051 (if on same Docker network)
|
||||
# http://localhost:8051 (if on same server)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hybrid Approach (Best of Both Worlds)
|
||||
|
||||
You can also do both:
|
||||
|
||||
```
|
||||
┌────────────────────────────────┐
|
||||
│ Your Server (Primary) │
|
||||
│ ┌────────┐ ┌────────────┐ │
|
||||
│ │Chat App│───▶│MCP (Docker)│ │
|
||||
│ └────────┘ └────────────┘ │
|
||||
│ ┌────────┐ ▲ │
|
||||
│ │Docs App│──────────┘ │
|
||||
│ └────────┘ │
|
||||
└────────────────────────────────┘
|
||||
│
|
||||
│ (Optional: Sync to Cloudflare)
|
||||
▼
|
||||
┌────────────────────────────────┐
|
||||
│ Cloudflare (Secondary) │
|
||||
│ ┌──────────────────────────┐ │
|
||||
│ │ Read-only MCP (Workers) │ │
|
||||
│ │ For public docs search │ │
|
||||
│ └──────────────────────────┘ │
|
||||
└────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Use Docker for:**
|
||||
- Internal apps (chat, docs)
|
||||
- Write operations
|
||||
- Full RAG features
|
||||
|
||||
**Use Cloudflare for:**
|
||||
- Public-facing documentation search
|
||||
- Global users
|
||||
- Read-only queries
|
||||
|
||||
---
|
||||
|
||||
## Example: Cloudflare Workers Code (If You Choose That Path)
|
||||
|
||||
See the `cloudflare-workers/` directory for a basic example of how to implement the MCP endpoints on Cloudflare Workers.
|
||||
|
||||
**Note:** This is a simplified example. A full implementation would require:
|
||||
- Complete D1 database migration
|
||||
- Vector embeddings in Vectorize
|
||||
- All 12 MCP tools reimplemented
|
||||
- Proper error handling and auth
|
||||
- Testing and deployment
|
||||
|
||||
---
|
||||
|
||||
## Decision Flowchart
|
||||
|
||||
```
|
||||
Do you need global distribution?
|
||||
├─ No → Use Docker on your server ✅
|
||||
│
|
||||
└─ Yes
|
||||
│
|
||||
Are your apps < 100k requests/day?
|
||||
├─ Yes
|
||||
│ │
|
||||
│ Is your database < 500MB?
|
||||
│ ├─ Yes → Consider Cloudflare Workers
|
||||
│ └─ No → Use Docker on your server ✅
|
||||
│
|
||||
└─ No → Use Docker + CDN or multiple regions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## My Recommendation
|
||||
|
||||
**Start with Docker on your server.** It's:
|
||||
- ✅ 10x faster to set up
|
||||
- ✅ No code changes
|
||||
- ✅ Uses your existing data
|
||||
- ✅ Full feature set
|
||||
- ✅ Easy to debug
|
||||
|
||||
Then, **if you need global scaling**, consider:
|
||||
1. Adding a CDN in front of your Docker deployment
|
||||
2. Deploying Docker containers in multiple regions
|
||||
3. Only then consider Cloudflare Workers (requires significant effort)
|
||||
|
||||
Most applications don't need Cloudflare-level scaling. Your Docker deployment will likely handle thousands of requests per second, which is probably more than enough.
|
||||
|
||||
---
|
||||
|
||||
## Questions?
|
||||
|
||||
See:
|
||||
- `README.md` - Full Docker setup guide
|
||||
- `examples/` - Integration examples
|
||||
- `cloudflare-workers/` - Cloudflare Workers starter (if you choose that path)
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
FROM python:3.11-slim as builder
|
||||
|
||||
# Install system dependencies for building
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc \
|
||||
g++ \
|
||||
make \
|
||||
libmagic1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create virtual environment
|
||||
RUN python -m venv /opt/venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# Copy requirements and install Python dependencies
|
||||
COPY requirements.txt /tmp/
|
||||
RUN pip install --no-cache-dir -r /tmp/requirements.txt
|
||||
|
||||
# Final stage
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libmagic1 \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy virtual environment from builder
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy only necessary application files
|
||||
COPY mcp_server/ /app/mcp_server/
|
||||
COPY app/core/ /app/app/core/
|
||||
COPY app/db/ /app/app/db/
|
||||
COPY app/utils/ /app/app/utils/
|
||||
COPY app/__init__.py /app/app/__init__.py
|
||||
|
||||
# Create data directory for database
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
# Expose MCP server port
|
||||
EXPOSE 8051
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8051/health || exit 1
|
||||
|
||||
# Run MCP server
|
||||
CMD ["python", "-m", "uvicorn", "mcp_server.server:app", "--host", "0.0.0.0", "--port", "8051"]
|
||||
|
|
@ -1,460 +0,0 @@
|
|||
# Claude OS - MCP-Only Deployment
|
||||
|
||||
This deployment package extracts **just the MCP knowledge base server** from Claude OS, allowing you to expose your populated knowledge bases to other applications without running the full coding environment.
|
||||
|
||||
## 🎯 What This Does
|
||||
|
||||
- ✅ Exposes MCP HTTP endpoints for knowledge base queries
|
||||
- ✅ Serves your existing SQLite database (read-only by default)
|
||||
- ✅ Provides RAG (Retrieval Augmented Generation) search
|
||||
- ✅ Lightweight: No CLI, no coding tools, no UI
|
||||
- ✅ Perfect for: Knowledge base APIs, live chat backends, documentation search
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Your Other Apps│
|
||||
│ (Chat, Docs) │
|
||||
└────────┬────────┘
|
||||
│ HTTP/JSON-RPC
|
||||
↓
|
||||
┌─────────────────┐
|
||||
│ MCP Server │
|
||||
│ (Port 8051) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
├──→ SQLite DB (your local data/claude-os.db)
|
||||
└──→ Ollama (embeddings + LLM)
|
||||
```
|
||||
|
||||
## 📦 What's Included
|
||||
|
||||
- **MCP Server**: HTTP endpoints for all knowledge base operations
|
||||
- **SQLite Manager**: Database access layer
|
||||
- **RAG Engine**: Semantic search with hybrid modes
|
||||
- **Ollama**: Local AI for embeddings and LLM (required)
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Option 1: Using Your Existing Local Database (Recommended)
|
||||
|
||||
This is the easiest approach - just mount your existing `data/claude-os.db`:
|
||||
|
||||
```bash
|
||||
# From the deployment/mcp-only directory
|
||||
docker-compose up -d
|
||||
|
||||
# This will:
|
||||
# 1. Start Ollama container
|
||||
# 2. Start MCP server with your DB mounted at ../../data (read-only)
|
||||
# 3. Pull required models on first run
|
||||
```
|
||||
|
||||
Your MCP server will be available at: `http://localhost:8051`
|
||||
|
||||
### Option 2: Copy Database to Server
|
||||
|
||||
If you're deploying to a remote server:
|
||||
|
||||
```bash
|
||||
# 1. Copy your local database to the server
|
||||
scp ../../data/claude-os.db user@server:/path/to/claude-os/data/
|
||||
|
||||
# 2. On the server, start services
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Option 3: Volume-Based (For Persistent Changes)
|
||||
|
||||
If you want the Docker container to have its own copy:
|
||||
|
||||
```bash
|
||||
# 1. Edit docker-compose.yml - uncomment the mcp_data volume
|
||||
# 2. Copy your database into the volume
|
||||
docker-compose up -d
|
||||
docker cp ../../data/claude-os.db claude-os-mcp:/app/data/
|
||||
```
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Edit `docker-compose.yml` to configure:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
# Database (required)
|
||||
SQLITE_DB_PATH: /app/data/claude-os.db
|
||||
|
||||
# Ollama (required)
|
||||
OLLAMA_HOST: http://ollama:11434
|
||||
OLLAMA_EMBED_MODEL: nomic-embed-text
|
||||
OLLAMA_MODEL: llama3.1:latest
|
||||
|
||||
# CORS - Important for your other apps!
|
||||
CORS_ORIGINS: "https://your-chat-app.com,https://your-docs-app.com"
|
||||
|
||||
# Optional: Authentication
|
||||
JWT_SECRET_KEY: your-secret-key-here
|
||||
API_USERNAME: admin
|
||||
API_PASSWORD: changeme
|
||||
```
|
||||
|
||||
### First-Time Setup: Pull Ollama Models
|
||||
|
||||
On first run, you need to download the models:
|
||||
|
||||
```bash
|
||||
# Wait for containers to start
|
||||
docker-compose up -d
|
||||
|
||||
# Pull embedding model (required)
|
||||
docker exec claude-os-ollama ollama pull nomic-embed-text
|
||||
|
||||
# Pull LLM model (optional, for agentic queries)
|
||||
docker exec claude-os-ollama ollama pull llama3.1:latest
|
||||
```
|
||||
|
||||
## 🔌 Using the MCP Endpoints
|
||||
|
||||
### Health Check
|
||||
|
||||
```bash
|
||||
curl http://localhost:8051/health
|
||||
```
|
||||
|
||||
### List All Knowledge Bases
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8051/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "list_knowledge_bases",
|
||||
"arguments": {}
|
||||
},
|
||||
"id": 1
|
||||
}'
|
||||
```
|
||||
|
||||
### Search Knowledge Base
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8051/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "search_knowledge_base",
|
||||
"arguments": {
|
||||
"kb_name": "my-docs",
|
||||
"query": "How do I configure authentication?",
|
||||
"top_k": 10
|
||||
}
|
||||
},
|
||||
"id": 1
|
||||
}'
|
||||
```
|
||||
|
||||
### KB-Specific Endpoint (Cleaner URLs)
|
||||
|
||||
```bash
|
||||
# Search a specific KB directly
|
||||
curl -X POST http://localhost:8051/mcp/kb/my-docs \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "search",
|
||||
"arguments": {
|
||||
"query": "authentication setup",
|
||||
"top_k": 5
|
||||
}
|
||||
},
|
||||
"id": 1
|
||||
}'
|
||||
```
|
||||
|
||||
## 🔗 Integration Examples
|
||||
|
||||
### Python Client
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
def search_knowledge(kb_name: str, query: str, top_k: int = 10):
|
||||
response = requests.post(
|
||||
"http://localhost:8051/mcp",
|
||||
json={
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "search_knowledge_base",
|
||||
"arguments": {
|
||||
"kb_name": kb_name,
|
||||
"query": query,
|
||||
"top_k": top_k
|
||||
}
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
)
|
||||
return response.json()["result"]
|
||||
|
||||
# Use in your chat app
|
||||
results = search_knowledge("my-docs", "How to deploy?")
|
||||
print(results)
|
||||
```
|
||||
|
||||
### JavaScript/Node.js Client
|
||||
|
||||
```javascript
|
||||
async function searchKnowledge(kbName, query, topK = 10) {
|
||||
const response = await fetch('http://localhost:8051/mcp', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'search_knowledge_base',
|
||||
arguments: { kb_name: kbName, query, top_k: topK }
|
||||
},
|
||||
id: 1
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return data.result;
|
||||
}
|
||||
|
||||
// Use in your app
|
||||
const results = await searchKnowledge('my-docs', 'How to deploy?');
|
||||
console.log(results);
|
||||
```
|
||||
|
||||
## 📊 Available MCP Tools
|
||||
|
||||
### Global Endpoint (`/mcp`)
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `search_knowledge_base` | Semantic search across any KB |
|
||||
| `list_knowledge_bases` | List all available KBs |
|
||||
| `list_knowledge_bases_by_type` | Filter KBs by type |
|
||||
| `create_knowledge_base` | Create new KB (if writable) |
|
||||
| `get_kb_stats` | Get KB statistics |
|
||||
| `list_documents` | List documents in KB |
|
||||
| `ingest_agent_os_profile` | Import Agent OS data |
|
||||
| `get_agent_os_stats` | Agent OS statistics |
|
||||
| `get_standards` | Retrieve standards |
|
||||
| `get_workflows` | Retrieve workflows |
|
||||
| `get_specs` | Retrieve specifications |
|
||||
| `get_product_context` | Get product context |
|
||||
|
||||
### KB-Specific Endpoint (`/mcp/kb/{kb_slug}`)
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `search` | Search within specific KB |
|
||||
| `get_stats` | Get stats for specific KB |
|
||||
| `list_documents` | List documents in KB |
|
||||
|
||||
## 🔒 Security Considerations
|
||||
|
||||
### 1. Read-Only Database (Default)
|
||||
|
||||
The default `docker-compose.yml` mounts your database as **read-only** (`:ro` flag). This prevents the container from modifying your local data.
|
||||
|
||||
To allow writes (e.g., for creating new KBs), change:
|
||||
```yaml
|
||||
volumes:
|
||||
- ../../data:/app/data:ro # Read-only
|
||||
```
|
||||
to:
|
||||
```yaml
|
||||
volumes:
|
||||
- ../../data:/app/data # Read-write
|
||||
```
|
||||
|
||||
### 2. Enable Authentication
|
||||
|
||||
Uncomment and set these in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
JWT_SECRET_KEY: "your-strong-secret-key-here"
|
||||
API_USERNAME: "admin"
|
||||
API_PASSWORD: "secure-password-here"
|
||||
```
|
||||
|
||||
Then authenticate:
|
||||
|
||||
```bash
|
||||
# Get token
|
||||
curl -X POST http://localhost:8051/token \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "username=admin&password=secure-password-here"
|
||||
|
||||
# Use token in requests
|
||||
curl -X POST http://localhost:8051/mcp \
|
||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '...'
|
||||
```
|
||||
|
||||
### 3. CORS Configuration
|
||||
|
||||
Restrict `CORS_ORIGINS` to your specific domains:
|
||||
|
||||
```yaml
|
||||
CORS_ORIGINS: "https://chat.yourapp.com,https://docs.yourapp.com"
|
||||
```
|
||||
|
||||
### 4. Network Isolation
|
||||
|
||||
If deploying with your other apps, use Docker networks:
|
||||
|
||||
```yaml
|
||||
networks:
|
||||
- your_app_network
|
||||
```
|
||||
|
||||
## 🌐 Cloudflare Workers Deployment
|
||||
|
||||
For a serverless approach similar to [cloudflare_mcp](https://github.com/forayconsulting/cloudflare_mcp):
|
||||
|
||||
### What You'd Need
|
||||
|
||||
1. **Export your SQLite DB to Cloudflare D1** (their SQLite service)
|
||||
2. **Rewrite MCP endpoints as Workers** (JavaScript/TypeScript)
|
||||
3. **Use Cloudflare AI** instead of Ollama (for embeddings)
|
||||
|
||||
### Pros of Cloudflare Approach
|
||||
- ✅ Globally distributed
|
||||
- ✅ Auto-scaling
|
||||
- ✅ No server management
|
||||
|
||||
### Cons
|
||||
- ❌ Must rewrite Python → JavaScript
|
||||
- ❌ Cloudflare AI models (less flexibility)
|
||||
- ❌ D1 has size limits (500MB free tier)
|
||||
- ❌ More complex to set up initially
|
||||
|
||||
**This Docker approach** is simpler if you just need to expose your existing data to your apps on a server you control.
|
||||
|
||||
## 📈 Monitoring
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# MCP Server logs
|
||||
docker-compose logs -f mcp-server
|
||||
|
||||
# Ollama logs
|
||||
docker-compose logs -f ollama
|
||||
```
|
||||
|
||||
### Check Health
|
||||
|
||||
```bash
|
||||
# MCP Server
|
||||
curl http://localhost:8051/health
|
||||
|
||||
# Ollama
|
||||
curl http://localhost:11434/api/tags
|
||||
```
|
||||
|
||||
## 🛠️ Troubleshooting
|
||||
|
||||
### Models Not Found
|
||||
|
||||
```bash
|
||||
# Pull models manually
|
||||
docker exec claude-os-ollama ollama pull nomic-embed-text
|
||||
docker exec claude-os-ollama ollama pull llama3.1:latest
|
||||
```
|
||||
|
||||
### Database Not Found
|
||||
|
||||
```bash
|
||||
# Verify database path
|
||||
docker exec claude-os-mcp ls -la /app/data/
|
||||
|
||||
# Check if mounted correctly
|
||||
docker inspect claude-os-mcp | grep -A 10 Mounts
|
||||
```
|
||||
|
||||
### CORS Errors
|
||||
|
||||
Update `CORS_ORIGINS` in `docker-compose.yml` to include your app's domain.
|
||||
|
||||
### Port Conflicts
|
||||
|
||||
If port 8051 or 11434 are in use, change them:
|
||||
|
||||
```yaml
|
||||
ports:
|
||||
- "9051:8051" # Use port 9051 externally
|
||||
```
|
||||
|
||||
## 🔄 Updating Your Database
|
||||
|
||||
### While Container is Running
|
||||
|
||||
Your database is mounted from `../../data/claude-os.db`. Any changes you make locally (using Claude OS) will be immediately visible to the container.
|
||||
|
||||
```bash
|
||||
# No restart needed - changes are live!
|
||||
# Just query the MCP endpoint and you'll see new data
|
||||
```
|
||||
|
||||
### Deploying to Production
|
||||
|
||||
1. **Option A: Sync database periodically**
|
||||
```bash
|
||||
# Cron job to sync from local to server
|
||||
rsync -avz data/claude-os.db user@server:/path/to/data/
|
||||
```
|
||||
|
||||
2. **Option B: Use shared network storage**
|
||||
- Mount same NFS/S3 volume on both local and server
|
||||
|
||||
3. **Option C: Use database replication**
|
||||
- Set up SQLite replication (more complex)
|
||||
|
||||
## 📚 Next Steps
|
||||
|
||||
1. **Deploy this to your server** alongside your other apps
|
||||
2. **Update CORS** to allow your chat/docs apps
|
||||
3. **Enable authentication** for production
|
||||
4. **Configure monitoring** (logs, health checks)
|
||||
5. **Set up database sync** if needed
|
||||
|
||||
## 🆚 Comparison: This vs. Full Claude OS
|
||||
|
||||
| Feature | MCP-Only | Full Claude OS |
|
||||
|---------|----------|----------------|
|
||||
| Knowledge Base API | ✅ | ✅ |
|
||||
| CLI Coding Assistant | ❌ | ✅ |
|
||||
| Frontend UI | ❌ | ✅ |
|
||||
| File Watchers | ❌ | ✅ |
|
||||
| Spec Tracking | ❌ | ✅ |
|
||||
| Project Management | ❌ | ✅ |
|
||||
| Container Size | ~500MB | ~2GB |
|
||||
| Memory Usage | ~200MB | ~1GB |
|
||||
|
||||
## 📞 Support
|
||||
|
||||
For issues specific to:
|
||||
- **MCP Endpoints**: See `/docs/API_REFERENCE.md` in main repo
|
||||
- **Deployment**: Open issue in claude-os repo
|
||||
- **Your Apps Integration**: This is the right approach!
|
||||
|
||||
## 🎉 You're All Set!
|
||||
|
||||
Your knowledge bases are now available as an API for your other applications to consume. No coding environment needed - just pure data access!
|
||||
|
|
@ -1,328 +0,0 @@
|
|||
# Cloudflare Workers Deployment (Alternative Approach)
|
||||
|
||||
This directory contains a **starter template** for deploying Claude OS MCP endpoints on Cloudflare Workers, similar to the [cloudflare_mcp](https://github.com/forayconsulting/cloudflare_mcp) project.
|
||||
|
||||
> ⚠️ **Important**: This is a **significant undertaking** compared to the Docker approach. Only use this if you need global distribution and auto-scaling.
|
||||
|
||||
## What This Requires
|
||||
|
||||
### 1. Cloudflare Account Setup
|
||||
- Cloudflare account (free tier available)
|
||||
- D1 database (Cloudflare's SQLite service)
|
||||
- Vectorize (Cloudflare's vector search service)
|
||||
- Workers AI (for embeddings)
|
||||
|
||||
### 2. Code Rewrite
|
||||
- Rewrite all Python code → JavaScript/TypeScript
|
||||
- Reimplement SQLiteManager → D1 queries
|
||||
- Reimplement RAGEngine → Vectorize queries
|
||||
- Port all 12 MCP tools
|
||||
|
||||
### 3. Data Migration
|
||||
- Export SQLite schema and data
|
||||
- Import to D1
|
||||
- Extract vectors and import to Vectorize
|
||||
- Set up sync process (if needed)
|
||||
|
||||
## Estimated Effort
|
||||
|
||||
| Task | Time Estimate |
|
||||
|------|--------------|
|
||||
| Setup Cloudflare infrastructure | 2-4 hours |
|
||||
| Migrate database schema | 1-2 hours |
|
||||
| Migrate data and vectors | 2-4 hours |
|
||||
| Rewrite MCP endpoints | 8-16 hours |
|
||||
| Testing and debugging | 4-8 hours |
|
||||
| **Total** | **17-34 hours** |
|
||||
|
||||
Compare to Docker approach: **5 minutes**
|
||||
|
||||
## When to Use This
|
||||
|
||||
✅ Only use Cloudflare Workers if:
|
||||
- You have **global users** across continents
|
||||
- You need **automatic scaling** beyond a single server
|
||||
- You're okay with **vendor lock-in** to Cloudflare
|
||||
- You have **time to rewrite** the codebase in JavaScript
|
||||
- Your database is **< 500MB** (free tier limit)
|
||||
|
||||
Otherwise, use the Docker approach.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Install Wrangler (Cloudflare CLI)
|
||||
|
||||
```bash
|
||||
npm install -g wrangler
|
||||
|
||||
# Login to Cloudflare
|
||||
wrangler login
|
||||
```
|
||||
|
||||
### 2. Create D1 Database
|
||||
|
||||
```bash
|
||||
# Create database
|
||||
wrangler d1 create claude-os-db
|
||||
|
||||
# This will output a database ID - save it!
|
||||
```
|
||||
|
||||
### 3. Export Your SQLite Database
|
||||
|
||||
```bash
|
||||
# From the root of claude-os repo
|
||||
sqlite3 data/claude-os.db .dump > deployment/mcp-only/cloudflare-workers/schema.sql
|
||||
```
|
||||
|
||||
### 4. Import to D1
|
||||
|
||||
```bash
|
||||
cd deployment/mcp-only/cloudflare-workers
|
||||
|
||||
# Import schema and data
|
||||
wrangler d1 execute claude-os-db --file=schema.sql
|
||||
```
|
||||
|
||||
### 5. Create Vectorize Index
|
||||
|
||||
```bash
|
||||
# Create vector index (for embeddings)
|
||||
wrangler vectorize create claude-os-vectors --dimensions=768 --metric=cosine
|
||||
|
||||
# Note: You'll need to export and import your vectors separately
|
||||
```
|
||||
|
||||
### 6. Configure wrangler.toml
|
||||
|
||||
Edit `wrangler.toml` with your database and vectorize IDs:
|
||||
|
||||
```toml
|
||||
name = "claude-os-mcp"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2024-01-01"
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
database_name = "claude-os-db"
|
||||
database_id = "your-database-id-here"
|
||||
|
||||
[[vectorize]]
|
||||
binding = "VECTORIZE"
|
||||
index_name = "claude-os-vectors"
|
||||
index_id = "your-vectorize-id-here"
|
||||
|
||||
[ai]
|
||||
binding = "AI"
|
||||
```
|
||||
|
||||
### 7. Implement MCP Endpoints
|
||||
|
||||
See `src/index.ts` for a basic example. You'll need to implement all tools:
|
||||
|
||||
```typescript
|
||||
// Example structure
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { pathname } = new URL(request.url);
|
||||
|
||||
// Health check
|
||||
if (pathname === '/health') {
|
||||
return new Response(JSON.stringify({ status: 'ok' }));
|
||||
}
|
||||
|
||||
// MCP endpoint
|
||||
if (pathname === '/mcp') {
|
||||
const body = await request.json();
|
||||
const result = await handleMCPRequest(body, env);
|
||||
return new Response(JSON.stringify(result));
|
||||
}
|
||||
|
||||
return new Response('Not found', { status: 404 });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 8. Deploy
|
||||
|
||||
```bash
|
||||
wrangler deploy
|
||||
```
|
||||
|
||||
Your MCP server will be available at: `https://claude-os-mcp.your-subdomain.workers.dev`
|
||||
|
||||
## Code Structure
|
||||
|
||||
```
|
||||
cloudflare-workers/
|
||||
├── src/
|
||||
│ ├── index.ts # Main worker entry point
|
||||
│ ├── handlers/
|
||||
│ │ ├── search.ts # Search knowledge base
|
||||
│ │ ├── list.ts # List KBs
|
||||
│ │ └── ... # Other tools
|
||||
│ ├── db/
|
||||
│ │ ├── queries.ts # D1 query helpers
|
||||
│ │ └── schema.ts # Type definitions
|
||||
│ └── utils/
|
||||
│ ├── embeddings.ts # Workers AI embeddings
|
||||
│ └── vectorize.ts # Vectorize queries
|
||||
├── wrangler.toml # Cloudflare config
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
└── schema.sql # Exported database
|
||||
```
|
||||
|
||||
## Key Differences from Python Version
|
||||
|
||||
### Database Access
|
||||
|
||||
**Python (SQLiteManager):**
|
||||
```python
|
||||
def query_documents(kb_name, query_embedding, n_results):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT ... FROM documents WHERE ...")
|
||||
return cursor.fetchall()
|
||||
```
|
||||
|
||||
**TypeScript (D1):**
|
||||
```typescript
|
||||
async function queryDocuments(env: Env, kbName: string, n: number) {
|
||||
const results = await env.DB.prepare(
|
||||
"SELECT ... FROM documents WHERE ..."
|
||||
).all();
|
||||
return results;
|
||||
}
|
||||
```
|
||||
|
||||
### Vector Search
|
||||
|
||||
**Python (sqlite-vec):**
|
||||
```python
|
||||
cursor.execute("""
|
||||
SELECT *, vec_distance_cosine(embedding, ?) as distance
|
||||
FROM documents
|
||||
ORDER BY distance
|
||||
LIMIT ?
|
||||
""", [query_embedding, n_results])
|
||||
```
|
||||
|
||||
**TypeScript (Vectorize):**
|
||||
```typescript
|
||||
const matches = await env.VECTORIZE.query(queryEmbedding, {
|
||||
topK: n,
|
||||
returnMetadata: true
|
||||
});
|
||||
```
|
||||
|
||||
### Embeddings
|
||||
|
||||
**Python (Ollama):**
|
||||
```python
|
||||
from llama_index.embeddings.ollama import OllamaEmbedding
|
||||
|
||||
embedding_model = OllamaEmbedding(
|
||||
model_name="nomic-embed-text",
|
||||
base_url="http://localhost:11434"
|
||||
)
|
||||
embedding = embedding_model.get_query_embedding(text)
|
||||
```
|
||||
|
||||
**TypeScript (Workers AI):**
|
||||
```typescript
|
||||
const embedding = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
|
||||
text: queryText
|
||||
});
|
||||
```
|
||||
|
||||
## Migration Script Example
|
||||
|
||||
Here's a script to export your vectors for Vectorize:
|
||||
|
||||
```python
|
||||
# export_vectors.py
|
||||
import sqlite3
|
||||
import json
|
||||
|
||||
conn = sqlite3.connect('../../data/claude-os.db')
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("""
|
||||
SELECT id, embedding, metadata
|
||||
FROM documents
|
||||
""")
|
||||
|
||||
vectors = []
|
||||
for doc_id, embedding, metadata in cursor.fetchall():
|
||||
vectors.append({
|
||||
'id': str(doc_id),
|
||||
'values': list(embedding), # Convert BLOB to list
|
||||
'metadata': json.loads(metadata)
|
||||
})
|
||||
|
||||
with open('vectors.json', 'w') as f:
|
||||
json.dump(vectors, f)
|
||||
```
|
||||
|
||||
Then import to Vectorize:
|
||||
```bash
|
||||
# You'll need to use the Vectorize API
|
||||
# See: https://developers.cloudflare.com/vectorize/
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
### Free Tier
|
||||
- D1: 5 GB storage, 5M rows read/day, 100K writes/day
|
||||
- Vectorize: 30M queries/month, 10M dimensions stored
|
||||
- Workers: 100K requests/day, 10ms CPU per request
|
||||
|
||||
### Paid Tier Required For
|
||||
- Large databases (> 500MB)
|
||||
- High query volume (> 100K/day)
|
||||
- Long-running queries (> 10ms CPU)
|
||||
|
||||
## Testing Locally
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Run local dev server
|
||||
wrangler dev
|
||||
|
||||
# Test endpoint
|
||||
curl http://localhost:8787/health
|
||||
```
|
||||
|
||||
## Comparison with Docker Approach
|
||||
|
||||
| Feature | Docker | Cloudflare Workers |
|
||||
|---------|--------|-------------------|
|
||||
| Setup time | 5 min | 17-34 hours |
|
||||
| Code changes | None | Complete rewrite |
|
||||
| Database | Direct use | Must migrate |
|
||||
| Scaling | Manual | Automatic |
|
||||
| Cost (small) | $0 | $0 |
|
||||
| Cost (large) | Fixed | Variable |
|
||||
| Latency | Regional | Global |
|
||||
|
||||
## My Recommendation
|
||||
|
||||
**Unless you have specific requirements for global distribution, use the Docker approach.**
|
||||
|
||||
The Cloudflare Workers approach is powerful but requires significant development effort. It's best suited for:
|
||||
- Public-facing APIs with global users
|
||||
- Applications that need to scale to millions of requests
|
||||
- Teams with JavaScript/TypeScript expertise
|
||||
|
||||
For most use cases, Docker on your server is simpler, faster, and more maintainable.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Cloudflare Workers Docs](https://developers.cloudflare.com/workers/)
|
||||
- [D1 Database Docs](https://developers.cloudflare.com/d1/)
|
||||
- [Vectorize Docs](https://developers.cloudflare.com/vectorize/)
|
||||
- [Workers AI Docs](https://developers.cloudflare.com/workers-ai/)
|
||||
- [Example: cloudflare_mcp](https://github.com/forayconsulting/cloudflare_mcp)
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"name": "claude-os-mcp-cloudflare",
|
||||
"version": "1.0.0",
|
||||
"description": "Claude OS MCP Server on Cloudflare Workers",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"dev": "wrangler dev",
|
||||
"deploy": "wrangler deploy",
|
||||
"tail": "wrangler tail",
|
||||
"d1:create": "wrangler d1 create claude-os-db",
|
||||
"d1:import": "wrangler d1 execute claude-os-db --file=schema.sql",
|
||||
"vectorize:create": "wrangler vectorize create claude-os-vectors --dimensions=768 --metric=cosine"
|
||||
},
|
||||
"keywords": [
|
||||
"mcp",
|
||||
"knowledge-base",
|
||||
"rag",
|
||||
"cloudflare",
|
||||
"workers"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20240129.0",
|
||||
"typescript": "^5.3.3",
|
||||
"wrangler": "^3.22.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,303 +0,0 @@
|
|||
/**
|
||||
* Claude OS MCP Server - Cloudflare Workers Implementation
|
||||
*
|
||||
* This is a BASIC EXAMPLE showing how to implement MCP endpoints
|
||||
* on Cloudflare Workers. A production implementation would require
|
||||
* all 12 tools and proper error handling.
|
||||
*/
|
||||
|
||||
export interface Env {
|
||||
DB: D1Database; // D1 database binding
|
||||
VECTORIZE: VectorizeIndex; // Vectorize binding
|
||||
AI: any; // Workers AI binding
|
||||
}
|
||||
|
||||
interface MCPRequest {
|
||||
jsonrpc: string;
|
||||
method: string;
|
||||
params?: {
|
||||
name?: string;
|
||||
arguments?: any;
|
||||
};
|
||||
id: number;
|
||||
}
|
||||
|
||||
interface MCPResponse {
|
||||
jsonrpc: string;
|
||||
result?: any;
|
||||
error?: {
|
||||
code: number;
|
||||
message: string;
|
||||
};
|
||||
id: number;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
|
||||
// CORS headers
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
};
|
||||
|
||||
// Handle preflight
|
||||
if (request.method === 'OPTIONS') {
|
||||
return new Response(null, { headers: corsHeaders });
|
||||
}
|
||||
|
||||
// Health check
|
||||
if (url.pathname === '/health') {
|
||||
return new Response(
|
||||
JSON.stringify({ status: 'ok', version: '1.0.0' }),
|
||||
{
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// MCP endpoint
|
||||
if (url.pathname === '/mcp' && request.method === 'POST') {
|
||||
try {
|
||||
const body: MCPRequest = await request.json();
|
||||
const response = await handleMCPRequest(body, env);
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
});
|
||||
} catch (error) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: -32603,
|
||||
message: error instanceof Error ? error.message : 'Internal error'
|
||||
},
|
||||
id: null
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response('Not found', { status: 404, headers: corsHeaders });
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle MCP JSON-RPC requests
|
||||
*/
|
||||
async function handleMCPRequest(request: MCPRequest, env: Env): Promise<MCPResponse> {
|
||||
const { method, params, id } = request;
|
||||
|
||||
// Initialize method
|
||||
if (method === 'initialize') {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
result: {
|
||||
protocolVersion: '2024-11-05',
|
||||
serverInfo: {
|
||||
name: 'claude-os-mcp',
|
||||
version: '1.0.0'
|
||||
},
|
||||
capabilities: {
|
||||
tools: {}
|
||||
}
|
||||
},
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
// List available tools
|
||||
if (method === 'tools/list') {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
result: {
|
||||
tools: [
|
||||
{
|
||||
name: 'search_knowledge_base',
|
||||
description: 'Search a knowledge base for relevant information',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
kb_name: { type: 'string' },
|
||||
query: { type: 'string' },
|
||||
top_k: { type: 'number' }
|
||||
},
|
||||
required: ['kb_name', 'query']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'list_knowledge_bases',
|
||||
description: 'List all available knowledge bases',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
// TODO: Add remaining 10 tools
|
||||
]
|
||||
},
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
// Call a tool
|
||||
if (method === 'tools/call') {
|
||||
const toolName = params?.name;
|
||||
const args = params?.arguments || {};
|
||||
|
||||
let result;
|
||||
|
||||
switch (toolName) {
|
||||
case 'search_knowledge_base':
|
||||
result = await searchKnowledgeBase(env, args);
|
||||
break;
|
||||
|
||||
case 'list_knowledge_bases':
|
||||
result = await listKnowledgeBases(env);
|
||||
break;
|
||||
|
||||
// TODO: Implement remaining tools:
|
||||
// - list_knowledge_bases_by_type
|
||||
// - create_knowledge_base
|
||||
// - get_kb_stats
|
||||
// - list_documents
|
||||
// - ingest_agent_os_profile
|
||||
// - get_agent_os_stats
|
||||
// - get_standards
|
||||
// - get_workflows
|
||||
// - get_specs
|
||||
// - get_product_context
|
||||
|
||||
default:
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: -32601,
|
||||
message: `Unknown tool: ${toolName}`
|
||||
},
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
result,
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: -32601,
|
||||
message: `Unknown method: ${method}`
|
||||
},
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Search knowledge base using vector similarity
|
||||
*/
|
||||
async function searchKnowledgeBase(
|
||||
env: Env,
|
||||
args: { kb_name: string; query: string; top_k?: number }
|
||||
): Promise<any> {
|
||||
const { kb_name, query, top_k = 10 } = args;
|
||||
|
||||
// 1. Get KB ID
|
||||
const kb = await env.DB.prepare(
|
||||
'SELECT id, slug FROM knowledge_bases WHERE name = ? OR slug = ?'
|
||||
).bind(kb_name, kb_name).first();
|
||||
|
||||
if (!kb) {
|
||||
throw new Error(`Knowledge base not found: ${kb_name}`);
|
||||
}
|
||||
|
||||
// 2. Generate query embedding using Workers AI
|
||||
const embeddingResponse: any = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
|
||||
text: query
|
||||
});
|
||||
|
||||
const queryEmbedding = embeddingResponse.data[0];
|
||||
|
||||
// 3. Search vectors using Vectorize
|
||||
const matches = await env.VECTORIZE.query(queryEmbedding, {
|
||||
topK: top_k,
|
||||
returnMetadata: true,
|
||||
filter: { kb_id: kb.id } // Filter by knowledge base
|
||||
});
|
||||
|
||||
// 4. Fetch full document content from D1
|
||||
const documentIds = matches.matches.map(m => m.id);
|
||||
|
||||
if (documentIds.length === 0) {
|
||||
return {
|
||||
results: [],
|
||||
kb_name,
|
||||
query,
|
||||
count: 0
|
||||
};
|
||||
}
|
||||
|
||||
const placeholders = documentIds.map(() => '?').join(',');
|
||||
const documents = await env.DB.prepare(
|
||||
`SELECT id, content, metadata FROM documents WHERE id IN (${placeholders})`
|
||||
).bind(...documentIds).all();
|
||||
|
||||
// 5. Combine results
|
||||
const results = matches.matches.map((match, idx) => {
|
||||
const doc = documents.results.find(d => d.id === match.id);
|
||||
|
||||
return {
|
||||
text: doc?.content || '',
|
||||
metadata: doc?.metadata ? JSON.parse(doc.metadata) : {},
|
||||
score: match.score,
|
||||
distance: 1 - match.score
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
results,
|
||||
kb_name,
|
||||
query,
|
||||
count: results.length,
|
||||
search_mode: 'vector'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* List all knowledge bases
|
||||
*/
|
||||
async function listKnowledgeBases(env: Env): Promise<any> {
|
||||
const result = await env.DB.prepare(
|
||||
'SELECT id, name, slug, kb_type, description, created_at FROM knowledge_bases ORDER BY created_at DESC'
|
||||
).all();
|
||||
|
||||
return {
|
||||
knowledge_bases: result.results.map(kb => ({
|
||||
id: kb.id,
|
||||
name: kb.name,
|
||||
slug: kb.slug,
|
||||
kb_type: kb.kb_type,
|
||||
description: kb.description,
|
||||
created_at: kb.created_at
|
||||
})),
|
||||
count: result.results.length
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional helper functions would go here:
|
||||
* - getKBStats()
|
||||
* - listDocuments()
|
||||
* - createKnowledgeBase()
|
||||
* - etc.
|
||||
*/
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2021",
|
||||
"lib": ["ES2021"],
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "node",
|
||||
"types": ["@cloudflare/workers-types"],
|
||||
"resolveJsonModule": true,
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"jsx": "react",
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
name = "claude-os-mcp"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2024-01-01"
|
||||
|
||||
# D1 Database (SQLite)
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
database_name = "claude-os-db"
|
||||
database_id = "your-database-id-here" # Get this from: wrangler d1 create claude-os-db
|
||||
|
||||
# Vectorize (Vector Search)
|
||||
[[vectorize]]
|
||||
binding = "VECTORIZE"
|
||||
index_name = "claude-os-vectors"
|
||||
index_id = "your-vectorize-id-here" # Get this from: wrangler vectorize create
|
||||
|
||||
# Workers AI (Embeddings & LLM)
|
||||
[ai]
|
||||
binding = "AI"
|
||||
|
||||
# Environment variables
|
||||
[vars]
|
||||
MCP_VERSION = "1.0.0"
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
# Ollama service (required for embeddings & LLM)
|
||||
ollama:
|
||||
image: ollama/ollama:latest
|
||||
container_name: claude-os-ollama
|
||||
ports:
|
||||
- "11434:11434"
|
||||
volumes:
|
||||
- ollama_data:/root/.ollama
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
|
||||
# MCP Server (knowledge base access only)
|
||||
mcp-server:
|
||||
build:
|
||||
context: ../.. # Build from root of claude-os
|
||||
dockerfile: deployment/mcp-only/Dockerfile
|
||||
container_name: claude-os-mcp
|
||||
ports:
|
||||
- "8051:8051"
|
||||
volumes:
|
||||
# Mount your existing database from local machine
|
||||
- ../../data:/app/data:ro # Read-only: your populated DB
|
||||
# Or use a volume if you want to persist changes
|
||||
# - mcp_data:/app/data
|
||||
environment:
|
||||
# Database
|
||||
SQLITE_DB_PATH: /app/data/claude-os.db
|
||||
|
||||
# MCP Server
|
||||
MCP_SERVER_HOST: 0.0.0.0
|
||||
MCP_SERVER_PORT: 8051
|
||||
|
||||
# Ollama connection
|
||||
OLLAMA_HOST: http://ollama:11434
|
||||
OLLAMA_EMBED_MODEL: nomic-embed-text
|
||||
OLLAMA_MODEL: llama3.1:latest
|
||||
|
||||
# Optional: Authentication
|
||||
# JWT_SECRET_KEY: your-secret-key-here
|
||||
# JWT_ALGORITHM: HS256
|
||||
# API_USERNAME: admin
|
||||
# API_PASSWORD: changeme
|
||||
|
||||
# Optional: CORS (adjust for your other apps)
|
||||
CORS_ORIGINS: "*"
|
||||
|
||||
depends_on:
|
||||
ollama:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8051/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
ollama_data:
|
||||
driver: local
|
||||
# Uncomment if you want persistent MCP data in Docker
|
||||
# mcp_data:
|
||||
# driver: local
|
||||
|
|
@ -1,290 +0,0 @@
|
|||
/**
|
||||
* Example: Integrating Claude OS MCP with a Node.js/Express Chat Application
|
||||
*
|
||||
* This example shows how to use the MCP server as a knowledge base
|
||||
* backend for a chat API.
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const axios = require('axios');
|
||||
|
||||
class ClaudeOSKnowledgeBase {
|
||||
constructor(baseUrl = 'http://localhost:8051', apiToken = null) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.headers = { 'Content-Type': 'application/json' };
|
||||
|
||||
if (apiToken) {
|
||||
this.headers['Authorization'] = `Bearer ${apiToken}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSON-RPC call to the MCP server
|
||||
*/
|
||||
async callTool(toolName, arguments, kbSlug = null) {
|
||||
// Use KB-specific endpoint if slug provided
|
||||
let url = `${this.baseUrl}/mcp`;
|
||||
if (kbSlug) {
|
||||
url = `${this.baseUrl}/mcp/kb/${kbSlug}`;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: toolName,
|
||||
arguments: arguments
|
||||
},
|
||||
id: 1
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await axios.post(url, payload, {
|
||||
headers: this.headers
|
||||
});
|
||||
|
||||
if (response.data.error) {
|
||||
throw new Error(`MCP Error: ${JSON.stringify(response.data.error)}`);
|
||||
}
|
||||
|
||||
return response.data.result || {};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to call MCP: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search a knowledge base for relevant information
|
||||
*/
|
||||
async search(kbName, query, options = {}) {
|
||||
const {
|
||||
topK = 10,
|
||||
useHybrid = false,
|
||||
useRerank = false
|
||||
} = options;
|
||||
|
||||
const result = await this.callTool('search_knowledge_base', {
|
||||
kb_name: kbName,
|
||||
query: query,
|
||||
top_k: topK,
|
||||
use_hybrid: useHybrid,
|
||||
use_rerank: useRerank
|
||||
});
|
||||
|
||||
return result.results || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* List all available knowledge bases
|
||||
*/
|
||||
async listKnowledgeBases() {
|
||||
const result = await this.callTool('list_knowledge_bases', {});
|
||||
return result.knowledge_bases || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get statistics for a knowledge base
|
||||
*/
|
||||
async getKBStats(kbName) {
|
||||
return await this.callTool('get_kb_stats', { kb_name: kbName });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Express.js API for the chat application
|
||||
*/
|
||||
function createChatAPI() {
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
// Initialize MCP client
|
||||
const mcp = new ClaudeOSKnowledgeBase(
|
||||
process.env.MCP_BASE_URL || 'http://localhost:8051',
|
||||
process.env.MCP_API_TOKEN || null
|
||||
);
|
||||
|
||||
/**
|
||||
* POST /api/chat
|
||||
* Ask a question and get a knowledge-based response
|
||||
*/
|
||||
app.post('/api/chat', async (req, res) => {
|
||||
try {
|
||||
const { question, kb_name, options = {} } = req.body;
|
||||
|
||||
if (!question || !kb_name) {
|
||||
return res.status(400).json({
|
||||
error: 'Missing required fields: question and kb_name'
|
||||
});
|
||||
}
|
||||
|
||||
// Search the knowledge base
|
||||
const results = await mcp.search(kb_name, question, {
|
||||
topK: options.top_k || 5,
|
||||
useHybrid: options.use_hybrid !== false, // default true
|
||||
useRerank: options.use_rerank !== false // default true
|
||||
});
|
||||
|
||||
if (results.length === 0) {
|
||||
return res.json({
|
||||
answer: "I couldn't find any relevant information in the knowledge base.",
|
||||
sources: []
|
||||
});
|
||||
}
|
||||
|
||||
// Format the context
|
||||
const sources = results.map((result, idx) => ({
|
||||
id: idx + 1,
|
||||
text: result.text,
|
||||
filename: result.metadata?.filename || 'Unknown',
|
||||
score: result.score || 0
|
||||
}));
|
||||
|
||||
// In a real app, you'd call your LLM here with the context
|
||||
// For this example, just return the sources
|
||||
res.json({
|
||||
answer: `Found ${results.length} relevant results. (In production, this would be processed by an LLM to generate a natural language answer)`,
|
||||
sources: sources,
|
||||
question: question
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Chat error:', error);
|
||||
res.status(500).json({
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/knowledge-bases
|
||||
* List all available knowledge bases
|
||||
*/
|
||||
app.get('/api/knowledge-bases', async (req, res) => {
|
||||
try {
|
||||
const kbs = await mcp.listKnowledgeBases();
|
||||
|
||||
res.json({
|
||||
knowledge_bases: kbs.map(kb => ({
|
||||
name: kb.name,
|
||||
slug: kb.slug,
|
||||
type: kb.kb_type,
|
||||
description: kb.description
|
||||
}))
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error listing KBs:', error);
|
||||
res.status(500).json({
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/knowledge-bases/:name/stats
|
||||
* Get statistics for a specific knowledge base
|
||||
*/
|
||||
app.get('/api/knowledge-bases/:name/stats', async (req, res) => {
|
||||
try {
|
||||
const stats = await mcp.getKBStats(req.params.name);
|
||||
res.json(stats);
|
||||
} catch (error) {
|
||||
console.error('Error getting stats:', error);
|
||||
res.status(500).json({
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /health
|
||||
* Health check endpoint
|
||||
*/
|
||||
app.get('/health', async (req, res) => {
|
||||
try {
|
||||
// Try to list KBs to verify MCP connection
|
||||
await mcp.listKnowledgeBases();
|
||||
res.json({ status: 'ok', mcp_connected: true });
|
||||
} catch (error) {
|
||||
res.status(503).json({
|
||||
status: 'degraded',
|
||||
mcp_connected: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example usage with streaming responses (for real-time chat)
|
||||
*/
|
||||
async function streamingChatExample(kbName, question) {
|
||||
const mcp = new ClaudeOSKnowledgeBase('http://localhost:8051');
|
||||
|
||||
// 1. Search the knowledge base
|
||||
const results = await mcp.search(kbName, question, {
|
||||
topK: 5,
|
||||
useHybrid: true,
|
||||
useRerank: true
|
||||
});
|
||||
|
||||
// 2. Build context for LLM
|
||||
const context = results
|
||||
.map((r, idx) => `[${idx + 1}] ${r.text}`)
|
||||
.join('\n\n---\n\n');
|
||||
|
||||
console.log('Context for LLM:');
|
||||
console.log(context);
|
||||
|
||||
// 3. In a real app, stream the LLM response
|
||||
// using OpenAI SDK, Anthropic SDK, etc.
|
||||
/*
|
||||
const stream = await openai.chat.completions.create({
|
||||
model: 'gpt-4',
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `You are a helpful assistant. Answer the user's question based on the following context:\n\n${context}`
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: question
|
||||
}
|
||||
],
|
||||
stream: true
|
||||
});
|
||||
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.choices[0]?.delta?.content || '');
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Run the server if this file is executed directly
|
||||
if (require.main === module) {
|
||||
const app = createChatAPI();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Chat API server running on port ${PORT}`);
|
||||
console.log('');
|
||||
console.log('Endpoints:');
|
||||
console.log(` POST http://localhost:${PORT}/api/chat`);
|
||||
console.log(` GET http://localhost:${PORT}/api/knowledge-bases`);
|
||||
console.log(` GET http://localhost:${PORT}/api/knowledge-bases/:name/stats`);
|
||||
console.log(` GET http://localhost:${PORT}/health`);
|
||||
console.log('');
|
||||
console.log('Example request:');
|
||||
console.log(` curl -X POST http://localhost:${PORT}/api/chat \\`);
|
||||
console.log(` -H "Content-Type: application/json" \\`);
|
||||
console.log(` -d '{"question": "How do I configure auth?", "kb_name": "my-docs"}'`);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ClaudeOSKnowledgeBase,
|
||||
createChatAPI,
|
||||
streamingChatExample
|
||||
};
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
"""
|
||||
Example: Integrating Claude OS MCP with a Chat Application
|
||||
|
||||
This example shows how to use the MCP server as a knowledge base
|
||||
backend for a chat application.
|
||||
"""
|
||||
|
||||
import requests
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
|
||||
class ClaudeOSKnowledgeBase:
|
||||
"""Client for Claude OS MCP Server"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
base_url: str = "http://localhost:8051",
|
||||
api_token: Optional[str] = None
|
||||
):
|
||||
self.base_url = base_url
|
||||
self.headers = {"Content-Type": "application/json"}
|
||||
|
||||
if api_token:
|
||||
self.headers["Authorization"] = f"Bearer {api_token}"
|
||||
|
||||
def _call_tool(
|
||||
self,
|
||||
tool_name: str,
|
||||
arguments: Dict,
|
||||
kb_slug: Optional[str] = None
|
||||
) -> Dict:
|
||||
"""Make a JSON-RPC call to the MCP server"""
|
||||
|
||||
# Use KB-specific endpoint if slug provided
|
||||
url = f"{self.base_url}/mcp"
|
||||
if kb_slug:
|
||||
url = f"{self.base_url}/mcp/kb/{kb_slug}"
|
||||
|
||||
payload = {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": tool_name,
|
||||
"arguments": arguments
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
|
||||
response = requests.post(url, json=payload, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
if "error" in result:
|
||||
raise Exception(f"MCP Error: {result['error']}")
|
||||
|
||||
return result.get("result", {})
|
||||
|
||||
def search(
|
||||
self,
|
||||
kb_name: str,
|
||||
query: str,
|
||||
top_k: int = 10,
|
||||
use_hybrid: bool = False,
|
||||
use_rerank: bool = False
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
Search a knowledge base for relevant information
|
||||
|
||||
Args:
|
||||
kb_name: Name of the knowledge base to search
|
||||
query: User's question or search query
|
||||
top_k: Number of results to return
|
||||
use_hybrid: Enable hybrid search (vector + keyword)
|
||||
use_rerank: Enable reranking for better results
|
||||
|
||||
Returns:
|
||||
List of relevant document chunks with metadata
|
||||
"""
|
||||
result = self._call_tool(
|
||||
"search_knowledge_base",
|
||||
{
|
||||
"kb_name": kb_name,
|
||||
"query": query,
|
||||
"top_k": top_k,
|
||||
"use_hybrid": use_hybrid,
|
||||
"use_rerank": use_rerank
|
||||
}
|
||||
)
|
||||
|
||||
# Parse the results
|
||||
if isinstance(result, dict) and "results" in result:
|
||||
return result["results"]
|
||||
return []
|
||||
|
||||
def list_knowledge_bases(self) -> List[Dict]:
|
||||
"""List all available knowledge bases"""
|
||||
result = self._call_tool("list_knowledge_bases", {})
|
||||
return result.get("knowledge_bases", [])
|
||||
|
||||
def get_kb_stats(self, kb_name: str) -> Dict:
|
||||
"""Get statistics for a knowledge base"""
|
||||
result = self._call_tool("get_kb_stats", {"kb_name": kb_name})
|
||||
return result
|
||||
|
||||
|
||||
class ChatWithKnowledge:
|
||||
"""
|
||||
Example chat application that uses Claude OS knowledge bases
|
||||
"""
|
||||
|
||||
def __init__(self, mcp_client: ClaudeOSKnowledgeBase):
|
||||
self.mcp = mcp_client
|
||||
|
||||
def answer_question(
|
||||
self,
|
||||
question: str,
|
||||
kb_name: str,
|
||||
use_hybrid: bool = True,
|
||||
use_rerank: bool = True
|
||||
) -> str:
|
||||
"""
|
||||
Answer a user question using the knowledge base
|
||||
|
||||
This is a simple example - in a real app, you'd:
|
||||
1. Search the knowledge base
|
||||
2. Pass results + question to your LLM
|
||||
3. Generate a response grounded in the retrieved docs
|
||||
"""
|
||||
|
||||
# 1. Search the knowledge base
|
||||
results = self.mcp.search(
|
||||
kb_name=kb_name,
|
||||
query=question,
|
||||
top_k=5,
|
||||
use_hybrid=use_hybrid,
|
||||
use_rerank=use_rerank
|
||||
)
|
||||
|
||||
if not results:
|
||||
return "I couldn't find any relevant information in the knowledge base."
|
||||
|
||||
# 2. Format the context from search results
|
||||
context_parts = []
|
||||
for idx, result in enumerate(results, 1):
|
||||
text = result.get("text", "")
|
||||
metadata = result.get("metadata", {})
|
||||
filename = metadata.get("filename", "Unknown")
|
||||
score = result.get("score", 0)
|
||||
|
||||
context_parts.append(
|
||||
f"[{idx}] {filename} (relevance: {score:.2f})\n{text}\n"
|
||||
)
|
||||
|
||||
context = "\n---\n".join(context_parts)
|
||||
|
||||
# 3. In a real app, you'd call your LLM here
|
||||
# For this example, just return the context
|
||||
response = f"""Based on the knowledge base, here's what I found:
|
||||
|
||||
{context}
|
||||
|
||||
Question: {question}
|
||||
|
||||
(In a production app, this would be processed by an LLM to generate a natural language answer)
|
||||
"""
|
||||
|
||||
return response
|
||||
|
||||
def list_available_topics(self) -> str:
|
||||
"""List all available knowledge bases"""
|
||||
kbs = self.mcp.list_knowledge_bases()
|
||||
|
||||
if not kbs:
|
||||
return "No knowledge bases available."
|
||||
|
||||
response = "Available topics:\n\n"
|
||||
for kb in kbs:
|
||||
name = kb.get("name", "Unknown")
|
||||
description = kb.get("description", "No description")
|
||||
kb_type = kb.get("kb_type", "generic")
|
||||
|
||||
response += f"- **{name}** ({kb_type})\n {description}\n\n"
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
# Initialize the MCP client
|
||||
mcp = ClaudeOSKnowledgeBase(
|
||||
base_url="http://localhost:8051",
|
||||
# api_token="your-jwt-token-here" # Uncomment if auth enabled
|
||||
)
|
||||
|
||||
# Create chat interface
|
||||
chat = ChatWithKnowledge(mcp)
|
||||
|
||||
# Example 1: List available knowledge bases
|
||||
print("=" * 60)
|
||||
print("AVAILABLE KNOWLEDGE BASES")
|
||||
print("=" * 60)
|
||||
print(chat.list_available_topics())
|
||||
print()
|
||||
|
||||
# Example 2: Ask a question
|
||||
print("=" * 60)
|
||||
print("ASKING A QUESTION")
|
||||
print("=" * 60)
|
||||
|
||||
question = "How do I configure authentication?"
|
||||
kb_name = "my-docs" # Change to your actual KB name
|
||||
|
||||
print(f"Question: {question}")
|
||||
print(f"Knowledge Base: {kb_name}")
|
||||
print()
|
||||
|
||||
try:
|
||||
answer = chat.answer_question(
|
||||
question=question,
|
||||
kb_name=kb_name,
|
||||
use_hybrid=True,
|
||||
use_rerank=True
|
||||
)
|
||||
print(answer)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
# Minimal requirements for MCP server only (no coding tools)
|
||||
|
||||
# Web Framework
|
||||
fastapi>=0.104.0
|
||||
uvicorn[standard]>=0.24.0
|
||||
python-multipart>=0.0.6
|
||||
|
||||
# CORS & Rate Limiting
|
||||
slowapi>=0.1.9
|
||||
|
||||
# Database & Vector Search
|
||||
sqlite-vec>=0.1.0
|
||||
|
||||
# RAG Framework
|
||||
llama-index-core>=0.10.0
|
||||
llama-index-embeddings-ollama>=0.1.0
|
||||
llama-index-llms-ollama>=0.1.0
|
||||
|
||||
# Reranking & Search
|
||||
sentence-transformers>=2.2.0
|
||||
rank-bm25>=0.2.2
|
||||
|
||||
# Document Processing
|
||||
PyMuPDF>=1.23.0
|
||||
pypdf>=3.17.0
|
||||
python-magic>=0.4.27
|
||||
|
||||
# Code Parsing
|
||||
tree-sitter>=0.20.0
|
||||
tree-sitter-python>=0.20.0
|
||||
tree-sitter-javascript>=0.20.0
|
||||
tree-sitter-typescript>=0.20.0
|
||||
|
||||
# Utilities
|
||||
python-dotenv>=1.0.0
|
||||
pydantic>=2.0.0
|
||||
pydantic-settings>=2.0.0
|
||||
|
||||
# Authentication (optional)
|
||||
python-jose[cryptography]>=3.3.0
|
||||
passlib[bcrypt]>=1.7.4
|
||||
|
||||
# Logging
|
||||
python-json-logger>=2.0.7
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Claude OS MCP-Only Setup Script
|
||||
# This script helps with first-time deployment
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Claude OS MCP-Only Deployment Setup"
|
||||
echo "======================================="
|
||||
echo ""
|
||||
|
||||
# Check if Docker is installed
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo "❌ Docker is not installed. Please install Docker first."
|
||||
echo " Visit: https://docs.docker.com/get-docker/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if docker-compose is installed
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
echo "❌ docker-compose is not installed. Please install docker-compose first."
|
||||
echo " Visit: https://docs.docker.com/compose/install/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Docker and docker-compose are installed"
|
||||
echo ""
|
||||
|
||||
# Check if database exists
|
||||
DB_PATH="../../data/claude-os.db"
|
||||
if [ ! -f "$DB_PATH" ]; then
|
||||
echo "⚠️ Database not found at: $DB_PATH"
|
||||
echo ""
|
||||
echo " This deployment expects your existing Claude OS database."
|
||||
echo " Please ensure Claude OS is set up locally first, or"
|
||||
echo " adjust the volume mount in docker-compose.yml"
|
||||
echo ""
|
||||
read -p " Continue anyway? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "✅ Database found at: $DB_PATH"
|
||||
# Get database size
|
||||
DB_SIZE=$(du -h "$DB_PATH" | cut -f1)
|
||||
echo " Size: $DB_SIZE"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Create .env from example if it doesn't exist
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "📝 Creating .env from .env.example..."
|
||||
cp .env.example .env
|
||||
echo "✅ Created .env file"
|
||||
echo " Please review and update .env with your settings"
|
||||
echo ""
|
||||
else
|
||||
echo "✅ .env file already exists"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Prompt for CORS origins
|
||||
echo "🔧 Configuration"
|
||||
echo "---------------"
|
||||
read -p "Enter CORS origins (comma-separated, or * for all) [*]: " CORS_INPUT
|
||||
CORS_INPUT=${CORS_INPUT:-*}
|
||||
|
||||
# Update .env
|
||||
sed -i.bak "s|CORS_ORIGINS=.*|CORS_ORIGINS=$CORS_INPUT|" .env && rm .env.bak
|
||||
echo "✅ CORS_ORIGINS set to: $CORS_INPUT"
|
||||
echo ""
|
||||
|
||||
# Ask about authentication
|
||||
read -p "Enable authentication? (y/n) [n]: " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
# Generate a random secret
|
||||
JWT_SECRET=$(openssl rand -hex 32 2>/dev/null || echo "CHANGE-THIS-SECRET-$(date +%s)")
|
||||
|
||||
read -p "Enter API username [admin]: " API_USER
|
||||
API_USER=${API_USER:-admin}
|
||||
|
||||
read -s -p "Enter API password: " API_PASS
|
||||
echo
|
||||
|
||||
if [ -z "$API_PASS" ]; then
|
||||
echo "❌ Password cannot be empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Uncomment and set auth variables in .env
|
||||
sed -i.bak "s|# JWT_SECRET_KEY=.*|JWT_SECRET_KEY=$JWT_SECRET|" .env && rm .env.bak
|
||||
sed -i.bak "s|# JWT_ALGORITHM=.*|JWT_ALGORITHM=HS256|" .env && rm .env.bak
|
||||
sed -i.bak "s|# API_USERNAME=.*|API_USERNAME=$API_USER|" .env && rm .env.bak
|
||||
sed -i.bak "s|# API_PASSWORD=.*|API_PASSWORD=$API_PASS|" .env && rm .env.bak
|
||||
|
||||
echo "✅ Authentication enabled"
|
||||
else
|
||||
echo "⚠️ Authentication disabled (not recommended for production)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Start services
|
||||
echo "🐳 Starting Docker containers..."
|
||||
echo "-------------------------------"
|
||||
docker-compose up -d
|
||||
|
||||
echo ""
|
||||
echo "⏳ Waiting for services to start..."
|
||||
sleep 5
|
||||
|
||||
# Wait for Ollama to be ready
|
||||
echo "Checking Ollama status..."
|
||||
OLLAMA_READY=0
|
||||
for i in {1..30}; do
|
||||
if docker exec claude-os-ollama ollama list &>/dev/null; then
|
||||
OLLAMA_READY=1
|
||||
break
|
||||
fi
|
||||
echo " Waiting for Ollama... ($i/30)"
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ $OLLAMA_READY -eq 0 ]; then
|
||||
echo "⚠️ Ollama is taking longer than expected to start"
|
||||
echo " You can check logs with: docker-compose logs ollama"
|
||||
else
|
||||
echo "✅ Ollama is ready"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Pull required models
|
||||
echo "📥 Pulling required Ollama models..."
|
||||
echo "-----------------------------------"
|
||||
echo "This may take a few minutes on first run..."
|
||||
echo ""
|
||||
|
||||
echo "Pulling nomic-embed-text (embeddings)..."
|
||||
docker exec claude-os-ollama ollama pull nomic-embed-text
|
||||
|
||||
echo "Pulling llama3.1:latest (LLM)..."
|
||||
docker exec claude-os-ollama ollama pull llama3.1:latest
|
||||
|
||||
echo ""
|
||||
echo "✅ Models downloaded successfully"
|
||||
echo ""
|
||||
|
||||
# Check MCP server health
|
||||
echo "🏥 Checking MCP server health..."
|
||||
sleep 3
|
||||
|
||||
if curl -f http://localhost:8051/health &>/dev/null; then
|
||||
echo "✅ MCP server is healthy!"
|
||||
else
|
||||
echo "⚠️ MCP server is not responding yet"
|
||||
echo " Check logs with: docker-compose logs mcp-server"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "✨ Setup Complete!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "Your MCP server is now running at:"
|
||||
echo " http://localhost:8051"
|
||||
echo ""
|
||||
echo "Test it with:"
|
||||
echo " curl http://localhost:8051/health"
|
||||
echo ""
|
||||
echo "View logs:"
|
||||
echo " docker-compose logs -f mcp-server"
|
||||
echo ""
|
||||
echo "Stop services:"
|
||||
echo " docker-compose down"
|
||||
echo ""
|
||||
echo "See README.md for usage examples and API documentation"
|
||||
echo ""
|
||||
Loading…
Add table
Reference in a new issue