#!/usr/bin/env python3 """ AI Research Engine — Thin MCP Proxy Runs on MacBook. Forwards all tool calls to the CT 145 backend. Minimal resource usage — all heavy lifting on Proxmox. """ import json import httpx from mcp.server import FastMCP BACKEND_URL = "http://10.30.20.249:8000" client = httpx.Client(timeout=120.0) mcp = FastMCP( "ai-research-engine", instructions=""" AI Research Engine — private knowledge acquisition system. search_web(query) — Full-text search across indexed documents semantic_search(query) — Find documents by meaning (vector search) crawl_url(url) — Crawl a URL into the index crawl_topic(topic) — Discover and crawl sources for a topic research_topic(topic) — Full pipeline: discover → crawl → summarize retrieve_document(url) — Get full content of an indexed document summarize_sources(urls, instruction) — AI summary of multiple sources extract_information(url, schema) — Structured data extraction create_report(topic, sources) — Generate comprehensive research report index_status() — System health and stats """, ) def _get(path: str) -> dict: r = client.get(f"{BACKEND_URL}{path}") r.raise_for_status() return r.json() @mcp.tool() def search_web(query: str, category: str = "", limit: int = 10) -> str: """Full-text search across indexed documents. Find by keywords, titles, content.""" r = _get(f"/api/search?q={query}&category={category}&limit={limit}") return json.dumps(r, indent=2) @mcp.tool() def semantic_search(query: str, limit: int = 10) -> str: """Search by meaning using vector embeddings. Finds conceptually related docs.""" r = _get(f"/api/semantic-search?q={query}&limit={limit}") return json.dumps(r, indent=2) @mcp.tool() def crawl_url(url: str, depth: int = 1) -> str: """Crawl a URL. depth: 0=just this page, 1=+linked pages.""" r = _get(f"/api/crawl?url={url}&depth={depth}") return json.dumps(r, indent=2) @mcp.tool() def crawl_topic(topic: str, max_urls: int = 20) -> str: """Discover and crawl sources for a topic using YaCy.""" r = _get(f"/api/crawl-topic?topic={topic}&max_urls={max_urls}") return json.dumps(r, indent=2) @mcp.tool() def research_topic(topic: str) -> str: """Full research pipeline: keyword search → semantic search → crawl new sources → AI summary.""" r = _get(f"/api/research?topic={topic}") return json.dumps(r, indent=2) @mcp.tool() def retrieve_document(url: str) -> str: """Get full indexed content of a document by URL.""" r = _get(f"/api/document?url={url}") return json.dumps(r, indent=2) @mcp.tool() def summarize_sources(urls: str, instruction: str = "Summarize key points") -> str: """Summarize multiple URLs using local LLM. urls: comma-separated.""" r = _get(f"/api/summarize?urls={urls}&instruction={instruction}") return json.dumps(r, indent=2) @mcp.tool() def extract_information(url: str, schema: str = "company names, products, prices, specifications") -> str: """Extract structured information from a document using LLM.""" r = _get(f"/api/extract?url={url}&schema={schema}") return json.dumps(r, indent=2) @mcp.tool() def create_report(topic: str, sources: str = "") -> str: """Generate a comprehensive research report. sources: optional comma-separated URLs.""" r = _get(f"/api/report?topic={topic}&sources={sources}") return json.dumps(r, indent=2) @mcp.tool() def index_status() -> str: """Check health of all backend services: OpenSearch, Qdrant, YaCy, Ollama.""" r = _get("/api/status") return json.dumps(r, indent=2) if __name__ == "__main__": mcp.run(transport="stdio")