Commercial launch: Postgres auth, BTCPay payments, premium UI
- Postgres: users, api_keys, usage_log, payments tables - Auth: API key system with rate limiting (402 when out of calls) - BTCPay: Bitcoin = 5 API calls, webhook for auto-credit - Admin: sk-admin-unlimited-2026 with unlimited calls - UI: Landing, Pricing (/5calls), Signup (no KYC), Dashboard with usage stats - Footer: Created by drjones + Buy Me a Coffee link - All endpoints auth-gated except signup, status, pages
This commit is contained in:
61
server.py
61
server.py
@@ -1,110 +1,81 @@
|
||||
#!/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.
|
||||
"""
|
||||
"""AI Research Engine — Thin MCP Proxy. Admin key for backend auth."""
|
||||
|
||||
import json
|
||||
import httpx
|
||||
from mcp.server import FastMCP
|
||||
|
||||
BACKEND_URL = "http://10.30.20.249:8000"
|
||||
ADMIN_KEY = "sk-admin-unlimited-2026"
|
||||
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}")
|
||||
sep = "&" if "?" in path else "?"
|
||||
r = client.get(f"{BACKEND_URL}{path}{sep}api_key={ADMIN_KEY}")
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
mcp = FastMCP("ai-research-engine", instructions="AI Research Engine — private knowledge acquisition. 10 tools.")
|
||||
|
||||
@mcp.tool()
|
||||
def search_web(query: str, category: str = "", limit: int = 10) -> str:
|
||||
"""Full-text search across indexed documents. Find by keywords, titles, content."""
|
||||
"""Full-text search across indexed documents."""
|
||||
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."""
|
||||
"""Search by meaning using vector embeddings."""
|
||||
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."""
|
||||
"""Crawl a URL and index it."""
|
||||
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."""
|
||||
"""Discover and crawl sources for a topic."""
|
||||
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."""
|
||||
"""Full pipeline: search → crawl → 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."""
|
||||
"""Get full indexed content of a document."""
|
||||
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."""
|
||||
"""AI summary of multiple URLs. 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."""
|
||||
def extract_information(url: str, schema: str = "company names, products, prices") -> str:
|
||||
"""Extract structured data 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."""
|
||||
"""Generate comprehensive research report."""
|
||||
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."""
|
||||
"""System health + business stats."""
|
||||
r = _get("/api/status")
|
||||
return json.dumps(r, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="stdio")
|
||||
|
||||
Reference in New Issue
Block a user