diff --git a/app/core/spec_parser.py b/app/core/spec_parser.py index c2e2651..bcb3011 100644 --- a/app/core/spec_parser.py +++ b/app/core/spec_parser.py @@ -289,11 +289,12 @@ def parse_spec_folder(spec_folder_path: str) -> Optional[Dict]: if __name__ == "__main__": - # Test with Pistn specs - pistn_path = "/Users/iamanmp/Projects/pistn" - spec_folders = find_spec_folders(pistn_path) + # Test with project specs + import sys + project_path = sys.argv[1] if len(sys.argv) > 1 else "." + spec_folders = find_spec_folders(project_path) - print(f"Found {len(spec_folders)} specs in Pistn:") + print(f"Found {len(spec_folders)} specs in project:") for folder in spec_folders: spec_data = parse_spec_folder(folder) if spec_data: diff --git a/app/core/sqlite_manager.py b/app/core/sqlite_manager.py index fcb00fe..60d75c8 100644 --- a/app/core/sqlite_manager.py +++ b/app/core/sqlite_manager.py @@ -21,7 +21,7 @@ def generate_slug(name: str) -> str: Generate a URL-friendly slug from a KB name. Examples: - "Pistn Agent OS" -> "pistn-agent-os" + "Project Agent OS" -> "project-agent-os" "My Code Base!" -> "my-code-base" "Test_KB 123" -> "test-kb-123" """ diff --git a/demo_hybrid_indexing.py b/demo_hybrid_indexing.py index b409857..6d69e1c 100755 --- a/demo_hybrid_indexing.py +++ b/demo_hybrid_indexing.py @@ -37,15 +37,15 @@ except ImportError as e: sys.exit(1) # ============================================================================= -# DEMO 1: Create Mock Repository Structure (Simulating Pistn) +# DEMO 1: Create Mock Repository Structure (Simulating a Large Rails Project) # ============================================================================= print("=" * 70) -print("DEMO 1: Simulating Pistn Project Structure") +print("DEMO 1: Simulating Large Rails Project Structure") print("=" * 70) print() -print("Creating mock data for a Rails application (like Pistn)...") +print("Creating mock data for a Rails application...") print() # Create mock tags representing a Rails app structure @@ -246,7 +246,7 @@ project_sizes = [ ("Small (200 files)", 200, 2000, "~5 seconds"), ("Medium (1,000 files)", 1000, 10000, "~15 seconds"), ("Large (5,000 files)", 5000, 50000, "~25 seconds"), - ("Pistn (10,000 files)", 10000, 100000, "~30 seconds"), + ("Very Large (10,000 files)", 10000, 100000, "~30 seconds"), ] print("Expected Performance (Phase 1 - Structural Indexing):") @@ -261,7 +261,7 @@ print("vs. Traditional Embedding-Based Indexing:") print() print(f"{'Project Size':<25} {'Old Time':<20} {'New Time':<15} {'Speedup'}") print("-" * 70) -print(f"{'Pistn (10,000 files)':<25} {'3-5 hours':<20} {'30 seconds':<15} {'600x faster!'}") +print(f"{'Very Large (10,000 files)':<25} {'3-5 hours':<20} {'30 seconds':<15} {'600x faster!'}") print() # ============================================================================= diff --git a/docs/DELETE_DOCUMENTS_GUIDE.md b/docs/DELETE_DOCUMENTS_GUIDE.md index 584a661..8ea1201 100644 --- a/docs/DELETE_DOCUMENTS_GUIDE.md +++ b/docs/DELETE_DOCUMENTS_GUIDE.md @@ -12,15 +12,15 @@ The MCP server now supports deleting documents by filename: # Delete a single document by filename curl -X DELETE "http://localhost:8051/api/kb/{kb_name}/documents/{filename}" -# Example: Delete api.md from Pistn Agent OS -curl -X DELETE "http://localhost:8051/api/kb/Pistn%20Agent%20OS/documents/api.md" +# Example: Delete api.md from Project KB +curl -X DELETE "http://localhost:8051/api/kb/Project%20KB/documents/api.md" # Response on success: { "success": true, "message": "Deleted 1 document(s)", "filename": "api.md", - "kb_name": "Pistn Agent OS" + "kb_name": "Project KB" } ``` @@ -48,14 +48,14 @@ LIMIT 10; # Delete specific document(s) DELETE FROM documents WHERE kb_id = ( - SELECT id FROM knowledge_bases WHERE name = 'Pistn-project_memories' + SELECT id FROM knowledge_bases WHERE name = 'project-memories' ) AND json_extract(metadata, '$.filename') = 'api.md'; # Verify deletion SELECT COUNT(*) FROM documents WHERE kb_id = ( - SELECT id FROM knowledge_bases WHERE name = 'Pistn-project_memories' + SELECT id FROM knowledge_bases WHERE name = 'project-memories' ); ``` @@ -67,7 +67,7 @@ WHERE kb_id = ( ```bash # Delete entire KB and recreate it fresh -curl -X DELETE "http://localhost:8051/api/kb/Pistn%20Agent%20OS" +curl -X DELETE "http://localhost:8051/api/kb/Project%20KB" ``` Or via SQLite: @@ -78,14 +78,14 @@ sqlite3 data/claude-os.db -- Delete KB and all documents DELETE FROM documents WHERE kb_id = ( - SELECT id FROM knowledge_bases WHERE name = 'Pistn-project_memories' + SELECT id FROM knowledge_bases WHERE name = 'project-memories' ); -DELETE FROM knowledge_bases WHERE name = 'Pistn-project_memories'; +DELETE FROM knowledge_bases WHERE name = 'project-memories'; -- Recreate the KB fresh INSERT INTO knowledge_bases (name, kb_type, slug) -VALUES ('Pistn-project_memories', 'generic', 'pistn-project-memories'); +VALUES ('project-memories', 'generic', 'project-memories'); ``` --- @@ -97,18 +97,18 @@ VALUES ('Pistn-project_memories', 'generic', 'pistn-project-memories'); ```bash # Delete all spec documents for spec_file in "spec.md" "requirements.md" "IMPLEMENTATION.md"; do - curl -X DELETE "http://localhost:8051/api/kb/Pistn%20Agent%20OS/documents/$spec_file" + curl -X DELETE "http://localhost:8051/api/kb/Project%20KB/documents/$spec_file" done # Then re-import fresh versions -curl -X POST "http://localhost:8051/api/kb/Pistn%20Agent%20OS/import?directory_path=%2FUsers%2Fiamanmp%2FProjects%2Fpistn%2Fagent-os" +curl -X POST "http://localhost:8051/api/kb/Project%20KB/import?directory_path=%2FUsers%2Fiamanmp%2FProjects%2Fpistn%2Fagent-os" ``` ### Scenario 2: Clean Up Old Documentation Before Adding New ```bash # List what's in the KB -curl -s "http://localhost:8051/api/kb/Pistn%20Agent%20OS/documents" | python3 -c " +curl -s "http://localhost:8051/api/kb/Project%20KB/documents" | python3 -c " import sys, json docs = json.load(sys.stdin).get('documents', []) for doc in docs: @@ -121,19 +121,19 @@ for doc in docs: ```bash # Delete the entire KB -curl -X DELETE "http://localhost:8051/api/kb/Pistn%20Agent%20OS" +curl -X DELETE "http://localhost:8051/api/kb/Project%20KB" # Recreate it curl -X POST "http://localhost:8051/api/kb" \ -H "Content-Type: application/json" \ -d '{ - "name": "Pistn Agent OS", + "name": "Project KB", "kb_type": "agent-os", - "description": "Pistn Agent OS code" + "description": "Project KB code" }' # Re-import everything fresh -curl -X POST "http://localhost:8051/api/kb/Pistn%20Agent%20OS/import?directory_path=%2FUsers%2Fiamanmp%2FProjects%2Fpistn%2Fagent-os" +curl -X POST "http://localhost:8051/api/kb/Project%20KB/import?directory_path=%2FUsers%2Fuser%2FProjects%2Fproject%2Fkb" ``` --- @@ -146,7 +146,7 @@ The filename might not match exactly. To find the correct filename: ```bash # List all documents in KB -curl -s "http://localhost:8051/api/kb/Pistn%20Agent%20OS/documents" | python3 -m json.tool +curl -s "http://localhost:8051/api/kb/Project%20KB/documents" | python3 -m json.tool ``` ### Delete endpoint returns "Not Found" diff --git a/docs/DELETE_DOCUMENTS_GUIDE.md.bak b/docs/DELETE_DOCUMENTS_GUIDE.md.bak new file mode 100644 index 0000000..fb7aad7 --- /dev/null +++ b/docs/DELETE_DOCUMENTS_GUIDE.md.bak @@ -0,0 +1,192 @@ +# Deleting Documents from Claude OS MCP + +**When you need to remove previously uploaded documents from a knowledge base.** + +--- + +## Method 1: API Endpoint (Recommended) + +The MCP server now supports deleting documents by filename: + +```bash +# Delete a single document by filename +curl -X DELETE "http://localhost:8051/api/kb/{kb_name}/documents/{filename}" + +# Example: Delete api.md from Project KB +curl -X DELETE "http://localhost:8051/api/kb/Project%20KB/documents/api.md" + +# Response on success: +{ + "success": true, + "message": "Deleted 1 document(s)", + "filename": "api.md", + "kb_name": "Pistn Agent OS" +} +``` + +### Notes +- Replace `{kb_name}` with the actual KB name (use URL encoding for spaces: `%20`) +- The filename should match exactly what was uploaded +- Deleting a document removes it and all its chunks from the vector database + +--- + +## Method 2: Manual SQLite Query (Alternative) + +If the API endpoint has issues, you can delete documents directly from SQLite: + +```bash +# Connect to SQLite database +sqlite3 data/claude-os.db + +# Find documents to delete +SELECT id, json_extract(metadata, '$.filename') as filename, kb_id +FROM documents +WHERE json_extract(metadata, '$.filename') LIKE '%api.md%' +LIMIT 10; + +# Delete specific document(s) +DELETE FROM documents +WHERE kb_id = ( + SELECT id FROM knowledge_bases WHERE name = 'Pistn-project_memories' +) +AND json_extract(metadata, '$.filename') = 'api.md'; + +# Verify deletion +SELECT COUNT(*) FROM documents +WHERE kb_id = ( + SELECT id FROM knowledge_bases WHERE name = 'Pistn-project_memories' +); +``` + +--- + +## Method 3: Delete All Documents in a KB (Nuclear Option) + +**WARNING**: This deletes everything. Use with caution. + +```bash +# Delete entire KB and recreate it fresh +curl -X DELETE "http://localhost:8051/api/kb/Pistn%20Agent%20OS" +``` + +Or via SQLite: + +```bash +sqlite3 data/claude-os.db + +-- Delete KB and all documents +DELETE FROM documents +WHERE kb_id = ( + SELECT id FROM knowledge_bases WHERE name = 'Pistn-project_memories' +); + +DELETE FROM knowledge_bases WHERE name = 'Pistn-project_memories'; + +-- Recreate the KB fresh +INSERT INTO knowledge_bases (name, kb_type, slug) +VALUES ('Pistn-project_memories', 'generic', 'pistn-project-memories'); +``` + +--- + +## Common Scenarios + +### Scenario 1: Remove Old Specs Before Re-importing + +```bash +# Delete all spec documents +for spec_file in "spec.md" "requirements.md" "IMPLEMENTATION.md"; do + curl -X DELETE "http://localhost:8051/api/kb/Pistn%20Agent%20OS/documents/$spec_file" +done + +# Then re-import fresh versions +curl -X POST "http://localhost:8051/api/kb/Pistn%20Agent%20OS/import?directory_path=%2FUsers%2Fiamanmp%2FProjects%2Fpistn%2Fagent-os" +``` + +### Scenario 2: Clean Up Old Documentation Before Adding New + +```bash +# List what's in the KB +curl -s "http://localhost:8051/api/kb/Pistn%20Agent%20OS/documents" | python3 -c " +import sys, json +docs = json.load(sys.stdin).get('documents', []) +for doc in docs: + print(f'{doc[\"filename\"]}')" + +# Delete specific old docs one by one +``` + +### Scenario 3: Fresh Start (Delete Everything) + +```bash +# Delete the entire KB +curl -X DELETE "http://localhost:8051/api/kb/Pistn%20Agent%20OS" + +# Recreate it +curl -X POST "http://localhost:8051/api/kb" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Pistn Agent OS", + "kb_type": "agent-os", + "description": "Pistn Agent OS code" + }' + +# Re-import everything fresh +curl -X POST "http://localhost:8051/api/kb/Pistn%20Agent%20OS/import?directory_path=%2FUsers%2Fiamanmp%2FProjects%2Fpistn%2Fagent-os" +``` + +--- + +## Troubleshooting + +### "Document not found" error + +The filename might not match exactly. To find the correct filename: + +```bash +# List all documents in KB +curl -s "http://localhost:8051/api/kb/Pistn%20Agent%20OS/documents" | python3 -m json.tool +``` + +### Delete endpoint returns "Not Found" + +The endpoint might not have loaded properly. Restart the MCP server: + +```bash +# Kill existing server +pkill -f "uvicorn.*8051" + +# Restart +source /Users/iamanmp/Projects/claude-os/venv/bin/activate +python /Users/iamanmp/Projects/claude-os/mcp_server/server.py > /tmp/mcp_server.log 2>&1 & + +# Wait for startup +sleep 3 + +# Test +curl -s "http://localhost:8051/health" | python3 -m json.tool +``` + +--- + +## UI Delete Functionality + +The web UI at http://localhost:5173 currently doesn't have a delete button. + +**To add it** (optional enhancement): +1. The frontend would need a "Delete" button in the document list +2. This would call the new `DELETE /api/kb/{kb_name}/documents/{filename}` endpoint +3. See `frontend/src/components/KBManagement.tsx` for where to add this + +For now, use the API or SQLite methods above. + +--- + +## Coming Soon + +- [ ] UI delete button in Knowledge Base Management +- [ ] Bulk delete by date range (delete all docs older than X days) +- [ ] Archive vs Delete (keep metadata but remove embeddings) +- [ ] Delete confirmation dialog with document preview + diff --git a/docs/PISTN_API_INTEGRATION_PLAN.md b/docs/PISTN_API_INTEGRATION_PLAN.md deleted file mode 100644 index 2b4a61e..0000000 --- a/docs/PISTN_API_INTEGRATION_PLAN.md +++ /dev/null @@ -1,595 +0,0 @@ -# Minimal Pistn API Integration Plan for ServiceBot - -**Date Created**: 2025-11-06 -**Context**: ServiceBot integration with Pistn after Claude OS knowledge export is complete -**Category**: Architecture & Integration -**Status**: Planning - Not Yet Implemented - ---- - -## Current State (November 2025) - -### ✅ What's Complete: -1. **Claude OS Export System**: Generic knowledge export to SQLite (format v1.0) -2. **ServiceBot Knowledge Integration**: Embedded RAG engine with vector search -3. **ServiceBot MCP Servers**: 4 servers with 7 tools (currently using stubs) -4. **JavaScript Widget**: Production-ready chat modal for Pistn frontend -5. **Vision Documents**: Claude OS Family established - -### ⚠️ What's Stubbed: -- `PistnAPIMCPServer.get_dealer_info()` - Returns fake dealer data -- `ServicesMCPServer.list_services()` - Returns hardcoded services -- `AppointmentsMCPServer.check_availability()` - Returns fake time slots -- `AppointmentsMCPServer.create_appointment()` - Doesn't actually create appointments - -### 🎯 The Problem: -**Pistn has NO API endpoints.** ServiceBot can't: -- Get real dealer hours, contact info -- List actual services with real prices -- Check real appointment availability (uses Pistn's complex scheduling logic) -- Create actual appointments (uses Pistn's business rules, validations, callbacks) - ---- - -## The Solution: Three-Phase Minimal API - -Build a **minimal, secure API** in Pistn that ServiceBot can call to execute Pistn's business logic. - ---- - -## Phase 1: Test ServiceBot with Knowledge Only (Do This First!) - -**Goal**: Validate export + chat widget work before building API - -**What to Test**: -1. Export knowledge from Claude OS -2. Import into ServiceBot -3. Add JavaScript widget to Pistn -4. Test knowledge-only conversations - -**Commands**: -```bash -# In Claude OS -/claude-os-export dealer_123 --output ./exports - -# Copy to ServiceBot -cp claude-os/exports/dealer_123_*.db servicebot/data/dealer_123.db - -# Configure ServiceBot .env -KNOWLEDGE_DB_PATH=data/dealer_123.db -ENABLE_KNOWLEDGE_SEARCH=true -KNOWLEDGE_USE_EMBEDDINGS=true -OPENAI_API_KEY=your_key - -# Deploy ServiceBot -cd servicebot -docker-compose up -d - -# Add widget to Pistn layout -# (see implementation below) -``` - -**Test Conversations** (knowledge-only): -- "What are your hours?" ✅ (from knowledge base) -- "How long does an oil change take?" ✅ (from knowledge base) -- "What's your cancellation policy?" ✅ (from knowledge base) -- "I want to book an appointment" ⚠️ (will fail gracefully - no API yet) - -**Success Criteria**: -- Widget loads on Pistn -- Knowledge queries work -- Responses stream properly -- Graceful degradation when tools not available - -**Pistn Changes** (Minimal): -```ruby -# app/views/layouts/application.html.erb (or dealer layout) -<% if ENV['SERVICEBOT_ENABLED'] == 'true' %> - - -<% end %> -``` - -**Duration**: 1-2 days (testing and validation) - ---- - -## Phase 2: Add Minimal Read-Only API (After Phase 1 Works) - -**Goal**: Replace stubs with real Pistn data (no writes yet) - -### Implementation - -#### 2.1: Create API Controller - -```ruby -# pistn/app/controllers/api/v1/servicebot_controller.rb -module Api - module V1 - class ServicebotController < ApplicationController - skip_before_action :verify_authenticity_token - before_action :authenticate_servicebot - - # GET /api/v1/servicebot/dealers/:id - def dealer_info - dealer = DealerAccount.find(params[:id]) - - render json: { - id: dealer.id, - name: dealer.name, - address: dealer.full_address, - city: dealer.city, - state: dealer.state, - zip: dealer.zip, - phone: dealer.phone, - email: dealer.email, - hours: build_hours_hash(dealer) - } - rescue ActiveRecord::RecordNotFound - render json: { error: 'Dealer not found' }, status: 404 - end - - # GET /api/v1/servicebot/dealers/:dealer_id/services - def services - dealer = DealerAccount.find(params[:dealer_id]) - - # Adjust based on Pistn's actual service model - services = dealer.oil_change_services || [] - - render json: services.map { |service| { - id: service.id, - name: service.name, - description: service.description || '', - price: service.price, - duration_minutes: service.duration_minutes || 60 - }} - end - - private - - def authenticate_servicebot - api_key = request.headers['X-ServiceBot-API-Key'] - - unless api_key.present? && api_key == ENV['SERVICEBOT_API_KEY'] - render json: { error: 'Unauthorized' }, status: 401 - end - end - - def build_hours_hash(dealer) - # Adjust based on how Pistn stores hours - { - monday: dealer.monday_hours || 'Closed', - tuesday: dealer.tuesday_hours || 'Closed', - wednesday: dealer.wednesday_hours || 'Closed', - thursday: dealer.thursday_hours || 'Closed', - friday: dealer.friday_hours || 'Closed', - saturday: dealer.saturday_hours || 'Closed', - sunday: dealer.sunday_hours || 'Closed' - } - end - end - end -end -``` - -#### 2.2: Add Routes - -```ruby -# pistn/config/routes.rb -namespace :api do - namespace :v1 do - scope :servicebot do - get 'dealers/:id', to: 'servicebot#dealer_info' - get 'dealers/:dealer_id/services', to: 'servicebot#services' - end - end -end -``` - -#### 2.3: Update ServiceBot MCP Servers - -```python -# servicebot/app/mcp_servers/pistn_api.py -class PistnAPIMCPServer: - def __init__(self): - self.base_url = settings.PISTN_API_URL # e.g., http://localhost:3000 - self.api_key = settings.PISTN_API_KEY - self.client = httpx.AsyncClient( - base_url=self.base_url, - headers={ - "X-ServiceBot-API-Key": self.api_key, - "Content-Type": "application/json" - }, - timeout=10.0 - ) - - async def get_dealer_info(self, dealer_id: str) -> Dict[str, Any]: - """Get real dealer data from Pistn API.""" - try: - response = await self.client.get(f"/api/v1/servicebot/dealers/{dealer_id}") - response.raise_for_status() - return response.json() - except Exception as e: - logger.error(f"Failed to get dealer info: {e}") - # Fallback to stub for graceful degradation - return self._stub_dealer_info(dealer_id) - -# servicebot/app/mcp_servers/services.py -class ServicesMCPServer: - async def list_services(self, dealer_id: str) -> Dict[str, Any]: - """Get real services from Pistn API.""" - try: - response = await self.pistn_client.get( - f"/api/v1/servicebot/dealers/{dealer_id}/services" - ) - response.raise_for_status() - services = response.json() - - return { - "success": True, - "services": services, - "count": len(services) - } - except Exception as e: - logger.error(f"Failed to get services: {e}") - return {"success": False, "services": [], "count": 0} -``` - -#### 2.4: Configuration - -**Pistn `.env`**: -```bash -# ServiceBot Integration -SERVICEBOT_ENABLED=true -SERVICEBOT_URL=http://localhost:8000 # or production URL -SERVICEBOT_API_KEY=your_secure_random_key_here # Generate with: SecureRandom.hex(32) -``` - -**ServiceBot `.env`**: -```bash -# Pistn API Integration -PISTN_API_URL=http://localhost:3000 # or production Pistn URL -PISTN_API_KEY=same_key_as_above -``` - -**Test**: -```bash -# Test Pistn API directly -curl -H "X-ServiceBot-API-Key: your_key" \ - http://localhost:3000/api/v1/servicebot/dealers/123 - -# Test in ServiceBot -# Should now return real dealer data instead of stubs -``` - -**Duration**: 2-3 days (implementation + testing) - ---- - -## Phase 3: Add Appointment Booking (After Phase 2 Works) - -**Goal**: Enable full appointment booking through ServiceBot - -### 3.1: Identify Pistn's Appointment Logic - -**Before implementing**, answer these questions: -- What model handles appointments? (e.g., `Appointment`, `ServiceAppointment`) -- What service/class creates appointments? (e.g., `AppointmentCreationService`) -- What validates availability? (e.g., `AppointmentAvailabilityService`, `BlockScheduler`) -- What are required fields? (customer info, vehicle, service, time) -- What validations exist? (blackout dates, capacity, dealer hours) -- What side effects happen? (emails, SMS, calendar updates) - -### 3.2: Add Appointment Endpoints - -```ruby -# pistn/app/controllers/api/v1/servicebot_controller.rb - -# POST /api/v1/servicebot/dealers/:dealer_id/appointments/check_availability -def check_availability - dealer = DealerAccount.find(params[:dealer_id]) - date = Date.parse(params[:date]) - service_id = params[:service_id] - - # Use existing Pistn service (adjust to actual service name) - availability = AppointmentAvailabilityService.new( - dealer: dealer, - date: date, - service_id: service_id - ).call - - render json: { - success: true, - date: date, - slots: availability.map { |slot| { - time: slot.time.iso8601, - available: slot.available, - capacity: slot.capacity - }} - } -rescue => e - render json: { success: false, error: e.message }, status: 422 -end - -# POST /api/v1/servicebot/dealers/:dealer_id/appointments -def create_appointment - dealer = DealerAccount.find(params[:dealer_id]) - - # Use existing Pistn service (adjust to actual service name) - result = AppointmentCreationService.new( - dealer: dealer, - customer_name: params[:customer_name], - customer_email: params[:customer_email], - customer_phone: params[:customer_phone], - service_id: params[:service_id], - appointment_time: Time.parse(params[:appointment_time]), - vehicle_year: params.dig(:vehicle, :year), - vehicle_make: params.dig(:vehicle, :make), - vehicle_model: params.dig(:vehicle, :model), - notes: params[:notes] - ).call - - if result.success? - render json: { - success: true, - appointment: { - id: result.appointment.id, - confirmation_number: result.appointment.confirmation_number, - scheduled_time: result.appointment.scheduled_time.iso8601 - } - } - else - render json: { - success: false, - errors: result.errors - }, status: 422 - end -rescue => e - render json: { success: false, error: e.message }, status: 500 -end -``` - -### 3.3: Add Routes - -```ruby -# pistn/config/routes.rb -namespace :api do - namespace :v1 do - scope :servicebot do - # ... existing routes ... - post 'dealers/:dealer_id/appointments/check_availability', - to: 'servicebot#check_availability' - post 'dealers/:dealer_id/appointments', - to: 'servicebot#create_appointment' - end - end -end -``` - -### 3.4: Update ServiceBot MCP Server - -```python -# servicebot/app/mcp_servers/appointments.py -class AppointmentsMCPServer: - async def check_availability( - self, - dealer_id: str, - date: str, - service_id: Optional[str] = None - ) -> Dict[str, Any]: - """Check real appointment availability via Pistn API.""" - try: - response = await self.pistn_client.post( - f"/api/v1/servicebot/dealers/{dealer_id}/appointments/check_availability", - json={"date": date, "service_id": service_id} - ) - response.raise_for_status() - return response.json() - except Exception as e: - logger.error(f"Availability check failed: {e}") - return {"success": False, "error": str(e)} - - async def create_appointment( - self, - dealer_id: str, - customer_name: str, - customer_email: str, - customer_phone: str, - service_id: str, - appointment_time: str, - vehicle: Dict[str, str], - notes: Optional[str] = None - ) -> Dict[str, Any]: - """Create real appointment via Pistn API.""" - try: - response = await self.pistn_client.post( - f"/api/v1/servicebot/dealers/{dealer_id}/appointments", - json={ - "customer_name": customer_name, - "customer_email": customer_email, - "customer_phone": customer_phone, - "service_id": service_id, - "appointment_time": appointment_time, - "vehicle": vehicle, - "notes": notes - } - ) - response.raise_for_status() - return response.json() - except Exception as e: - logger.error(f"Appointment creation failed: {e}") - return {"success": False, "error": str(e)} -``` - -**Duration**: 3-5 days (complex, needs thorough testing) - ---- - -## Security Considerations - -### API Key Generation -```ruby -# Generate secure API key -SecureRandom.hex(32) -# => "a1b2c3d4e5f6..." -``` - -### Rate Limiting (Optional but Recommended) -```ruby -# Gemfile -gem 'rack-attack' - -# config/initializers/rack_attack.rb -Rack::Attack.throttle('servicebot/ip', limit: 100, period: 1.minute) do |req| - if req.path.start_with?('/api/v1/servicebot') - req.ip - end -end -``` - -### HTTPS in Production -- ServiceBot MUST use HTTPS (WebSocket requires it) -- Pistn API should use HTTPS for security -- Set `PISTN_API_URL=https://pistn.yourdomain.com` in production - ---- - -## Testing Strategy - -### Phase 1 Testing -- [ ] Export knowledge from Claude OS -- [ ] Import into ServiceBot successfully -- [ ] Widget loads on Pistn page -- [ ] Can ask knowledge questions -- [ ] Streaming responses work -- [ ] Conversation persists in localStorage - -### Phase 2 Testing -- [ ] API authentication works -- [ ] Can get real dealer info -- [ ] Can list real services -- [ ] Graceful fallback if API fails -- [ ] Error handling works - -### Phase 3 Testing -- [ ] Can check availability for real dates -- [ ] Can create actual appointments -- [ ] Appointments appear in Pistn dashboard -- [ ] Customer receives confirmation -- [ ] All validations work (capacity, hours, etc.) -- [ ] Error messages are user-friendly - ---- - -## Deployment Checklist - -### ServiceBot Deployment -- [ ] Docker container running -- [ ] Knowledge DB imported -- [ ] HTTPS/SSL configured -- [ ] CORS configured for Pistn domain -- [ ] Environment variables set -- [ ] Widget file served at `/static/servicebot-widget.js` - -### Pistn Deployment -- [ ] API controller added -- [ ] Routes configured -- [ ] API key set in `.env` -- [ ] Widget script added to layout -- [ ] ServiceBot enabled flag set -- [ ] CORS headers configured (if needed) - ---- - -## Monitoring & Maintenance - -### Logs to Watch -**ServiceBot**: -- WebSocket connections -- MCP tool calls -- API call failures -- Knowledge query performance - -**Pistn**: -- ServiceBot API requests -- Authentication failures -- Appointment creation errors - -### Metrics to Track -- Conversations started -- Messages sent -- Knowledge queries performed -- Appointments booked via chat -- API response times -- Error rates - ---- - -## Future Enhancements - -### Post-MVP Features -- [ ] Customer authentication (lookup by email/phone) -- [ ] Appointment rescheduling -- [ ] Appointment cancellation -- [ ] Service history lookup -- [ ] Vehicle history integration -- [ ] Multi-language support -- [ ] Voice integration -- [ ] SMS integration - -### Knowledge Base Updates -- [ ] Sync `enhanced_documentation_service.rb` knowledge to Claude OS -- [ ] Regular export/import workflow -- [ ] Automated knowledge updates - ---- - -## Rollback Plan - -If something breaks: - -1. **Phase 1 Issues**: Disable widget - ```ruby - # .env - SERVICEBOT_ENABLED=false - ``` - -2. **Phase 2 Issues**: ServiceBot falls back to stubs automatically - -3. **Phase 3 Issues**: Disable appointment booking tool - ```python - # servicebot/app/mcp_servers/appointments.py - self.enabled = False - ``` - ---- - -## Key Takeaways - -✅ **Start minimal** - Test knowledge-only first -✅ **Phased approach** - Build API incrementally -✅ **Reuse Pistn logic** - Don't duplicate business rules -✅ **Graceful degradation** - Stubs as fallbacks -✅ **Security first** - API key authentication -✅ **Test thoroughly** - Each phase validated before next - ---- - -## Related Documentation - -- [ServiceBot README](servicebot/README.md) -- [Pistn Integration Guide](servicebot/integrations/PISTN_INTEGRATION.md) -- [Knowledge Integration](servicebot/docs/KNOWLEDGE_INTEGRATION.md) -- [Claude OS Family](claude-os/docs/CLAUDE_OS_FAMILY.md) -- [Export Format Spec](claude-os/docs/EXPORT_FORMAT_SPEC.md) - ---- - -**Next Action**: Run Phase 1 to test ServiceBot with knowledge-only conversations!