Skip to main content
Glama
main.py3.93 kB
import sys import os # Add project root to sys.path to support running as a script # current: src/server/main.py -> parent: src/server -> parent: src -> parent: root current_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.dirname(os.path.dirname(current_dir)) if project_root not in sys.path: sys.path.insert(0, project_root) from mcp.server.fastmcp import FastMCP from src.core.parser import PDFParser import asyncio # Initialize FastMCP Server mcp = FastMCP("pdf-reader") # Initialize PDF Parser # Note: PDFParser is stateless or manages its own state per call, # but here we instantiate it once. If it holds state, be careful. # Our PDFParser implementation is mostly stateless (creating sub-components). parser = PDFParser() @mcp.tool() async def read_pdf( source: str, page_range: str = None, extract_images: bool = False, force_ocr: bool = False, ) -> str: """ Read content from a PDF file (local path or URL). Returns a unified Markdwon string containing text, tables, and image references. Args: source: Local file path or URL to the PDF. page_range: Format "1-5" or "10". If not provided, reads all pages. extract_images: If True, extracts images to temp dir and links them. """ result = await parser.parse(source, page_range, extract_images, force_ocr) # Format the result for the AI # We return a string content. If the client expects JSON, we can return json.dumps(result). # But text-based LLMs usually prefer direct text content. # Let's construct a rich report. metadata = result["metadata"] content = result["content"] report = f"""# PDF Extraction Result ## Metadata - **Title**: {metadata['title']} - **Page Count**: {metadata['page_count']} - **Source**: {metadata['source']} ## Content {content} """ return report @mcp.tool() async def get_pdf_metadata(source: str) -> str: """ Quickly retrieve metadata from a PDF without reading the full content. """ # We can reuse parser but limit the logical scope. # Actually parser loads the doc anyway. # For optimization, we might make a separate method in parser, but for now reuse. # We load it but generate result without full extraction doc = await parser.loader.load(source) try: meta = { "page_count": len(doc), "title": doc.metadata.get("title", ""), "author": doc.metadata.get("author", ""), } return str(meta) finally: doc.close() @mcp.resource("pdf://{file_path}") async def read_pdf_resource(file_path: str) -> str: """ Directly read a PDF file as a resource using URI scheme pdf://... Warning: file_path must be absolute. """ # Simply delegate to the parsing logic # Note: Resources usually return raw content, but for PDF we want the processed markdown # because raw PDF bytes are not useful to the LLM directly as text. # Resource templates extract the variables. # FastMCP resources route passes the variable. # We need to reconstruct full path if needed, but here it comes as string. # Re-adding the leading slash if it was stripped is a common gotcha with URI templates, # but let's assume valid absolute path for now. # Using the same parser logic result = await parser.parse(file_path) return result["content"] @mcp.prompt() def summarize_pdf(source: str) -> str: """ Create a prompt to summarize a PDF document. """ return f"""Please read the PDF content from the following source: {source} You can use the 'read_pdf' tool to get the content. Once you have the content, please provide a comprehensive summary including: 1. Main purpose of the document 2. Key findings or arguments 3. Important data points (from tables if any) 4. Conclusions Source: {source} """ if __name__ == "__main__": mcp.run()

Implementation Reference

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/rexfelix/readPDF_mcp_server'

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