import json import os import platform from django.http import JsonResponse, HttpResponse from django.shortcuts import render, get_object_or_404 from django.utils import timezone from django.views.decorators.csrf import csrf_exempt from asgiref.sync import sync_to_async from .models import MCPToolRequest, HumanResponse, SlackSettings from .mcp import handle_mcp_request @csrf_exempt async def mcp_endpoint(request): """ Main entry point for MCP clients via HTTP. Accepts JSON-RPC 2.0 payloads. """ if request.method != "POST": return JsonResponse({"error": "Method not allowed"}, status=405) body = request.body.decode("utf-8") response_data = await handle_mcp_request(body) if response_data is None: return HttpResponse(status=204) return JsonResponse(response_data) def dashboard(request): """ Polished landing page showing active server status and recent logs. """ requests_list = MCPToolRequest.objects.all().order_by('-created_at')[:20] # Slack config check slack_token = os.getenv("SLACK_BOT_TOKEN") if not slack_token: config = SlackSettings.objects.first() if config and config.bot_token: slack_token = config.bot_token is_slack_configured = bool(slack_token) context = { "is_slack_configured": is_slack_configured, "requests": requests_list, "django_version": platform.python_version(), "now": timezone.now(), "server_status": "ONLINE" if is_slack_configured else "CONFIG_PENDING" } return render(request, "core/index.html", context) @csrf_exempt def slack_webhook(request): """ Slack Event API Webhook. Captures threaded replies and saves them as HumanResponse. """ if request.method == "POST": try: data = json.loads(request.body) # 1. Verification Challenge if data.get("type") == "url_verification": return JsonResponse({"challenge": data.get("challenge")}) # 2. Event Callback if data.get("type") == "event_callback": event = data.get("event", {}) # We only care about user messages (no bot_id) in threads (has thread_ts) if event.get("type") == "message" and not event.get("bot_id"): thread_ts = event.get("thread_ts") text = event.get("text", "") user_id = event.get("user", "Unknown User") # Basic extraction user_name = user_id files = event.get("files", []) file_url = None if files: file_url = files[0].get("url_private", "") if thread_ts: # Find the corresponding tool request mcp_request = MCPToolRequest.objects.filter(slack_ts=thread_ts).first() if mcp_request: # Avoid duplicate responses if Slack retries # We can just check if we already have this text/user in last 10 seconds, or just save it HumanResponse.objects.create( request=mcp_request, text=text, user_name=user_name, file_url=file_url ) return HttpResponse("Created", status=201) except Exception as e: print("Webhook error:", e) return HttpResponse("Error", status=400) return HttpResponse("OK") def request_detail(request, pk): """Detailed view of a single tool call.""" tool_request = get_object_or_404(MCPToolRequest, pk=pk) responses = tool_request.responses.all().order_by('-created_at') return render(request, "core/request_detail.html", { "req": tool_request, "responses": responses })