From df11f4e79bd202dd657b5232e502c628791dbf5e Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 9 May 2026 15:02:56 +0800 Subject: [PATCH] feat: support single file input (input_file) alongside directory mode --- README.md | 22 +++++- .../__pycache__/server.cpython-311.pyc | Bin 14718 -> 10607 bytes src/procleaning_mcp/server.py | 73 +++++++++++++----- 3 files changed, 74 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 02da7d5..3f3a82e 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,35 @@ Python MCP Server for PDF OCR batch processing. ## Features - **Batch OCR** - Process multiple PDF files and save results as Markdown +- **Single File OCR** - Process a single PDF file directly ## Tool ### `batch_ocr_pdf` -Batch process all PDF files in a directory using the OCR API. +OCR process PDF file(s) using the OCR API. Supports both single file and directory modes. **Parameters:** -- `input_dir`: Directory containing PDF files -- `output_dir`: Directory to save Markdown results +- `input_file`: Path to a single PDF file to process (mutually exclusive with `input_dir`) +- `input_dir`: Directory containing PDF files to process (mutually exclusive with `input_file`) +- `output_dir`: Directory to save Markdown results (auto-created if not provided) - `api_key`: OCR API key (optional, reads from `OCR_API_KEY` env var) - `base_url`: OCR API base URL (default: `https://agent.imqimacau.com`) +**Usage Modes:** + +1. **Single File Mode**: Provide `input_file` only + ``` + input_file: /path/to/document.pdf + ``` + +2. **Batch Mode**: Provide `input_dir` only + ``` + input_dir: /path/to/pdf_directory + ``` + +**Note:** `input_file` and `input_dir` are mutually exclusive. At least one must be provided. + **Returns:** JSON with processing results (success/fail counts per file) ## Setup diff --git a/src/procleaning_mcp/__pycache__/server.cpython-311.pyc b/src/procleaning_mcp/__pycache__/server.cpython-311.pyc index 7a192d4e5f37fafe1c26e8c2216700663a678bdd..83cf035371bc41ba55c7629adf278a3aba825096 100644 GIT binary patch literal 10607 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 diff --git a/src/procleaning_mcp/server.py b/src/procleaning_mcp/server.py index 5482c7e..8c1c55c 100644 --- a/src/procleaning_mcp/server.py +++ b/src/procleaning_mcp/server.py @@ -53,43 +53,79 @@ async def _poll_task_result(api_url: str, task_id: str, api_key: str) -> dict: @mcp.tool() def batch_ocr_pdf( - input_dir: str, - output_dir: str, + input_file: str = None, + input_dir: str = None, + output_dir: str = None, api_key: str = None, base_url: str = "https://agent.imqimacau.com", ) -> str: """ - Batch OCR process all PDF files in the input directory. - Submits each PDF to the OCR API and saves results as Markdown files. + OCR process PDF file(s) — either a single file or an entire directory. + Submits PDF to the OCR API and saves results as Markdown files. Args: - input_dir: Directory containing PDF files to process - output_dir: Directory to save OCR results (Markdown files) + input_file: Path to a single PDF file to process (mutually exclusive with input_dir) + input_dir: Directory containing PDF files to process (mutually exclusive with input_file) + output_dir: Directory to save OCR results (Markdown files). Auto-created if not exists. api_key: API key for the OCR service (optional, uses environment variable if not provided) base_url: Base URL of the OCR API (default: https://agent.imqimacau.com) Returns: - JSON string with batch processing results (success/fail counts and per-file status) + JSON string with processing results (success/fail counts and per-file status) + + Note: + input_file and input_dir are mutually exclusive. If neither is provided, defaults to input_dir. """ # Use environment variable if api_key not provided if not api_key: api_key = os.environ.get("OCR_API_KEY", "") - if not os.path.exists(input_dir): + # Validate mutual exclusivity + if input_file and input_dir: return json.dumps( - {"error": f"Input directory does not exist: {input_dir}"}, + {"error": "input_file and input_dir are mutually exclusive. Please provide only one."}, ensure_ascii=False, indent=2, ) - os.makedirs(output_dir, exist_ok=True) + # If neither provided, default to input_dir behavior with an error message + if not input_file and not input_dir: + return json.dumps( + {"error": "Either input_file or input_dir must be provided."}, + ensure_ascii=False, + indent=2, + ) - # Find all PDF files - pdf_files = [ - os.path.join(input_dir, f) - for f in os.listdir(input_dir) - if f.lower().endswith(".pdf") - ] + # Determine file list based on input mode + if input_file: + # Single file mode + if not os.path.isfile(input_file): + return json.dumps( + {"error": f"Input file does not exist: {input_file}"}, + ensure_ascii=False, + indent=2, + ) + # Auto-create output_dir based on input file location if not provided + if not output_dir: + output_dir = os.path.join(os.path.dirname(input_file) or ".", "ocr_output") + pdf_files = [input_file] + else: + # Directory mode + if not os.path.isdir(input_dir): + return json.dumps( + {"error": f"Input directory does not exist: {input_dir}"}, + ensure_ascii=False, + indent=2, + ) + if not output_dir: + output_dir = os.path.join(input_dir, "ocr_output") + pdf_files = [ + os.path.join(input_dir, f) + for f in os.listdir(input_dir) + if f.lower().endswith(".pdf") + ] + + os.makedirs(output_dir, exist_ok=True) if not pdf_files: return json.dumps( @@ -208,8 +244,9 @@ def batch_ocr_pdf( fail_count += 1 results.append({"file": filename, "status": "failed", "error": str(e)}) - # Delay between files - time.sleep(2) + # Delay between files (only in batch mode) + if len(pdf_files) > 1: + time.sleep(2) final_result = { "summary": {