├── cli ├── __init__.py └── commands.py ├── core ├── __init__.py └── calendar.py ├── rag ├── __init__.py └── embeddings.py ├── scraper └── __init__.py ├── data_analysis └── __init__.py ├── api └── __init__.py ├── mcp_gateway ├── __init__.py ├── claude_config.json ├── test_server.py ├── client_config.json └── README.md ├── app ├── favicon.ico ├── globals.css ├── layout.tsx ├── JRVS APP.tsx └── components │ └── AISchedulingPlatform.tsx ├── data ├── jarvis.db ├── faiss_index.map └── faiss_index.index ├── __init__.py ├── postcss.config.mjs ├── public ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── next.config.ts ├── llm └── __init__.py ├── .dockerignore ├── .claude └── settings.local.json ├── requirements.txt ├── jrvs-web.service ├── eslint.config.mjs ├── docker-compose.yml ├── Dockerfile ├── start-api.sh ├── start_jrvs.sh ├── package.json ├── tsconfig.json ├── issues └── 1.md ├── start_web_server.sh ├── .gitignore ├── mcp ├── config.json ├── shutdown.py ├── logging_config.py └── exceptions.py ├── push_to_github.sh ├── add_event.py ├── docs ├── START_HERE.md ├── TAILSCALE_QUICKSTART.md ├── COMMANDS_FIXED.md ├── WHATS_NEW.md ├── JARCORE_QUICKSTART.md ├── MCP_QUICKSTART.md ├── BRAVE_SEARCH_SETUP.md ├── MCP_CLIENT_GUIDE.md ├── SETUP_COMPLETE.md ├── README_FRONTEND.md ├── QUICKSTART.md ├── NEW_WEB_UI.md ├── SECURITY_HARDENING_SUMMARY.md └── JARCORE_UPDATE.md ├── tests ├── test_mcp_client.py ├── test_coding_agent.py ├── test_calendar_api.py └── test_lmstudio_client.py ├── Dockerfile.mcp ├── scripts └── start_mcp_enhanced.sh ├── lib ├── firebase.ts ├── firebase-utils.ts └── jarvis-api.ts ├── config.py ├── docker-compose.mcp.yml ├── setup_windows.bat ├── ENTERPRISE.md └── setup_mac.sh /cli/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rag/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scraper/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data_analysis/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- 1 | """API module for Jarvis AI Agent""" 2 | -------------------------------------------------------------------------------- /mcp_gateway/__init__.py: -------------------------------------------------------------------------------- 1 | """MCP (Model Context Protocol) server for JRVS""" 2 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xthebuilder/JRVS/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /data/jarvis.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xthebuilder/JRVS/HEAD/data/jarvis.db -------------------------------------------------------------------------------- /data/faiss_index.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xthebuilder/JRVS/HEAD/data/faiss_index.map -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | """Jarvis AI Agent - Advanced RAG-powered CLI assistant""" 2 | __version__ = "1.0.0" -------------------------------------------------------------------------------- /data/faiss_index.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xthebuilder/JRVS/HEAD/data/faiss_index.index -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | 7 | export default config; 8 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /llm/__init__.py: -------------------------------------------------------------------------------- 1 | from llm.ollama_client import OllamaClient, ollama_client 2 | from llm.lmstudio_client import LMStudioClient, lmstudio_client 3 | 4 | __all__ = ['OllamaClient', 'ollama_client', 'LMStudioClient', 'lmstudio_client'] 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | *.pyo 4 | *.pyd 5 | .Python 6 | *.so 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | .git 12 | .gitignore 13 | .dockerignore 14 | *.md 15 | data/faiss_index 16 | data/*.db 17 | .venv 18 | venv 19 | ENV 20 | -------------------------------------------------------------------------------- /mcp_gateway/claude_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "jrvs": { 4 | "command": "python", 5 | "args": [ 6 | "/home/xmanz/JRVS/mcp/server.py" 7 | ], 8 | "env": { 9 | "PYTHONPATH": "/home/xmanz/JRVS" 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.claude/settings.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "allow": [ 4 | "Bash(python:*)", 5 | "Bash(curl:*)", 6 | "Bash(cat:*)", 7 | "Bash(pip install:*)", 8 | "Bash(timeout:*)", 9 | "Bash(tree:*)", 10 | "Bash(python3:*)", 11 | "Bash(tailscale:*)", 12 | "Bash(chmod:*)" 13 | ], 14 | "deny": [], 15 | "ask": [] 16 | } 17 | } -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | rich>=13.0.0 2 | requests>=2.28.0 3 | beautifulsoup4>=4.11.0 4 | faiss-cpu>=1.7.0 5 | sentence-transformers>=2.2.0 6 | aiohttp>=3.8.0 7 | aiosqlite>=0.19.0 8 | numpy>=1.24.0 9 | torch>=2.0.0 10 | transformers>=4.30.0 11 | psutil>=5.9.0 12 | fastapi>=0.104.0 13 | uvicorn>=0.24.0 14 | pydantic>=2.0.0 15 | websockets>=12.0 16 | mcp>=1.2.0 17 | httpx>=0.25.0 18 | anyio>=4.0.0 19 | slowapi>=0.1.9 -------------------------------------------------------------------------------- /jrvs-web.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=JRVS AI Agent Web Server (Tailscale Only) 3 | After=network.target tailscaled.service 4 | Wants=tailscaled.service 5 | 6 | [Service] 7 | Type=simple 8 | User=xmanz 9 | WorkingDirectory=/home/xmanz/JRVS 10 | Environment="PATH=/usr/bin:/usr/local/bin" 11 | ExecStart=/usr/bin/python3 /home/xmanz/JRVS/web_server.py 12 | Restart=on-failure 13 | RestartSec=10 14 | 15 | # Security 16 | PrivateTmp=yes 17 | NoNewPrivileges=true 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig, globalIgnores } from "eslint/config"; 2 | import nextVitals from "eslint-config-next/core-web-vitals"; 3 | import nextTs from "eslint-config-next/typescript"; 4 | 5 | const eslintConfig = defineConfig([ 6 | ...nextVitals, 7 | ...nextTs, 8 | // Override default ignores of eslint-config-next. 9 | globalIgnores([ 10 | // Default ignores of eslint-config-next: 11 | ".next/**", 12 | "out/**", 13 | "build/**", 14 | "next-env.d.ts", 15 | ]), 16 | ]); 17 | 18 | export default eslintConfig; 19 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | :root { 4 | --background: #ffffff; 5 | --foreground: #171717; 6 | } 7 | 8 | @theme inline { 9 | --color-background: var(--background); 10 | --color-foreground: var(--foreground); 11 | --font-sans: var(--font-geist-sans); 12 | --font-mono: var(--font-geist-mono); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | :root { 17 | --background: #0a0a0a; 18 | --foreground: #ededed; 19 | } 20 | } 21 | 22 | body { 23 | background: var(--background); 24 | color: var(--foreground); 25 | font-family: Arial, Helvetica, sans-serif; 26 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | jarvis: 5 | build: . 6 | container_name: jarvis_ai_agent 7 | stdin_open: true 8 | tty: true 9 | volumes: 10 | - ./data:/app/data 11 | network_mode: "host" 12 | environment: 13 | - OLLAMA_BASE_URL=http://localhost:11434 14 | depends_on: 15 | - ollama 16 | 17 | ollama: 18 | image: ollama/ollama:latest 19 | container_name: ollama 20 | ports: 21 | - "11434:11434" 22 | volumes: 23 | - ollama_data:/root/.ollama 24 | restart: unless-stopped 25 | 26 | volumes: 27 | ollama_data: 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim 2 | 3 | # Set working directory 4 | WORKDIR /app 5 | 6 | # Install system dependencies 7 | RUN apt-get update && apt-get install -y \ 8 | curl \ 9 | && rm -rf /var/lib/apt/lists/* 10 | 11 | # Copy requirements first for better caching 12 | COPY requirements.txt . 13 | 14 | # Install Python dependencies 15 | RUN pip install --no-cache-dir -r requirements.txt 16 | 17 | # Copy application code 18 | COPY . . 19 | 20 | # Create data directory 21 | RUN mkdir -p /app/data 22 | 23 | # Set environment variables 24 | ENV PYTHONUNBUFFERED=1 25 | 26 | # Run the application 27 | CMD ["python", "main.py"] 28 | -------------------------------------------------------------------------------- /start-api.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Start Jarvis API Server 3 | 4 | echo "🚀 Starting Jarvis API Server..." 5 | cd "$(dirname "$0")" 6 | 7 | # Check if Ollama is running 8 | if ! curl -s http://localhost:11434/api/tags > /dev/null 2>&1; then 9 | echo "❌ Ollama is not running!" 10 | echo "Start it with: ollama serve" 11 | exit 1 12 | fi 13 | 14 | # Install dependencies if needed 15 | if ! python -c "import fastapi" 2>/dev/null; then 16 | echo "📦 Installing API dependencies..." 17 | pip install fastapi uvicorn websockets pydantic 18 | fi 19 | 20 | # Start the API server 21 | echo "✅ Starting API on http://localhost:8000" 22 | python api/server.py 23 | -------------------------------------------------------------------------------- /start_jrvs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Quick start script for JRVS 3 | 4 | echo "🤖 Starting JRVS AI Agent..." 5 | echo "" 6 | 7 | # Check Ollama 8 | if ! systemctl is-active --quiet ollama && ! pgrep -f "ollama serve" > /dev/null; then 9 | echo "⚠️ Warning: Ollama doesn't appear to be running" 10 | echo " Start it with: ollama serve" 11 | echo "" 12 | fi 13 | 14 | # Check Node.js for MCP servers 15 | if ! command -v node &> /dev/null; then 16 | echo "⚠️ Warning: Node.js not found - MCP servers won't work" 17 | echo " Install from: https://nodejs.org/" 18 | echo "" 19 | fi 20 | 21 | # Start JRVS 22 | cd "$(dirname "$0")" 23 | python main.py "$@" 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jrvs", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "eslint" 10 | }, 11 | "dependencies": { 12 | "firebase": "^12.5.0", 13 | "lucide-react": "^0.553.0", 14 | "next": "16.0.1", 15 | "react": "^19.2.0", 16 | "react-dom": "19.2.0" 17 | }, 18 | "devDependencies": { 19 | "@tailwindcss/postcss": "^4", 20 | "@types/node": "^20", 21 | "@types/react": "^19", 22 | "@types/react-dom": "^19", 23 | "eslint": "^9", 24 | "eslint-config-next": "16.0.1", 25 | "tailwindcss": "^4", 26 | "typescript": "^5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "react-jsx", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": [ 26 | "next-env.d.ts", 27 | "**/*.ts", 28 | "**/*.tsx", 29 | ".next/types/**/*.ts", 30 | ".next/dev/types/**/*.ts", 31 | "**/*.mts" 32 | ], 33 | "exclude": ["node_modules"] 34 | } 35 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const geistSans = Geist({ 6 | variable: "--font-geist-sans", 7 | subsets: ["latin"], 8 | }); 9 | 10 | const geistMono = Geist_Mono({ 11 | variable: "--font-geist-mono", 12 | subsets: ["latin"], 13 | }); 14 | 15 | export const metadata: Metadata = { 16 | title: "Create Next App", 17 | description: "Generated by create next app", 18 | }; 19 | 20 | export default function RootLayout({ 21 | children, 22 | }: Readonly<{ 23 | children: React.ReactNode; 24 | }>) { 25 | return ( 26 | 27 | 30 | {children} 31 | 32 | 33 | ); 34 | } -------------------------------------------------------------------------------- /issues/1.md: -------------------------------------------------------------------------------- 1 | ### Title 2 | Migrate Tool Calling Protocol to UTCP 3 | 4 | ### Body 5 | The current tool calling implementation relies on the Model Context Protocol (MCP). This issue proposes migrating to the [Universal Tool Calling Protocol (UTCP)](https://github.com/universal-tool-calling-protocol) to enhance interoperability and standardization. 6 | 7 | **Tasks:** 8 | - [ ] Research UTCP specification and integration requirements. 9 | - [ ] Refactor `mcp/agent.py` and `mcp/client.py` to support UTCP. 10 | - [ ] Update configuration schemas in `mcp/client_config.json`. 11 | - [ ] Verify tool discovery and execution with the new protocol. 12 | 13 | --- 14 | 15 | **Note**: File paths updated from `mcp/` to `mcp_gateway/` due to namespace collision resolution. The directory was renamed to avoid conflicts with the pip-installed `mcp` package. Current tasks should reference `mcp_gateway/` paths. -------------------------------------------------------------------------------- /start_web_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Start JRVS Web Server on Tailscale network 3 | 4 | cd "$(dirname "$0")" 5 | 6 | # Check Tailscale 7 | if ! tailscale status &>/dev/null; then 8 | echo "❌ Error: Tailscale is not running" 9 | echo " Start it with: sudo systemctl start tailscaled" 10 | exit 1 11 | fi 12 | 13 | TAILSCALE_IP=$(tailscale ip -4) 14 | if [ -z "$TAILSCALE_IP" ]; then 15 | echo "❌ Error: Could not get Tailscale IP" 16 | exit 1 17 | fi 18 | 19 | echo "🚀 Starting JRVS Web Server..." 20 | echo " Tailscale IP: $TAILSCALE_IP" 21 | echo " Port: 8080" 22 | echo "" 23 | echo "🔒 Access JRVS at: http://$TAILSCALE_IP:8080/" 24 | echo "" 25 | echo " From any Tailscale device:" 26 | echo " - Desktop browsers" 27 | echo " - Mobile devices" 28 | echo " - Tablets" 29 | echo "" 30 | echo "Press Ctrl+C to stop" 31 | echo "" 32 | 33 | python3 web_server.py 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # Python 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | *.so 8 | .Python 9 | *.egg-info/ 10 | .eggs/ 11 | dist/ 12 | build/ 13 | *.egg 14 | 15 | # Virtual environments 16 | venv/ 17 | .venv/ 18 | ENV/ 19 | env/ 20 | 21 | # dependencies 22 | /node_modules 23 | /.pnp 24 | .pnp.* 25 | .yarn/* 26 | !.yarn/patches 27 | !.yarn/plugins 28 | !.yarn/releases 29 | !.yarn/versions 30 | 31 | # testing 32 | /coverage 33 | 34 | # next.js 35 | /.next/ 36 | /out/ 37 | 38 | # production 39 | /build 40 | 41 | # misc 42 | .DS_Store 43 | *.pem 44 | 45 | # debug 46 | npm-debug.log* 47 | yarn-debug.log* 48 | yarn-error.log* 49 | .pnpm-debug.log* 50 | 51 | # env files (can opt-in for committing if needed) 52 | .env* 53 | 54 | # vercel 55 | .vercel 56 | 57 | # typescript 58 | *.tsbuildinfo 59 | next-env.d.ts 60 | -------------------------------------------------------------------------------- /public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mcp/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "host": "localhost", 4 | "port": 3000, 5 | "log_level": "INFO", 6 | "log_file": "logs/jrvs-mcp.log", 7 | "json_logs": true 8 | }, 9 | "ollama": { 10 | "base_url": "http://localhost:11434", 11 | "default_model": "deepseek-r1:14b", 12 | "timeout_seconds": 300, 13 | "max_retries": 3 14 | }, 15 | "database": { 16 | "path": "data/jarvis.db", 17 | "max_connections": 10, 18 | "timeout_seconds": 30 19 | }, 20 | "rag": { 21 | "vector_index_path": "data/faiss_index", 22 | "embedding_model": "all-MiniLM-L6-v2", 23 | "max_context_length": 4000, 24 | "max_retrieved_chunks": 5, 25 | "chunk_size": 512, 26 | "chunk_overlap": 50, 27 | "embedding_batch_size": 64 28 | }, 29 | "cache": { 30 | "enabled": true, 31 | "max_size": 1000, 32 | "default_ttl_seconds": 300, 33 | "cleanup_interval_seconds": 60 34 | }, 35 | "rate_limit": { 36 | "enabled": true, 37 | "default_rate_per_minute": 60, 38 | "default_burst": 10, 39 | "per_client_limits": {} 40 | }, 41 | "resource": { 42 | "max_memory_mb": 2048, 43 | "max_concurrent_requests": 100, 44 | "max_request_duration_seconds": 300 45 | }, 46 | "auth": { 47 | "enabled": false, 48 | "require_api_key": false, 49 | "development_mode": true 50 | }, 51 | "monitoring": { 52 | "enabled": true, 53 | "metrics_interval_seconds": 30, 54 | "health_check_interval_seconds": 60 55 | } 56 | } -------------------------------------------------------------------------------- /push_to_github.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to push JRVS with JARCORE to GitHub 3 | 4 | cd /home/xmanz/JRVS 5 | 6 | echo "Initializing git repository..." 7 | git init 8 | 9 | echo "Configuring git..." 10 | git config user.email "your-email@example.com" 11 | git config user.name "Xthebuilder" 12 | 13 | echo "Staging all files..." 14 | git add -A 15 | 16 | echo "Creating commit..." 17 | git commit -m "feat: Add JARCORE - JARVIS Autonomous Reasoning & Coding Engine 18 | 19 | - AI-powered coding assistant using local Ollama models 20 | - 11 new MCP tools for code generation, analysis, execution 21 | - CLI interface with 7 commands (jarcore_cli.py) 22 | - Interactive demo showcasing all features 23 | - Support for 10+ programming languages 24 | - Comprehensive documentation 25 | 26 | Features: 27 | - Code generation from natural language 28 | - Comprehensive code analysis (bugs, security, performance) 29 | - AI-powered refactoring 30 | - Automatic test generation 31 | - Safe code execution 32 | - Intelligent error fixing 33 | - File operations with auto-backup 34 | - Code explanations in natural language 35 | 36 | 100% local, private, and free using Ollama models." 37 | 38 | echo "Setting remote..." 39 | git remote add origin https://github.com/Xthebuilder/JRVS.git || git remote set-url origin https://github.com/Xthebuilder/JRVS.git 40 | 41 | echo "Setting up GitHub authentication..." 42 | gh auth setup-git 43 | 44 | echo "Pushing to GitHub (force to replace old JRVS)..." 45 | git push -u origin main --force 46 | 47 | echo "Done! JRVS with JARCORE pushed to GitHub." 48 | -------------------------------------------------------------------------------- /add_event.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Quick script to add calendar events to JRVS""" 3 | import asyncio 4 | import aiosqlite 5 | from datetime import datetime 6 | import sys 7 | 8 | async def add_event(title: str, date_str: str, time_str: str = "09:00", description: str = ""): 9 | """Add event to JRVS calendar 10 | 11 | Args: 12 | title: Event title 13 | date_str: Date in format YYYY-MM-DD 14 | time_str: Time in format HH:MM (default: 09:00) 15 | description: Optional event description 16 | """ 17 | db_path = 'data/jarvis.db' 18 | 19 | # Parse datetime 20 | dt_str = f"{date_str} {time_str}" 21 | event_date = datetime.strptime(dt_str, "%Y-%m-%d %H:%M") 22 | 23 | async with aiosqlite.connect(db_path) as db: 24 | cursor = await db.execute(''' 25 | INSERT INTO events (title, description, event_date, reminder_minutes, completed) 26 | VALUES (?, ?, ?, ?, ?) 27 | ''', (title, description, event_date.isoformat(), 0, False)) 28 | await db.commit() 29 | event_id = cursor.lastrowid 30 | print(f"✓ Event added (ID: {event_id})") 31 | print(f" Title: {title}") 32 | print(f" Date: {event_date.strftime('%Y-%m-%d %H:%M')}") 33 | 34 | if __name__ == "__main__": 35 | if len(sys.argv) < 3: 36 | print("Usage: python add_event.py <date> [time] [description]") 37 | print("Example: python add_event.py 'Team meeting' 2025-11-15 14:30 'Discuss project'") 38 | sys.exit(1) 39 | 40 | title = sys.argv[1] 41 | date = sys.argv[2] 42 | time = sys.argv[3] if len(sys.argv) > 3 else "09:00" 43 | desc = sys.argv[4] if len(sys.argv) > 4 else "" 44 | 45 | asyncio.run(add_event(title, date, time, desc)) 46 | -------------------------------------------------------------------------------- /docs/START_HERE.md: -------------------------------------------------------------------------------- 1 | # 🚀 START HERE - JRVS Quick Launch 2 | 3 | ## ✅ Setup Status: COMPLETE 4 | 5 | Everything is configured and ready to go! 6 | 7 | ## Launch JRVS Now 8 | 9 | ```bash 10 | ./start_jrvs.sh 11 | ``` 12 | 13 | Or: 14 | 15 | ```bash 16 | python main.py 17 | ``` 18 | 19 | ## What's Configured 20 | 21 | ✅ **Python & Dependencies** - All installed 22 | ✅ **Database** - Initialized 23 | ✅ **Ollama** - Running with 12 models 24 | ✅ **MCP Servers** - Filesystem + Memory ready 25 | ✅ **Calendar** - With your event tomorrow at 10am 26 | ✅ **Intelligent Agent** - Auto tool selection active 27 | 28 | ## First Commands to Try 29 | 30 | ```bash 31 | # See what's available 32 | /help 33 | 34 | # View your calendar 35 | /month 36 | 37 | # Check MCP tools 38 | /mcp-servers 39 | 40 | # Just chat naturally! 41 | what is async programming? 42 | remember that I like Python 43 | read the file README.md 44 | ``` 45 | 46 | ## Key Features 47 | 48 | 🤖 **Intelligent Agent** - JRVS automatically uses tools when needed 49 | 📅 **Smart Calendar** - "add meeting tomorrow at 2pm" 50 | 🔧 **MCP Tools** - File access, memory, web search (w/ API key) 51 | 🧠 **RAG System** - Context-aware responses 52 | 📊 **Reports** - `/report` shows what JRVS did 53 | 54 | ## Documentation 55 | 56 | - **SETUP_COMPLETE.md** - Full setup details 57 | - **README.md** - Main documentation 58 | - **MCP_CLIENT_GUIDE.md** - MCP usage 59 | - **BRAVE_SEARCH_SETUP.md** - Enable web search 60 | 61 | ## Quick Tips 62 | 63 | 💡 **Chat naturally** - JRVS figures out what tools to use 64 | 💡 **Use /report** - See what tools were called and why 65 | 💡 **Add Brave API key** - Enable web search (optional) 66 | 💡 **Check /help** - See all commands 67 | 68 | --- 69 | 70 | **Ready to go! Launch JRVS and start chatting! 🎉** 71 | 72 | ```bash 73 | ./start_jrvs.sh 74 | ``` 75 | -------------------------------------------------------------------------------- /tests/test_mcp_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Test script for JRVS MCP Client 4 | 5 | This script demonstrates connecting to MCP servers and calling tools. 6 | """ 7 | 8 | import asyncio 9 | from mcp_gateway.client import mcp_client 10 | 11 | async def main(): 12 | print("🔌 Testing JRVS MCP Client\n") 13 | 14 | # Initialize 15 | print("Initializing MCP client...") 16 | success = await mcp_client.initialize() 17 | 18 | if not success: 19 | print("❌ Failed to initialize MCP client") 20 | return 21 | 22 | # List servers 23 | servers = await mcp_client.list_servers() 24 | print(f"\n✓ Connected to {len(servers)} server(s):") 25 | for server in servers: 26 | print(f" • {server}") 27 | 28 | # List all tools 29 | print("\n📋 Available tools:") 30 | all_tools = await mcp_client.list_all_tools() 31 | for server, tools in all_tools.items(): 32 | print(f"\n {server}:") 33 | for tool in tools[:3]: # Show first 3 tools 34 | print(f" • {tool['name']} - {tool.get('description', 'No description')}") 35 | if len(tools) > 3: 36 | print(f" ... and {len(tools) - 3} more") 37 | 38 | # Example tool call (if filesystem server is available) 39 | if "filesystem" in servers: 40 | print("\n🔧 Testing filesystem tool...") 41 | try: 42 | # List files in current directory 43 | result = await mcp_client.call_tool( 44 | "filesystem", 45 | "list_directory", 46 | {"path": "."} 47 | ) 48 | print(f"✓ Listed directory successfully") 49 | except Exception as e: 50 | print(f"⚠️ Tool call failed: {e}") 51 | 52 | # Cleanup 53 | print("\n🧹 Cleaning up...") 54 | await mcp_client.cleanup() 55 | print("✓ Done!") 56 | 57 | if __name__ == "__main__": 58 | asyncio.run(main()) 59 | -------------------------------------------------------------------------------- /Dockerfile.mcp: -------------------------------------------------------------------------------- 1 | # Multi-stage Dockerfile for JRVS Enhanced MCP Server 2 | # Optimized for production deployment 3 | 4 | # ============================================================================ 5 | # Stage 1: Builder - Install dependencies 6 | # ============================================================================ 7 | FROM python:3.11-slim as builder 8 | 9 | WORKDIR /build 10 | 11 | # Install build dependencies 12 | RUN apt-get update && apt-get install -y --no-install-recommends \ 13 | gcc \ 14 | g++ \ 15 | build-essential \ 16 | && rm -rf /var/lib/apt/lists/* 17 | 18 | # Copy requirements 19 | COPY requirements.txt . 20 | 21 | # Install Python dependencies 22 | RUN pip install --no-cache-dir --user -r requirements.txt 23 | 24 | # ============================================================================ 25 | # Stage 2: Runtime - Minimal production image 26 | # ============================================================================ 27 | FROM python:3.11-slim 28 | 29 | LABEL maintainer="JRVS Team" 30 | LABEL description="JRVS Enhanced MCP Server - Production Ready" 31 | LABEL version="1.0.0" 32 | 33 | # Create non-root user 34 | RUN groupadd -r jrvs && useradd -r -g jrvs jrvs 35 | 36 | # Set working directory 37 | WORKDIR /app 38 | 39 | # Install runtime dependencies only 40 | RUN apt-get update && apt-get install -y --no-install-recommends \ 41 | curl \ 42 | && rm -rf /var/lib/apt/lists/* 43 | 44 | # Copy Python packages from builder 45 | COPY --from=builder /root/.local /home/jrvs/.local 46 | 47 | # Copy application code 48 | COPY --chown=jrvs:jrvs . . 49 | 50 | # Create necessary directories 51 | RUN mkdir -p /app/data /app/logs && \ 52 | chown -R jrvs:jrvs /app/data /app/logs 53 | 54 | # Set environment variables 55 | ENV PATH=/home/jrvs/.local/bin:$PATH \ 56 | PYTHONUNBUFFERED=1 \ 57 | PYTHONDONTWRITEBYTECODE=1 \ 58 | JRVS_LOG_LEVEL=INFO \ 59 | OLLAMA_BASE_URL=http://ollama:11434 60 | 61 | # Switch to non-root user 62 | USER jrvs 63 | 64 | # Health check 65 | HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ 66 | CMD python -c "import sys; sys.exit(0)" 67 | 68 | # Expose port (if needed for HTTP interface) 69 | EXPOSE 3000 70 | 71 | # Run the enhanced MCP server 72 | CMD ["python", "mcp/server_enhanced.py"] 73 | -------------------------------------------------------------------------------- /tests/test_coding_agent.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Test script for JRVS Coding Agent""" 3 | 4 | import sys 5 | import asyncio 6 | from pathlib import Path 7 | 8 | # Add project root to path 9 | sys.path.insert(0, str(Path(__file__).parent)) 10 | 11 | async def test_coding_agent(): 12 | """Test the coding agent functionality""" 13 | try: 14 | from mcp_gateway.coding_agent import coding_agent 15 | print("✓ Coding agent imported successfully") 16 | 17 | # Test code generation 18 | print("\n=== Testing Code Generation ===") 19 | result = await coding_agent.generate_code( 20 | task="Create a function that calculates fibonacci numbers", 21 | language="python", 22 | include_tests=False 23 | ) 24 | 25 | if "error" not in result: 26 | print("✓ Code generation successful") 27 | print(f"Generated code:\n{result.get('code', 'N/A')[:200]}...") 28 | else: 29 | print(f"✗ Code generation failed: {result['error']}") 30 | 31 | # Test code explanation 32 | print("\n=== Testing Code Explanation ===") 33 | test_code = """ 34 | def fibonacci(n): 35 | if n <= 1: 36 | return n 37 | return fibonacci(n-1) + fibonacci(n-2) 38 | """ 39 | explanation = await coding_agent.explain_code(test_code, "python", "brief") 40 | print(f"✓ Explanation: {explanation[:200]}...") 41 | 42 | # Test file operations 43 | print("\n=== Testing File Operations ===") 44 | test_file = "/tmp/jrvs_test.py" 45 | write_result = await coding_agent.write_file( 46 | test_file, 47 | test_code, 48 | create_dirs=True, 49 | backup=False 50 | ) 51 | 52 | if write_result.get("success"): 53 | print(f"✓ File written: {test_file}") 54 | 55 | read_result = await coding_agent.read_file(test_file) 56 | if not read_result.get("error"): 57 | print(f"✓ File read: {read_result.get('lines')} lines") 58 | else: 59 | print(f"✗ File read failed: {read_result.get('error')}") 60 | else: 61 | print(f"✗ File write failed: {write_result.get('error')}") 62 | 63 | print("\n=== All Tests Complete ===") 64 | 65 | except Exception as e: 66 | print(f"✗ Test failed: {e}") 67 | import traceback 68 | traceback.print_exc() 69 | 70 | if __name__ == "__main__": 71 | asyncio.run(test_coding_agent()) 72 | -------------------------------------------------------------------------------- /scripts/start_mcp_enhanced.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Quick start script for JRVS Enhanced MCP Server 3 | 4 | set -e 5 | 6 | echo "================================================================" 7 | echo " JRVS Enhanced MCP Server - Quick Start" 8 | echo "================================================================" 9 | echo "" 10 | 11 | # Colors 12 | GREEN='\033[0;32m' 13 | YELLOW='\033[1;33m' 14 | RED='\033[0;31m' 15 | NC='\033[0m' # No Color 16 | 17 | # Check Python version 18 | echo -n "Checking Python version... " 19 | PYTHON_VERSION=$(python --version 2>&1 | awk '{print $2}') 20 | if python -c "import sys; exit(0 if sys.version_info >= (3, 11) else 1)"; then 21 | echo -e "${GREEN}✓${NC} Python $PYTHON_VERSION" 22 | else 23 | echo -e "${RED}✗${NC} Python 3.11+ required, found $PYTHON_VERSION" 24 | exit 1 25 | fi 26 | 27 | # Check if Ollama is running 28 | echo -n "Checking Ollama service... " 29 | if curl -s http://localhost:11434/api/tags > /dev/null 2>&1; then 30 | echo -e "${GREEN}✓${NC} Ollama is running" 31 | else 32 | echo -e "${YELLOW}⚠${NC} Ollama not detected" 33 | echo " Start Ollama with: ollama serve" 34 | echo " Then pull a model: ollama pull deepseek-r1:14b" 35 | fi 36 | 37 | # Check dependencies 38 | echo -n "Checking dependencies... " 39 | if python -c "import mcp, faiss, sentence_transformers, aiohttp" > /dev/null 2>&1; then 40 | echo -e "${GREEN}✓${NC} All dependencies installed" 41 | else 42 | echo -e "${YELLOW}⚠${NC} Missing dependencies" 43 | echo " Installing dependencies..." 44 | pip install -r requirements.txt 45 | fi 46 | 47 | # Create required directories 48 | echo -n "Creating directories... " 49 | mkdir -p data logs mcp/tests 50 | echo -e "${GREEN}✓${NC}" 51 | 52 | # Create default config if doesn't exist 53 | if [ ! -f "mcp/config.json" ]; then 54 | echo -n "Creating default configuration... " 55 | python -c "from mcp.config_manager import create_default_config; create_default_config('mcp/config.json')" 56 | echo -e "${GREEN}✓${NC}" 57 | else 58 | echo -e "${GREEN}✓${NC} Configuration exists" 59 | fi 60 | 61 | echo "" 62 | echo "================================================================" 63 | echo " Starting JRVS Enhanced MCP Server" 64 | echo "================================================================" 65 | echo "" 66 | echo " Config: mcp/config.json" 67 | echo " Logs: logs/jrvs-mcp-enhanced.log" 68 | echo "" 69 | echo " Press Ctrl+C to stop the server" 70 | echo "" 71 | echo "================================================================" 72 | echo "" 73 | 74 | # Start the server 75 | python mcp/server_enhanced.py 76 | -------------------------------------------------------------------------------- /tests/test_calendar_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Test calendar API endpoint""" 3 | 4 | import asyncio 5 | import httpx 6 | from datetime import datetime 7 | 8 | async def test_calendar(): 9 | """Test adding calendar event via API""" 10 | 11 | # Start by testing the calendar module directly 12 | print("1. Testing calendar module directly...") 13 | from core.calendar import calendar 14 | from datetime import datetime 15 | 16 | try: 17 | await calendar.initialize() 18 | event_id = await calendar.add_event( 19 | title="Direct Test Event", 20 | event_date=datetime.now(), 21 | description="Testing calendar directly" 22 | ) 23 | print(f"✓ Direct calendar test passed! Event ID: {event_id}") 24 | 25 | # Get events 26 | events = await calendar.get_today_events() 27 | print(f"✓ Today's events: {len(events)}") 28 | for event in events: 29 | print(f" - {event['title']} at {event['event_date']}") 30 | except Exception as e: 31 | print(f"✗ Direct calendar test failed: {e}") 32 | import traceback 33 | traceback.print_exc() 34 | 35 | # Now test via HTTP API 36 | print("\n2. Testing via HTTP API...") 37 | try: 38 | async with httpx.AsyncClient() as client: 39 | # Get Tailscale IP 40 | import subprocess 41 | result = subprocess.run( 42 | ["tailscale", "ip", "-4"], 43 | capture_output=True, 44 | text=True 45 | ) 46 | tailscale_ip = result.stdout.strip() if result.returncode == 0 else "localhost" 47 | 48 | url = f"http://{tailscale_ip}:8080/api/calendar/event" 49 | 50 | data = { 51 | "title": "API Test Event", 52 | "date": datetime.now().strftime("%Y-%m-%d"), 53 | "time": "14:30", 54 | "description": "Testing via HTTP API" 55 | } 56 | 57 | print(f"Posting to: {url}") 58 | print(f"Data: {data}") 59 | 60 | response = await client.post(url, data=data) 61 | print(f"Status: {response.status_code}") 62 | print(f"Response: {response.text}") 63 | 64 | if response.status_code == 200: 65 | print("✓ API test passed!") 66 | else: 67 | print(f"✗ API test failed with status {response.status_code}") 68 | 69 | except Exception as e: 70 | print(f"✗ API test failed: {e}") 71 | import traceback 72 | traceback.print_exc() 73 | 74 | if __name__ == "__main__": 75 | asyncio.run(test_calendar()) 76 | -------------------------------------------------------------------------------- /lib/firebase.ts: -------------------------------------------------------------------------------- 1 | import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app'; 2 | import { getAuth, Auth } from 'firebase/auth'; 3 | import { getFirestore, Firestore } from 'firebase/firestore'; 4 | import { getStorage, FirebaseStorage } from 'firebase/storage'; 5 | 6 | // Firebase configuration 7 | const firebaseConfig = { 8 | apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, 9 | authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, 10 | projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, 11 | storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, 12 | messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, 13 | appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, 14 | measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID 15 | }; 16 | 17 | // Validate Firebase configuration 18 | const validateFirebaseConfig = () => { 19 | const requiredEnvVars = [ 20 | 'NEXT_PUBLIC_FIREBASE_API_KEY', 21 | 'NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN', 22 | 'NEXT_PUBLIC_FIREBASE_PROJECT_ID', 23 | 'NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET', 24 | 'NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID', 25 | 'NEXT_PUBLIC_FIREBASE_APP_ID' 26 | ]; 27 | 28 | const missingVars = requiredEnvVars.filter((varName) => { 29 | const value = process.env[varName]; 30 | return !value || 31 | value.includes('your-') || 32 | value.includes('here') || 33 | value.trim() === ''; 34 | }); 35 | 36 | if (missingVars.length > 0) { 37 | console.warn( 38 | '⚠️ Firebase configuration is incomplete. Missing or invalid environment variables:', 39 | missingVars.join(', ') 40 | ); 41 | console.warn( 42 | 'Please create a .env.local file with your Firebase credentials. See .env.local.example for reference.' 43 | ); 44 | console.warn( 45 | '📖 Setup guide: Check README.md for detailed Firebase setup instructions.' 46 | ); 47 | } else { 48 | console.log('✅ Firebase configuration loaded successfully'); 49 | } 50 | }; 51 | 52 | // Validate configuration (only in browser/client) 53 | if (typeof window !== 'undefined') { 54 | validateFirebaseConfig(); 55 | } 56 | 57 | // Initialize Firebase 58 | let app: FirebaseApp; 59 | try { 60 | app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp(); 61 | } catch (error) { 62 | console.error('❌ Firebase initialization error:', error); 63 | throw new Error( 64 | 'Failed to initialize Firebase. Please check your environment variables.' 65 | ); 66 | } 67 | 68 | // Initialize Firebase services 69 | export const auth: Auth = getAuth(app); 70 | export const db: Firestore = getFirestore(app); 71 | export const storage: FirebaseStorage = getStorage(app); 72 | 73 | export default app; 74 | -------------------------------------------------------------------------------- /mcp_gateway/test_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Quick test to verify MCP server tools are working""" 3 | 4 | import sys 5 | import asyncio 6 | from pathlib import Path 7 | 8 | # Add project root 9 | project_root = Path(__file__).parent.parent 10 | sys.path.insert(0, str(project_root)) 11 | 12 | async def test_tools(): 13 | """Test basic MCP tool functionality""" 14 | print("Testing JRVS MCP Server Tools...\n") 15 | 16 | # Import after path setup 17 | from llm.ollama_client import ollama_client 18 | from rag.retriever import rag_retriever 19 | from core.database import db 20 | from core.calendar import calendar 21 | 22 | results = [] 23 | 24 | # Test 1: Database initialization 25 | try: 26 | await db.initialize() 27 | results.append(("✓", "Database initialized")) 28 | except Exception as e: 29 | results.append(("✗", f"Database failed: {e}")) 30 | 31 | # Test 2: RAG system 32 | try: 33 | await rag_retriever.initialize() 34 | stats = await rag_retriever.get_stats() 35 | results.append(("✓", f"RAG initialized (vectors: {stats.get('vector_store', {}).get('total_vectors', 0)})")) 36 | except Exception as e: 37 | results.append(("✗", f"RAG failed: {e}")) 38 | 39 | # Test 3: Calendar 40 | try: 41 | await calendar.initialize() 42 | events = await calendar.get_upcoming_events(days=7) 43 | results.append(("✓", f"Calendar initialized ({len(events)} events)")) 44 | except Exception as e: 45 | results.append(("✗", f"Calendar failed: {e}")) 46 | 47 | # Test 4: Ollama connection 48 | try: 49 | models = await ollama_client.discover_models() 50 | if models: 51 | results.append(("✓", f"Ollama connected ({len(models)} models)")) 52 | else: 53 | results.append(("⚠", "Ollama connected but no models found")) 54 | except Exception as e: 55 | results.append(("⚠", f"Ollama not available: {e}")) 56 | 57 | # Print results 58 | print("\nTest Results:") 59 | print("-" * 60) 60 | for status, message in results: 61 | print(f"{status} {message}") 62 | 63 | # Summary 64 | passed = sum(1 for s, _ in results if s == "✓") 65 | failed = sum(1 for s, _ in results if s == "✗") 66 | warnings = sum(1 for s, _ in results if s == "⚠") 67 | 68 | print("-" * 60) 69 | print(f"Passed: {passed} | Failed: {failed} | Warnings: {warnings}") 70 | 71 | if failed == 0: 72 | print("\n✓ MCP server components are ready!") 73 | return True 74 | else: 75 | print("\n✗ Some components failed. Check errors above.") 76 | return False 77 | 78 | if __name__ == "__main__": 79 | success = asyncio.run(test_tools()) 80 | sys.exit(0 if success else 1) 81 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | """Configuration settings for Jarvis AI Agent""" 2 | import os 3 | from pathlib import Path 4 | 5 | # Base paths 6 | BASE_DIR = Path(__file__).parent 7 | DATA_DIR = BASE_DIR / "data" 8 | MODELS_DIR = DATA_DIR / "models" 9 | 10 | # JARCORE workspace settings 11 | JARCORE_WORKSPACE = Path(os.environ.get("JARCORE_WORKSPACE", Path.cwd())) 12 | 13 | # Ensure directories exist 14 | DATA_DIR.mkdir(exist_ok=True) 15 | MODELS_DIR.mkdir(exist_ok=True) 16 | 17 | # Database settings 18 | DATABASE_PATH = DATA_DIR / "jarvis.db" 19 | VECTOR_INDEX_PATH = DATA_DIR / "faiss_index" 20 | 21 | # Ollama settings 22 | OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434") 23 | DEFAULT_MODEL = os.environ.get("OLLAMA_DEFAULT_MODEL", "deepseek-r1:14b") 24 | 25 | # LM Studio settings (OpenAI-compatible API) 26 | LMSTUDIO_BASE_URL = os.environ.get("LMSTUDIO_BASE_URL", "http://127.0.0.1:1234/v1") 27 | LMSTUDIO_DEFAULT_MODEL = os.environ.get("LMSTUDIO_DEFAULT_MODEL", "") 28 | 29 | # Timeout settings (in seconds) 30 | TIMEOUTS = { 31 | "embedding_generation": 60, 32 | "vector_search": 10, 33 | "context_building": 60, 34 | "ollama_response": 300, # 5 minutes 35 | "llm_response": 300, # Generic LLM response timeout (5 minutes) 36 | "web_scraping": 45 37 | } 38 | 39 | # RAG settings 40 | MAX_CONTEXT_LENGTH = 4000 41 | MAX_RETRIEVED_CHUNKS = 5 42 | CHUNK_SIZE = 512 43 | CHUNK_OVERLAP = 50 44 | 45 | # Performance settings 46 | MAX_MEMORY_MB = 2024 47 | EMBEDDING_BATCH_SIZE = 64 48 | VECTOR_CACHE_SIZE = 2000 49 | 50 | # CLI Theme settings 51 | THEMES = { 52 | "matrix": { 53 | "primary": "bright_green", 54 | "secondary": "green", 55 | "accent": "bright_cyan", 56 | "error": "bright_red", 57 | "warning": "bright_yellow", 58 | "prompt": "bright_green", 59 | "response": "white" 60 | }, 61 | "cyberpunk": { 62 | "primary": "bright_magenta", 63 | "secondary": "magenta", 64 | "accent": "bright_cyan", 65 | "error": "bright_red", 66 | "warning": "bright_yellow", 67 | "prompt": "bright_magenta", 68 | "response": "bright_white" 69 | }, 70 | "minimal": { 71 | "primary": "white", 72 | "secondary": "bright_black", 73 | "accent": "blue", 74 | "error": "red", 75 | "warning": "yellow", 76 | "prompt": "blue", 77 | "response": "white" 78 | } 79 | } 80 | 81 | DEFAULT_THEME = "matrix" 82 | 83 | # ASCII Art 84 | JARVIS_ASCII = """ 85 | ██╗ █████╗ ██████╗ ██╗ ██╗██╗███████╗ 86 | ██║██╔══██╗██╔══██╗██║ ██║██║██╔════╝ 87 | ██║███████║██████╔╝██║ ██║██║███████╗ 88 | ██ ██║██╔══██║██╔══██╗╚██╗ ██╔╝██║╚════██║ 89 | ╚█████╔╝██║ ██║██║ ██║ ╚████╔╝ ██║███████║ 90 | ╚════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝╚══════╝ 91 | Advanced RAG-Powered AI Assistant 92 | """ 93 | -------------------------------------------------------------------------------- /docker-compose.mcp.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | # ============================================================================ 5 | # Ollama Service 6 | # ============================================================================ 7 | ollama: 8 | image: ollama/ollama:latest 9 | container_name: jrvs-ollama 10 | ports: 11 | - "11434:11434" 12 | volumes: 13 | - ollama_data:/root/.ollama 14 | environment: 15 | - OLLAMA_ORIGINS=* 16 | healthcheck: 17 | test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"] 18 | interval: 30s 19 | timeout: 10s 20 | retries: 3 21 | start_period: 40s 22 | restart: unless-stopped 23 | networks: 24 | - jrvs-network 25 | 26 | # ============================================================================ 27 | # JRVS Enhanced MCP Server 28 | # ============================================================================ 29 | jrvs-mcp: 30 | build: 31 | context: . 32 | dockerfile: Dockerfile.mcp 33 | container_name: jrvs-mcp-server 34 | depends_on: 35 | ollama: 36 | condition: service_healthy 37 | environment: 38 | # Server config 39 | - JRVS_LOG_LEVEL=INFO 40 | - JRVS_HOST=0.0.0.0 41 | - JRVS_PORT=3000 42 | 43 | # Ollama config 44 | - OLLAMA_BASE_URL=http://ollama:11434 45 | - OLLAMA_DEFAULT_MODEL=deepseek-r1:14b 46 | 47 | # Database 48 | - JRVS_DB_PATH=/app/data/jarvis.db 49 | 50 | # Cache 51 | - JRVS_CACHE_ENABLED=true 52 | 53 | # Rate limiting 54 | - JRVS_RATE_LIMIT_ENABLED=true 55 | - JRVS_RATE_LIMIT_PER_MINUTE=60 56 | 57 | # Authentication (disable in production with real auth) 58 | - JRVS_AUTH_ENABLED=false 59 | - JRVS_REQUIRE_API_KEY=false 60 | volumes: 61 | # Persistent data 62 | - jrvs_data:/app/data 63 | - jrvs_logs:/app/logs 64 | 65 | # Mount code for development (comment out for production) 66 | # - ./:/app 67 | ports: 68 | - "3000:3000" 69 | healthcheck: 70 | test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] 71 | interval: 30s 72 | timeout: 10s 73 | retries: 3 74 | start_period: 60s 75 | restart: unless-stopped 76 | networks: 77 | - jrvs-network 78 | stdin_open: true 79 | tty: true 80 | 81 | # ============================================================================ 82 | # Volumes 83 | # ============================================================================ 84 | volumes: 85 | ollama_data: 86 | driver: local 87 | jrvs_data: 88 | driver: local 89 | jrvs_logs: 90 | driver: local 91 | 92 | # ============================================================================ 93 | # Networks 94 | # ============================================================================ 95 | networks: 96 | jrvs-network: 97 | driver: bridge 98 | -------------------------------------------------------------------------------- /mcp_gateway/client_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "filesystem": { 4 | "command": "npx", 5 | "args": [ 6 | "-y", 7 | "@modelcontextprotocol/server-filesystem", 8 | "/tmp" 9 | ], 10 | "description": "File operations - JRVS can read, write, search, and manage files in allowed directories. Add your own paths to the 'args' array above." 11 | }, 12 | "memory": { 13 | "command": "npx", 14 | "args": ["-y", "@modelcontextprotocol/server-memory"], 15 | "description": "Persistent memory - JRVS remembers facts, notes, and preferences across sessions" 16 | }, 17 | "brave-search": { 18 | "command": "npx", 19 | "args": ["-y", "@modelcontextprotocol/server-brave-search"], 20 | "env": { 21 | "BRAVE_API_KEY": "" 22 | }, 23 | "description": "Web search via Brave - ADD YOUR API KEY ABOVE (get from https://brave.com/search/api/)" 24 | } 25 | }, 26 | "_disabled_servers": {}, 27 | "_comment": "Active MCP Servers for JRVS", 28 | "_setup_instructions": { 29 | "filesystem": { 30 | "status": "READY TO USE", 31 | "description": "Filesystem server allows JRVS to access files in specified directories", 32 | "allowed_paths": [ 33 | "/tmp - Temporary files (default, always available)" 34 | ], 35 | "setup_note": "Add your own directories to the 'args' array in the filesystem server config above. Example: add '/home/yourusername/Documents' to allow access to your Documents folder.", 36 | "security": "Only directories listed in 'args' are accessible. Add/remove paths as needed. Paths are validated before connection - invalid paths are filtered out with warnings.", 37 | "tools": [ 38 | "read_file - Read file contents", 39 | "write_file - Write to files", 40 | "list_directory - List directory contents", 41 | "create_directory - Create new directories", 42 | "move_file - Move/rename files", 43 | "search_files - Search for files", 44 | "get_file_info - Get file metadata" 45 | ] 46 | }, 47 | "memory": { 48 | "status": "READY TO USE", 49 | "description": "Memory server gives JRVS persistent memory across sessions", 50 | "storage": "~/.memory-server/ (automatically created)", 51 | "tools": [ 52 | "create_memory - Store a new memory/note", 53 | "search_memories - Search stored memories", 54 | "list_memories - List all memories", 55 | "delete_memory - Remove a memory" 56 | ], 57 | "examples": [ 58 | "Remember: User prefers Python 3.11", 59 | "Remember: Daily standup at 9am every Monday", 60 | "Remember: Database password is in ~/.secrets/db.txt" 61 | ] 62 | }, 63 | "brave-search": { 64 | "status": "DISABLED - NEEDS API KEY", 65 | "description": "Brave Search gives JRVS web search capabilities", 66 | "setup_steps": [ 67 | "1. Get API key from: https://brave.com/search/api/", 68 | "2. Replace 'GET_YOUR_KEY_FROM_...' with your actual key", 69 | "3. Move this config from '_disabled_servers' to 'mcpServers'", 70 | "4. Restart JRVS" 71 | ], 72 | "pricing": "Free tier: 2,000 queries/month", 73 | "tools": [ 74 | "brave_web_search - Search the web", 75 | "brave_local_search - Search for local businesses/places" 76 | ] 77 | } 78 | }, 79 | "_notes": { 80 | "security": "Be careful with filesystem paths - only add directories you trust JRVS to access", 81 | "performance": "Each connected server uses some resources. Disable unused servers by moving them to _disabled_servers", 82 | "more_servers": "See available servers at: https://github.com/modelcontextprotocol/servers" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /docs/TAILSCALE_QUICKSTART.md: -------------------------------------------------------------------------------- 1 | # 🚀 JRVS on Tailscale - Quick Start 2 | 3 | ## ✅ Setup Complete! 4 | 5 | JRVS is now configured to run as a **private web server** on your Tailscale network. 6 | 7 | ## 🔒 Your Tailscale IP 8 | 9 | ``` 10 | 100.113.61.115 11 | ``` 12 | 13 | ## 🌐 Start the Web Server 14 | 15 | ```bash 16 | ./start_web_server.sh 17 | ``` 18 | 19 | ## 📱 Access JRVS 20 | 21 | From **any device** on your Tailscale network: 22 | 23 | ### In Your Browser 24 | ``` 25 | http://100.113.61.115:8080/ 26 | ``` 27 | 28 | ### From Your Devices 29 | - ✅ **Desktop** - Any browser 30 | - ✅ **Laptop** - Any browser 31 | - ✅ **iPhone/iPad** - Safari/Chrome (install Tailscale app first) 32 | - ✅ **Android** - Chrome/Firefox (install Tailscale app first) 33 | - ✅ **Tablet** - Any browser 34 | 35 | ## 🎯 What You Get 36 | 37 | ### Modern Web Interface 38 | - Real-time chat with JRVS 39 | - Tool usage indicators 40 | - Mobile-friendly design 41 | - Auto-reconnect 42 | 43 | ### All JRVS Features 44 | - 🤖 Intelligent agent (auto tool selection) 45 | - 📅 Calendar 46 | - 🔧 MCP tools (filesystem, memory, brave-search*) 47 | - 🧠 RAG system 48 | - 📊 Activity reports 49 | 50 | ### Secure Access 51 | - 🔒 **Only on Tailscale** - Not public internet 52 | - 🔒 **Encrypted** - All traffic encrypted by Tailscale 53 | - 🔒 **Authenticated** - Only your devices 54 | 55 | ## ⚡ Quick Test 56 | 57 | 1. **Start server:** 58 | ```bash 59 | ./start_web_server.sh 60 | ``` 61 | 62 | 2. **Open browser on any Tailscale device:** 63 | ``` 64 | http://100.113.61.115:8080/ 65 | ``` 66 | 67 | 3. **Chat with JRVS:** 68 | - "what files are in my JRVS directory?" 69 | - "remember that I prefer Python 3.11" 70 | - "add event tomorrow at 2pm" 71 | 72 | ## 📱 Mobile Setup 73 | 74 | ### iPhone/iPad 75 | 1. Install **Tailscale** from App Store 76 | 2. Login with your account 77 | 3. Open **Safari** or **Chrome** 78 | 4. Go to `http://100.113.61.115:8080/` 79 | 5. **Bookmark it** or Add to Home Screen 80 | 81 | ### Android 82 | 1. Install **Tailscale** from Play Store 83 | 2. Login with your account 84 | 3. Open **Chrome** or **Firefox** 85 | 4. Go to `http://100.113.61.115:8080/` 86 | 5. **Bookmark it** or Add to Home Screen 87 | 88 | ## 🔄 Auto-Start (Optional) 89 | 90 | To start JRVS web server on boot: 91 | 92 | ```bash 93 | sudo cp jrvs-web.service /etc/systemd/system/ 94 | sudo systemctl daemon-reload 95 | sudo systemctl enable jrvs-web 96 | sudo systemctl start jrvs-web 97 | ``` 98 | 99 | Check status: 100 | ```bash 101 | sudo systemctl status jrvs-web 102 | ``` 103 | 104 | ## 🎮 Both Interfaces Available 105 | 106 | ### CLI (Terminal) 107 | ```bash 108 | python main.py 109 | ``` 110 | - Traditional command-line interface 111 | - All slash commands (/help, /month, etc.) 112 | 113 | ### Web (Browser) 114 | ```bash 115 | ./start_web_server.sh 116 | ``` 117 | - Modern web interface 118 | - Access from any device 119 | - Same features, better UX 120 | 121 | **Use whichever you prefer!** 🎉 122 | 123 | ## 📚 Documentation 124 | 125 | - **TAILSCALE_WEB_SETUP.md** - Full web server guide 126 | - **SETUP_COMPLETE.md** - Initial setup 127 | - **README.md** - Main documentation 128 | 129 | ## 🐛 Troubleshooting 130 | 131 | ### Can't access web interface 132 | 133 | **Check Tailscale:** 134 | ```bash 135 | tailscale status 136 | ``` 137 | 138 | **Check server is running:** 139 | ```bash 140 | ps aux | grep web_server 141 | ``` 142 | 143 | **Restart server:** 144 | ```bash 145 | ./start_web_server.sh 146 | ``` 147 | 148 | ### Different devices 149 | 150 | Each device needs: 151 | 1. ✅ Tailscale app installed 152 | 2. ✅ Logged into your Tailnet 153 | 3. ✅ Connected (not paused) 154 | 155 | ## 🎉 You're Ready! 156 | 157 | Start JRVS web server and access from any device: 158 | 159 | ```bash 160 | ./start_web_server.sh 161 | ``` 162 | 163 | Then open `http://100.113.61.115:8080/` 164 | 165 | **Private. Secure. Accessible everywhere.** 🔒 166 | 167 | --- 168 | 169 | For detailed information, see **TAILSCALE_WEB_SETUP.md** 170 | -------------------------------------------------------------------------------- /mcp_gateway/README.md: -------------------------------------------------------------------------------- 1 | # JRVS MCP Server 2 | 3 | This directory contains the Model Context Protocol (MCP) server implementation for JRVS. 4 | 5 | ## Quick Start 6 | 7 | ### 1. Install Dependencies 8 | ```bash 9 | pip install -r ../requirements.txt 10 | ``` 11 | 12 | ### 2. Test the Server 13 | ```bash 14 | python test_server.py 15 | ``` 16 | 17 | ### 3. Run the MCP Server 18 | ```bash 19 | python server.py 20 | ``` 21 | 22 | The server will start and wait for MCP client connections via stdio. 23 | 24 | ## Files 25 | 26 | - `server.py` - Main MCP server with all JRVS tools 27 | - `test_server.py` - Component test script 28 | - `claude_config.json` - Example Claude Code configuration 29 | - `README.md` - This file 30 | 31 | ## Available Tools 32 | 33 | ### Knowledge Base (17 tools total) 34 | - `search_knowledge_base` - Semantic search 35 | - `get_context_for_query` - Get RAG context 36 | - `add_document_to_knowledge_base` - Index documents 37 | - `scrape_and_index_url` - Web scraping 38 | - `get_rag_stats` - System statistics 39 | - `list_ollama_models` - List LLM models 40 | - `get_current_model` - Current model info 41 | - `switch_ollama_model` - Switch models 42 | - `generate_with_ollama` - Generate with RAG 43 | - `get_calendar_events` - Upcoming events 44 | - `get_today_events` - Today's events 45 | - `create_calendar_event` - Create events 46 | - `delete_calendar_event` - Delete events 47 | - `mark_event_completed` - Complete events 48 | - `get_conversation_history` - Chat history 49 | 50 | ### Resources 51 | - `jrvs://config` - Configuration info 52 | - `jrvs://status` - System status 53 | 54 | ## Integration with Claude Code 55 | 56 | ### Method 1: Environment Variable (Recommended for Testing) 57 | 58 | ```bash 59 | export CLAUDE_MCP_SERVER='{"jrvs": {"command": "python", "args": ["/home/xmanz/JRVS/mcp/server.py"], "env": {"PYTHONPATH": "/home/xmanz/JRVS"}}}' 60 | ``` 61 | 62 | ### Method 2: Configuration File 63 | 64 | Create or edit `~/.config/claude/mcp_servers.json`: 65 | 66 | ```json 67 | { 68 | "mcpServers": { 69 | "jrvs": { 70 | "command": "python", 71 | "args": ["/home/xmanz/JRVS/mcp/server.py"], 72 | "env": { 73 | "PYTHONPATH": "/home/xmanz/JRVS" 74 | } 75 | } 76 | } 77 | } 78 | ``` 79 | 80 | Then restart Claude Code. 81 | 82 | ## Testing 83 | 84 | Test individual components: 85 | ```bash 86 | python test_server.py 87 | ``` 88 | 89 | Test with MCP inspector (requires Node.js): 90 | ```bash 91 | npx @modelcontextprotocol/inspector python server.py 92 | ``` 93 | 94 | ## Troubleshooting 95 | 96 | **Import Error**: Make sure `mcp` package is installed: 97 | ```bash 98 | pip install 'mcp[cli]' 99 | ``` 100 | 101 | **Ollama Not Available**: Start Ollama service: 102 | ```bash 103 | ollama serve 104 | ``` 105 | 106 | **Database Not Found**: Run JRVS normally first: 107 | ```bash 108 | cd .. && python main.py 109 | ``` 110 | 111 | ## Architecture 112 | 113 | ``` 114 | MCP Client (Claude Code) 115 | ↓ stdio/JSON-RPC 116 | MCP Server (server.py) 117 | ↓ 118 | FastMCP (@tool decorators) 119 | ↓ 120 | JRVS Components 121 | ├─ rag_retriever (RAG/Vector Search) 122 | ├─ ollama_client (LLM Interface) 123 | ├─ calendar (Event Management) 124 | ├─ db (SQLite Storage) 125 | └─ web_scraper (Content Ingestion) 126 | ``` 127 | 128 | ## Development 129 | 130 | To add new tools, use the `@mcp.tool()` decorator in `server.py`: 131 | 132 | ```python 133 | @mcp.tool() 134 | async def my_new_tool(param: str) -> dict: 135 | """Tool description for Claude""" 136 | # Your implementation 137 | return {"result": "value"} 138 | ``` 139 | 140 | For resources (read-only data), use `@mcp.resource()`: 141 | 142 | ```python 143 | @mcp.resource("jrvs://my-resource") 144 | def my_resource() -> str: 145 | """Resource description""" 146 | return "Resource data" 147 | ``` 148 | 149 | ## See Also 150 | 151 | - [MCP_SETUP.md](../MCP_SETUP.md) - Complete setup guide 152 | - [README.md](../README.md) - JRVS documentation 153 | - [Model Context Protocol](https://modelcontextprotocol.io) - Official MCP docs 154 | -------------------------------------------------------------------------------- /tests/test_lmstudio_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Test script for JRVS LM Studio Client 4 | 5 | This script tests the LM Studio client functionality. 6 | """ 7 | 8 | import sys 9 | import asyncio 10 | from pathlib import Path 11 | 12 | # Add project root to path 13 | sys.path.insert(0, str(Path(__file__).parent.parent)) 14 | 15 | 16 | async def test_lmstudio_client(): 17 | """Test the LM Studio client functionality""" 18 | from llm.lmstudio_client import LMStudioClient 19 | 20 | print("🔌 Testing JRVS LM Studio Client\n") 21 | 22 | # Create client with test URL 23 | client = LMStudioClient(base_url="http://127.0.0.1:1234/v1") 24 | 25 | # Test 1: Try to discover models (will return empty list if LM Studio is not running) 26 | print("1. Testing model discovery (connection check)...") 27 | models = await client.discover_models() 28 | if models: 29 | print(f" ✓ Connected to LM Studio, found {len(models)} model(s)") 30 | else: 31 | print(" ✗ LM Studio not running (expected if not installed)") 32 | print(" Skipping further tests as LM Studio is not available") 33 | await client.cleanup() 34 | return True # Not a failure, just not available 35 | 36 | # Test 2: List models with details 37 | print("\n2. Testing list_models...") 38 | model_list = await client.list_models() 39 | if model_list: 40 | print(f" ✓ Got model list with {len(model_list)} items") 41 | for model in model_list[:2]: 42 | print(f" • {model['name']} (current: {model['current']})") 43 | else: 44 | print(" ⚠ Empty model list") 45 | 46 | # Test 3: Test generate (non-streaming) 47 | if models: 48 | print("\n3. Testing text generation...") 49 | response = await client.generate( 50 | prompt="Say 'hello' and nothing else.", 51 | stream=False 52 | ) 53 | if response: 54 | print(f" ✓ Generated response: {response[:100]}...") 55 | else: 56 | print(" ✗ Failed to generate response") 57 | 58 | # Test 4: Test set_base_url 59 | print("\n4. Testing set_base_url...") 60 | await client.set_base_url("http://localhost:1234/v1") 61 | assert client.base_url == "http://localhost:1234/v1" 62 | print(" ✓ Base URL updated successfully") 63 | 64 | # Cleanup 65 | print("\n🧹 Cleaning up...") 66 | await client.cleanup() 67 | print("✓ All tests passed!") 68 | 69 | return True 70 | 71 | 72 | async def test_client_initialization(): 73 | """Test client initialization and configuration""" 74 | from llm.lmstudio_client import lmstudio_client 75 | from config import LMSTUDIO_BASE_URL 76 | 77 | print("\n📋 Testing client initialization...\n") 78 | 79 | # Test that global instance exists 80 | assert lmstudio_client is not None 81 | print(" ✓ Global lmstudio_client instance exists") 82 | 83 | # Test that it uses the configured base URL 84 | assert lmstudio_client.base_url == LMSTUDIO_BASE_URL.rstrip('/') 85 | print(f" ✓ Base URL is configured: {lmstudio_client.base_url}") 86 | 87 | # Test session starts as None 88 | assert lmstudio_client.session is None 89 | print(" ✓ Session is None initially") 90 | 91 | print("\n✓ Initialization tests passed!") 92 | return True 93 | 94 | 95 | async def main(): 96 | """Run all tests""" 97 | print("=" * 50) 98 | print("LM Studio Client Test Suite") 99 | print("=" * 50) 100 | 101 | try: 102 | # Run initialization tests 103 | await test_client_initialization() 104 | 105 | # Run client tests 106 | await test_lmstudio_client() 107 | 108 | print("\n" + "=" * 50) 109 | print("All tests completed!") 110 | print("=" * 50) 111 | 112 | except Exception as e: 113 | print(f"\n❌ Test failed with error: {e}") 114 | import traceback 115 | traceback.print_exc() 116 | return 1 117 | 118 | return 0 119 | 120 | 121 | if __name__ == "__main__": 122 | exit_code = asyncio.run(main()) 123 | sys.exit(exit_code) 124 | -------------------------------------------------------------------------------- /docs/COMMANDS_FIXED.md: -------------------------------------------------------------------------------- 1 | # ✅ Slash Commands Fixed! 2 | 3 | ## What Was Wrong 4 | 5 | When you typed `/stats` or clicked the "System Stats" button, JRVS was: 6 | - ❌ Sending the command to the AI model 7 | - ❌ Getting general world statistics instead of JRVS stats 8 | - ❌ Not recognizing it as a command 9 | 10 | ## What I Fixed 11 | 12 | Added **command detection** to the WebSocket handler: 13 | 14 | 1. **Detects slash commands** - Checks if message starts with `/` 15 | 2. **Intercepts them** - Doesn't send to AI 16 | 3. **Handles properly** - Calls `handle_command()` function 17 | 4. **Returns correct data** - JRVS system stats, not AI-generated content 18 | 19 | ## Now All Commands Work! 20 | 21 | ### ✅ `/stats` - System Statistics 22 | Shows: 23 | - Current AI model 24 | - Available Ollama models 25 | - Connected MCP servers 26 | - RAG database stats (documents, chunks, embeddings) 27 | - Session info 28 | 29 | ### ✅ `/help` - Command List 30 | Shows all available commands organized by category 31 | 32 | ### ✅ `/models` - List AI Models 33 | Shows all Ollama models with current one marked 34 | 35 | ### ✅ `/mcp-servers` - MCP Servers 36 | Lists connected servers with tool counts 37 | 38 | ### ✅ `/mcp-tools` - MCP Tools 39 | Shows all available tools from all servers 40 | 41 | ### ✅ `/report` - Activity Report 42 | Displays MCP agent activity log with timestamps 43 | 44 | ### ✅ `/calendar` - Upcoming Events 45 | Shows next 7 days of events 46 | 47 | ### ✅ `/month` - Monthly Calendar 48 | ASCII calendar + event list for current month 49 | 50 | ### ✅ `/today` - Today's Events 51 | Lists all events for today 52 | 53 | ### ✅ `/history` - Conversation History 54 | Shows recent conversations from this session 55 | 56 | ## How It Works Now 57 | 58 | ### Typing a Command 59 | ``` 60 | You type: /stats 61 | ↓ 62 | WebSocket detects "/" prefix 63 | ↓ 64 | Calls handle_command("stats") 65 | ↓ 66 | Returns JRVS system statistics 67 | ↓ 68 | Displays in chat 69 | ``` 70 | 71 | ### Clicking a Button 72 | ``` 73 | You click: "📊 System Stats" 74 | ↓ 75 | Calls quickCommand('/stats') 76 | ↓ 77 | Sends "/stats" via WebSocket 78 | ↓ 79 | Same flow as above 80 | ``` 81 | 82 | ### Regular Chat 83 | ``` 84 | You type: "What is Python?" 85 | ↓ 86 | No "/" prefix 87 | ↓ 88 | Goes to AI + MCP agent 89 | ↓ 90 | Normal response 91 | ``` 92 | 93 | ## Test It Now! 94 | 95 | ### Try These Commands: 96 | 97 | **In the chat:** 98 | ``` 99 | /stats 100 | /help 101 | /models 102 | /mcp-servers 103 | /calendar 104 | /month 105 | /report 106 | ``` 107 | 108 | **Or click the sidebar buttons:** 109 | - 📊 System Stats 110 | - 📚 Help & Commands 111 | - 🔌 Connected Servers 112 | - 📅 View Calendar 113 | - 📝 Activity Report 114 | 115 | ## What You'll See 116 | 117 | ### `/stats` now shows: 118 | ``` 119 | JRVS System Statistics 120 | 121 | AI Model: 122 | • Current: gemma3:12b 123 | • Available: 12 Ollama models 124 | 125 | MCP Integration: 126 | • Connected Servers: 2 127 | • Servers: filesystem, memory 128 | 129 | RAG System: 130 | • Documents: X 131 | • Chunks: Y 132 | • Embeddings: Z 133 | 134 | Calendar: 135 | • Events in database: (check /calendar) 136 | 137 | Session: 138 | • Session ID: abc12345... 139 | • WebSocket: Connected ✓ 140 | ``` 141 | 142 | ### Instead of: 143 | ``` 144 | ❌ General world population statistics 145 | ❌ CO2 emissions data 146 | ❌ Random facts 147 | ``` 148 | 149 | ## All Commands Implemented 150 | 151 | ✅ `/help` - Command list 152 | ✅ `/stats` - System statistics 153 | ✅ `/models` - Ollama models 154 | ✅ `/mcp-servers` - MCP servers 155 | ✅ `/mcp-tools` - MCP tools 156 | ✅ `/report` - Activity report 157 | ✅ `/calendar` - Upcoming events 158 | ✅ `/month` - Monthly calendar 159 | ✅ `/today` - Today's events 160 | ✅ `/history` - Chat history 161 | 162 | ## Buttons Work Too! 163 | 164 | All sidebar buttons now send the correct commands: 165 | - 📚 Help & Commands → `/help` 166 | - 📊 System Stats → `/stats` 167 | - 📝 Activity Report → `/report` 168 | - 📅 View Calendar → Opens modal (special handling) 169 | - 📆 Today's Events → `/today` 170 | - 🔌 Connected Servers → Opens modal (special handling) 171 | - 🔧 Available Tools → Opens modal (special handling) 172 | 173 | ## Try It! 174 | 175 | ```bash 176 | ./start_web_server.sh 177 | ``` 178 | 179 | Then: 180 | 1. Type `/stats` in chat 181 | 2. Or click "📊 System Stats" button 182 | 3. Get actual JRVS system information! 183 | 184 | **All commands now work correctly!** ✅ 185 | -------------------------------------------------------------------------------- /docs/WHATS_NEW.md: -------------------------------------------------------------------------------- 1 | # What's New in JRVS 🎉 2 | 3 | ## Recent Updates 4 | 5 | ### 🔌 MCP Client Integration (NEW!) 6 | 7 | JRVS can now **use external tools** by connecting to MCP servers! This is a game-changer. 8 | 9 | **What this means:** 10 | - JRVS was already an MCP **server** (others can use JRVS as a tool) 11 | - Now JRVS is also an MCP **client** (JRVS can use external tools) 12 | - Think of it like giving JRVS superpowers from other services! 13 | 14 | **Examples of what JRVS can now do:** 15 | - 📁 Read/write files on your system (via filesystem server) 16 | - 🐙 Create GitHub issues and PRs (via github server) 17 | - 🔍 Search the web (via brave-search server) 18 | - 💾 Access databases (via postgres/sqlite servers) 19 | - 📝 Keep persistent notes across sessions (via memory server) 20 | - 💬 Send Slack messages (via slack server) 21 | 22 | **Quick Start:** 23 | 1. Edit `mcp_gateway/client_config.json` to add servers 24 | 2. Start JRVS - it auto-connects 25 | 3. Use `/mcp-servers` and `/mcp-tools` to explore 26 | 4. Call tools with `/mcp-call <server> <tool> <json_args>` 27 | 28 | 📖 Full guide: [MCP_CLIENT_GUIDE.md](MCP_CLIENT_GUIDE.md) 29 | 30 | --- 31 | 32 | ### 📅 Smart Calendar with ASCII View 33 | 34 | **Interactive monthly calendar:** 35 | ``` 36 | ╔══════════════════════════════════════════════════════════════╗ 37 | ║ November 2025 ║ 38 | ╠══════════════════════════════════════════════════════════════╣ 39 | ║ Sun Mon Tue Wed Thu Fri Sat ║ 40 | ╠══════════════════════════════════════════════════════════════╣ 41 | ║ 2 3 4 5 6 7 8 ║ 42 | ║ 9 10 [11] 12* 13 14 15 ║ 43 | ║ 16 17 18 19 20 21 22 ║ 44 | ║ 23 24 25 26 27 28 29 ║ 45 | ║ 30 ║ 46 | ╚══════════════════════════════════════════════════════════════╝ 47 | 48 | Legend: [DD] = Today | DD* = Has Events | [DD]* = Today + Events 49 | ``` 50 | 51 | **Natural language event creation:** 52 | - "add event study time tomorrow at 10 am" 53 | - "meeting with team today at 3pm" 54 | - "schedule dentist 2025-11-20 at 2:30 pm" 55 | 56 | **Commands:** 57 | - `/month` - Show current month calendar 58 | - `/month 12 2025` - Show specific month 59 | - `/calendar` - Upcoming events (7 days) 60 | - `/today` - Today's events 61 | 62 | --- 63 | 64 | ### 🎨 Enhanced CLI Experience 65 | 66 | **New commands:** 67 | - `/mcp-servers` - List connected MCP servers 68 | - `/mcp-tools` - Browse available tools 69 | - `/month` - ASCII calendar view 70 | - `/today` - Today's events 71 | 72 | **Improved help:** 73 | - Organized by category 74 | - Examples for complex commands 75 | - Tips for natural language features 76 | 77 | --- 78 | 79 | ## Previous Features 80 | 81 | ### 🧠 RAG Pipeline 82 | - FAISS vector search with BERT embeddings 83 | - Automatic context injection for better responses 84 | - Web scraping and document indexing 85 | 86 | ### 🔄 Dynamic Model Switching 87 | - Hot-swap between Ollama models 88 | - Multiple model support 89 | - No restart needed 90 | 91 | ### 🎨 Beautiful Themes 92 | - Matrix (green hacker style) 93 | - Cyberpunk (magenta/cyan) 94 | - Minimal (clean B&W) 95 | 96 | ### 💾 Persistent Storage 97 | - SQLite database for conversations 98 | - Document and embedding storage 99 | - Session history 100 | 101 | ### ⚡ Performance 102 | - Lazy loading 103 | - Async operations 104 | - Connection pooling 105 | - Caching 106 | 107 | --- 108 | 109 | ## Coming Soon 110 | 111 | - 🤖 AI-powered MCP tool selection (JRVS automatically picks the right tool) 112 | - 🗣️ Voice input/output 113 | - 🌐 Web UI alongside CLI 114 | - 📊 Analytics dashboard 115 | - 🔐 Better auth/security for MCP connections 116 | - 📱 Mobile companion app 117 | 118 | --- 119 | 120 | ## Migration Notes 121 | 122 | **For existing users:** 123 | - No breaking changes! 124 | - MCP client is optional - JRVS works fine without it 125 | - Calendar feature uses existing database 126 | - All old commands still work 127 | 128 | **New dependencies:** 129 | - MCP client libraries (already in requirements.txt) 130 | - Node.js (only if using MCP servers that need it) 131 | 132 | --- 133 | 134 | ## Support 135 | 136 | - 📖 Docs: See README.md and MCP_CLIENT_GUIDE.md 137 | - 🐛 Issues: Create an issue on GitHub 138 | - 💡 Ideas: Open a discussion 139 | 140 | Enjoy the new features! 🚀 141 | -------------------------------------------------------------------------------- /docs/JARCORE_QUICKSTART.md: -------------------------------------------------------------------------------- 1 | # JARCORE Quick Start 2 | 3 | **JARCORE** = **J**ARVIS **A**utonomous **R**easoning & **C**oding **E**ngine 4 | 5 | ## 🚀 Quick Start 6 | 7 | ### 1. Make sure Ollama is running 8 | 9 | ```bash 10 | # Check Ollama 11 | curl http://localhost:11434/api/tags 12 | 13 | # Install a coding model if needed 14 | ollama pull deepseek-coder:6.7b 15 | ``` 16 | 17 | ### 2. Run the Demo 18 | 19 | ```bash 20 | cd /home/xmanz/JRVS 21 | python3 demo_jarcore.py 22 | ``` 23 | 24 | The demo shows all 8 JARCORE capabilities: 25 | - ✓ Code Generation 26 | - ✓ Code Analysis 27 | - ✓ Code Refactoring 28 | - ✓ Test Generation 29 | - ✓ Code Execution 30 | - ✓ Error Fixing 31 | - ✓ File Operations 32 | - ✓ Code Explanation 33 | 34 | ### 3. Use the CLI 35 | 36 | ```bash 37 | # Generate code 38 | python3 jarcore_cli.py generate "create a binary search function" -l python -o search.py 39 | 40 | # Analyze code for security issues 41 | python3 jarcore_cli.py analyze myfile.py --type security 42 | 43 | # Fix code errors 44 | python3 jarcore_cli.py fix broken.py "NameError: name 'x' is not defined" --write 45 | 46 | # Generate tests 47 | python3 jarcore_cli.py test mycode.py -o test_mycode.py 48 | 49 | # Explain code 50 | python3 jarcore_cli.py explain complex.py --detail detailed 51 | 52 | # Refactor code 53 | python3 jarcore_cli.py refactor messy.py --write 54 | 55 | # Run code 56 | python3 jarcore_cli.py run script.py 57 | ``` 58 | 59 | ### 4. Use via MCP 60 | 61 | Add to your MCP client configuration: 62 | 63 | ```json 64 | { 65 | "mcpServers": { 66 | "jrvs": { 67 | "command": "python3", 68 | "args": ["/home/xmanz/JRVS/mcp_gateway/server.py"] 69 | } 70 | } 71 | } 72 | ``` 73 | 74 | Then in your MCP client: 75 | ``` 76 | > Use JARCORE to create a REST API for user authentication 77 | > Analyze this code for security issues 78 | > Fix the error in my Python script 79 | ``` 80 | 81 | ### 5. Use in Python 82 | 83 | ```python 84 | import asyncio 85 | from mcp_gateway.coding_agent import jarcore 86 | 87 | async def main(): 88 | # Generate code 89 | result = await jarcore.generate_code( 90 | task="Create a function to validate email addresses", 91 | language="python" 92 | ) 93 | print(result["code"]) 94 | 95 | # Analyze it 96 | analysis = await jarcore.analyze_code( 97 | code=result["code"], 98 | language="python", 99 | analysis_type="security" 100 | ) 101 | print(analysis) 102 | 103 | asyncio.run(main()) 104 | ``` 105 | 106 | ## 🎯 Common Tasks 107 | 108 | ### Generate a complete module 109 | ```bash 110 | python3 jarcore_cli.py generate "Create a user authentication system with password hashing" \ 111 | --language python \ 112 | --tests \ 113 | --output auth.py 114 | ``` 115 | 116 | ### Debug and fix code 117 | ```bash 118 | # Run code and capture error 119 | python3 mycode.py 2> error.txt 120 | 121 | # Fix it 122 | python3 jarcore_cli.py fix mycode.py "$(cat error.txt)" --write 123 | ``` 124 | 125 | ### Complete workflow 126 | ```bash 127 | # 1. Generate 128 | python3 jarcore_cli.py generate "REST API for TODO items" -l python -o api.py 129 | 130 | # 2. Analyze 131 | python3 jarcore_cli.py analyze api.py --type comprehensive 132 | 133 | # 3. Refactor if needed 134 | python3 jarcore_cli.py refactor api.py --write 135 | 136 | # 4. Generate tests 137 | python3 jarcore_cli.py test api.py -o test_api.py 138 | 139 | # 5. Run tests 140 | python3 jarcore_cli.py run test_api.py 141 | ``` 142 | 143 | ## 📚 Documentation 144 | 145 | Full documentation: `docs/CODING_AGENT.md` 146 | 147 | ## 🔧 Troubleshooting 148 | 149 | **Ollama not responding:** 150 | ```bash 151 | systemctl status ollama 152 | # or 153 | ollama serve 154 | ``` 155 | 156 | **Import errors:** 157 | ```bash 158 | cd /home/xmanz/JRVS 159 | pip install -r requirements.txt 160 | pip install rich # For CLI colors 161 | ``` 162 | 163 | **Slow generation:** 164 | - Use smaller models (7B instead of 13B+) 165 | - Check `config.py` for model settings 166 | 167 | ## 🌟 Features 168 | 169 | - **100% Local** - All code runs on your machine 170 | - **Multi-Language** - Python, JS, Go, Rust, Java, C/C++, Bash 171 | - **Context-Aware** - Uses RAG for project understanding 172 | - **Safe Execution** - Sandboxed with timeout protection 173 | - **Auto-Backup** - Files backed up before editing 174 | - **Test Generation** - Automatic unit test creation 175 | - **Error Fixing** - AI-powered debugging 176 | - **Code Analysis** - Security, performance, style checks 177 | 178 | ## 🎓 Examples 179 | 180 | See `demo_jarcore.py` for comprehensive examples of all features. 181 | 182 | --- 183 | 184 | **JARCORE** - AI-Powered Coding with Local Ollama Models 185 | -------------------------------------------------------------------------------- /docs/MCP_QUICKSTART.md: -------------------------------------------------------------------------------- 1 | # JRVS MCP Integration - Quick Start 2 | 3 | Your JRVS AI assistant now has full MCP (Model Context Protocol) support! 🚀 4 | 5 | ## What You Get 6 | 7 | Claude Code can now directly: 8 | - 🔍 Search your JRVS knowledge base semantically 9 | - 🌐 Scrape websites and add them to JRVS 10 | - 📅 Manage your calendar events 11 | - 🤖 Switch between Ollama models 12 | - 💬 Access conversation history 13 | - 📊 Get system statistics 14 | 15 | ## Installation (Already Done!) 16 | 17 | ✅ MCP server created at `mcp_gateway/server.py` 18 | ✅ 17 tools + 2 resources implemented 19 | ✅ Dependencies added to requirements.txt 20 | ✅ Test suite passed (4/4 components working) 21 | 22 | ## Usage 23 | 24 | ### Option 1: Test Locally First 25 | 26 | ```bash 27 | # Test components 28 | python mcp_gateway/test_server.py 29 | 30 | # Run server (will wait for stdio connection) 31 | python mcp_gateway/server.py 32 | ``` 33 | 34 | ### Option 2: Connect to Claude Code 35 | 36 | **Edit your Claude Code MCP config** (`~/.config/claude/mcp_servers.json` or similar): 37 | 38 | ```json 39 | { 40 | "mcpServers": { 41 | "jrvs": { 42 | "command": "python", 43 | "args": ["/home/xmanz/JRVS/mcp_gateway/server.py"], 44 | "env": { 45 | "PYTHONPATH": "/home/xmanz/JRVS" 46 | } 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | **Then restart Claude Code.** 53 | 54 | ## Example Usage in Claude Code 55 | 56 | Once connected, you can ask: 57 | 58 | ``` 59 | "Search JRVS for information about Python best practices" 60 | → Uses: search_knowledge_base(query="Python best practices") 61 | 62 | "Add https://docs.python.org to JRVS knowledge base" 63 | → Uses: scrape_and_index_url(url="https://docs.python.org") 64 | 65 | "What's on my calendar this week?" 66 | → Uses: get_calendar_events(days=7) 67 | 68 | "Switch JRVS to llama3.1 model" 69 | → Uses: switch_ollama_model(model_name="llama3.1") 70 | 71 | "Ask JRVS about machine learning with context" 72 | → Uses: generate_with_ollama(prompt="...", context=<RAG>) 73 | ``` 74 | 75 | ## Available Tools (17 total) 76 | 77 | ### 🧠 RAG & Knowledge Base 78 | - `search_knowledge_base` - Semantic vector search 79 | - `get_context_for_query` - Get enriched RAG context 80 | - `add_document_to_knowledge_base` - Index documents 81 | - `scrape_and_index_url` - Web scraping 82 | - `get_rag_stats` - System statistics 83 | 84 | ### 🤖 Ollama LLM 85 | - `list_ollama_models` - Available models 86 | - `get_current_model` - Current model info 87 | - `switch_ollama_model` - Change models 88 | - `generate_with_ollama` - Generate with RAG 89 | 90 | ### 📅 Calendar 91 | - `get_calendar_events` - Upcoming events 92 | - `get_today_events` - Today's schedule 93 | - `create_calendar_event` - New events 94 | - `delete_calendar_event` - Remove events 95 | - `mark_event_completed` - Complete events 96 | 97 | ### 💬 Conversation 98 | - `get_conversation_history` - Past conversations 99 | 100 | ### 📊 Resources (Read-Only) 101 | - `jrvs://config` - Configuration 102 | - `jrvs://status` - System status 103 | 104 | ## Test Results 105 | 106 | ``` 107 | ✓ Database initialized 108 | ✓ RAG initialized (vectors: 0) 109 | ✓ Calendar initialized (0 events) 110 | ✓ Ollama connected (12 models) 111 | ``` 112 | 113 | All systems ready! 114 | 115 | ## Next Steps 116 | 117 | 1. **Add Content**: Scrape some websites to build your knowledge base 118 | ```bash 119 | # Via CLI 120 | python main.py 121 | /scrape https://example.com 122 | ``` 123 | 124 | 2. **Try MCP**: Connect Claude Code and start using JRVS tools 125 | 126 | 3. **Explore**: Check `MCP_SETUP.md` for detailed documentation 127 | 128 | ## Files Created 129 | 130 | ``` 131 | mcp_gateway/ 132 | ├── __init__.py # Package init 133 | ├── server.py # MCP server (17 tools) 134 | ├── test_server.py # Component tests 135 | ├── claude_config.json # Example config 136 | └── README.md # MCP gateway directory docs 137 | 138 | MCP_SETUP.md # Complete setup guide (this file) 139 | MCP_QUICKSTART.md # Quick reference 140 | ``` 141 | 142 | ## Troubleshooting 143 | 144 | **"MCP package not found"** 145 | ```bash 146 | pip install 'mcp[cli]' 147 | ``` 148 | 149 | **"Cannot connect to Ollama"** 150 | ```bash 151 | ollama serve 152 | ``` 153 | 154 | **"Database not found"** 155 | ```bash 156 | python main.py # Run JRVS once to initialize 157 | ``` 158 | 159 | ## Documentation 160 | 161 | - **Full Guide**: See `MCP_SETUP.md` 162 | - **MCP Gateway Dir**: See `mcp_gateway/README.md` 163 | - **JRVS Docs**: See `README.md` 164 | - **Official MCP**: https://modelcontextprotocol.io 165 | 166 | --- 167 | 168 | **Your JRVS is now MCP-enabled!** 🎉 169 | 170 | Use it with Claude Code to supercharge your AI workflows with persistent knowledge, RAG-enhanced responses, and calendar management. 171 | -------------------------------------------------------------------------------- /setup_windows.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM JRVS Windows Setup Script 3 | REM This script installs and configures prerequisites for running JRVS on Windows 4 | 5 | echo ======================================== 6 | echo JRVS Windows Setup Script 7 | echo ======================================== 8 | echo. 9 | 10 | REM Check for Administrator privileges 11 | net session >nul 2>&1 12 | if %errorLevel% neq 0 ( 13 | echo [WARNING] Not running as Administrator. Some installations may require elevated privileges. 14 | echo [INFO] Right-click this script and select "Run as administrator" if you encounter issues. 15 | echo. 16 | ) 17 | 18 | REM Check Python installation 19 | echo [1/6] Checking Python installation... 20 | python --version >nul 2>&1 21 | if %errorLevel% neq 0 ( 22 | echo [ERROR] Python is not installed or not in PATH. 23 | echo [INFO] Please install Python 3.8+ from https://python.org/downloads/ 24 | echo [INFO] Make sure to check "Add Python to PATH" during installation. 25 | echo. 26 | pause 27 | exit /b 1 28 | ) else ( 29 | for /f "tokens=*" %%i in ('python --version 2^>^&1') do set PYTHON_VERSION_STR=%%i 30 | echo [OK] %PYTHON_VERSION_STR% found 31 | ) 32 | echo. 33 | 34 | REM Check pip installation 35 | echo [2/6] Checking pip installation... 36 | pip --version >nul 2>&1 37 | if %errorLevel% neq 0 ( 38 | echo [ERROR] pip is not installed. 39 | echo [INFO] Installing pip... 40 | python -m ensurepip --upgrade 41 | ) else ( 42 | echo [OK] pip is available 43 | ) 44 | echo. 45 | 46 | REM Check Node.js installation (required for MCP servers) 47 | echo [3/6] Checking Node.js installation... 48 | node --version >nul 2>&1 49 | if %errorLevel% neq 0 ( 50 | echo [WARNING] Node.js is not installed. 51 | echo [INFO] Node.js is required for MCP server functionality. 52 | echo [INFO] Please install Node.js from https://nodejs.org/ 53 | echo. 54 | ) else ( 55 | for /f "tokens=*" %%i in ('node --version 2^>^&1') do set NODE_VERSION=%%i 56 | echo [OK] Node.js %NODE_VERSION% found 57 | ) 58 | echo. 59 | 60 | REM Install Python dependencies 61 | echo [4/6] Installing Python dependencies... 62 | if not exist requirements.txt ( 63 | echo [ERROR] requirements.txt not found! 64 | echo [INFO] Make sure you are running this script from the JRVS directory. 65 | pause 66 | exit /b 1 67 | ) 68 | echo [INFO] This may take a few minutes... 69 | pip install -r requirements.txt 70 | if %errorLevel% neq 0 ( 71 | echo [ERROR] Failed to install Python dependencies. 72 | echo [INFO] Try running: pip install -r requirements.txt manually 73 | pause 74 | exit /b 1 75 | ) else ( 76 | echo [OK] Python dependencies installed successfully 77 | ) 78 | echo. 79 | 80 | REM Check for Ollama 81 | echo [5/6] Checking Ollama installation... 82 | ollama --version >nul 2>&1 83 | if %errorLevel% neq 0 ( 84 | echo [WARNING] Ollama is not installed or not in PATH. 85 | echo [INFO] Ollama is required to run AI models locally. 86 | echo [INFO] Please install Ollama from https://ollama.ai/download 87 | echo. 88 | echo [INFO] After installing Ollama, run these commands to get started: 89 | echo ollama serve 90 | echo ollama pull llama3.1 91 | echo. 92 | ) else ( 93 | echo [OK] Ollama is installed 94 | echo [INFO] Make sure Ollama is running with: ollama serve 95 | ) 96 | echo. 97 | 98 | REM Install npm dependencies (if package.json exists) 99 | echo [6/6] Installing npm dependencies for frontend... 100 | if exist package.json ( 101 | npm --version >nul 2>&1 102 | if %errorLevel% neq 0 ( 103 | echo [WARNING] npm is not available. Skipping frontend dependencies. 104 | echo [INFO] Install Node.js to enable frontend features. 105 | ) else ( 106 | npm install 107 | if %errorLevel% neq 0 ( 108 | echo [WARNING] Failed to install npm dependencies. 109 | ) else ( 110 | echo [OK] npm dependencies installed successfully 111 | ) 112 | ) 113 | ) else ( 114 | echo [INFO] No package.json found. Skipping npm dependencies. 115 | ) 116 | echo. 117 | 118 | REM Create data directory if it doesn't exist 119 | if not exist data mkdir data 120 | echo [OK] Data directory ready 121 | echo. 122 | 123 | echo ======================================== 124 | echo Setup Complete! 125 | echo ======================================== 126 | echo. 127 | echo Next steps: 128 | echo 1. Start Ollama: ollama serve 129 | echo 2. Pull a model: ollama pull llama3.1 130 | echo 3. Run JRVS: python main.py 131 | echo. 132 | echo For the web interface: 133 | echo 1. Start API: python api\server.py 134 | echo 2. Start frontend: npm run dev 135 | echo 3. Open browser: http://localhost:3000 136 | echo. 137 | echo See README.md for more information. 138 | echo. 139 | pause 140 | -------------------------------------------------------------------------------- /docs/BRAVE_SEARCH_SETUP.md: -------------------------------------------------------------------------------- 1 | # Brave Search API Setup for JRVS 2 | 3 | This guide will help you enable web search capabilities in JRVS using the Brave Search API. 4 | 5 | ## Why Brave Search? 6 | 7 | - **Privacy-focused**: Brave doesn't track your searches 8 | - **Free tier**: 2,000 queries per month (plenty for personal use) 9 | - **Fast**: Direct API access without rate limiting issues 10 | - **Quality**: Brave has its own independent search index 11 | 12 | ## Step-by-Step Setup 13 | 14 | ### 1. Get Your Brave Search API Key 15 | 16 | 1. **Visit** https://brave.com/search/api/ 17 | 2. **Sign up** for a free account or log in 18 | 3. **Subscribe** to the free tier (Data for AI): 19 | - Go to your dashboard 20 | - Select "Data for AI" plan 21 | - It's FREE for up to 2,000 queries/month 22 | 4. **Copy your API key** from the dashboard 23 | 24 | ### 2. Add API Key to JRVS 25 | 26 | 1. **Open** `mcp_gateway/client_config.json` 27 | 28 | 2. **Find** the brave-search config in `_disabled_servers`: 29 | ```json 30 | "brave-search": { 31 | "command": "npx", 32 | "args": ["-y", "@modelcontextprotocol/server-brave-search"], 33 | "env": { 34 | "BRAVE_API_KEY": "GET_YOUR_KEY_FROM_https://brave.com/search/api/" 35 | }, 36 | "description": "Web search - DISABLED until you add your Brave API key" 37 | } 38 | ``` 39 | 40 | 3. **Replace** `GET_YOUR_KEY_FROM_...` with your actual API key: 41 | ```json 42 | "BRAVE_API_KEY": "BSA1234567890abcdef..." 43 | ``` 44 | 45 | 4. **Move** the entire brave-search config from `_disabled_servers` to `mcpServers`: 46 | 47 | **Before:** 48 | ```json 49 | { 50 | "mcpServers": { 51 | "filesystem": {...}, 52 | "memory": {...} 53 | }, 54 | "_disabled_servers": { 55 | "brave-search": {...} ← Move this 56 | } 57 | } 58 | ``` 59 | 60 | **After:** 61 | ```json 62 | { 63 | "mcpServers": { 64 | "filesystem": {...}, 65 | "memory": {...}, 66 | "brave-search": {...} ← Now active! 67 | }, 68 | "_disabled_servers": {} 69 | } 70 | ``` 71 | 72 | ### 3. Restart JRVS 73 | 74 | ```bash 75 | python main.py 76 | ``` 77 | 78 | You should see: 79 | ``` 80 | Connected to 3 MCP server(s): filesystem, memory, brave-search 81 | ``` 82 | 83 | ### 4. Test Web Search 84 | 85 | In JRVS, try: 86 | 87 | ```bash 88 | # List tools to confirm brave-search is connected 89 | /mcp-tools brave-search 90 | 91 | # Search the web 92 | /mcp-call brave-search brave_web_search '{"q": "Python async programming", "count": 5}' 93 | 94 | # Local business search 95 | /mcp-call brave-search brave_local_search '{"q": "pizza near me", "count": 3}' 96 | ``` 97 | 98 | ## Usage Examples 99 | 100 | ### Web Search 101 | ```bash 102 | /mcp-call brave-search brave_web_search '{"q": "latest AI news", "count": 10}' 103 | ``` 104 | 105 | Returns: Title, URL, description, and snippet for each result 106 | 107 | ### Local Search 108 | ```bash 109 | /mcp-call brave-search brave_local_search '{"q": "coffee shops in San Francisco", "count": 5}' 110 | ``` 111 | 112 | Returns: Business name, address, phone, rating, hours 113 | 114 | ## API Limits 115 | 116 | **Free Tier (Data for AI):** 117 | - 2,000 queries/month 118 | - No credit card required 119 | - Rate limit: ~1 request/second 120 | - Perfect for personal JRVS usage 121 | 122 | **If you exceed the limit:** 123 | - Upgrade to paid plan (starts at $5/month for 15k queries) 124 | - Or wait until next month for free tier reset 125 | 126 | ## Monitoring Usage 127 | 128 | 1. Visit https://brave.com/search/api/dashboard 129 | 2. Check your usage stats 130 | 3. Set up alerts if needed 131 | 132 | ## Troubleshooting 133 | 134 | ### "API key invalid" error 135 | - Double-check your API key is correct 136 | - Make sure you're subscribed to the Data for AI plan 137 | - API key might take a few minutes to activate after signup 138 | 139 | ### Connection timeout 140 | - Check your internet connection 141 | - Brave API might be temporarily down (rare) 142 | - Try again in a few minutes 143 | 144 | ### No results returned 145 | - Check your search query format 146 | - Some queries might not return results 147 | - Try a more general search term 148 | 149 | ## Alternative: Use Without Brave Search 150 | 151 | JRVS works great without web search! You already have: 152 | - ✅ **Filesystem** - File operations 153 | - ✅ **Memory** - Persistent notes 154 | - ✅ **Web scraping** - Built-in scraper with `/scrape` command 155 | - ✅ **RAG** - Search your scraped documents 156 | 157 | The built-in web scraper can handle most needs: 158 | ```bash 159 | /scrape https://example.com 160 | /search "topic I'm interested in" 161 | ``` 162 | 163 | ## Security Notes 164 | 165 | - **Keep your API key private** - Don't share it or commit to Git 166 | - API key is stored locally in `mcp_gateway/client_config.json` 167 | - Consider using environment variables for production use 168 | - Brave doesn't track or log your searches (privacy-focused) 169 | 170 | ## Additional Resources 171 | 172 | - [Brave Search API Docs](https://brave.com/search/api/) 173 | - [Brave Search API GitHub](https://github.com/brave/brave-search-api) 174 | - [MCP Brave Search Server](https://github.com/modelcontextprotocol/servers/tree/main/src/brave-search) 175 | 176 | --- 177 | 178 | **Ready to search!** 🔍 Once configured, JRVS can search the web on demand for real-time information. 179 | -------------------------------------------------------------------------------- /cli/commands.py: -------------------------------------------------------------------------------- 1 | """Command handler for CLI interface""" 2 | import shlex 3 | from typing import List, Optional 4 | from .themes import theme 5 | 6 | class CommandHandler: 7 | def __init__(self, cli_instance): 8 | self.cli = cli_instance 9 | 10 | async def handle_command(self, command_line: str): 11 | """Parse and handle CLI commands""" 12 | try: 13 | args = shlex.split(command_line) 14 | if not args: 15 | return 16 | 17 | command = args[0].lower() 18 | command_args = args[1:] if len(args) > 1 else [] 19 | 20 | # Route commands 21 | if command == "help": 22 | self.cli.show_help() 23 | 24 | elif command == "models": 25 | await self.cli.list_models() 26 | 27 | elif command == "model": 28 | if command_args: 29 | await self.cli.switch_model(command_args[0]) 30 | else: 31 | await self.cli.list_models() 32 | 33 | elif command == "switch": 34 | if command_args: 35 | await self.cli.switch_model(command_args[0]) 36 | else: 37 | theme.print_error("Usage: /switch <model_name>") 38 | 39 | elif command == "scrape": 40 | if command_args: 41 | await self.cli.scrape_url(command_args[0]) 42 | else: 43 | theme.print_error("Usage: /scrape <url>") 44 | 45 | elif command == "search": 46 | if command_args: 47 | query = " ".join(command_args) 48 | await self.cli.search_documents(query) 49 | else: 50 | theme.print_error("Usage: /search <query>") 51 | 52 | elif command == "stats": 53 | await self.cli.show_stats() 54 | 55 | elif command == "history": 56 | limit = 5 57 | if command_args and command_args[0].isdigit(): 58 | limit = int(command_args[0]) 59 | self.cli.show_conversation_history(limit) 60 | 61 | elif command == "theme": 62 | if command_args: 63 | self.cli.set_theme(command_args[0]) 64 | else: 65 | theme.print_error("Usage: /theme <theme_name>") 66 | theme.print_info("Available themes: matrix, cyberpunk, minimal") 67 | 68 | elif command == "clear": 69 | theme.clear_screen() 70 | theme.print_banner() 71 | 72 | elif command == "calendar": 73 | await self.cli.show_calendar() 74 | 75 | elif command == "month": 76 | # /month or /month 11 2025 77 | month = None 78 | year = None 79 | if len(command_args) >= 1: 80 | month = int(command_args[0]) 81 | if len(command_args) >= 2: 82 | year = int(command_args[1]) 83 | await self.cli.show_month_calendar(month, year) 84 | 85 | elif command == "event": 86 | if len(command_args) >= 2: 87 | await self.cli.add_event(command_args) 88 | else: 89 | theme.print_error("Usage: /event <date> <time> <title>") 90 | theme.print_info("Example: /event 2025-11-10 14:30 Team meeting") 91 | 92 | elif command == "today": 93 | await self.cli.show_today_events() 94 | 95 | elif command == "complete": 96 | if command_args and command_args[0].isdigit(): 97 | await self.cli.complete_event(int(command_args[0])) 98 | else: 99 | theme.print_error("Usage: /complete <event_id>") 100 | 101 | elif command == "mcp-servers": 102 | await self.cli.list_mcp_servers() 103 | 104 | elif command == "mcp-tools": 105 | server = command_args[0] if command_args else None 106 | await self.cli.list_mcp_tools(server) 107 | 108 | elif command == "mcp-call": 109 | if len(command_args) >= 3: 110 | server = command_args[0] 111 | tool = command_args[1] 112 | args_json = " ".join(command_args[2:]) 113 | await self.cli.call_mcp_tool(server, tool, args_json) 114 | else: 115 | theme.print_error("Usage: /mcp-call <server> <tool> <json_args>") 116 | theme.print_info("Example: /mcp-call filesystem read_file '{\"path\": \"/tmp/test.txt\"}'") 117 | 118 | elif command == "report": 119 | self.cli.show_agent_report() 120 | 121 | elif command == "save-report": 122 | self.cli.save_agent_report() 123 | 124 | elif command in ["exit", "quit", "bye"]: 125 | if theme.confirm("Are you sure you want to exit?"): 126 | self.cli.running = False 127 | 128 | else: 129 | theme.print_error(f"Unknown command: /{command}") 130 | theme.print_info("Type '/help' for available commands") 131 | 132 | except Exception as e: 133 | theme.print_error(f"Command error: {e}") -------------------------------------------------------------------------------- /app/JRVS APP.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { useState, useEffect, useRef } from 'react'; 4 | import { Mic, MicOff, Calendar, Clock, MessageSquare, Sparkles, Plus, X } from 'lucide-react'; 5 | 6 | // Types 7 | interface Message { 8 | id: string; 9 | sender: 'user' | 'assistant'; 10 | text: string; 11 | timestamp: Date; 12 | } 13 | 14 | interface CalendarEvent { 15 | id: string; 16 | title: string; 17 | start: string; 18 | end: string; 19 | color: string; 20 | } 21 | 22 | interface ConnectedCalendar { 23 | id: string; 24 | name: string; 25 | type: 'google' | 'outlook' | 'apple' | 'custom'; 26 | color: string; 27 | events: CalendarEvent[]; 28 | } 29 | 30 | // TechBackground Component 31 | const TechBackground = () => { 32 | return ( 33 | <div className="fixed inset-0 -z-10 overflow-hidden bg-slate-950"> 34 | {/* Grid pattern */} 35 | <div 36 | className="absolute inset-0 opacity-30" 37 | style={{ 38 | backgroundImage: ` 39 | linear-gradient(rgba(100, 116, 139, 0.5) 1px, transparent 1px), 40 | linear-gradient(90deg, rgba(100, 116, 139, 0.5) 1px, transparent 1px) 41 | `, 42 | backgroundSize: '50px 50px' 43 | }} 44 | /> 45 | 46 | {/* Central orb */} 47 | <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"> 48 | <div className="relative w-64 h-64"> 49 | {/* Outer ring */} 50 | <div 51 | className="absolute inset-0 rounded-full border-2 border-cyan-500/30" 52 | style={{ 53 | animation: 'spin 20s linear infinite' 54 | }} 55 | > 56 | <div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 w-3 h-3 bg-cyan-500 rounded-full shadow-[0_0_20px_rgba(6,182,212,0.8)]" /> 57 | <div className="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2 w-3 h-3 bg-teal-500 rounded-full shadow-[0_0_20px_rgba(20,184,166,0.8)]" /> 58 | </div> 59 | 60 | {/* Middle ring */} 61 | <div 62 | className="absolute inset-4 rounded-full border-2 border-teal-500/30" 63 | style={{ 64 | animation: 'spin 15s linear infinite reverse' 65 | }} 66 | > 67 | <div className="absolute top-1/2 left-0 -translate-x-1/2 -translate-y-1/2 w-2 h-2 bg-teal-400 rounded-full shadow-[0_0_15px_rgba(45,212,191,0.8)]" /> 68 | <div className="absolute top-1/2 right-0 translate-x-1/2 -translate-y-1/2 w-2 h-2 bg-cyan-500 rounded-full shadow-[0_0_15px_rgba(6,182,212,0.8)]" /> 69 | </div> 70 | 71 | {/* Inner core */} 72 | <div className="absolute inset-8 rounded-full bg-gradient-to-br from-cyan-500/20 to-teal-500/20 backdrop-blur-sm shadow-[0_0_60px_rgba(6,182,212,0.4)]"> 73 | <div 74 | className="absolute inset-2 rounded-full bg-gradient-to-br from-cyan-500/40 to-teal-500/40" 75 | style={{ 76 | animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite' 77 | }} 78 | /> 79 | </div> 80 | </div> 81 | </div> 82 | 83 | {/* Tech elements */} 84 | <div 85 | className="absolute top-20 left-20 w-32 h-32 border border-cyan-500/20 rotate-45" 86 | style={{ 87 | animation: 'spin 30s linear infinite' 88 | }} 89 | /> 90 | <div 91 | className="absolute bottom-32 right-32 w-24 h-24 border border-teal-500/20" 92 | style={{ 93 | animation: 'spin 25s linear infinite reverse' 94 | }} 95 | /> 96 | 97 | {/* Circuit lines */} 98 | <svg className="absolute inset-0 w-full h-full opacity-20" xmlns="http://www.w3.org/2000/svg"> 99 | <line x1="0" y1="50%" x2="30%" y2="50%" stroke="rgb(6, 182, 212)" strokeWidth="2" /> 100 | <line x1="70%" y1="50%" x2="100%" y2="50%" stroke="rgb(6, 182, 212)" strokeWidth="2" /> 101 | <line x1="50%" y1="0" x2="50%" y2="30%" stroke="rgb(20, 184, 166)" strokeWidth="2" /> 102 | <line x1="50%" y1="70%" x2="50%" y2="100%" stroke="rgb(20, 184, 166)" strokeWidth="2" /> 103 | </svg> 104 | </div> 105 | ); 106 | }; 107 | 108 | const AISchedulingPlatform = () => { 109 | // State Management 110 | const [isRecording, setIsRecording] = useState(false); 111 | const [userInput, setUserInput] = useState(''); 112 | const [messages, setMessages] = useState<Message[]>([ 113 | { 114 | id: '1', 115 | sender: 'assistant', 116 | text: "Hi! I'm your AI scheduling assistant. Connect your calendars on the right, and I'll help you find the perfect meeting times. What would you like to schedule?", 117 | timestamp: new Date() 118 | } 119 | ]); 120 | const [calendars, setCalendars] = useState<ConnectedCalendar[]>([]); 121 | const [showAddCalendar, setShowAddCalendar] = useState(false); 122 | const [selectedView, setSelectedView] = useState<'week' | 'day'>('week'); 123 | const messagesEndRef = useRef<HTMLDivElement>(null); 124 | 125 | // Calendar providers 126 | const calendarProviders = [ 127 | { type: 'google' as const, name: 'Google Calendar', color: 'bg-blue-500' }, 128 | { type: 'outlook' as const, name: 'Outlook Calendar', color: 'bg-cyan-500' }, 129 | { type: 'apple' as const, name: 'Apple Calendar', color: 'bg-gray-500' }, 130 | { type: 'custom' as const, name: 'Custom Calendar', color: 'bg-purple-500' } 131 | ]; 132 | }; 133 | 134 | export default AISchedulingPlatform; -------------------------------------------------------------------------------- /app/components/AISchedulingPlatform.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { useState, useEffect, useRef } from 'react'; 4 | import { Mic, MicOff, Calendar, Clock, MessageSquare, Sparkles, Plus, X } from 'lucide-react'; 5 | 6 | // Types 7 | interface Message { 8 | id: string; 9 | sender: 'user' | 'assistant'; 10 | text: string; 11 | timestamp: Date; 12 | } 13 | 14 | interface CalendarEvent { 15 | id: string; 16 | title: string; 17 | start: string; 18 | end: string; 19 | color: string; 20 | } 21 | 22 | interface ConnectedCalendar { 23 | id: string; 24 | name: string; 25 | type: 'google' | 'outlook' | 'apple' | 'custom'; 26 | color: string; 27 | events: CalendarEvent[]; 28 | } 29 | 30 | // TechBackground Component 31 | const TechBackground = () => { 32 | return ( 33 | <div className="fixed inset-0 -z-10 overflow-hidden bg-slate-950"> 34 | {/* Grid pattern */} 35 | <div 36 | className="absolute inset-0 opacity-30" 37 | style={{ 38 | backgroundImage: ` 39 | linear-gradient(rgba(100, 116, 139, 0.5) 1px, transparent 1px), 40 | linear-gradient(90deg, rgba(100, 116, 139, 0.5) 1px, transparent 1px) 41 | `, 42 | backgroundSize: '50px 50px' 43 | }} 44 | /> 45 | 46 | {/* Central orb */} 47 | <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"> 48 | <div className="relative w-64 h-64"> 49 | {/* Outer ring */} 50 | <div 51 | className="absolute inset-0 rounded-full border-2 border-cyan-500/30" 52 | style={{ 53 | animation: 'spin 20s linear infinite' 54 | }} 55 | > 56 | <div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 w-3 h-3 bg-cyan-500 rounded-full shadow-[0_0_20px_rgba(6,182,212,0.8)]" /> 57 | <div className="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2 w-3 h-3 bg-teal-500 rounded-full shadow-[0_0_20px_rgba(20,184,166,0.8)]" /> 58 | </div> 59 | 60 | {/* Middle ring */} 61 | <div 62 | className="absolute inset-4 rounded-full border-2 border-teal-500/30" 63 | style={{ 64 | animation: 'spin 15s linear infinite reverse' 65 | }} 66 | > 67 | <div className="absolute top-1/2 left-0 -translate-x-1/2 -translate-y-1/2 w-2 h-2 bg-teal-400 rounded-full shadow-[0_0_15px_rgba(45,212,191,0.8)]" /> 68 | <div className="absolute top-1/2 right-0 translate-x-1/2 -translate-y-1/2 w-2 h-2 bg-cyan-500 rounded-full shadow-[0_0_15px_rgba(6,182,212,0.8)]" /> 69 | </div> 70 | 71 | {/* Inner core */} 72 | <div className="absolute inset-8 rounded-full bg-gradient-to-br from-cyan-500/20 to-teal-500/20 backdrop-blur-sm shadow-[0_0_60px_rgba(6,182,212,0.4)]"> 73 | <div 74 | className="absolute inset-2 rounded-full bg-gradient-to-br from-cyan-500/40 to-teal-500/40" 75 | style={{ 76 | animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite' 77 | }} 78 | /> 79 | </div> 80 | </div> 81 | </div> 82 | 83 | {/* Tech elements */} 84 | <div 85 | className="absolute top-20 left-20 w-32 h-32 border border-cyan-500/20 rotate-45" 86 | style={{ 87 | animation: 'spin 30s linear infinite' 88 | }} 89 | /> 90 | <div 91 | className="absolute bottom-32 right-32 w-24 h-24 border border-teal-500/20" 92 | style={{ 93 | animation: 'spin 25s linear infinite reverse' 94 | }} 95 | /> 96 | 97 | {/* Circuit lines */} 98 | <svg className="absolute inset-0 w-full h-full opacity-20" xmlns="http://www.w3.org/2000/svg"> 99 | <line x1="0" y1="50%" x2="30%" y2="50%" stroke="rgb(6, 182, 212)" strokeWidth="2" /> 100 | <line x1="70%" y1="50%" x2="100%" y2="50%" stroke="rgb(6, 182, 212)" strokeWidth="2" /> 101 | <line x1="50%" y1="0" x2="50%" y2="30%" stroke="rgb(20, 184, 166)" strokeWidth="2" /> 102 | <line x1="50%" y1="70%" x2="50%" y2="100%" stroke="rgb(20, 184, 166)" strokeWidth="2" /> 103 | </svg> 104 | </div> 105 | ); 106 | }; 107 | 108 | const AISchedulingPlatform = () => { 109 | // State Management 110 | const [isRecording, setIsRecording] = useState(false); 111 | const [userInput, setUserInput] = useState(''); 112 | const [messages, setMessages] = useState<Message[]>([ 113 | { 114 | id: '1', 115 | sender: 'assistant', 116 | text: "Hi! I'm your AI scheduling assistant. Connect your calendars on the right, and I'll help you find the perfect meeting times. What would you like to schedule?", 117 | timestamp: new Date() 118 | } 119 | ]); 120 | const [calendars, setCalendars] = useState<ConnectedCalendar[]>([]); 121 | const [showAddCalendar, setShowAddCalendar] = useState(false); 122 | const [selectedView, setSelectedView] = useState<'week' | 'day'>('week'); 123 | const messagesEndRef = useRef<HTMLDivElement>(null); 124 | 125 | // Calendar providers 126 | const calendarProviders = [ 127 | { type: 'google' as const, name: 'Google Calendar', color: 'bg-blue-500' }, 128 | { type: 'outlook' as const, name: 'Outlook Calendar', color: 'bg-cyan-500' }, 129 | { type: 'apple' as const, name: 'Apple Calendar', color: 'bg-gray-500' }, 130 | { type: 'custom' as const, name: 'Custom Calendar', color: 'bg-purple-500' } 131 | ]; 132 | }; 133 | 134 | export default AISchedulingPlatform; -------------------------------------------------------------------------------- /docs/MCP_CLIENT_GUIDE.md: -------------------------------------------------------------------------------- 1 | # JRVS MCP Client Guide 2 | 3 | JRVS can now act as an **MCP Client** to connect to MCP servers and access their tools! This makes JRVS more powerful by letting it use external services and capabilities. 4 | 5 | ## What This Means 6 | 7 | Instead of just *being* a tool for others, JRVS can now *use* tools from: 8 | - File systems 9 | - Databases (PostgreSQL, SQLite) 10 | - APIs (GitHub, GitLab, Slack, etc.) 11 | - Web search 12 | - Memory/notes systems 13 | - Custom MCP servers you build 14 | 15 | ## Quick Start 16 | 17 | ### 1. Configure MCP Servers 18 | 19 | Edit `mcp_gateway/client_config.json` to add servers you want to connect to: 20 | 21 | ```json 22 | { 23 | "mcpServers": { 24 | "filesystem": { 25 | "command": "npx", 26 | "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/xmanz"], 27 | "description": "File operations" 28 | }, 29 | "memory": { 30 | "command": "npx", 31 | "args": ["-y", "@modelcontextprotocol/server-memory"], 32 | "description": "Persistent memory" 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | ### 2. Start JRVS 39 | 40 | ```bash 41 | python main.py 42 | ``` 43 | 44 | JRVS will automatically connect to configured servers on startup. 45 | 46 | ### 3. Use MCP Commands 47 | 48 | In JRVS, use these commands: 49 | 50 | ```bash 51 | # List connected servers 52 | /mcp-servers 53 | 54 | # List all available tools 55 | /mcp-tools 56 | 57 | # List tools from specific server 58 | /mcp-tools filesystem 59 | 60 | # Call a tool directly 61 | /mcp-call filesystem read_file '{"path": "/tmp/test.txt"}' 62 | ``` 63 | 64 | ## Available MCP Servers 65 | 66 | Here are some official MCP servers you can connect to: 67 | 68 | ### Filesystem 69 | ```json 70 | { 71 | "filesystem": { 72 | "command": "npx", 73 | "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allow"] 74 | } 75 | } 76 | ``` 77 | Tools: read_file, write_file, list_directory, search_files, etc. 78 | 79 | ### GitHub 80 | ```json 81 | { 82 | "github": { 83 | "command": "npx", 84 | "args": ["-y", "@modelcontextprotocol/server-github"], 85 | "env": { 86 | "GITHUB_PERSONAL_ACCESS_TOKEN": "your_token" 87 | } 88 | } 89 | } 90 | ``` 91 | Tools: create_issue, create_pr, search_repos, get_file_contents, etc. 92 | 93 | ### PostgreSQL 94 | ```json 95 | { 96 | "postgres": { 97 | "command": "npx", 98 | "args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/dbname"] 99 | } 100 | } 101 | ``` 102 | Tools: query, list_tables, describe_table, etc. 103 | 104 | ### Brave Search 105 | ```json 106 | { 107 | "brave-search": { 108 | "command": "npx", 109 | "args": ["-y", "@modelcontextprotocol/server-brave-search"], 110 | "env": { 111 | "BRAVE_API_KEY": "your_api_key" 112 | } 113 | } 114 | } 115 | ``` 116 | Tools: brave_web_search, brave_local_search 117 | 118 | ### Memory (Persistent Notes) 119 | ```json 120 | { 121 | "memory": { 122 | "command": "npx", 123 | "args": ["-y", "@modelcontextprotocol/server-memory"] 124 | } 125 | } 126 | ``` 127 | Tools: create_memory, search_memories, etc. 128 | 129 | ### Slack 130 | ```json 131 | { 132 | "slack": { 133 | "command": "npx", 134 | "args": ["-y", "@modelcontextprotocol/server-slack"], 135 | "env": { 136 | "SLACK_BOT_TOKEN": "xoxb-your-token", 137 | "SLACK_TEAM_ID": "T01234567" 138 | } 139 | } 140 | } 141 | ``` 142 | Tools: send_message, list_channels, get_channel_history, etc. 143 | 144 | ## Examples 145 | 146 | ### Read a file 147 | ``` 148 | /mcp-call filesystem read_file '{"path": "/home/xmanz/notes.txt"}' 149 | ``` 150 | 151 | ### Search the web (with Brave) 152 | ``` 153 | /mcp-call brave-search brave_web_search '{"query": "Python async programming", "count": 5}' 154 | ``` 155 | 156 | ### Create GitHub issue 157 | ``` 158 | /mcp-call github create_issue '{"owner": "user", "repo": "project", "title": "Bug fix", "body": "Description"}' 159 | ``` 160 | 161 | ### Store a memory 162 | ``` 163 | /mcp-call memory create_memory '{"content": "Remember to backup database every Sunday"}' 164 | ``` 165 | 166 | ## Natural Language Integration (Coming Soon) 167 | 168 | In the future, JRVS will automatically detect when to use MCP tools based on your natural language requests: 169 | 170 | - "Read the file at /tmp/data.txt" → Uses filesystem server 171 | - "Search GitHub for React repositories" → Uses GitHub server 172 | - "Remember that I prefer Python 3.11" → Uses memory server 173 | 174 | ## Troubleshooting 175 | 176 | ### Server won't connect 177 | - Make sure Node.js and npm are installed: `node --version` 178 | - Try installing the server manually: `npx -y @modelcontextprotocol/server-NAME` 179 | - Check server logs in JRVS startup output 180 | 181 | ### API key errors 182 | - Make sure API keys are set in the `env` field 183 | - Some servers need tokens from their respective platforms: 184 | - GitHub: https://github.com/settings/tokens 185 | - Brave Search: https://brave.com/search/api/ 186 | - Slack: https://api.slack.com/apps 187 | 188 | ### Tool not found 189 | - Run `/mcp-tools` to see available tools 190 | - Each server exposes different tools - check the server's documentation 191 | 192 | ## Building Your Own MCP Server 193 | 194 | You can build custom MCP servers for JRVS! See: 195 | - https://modelcontextprotocol.io/ 196 | - https://github.com/modelcontextprotocol/servers 197 | 198 | Then add it to `mcp_gateway/client_config.json`: 199 | 200 | ```json 201 | { 202 | "my-custom-server": { 203 | "command": "python", 204 | "args": ["/path/to/my_server.py"] 205 | } 206 | } 207 | ``` 208 | 209 | ## Resources 210 | 211 | - MCP Specification: https://modelcontextprotocol.io/ 212 | - Official Servers: https://github.com/modelcontextprotocol/servers 213 | - JRVS MCP Server (for others to use JRVS): `mcp_gateway/server.py` 214 | -------------------------------------------------------------------------------- /lib/firebase-utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Firebase Utility Functions 3 | * Helper functions for Firebase operations 4 | */ 5 | 6 | import { auth, db } from './firebase'; 7 | import { 8 | User, 9 | signInWithEmailAndPassword, 10 | createUserWithEmailAndPassword, 11 | signOut, 12 | onAuthStateChanged, 13 | GoogleAuthProvider, 14 | signInWithPopup 15 | } from 'firebase/auth'; 16 | import { 17 | collection, 18 | doc, 19 | getDoc, 20 | setDoc, 21 | updateDoc, 22 | deleteDoc, 23 | getDocs, 24 | query, 25 | where, 26 | Timestamp 27 | } from 'firebase/firestore'; 28 | 29 | // Authentication helpers 30 | export const authHelpers = { 31 | /** 32 | * Sign in with email and password 33 | */ 34 | signIn: async (email: string, password: string) => { 35 | try { 36 | const userCredential = await signInWithEmailAndPassword(auth, email, password); 37 | return { user: userCredential.user, error: null }; 38 | } catch (error: any) { 39 | return { user: null, error: error.message }; 40 | } 41 | }, 42 | 43 | /** 44 | * Sign up with email and password 45 | */ 46 | signUp: async (email: string, password: string) => { 47 | try { 48 | const userCredential = await createUserWithEmailAndPassword(auth, email, password); 49 | return { user: userCredential.user, error: null }; 50 | } catch (error: any) { 51 | return { user: null, error: error.message }; 52 | } 53 | }, 54 | 55 | /** 56 | * Sign in with Google 57 | */ 58 | signInWithGoogle: async () => { 59 | try { 60 | const provider = new GoogleAuthProvider(); 61 | const userCredential = await signInWithPopup(auth, provider); 62 | return { user: userCredential.user, error: null }; 63 | } catch (error: any) { 64 | return { user: null, error: error.message }; 65 | } 66 | }, 67 | 68 | /** 69 | * Sign out 70 | */ 71 | signOut: async () => { 72 | try { 73 | await signOut(auth); 74 | return { error: null }; 75 | } catch (error: any) { 76 | return { error: error.message }; 77 | } 78 | }, 79 | 80 | /** 81 | * Get current user 82 | */ 83 | getCurrentUser: (): User | null => { 84 | return auth.currentUser; 85 | }, 86 | 87 | /** 88 | * Listen to auth state changes 89 | */ 90 | onAuthStateChange: (callback: (user: User | null) => void) => { 91 | return onAuthStateChanged(auth, callback); 92 | } 93 | }; 94 | 95 | // Firestore helpers 96 | export const firestoreHelpers = { 97 | /** 98 | * Create a document 99 | */ 100 | createDocument: async <T>( 101 | collectionName: string, 102 | documentId: string, 103 | data: T 104 | ) => { 105 | try { 106 | const docRef = doc(db, collectionName, documentId); 107 | await setDoc(docRef, { 108 | ...data, 109 | createdAt: Timestamp.now(), 110 | updatedAt: Timestamp.now() 111 | }); 112 | return { success: true, error: null }; 113 | } catch (error: any) { 114 | return { success: false, error: error.message }; 115 | } 116 | }, 117 | 118 | /** 119 | * Get a document 120 | */ 121 | getDocument: async <T>(collectionName: string, documentId: string) => { 122 | try { 123 | const docRef = doc(db, collectionName, documentId); 124 | const docSnap = await getDoc(docRef); 125 | 126 | if (docSnap.exists()) { 127 | return { data: docSnap.data() as T, error: null }; 128 | } else { 129 | return { data: null, error: 'Document not found' }; 130 | } 131 | } catch (error: any) { 132 | return { data: null, error: error.message }; 133 | } 134 | }, 135 | 136 | /** 137 | * Update a document 138 | */ 139 | updateDocument: async <T>( 140 | collectionName: string, 141 | documentId: string, 142 | data: Partial<T> 143 | ) => { 144 | try { 145 | const docRef = doc(db, collectionName, documentId); 146 | await updateDoc(docRef, { 147 | ...data, 148 | updatedAt: Timestamp.now() 149 | }); 150 | return { success: true, error: null }; 151 | } catch (error: any) { 152 | return { success: false, error: error.message }; 153 | } 154 | }, 155 | 156 | /** 157 | * Delete a document 158 | */ 159 | deleteDocument: async (collectionName: string, documentId: string) => { 160 | try { 161 | const docRef = doc(db, collectionName, documentId); 162 | await deleteDoc(docRef); 163 | return { success: true, error: null }; 164 | } catch (error: any) { 165 | return { success: false, error: error.message }; 166 | } 167 | }, 168 | 169 | /** 170 | * Get all documents from a collection 171 | */ 172 | getDocuments: async <T>(collectionName: string) => { 173 | try { 174 | const querySnapshot = await getDocs(collection(db, collectionName)); 175 | const documents = querySnapshot.docs.map(doc => ({ 176 | id: doc.id, 177 | ...doc.data() 178 | })) as T[]; 179 | return { data: documents, error: null }; 180 | } catch (error: any) { 181 | return { data: [], error: error.message }; 182 | } 183 | }, 184 | 185 | /** 186 | * Query documents with conditions 187 | */ 188 | queryDocuments: async <T>( 189 | collectionName: string, 190 | field: string, 191 | operator: any, 192 | value: any 193 | ) => { 194 | try { 195 | const q = query(collection(db, collectionName), where(field, operator, value)); 196 | const querySnapshot = await getDocs(q); 197 | const documents = querySnapshot.docs.map(doc => ({ 198 | id: doc.id, 199 | ...doc.data() 200 | })) as T[]; 201 | return { data: documents, error: null }; 202 | } catch (error: any) { 203 | return { data: [], error: error.message }; 204 | } 205 | } 206 | }; 207 | 208 | // Collection names (for type safety) 209 | export const COLLECTIONS = { 210 | USERS: 'users', 211 | MESSAGES: 'messages', 212 | CALENDARS: 'calendars', 213 | EVENTS: 'events' 214 | } as const; 215 | 216 | -------------------------------------------------------------------------------- /docs/SETUP_COMPLETE.md: -------------------------------------------------------------------------------- 1 | # ✅ JRVS Setup Complete! 2 | 3 | ## 🎉 What's Ready 4 | 5 | ### ✅ Core Systems 6 | - **Python 3.13.9** - Installed and working 7 | - **All dependencies** - Installed from requirements.txt 8 | - **Database** - Initialized (SQLite) 9 | - **Calendar** - Ready with events system 10 | - **Ollama** - Running with 12 models available 11 | 12 | ### ✅ MCP Integration 13 | - **Filesystem Server** - Ready (access to JRVS, Documents, Downloads, /tmp) 14 | - **Memory Server** - Ready (persistent notes/memories) 15 | - **Brave Search** - Configured (needs API key to activate) 16 | 17 | ### ✅ Intelligent Agent 18 | - **Auto tool detection** - JRVS picks the right tools 19 | - **Action logging** - Everything timestamped 20 | - **Report generation** - View what JRVS does 21 | 22 | ### ✅ Features Active 23 | - 📅 **Calendar** - ASCII view + natural language events 24 | - 🧠 **RAG** - Vector search with FAISS + BERT 25 | - 🌐 **Web Scraper** - BeautifulSoup integration 26 | - 🔧 **MCP Client** - Connect to external tools 27 | - 🤖 **AI Agent** - Automatic tool selection 28 | - 🎨 **Themes** - Matrix, Cyberpunk, Minimal 29 | 30 | --- 31 | 32 | ## 🚀 How to Start 33 | 34 | ### Simple Start 35 | ```bash 36 | ./start_jrvs.sh 37 | ``` 38 | 39 | ### Or manually 40 | ```bash 41 | python main.py 42 | ``` 43 | 44 | ### With options 45 | ```bash 46 | python main.py --theme cyberpunk 47 | python main.py --model gemma3:12b 48 | python main.py --no-banner 49 | ``` 50 | 51 | --- 52 | 53 | ## 📖 Quick Start Guide 54 | 55 | ### 1. Launch JRVS 56 | ```bash 57 | ./start_jrvs.sh 58 | ``` 59 | 60 | ### 2. Try Some Commands 61 | ```bash 62 | # Get help 63 | /help 64 | 65 | # Check your calendar 66 | /month 67 | 68 | # Add an event 69 | add meeting tomorrow at 2pm 70 | 71 | # View connected MCP servers 72 | /mcp-servers 73 | 74 | # See available tools 75 | /mcp-tools 76 | 77 | # Switch AI model 78 | /models 79 | /switch gemma2:2b 80 | ``` 81 | 82 | ### 3. Chat Naturally 83 | JRVS automatically uses tools when needed! 84 | 85 | ```bash 86 | # File operations (auto-uses filesystem tools) 87 | read the file /tmp/test.txt 88 | list files in my JRVS directory 89 | 90 | # Memory (auto-uses memory tools) 91 | remember that I prefer Python 3.11 92 | what do you remember about me? 93 | 94 | # Calendar (built-in) 95 | add event study time tomorrow at 10 am 96 | show me this month's calendar 97 | 98 | # General chat (uses RAG + Ollama) 99 | what is machine learning? 100 | explain async programming in python 101 | ``` 102 | 103 | --- 104 | 105 | ## 📊 Current Configuration 106 | 107 | ### Available Ollama Models 108 | ``` 109 | ✓ gemma2:2b (fast, small) 110 | ✓ gemma3:12b (balanced) 111 | ✓ deepseek-r1:14b (reasoning) 112 | ✓ deepseek-r1:32b (powerful) 113 | ✓ codestral:22b (coding) 114 | ✓ mistral-small:22b (general) 115 | ... and 6 more custom models 116 | ``` 117 | 118 | ### MCP Servers 119 | ``` 120 | ✓ filesystem - Read/write files in allowed paths 121 | ✓ memory - Persistent notes across sessions 122 | ⚠ brave-search - Needs API key (see BRAVE_SEARCH_SETUP.md) 123 | ``` 124 | 125 | ### File Locations 126 | ``` 127 | data/jarvis.db - Main database 128 | data/mcp_logs/ - Tool usage logs 129 | data/embeddings/ - RAG vector store 130 | mcp_gateway/client_config.json - MCP server configuration 131 | ``` 132 | 133 | --- 134 | 135 | ## 🔧 Optional Setup 136 | 137 | ### Enable Brave Web Search 138 | 1. Get API key from https://brave.com/search/api/ 139 | 2. Edit `mcp_gateway/client_config.json` 140 | 3. Add your key to `"BRAVE_API_KEY": ""` 141 | 4. Restart JRVS 142 | 143 | See full guide: `BRAVE_SEARCH_SETUP.md` 144 | 145 | ### Add More MCP Servers 146 | Edit `mcp_gateway/client_config.json` to add: 147 | - **github** - GitHub API (issues, PRs, repos) 148 | - **postgres** - Database access 149 | - **slack** - Send messages 150 | - **sqlite** - Database queries 151 | - Custom servers you build 152 | 153 | See options: `MCP_CLIENT_GUIDE.md` 154 | 155 | --- 156 | 157 | ## 📚 Documentation 158 | 159 | - **README.md** - Main documentation 160 | - **MCP_CLIENT_GUIDE.md** - MCP client usage 161 | - **INTELLIGENT_AGENT_GUIDE.md** - How the AI agent works 162 | - **BRAVE_SEARCH_SETUP.md** - Web search setup 163 | - **WHATS_NEW.md** - Recent features 164 | 165 | --- 166 | 167 | ## 🎯 What to Try First 168 | 169 | ### Beginner 170 | ```bash 171 | /help # See all commands 172 | /month # View calendar 173 | add event lunch tomorrow 12pm # Natural language 174 | what is Python? # Chat with AI 175 | ``` 176 | 177 | ### Intermediate 178 | ```bash 179 | /mcp-servers # See connected tools 180 | /mcp-tools filesystem # Browse filesystem tools 181 | remember my birthday is Jan 15 # Use memory 182 | /report # See tool usage 183 | ``` 184 | 185 | ### Advanced 186 | ```bash 187 | # Let JRVS automatically use tools 188 | read the file config.py and explain it 189 | search my documents for "python" 190 | remember all my preferences for future sessions 191 | 192 | # Manual tool calls 193 | /mcp-call filesystem read_file '{"path": "/tmp/test.txt"}' 194 | /mcp-call memory create_memory '{"content": "User likes dark mode"}' 195 | 196 | # Generate activity report 197 | /save-report 198 | ``` 199 | 200 | --- 201 | 202 | ## 🐛 Troubleshooting 203 | 204 | ### Ollama connection failed 205 | ```bash 206 | # Check if running 207 | systemctl status ollama 208 | # Or 209 | pgrep -f "ollama serve" 210 | 211 | # Start if needed 212 | ollama serve 213 | ``` 214 | 215 | ### MCP server won't connect 216 | - Check Node.js: `node --version` 217 | - Install server: `npx -y @modelcontextprotocol/server-NAME` 218 | - Check config: `mcp_gateway/client_config.json` 219 | 220 | ### Python errors 221 | ```bash 222 | # Reinstall dependencies 223 | pip install -r requirements.txt 224 | 225 | # Check Python version (need 3.8+) 226 | python --version 227 | ``` 228 | 229 | --- 230 | 231 | ## 🎊 You're All Set! 232 | 233 | JRVS is fully configured and ready to use. Start chatting and let the AI agent handle the tools for you! 234 | 235 | ```bash 236 | ./start_jrvs.sh 237 | ``` 238 | 239 | Need help? Type `/help` in JRVS or check the documentation files. 240 | 241 | **Happy chatting! 🤖** 242 | -------------------------------------------------------------------------------- /mcp/shutdown.py: -------------------------------------------------------------------------------- 1 | """ 2 | Graceful shutdown handler for JRVS MCP Server 3 | 4 | Handles SIGTERM/SIGINT signals and ensures clean shutdown 5 | of all components with proper cleanup. 6 | """ 7 | 8 | import signal 9 | import asyncio 10 | import sys 11 | from typing import List, Callable, Optional 12 | from datetime import datetime 13 | import logging 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | 18 | class ShutdownHandler: 19 | """Handle graceful shutdown of the server""" 20 | 21 | def __init__(self): 22 | self._shutdown_requested = False 23 | self._cleanup_tasks: List[tuple] = [] # (name, async_func) 24 | self._shutdown_timeout = 30 # seconds 25 | self._start_time: Optional[datetime] = None 26 | 27 | def register_cleanup(self, name: str, cleanup_func: Callable): 28 | """ 29 | Register a cleanup function to run on shutdown 30 | 31 | Args: 32 | name: Name of the cleanup task 33 | cleanup_func: Async function to call on shutdown 34 | """ 35 | self._cleanup_tasks.append((name, cleanup_func)) 36 | logger.debug(f"Registered shutdown cleanup: {name}") 37 | 38 | def setup_signal_handlers(self): 39 | """Setup signal handlers for graceful shutdown""" 40 | # Handle SIGTERM and SIGINT 41 | signal.signal(signal.SIGTERM, self._signal_handler) 42 | signal.signal(signal.SIGINT, self._signal_handler) 43 | 44 | logger.info("Signal handlers registered for graceful shutdown") 45 | 46 | def _signal_handler(self, signum, frame): 47 | """Handle shutdown signals""" 48 | signal_name = signal.Signals(signum).name 49 | logger.info(f"Received {signal_name} signal, initiating graceful shutdown...") 50 | 51 | self._shutdown_requested = True 52 | 53 | # For asyncio, we need to schedule the shutdown 54 | # This will be picked up by the main event loop 55 | asyncio.create_task(self.shutdown()) 56 | 57 | async def shutdown(self): 58 | """Execute graceful shutdown""" 59 | if self._start_time is not None: 60 | logger.warning("Shutdown already in progress") 61 | return 62 | 63 | self._start_time = datetime.utcnow() 64 | 65 | logger.info("=" * 70) 66 | logger.info("GRACEFUL SHUTDOWN INITIATED") 67 | logger.info("=" * 70) 68 | 69 | # Run cleanup tasks 70 | success_count = 0 71 | failed_count = 0 72 | 73 | for name, cleanup_func in self._cleanup_tasks: 74 | try: 75 | logger.info(f"Running cleanup: {name}") 76 | 77 | # Run with timeout 78 | await asyncio.wait_for( 79 | cleanup_func(), 80 | timeout=10.0 # 10 seconds per cleanup task 81 | ) 82 | 83 | logger.info(f"✓ Cleanup completed: {name}") 84 | success_count += 1 85 | 86 | except asyncio.TimeoutError: 87 | logger.error(f"✗ Cleanup timeout: {name}") 88 | failed_count += 1 89 | 90 | except Exception as e: 91 | logger.error(f"✗ Cleanup failed: {name} - {e}", exc_info=True) 92 | failed_count += 1 93 | 94 | # Calculate shutdown time 95 | shutdown_duration = (datetime.utcnow() - self._start_time).total_seconds() 96 | 97 | logger.info("=" * 70) 98 | logger.info("SHUTDOWN SUMMARY") 99 | logger.info("=" * 70) 100 | logger.info(f"Successful cleanups: {success_count}") 101 | logger.info(f"Failed cleanups: {failed_count}") 102 | logger.info(f"Shutdown duration: {shutdown_duration:.2f}s") 103 | logger.info("=" * 70) 104 | 105 | logger.info("Server shutdown complete") 106 | 107 | # Exit 108 | sys.exit(0) 109 | 110 | def is_shutting_down(self) -> bool: 111 | """Check if shutdown has been requested""" 112 | return self._shutdown_requested 113 | 114 | 115 | # Global shutdown handler 116 | shutdown_handler = ShutdownHandler() 117 | 118 | 119 | async def cleanup_database(): 120 | """Cleanup database connections""" 121 | try: 122 | from core.database import db 123 | if hasattr(db, 'close'): 124 | await db.close() 125 | logger.info("Database connections closed") 126 | except Exception as e: 127 | logger.error(f"Database cleanup error: {e}") 128 | 129 | 130 | async def cleanup_cache(): 131 | """Cleanup cache""" 132 | try: 133 | from .cache import cache_manager 134 | cache_manager.clear_all() 135 | logger.info("Cache cleared") 136 | except Exception as e: 137 | logger.error(f"Cache cleanup error: {e}") 138 | 139 | 140 | async def cleanup_ollama(): 141 | """Cleanup Ollama client""" 142 | try: 143 | from llm.ollama_client import ollama_client 144 | # Close any open connections 145 | if hasattr(ollama_client, 'close'): 146 | await ollama_client.close() 147 | logger.info("Ollama client closed") 148 | except Exception as e: 149 | logger.error(f"Ollama cleanup error: {e}") 150 | 151 | 152 | async def cleanup_mcp_client(): 153 | """Cleanup MCP client connections""" 154 | try: 155 | from .client import mcp_client 156 | if mcp_client.initialized: 157 | await mcp_client.cleanup() 158 | logger.info("MCP client connections closed") 159 | except Exception as e: 160 | logger.error(f"MCP client cleanup error: {e}") 161 | 162 | 163 | async def save_metrics(): 164 | """Save metrics before shutdown""" 165 | try: 166 | from .metrics import metrics 167 | summary = metrics.get_summary() 168 | logger.info(f"Final metrics: {summary}") 169 | except Exception as e: 170 | logger.error(f"Metrics save error: {e}") 171 | 172 | 173 | def register_default_cleanup_tasks(): 174 | """Register all default cleanup tasks""" 175 | shutdown_handler.register_cleanup("save_metrics", save_metrics) 176 | shutdown_handler.register_cleanup("database", cleanup_database) 177 | shutdown_handler.register_cleanup("cache", cleanup_cache) 178 | shutdown_handler.register_cleanup("ollama", cleanup_ollama) 179 | shutdown_handler.register_cleanup("mcp_client", cleanup_mcp_client) 180 | -------------------------------------------------------------------------------- /ENTERPRISE.md: -------------------------------------------------------------------------------- 1 | # AI Tools for Teams That Can't Use ChatGPT 2 | 3 | **JRVS provides ChatGPT-like interface for your own local AI models. Keep data on-premise. Stay compliant. Full control.** 4 | 5 | --- 6 | 7 | ## The Problem 8 | 9 | Your team needs AI capabilities but ChatGPT violates data privacy policies: 10 | - **HIPAA** compliance required for healthcare data 11 | - **SOC2** audit requirements prevent external APIs 12 | - **GDPR** mandates data residency in EU 13 | - **Internal security** policies prohibit cloud AI services 14 | - **Client confidentiality** agreements restrict third-party processing 15 | 16 | **You need AI tooling that works within your compliance framework.** 17 | 18 | --- 19 | 20 | ## The Solution 21 | 22 | Deploy JRVS on your infrastructure. **No data leaves your servers.** Use any models. Full compliance. 23 | 24 | JRVS is backend-agnostic AI interface that connects to your own local AI models (Ollama, LM Studio, vLLM, etc.). Your team gets ChatGPT-like capabilities while you maintain complete control over data, models, and infrastructure. 25 | 26 | --- 27 | 28 | ## Key Benefits 29 | 30 | ### 🔒 **Data Privacy** 31 | All data stays on your infrastructure. Zero external API calls. Complete control over where information is processed and stored. 32 | 33 | ### 🏢 **Enterprise-Ready** 34 | SSO/SAML integration, team management, audit logs, compliance documentation support. Built for regulated industries. 35 | 36 | ### ⚡ **Flexible** 37 | Backend-agnostic architecture. Works with any local AI: Ollama, LM Studio, vLLM, LocalAI, and more. Switch models without changing your workflow. 38 | 39 | ### 🛡️ **Compliant** 40 | HIPAA, SOC2, GDPR-friendly. You control the data, models, and deployment. Self-hosted means you own the compliance story. 41 | 42 | ### 🚀 **Fast Deployment** 43 | Original creator provides implementation support. Proven with 175+ GitHub stars and senior developer contributions within 3 days of launch. 44 | 45 | --- 46 | 47 | ## Pricing & Tiers 48 | 49 | | Feature | Open Source (Free) | Hosted ($79/mo) | Enterprise (Custom) | 50 | |---------|-------------------|-----------------|---------------------| 51 | | **Self-hosted** | ✓ | Managed by us | On your infrastructure | 52 | | **Backend flexibility** | ✓ | ✓ | ✓ | 53 | | **Community support** | ✓ | ✓ | ✓ | 54 | | **Automatic updates** | - | ✓ | ✓ | 55 | | **Team management** | - | Basic | Advanced | 56 | | **SSO/SAML** | - | - | ✓ | 57 | | **Audit logs** | - | - | ✓ | 58 | | **SLA & Priority support** | - | - | ✓ | 59 | | **Custom deployment** | - | - | ✓ | 60 | | **Compliance consulting** | - | - | ✓ | 61 | 62 | ### Open Source 63 | **Free forever.** Self-hosted, community-supported. Full access to codebase. Deploy on your infrastructure, use any backend, configure as needed. 64 | 65 | ### Hosted 66 | **$79/month.** We manage hosting, updates, and basic support. For individuals and small teams who want simplicity without infrastructure overhead. 67 | 68 | ### Enterprise 69 | **$299-999/month** (based on team size). Everything in Open Source + SSO, audit logs, SLA, custom deployment, compliance support, priority access to original creator. 70 | 71 | **Enterprise includes:** 72 | - SSO/SAML integration (Okta, Azure AD, Google Workspace) 73 | - Comprehensive audit logging 74 | - Compliance documentation support (HIPAA, SOC2, GDPR) 75 | - Custom deployment on your infrastructure 76 | - Priority support from original creator 77 | - SLA guarantees 78 | - Team training and onboarding 79 | - Custom feature development 80 | 81 | --- 82 | 83 | ## Use Cases 84 | 85 | **Healthcare Organizations** 86 | Deploy JRVS for clinical documentation assistance, research queries, and administrative automation while maintaining HIPAA compliance. 87 | 88 | **Financial Services** 89 | Enable AI-powered analysis and customer service tools within strict regulatory frameworks and data residency requirements. 90 | 91 | **Legal Firms** 92 | Provide attorneys with AI research and drafting assistance without violating client confidentiality or attorney-client privilege. 93 | 94 | **Government Contractors** 95 | Deploy AI tooling in secure environments with security clearance requirements and air-gapped networks. 96 | 97 | **Enterprise IT** 98 | Roll out AI capabilities across development and operations teams while respecting data sovereignty policies and internal security standards. 99 | 100 | **Privacy-Conscious Companies** 101 | Empower teams with modern AI tools without compromising on data privacy principles or sending proprietary information to external APIs. 102 | 103 | --- 104 | 105 | ## Bottom-Up Adoption Strategy 106 | 107 | **How JRVS enters your organization:** 108 | 109 | 1. **Developers discover JRVS** through GitHub, use it personally with local models 110 | 2. **Team adoption** as developers share the productivity gains with colleagues 111 | 3. **IT/Security approval** because it's self-hosted, auditable, and meets compliance requirements 112 | 4. **Enterprise deployment** with official support from original creator 113 | 114 | We support this natural adoption path with flexible pricing: developers start free, teams upgrade to hosted for convenience, enterprises get full compliance support. 115 | 116 | --- 117 | 118 | ## Why JRVS? 119 | 120 | **Backend-agnostic architecture** means you're never locked into a specific AI provider. Start with Ollama, switch to vLLM, add commercial models later—JRVS adapts to your needs. 121 | 122 | **Original creator involvement** ensures fastest feature delivery and official support. Direct access to the person who built JRVS means faster resolutions and better customization. 123 | 124 | **Proven adoption** with 175+ GitHub stars and senior developer contributions within 3 days of launch demonstrates real-world validation. 125 | 126 | **Open source foundation** provides transparency and auditability. Your security team can review every line of code. No black boxes. 127 | 128 | --- 129 | 130 | ## Get Started 131 | 132 | **Need AI tools without compromising data privacy? Let's talk.** 133 | 134 | 📧 Contact: cmartin.xavier@yahoo.com 135 | 136 | Whether you're exploring options for a small team or planning enterprise-wide deployment, I'm available to discuss your specific compliance requirements and how JRVS can help. 137 | 138 | --- 139 | 140 | **Built by Xavier Martin** 141 | Original creator • Fastest feature delivery • Official support 142 | 143 | [← Back to README](./README.md) | [View on GitHub](https://github.com/xthebuilder/jrvs) 144 | -------------------------------------------------------------------------------- /rag/embeddings.py: -------------------------------------------------------------------------------- 1 | """Embeddings generation using BERT models""" 2 | import asyncio 3 | import numpy as np 4 | from typing import List, Optional, Union 5 | from sentence_transformers import SentenceTransformer 6 | import torch 7 | from functools import lru_cache 8 | import time 9 | from config import EMBEDDING_BATCH_SIZE, TIMEOUTS 10 | 11 | class EmbeddingManager: 12 | def __init__(self, model_name: str = "all-MiniLM-L6-v2"): 13 | self.model_name = model_name 14 | self._model = None 15 | self._device = None 16 | self._embedding_cache = {} 17 | self._max_cache_size = 1000 18 | 19 | async def initialize(self): 20 | """Lazy initialization of the embedding model""" 21 | if self._model is None: 22 | await self._load_model() 23 | 24 | async def _load_model(self): 25 | """Load the sentence transformer model""" 26 | loop = asyncio.get_event_loop() 27 | 28 | def _load(): 29 | # Determine device 30 | device = 'cuda' if torch.cuda.is_available() else 'cpu' 31 | self._device = device 32 | 33 | # Load model 34 | model = SentenceTransformer(self.model_name, device=device) 35 | return model 36 | 37 | # Run in thread pool to avoid blocking 38 | self._model = await loop.run_in_executor(None, _load) 39 | 40 | async def encode_text(self, text: Union[str, List[str]], 41 | batch_size: int = EMBEDDING_BATCH_SIZE) -> np.ndarray: 42 | """ 43 | Generate embeddings for text(s) 44 | Returns numpy array of embeddings 45 | """ 46 | await self.initialize() 47 | 48 | if isinstance(text, str): 49 | text = [text] 50 | 51 | # Check cache first 52 | cached_embeddings = [] 53 | uncached_texts = [] 54 | uncached_indices = [] 55 | 56 | for i, t in enumerate(text): 57 | cache_key = hash(t) 58 | if cache_key in self._embedding_cache: 59 | cached_embeddings.append((i, self._embedding_cache[cache_key])) 60 | else: 61 | uncached_texts.append(t) 62 | uncached_indices.append(i) 63 | 64 | # Generate embeddings for uncached texts 65 | new_embeddings = [] 66 | if uncached_texts: 67 | try: 68 | start_time = time.time() 69 | 70 | # Process in batches 71 | all_new_embeddings = [] 72 | for i in range(0, len(uncached_texts), batch_size): 73 | batch = uncached_texts[i:i + batch_size] 74 | 75 | # Run encoding in thread pool 76 | loop = asyncio.get_event_loop() 77 | batch_embeddings = await loop.run_in_executor( 78 | None, 79 | lambda: self._model.encode(batch, convert_to_numpy=True) 80 | ) 81 | all_new_embeddings.extend(batch_embeddings) 82 | 83 | # Cache new embeddings 84 | for i, emb in enumerate(all_new_embeddings): 85 | cache_key = hash(uncached_texts[i]) 86 | self._embedding_cache[cache_key] = emb 87 | 88 | # Manage cache size 89 | if len(self._embedding_cache) > self._max_cache_size: 90 | # Remove oldest entries (simple LRU approximation) 91 | items = list(self._embedding_cache.items()) 92 | for key, _ in items[:len(items) - self._max_cache_size + 100]: 93 | del self._embedding_cache[key] 94 | 95 | new_embeddings = all_new_embeddings 96 | 97 | elapsed = time.time() - start_time 98 | if elapsed > TIMEOUTS["embedding_generation"]: 99 | print(f"Warning: Embedding generation took {elapsed:.2f}s") 100 | 101 | except Exception as e: 102 | print(f"Error generating embeddings: {e}") 103 | # Return zero embeddings as fallback 104 | embedding_dim = 384 # Default for all-MiniLM-L6-v2 105 | return np.zeros((len(text), embedding_dim)) 106 | 107 | # Combine cached and new embeddings in correct order 108 | final_embeddings = [None] * len(text) 109 | 110 | # Place cached embeddings 111 | for idx, emb in cached_embeddings: 112 | final_embeddings[idx] = emb 113 | 114 | # Place new embeddings 115 | for i, idx in enumerate(uncached_indices): 116 | if i < len(new_embeddings): 117 | final_embeddings[idx] = new_embeddings[i] 118 | 119 | return np.array(final_embeddings) 120 | 121 | async def encode_chunks(self, chunks: List[str]) -> List[np.ndarray]: 122 | """Generate embeddings for document chunks""" 123 | if not chunks: 124 | return [] 125 | 126 | embeddings = await self.encode_text(chunks) 127 | return [embeddings[i] for i in range(len(chunks))] 128 | 129 | def get_embedding_dimension(self) -> int: 130 | """Get the dimension of embeddings""" 131 | if self._model: 132 | return self._model.get_sentence_embedding_dimension() 133 | return 384 # Default for all-MiniLM-L6-v2 134 | 135 | async def similarity(self, text1: str, text2: str) -> float: 136 | """Calculate cosine similarity between two texts""" 137 | embeddings = await self.encode_text([text1, text2]) 138 | 139 | # Calculate cosine similarity 140 | emb1, emb2 = embeddings[0], embeddings[1] 141 | dot_product = np.dot(emb1, emb2) 142 | norm1 = np.linalg.norm(emb1) 143 | norm2 = np.linalg.norm(emb2) 144 | 145 | if norm1 == 0 or norm2 == 0: 146 | return 0.0 147 | 148 | return float(dot_product / (norm1 * norm2)) 149 | 150 | def clear_cache(self): 151 | """Clear embedding cache""" 152 | self._embedding_cache.clear() 153 | 154 | def get_cache_size(self) -> int: 155 | """Get current cache size""" 156 | return len(self._embedding_cache) 157 | 158 | # Global embedding manager 159 | embedding_manager = EmbeddingManager() -------------------------------------------------------------------------------- /setup_mac.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # JRVS Mac Setup Script 3 | # This script installs and configures prerequisites for running JRVS on macOS 4 | 5 | set -e 6 | 7 | echo "========================================" 8 | echo " JRVS Mac Setup Script" 9 | echo "========================================" 10 | echo "" 11 | 12 | # Colors for output 13 | RED='\033[0;31m' 14 | GREEN='\033[0;32m' 15 | YELLOW='\033[1;33m' 16 | NC='\033[0m' # No Color 17 | 18 | # Helper functions 19 | print_ok() { 20 | echo -e "${GREEN}[OK]${NC} $1" 21 | } 22 | 23 | print_warning() { 24 | echo -e "${YELLOW}[WARNING]${NC} $1" 25 | } 26 | 27 | print_error() { 28 | echo -e "${RED}[ERROR]${NC} $1" 29 | } 30 | 31 | print_info() { 32 | echo "[INFO] $1" 33 | } 34 | 35 | # Check if Homebrew is installed 36 | echo "[1/7] Checking Homebrew installation..." 37 | if ! command -v brew &> /dev/null; then 38 | print_warning "Homebrew is not installed." 39 | print_info "Homebrew is recommended for managing dependencies on macOS." 40 | read -p "Would you like to install Homebrew? (y/n) " -n 1 -r 41 | echo 42 | if [[ $REPLY =~ ^[Yy]$ ]]; then 43 | print_info "Installing Homebrew..." 44 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 45 | # Add Homebrew to PATH for Apple Silicon Macs 46 | if [[ -f "/opt/homebrew/bin/brew" ]]; then 47 | eval "$(/opt/homebrew/bin/brew shellenv)" 48 | fi 49 | print_ok "Homebrew installed successfully" 50 | else 51 | print_info "Skipping Homebrew installation. Some features may require manual setup." 52 | fi 53 | else 54 | print_ok "Homebrew is installed" 55 | fi 56 | echo "" 57 | 58 | # Check Python installation 59 | echo "[2/7] Checking Python installation..." 60 | if ! command -v python3 &> /dev/null; then 61 | print_error "Python 3 is not installed." 62 | if command -v brew &> /dev/null; then 63 | print_info "Installing Python 3 via Homebrew..." 64 | brew install python@3.11 65 | print_ok "Python 3 installed successfully" 66 | else 67 | print_info "Please install Python 3.8+ from https://python.org/downloads/" 68 | exit 1 69 | fi 70 | else 71 | PYTHON_VERSION=$(python3 --version 2>&1 | awk '{print $2}') 72 | print_ok "Python $PYTHON_VERSION found" 73 | fi 74 | echo "" 75 | 76 | # Check pip installation 77 | echo "[3/7] Checking pip installation..." 78 | if ! command -v pip3 &> /dev/null; then 79 | print_warning "pip is not installed." 80 | print_info "Installing pip..." 81 | python3 -m ensurepip --upgrade 82 | else 83 | print_ok "pip is available" 84 | fi 85 | echo "" 86 | 87 | # Check Node.js installation 88 | echo "[4/7] Checking Node.js installation..." 89 | if ! command -v node &> /dev/null; then 90 | print_warning "Node.js is not installed." 91 | print_info "Node.js is required for MCP server functionality." 92 | if command -v brew &> /dev/null; then 93 | read -p "Would you like to install Node.js via Homebrew? (y/n) " -n 1 -r 94 | echo 95 | if [[ $REPLY =~ ^[Yy]$ ]]; then 96 | print_info "Installing Node.js..." 97 | brew install node 98 | print_ok "Node.js installed successfully" 99 | fi 100 | else 101 | print_info "Please install Node.js from https://nodejs.org/" 102 | fi 103 | else 104 | NODE_VERSION=$(node --version 2>&1) 105 | print_ok "Node.js $NODE_VERSION found" 106 | fi 107 | echo "" 108 | 109 | # Install Python dependencies 110 | echo "[5/7] Installing Python dependencies..." 111 | if [ ! -f "requirements.txt" ]; then 112 | print_error "requirements.txt not found!" 113 | print_info "Make sure you are running this script from the JRVS directory." 114 | exit 1 115 | fi 116 | print_info "This may take a few minutes..." 117 | if pip3 install -r requirements.txt; then 118 | print_ok "Python dependencies installed successfully" 119 | else 120 | print_error "Failed to install Python dependencies." 121 | print_info "Try running: pip3 install -r requirements.txt manually" 122 | exit 1 123 | fi 124 | echo "" 125 | 126 | # Check for Ollama 127 | echo "[6/7] Checking Ollama installation..." 128 | if ! command -v ollama &> /dev/null; then 129 | print_warning "Ollama is not installed." 130 | print_info "Ollama is required to run AI models locally." 131 | if command -v brew &> /dev/null; then 132 | read -p "Would you like to install Ollama via Homebrew? (y/n) " -n 1 -r 133 | echo 134 | if [[ $REPLY =~ ^[Yy]$ ]]; then 135 | print_info "Installing Ollama..." 136 | brew install ollama 137 | print_ok "Ollama installed successfully" 138 | print_info "After setup, run: ollama serve" 139 | print_info "Then pull a model: ollama pull llama3.1" 140 | fi 141 | else 142 | print_info "Please install Ollama from https://ollama.ai/download" 143 | fi 144 | else 145 | print_ok "Ollama is installed" 146 | print_info "Make sure Ollama is running with: ollama serve" 147 | fi 148 | echo "" 149 | 150 | # Install npm dependencies (if package.json exists) 151 | echo "[7/7] Installing npm dependencies for frontend..." 152 | if [ -f "package.json" ]; then 153 | if command -v npm &> /dev/null; then 154 | if npm install; then 155 | print_ok "npm dependencies installed successfully" 156 | else 157 | print_warning "Failed to install npm dependencies." 158 | fi 159 | else 160 | print_warning "npm is not available. Skipping frontend dependencies." 161 | print_info "Install Node.js to enable frontend features." 162 | fi 163 | else 164 | print_info "No package.json found. Skipping npm dependencies." 165 | fi 166 | echo "" 167 | 168 | # Create data directory if it doesn't exist 169 | mkdir -p data 170 | print_ok "Data directory ready" 171 | echo "" 172 | 173 | # Make scripts executable (if they exist) 174 | print_info "Making scripts executable..." 175 | for script in start_jrvs.sh start-api.sh setup_mac.sh; do 176 | if [ -f "$script" ]; then 177 | chmod +x "$script" 178 | fi 179 | done 180 | print_ok "Scripts configured" 181 | 182 | echo "========================================" 183 | echo " Setup Complete!" 184 | echo "========================================" 185 | echo "" 186 | echo "Next steps:" 187 | echo " 1. Start Ollama: ollama serve" 188 | echo " 2. Pull a model: ollama pull llama3.1" 189 | echo " 3. Run JRVS: python3 main.py" 190 | echo "" 191 | echo "For the web interface:" 192 | echo " 1. Start API: python3 api/server.py" 193 | echo " 2. Start frontend: npm run dev" 194 | echo " 3. Open browser: http://localhost:3000" 195 | echo "" 196 | echo "See README.md for more information." 197 | echo "" 198 | -------------------------------------------------------------------------------- /mcp/logging_config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Structured logging configuration for JRVS MCP Server 3 | 4 | Provides JSON-formatted logging with request tracking, performance metrics, 5 | and error aggregation. 6 | """ 7 | 8 | import logging 9 | import json 10 | import sys 11 | from datetime import datetime 12 | from typing import Any, Dict, Optional 13 | from pathlib import Path 14 | import traceback 15 | 16 | 17 | class JSONFormatter(logging.Formatter): 18 | """Format log records as JSON for better machine readability""" 19 | 20 | def __init__(self, service_name: str = "jrvs-mcp"): 21 | super().__init__() 22 | self.service_name = service_name 23 | 24 | def format(self, record: logging.LogRecord) -> str: 25 | """Format log record as JSON""" 26 | log_data = { 27 | "timestamp": datetime.utcnow().isoformat() + "Z", 28 | "service": self.service_name, 29 | "level": record.levelname, 30 | "logger": record.name, 31 | "message": record.getMessage(), 32 | "module": record.module, 33 | "function": record.funcName, 34 | "line": record.lineno, 35 | } 36 | 37 | # Add exception info if present 38 | if record.exc_info: 39 | log_data["exception"] = { 40 | "type": record.exc_info[0].__name__ if record.exc_info[0] else None, 41 | "message": str(record.exc_info[1]) if record.exc_info[1] else None, 42 | "traceback": traceback.format_exception(*record.exc_info) 43 | } 44 | 45 | # Add custom fields from extra 46 | if hasattr(record, 'extra_data'): 47 | log_data.update(record.extra_data) 48 | 49 | return json.dumps(log_data) 50 | 51 | 52 | class ConsoleFormatter(logging.Formatter): 53 | """Human-readable console formatter with colors""" 54 | 55 | COLORS = { 56 | 'DEBUG': '\033[36m', # Cyan 57 | 'INFO': '\033[32m', # Green 58 | 'WARNING': '\033[33m', # Yellow 59 | 'ERROR': '\033[31m', # Red 60 | 'CRITICAL': '\033[35m', # Magenta 61 | 'RESET': '\033[0m' 62 | } 63 | 64 | def format(self, record: logging.LogRecord) -> str: 65 | """Format with colors for console""" 66 | color = self.COLORS.get(record.levelname, self.COLORS['RESET']) 67 | reset = self.COLORS['RESET'] 68 | 69 | timestamp = datetime.fromtimestamp(record.created).strftime('%Y-%m-%d %H:%M:%S') 70 | 71 | # Build message 72 | msg = f"{color}[{timestamp}] {record.levelname:8}{reset} {record.name}: {record.getMessage()}" 73 | 74 | # Add exception if present 75 | if record.exc_info: 76 | msg += f"\n{self.formatException(record.exc_info)}" 77 | 78 | return msg 79 | 80 | 81 | class LoggerAdapter(logging.LoggerAdapter): 82 | """Adapter to add context to log messages""" 83 | 84 | def process(self, msg: str, kwargs: Dict[str, Any]) -> tuple: 85 | """Add context information to log records""" 86 | extra = kwargs.get('extra', {}) 87 | 88 | # Merge context with extra 89 | if self.extra: 90 | extra = {**self.extra, **extra} 91 | 92 | kwargs['extra'] = {'extra_data': extra} 93 | return msg, kwargs 94 | 95 | 96 | def setup_logging( 97 | level: str = "INFO", 98 | log_file: Optional[str] = None, 99 | json_logs: bool = True, 100 | service_name: str = "jrvs-mcp" 101 | ) -> logging.Logger: 102 | """ 103 | Setup logging configuration 104 | 105 | Args: 106 | level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) 107 | log_file: Optional file path for logs 108 | json_logs: Use JSON formatting for file logs 109 | service_name: Service name for log identification 110 | 111 | Returns: 112 | Configured root logger 113 | """ 114 | 115 | # Create root logger 116 | root_logger = logging.getLogger() 117 | root_logger.setLevel(getattr(logging, level.upper())) 118 | 119 | # Remove existing handlers 120 | root_logger.handlers.clear() 121 | 122 | # Console handler (human-readable) 123 | console_handler = logging.StreamHandler(sys.stderr) 124 | console_handler.setLevel(logging.INFO) 125 | console_handler.setFormatter(ConsoleFormatter()) 126 | root_logger.addHandler(console_handler) 127 | 128 | # File handler (JSON) 129 | if log_file: 130 | log_path = Path(log_file) 131 | log_path.parent.mkdir(parents=True, exist_ok=True) 132 | 133 | file_handler = logging.FileHandler(log_file) 134 | file_handler.setLevel(logging.DEBUG) 135 | 136 | if json_logs: 137 | file_handler.setFormatter(JSONFormatter(service_name)) 138 | else: 139 | file_handler.setFormatter( 140 | logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 141 | ) 142 | 143 | root_logger.addHandler(file_handler) 144 | 145 | return root_logger 146 | 147 | 148 | def get_logger(name: str, **context) -> LoggerAdapter: 149 | """ 150 | Get a logger with context 151 | 152 | Args: 153 | name: Logger name 154 | **context: Context to add to all log messages 155 | 156 | Returns: 157 | Logger adapter with context 158 | """ 159 | logger = logging.getLogger(name) 160 | return LoggerAdapter(logger, context) 161 | 162 | 163 | # Request tracking utilities 164 | class RequestContext: 165 | """Track request context for logging""" 166 | 167 | def __init__(self, request_id: str, tool_name: str, client_id: str = None): 168 | self.request_id = request_id 169 | self.tool_name = tool_name 170 | self.client_id = client_id 171 | self.start_time = datetime.utcnow() 172 | 173 | def to_dict(self) -> Dict[str, Any]: 174 | """Convert to dictionary""" 175 | return { 176 | "request_id": self.request_id, 177 | "tool_name": self.tool_name, 178 | "client_id": self.client_id, 179 | "start_time": self.start_time.isoformat() + "Z" 180 | } 181 | 182 | def log_completion(self, logger: logging.Logger, success: bool, error: str = None): 183 | """Log request completion""" 184 | duration_ms = (datetime.utcnow() - self.start_time).total_seconds() * 1000 185 | 186 | log_data = { 187 | **self.to_dict(), 188 | "success": success, 189 | "duration_ms": round(duration_ms, 2), 190 | } 191 | 192 | if error: 193 | log_data["error"] = error 194 | 195 | if success: 196 | logger.info( 197 | f"Request completed: {self.tool_name}", 198 | extra=log_data 199 | ) 200 | else: 201 | logger.error( 202 | f"Request failed: {self.tool_name}", 203 | extra=log_data 204 | ) 205 | -------------------------------------------------------------------------------- /docs/README_FRONTEND.md: -------------------------------------------------------------------------------- 1 | # Jarvis Frontend Integration Guide 2 | 3 | ## Quick Start 4 | 5 | ### 1. Install API Dependencies 6 | ```bash 7 | pip install fastapi uvicorn websockets pydantic 8 | ``` 9 | 10 | ### 2. Start the API Server 11 | ```bash 12 | cd /home/xavier/jarvis_ai_agent 13 | python api/server.py 14 | ``` 15 | 16 | Server runs at: `http://localhost:8000` 17 | 18 | ### 3. Open Frontend Example 19 | ```bash 20 | # Just open the HTML file in your browser 21 | firefox frontend-example.html 22 | # or 23 | chrome frontend-example.html 24 | ``` 25 | 26 | --- 27 | 28 | ## API Endpoints 29 | 30 | ### Chat 31 | ```bash 32 | POST /api/chat 33 | { 34 | "message": "Hello Jarvis", 35 | "session_id": "optional-uuid", 36 | "stream": false 37 | } 38 | 39 | Response: 40 | { 41 | "response": "Hello! How can I help?", 42 | "session_id": "uuid", 43 | "model_used": "gemma3:12b", 44 | "context_used": "..." 45 | } 46 | ``` 47 | 48 | ### WebSocket Streaming 49 | ```javascript 50 | const ws = new WebSocket('ws://localhost:8000/ws/chat'); 51 | ws.send(JSON.stringify({ message: "Hello" })); 52 | ws.onmessage = (event) => { 53 | const data = JSON.parse(event.data); 54 | if (data.type === 'chunk') { 55 | console.log(data.content); 56 | } 57 | }; 58 | ``` 59 | 60 | ### Models 61 | ```bash 62 | GET /api/models # List models 63 | POST /api/models/switch/{model} # Switch model 64 | ``` 65 | 66 | ### Calendar 67 | ```bash 68 | GET /api/calendar/events?days=7 # Upcoming events 69 | GET /api/calendar/today # Today's events 70 | POST /api/calendar/events # Create event 71 | { 72 | "title": "Meeting", 73 | "event_date": "2025-11-10T14:30:00", 74 | "description": "Team sync" 75 | } 76 | 77 | POST /api/calendar/events/{id}/complete 78 | DELETE /api/calendar/events/{id} 79 | ``` 80 | 81 | ### Knowledge Base 82 | ```bash 83 | POST /api/scrape 84 | { 85 | "url": "https://example.com" 86 | } 87 | 88 | GET /api/search?query=react&limit=5 89 | ``` 90 | 91 | ### History 92 | ```bash 93 | GET /api/history/{session_id}?limit=10 94 | ``` 95 | 96 | ### Stats 97 | ```bash 98 | GET /api/stats 99 | GET /api/health 100 | ``` 101 | 102 | --- 103 | 104 | ## Frontend Frameworks 105 | 106 | ### React Example 107 | ```jsx 108 | import { useState } from 'react'; 109 | 110 | function JarvisChat() { 111 | const [messages, setMessages] = useState([]); 112 | const [input, setInput] = useState(''); 113 | 114 | const sendMessage = async () => { 115 | const response = await fetch('http://localhost:8000/api/chat', { 116 | method: 'POST', 117 | headers: { 'Content-Type': 'application/json' }, 118 | body: JSON.stringify({ message: input }) 119 | }); 120 | const data = await response.json(); 121 | 122 | setMessages([ 123 | ...messages, 124 | { role: 'user', content: input }, 125 | { role: 'assistant', content: data.response } 126 | ]); 127 | setInput(''); 128 | }; 129 | 130 | return ( 131 | <div> 132 | {messages.map((msg, i) => ( 133 | <div key={i}>{msg.role}: {msg.content}</div> 134 | ))} 135 | <input value={input} onChange={e => setInput(e.target.value)} /> 136 | <button onClick={sendMessage}>Send</button> 137 | </div> 138 | ); 139 | } 140 | ``` 141 | 142 | ### Vue Example 143 | ```vue 144 | <template> 145 | <div> 146 | <div v-for="msg in messages" :key="msg.id"> 147 | {{ msg.role }}: {{ msg.content }} 148 | </div> 149 | <input v-model="input" @keyup.enter="sendMessage" /> 150 | <button @click="sendMessage">Send</button> 151 | </div> 152 | </template> 153 | 154 | <script> 155 | export default { 156 | data() { 157 | return { 158 | messages: [], 159 | input: '' 160 | } 161 | }, 162 | methods: { 163 | async sendMessage() { 164 | const res = await fetch('http://localhost:8000/api/chat', { 165 | method: 'POST', 166 | headers: { 'Content-Type': 'application/json' }, 167 | body: JSON.stringify({ message: this.input }) 168 | }); 169 | const data = await res.json(); 170 | 171 | this.messages.push( 172 | { role: 'user', content: this.input }, 173 | { role: 'assistant', content: data.response } 174 | ); 175 | this.input = ''; 176 | } 177 | } 178 | } 179 | </script> 180 | ``` 181 | 182 | ### Svelte Example 183 | ```svelte 184 | <script> 185 | let messages = []; 186 | let input = ''; 187 | 188 | async function sendMessage() { 189 | const res = await fetch('http://localhost:8000/api/chat', { 190 | method: 'POST', 191 | headers: { 'Content-Type': 'application/json' }, 192 | body: JSON.stringify({ message: input }) 193 | }); 194 | const data = await res.json(); 195 | 196 | messages = [ 197 | ...messages, 198 | { role: 'user', content: input }, 199 | { role: 'assistant', content: data.response } 200 | ]; 201 | input = ''; 202 | } 203 | </script> 204 | 205 | {#each messages as msg} 206 | <div>{msg.role}: {msg.content}</div> 207 | {/each} 208 | 209 | <input bind:value={input} on:keyup={(e) => e.key === 'Enter' && sendMessage()} /> 210 | <button on:click={sendMessage}>Send</button> 211 | ``` 212 | 213 | --- 214 | 215 | ## Production Considerations 216 | 217 | ### 1. CORS 218 | Update `api/server.py`: 219 | ```python 220 | app.add_middleware( 221 | CORSMiddleware, 222 | allow_origins=["https://yourdomain.com"], # Your frontend domain 223 | allow_credentials=True, 224 | allow_methods=["*"], 225 | allow_headers=["*"], 226 | ) 227 | ``` 228 | 229 | ### 2. Authentication 230 | Add JWT or API key middleware 231 | 232 | ### 3. Rate Limiting 233 | ```bash 234 | pip install slowapi 235 | ``` 236 | 237 | ### 4. HTTPS 238 | Use nginx or Caddy as reverse proxy 239 | 240 | ### 5. Docker Compose 241 | ```yaml 242 | version: '3.8' 243 | services: 244 | api: 245 | build: . 246 | command: python api/server.py 247 | ports: 248 | - "8000:8000" 249 | 250 | frontend: 251 | image: nginx:alpine 252 | volumes: 253 | - ./dist:/usr/share/nginx/html 254 | ports: 255 | - "80:80" 256 | ``` 257 | 258 | --- 259 | 260 | ## Mobile Apps 261 | 262 | ### React Native 263 | Same API calls, use `fetch` or `axios` 264 | 265 | ### Flutter 266 | ```dart 267 | import 'package:http/http.dart' as http; 268 | import 'dart:convert'; 269 | 270 | Future<String> chat(String message) async { 271 | final response = await http.post( 272 | Uri.parse('http://localhost:8000/api/chat'), 273 | headers: {'Content-Type': 'application/json'}, 274 | body: json.encode({'message': message}), 275 | ); 276 | return json.decode(response.body)['response']; 277 | } 278 | ``` 279 | 280 | --- 281 | 282 | ## Desktop Apps 283 | 284 | ### Electron 285 | Wrap your web frontend + bundle API server 286 | 287 | ### Tauri 288 | Rust-based alternative to Electron 289 | 290 | --- 291 | 292 | Your Jarvis backend is now **API-first** and can connect to any frontend! 293 | -------------------------------------------------------------------------------- /docs/QUICKSTART.md: -------------------------------------------------------------------------------- 1 | # Jarvis AI - Complete Setup Guide 2 | 3 | ## 🚀 Quick Start (3 Minutes) 4 | 5 | ### **Step 1: Start Ollama** (if not running) 6 | ```bash 7 | ollama serve 8 | ``` 9 | 10 | ### **Step 2: Start Jarvis API Backend** 11 | ```bash 12 | cd /home/xavier/jarvis_ai_agent 13 | ./start-api.sh 14 | 15 | # Or manually: 16 | python api/server.py 17 | ``` 18 | 19 | You should see: 20 | ``` 21 | INFO: Started server process 22 | INFO: Uvicorn running on http://0.0.0.0:8000 23 | ``` 24 | 25 | ### **Step 3: Start Next.js Frontend** 26 | ```bash 27 | # Install dependencies (first time only) 28 | npm install 29 | 30 | # Start development server 31 | npm run dev 32 | ``` 33 | 34 | Open: **http://localhost:3000** 35 | 36 | --- 37 | 38 | ## 📋 Complete Setup Options 39 | 40 | ### **Option A: Simple HTML Frontend** (No Build Required) 41 | 42 | 1. **Start API:** 43 | ```bash 44 | python api/server.py 45 | ``` 46 | 47 | 2. **Open in Browser:** 48 | ```bash 49 | firefox frontend-example.html 50 | # or 51 | chrome frontend-example.html 52 | ``` 53 | 54 | **Pros:** 55 | - No build step 56 | - Works immediately 57 | - Single file 58 | 59 | **Cons:** 60 | - Basic UI 61 | - No hot reload 62 | 63 | --- 64 | 65 | ### **Option B: Next.js App** (Full-Featured) 66 | 67 | 1. **Start API:** 68 | ```bash 69 | ./start-api.sh 70 | ``` 71 | 72 | 2. **Install Dependencies:** 73 | ```bash 74 | npm install 75 | ``` 76 | 77 | 3. **Configure Environment:** 78 | Already created `.env.local`: 79 | ```env 80 | NEXT_PUBLIC_API_URL=http://localhost:8000/api 81 | ``` 82 | 83 | 4. **Start Dev Server:** 84 | ```bash 85 | npm run dev 86 | ``` 87 | 88 | 5. **Access:** 89 | - App: http://localhost:3000 90 | - API Docs: http://localhost:8000/docs 91 | 92 | **Pros:** 93 | - Modern UI (Tailwind CSS) 94 | - Hot reload 95 | - TypeScript 96 | - Production ready 97 | 98 | --- 99 | 100 | ### **Option C: Docker (Production)** 101 | 102 | 1. **Build:** 103 | ```bash 104 | docker-compose up --build 105 | ``` 106 | 107 | 2. **Access:** 108 | - Jarvis CLI: `docker exec -it jarvis_ai_agent python main.py` 109 | - Ollama: http://localhost:11434 110 | - API: http://localhost:8000 111 | 112 | --- 113 | 114 | ## 🔧 API Endpoints Reference 115 | 116 | ### **Chat** 117 | ```bash 118 | # Simple chat 119 | curl -X POST http://localhost:8000/api/chat \ 120 | -H "Content-Type: application/json" \ 121 | -d '{"message": "Hello Jarvis"}' 122 | 123 | # Response 124 | { 125 | "response": "Hello! How can I help you?", 126 | "session_id": "uuid", 127 | "model_used": "gemma3:4b" 128 | } 129 | ``` 130 | 131 | ### **Models** 132 | ```bash 133 | # List models 134 | curl http://localhost:8000/api/models 135 | 136 | # Switch model 137 | curl -X POST http://localhost:8000/api/models/switch/deepseek-r1:14b 138 | ``` 139 | 140 | ### **Calendar** 141 | ```bash 142 | # Get upcoming events 143 | curl http://localhost:8000/api/calendar/events?days=7 144 | 145 | # Create event 146 | curl -X POST http://localhost:8000/api/calendar/events \ 147 | -H "Content-Type: application/json" \ 148 | -d '{ 149 | "title": "Team Meeting", 150 | "event_date": "2025-11-10T14:30:00", 151 | "description": "Weekly sync" 152 | }' 153 | 154 | # Complete event 155 | curl -X POST http://localhost:8000/api/calendar/events/1/complete 156 | ``` 157 | 158 | ### **Knowledge Base** 159 | ```bash 160 | # Scrape website 161 | curl -X POST http://localhost:8000/api/scrape \ 162 | -H "Content-Type: application/json" \ 163 | -d '{"url": "https://docs.react.dev"}' 164 | 165 | # Search documents 166 | curl "http://localhost:8000/api/search?query=react%20hooks&limit=5" 167 | ``` 168 | 169 | --- 170 | 171 | ## 🧪 Testing the Integration 172 | 173 | ### **Test 1: Chat Works** 174 | ```bash 175 | curl -X POST http://localhost:8000/api/chat \ 176 | -H "Content-Type: application/json" \ 177 | -d '{"message": "What is 2+2?"}' 178 | ``` 179 | 180 | Expected: JSON response with answer 181 | 182 | ### **Test 2: Frontend Connects** 183 | 1. Open http://localhost:3000 184 | 2. Type "Hello" 185 | 3. Should get response from Jarvis 186 | 187 | ### **Test 3: Calendar Integration** 188 | 1. In frontend, type: "meeting tomorrow at 10am" 189 | 2. Should create calendar event 190 | 191 | --- 192 | 193 | ## 📁 Files Created 194 | 195 | ### **Backend API:** 196 | - `api/server.py` - FastAPI server 197 | - `api/__init__.py` - Package marker 198 | - `start-api.sh` - Startup script 199 | 200 | ### **Frontend:** 201 | - `lib/jarvis-api.ts` - TypeScript API client 202 | - `app/components/JarvisChat.tsx` - Chat UI component 203 | - `.env.local` - Environment config 204 | 205 | ### **Documentation:** 206 | - `README_FRONTEND.md` - Full API docs 207 | - `QUICKSTART.md` - This file 208 | 209 | --- 210 | 211 | ## 🐛 Troubleshooting 212 | 213 | ### **"Connection refused" error:** 214 | ```bash 215 | # Make sure API is running 216 | python api/server.py 217 | 218 | # Check if port 8000 is in use 219 | lsof -i :8000 220 | ``` 221 | 222 | ### **"Ollama not running":** 223 | ```bash 224 | # Start Ollama 225 | ollama serve 226 | 227 | # Test 228 | curl http://localhost:11434/api/tags 229 | ``` 230 | 231 | ### **"Module not found" in frontend:** 232 | ```bash 233 | # Reinstall dependencies 234 | rm -rf node_modules package-lock.json 235 | npm install 236 | ``` 237 | 238 | ### **CORS errors:** 239 | API already configured for `allow_origins=["*"]` 240 | For production, update `api/server.py`: 241 | ```python 242 | allow_origins=["https://yourdomain.com"] 243 | ``` 244 | 245 | --- 246 | 247 | ## 🎯 Next Steps 248 | 249 | ### **1. Customize the UI** 250 | Edit `app/components/JarvisChat.tsx` for styling 251 | 252 | ### **2. Add Features** 253 | The API already supports: 254 | - Calendar events 255 | - Document scraping 256 | - Knowledge search 257 | - Model switching 258 | 259 | ### **3. Deploy** 260 | ```bash 261 | # Build for production 262 | npm run build 263 | 264 | # Start production server 265 | npm start 266 | ``` 267 | 268 | --- 269 | 270 | ## 📚 Architecture 271 | 272 | ``` 273 | User Browser 274 | ↓ 275 | Next.js Frontend (localhost:3000) 276 | ↓ HTTP/REST 277 | FastAPI Backend (localhost:8000) 278 | ↓ 279 | ├─ Ollama API (localhost:11434) → AI Responses 280 | ├─ SQLite Database → Conversations, Events 281 | ├─ FAISS Vector Store → RAG Context 282 | └─ Web Scraper → Knowledge Ingestion 283 | ``` 284 | 285 | --- 286 | 287 | ## 🔐 Security Notes 288 | 289 | **Development:** 290 | - CORS: `allow_origins=["*"]` ✅ 291 | - No auth required ✅ 292 | 293 | **Production:** 294 | - Update CORS to your domain 295 | - Add API authentication (JWT) 296 | - Use HTTPS 297 | - Rate limiting 298 | 299 | --- 300 | 301 | ## ✅ You're Done! 302 | 303 | You now have: 304 | ✅ Jarvis backend running on port 8000 305 | ✅ Next.js frontend on port 3000 306 | ✅ Full API integration 307 | ✅ Calendar, chat, and RAG features 308 | ✅ Production-ready architecture 309 | 310 | **Test it:** Open http://localhost:3000 and start chatting! 🚀 311 | -------------------------------------------------------------------------------- /docs/NEW_WEB_UI.md: -------------------------------------------------------------------------------- 1 | # 🎨 New ChatGPT-Style Web Interface 2 | 3 | ## ✅ Complete! 4 | 5 | JRVS now has a **beautiful, modern ChatGPT-style interface** with all commands accessible as buttons! 6 | 7 | ## 🚀 Launch the New UI 8 | 9 | ```bash 10 | ./start_web_server.sh 11 | ``` 12 | 13 | Then open in your browser: 14 | ``` 15 | http://100.113.61.115:8080/ 16 | ``` 17 | 18 | ## 🎯 What's New 19 | 20 | ### ChatGPT-Like Design 21 | - ✅ **Left Sidebar** - All commands and features as buttons 22 | - ✅ **Clean Chat Area** - ChatGPT-style message bubbles 23 | - ✅ **Welcome Screen** - Quick action cards 24 | - ✅ **Modern Input** - Smooth textarea with auto-resize 25 | - ✅ **Professional Look** - Clean white/green color scheme 26 | 27 | ### Sidebar Features (All as Buttons!) 28 | 29 | **Quick Actions** 30 | - 📚 Help & Commands 31 | - 📊 System Stats 32 | - 📝 Activity Report 33 | 34 | **Calendar** 35 | - 📅 View Calendar (shows ASCII + events) 36 | - 📆 Today's Events 37 | - ➕ Add Event (opens form modal) 38 | 39 | **MCP Tools** 40 | - 🔌 Connected Servers (view all MCP servers) 41 | - 🔧 Available Tools (browse all tools) 42 | 43 | **AI Settings** 44 | - 🧠 Switch Model 45 | - 🎨 Change Theme 46 | 47 | **Knowledge Base** 48 | - 🌐 Scrape Website 49 | - 🔍 Search Documents 50 | 51 | ### Welcome Screen 52 | - 4 Quick action cards 53 | - Example prompts you can click 54 | - Beautiful introduction 55 | 56 | ### Modals 57 | - **Calendar Modal** - Shows ASCII calendar + event list 58 | - **Add Event Form** - Easy event creation 59 | - **MCP Servers** - View connected servers and tools 60 | 61 | ## 📱 Features 62 | 63 | ### Chat Interface 64 | - **User messages** - Right-aligned, green background 65 | - **JRVS responses** - Left-aligned, clean formatting 66 | - **Tool indicators** - Blue badges showing tools used 67 | - **Timestamps** - On all messages 68 | - **Auto-scroll** - Always shows latest 69 | 70 | ### Input Area 71 | - **Auto-resize** - Grows as you type 72 | - **Enter to send** - Shift+Enter for new line 73 | - **Attach button** - (Coming soon) 74 | - **Send button** - Beautiful icon button 75 | 76 | ### Responsive 77 | - Works on desktop, tablet, and mobile 78 | - Sidebar collapses on mobile 79 | - Touch-friendly buttons 80 | 81 | ## 🎨 Color Scheme 82 | 83 | ChatGPT-inspired: 84 | - **Background**: Clean white 85 | - **Sidebar**: Dark gray (#202123) 86 | - **Accent**: Green (#19c37d) 87 | - **Text**: Professional black/gray 88 | - **Messages**: User (green), Assistant (light gray) 89 | 90 | ## 🔘 All Buttons Available 91 | 92 | ### Sidebar Buttons 93 | 94 | 1. **New Chat** - Clear conversation 95 | 2. **Help & Commands** - Show all commands 96 | 3. **System Stats** - View system status 97 | 4. **Activity Report** - See tool usage 98 | 5. **View Calendar** - Opens calendar modal 99 | 6. **Today's Events** - List today's events 100 | 7. **Add Event** - Opens event form 101 | 8. **Connected Servers** - Shows MCP servers 102 | 9. **Available Tools** - Browse MCP tools 103 | 10. **Switch Model** - Change AI model 104 | 11. **Change Theme** - Switch CLI theme 105 | 12. **Scrape Website** - Add URL to knowledge base 106 | 13. **Search Documents** - Search RAG database 107 | 108 | ### Welcome Screen Cards 109 | 110 | 1. **Get Started** - "What can you help me with?" 111 | 2. **View Calendar** - Opens calendar 112 | 3. **Browse Files** - Example file browsing 113 | 4. **MCP Tools** - View connected servers 114 | 115 | ### Example Prompts (Clickable) 116 | 117 | - "Remember that I prefer Python 3.11 for all projects" 118 | - "Add meeting tomorrow at 2pm" 119 | - "What are the latest Python best practices?" 120 | 121 | ## 📋 Calendar Modal Features 122 | 123 | When you click "View Calendar": 124 | - Shows ASCII calendar art 125 | - Marks today with brackets [DD] 126 | - Marks events with asterisk DD* 127 | - Lists all events below 128 | - Shows event status (completed ✓ or pending ○) 129 | - Click-to-close background 130 | 131 | ## ➕ Add Event Form 132 | 133 | Beautiful form with: 134 | - Event Title (required) 135 | - Date picker (required) 136 | - Time picker (default 2pm) 137 | - Description (optional) 138 | - Submit button 139 | 140 | ## 🔧 MCP Modals 141 | 142 | **Server List:** 143 | - Shows all connected servers 144 | - Tool count for each 145 | - "View Tools" button for details 146 | 147 | **Tool List:** 148 | - Organized by server 149 | - Tool names and descriptions 150 | - Browse all available tools 151 | 152 | ## 💡 Usage Tips 153 | 154 | ### Quick Actions 155 | 1. Click sidebar buttons for instant commands 156 | 2. Click example prompts to try features 157 | 3. Use modals for complex actions 158 | 159 | ### Natural Chat 160 | - Just type and chat normally 161 | - JRVS automatically uses tools 162 | - See tool usage in blue badges 163 | 164 | ### Keyboard Shortcuts 165 | - **Enter** - Send message 166 | - **Shift+Enter** - New line 167 | - **Esc** - Close modals (soon) 168 | 169 | ## 🎯 Example Workflow 170 | 171 | 1. **Click** "View Calendar" in sidebar 172 | 2. **See** your month's events 173 | 3. **Click** "Add Event" button 174 | 4. **Fill** the form 175 | 5. **Submit** - Event added! 176 | 6. **Chat** naturally: "What's on my schedule tomorrow?" 177 | 178 | Or: 179 | 180 | 1. **Click** "Connected Servers" 181 | 2. **View** available MCP servers 182 | 3. **Click** "View Tools" on filesystem 183 | 4. **See** all filesystem tools 184 | 5. **Chat**: "List files in my Documents folder" 185 | 6. **Watch** JRVS automatically use the tool! 186 | 187 | ## 🌟 Best Features 188 | 189 | ### 1. Everything is Clickable 190 | - No need to remember commands 191 | - All features accessible via buttons 192 | - Modals for complex actions 193 | 194 | ### 2. Real-time Updates 195 | - WebSocket connection 196 | - Live status messages 197 | - Instant tool indicators 198 | 199 | ### 3. Professional Design 200 | - Clean, modern look 201 | - Smooth animations 202 | - ChatGPT-inspired UX 203 | 204 | ### 4. Mobile-Friendly 205 | - Works on phones/tablets 206 | - Responsive design 207 | - Touch-optimized buttons 208 | 209 | ## 📱 Mobile Experience 210 | 211 | ### On Your Phone 212 | 1. Open browser 213 | 2. Go to Tailscale IP 214 | 3. Bookmark it! 215 | 4. Add to Home Screen 216 | 217 | ### Features 218 | - Swipe sidebar open/closed 219 | - Touch-friendly buttons 220 | - Optimized for small screens 221 | - Voice typing works! 222 | 223 | ## 🔄 vs Old Interface 224 | 225 | ### Old (Simple) 226 | - Basic purple gradient 227 | - Simple input box 228 | - No buttons 229 | - Basic messages 230 | 231 | ### New (ChatGPT-style) 232 | - Professional white design 233 | - Full sidebar with buttons 234 | - Modals and forms 235 | - Rich message formatting 236 | - Quick action cards 237 | - Status indicators 238 | 239 | ## 🚀 Launch Now! 240 | 241 | ```bash 242 | ./start_web_server.sh 243 | ``` 244 | 245 | Access from any Tailscale device: 246 | ``` 247 | http://100.113.61.115:8080/ 248 | ``` 249 | 250 | **Enjoy your new ChatGPT-style JRVS interface!** 🎉 251 | 252 | --- 253 | 254 | All features accessible, beautifully designed, mobile-friendly, and secure on Tailscale! 🔒 255 | -------------------------------------------------------------------------------- /core/calendar.py: -------------------------------------------------------------------------------- 1 | """Simple calendar/reminder system for Jarvis""" 2 | from datetime import datetime, timedelta 3 | from typing import List, Dict, Optional 4 | import aiosqlite 5 | import calendar as pycal 6 | from config import DATABASE_PATH 7 | 8 | class Calendar: 9 | def __init__(self, db_path: str = str(DATABASE_PATH)): 10 | self.db_path = db_path 11 | 12 | async def initialize(self): 13 | """Create calendar tables""" 14 | async with aiosqlite.connect(self.db_path) as db: 15 | await db.execute(""" 16 | CREATE TABLE IF NOT EXISTS events ( 17 | id INTEGER PRIMARY KEY AUTOINCREMENT, 18 | title TEXT NOT NULL, 19 | description TEXT, 20 | event_date TIMESTAMP NOT NULL, 21 | reminder_minutes INTEGER DEFAULT 0, 22 | completed BOOLEAN DEFAULT FALSE, 23 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 24 | ) 25 | """) 26 | await db.execute("CREATE INDEX IF NOT EXISTS idx_events_date ON events(event_date)") 27 | await db.commit() 28 | 29 | async def add_event(self, title: str, event_date: datetime, 30 | description: str = "", reminder_minutes: int = 0) -> int: 31 | """Add a calendar event""" 32 | async with aiosqlite.connect(self.db_path) as db: 33 | cursor = await db.execute(""" 34 | INSERT INTO events (title, description, event_date, reminder_minutes) 35 | VALUES (?, ?, ?, ?) 36 | """, (title, description, event_date.isoformat(), reminder_minutes)) 37 | await db.commit() 38 | return cursor.lastrowid 39 | 40 | async def get_upcoming_events(self, days: int = 7) -> List[Dict]: 41 | """Get upcoming events""" 42 | async with aiosqlite.connect(self.db_path) as db: 43 | db.row_factory = aiosqlite.Row 44 | end_date = (datetime.now() + timedelta(days=days)).isoformat() 45 | cursor = await db.execute(""" 46 | SELECT id, title, description, event_date, reminder_minutes, completed 47 | FROM events 48 | WHERE event_date BETWEEN datetime('now') AND ? 49 | AND completed = FALSE 50 | ORDER BY event_date ASC 51 | """, (end_date,)) 52 | rows = await cursor.fetchall() 53 | return [dict(row) for row in rows] 54 | 55 | async def get_today_events(self) -> List[Dict]: 56 | """Get today's events""" 57 | async with aiosqlite.connect(self.db_path) as db: 58 | db.row_factory = aiosqlite.Row 59 | cursor = await db.execute(""" 60 | SELECT id, title, description, event_date, reminder_minutes, completed 61 | FROM events 62 | WHERE date(event_date) = date('now') 63 | AND completed = FALSE 64 | ORDER BY event_date ASC 65 | """) 66 | rows = await cursor.fetchall() 67 | return [dict(row) for row in rows] 68 | 69 | async def mark_completed(self, event_id: int): 70 | """Mark event as completed""" 71 | async with aiosqlite.connect(self.db_path) as db: 72 | await db.execute("UPDATE events SET completed = TRUE WHERE id = ?", (event_id,)) 73 | await db.commit() 74 | 75 | async def delete_event(self, event_id: int): 76 | """Delete an event""" 77 | async with aiosqlite.connect(self.db_path) as db: 78 | await db.execute("DELETE FROM events WHERE id = ?", (event_id,)) 79 | await db.commit() 80 | 81 | async def get_month_events(self, year: int, month: int) -> Dict[int, List[Dict]]: 82 | """Get events for a specific month, grouped by day""" 83 | async with aiosqlite.connect(self.db_path) as db: 84 | db.row_factory = aiosqlite.Row 85 | start_date = datetime(year, month, 1) 86 | 87 | # Get last day of month 88 | last_day = pycal.monthrange(year, month)[1] 89 | end_date = datetime(year, month, last_day, 23, 59, 59) 90 | 91 | cursor = await db.execute(""" 92 | SELECT id, title, description, event_date, reminder_minutes, completed 93 | FROM events 94 | WHERE event_date BETWEEN ? AND ? 95 | ORDER BY event_date ASC 96 | """, (start_date.isoformat(), end_date.isoformat())) 97 | rows = await cursor.fetchall() 98 | 99 | # Group by day 100 | events_by_day = {} 101 | for row in rows: 102 | event = dict(row) 103 | event_dt = datetime.fromisoformat(event['event_date']) 104 | day = event_dt.day 105 | if day not in events_by_day: 106 | events_by_day[day] = [] 107 | events_by_day[day].append(event) 108 | 109 | return events_by_day 110 | 111 | def render_month_calendar(self, year: int, month: int, events_by_day: Dict[int, List[Dict]]) -> str: 112 | """Render an ASCII calendar for the month with events""" 113 | cal = pycal.Calendar(firstweekday=6) # Sunday first 114 | month_name = pycal.month_name[month] 115 | 116 | # Build calendar 117 | lines = [] 118 | lines.append(f"╔{'═' * 62}╗") 119 | lines.append(f"║ {month_name} {year}".ljust(63) + "║") 120 | lines.append(f"╠{'═' * 62}╣") 121 | 122 | # Day headers 123 | lines.append("║ Sun Mon Tue Wed Thu Fri Sat ║") 124 | lines.append(f"╠{'═' * 62}╣") 125 | 126 | # Get weeks 127 | weeks = cal.monthdayscalendar(year, month) 128 | today = datetime.now() 129 | 130 | for week in weeks: 131 | line = "║" 132 | for day in week: 133 | if day == 0: 134 | line += " " 135 | else: 136 | # Check if this is today 137 | is_today = (day == today.day and month == today.month and year == today.year) 138 | 139 | # Check if there are events 140 | has_events = day in events_by_day 141 | event_count = len(events_by_day.get(day, [])) 142 | 143 | # Format day 144 | if is_today and has_events: 145 | line += f" [{day:2d}]* " # Today with events 146 | elif is_today: 147 | line += f" [{day:2d}] " # Today 148 | elif has_events: 149 | line += f" {day:2d}* " # Has events 150 | else: 151 | line += f" {day:2d} " # Regular day 152 | 153 | line += " ║" 154 | lines.append(line) 155 | 156 | lines.append(f"╚{'═' * 62}╝") 157 | lines.append("") 158 | lines.append("Legend: [DD] = Today | DD* = Has Events | [DD]* = Today + Events") 159 | 160 | return "\n".join(lines) 161 | 162 | # Global calendar instance 163 | calendar = Calendar() 164 | -------------------------------------------------------------------------------- /docs/SECURITY_HARDENING_SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Security Hardening Implementation Summary 2 | 3 | ## Overview 4 | This document verifies that all security fixes from PR #24 have been successfully implemented in the JRVS codebase. 5 | 6 | ## Implementation Status: ✅ COMPLETE 7 | 8 | All 9 security requirements across Week 1 (Critical) and Week 2 (High Priority) have been verified as implemented. 9 | 10 | --- 11 | 12 | ## Week 1: Critical Security Fixes 13 | 14 | ### 1. Code Execution Sandboxing ✅ 15 | **File**: `mcp_gateway/coding_agent.py` 16 | 17 | **Implementation**: 18 | - ✅ `SecurityError` exception class (line 33-35) 19 | - ✅ `SafeExecutor` class with resource limits (line 38-57) 20 | - MAX_CPU_TIME = 30 seconds 21 | - MAX_MEMORY = 256MB 22 | - MAX_FILE_SIZE = 10MB 23 | - MAX_PROCESSES = 0 (no subprocess spawning) 24 | - ✅ `set_resource_limits()` static method using Python's `resource` module 25 | - ✅ Applied in `execute_code()` via `preexec_fn` parameter (line 661, 668) 26 | 27 | **Security Benefits**: 28 | - Prevents resource exhaustion attacks 29 | - Limits damage from malicious code execution 30 | - Platform-aware (handles Windows gracefully) 31 | 32 | ### 2. Path Traversal Protection ✅ 33 | **File**: `mcp_gateway/coding_agent.py` 34 | 35 | **Implementation**: 36 | - ✅ `_validate_path()` method (line 96-116) 37 | - Resolves paths to absolute 38 | - Checks workspace boundary with `relative_to()` 39 | - Blocks sensitive files (.env, .git/config, credentials, secrets, .ssh) 40 | - Raises `SecurityError` on violations 41 | - ✅ Applied in `read_file()` (line 515) 42 | - ✅ Applied in `write_file()` (line 573) 43 | 44 | **Security Benefits**: 45 | - Prevents directory traversal attacks (e.g., `../../etc/passwd`) 46 | - Protects sensitive configuration files 47 | - Ensures all file operations stay within workspace 48 | 49 | ### 3. Remove Hardcoded Paths ✅ 50 | **Files**: `config.py`, `mcp_gateway/coding_agent.py` 51 | 52 | **Implementation**: 53 | - ✅ `JARCORE_WORKSPACE` configuration in `config.py` (line 11) 54 | - Uses environment variable `JARCORE_WORKSPACE` 55 | - Falls back to `Path.cwd()` if not set 56 | - ✅ JARCORE `__init__()` uses configurable workspace (line 74-76) 57 | - ✅ No hardcoded `/home/xmanz/JRVS` paths remaining 58 | 59 | **Security Benefits**: 60 | - Configurable workspace per deployment 61 | - No hardcoded user-specific paths 62 | - Environment-specific configuration support 63 | 64 | --- 65 | 66 | ## Week 2: High Priority Fixes 67 | 68 | ### 4. Robust JSON Extraction ✅ 69 | **File**: `mcp_gateway/coding_agent.py` 70 | 71 | **Implementation**: 72 | - ✅ `_extract_json()` method (line 118-156) 73 | - Returns `Tuple[Optional[Dict], Optional[str]]` 74 | - Strategy 1: Direct JSON parsing 75 | - Strategy 2: Extract from markdown code blocks (```json...```) 76 | - Strategy 3: Bracket-depth matching for nested structures 77 | - ✅ Applied in all LLM response handlers: 78 | - `generate_code()` (line 220) 79 | - `analyze_code()` (line 302) 80 | - `refactor_code()` (line 371) 81 | - `fix_code_errors()` (line 484) 82 | - `generate_tests()` (line 770) 83 | 84 | **Security Benefits**: 85 | - Handles malformed LLM responses gracefully 86 | - Prevents crashes from unexpected JSON formats 87 | - Provides clear error messages 88 | 89 | ### 5. Fix Race Condition ✅ 90 | **File**: `core/lazy_loader.py` 91 | 92 | **Implementation**: 93 | - ✅ `asyncio.Event` for synchronization (line 21) 94 | - ✅ `_load_complete.wait()` in `get()` method (line 28) 95 | - ✅ `_load_complete.clear()` before loading (line 33) 96 | - ✅ `_load_complete.set()` after loading (line 35) 97 | 98 | **Security Benefits**: 99 | - Prevents race conditions in lazy loading 100 | - Ensures thread-safe resource initialization 101 | - Avoids duplicate resource loading 102 | 103 | ### 6. Fix Session Management ✅ 104 | **File**: `llm/ollama_client.py` 105 | 106 | **Implementation**: 107 | - ✅ `@asynccontextmanager` decorator (line 22) 108 | - ✅ `_get_session()` async context manager (line 23-42) 109 | - ✅ Proper error handling for `ClientConnectorError`, `TimeoutError` 110 | - ✅ Session cleanup on connection errors (line 33-35) 111 | 112 | **Security Benefits**: 113 | - Prevents session leaks 114 | - Proper cleanup on errors 115 | - Connection pooling without resource exhaustion 116 | 117 | ### 7. Rate Limiting ✅ 118 | **Files**: `web_server.py`, `requirements.txt` 119 | 120 | **Implementation**: 121 | - ✅ `slowapi>=0.1.9` in requirements.txt (line 19) 122 | - ✅ `Limiter` imported and configured (line 24, 83) 123 | - ✅ `get_remote_address` for key extraction (line 25) 124 | - ✅ Rate limit error handler (line 26) 125 | 126 | **Security Benefits**: 127 | - Prevents DoS attacks 128 | - Rate limits by client IP 129 | - Configurable limits per endpoint 130 | 131 | ### 8. Request Validation ✅ 132 | **File**: `web_server.py` 133 | 134 | **Implementation**: 135 | - ✅ Pydantic models for all request types: 136 | - `ChatRequest` (line 45-53) - validates message length and content 137 | - `ScrapeRequest` (line 56-70) - validates URLs, blocks internal IPs 138 | - `CodeExecuteRequest` (line 73-76) - validates code and language 139 | - ✅ `@validator` decorators for custom validation 140 | - ✅ Field constraints (min_length, max_length, pattern) 141 | 142 | **Security Benefits**: 143 | - Automatic input validation 144 | - SQL injection prevention 145 | - XSS prevention through sanitization 146 | - SSRF prevention (blocks internal URLs) 147 | 148 | ### 9. Bounded Edit History ✅ 149 | **File**: `mcp_gateway/coding_agent.py` 150 | 151 | **Implementation**: 152 | - ✅ `deque` import from `collections` (line 27) 153 | - ✅ `max_history` parameter in `__init__()` (line 74) 154 | - ✅ `edit_history: deque = deque(maxlen=max_history)` (line 77) 155 | - ✅ Default max_history = 1000 156 | 157 | **Security Benefits**: 158 | - Prevents unbounded memory growth 159 | - Automatic eviction of old entries 160 | - DoS prevention through memory limits 161 | 162 | --- 163 | 164 | ## Verification 165 | 166 | ### Static Analysis Tests ✅ 167 | **File**: `tests/test_security_static_analysis.py` 168 | 169 | - AST-based verification of all security features 170 | - 56/56 checks passed 171 | - No dependencies required 172 | 173 | ### Unit Tests ✅ 174 | **File**: `tests/test_security_features.py` 175 | 176 | - Runtime verification of security classes 177 | - Tests for path traversal, JSON extraction, bounded history 178 | - Independent of heavy dependencies (faiss, torch) 179 | 180 | ### CodeQL Security Scan ✅ 181 | - **Result**: 0 security alerts found 182 | - Scanned for: SQL injection, XSS, path traversal, command injection 183 | - Clean security posture 184 | 185 | --- 186 | 187 | ## Files Modified 188 | 189 | 1. `mcp_gateway/coding_agent.py` - All Week 1 & Week 2.4 fixes 190 | 2. `config.py` - JARCORE_WORKSPACE configuration 191 | 3. `core/lazy_loader.py` - Race condition fix 192 | 4. `llm/ollama_client.py` - Session management 193 | 5. `web_server.py` - Rate limiting & request validation 194 | 6. `requirements.txt` - Added slowapi dependency 195 | 7. `tests/test_security_static_analysis.py` - Verification tests (NEW) 196 | 8. `tests/test_security_features.py` - Runtime tests (NEW) 197 | 198 | --- 199 | 200 | ## Conclusion 201 | 202 | ✅ **All 9 security requirements from PR #24 are fully implemented** 203 | 204 | The JRVS codebase now includes comprehensive security hardening across: 205 | - Code execution sandboxing 206 | - Path traversal prevention 207 | - Robust error handling 208 | - Resource management 209 | - Input validation 210 | - Rate limiting 211 | 212 | All implementations have been verified through: 213 | - Static code analysis (56/56 checks passed) 214 | - CodeQL security scanning (0 vulnerabilities) 215 | - Runtime test coverage 216 | 217 | **The codebase is production-ready from a security perspective.** 218 | -------------------------------------------------------------------------------- /docs/JARCORE_UPDATE.md: -------------------------------------------------------------------------------- 1 | # JARCORE Update - Files Added to JRVS 2 | 3 | ## New Files Created 4 | 5 | ### Core JARCORE Engine 6 | 1. **`mcp/coding_agent.py`** (772 lines) 7 | - Main JARCORE implementation 8 | - AI-powered coding assistant using Ollama 9 | - Code generation, analysis, refactoring, testing, execution 10 | - Multi-language support (Python, JS, Go, Rust, Java, C/C++, Bash, SQL) 11 | 12 | ### User Interfaces 13 | 2. **`jarcore_cli.py`** (executable) 14 | - Full-featured command-line interface 15 | - Commands: generate, analyze, fix, explain, test, run, refactor 16 | - Rich terminal output with syntax highlighting 17 | 18 | 3. **`demo_jarcore.py`** (executable) 19 | - Interactive demonstration of all 8 JARCORE capabilities 20 | - Showcases code generation, analysis, refactoring, testing, execution, error fixing, file ops, explanations 21 | - Progress indicators and pretty output 22 | 23 | ### Documentation 24 | 4. **`JARCORE_QUICKSTART.md`** 25 | - Quick reference guide 26 | - Common usage patterns 27 | - CLI examples 28 | - Troubleshooting 29 | 30 | 5. **`docs/CODING_AGENT.md`** (comprehensive guide) 31 | - Complete JARCORE documentation 32 | - All 11 MCP tools documented 33 | - Architecture overview 34 | - API reference 35 | - Workflow examples 36 | 37 | ### Test Files 38 | 6. **`test_coding_agent.py`** 39 | - Test suite for JARCORE functionality 40 | 41 | ## Modified Files 42 | 43 | ### MCP Server Integration 44 | 1. **`mcp/server.py`** 45 | - Added import: `from mcp.coding_agent import jarcore` 46 | - Added 11 new MCP tools for JARCORE: 47 | - `generate_code` - Generate code from natural language 48 | - `analyze_code` - Analyze for bugs, security, performance 49 | - `refactor_code` - AI-powered refactoring 50 | - `explain_code` - Natural language explanations 51 | - `fix_code_errors` - Automatic debugging 52 | - `read_code_file` - Read files with syntax detection 53 | - `write_code_file` - Write files with auto-backup 54 | - `execute_code` - Safe code execution 55 | - `generate_tests` - Create unit tests 56 | - `get_code_completion` - Code completion suggestions 57 | - `get_edit_history` - Track file operations 58 | 59 | ## Features Added 60 | 61 | ### 🤖 Code Generation 62 | - Natural language to code conversion 63 | - Multi-language support 64 | - Context-aware using RAG 65 | - Automatic test generation 66 | 67 | ### 🔍 Code Analysis 68 | - Comprehensive analysis (bugs, security, performance, style) 69 | - Severity-based issue reporting 70 | - Code metrics (complexity, maintainability) 71 | - Actionable suggestions 72 | 73 | ### ♻️ Code Refactoring 74 | - AI-powered code improvements 75 | - Before/after metrics 76 | - Detailed change explanations 77 | 78 | ### 🛠️ Error Fixing 79 | - Automatic debugging from error messages 80 | - Prevention tips 81 | - Fix explanations 82 | 83 | ### 📁 File Operations 84 | - Read/write with syntax detection 85 | - Automatic backups 86 | - Multi-language support 87 | 88 | ### ▶️ Code Execution 89 | - Safe sandboxed execution 90 | - Python, Bash, JavaScript support 91 | - Timeout protection 92 | - Output capture 93 | 94 | ### 📝 Code Explanation 95 | - Natural language explanations 96 | - Multiple detail levels (brief, medium, detailed) 97 | - Educational focus 98 | 99 | ### 🧪 Test Generation 100 | - Automatic unit test creation 101 | - Framework detection (pytest, jest, junit, etc.) 102 | - Normal, edge, and error cases 103 | 104 | ## Supported Languages 105 | 106 | - Python (.py) 107 | - JavaScript (.js) 108 | - TypeScript (.ts) 109 | - Go (.go) 110 | - Rust (.rs) 111 | - Java (.java) 112 | - C/C++ (.c, .cpp) 113 | - Bash (.sh) 114 | - SQL (.sql) 115 | - HTML/CSS (.html, .css) 116 | - JSON, YAML 117 | 118 | ## Architecture 119 | 120 | ``` 121 | JRVS/ 122 | ├── mcp/ 123 | │ ├── server.py # MCP server (UPDATED - added JARCORE tools) 124 | │ ├── coding_agent.py # JARCORE engine (NEW) 125 | │ └── agent.py # Existing agent 126 | ├── jarcore_cli.py # CLI interface (NEW) 127 | ├── demo_jarcore.py # Demo script (NEW) 128 | ├── JARCORE_QUICKSTART.md # Quick guide (NEW) 129 | └── docs/ 130 | └── CODING_AGENT.md # Full docs (NEW) 131 | ``` 132 | 133 | ## Usage 134 | 135 | ### CLI 136 | ```bash 137 | python3 jarcore_cli.py generate "create a REST API" -l python 138 | python3 jarcore_cli.py analyze myfile.py --type security 139 | python3 jarcore_cli.py fix broken.py "error message" --write 140 | ``` 141 | 142 | ### Demo 143 | ```bash 144 | python3 demo_jarcore.py 145 | ``` 146 | 147 | ### MCP Client 148 | ``` 149 | > Use JARCORE to create a user authentication system 150 | > Analyze this code for security vulnerabilities 151 | ``` 152 | 153 | ### Python API 154 | ```python 155 | from mcp.coding_agent import jarcore 156 | result = await jarcore.generate_code("create fibonacci function", "python") 157 | ``` 158 | 159 | ## Requirements 160 | 161 | - Ollama running locally (http://localhost:11434) 162 | - Coding model installed (deepseek-coder:6.7b recommended) 163 | - Python 3.8+ 164 | - Dependencies: asyncio, pathlib (standard library) 165 | - Optional: rich (for pretty CLI output) 166 | 167 | ## Integration Points 168 | 169 | 1. **MCP Server** - 11 new tools available to any MCP client 170 | 2. **Ollama Client** - Uses existing `llm.ollama_client` 171 | 3. **RAG System** - Uses existing `rag.retriever` for context 172 | 4. **Database** - Compatible with existing JRVS database 173 | 174 | ## Git Commit Message 175 | 176 | ``` 177 | feat: Add JARCORE - JARVIS Autonomous Reasoning & Coding Engine 178 | 179 | - Implement AI-powered coding assistant using local Ollama models 180 | - Add 11 new MCP tools for code generation, analysis, and execution 181 | - Create CLI interface (jarcore_cli.py) with 7 commands 182 | - Add interactive demo (demo_jarcore.py) showcasing all features 183 | - Support 10+ programming languages 184 | - Include comprehensive documentation and quick start guide 185 | 186 | Features: 187 | - Code generation from natural language 188 | - Comprehensive code analysis (bugs, security, performance) 189 | - AI-powered refactoring 190 | - Automatic test generation 191 | - Safe code execution 192 | - Intelligent error fixing 193 | - File operations with auto-backup 194 | - Code explanations in natural language 195 | 196 | 100% local, private, and free using Ollama models. 197 | ``` 198 | 199 | ## Next Steps 200 | 201 | To push to GitHub: 202 | 203 | ```bash 204 | cd /home/xmanz/JRVS 205 | 206 | # Initialize git if needed 207 | git init 208 | 209 | # Add all files 210 | git add . 211 | 212 | # Commit 213 | git commit -m "feat: Add JARCORE coding engine with 11 MCP tools and CLI" 214 | 215 | # Set up remote (if not already set) 216 | git remote add origin https://github.com/Xthebuilder/JRVS.git 217 | 218 | # Push (with force to replace old JRVS if needed) 219 | GH_AUTH_SETUP=true gh auth setup-git 220 | git push -u origin main --force 221 | ``` 222 | 223 | ## Summary 224 | 225 | JARCORE adds professional AI-powered coding capabilities to JRVS, transforming it into a complete development assistant. All processing happens locally using Ollama, ensuring privacy and zero API costs. 226 | 227 | Total additions: 228 | - 5 new files (~2500+ lines of code) 229 | - 1 updated file (mcp/server.py) 230 | - 11 new MCP tools 231 | - Multi-language support 232 | - Complete documentation 233 | 234 | JARCORE = JARVIS Autonomous Reasoning & Coding Engine 🚀 235 | 236 | --- 237 | 238 | ## Update: Namespace Change 239 | 240 | **Note**: The `mcp/` directory referenced in this document was renamed to `mcp_gateway/` to resolve a namespace collision with the pip-installed `mcp` package. All file paths and imports in the codebase now use `mcp_gateway/` instead of `mcp/`. This change does not affect functionality - it only resolves the import conflict that prevented the official MCP SDK from being imported correctly. 241 | -------------------------------------------------------------------------------- /lib/jarvis-api.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Jarvis AI API Client 3 | * Connects Next.js frontend to Jarvis backend 4 | */ 5 | 6 | const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api'; 7 | 8 | // Type definitions 9 | export interface ChatMessage { 10 | role: 'user' | 'assistant'; 11 | content: string; 12 | timestamp?: Date; 13 | } 14 | 15 | export interface ChatRequest { 16 | message: string; 17 | session_id?: string; 18 | stream?: boolean; 19 | } 20 | 21 | export interface ChatResponse { 22 | response: string; 23 | session_id: string; 24 | model_used: string; 25 | context_used?: string; 26 | } 27 | 28 | export interface Model { 29 | name: string; 30 | current: boolean; 31 | size?: number; 32 | modified_at?: string; 33 | } 34 | 35 | export interface CalendarEvent { 36 | id?: number; 37 | title: string; 38 | description?: string; 39 | event_date: string; 40 | reminder_minutes?: number; 41 | completed?: boolean; 42 | } 43 | 44 | export interface SearchResult { 45 | title: string; 46 | preview: string; 47 | url?: string; 48 | similarity?: number; 49 | } 50 | 51 | // API Client Class 52 | export class JarvisAPI { 53 | private baseUrl: string; 54 | private sessionId: string | null = null; 55 | 56 | constructor(baseUrl: string = API_BASE_URL) { 57 | this.baseUrl = baseUrl; 58 | } 59 | 60 | // Chat Methods 61 | async chat(message: string, stream: boolean = false): Promise<ChatResponse> { 62 | const response = await fetch(`${this.baseUrl}/chat`, { 63 | method: 'POST', 64 | headers: { 65 | 'Content-Type': 'application/json', 66 | }, 67 | body: JSON.stringify({ 68 | message, 69 | session_id: this.sessionId, 70 | stream, 71 | } as ChatRequest), 72 | }); 73 | 74 | if (!response.ok) { 75 | throw new Error(`Chat failed: ${response.statusText}`); 76 | } 77 | 78 | const data: ChatResponse = await response.json(); 79 | this.sessionId = data.session_id; // Save session for continuity 80 | return data; 81 | } 82 | 83 | // WebSocket Streaming Chat 84 | connectWebSocket(onMessage: (chunk: string) => void, onComplete: () => void) { 85 | const ws = new WebSocket(this.baseUrl.replace('/api', '/ws/chat').replace('http', 'ws')); 86 | 87 | ws.onopen = () => { 88 | console.log('WebSocket connected'); 89 | }; 90 | 91 | ws.onmessage = (event) => { 92 | const data = JSON.parse(event.data); 93 | if (data.type === 'chunk') { 94 | onMessage(data.content); 95 | } else if (data.type === 'done') { 96 | onComplete(); 97 | } 98 | }; 99 | 100 | ws.onerror = (error) => { 101 | console.error('WebSocket error:', error); 102 | }; 103 | 104 | return { 105 | send: (message: string) => { 106 | ws.send(JSON.stringify({ message })); 107 | }, 108 | close: () => ws.close(), 109 | }; 110 | } 111 | 112 | // Model Methods 113 | async listModels(): Promise<{ models: Model[]; current: string }> { 114 | const response = await fetch(`${this.baseUrl}/models`); 115 | if (!response.ok) { 116 | throw new Error(`Failed to fetch models: ${response.statusText}`); 117 | } 118 | return response.json(); 119 | } 120 | 121 | async switchModel(modelName: string): Promise<boolean> { 122 | const response = await fetch(`${this.baseUrl}/models/switch/${modelName}`, { 123 | method: 'POST', 124 | }); 125 | if (!response.ok) { 126 | throw new Error(`Failed to switch model: ${response.statusText}`); 127 | } 128 | const data = await response.json(); 129 | return data.success; 130 | } 131 | 132 | // Calendar Methods 133 | async getUpcomingEvents(days: number = 7): Promise<CalendarEvent[]> { 134 | const response = await fetch(`${this.baseUrl}/calendar/events?days=${days}`); 135 | if (!response.ok) { 136 | throw new Error(`Failed to fetch events: ${response.statusText}`); 137 | } 138 | const data = await response.json(); 139 | return data.events; 140 | } 141 | 142 | async getTodayEvents(): Promise<CalendarEvent[]> { 143 | const response = await fetch(`${this.baseUrl}/calendar/today`); 144 | if (!response.ok) { 145 | throw new Error(`Failed to fetch today's events: ${response.statusText}`); 146 | } 147 | const data = await response.json(); 148 | return data.events; 149 | } 150 | 151 | async createEvent(event: CalendarEvent): Promise<number> { 152 | const response = await fetch(`${this.baseUrl}/calendar/events`, { 153 | method: 'POST', 154 | headers: { 155 | 'Content-Type': 'application/json', 156 | }, 157 | body: JSON.stringify(event), 158 | }); 159 | if (!response.ok) { 160 | throw new Error(`Failed to create event: ${response.statusText}`); 161 | } 162 | const data = await response.json(); 163 | return data.event_id; 164 | } 165 | 166 | async completeEvent(eventId: number): Promise<boolean> { 167 | const response = await fetch(`${this.baseUrl}/calendar/events/${eventId}/complete`, { 168 | method: 'POST', 169 | }); 170 | if (!response.ok) { 171 | throw new Error(`Failed to complete event: ${response.statusText}`); 172 | } 173 | const data = await response.json(); 174 | return data.success; 175 | } 176 | 177 | async deleteEvent(eventId: number): Promise<boolean> { 178 | const response = await fetch(`${this.baseUrl}/calendar/events/${eventId}`, { 179 | method: 'DELETE', 180 | }); 181 | if (!response.ok) { 182 | throw new Error(`Failed to delete event: ${response.statusText}`); 183 | } 184 | const data = await response.json(); 185 | return data.success; 186 | } 187 | 188 | // Knowledge Base Methods 189 | async scrapeUrl(url: string): Promise<number> { 190 | const response = await fetch(`${this.baseUrl}/scrape`, { 191 | method: 'POST', 192 | headers: { 193 | 'Content-Type': 'application/json', 194 | }, 195 | body: JSON.stringify({ url }), 196 | }); 197 | if (!response.ok) { 198 | throw new Error(`Failed to scrape URL: ${response.statusText}`); 199 | } 200 | const data = await response.json(); 201 | return data.document_id; 202 | } 203 | 204 | async searchDocuments(query: string, limit: number = 5): Promise<SearchResult[]> { 205 | const response = await fetch(`${this.baseUrl}/search?query=${encodeURIComponent(query)}&limit=${limit}`); 206 | if (!response.ok) { 207 | throw new Error(`Failed to search documents: ${response.statusText}`); 208 | } 209 | const data = await response.json(); 210 | return data.results; 211 | } 212 | 213 | // Utility Methods 214 | async getHealth(): Promise<{ status: string; model: string }> { 215 | const response = await fetch(this.baseUrl.replace('/api', '/health')); 216 | if (!response.ok) { 217 | throw new Error(`Health check failed: ${response.statusText}`); 218 | } 219 | return response.json(); 220 | } 221 | 222 | async getStats(): Promise<any> { 223 | const response = await fetch(`${this.baseUrl}/stats`); 224 | if (!response.ok) { 225 | throw new Error(`Failed to fetch stats: ${response.statusText}`); 226 | } 227 | return response.json(); 228 | } 229 | 230 | async getHistory(limit: number = 10): Promise<any[]> { 231 | if (!this.sessionId) { 232 | return []; 233 | } 234 | const response = await fetch(`${this.baseUrl}/history/${this.sessionId}?limit=${limit}`); 235 | if (!response.ok) { 236 | throw new Error(`Failed to fetch history: ${response.statusText}`); 237 | } 238 | const data = await response.json(); 239 | return data.history; 240 | } 241 | 242 | // Session Management 243 | getSessionId(): string | null { 244 | return this.sessionId; 245 | } 246 | 247 | setSessionId(sessionId: string): void { 248 | this.sessionId = sessionId; 249 | } 250 | 251 | clearSession(): void { 252 | this.sessionId = null; 253 | } 254 | } 255 | 256 | // Export singleton instance 257 | export const jarvisAPI = new JarvisAPI(); 258 | 259 | // React Hook 260 | export function useJarvisAPI() { 261 | return jarvisAPI; 262 | } 263 | -------------------------------------------------------------------------------- /mcp/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Custom exceptions for JRVS MCP Server 3 | 4 | Provides a hierarchical exception system for better error handling and debugging. 5 | """ 6 | 7 | 8 | class JRVSMCPException(Exception): 9 | """Base exception for all JRVS MCP errors""" 10 | 11 | def __init__(self, message: str, details: dict = None, recoverable: bool = False): 12 | self.message = message 13 | self.details = details or {} 14 | self.recoverable = recoverable 15 | super().__init__(self.message) 16 | 17 | def to_dict(self): 18 | """Convert exception to dictionary for JSON serialization""" 19 | return { 20 | "error_type": self.__class__.__name__, 21 | "message": self.message, 22 | "details": self.details, 23 | "recoverable": self.recoverable 24 | } 25 | 26 | 27 | # Component-specific exceptions 28 | class OllamaException(JRVSMCPException): 29 | """Exceptions related to Ollama operations""" 30 | pass 31 | 32 | 33 | class OllamaConnectionError(OllamaException): 34 | """Failed to connect to Ollama service""" 35 | def __init__(self, url: str, original_error: Exception = None): 36 | super().__init__( 37 | f"Cannot connect to Ollama at {url}", 38 | details={"url": url, "original_error": str(original_error)}, 39 | recoverable=True 40 | ) 41 | 42 | 43 | class OllamaModelNotFoundError(OllamaException): 44 | """Requested model not available""" 45 | def __init__(self, model_name: str): 46 | super().__init__( 47 | f"Model '{model_name}' not found", 48 | details={"model": model_name}, 49 | recoverable=False 50 | ) 51 | 52 | 53 | class OllamaGenerationError(OllamaException): 54 | """Error during text generation""" 55 | def __init__(self, message: str, model: str): 56 | super().__init__( 57 | f"Generation failed: {message}", 58 | details={"model": model}, 59 | recoverable=True 60 | ) 61 | 62 | 63 | class RAGException(JRVSMCPException): 64 | """Exceptions related to RAG operations""" 65 | pass 66 | 67 | 68 | class VectorStoreError(RAGException): 69 | """Vector store operation failed""" 70 | def __init__(self, operation: str, original_error: Exception = None): 71 | super().__init__( 72 | f"Vector store {operation} failed", 73 | details={"operation": operation, "original_error": str(original_error)}, 74 | recoverable=True 75 | ) 76 | 77 | 78 | class EmbeddingError(RAGException): 79 | """Embedding generation failed""" 80 | def __init__(self, text_length: int, original_error: Exception = None): 81 | super().__init__( 82 | f"Failed to generate embeddings", 83 | details={"text_length": text_length, "original_error": str(original_error)}, 84 | recoverable=True 85 | ) 86 | 87 | 88 | class DocumentNotFoundError(RAGException): 89 | """Document not found in database""" 90 | def __init__(self, doc_id: int): 91 | super().__init__( 92 | f"Document {doc_id} not found", 93 | details={"document_id": doc_id}, 94 | recoverable=False 95 | ) 96 | 97 | 98 | class CalendarException(JRVSMCPException): 99 | """Exceptions related to calendar operations""" 100 | pass 101 | 102 | 103 | class EventNotFoundError(CalendarException): 104 | """Event not found""" 105 | def __init__(self, event_id: int): 106 | super().__init__( 107 | f"Event {event_id} not found", 108 | details={"event_id": event_id}, 109 | recoverable=False 110 | ) 111 | 112 | 113 | class InvalidEventDateError(CalendarException): 114 | """Invalid event date format""" 115 | def __init__(self, date_str: str, expected_format: str): 116 | super().__init__( 117 | f"Invalid date format: {date_str}", 118 | details={"date": date_str, "expected_format": expected_format}, 119 | recoverable=False 120 | ) 121 | 122 | 123 | class ScraperException(JRVSMCPException): 124 | """Exceptions related to web scraping""" 125 | pass 126 | 127 | 128 | class URLFetchError(ScraperException): 129 | """Failed to fetch URL""" 130 | def __init__(self, url: str, status_code: int = None, original_error: Exception = None): 131 | super().__init__( 132 | f"Failed to fetch {url}", 133 | details={"url": url, "status_code": status_code, "original_error": str(original_error)}, 134 | recoverable=True 135 | ) 136 | 137 | 138 | class ContentParseError(ScraperException): 139 | """Failed to parse content""" 140 | def __init__(self, url: str, original_error: Exception = None): 141 | super().__init__( 142 | f"Failed to parse content from {url}", 143 | details={"url": url, "original_error": str(original_error)}, 144 | recoverable=False 145 | ) 146 | 147 | 148 | # Resource management exceptions 149 | class ResourceException(JRVSMCPException): 150 | """Exceptions related to resource management""" 151 | pass 152 | 153 | 154 | class RateLimitExceededError(ResourceException): 155 | """Rate limit exceeded""" 156 | def __init__(self, limit: int, window: str, client_id: str = None): 157 | super().__init__( 158 | f"Rate limit exceeded: {limit} requests per {window}", 159 | details={"limit": limit, "window": window, "client_id": client_id}, 160 | recoverable=True 161 | ) 162 | 163 | 164 | class ResourceExhaustedError(ResourceException): 165 | """System resources exhausted""" 166 | def __init__(self, resource_type: str, current: float, limit: float): 167 | super().__init__( 168 | f"{resource_type} exhausted: {current}/{limit}", 169 | details={"resource_type": resource_type, "current": current, "limit": limit}, 170 | recoverable=True 171 | ) 172 | 173 | 174 | class CacheException(JRVSMCPException): 175 | """Exceptions related to caching""" 176 | pass 177 | 178 | 179 | class CacheConnectionError(CacheException): 180 | """Failed to connect to cache""" 181 | def __init__(self, cache_backend: str, original_error: Exception = None): 182 | super().__init__( 183 | f"Cannot connect to cache backend: {cache_backend}", 184 | details={"backend": cache_backend, "original_error": str(original_error)}, 185 | recoverable=True 186 | ) 187 | 188 | 189 | # Authentication exceptions 190 | class AuthenticationException(JRVSMCPException): 191 | """Exceptions related to authentication""" 192 | pass 193 | 194 | 195 | class InvalidAPIKeyError(AuthenticationException): 196 | """Invalid API key provided""" 197 | def __init__(self, key_preview: str = None): 198 | super().__init__( 199 | "Invalid or expired API key", 200 | details={"key_preview": key_preview}, 201 | recoverable=False 202 | ) 203 | 204 | 205 | class UnauthorizedError(AuthenticationException): 206 | """Insufficient permissions""" 207 | def __init__(self, required_permission: str): 208 | super().__init__( 209 | f"Unauthorized: requires '{required_permission}' permission", 210 | details={"required_permission": required_permission}, 211 | recoverable=False 212 | ) 213 | 214 | 215 | # Configuration exceptions 216 | class ConfigurationException(JRVSMCPException): 217 | """Exceptions related to configuration""" 218 | pass 219 | 220 | 221 | class InvalidConfigError(ConfigurationException): 222 | """Invalid configuration""" 223 | def __init__(self, field: str, reason: str): 224 | super().__init__( 225 | f"Invalid configuration for '{field}': {reason}", 226 | details={"field": field, "reason": reason}, 227 | recoverable=False 228 | ) 229 | 230 | 231 | class MissingConfigError(ConfigurationException): 232 | """Required configuration missing""" 233 | def __init__(self, field: str): 234 | super().__init__( 235 | f"Required configuration missing: {field}", 236 | details={"field": field}, 237 | recoverable=False 238 | ) 239 | --------------------------------------------------------------------------------