Skip to main content
Glama
__init__.py6.4 kB
"""General-purpose AskDocs MCP Server.""" import argparse import logging import os import sys from pathlib import Path import requests from mcp.server.fastmcp import FastMCP from .config import ( DEFAULT_EMBEDDING_MODEL, DEFAULT_LLM_MODEL, load_config, log, ) from .document_processor import get_pdf_page_content from .server import AskDocsServer # Configure logging to go to stderr for MCP compatibility logging.basicConfig( level=logging.WARNING, stream=sys.stderr, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) # Suppress noisy library loggers logging.getLogger("pypdf").setLevel(logging.ERROR) logging.getLogger("pypdf._reader").setLevel(logging.ERROR) logging.getLogger("chromadb").setLevel(logging.ERROR) # Suppress ChromaDB telemetry output and UV progress os.environ["ANONYMIZED_TELEMETRY"] = "False" os.environ["UV_NO_PROGRESS"] = "1" # Initialize MCP server mcp = FastMCP("AskDocs") # Global server instance (initialized in main()) server = None # Register MCP tools @mcp.tool() def list_docs() -> str: """List all available documentation sources. Returns a formatted list of all documentation sources that can be queried, including their names and descriptions. Returns: Formatted string listing all available documentation sources Example: list_docs() """ if server is None: return "Server not initialized." return server.list_docs() @mcp.tool() def ask_docs(source_name: str, query: str) -> str: """Search documentation sources with semantic search. Query technical documentation using natural language questions. Returns answers with citations to specific pages. Args: source_name: Name of the documentation source to search. Use list_docs() to see all available sources. query: Your question about the documentation Returns: Answer based on the documentation with source page references Example: ask_docs("my_manual", "What is the base address of the peripheral?") ask_docs("product_spec", "How do I configure the interface?") """ if server is None: return "Server not initialized." return server.ask_docs(source_name, query) @mcp.tool() def get_doc_page(source_name: str, page_start: int, page_end: int = None) -> str: """Retrieve full text content of PDF page(s). Use this tool when you need to see the complete text content of specific pages from search results, including tables and detailed sections. Args: source_name: Name of the PDF source (e.g., "my_manual", "product_spec") page_start: Starting page number (use the page number from search results) page_end: Optional ending page number for ranges (default: same as page_start) Returns: Complete text content extracted from the specified pages Example: get_doc_page("my_manual", 23, 25) # Get pages 23-25 get_doc_page("product_spec", 108) # Get single page 108 """ if server is None: return "Server not initialized." return get_pdf_page_content(server.doc_sources, source_name, page_start, page_end) def check_server_connection(server_url: str, timeout: int = 5) -> bool: """Check if the Ollama server is reachable. Args: server_url: The URL of the Ollama server timeout: Connection timeout in seconds Returns: True if server is reachable, False otherwise """ try: log(f"Checking connection to Ollama server at {server_url}...") response = requests.get(f"{server_url}/api/tags", timeout=timeout) response.raise_for_status() log("✓ Successfully connected to Ollama server") return True except requests.exceptions.ConnectionError: log(f"✗ Error: Cannot connect to Ollama server at {server_url}") log(" Please ensure Ollama is running and accessible at the specified URL") return False except requests.exceptions.Timeout: log(f"✗ Error: Connection to {server_url} timed out after {timeout} seconds") return False except requests.exceptions.RequestException as e: log(f"✗ Error: Failed to connect to Ollama server: {e}") return False def main(): """Main entry point for the askdocs-mcp CLI""" global server # Parse command line arguments parser = argparse.ArgumentParser( description="AskDocs MCP Server - RAG-powered documentation search" ) parser.add_argument( "--docs-dir", type=Path, default=Path("/docs"), help="Path to documentation directory containing askdocs-mcp.toml (default: /docs)", ) args = parser.parse_args() # Load configuration from docs directory docs_dir = args.docs_dir config_file = docs_dir / "askdocs-mcp.toml" try: doc_sources, settings = load_config(config_file) except FileNotFoundError as e: log(f"Error: {e}") log(f"\nExpected config file at: {config_file}") log("Please create askdocs-mcp.toml in your docs directory") log("Example config file format:") log( f""" # Optional: Configure Ollama models embedding_model = "{DEFAULT_EMBEDDING_MODEL}" llm_model = "{DEFAULT_LLM_MODEL}" [[doc]] name = "my_manual" description = "My Product Manual" path = "pdf/manual.pdf" [[doc]] name = "another_doc" description = "Another Documentation" path = "pdf/another.pdf" """ ) sys.exit(1) except (ValueError, Exception) as e: log(f"Error loading configuration: {e}") sys.exit(1) # Check server connectivity before proceeding if not check_server_connection(settings["server_url"]): log("\nCannot proceed without a working Ollama server connection.") log("Please start Ollama or check your server_url configuration.") sys.exit(1) # Initialize server with configured settings log("Initializing AskDocs server...") server = AskDocsServer( doc_sources=doc_sources, embedding_model=settings["embedding_model"], llm_model=settings["llm_model"], server_url=settings["server_url"], cache_dir=docs_dir, ) # Initialize documentation sources server.initialize_docs(doc_sources) # Start MCP server mcp.run() if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dymk/askdocs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server