| """ |
| RAG Module for Archon MCP Server (HTTP-based version) |
| |
| This module provides tools for: |
| - RAG query and search |
| - Source management |
| - Code example extraction and search |
| |
| This version uses HTTP calls to the server service instead of importing |
| service modules directly, enabling true microservices architecture. |
| """ |
|
|
| import json |
| import logging |
| import os |
|
|
| from mcp.server.fastmcp import Context, FastMCP |
|
|
| from ...utils.http_client import call_api |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| def get_setting(key: str, default: str = "false") -> str: |
| """Get a setting from environment variable.""" |
| return os.getenv(key, default) |
|
|
|
|
| def get_bool_setting(key: str, default: bool = False) -> bool: |
| """Get a boolean setting from environment variable.""" |
| value = get_setting(key, "false" if not default else "true") |
| return value.lower() in ("true", "1", "yes", "on") |
|
|
|
|
| def register_rag_tools(mcp: FastMCP): |
| """Register all RAG tools with the MCP server.""" |
|
|
| @mcp.tool() |
| async def rag_get_available_sources(ctx: Context) -> str: |
| """ |
| Get list of available sources in the knowledge base. |
| |
| Returns: |
| JSON string with structure: |
| - success: bool - Operation success status |
| - sources: list[dict] - Array of source objects |
| - count: int - Number of sources |
| - error: str - Error description if success=false |
| """ |
| result = await call_api("GET", "/api/rag/sources") |
| if result.get("success"): |
| sources = result.get("sources", []) |
| return json.dumps({"success": True, "sources": sources, "count": len(sources)}, indent=2) |
| return json.dumps(result, indent=2) |
|
|
| @mcp.tool() |
| async def rag_search_knowledge_base( |
| ctx: Context, query: str, source_id: str | None = None, match_count: int = 5, return_mode: str = "pages" |
| ) -> str: |
| """ |
| Search knowledge base for relevant content using RAG. |
| |
| Args: |
| query: Search query - Keep it SHORT and FOCUSED (2-5 keywords). |
| source_id: Optional source ID filter from rag_get_available_sources(). |
| match_count: Max results (default: 5) |
| return_mode: "pages" (default) or "chunks" |
| |
| Returns: |
| JSON string with success status and search results. |
| """ |
| request_data = {"query": query, "match_count": match_count, "return_mode": return_mode} |
| if source_id: |
| request_data["source"] = source_id |
|
|
| result = await call_api("POST", "/api/rag/query", json=request_data) |
|
|
| if result.get("success"): |
| return json.dumps( |
| { |
| "success": True, |
| "results": result.get("results", []), |
| "return_mode": result.get("return_mode", return_mode), |
| "reranked": result.get("reranked", False), |
| "error": None, |
| }, |
| indent=2, |
| ) |
| return json.dumps(result, indent=2) |
|
|
| @mcp.tool() |
| async def rag_search_code_examples( |
| ctx: Context, query: str, source_id: str | None = None, match_count: int = 5 |
| ) -> str: |
| """ |
| Search for relevant code examples in the knowledge base. |
| |
| Args: |
| query: Search query - Keep it SHORT and FOCUSED (2-5 keywords). |
| source_id: Optional source ID filter. |
| match_count: Max results (default: 5) |
| |
| Returns: |
| JSON string with code example results. |
| """ |
| request_data = {"query": query, "match_count": match_count} |
| if source_id: |
| request_data["source"] = source_id |
|
|
| result = await call_api("POST", "/api/rag/code-examples", json=request_data) |
|
|
| if result.get("success"): |
| return json.dumps( |
| { |
| "success": True, |
| "results": result.get("results", []), |
| "reranked": result.get("reranked", False), |
| "error": None, |
| }, |
| indent=2, |
| ) |
| return json.dumps(result, indent=2) |
|
|
| @mcp.tool() |
| async def rag_list_pages_for_source(ctx: Context, source_id: str, section: str | None = None) -> str: |
| """ |
| List all pages for a given knowledge source. |
| |
| Args: |
| source_id: Source ID (e.g., "src_1234abcd") |
| section: Optional filter for section title |
| """ |
| params = {"source_id": source_id} |
| if section: |
| params["section"] = section |
|
|
| result = await call_api("GET", "/api/pages", params=params) |
|
|
| if result.get("success"): |
| return json.dumps( |
| { |
| "success": True, |
| "pages": result.get("pages", []), |
| "total": result.get("total", 0), |
| "source_id": result.get("source_id", source_id), |
| "error": None, |
| }, |
| indent=2, |
| ) |
| return json.dumps(result, indent=2) |
|
|
| @mcp.tool() |
| async def rag_read_full_page(ctx: Context, page_id: str | None = None, url: str | None = None) -> str: |
| """ |
| Retrieve full page content from knowledge base. |
| |
| Args: |
| page_id: Page UUID (e.g., "550e8400-e29b-41d4-a716-446655440000") |
| url: Page URL |
| """ |
| if not page_id and not url: |
| return json.dumps({"success": False, "error": "Must provide either page_id or url"}, indent=2) |
|
|
| if page_id: |
| result = await call_api("GET", f"/api/pages/{page_id}") |
| else: |
| result = await call_api("GET", "/api/pages/by-url", params={"url": url}) |
|
|
| if result.get("success"): |
| |
| |
| return json.dumps(result, indent=2) |
| return json.dumps(result, indent=2) |
|
|
| |
| logger.info("✓ RAG tools registered (HTTP-based version)") |
|
|