diff --git a/README.md b/README.md index f285e95..2e18448 100644 --- a/README.md +++ b/README.md @@ -646,48 +646,7 @@ curl http://localhost:8051/api/projects/1/kanban --- -## πŸ—οΈ Architecture - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Claude Code CLI β”‚ -β”‚ (with Claude OS integration) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ MCP HTTP -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ MCP Server (Port 8051) β”‚ -β”‚ FastAPI Backend β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ β”‚ -β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ RAG Engine β”‚ β”‚ React UI β”‚ -β”‚ (llama-index) β”‚ β”‚ (Port 5173) β”‚ -β”‚ β€’ Vector Search β”‚ β”‚ β€’ Project Mgmt β”‚ -β”‚ β€’ Hybrid Search β”‚ β”‚ β€’ KB Browser β”‚ -β”‚ β€’ Reranking β”‚ β”‚ β€’ Doc Upload β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ -β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ SQLite + sqlite-vec (Local Database) β”‚ -β”‚ β€’ projects β”‚ -β”‚ β€’ knowledge_bases β”‚ -β”‚ β€’ documents (with embeddings) β”‚ -β”‚ β€’ Single-file database β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - └─────────────────────┐ - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” - β”‚ Ollama β”‚ - β”‚ (Port 11434) β”‚ - β”‚ β€’ llama3.1 β”‚ - β”‚ β€’ Embeddings β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -### Template System +## πŸ“‚ Template System ``` claude-os/ diff --git a/deployment/mcp-only/.env.example b/deployment/mcp-only/.env.example new file mode 100644 index 0000000..2ba1c50 --- /dev/null +++ b/deployment/mcp-only/.env.example @@ -0,0 +1,48 @@ +# 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 diff --git a/deployment/mcp-only/DEPLOYMENT_COMPARISON.md b/deployment/mcp-only/DEPLOYMENT_COMPARISON.md new file mode 100644 index 0000000..454face --- /dev/null +++ b/deployment/mcp-only/DEPLOYMENT_COMPARISON.md @@ -0,0 +1,382 @@ +# 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) diff --git a/deployment/mcp-only/Dockerfile b/deployment/mcp-only/Dockerfile new file mode 100644 index 0000000..16105cf --- /dev/null +++ b/deployment/mcp-only/Dockerfile @@ -0,0 +1,53 @@ +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"] diff --git a/deployment/mcp-only/README.md b/deployment/mcp-only/README.md new file mode 100644 index 0000000..f1047e2 --- /dev/null +++ b/deployment/mcp-only/README.md @@ -0,0 +1,460 @@ +# 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! diff --git a/deployment/mcp-only/cloudflare-workers/README.md b/deployment/mcp-only/cloudflare-workers/README.md new file mode 100644 index 0000000..ff19fe9 --- /dev/null +++ b/deployment/mcp-only/cloudflare-workers/README.md @@ -0,0 +1,328 @@ +# 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 { + 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) diff --git a/deployment/mcp-only/cloudflare-workers/package.json b/deployment/mcp-only/cloudflare-workers/package.json new file mode 100644 index 0000000..30502c5 --- /dev/null +++ b/deployment/mcp-only/cloudflare-workers/package.json @@ -0,0 +1,28 @@ +{ + "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" + } +} diff --git a/deployment/mcp-only/cloudflare-workers/src/index.ts b/deployment/mcp-only/cloudflare-workers/src/index.ts new file mode 100644 index 0000000..3c5a9dd --- /dev/null +++ b/deployment/mcp-only/cloudflare-workers/src/index.ts @@ -0,0 +1,303 @@ +/** + * 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 { + 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 { + 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 { + 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 { + 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. + */ diff --git a/deployment/mcp-only/cloudflare-workers/tsconfig.json b/deployment/mcp-only/cloudflare-workers/tsconfig.json new file mode 100644 index 0000000..9070052 --- /dev/null +++ b/deployment/mcp-only/cloudflare-workers/tsconfig.json @@ -0,0 +1,23 @@ +{ + "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"] +} diff --git a/deployment/mcp-only/cloudflare-workers/wrangler.toml b/deployment/mcp-only/cloudflare-workers/wrangler.toml new file mode 100644 index 0000000..d99894a --- /dev/null +++ b/deployment/mcp-only/cloudflare-workers/wrangler.toml @@ -0,0 +1,23 @@ +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" diff --git a/deployment/mcp-only/docker-compose.yml b/deployment/mcp-only/docker-compose.yml new file mode 100644 index 0000000..7bb991a --- /dev/null +++ b/deployment/mcp-only/docker-compose.yml @@ -0,0 +1,69 @@ +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 diff --git a/deployment/mcp-only/examples/chat-integration.js b/deployment/mcp-only/examples/chat-integration.js new file mode 100644 index 0000000..16a1aa5 --- /dev/null +++ b/deployment/mcp-only/examples/chat-integration.js @@ -0,0 +1,290 @@ +/** + * 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 +}; diff --git a/deployment/mcp-only/examples/chat-integration.py b/deployment/mcp-only/examples/chat-integration.py new file mode 100644 index 0000000..7ea1c20 --- /dev/null +++ b/deployment/mcp-only/examples/chat-integration.py @@ -0,0 +1,226 @@ +""" +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}") diff --git a/deployment/mcp-only/requirements.txt b/deployment/mcp-only/requirements.txt new file mode 100644 index 0000000..a21e8a6 --- /dev/null +++ b/deployment/mcp-only/requirements.txt @@ -0,0 +1,44 @@ +# 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 diff --git a/deployment/mcp-only/setup.sh b/deployment/mcp-only/setup.sh new file mode 100755 index 0000000..a7e4766 --- /dev/null +++ b/deployment/mcp-only/setup.sh @@ -0,0 +1,178 @@ +#!/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 ""