#!/usr/bin/env python3
"""
CRM PDF Documentation Server using FastMCP 2.0
Exposes PDF files from Docs folder as MCP tools
"""
from pathlib import Path
import pdfplumber
from fastmcp import FastMCP
mcp = FastMCP("CRM PDF Documentation Server")
@mcp.tool
def list_pdfs() -> str:
"""List all PDF files in the Docs directory"""
pdf_dir = Path(__file__).parent / "Docs"
pdf_files = list(pdf_dir.rglob("*.pdf"))
if not pdf_files:
return "No PDF files found in Docs directory"
files_by_folder = {}
for pdf_file in pdf_files:
relative_path = pdf_file.relative_to(pdf_dir)
folder = relative_path.parent
if folder == Path('.'):
folder_name = "Root"
else:
folder_name = str(folder)
if folder_name not in files_by_folder:
files_by_folder[folder_name] = []
files_by_folder[folder_name].append(pdf_file.name)
output = f"Available PDF files ({len(pdf_files)}):\n\n"
for folder_name in sorted(files_by_folder.keys()):
output += f"{folder_name}:\n"
for filename in sorted(files_by_folder[folder_name]):
output += f" - {filename}\n"
output += "\n"
return output.rstrip()
@mcp.tool
def read_pdf(filename: str) -> str:
"""Read content from a specific PDF file
Args:
filename: Name of the PDF file to read
(e.g., 'testing.pdf')
"""
pdf_dir = Path(__file__).parent / "Docs"
pdf_files = list(pdf_dir.rglob(filename))
if not pdf_files:
return f"Error: PDF file '{filename}' not found"
pdf_path = pdf_files[0]
if not pdf_path.exists():
return f"Error: PDF file '{filename}' not found"
try:
with pdfplumber.open(pdf_path) as pdf:
text = ""
for page in pdf.pages:
page_text = page.extract_text()
if page_text:
page_num = page.page_number
text += f"--- Page {page_num} ---\n{page_text}\n\n"
content_text = (f"Content of {filename}:\n\n{text[:5000]}"
f"{'...' if len(text) > 5000 else ''}")
return content_text
except Exception as e:
return f"Error reading PDF: {str(e)}"
@mcp.tool
def search_pdfs(query: str) -> str:
"""Search for specific text across all PDF files
Args:
query: Text to search for in the PDF files
"""
pdf_dir = Path(__file__).parent / "Docs"
pdf_files = list(pdf_dir.rglob("*.pdf"))
search_query = query.lower()
if not pdf_files:
return "No PDF files found in Docs directory"
results = []
for pdf_file in pdf_files:
try:
with pdfplumber.open(pdf_file) as pdf:
text = ""
for page in pdf.pages:
page_text = page.extract_text()
if page_text:
text += page_text + "\n"
if search_query in text.lower():
lines = text.split('\n')
matching_lines = [
f"- {line.strip()}"
for line in lines
if search_query in line.lower()
]
if matching_lines:
file_header = f"\n=== {pdf_file.name} ==="
matches = "\n".join(matching_lines[:5])
results.append(f"{file_header}\n{matches}")
except Exception as e:
msg = f"\n=== {pdf_file.name} ===\nError: {str(e)}"
results.append(msg)
if results:
return f"Search results for '{query}':\n" + "\n".join(results)
else:
return f"No matches found for '{query}'"
@mcp.tool
def get_pdf_info(filename: str = None) -> str:
"""Get metadata and basic information about PDF files
Args:
filename: Optional specific PDF file name.
If not provided, returns info for all files
"""
pdf_dir = Path(__file__).parent / "Docs"
pdf_files = list(pdf_dir.rglob("*.pdf"))
if filename:
pdf_files_found = list(pdf_dir.rglob(filename))
if not pdf_files_found:
return f"Error: PDF file '{filename}' not found"
files_to_process = [pdf_files_found[0]]
else:
files_to_process = pdf_files
if not files_to_process:
return "No PDF files found in Docs directory"
info_results = []
for pdf_path in files_to_process:
try:
with pdfplumber.open(pdf_path) as pdf:
size_kb = pdf_path.stat().st_size / 1024
info = f"""
File: {pdf_path.name}
Pages: {len(pdf.pages)}
Size: {size_kb:.1f} KB
"""
info_results.append(info)
except Exception as e:
error_msg = f"\nFile: {pdf_path.name}\nError: {str(e)}"
info_results.append(error_msg)
return "PDF Information:\n" + "\n".join(info_results)
if __name__ == "__main__":
mcp.run()
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/jcruz-impel/local-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server