MCP_PROTOCOL, 'capabilities' => [ 'tools' => ['listChanged' => false], 'resources' => ['subscribe' => false, 'listChanged' => false], ], 'serverInfo' => [ 'name' => 'wevia-mcp-server', 'version' => MCP_VERSION, 'description' => 'WEVIA Sovereign AI — Tool Server' ] ]); case 'tools/list': return mcp_response($id, ['tools' => mcp_list_tools()]); case 'tools/call': $toolName = $params['name'] ?? ''; $toolArgs = $params['arguments'] ?? []; return mcp_response($id, mcp_call_tool($toolName, $toolArgs)); case 'resources/list': return mcp_response($id, ['resources' => mcp_list_resources()]); case 'resources/read': $uri = $params['uri'] ?? ''; return mcp_response($id, mcp_read_resource($uri)); case 'ping': return mcp_response($id, []); default: return mcp_error($id, -32601, "Method not found: $method"); } } function mcp_list_tools() { return [ [ 'name' => 'wevia_chat', 'description' => 'Send a message to WEVIA sovereign AI chatbot', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'message' => ['type' => 'string', 'description' => 'User message'], 'mode' => ['type' => 'string', 'enum' => ['fast', 'balanced', 'verified'], 'default' => 'balanced'], 'lang' => ['type' => 'string', 'default' => 'fr'], ], 'required' => ['message'] ] ], [ 'name' => 'wevia_kb_search', 'description' => 'Search WEVIA knowledge base (32 tables, 2490+ entries)', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'query' => ['type' => 'string', 'description' => 'Search query'], 'limit' => ['type' => 'integer', 'default' => 5], ], 'required' => ['query'] ] ], [ 'name' => 'wevia_pdf_generate', 'description' => 'Generate a professional PDF document on any topic', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'topic' => ['type' => 'string', 'description' => 'Document topic/title'], 'sections' => ['type' => 'integer', 'default' => 8], ], 'required' => ['topic'] ] ], [ 'name' => 'wevia_mermaid', 'description' => 'Generate a Mermaid diagram (flowchart, sequence, class, etc.)', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'description' => ['type' => 'string', 'description' => 'What to diagram'], 'type' => ['type' => 'string', 'enum' => ['flowchart', 'sequence', 'class', 'state', 'er', 'gantt'], 'default' => 'flowchart'], ], 'required' => ['description'] ] ], [ 'name' => 'wevia_web_search', 'description' => 'Sovereign web search via SearXNG', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'query' => ['type' => 'string'], 'max_results' => ['type' => 'integer', 'default' => 5], ], 'required' => ['query'] ] ], [ 'name' => 'wevia_benchmark', 'description' => 'Get AI ecosystem benchmark data (66 AIs, 222 skills)', 'inputSchema' => [ 'type' => 'object', 'properties' => [ 'ai_name' => ['type' => 'string', 'description' => 'Specific AI name or "all"'], ], 'required' => [] ] ], ]; } function mcp_call_tool($name, $args) { switch ($name) { case 'wevia_chat': $ch = curl_init('http://127.0.0.1/wevia-ia/weval-chatbot-api.php'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'message' => $args['message'] ?? '', 'mode' => $args['mode'] ?? 'balanced', 'lang' => $args['lang'] ?? 'fr', ]) ]); $r = curl_exec($ch); curl_close($ch); $d = json_decode($r, true); return ['content' => [['type' => 'text', 'text' => $d['response'] ?? 'No response']]]; case 'wevia_kb_search': $query = $args['query'] ?? ''; $limit = $args['limit'] ?? 5; // Direct KB query try { @require_once('/opt/wevads/vault/credentials.php'); $pdo = new PDO("pgsql:host=10.1.0.3;dbname=adx_system", "admin", "admin123"); $st = $pdo->prepare("SELECT title, content, category FROM admin.wevia_kb WHERE LOWER(content) LIKE LOWER(?) LIMIT ?"); $st->execute(["%$query%", $limit]); $results = $st->fetchAll(PDO::FETCH_ASSOC); return ['content' => [['type' => 'text', 'text' => json_encode($results, JSON_UNESCAPED_UNICODE)]]]; } catch (Exception $e) { return ['content' => [['type' => 'text', 'text' => 'KB search error: ' . $e->getMessage()]]]; } case 'wevia_benchmark': $cache = @json_decode(@file_get_contents('/var/www/html/api/ai-benchmark-cache.json'), true); $aiName = $args['ai_name'] ?? 'all'; if ($aiName === 'all') { $lb = $cache['leaderboard'] ?? []; arsort($lb); return ['content' => [['type' => 'text', 'text' => json_encode(array_slice($lb, 0, 20, true))]]]; } $ai = $cache['all_ais'][$aiName] ?? null; return ['content' => [['type' => 'text', 'text' => $ai ? json_encode($ai) : "AI '$aiName' not found"]]]; case 'wevia_web_search': $ch = curl_init('http://127.0.0.1:8888/search?q=' . urlencode($args['query'] ?? '') . '&format=json&categories=general'); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10]); $r = curl_exec($ch); curl_close($ch); $d = json_decode($r, true); $results = array_slice($d['results'] ?? [], 0, $args['max_results'] ?? 5); $text = implode("\n\n", array_map(fn($r) => "**{$r['title']}**\n{$r['url']}\n{$r['content']}", $results)); return ['content' => [['type' => 'text', 'text' => $text ?: 'No results']]]; default: return ['content' => [['type' => 'text', 'text' => "Unknown tool: $name"]], 'isError' => true]; } } function mcp_list_resources() { return [ ['uri' => 'wevia://benchmark/leaderboard', 'name' => 'AI Benchmark Leaderboard', 'mimeType' => 'application/json'], ['uri' => 'wevia://config/providers', 'name' => 'Active Providers', 'mimeType' => 'application/json'], ['uri' => 'wevia://status/health', 'name' => 'System Health', 'mimeType' => 'application/json'], ]; } function mcp_read_resource($uri) { switch ($uri) { case 'wevia://benchmark/leaderboard': $cache = @json_decode(@file_get_contents('/var/www/html/api/ai-benchmark-cache.json'), true); return ['contents' => [['uri' => $uri, 'mimeType' => 'application/json', 'text' => json_encode($cache['leaderboard'] ?? [])]]]; case 'wevia://config/providers': return ['contents' => [['uri' => $uri, 'mimeType' => 'application/json', 'text' => json_encode(['chain' => 'Groq→Cerebras→SambaNova→Mistral→Alibaba', 'sovereign' => 'Local→EU→Free→Paid'])]]]; case 'wevia://status/health': return ['contents' => [['uri' => $uri, 'mimeType' => 'application/json', 'text' => json_encode(['status' => 'ok', 'modules' => ['WCP' => 'v1.0', 'WSI' => 'v1.0', 'Dream' => 'active', 'MCP' => 'v1.0'], 'uptime' => exec('uptime -p 2>/dev/null')])]]]; default: return ['contents' => []]; } } // ═══════════════════════════════════════════════════════════════ // MCP CLIENT — Consume external MCP servers // ═══════════════════════════════════════════════════════════════ function wevia_mcp_discover($serverUrl, $timeout = 10) { /** * Discover tools from an external MCP server. * Returns array of available tools with schemas. */ // Initialize $init = wevia_mcp_call($serverUrl, 'initialize', [ 'protocolVersion' => MCP_PROTOCOL, 'capabilities' => [], 'clientInfo' => ['name' => 'wevia-mcp-client', 'version' => MCP_VERSION] ], $timeout); if (!$init) return ['error' => 'init_failed']; // List tools $tools = wevia_mcp_call($serverUrl, 'tools/list', [], $timeout); return $tools['tools'] ?? []; } function wevia_mcp_invoke($serverUrl, $toolName, $arguments = [], $timeout = 30) { /** * Invoke a tool on an external MCP server. */ $result = wevia_mcp_call($serverUrl, 'tools/call', [ 'name' => $toolName, 'arguments' => $arguments, ], $timeout); return $result; } function wevia_mcp_call($serverUrl, $method, $params = [], $timeout = 10) { $payload = json_encode([ 'jsonrpc' => '2.0', 'id' => uniqid('mcp_'), 'method' => $method, 'params' => $params, ]); $ch = curl_init($serverUrl); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $timeout, CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Accept: application/json'], CURLOPT_POSTFIELDS => $payload, ]); $r = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($code !== 200 || !$r) return null; $d = json_decode($r, true); return $d['result'] ?? null; } // ═══════════════════════════════════════════════════════════════ // MCP REGISTRY — Track connected MCP servers // ═══════════════════════════════════════════════════════════════ function wevia_mcp_registry() { /** * Registry of known MCP servers. * Auto-populated by OSS Discovery + manual config. */ $registryFile = '/opt/wevads/vault/mcp-registry.json'; if (!file_exists($registryFile)) { $default = [ 'servers' => [ ['name' => 'wevia-local', 'url' => 'http://127.0.0.1/api/mcp.php', 'type' => 'local', 'active' => true], ], 'last_scan' => null, ]; @file_put_contents($registryFile, json_encode($default, JSON_PRETTY_PRINT)); return $default; } return json_decode(file_get_contents($registryFile), true) ?: []; } function wevia_mcp_register_server($name, $url, $type = 'external') { $registry = wevia_mcp_registry(); $registry['servers'][] = ['name' => $name, 'url' => $url, 'type' => $type, 'active' => true, 'added' => date('c')]; @file_put_contents('/opt/wevads/vault/mcp-registry.json', json_encode($registry, JSON_PRETTY_PRINT)); return true; } // ═══════════════════════════════════════════════════════════════ // JSON-RPC HELPERS // ═══════════════════════════════════════════════════════════════ function mcp_response($id, $result) { return ['jsonrpc' => '2.0', 'id' => $id, 'result' => $result]; } function mcp_error($id, $code, $message) { return ['jsonrpc' => '2.0', 'id' => $id, 'error' => ['code' => $code, 'message' => $message]]; } // ═══════════════════════════════════════════════════════════════ // STANDALONE SERVER MODE — if called directly as /api/mcp.php // ═══════════════════════════════════════════════════════════════ if (basename($_SERVER['SCRIPT_FILENAME'] ?? '') === 'wevia-mcp-layer.php' || basename($_SERVER['SCRIPT_FILENAME'] ?? '') === 'mcp.php') { header('Content-Type: application/json'); // Auth check $key = $_GET['k'] ?? $_SERVER['HTTP_X_MCP_KEY'] ?? ''; if ($key !== 'WEVADS2026' && $key !== 'MCP2026') { http_response_code(403); die(json_encode(mcp_error(null, -32000, 'Unauthorized'))); } $body = json_decode(file_get_contents('php://input'), true); if (!$body || !isset($body['method'])) { die(json_encode(mcp_error(null, -32700, 'Parse error'))); } die(json_encode(mcp_server_handle($body))); } error_log("MCP: wevia-mcp-layer v" . MCP_VERSION . " loaded (server+client)");