AP Invoice Intelligence MCP Server
AP Invoice Intelligence MCP Server
AP Invoice Intelligence is a portable Accounts Payable MCP server for extracting invoice data, checking duplicates, normalizing vendor names, calculating payment terms, and validating completeness.
The project now supports two clear operating modes:
Text PDFs use local extraction and do not require an API key.
Scanned PDFs and images require a vision provider.
Project Overview
This repository exposes invoice-processing capabilities through the Model Context Protocol (MCP). The core tools are:
extract_invoicenormalize_vendordetect_duplicatecalculate_payment_termscheck_completeness
The server stores processed records in local SQLite and starts with a single command.
Architecture
MCP Client / MCP Inspector
|
v
main.py
|
v
InvoiceProcessor
|
v
ExtractionRouter
| |
| +--> VisionProvider
| |
| +--> GeminiProvider
| +--> OpenAIProvider
| +--> AnthropicProvider
|
+--> LocalInvoiceExtractor
|
+--> pdfplumber + regex + Python parsing
Downstream processing remains unchanged:
Extracted JSON
|
+--> Duplicate detection
+--> Vendor normalization
+--> Payment terms calculation
+--> Completeness validation
+--> SQLite persistenceStartup Behavior
Running python main.py will:
Load environment variables from
.envInitialize the SQLite tables if they do not already exist
Seed the vendor master if the database is empty
Start the MCP server over stdio
No separate database seed command is required.
Installation
1. Clone the repository
git clone <repository-url>
cd hackthon2. Create and activate a virtual environment
Windows:
python -m venv venv
.\venv\Scripts\Activate.ps1Linux / macOS:
python -m venv venv
source venv/bin/activate3. Install dependencies
python -m pip install -r requirements.txtOptional provider SDKs can be installed only if you plan to use them:
python -m pip install google-genai openai anthropic4. Start the server
python main.pyOne-Command Startup
A new developer can now:
Clone the repository
Install dependencies
Run
python main.py
That single startup command initializes SQLite automatically and starts the MCP server.
Environment Variables
Copy .env.example to .env and edit values as needed.
VISION_PROVIDER=none
GEMINI_API_KEY=
OPENAI_API_KEY=
ANTHROPIC_API_KEY=VISION_PROVIDER
Controls which vision backend is used for scanned invoices and images.
geminiopenaianthropicnone
API keys
GEMINI_API_KEYis required only whenVISION_PROVIDER=geminiOPENAI_API_KEYis required only whenVISION_PROVIDER=openaiANTHROPIC_API_KEYis required only whenVISION_PROVIDER=anthropic
If VISION_PROVIDER=none, text PDFs still work locally without any API key.
How Local Extraction Works
Text-based PDFs are routed to the local extractor when selectable text is present.
The local path uses:
pdfplumberto extract text from PDF pagesregex and Python parsing to identify invoice fields
no LLM
no API key
The local extractor attempts to populate the same JSON schema as the vision path and returns null for anything it cannot find.
Fields handled by local extraction include:
Invoice Number
Vendor Name
Invoice Date
Due Date
PO Number
Currency
Subtotal
Tax
Total
Payment Terms
If a field is not visible in the document, it is returned as null.
How Scanned Invoices Work
If a PDF does not contain selectable text, the router treats it as scanned and sends it to the configured vision provider.
Images always use the vision provider.
Scanned invoice support requires one of:
Gemini
OpenAI
Anthropic
If VISION_PROVIDER=none, scanned PDFs and images fail with a clear error message. Text PDFs still work locally.
VISION_PROVIDER Configuration
VISION_PROVIDER=gemini
Use Gemini for scanned PDFs and images.
Requires
GEMINI_API_KEYLazily imports the Gemini SDK only when selected
VISION_PROVIDER=openai
Use OpenAI for scanned PDFs and images.
Requires
OPENAI_API_KEYLazily imports the OpenAI SDK only when selected
VISION_PROVIDER=anthropic
Use Anthropic for scanned PDFs and images.
Requires
ANTHROPIC_API_KEYLazily imports the Anthropic SDK only when selected
VISION_PROVIDER=none
Disable vision processing.
Text PDFs still work locally
Scanned PDFs and images raise a meaningful exception
No API key is required for text-only invoices
Gemini Mode
Gemini mode processes scanned PDFs and images through Gemini Vision.
Use this mode when you want:
cloud-based vision extraction
OCR-like handling for image-only invoices
minimal local setup beyond the API key
Example:
VISION_PROVIDER=gemini
GEMINI_API_KEY=your_key_hereOpenAI Mode
OpenAI mode processes scanned PDFs and images through OpenAI vision models.
Example:
VISION_PROVIDER=openai
OPENAI_API_KEY=your_key_hereAnthropic Mode
Anthropic mode processes scanned PDFs and images through Anthropic vision models.
Example:
VISION_PROVIDER=anthropic
ANTHROPIC_API_KEY=your_key_hereNo API Key Mode
Set:
VISION_PROVIDER=noneThis mode is intended for text-based invoices only.
What works:
Text PDFs with selectable text
Local extraction
Duplicate detection
Vendor normalization
Payment terms calculation
Completeness validation
What does not work:
Scanned PDFs
Image-only invoices
Example MCP Tool Usage
The MCP server exposes the following tools:
extract_invoicenormalize_vendordetect_duplicatecalculate_payment_termscheck_completeness
Example: extract_invoice
Input:
path/to/invoice.pdfExample response:
{
"invoice": {
"invoice_number": {
"value": "INV-100245",
"confidence": 0.95
},
"vendor_name": {
"value": "ACME Corporation",
"confidence": 0.95
},
"invoice_date": {
"value": "2026-07-31",
"confidence": 0.95
},
"due_date": {
"value": "2026-08-30",
"confidence": 0.95
},
"subtotal": {
"value": 1000.0,
"confidence": 0.95
},
"discount_percentage": {
"value": null,
"confidence": 0.0
},
"discount_amount": {
"value": null,
"confidence": 0.0
},
"tax": {
"value": 180.0,
"confidence": 0.95
},
"shipping_charges": {
"value": null,
"confidence": 0.0
},
"freight_charges": {
"value": null,
"confidence": 0.0
},
"handling_charges": {
"value": null,
"confidence": 0.0
},
"insurance_charges": {
"value": null,
"confidence": 0.0
},
"packaging_charges": {
"value": null,
"confidence": 0.0
},
"other_charges": [],
"grand_total": {
"value": 1180.0,
"confidence": 0.95
},
"line_items": []
},
"duplicate": {
"is_duplicate": false,
"match_type": "Unique Invoice",
"confidence": 1.0
}
}Example: normalize_vendor
Input:
MSFT Corp.Example output:
{
"recognized": true,
"input_vendor": "MSFT Corp.",
"matched_vendor": "MSFT Corp.",
"canonical_vendor": "Microsoft Corporation",
"email": "accounts@microsoft.com",
"country": "USA",
"confidence": 100.0
}Example Workflow
Text PDF
-> LocalInvoiceExtractor
-> Duplicate detection
-> Vendor normalization
-> Payment terms calculation
-> Completeness validation
-> SQLite save
Scanned PDF
-> VisionProvider
-> Duplicate detection
-> Vendor normalization
-> Payment terms calculation
-> Completeness validation
-> SQLite saveProject Structure
hackthon/
├── app/
│ ├── config.py
│ ├── database/
│ │ ├── database.py
│ │ ├── models.py
│ │ └── seed.py
│ ├── schemas/
│ │ └── invoice.py
│ ├── services/
│ │ ├── extraction_router.py
│ │ ├── gemini_service.py
│ │ ├── invoice_processor.py
│ │ ├── local_invoice_extractor.py
│ │ ├── pdf_parser.py
│ │ └── vision_provider.py
│ └── tools/
│ ├── completeness_checker.py
│ ├── duplicate_detector.py
│ ├── payment_terms.py
│ └── vendor_normalizer.py
├── main.py
├── requirements.txt
├── README.md
└── .env.exampleTroubleshooting
No module named 'app'
Run the server from the project root:
python main.pyNo module named 'google'
This usually means VISION_PROVIDER=gemini is set but the Gemini SDK is not installed.
Install it only if needed:
python -m pip install google-genaiNo module named 'openai'
Install the OpenAI SDK only if VISION_PROVIDER=openai:
python -m pip install openaiNo module named 'anthropic'
Install the Anthropic SDK only if VISION_PROVIDER=anthropic:
python -m pip install anthropicNo vision provider configured
This means VISION_PROVIDER=none and you tried to process a scanned PDF or image.
Text PDFs still work locally without any API key.
Invalid Gemini API Key
Check that .env contains a valid GEMINI_API_KEY and that VISION_PROVIDER=gemini.
Vendor database already seeded
This is expected on startup when the SQLite database already exists.
Known Limitations
Local extraction is best for text-based PDFs and depends on the document layout.
Scanned PDFs and images require a configured vision provider.
The local extractor returns
nullinstead of guessing when a field is not visible.The current project keeps a lightweight SQLite backend for portability.
Provider SDKs are optional, but the matching package must be installed when that provider is selected.
License
Developed for educational and hackathon purposes.