Skip to main content
Glama
a-wiseguy

InvoiceNinja MCP Server

by a-wiseguy
README.md
# InvoiceNinja MCP Server

Model Context Protocol (MCP) server for InvoiceNinja v5.11.62 integration with Claude Desktop, Claude Code, and Cursor.ai.

## Features

**āœ… READ-ONLY Operations (Tested & Working)**
- šŸ“„ List and view invoices with tax calculations
- šŸ’³ List and view expenses
- šŸ‘„ List clients, vendors, and expense categories
- šŸ“Š Generate tax reports (quarterly and custom date ranges)
- šŸ“ˆ Invoice and expense reports

**āš ļø OTHER WRITE OPERATIONS (NOT YET REVIEW-GATED)**
- Create invoices and expenses
- Update invoices and expenses
- Clone invoices and expenses
- Send invoice emails

**āœ… REVIEWED EXPENSE PDF WORKFLOW**
- Parse machine-readable EUR supplier PDFs with explicit 21% BTW
- Preserve and reconcile net, BTW, and gross values using decimal arithmetic
- Match one existing vendor, require a reviewed category, and detect likely duplicates
- Preview an approval token before creating anything
- Create the gross/inclusive-tax expense, upload the PDF privately, and verify all fields and file bytes
- Refuse creation when required fields are missing, conflicting, scanned/image-only, or otherwise uncertain

## Installation

### Prerequisites
- Docker with Docker Compose
- InvoiceNinja v5.11.62 instance
- API token from your InvoiceNinja admin panel

### Setup

1. **Clone or navigate to the project:**
```bash
cd in-mcp
```

2. **Configure environment variables:**
```bash
cp .env.example .env
```

Edit `.env` with the development InvoiceNinja credentials. This file is passed
to the container at runtime and excluded from the image build.

3. **Build the container:**
```bash
docker compose build
```

4. **Run safe, isolated tests:**
```bash
docker compose run --rm test
```

The image uses the official multi-architecture Python image and therefore runs
natively on Raspberry Pi 5 (`arm64`) as well as typical `amd64` development
machines. Poetry resolves dependencies inside the image; no host Python packages
are installed.

## MCP Server Configuration

### Claude Desktop

Add to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "invoiceninja": {
      "command": "docker",
      "args": ["compose", "run", "--rm", "-T", "mcp"],
      "cwd": "/full/path/to/in-mcp"
    }
  }
}
```

### Cursor.ai

Add similar configuration in Cursor's MCP settings.

### Claude Code

The MCP server should be automatically detected when running in the project directory.

## Available Tools

### šŸ“Š Utility Tools

- **`test_connection()`** - Test API connection and authentication
- **`list_clients(per_page=100)`** - List all clients
- **`list_vendors(per_page=100)`** - List all vendors
- **`list_expense_categories(per_page=100)`** - List expense categories

### šŸ“„ Invoice Tools (Read-Only)

- **`list_invoices(status?, client_id?, per_page=20)`** - List invoices with filters
  - Status: draft, sent, viewed, approved, partial, paid
- **`get_invoice(invoice_id)`** - Get detailed invoice information
  - Shows amounts including/excluding tax
  - Line items breakdown
  - Payment status
- **`get_invoice_status(invoice_id)`** - Get current invoice status

### šŸ’³ Expense Tools (Read-Only)

- **`list_expenses(per_page=20)`** - List expenses
- **`get_expense(expense_id)`** - Get detailed expense information

### šŸ’³ Reviewed Expense PDF Tools

- **`preview_expense_from_pdf(pdf_path, category_id, private_notes=null)`** - Parse, validate, vendor-match, and show the exact proposed expense without writing
- **`create_expense_from_pdf(pdf_path, category_id, approval_token_value, allow_duplicate=false, private_notes=null)`** - Create only when the token still matches the exact PDF and reviewed fields

The PDF path must be readable inside the MCP container. Mount an input directory
read-only, for example `-v /host/expense-input:/expense-input:ro`, and pass a path
such as `/expense-input/supplier-invoice.pdf`. A possible duplicate is blocked unless
`allow_duplicate=true` is explicitly approved. Scanned PDFs currently fall back to
manual review; OCR is not performed.

### šŸ“Š Report Tools

- **`get_tax_report_quarterly(year, quarter)`** - Get tax report for Q1/Q2/Q3/Q4
  - Example: `get_tax_report_quarterly(2024, 1)` for Q1 2024
- **`get_tax_report_custom(start_date, end_date)`** - Custom date range tax report
  - Dates in YYYY-MM-DD format
- **`get_expense_report(start_date, end_date)`** - Expense summary report
- **`get_invoice_report(start_date, end_date)`** - Invoice summary report

## Usage Examples

### With Claude Desktop

```
List all invoices from this month

Show me invoices that are still unpaid

Get the tax report for Q3 2024

What's the status of invoice ID abc123?

List all expenses from January 2024
```

### Programmatic Usage

```python
from invoiceninja_mcp.client import InvoiceNinjaClient

async def example():
    client = InvoiceNinjaClient()

    # List invoices
    invoices = await client.list_invoices(status="sent", per_page=10)

    # Get specific invoice
    invoice = await client.get_invoice("invoice_id_here")

    # List clients
    clients = await client.list_clients()
```

## Project Structure

```
in-mcp/
└── invoiceninja_mcp/
│  ā”œā”€ā”€ __init__.py
│  ā”œā”€ā”€ __main__.py        # Entry point
│  ā”œā”€ā”€ server.py          # FastMCP server with tools
│  ā”œā”€ā”€ client.py          # InvoiceNinja API client
│  ā”œā”€ā”€ config.py          # Settings management
│  └── models.py          # Pydantic models
ā”œā”€ā”€ pyproject.toml             # Dependencies
ā”œā”€ā”€ .env                       # Your config (gitignored)
ā”œā”€ā”€ .env.example               # Example config
ā”œā”€ā”€ .gitignore
└── README.md
```

## Development

### Running Tests

```bash
# Mocked tests; does not contact InvoiceNinja
docker compose run --rm test

# Full integration suite; writes to the configured development instance
docker compose --profile integration run --rm test-integration

# Lint without installing Ruff on the host
docker compose run --rm test ruff check invoiceninja_mcp tests
```

### Running the MCP Server

```bash
# Interactive stdio transport for MCP clients
docker compose run --rm -T mcp
```

Do not run the integration suite against production. Several integration tests
create invoices, expenses, and vendors and may change invoice status.

## API Details

### Authentication Headers

The client automatically includes:
- `X-API-Token`: Your API token
- `X-Requested-With`: XMLHttpRequest
- `Content-Type`: application/json
- `Accept`: application/json

### Invoice Status Codes

- 1 = Draft
- 2 = Sent
- 3 = Viewed
- 4 = Approved
- 5 = Partial
- 6 = Paid

### Tax Calculations

Invoices return both:
- **Amount including tax** - Full invoice total
- **Amount excluding tax** - Subtotal before tax
- **Tax amount** - Total tax

## Troubleshooting

### 403 Forbidden Error

- Verify your API token in InvoiceNinja admin panel
- Check that the token has appropriate permissions
- Ensure API_URL includes `/api/v1`

### Connection Timeout

- Increase `INVOICENINJA_TIMEOUT` in `.env`
- Check your InvoiceNinja instance is accessible

### Validation Errors

- Ensure you're using InvoiceNinja v5.11.62 or compatible version
- Check API responses match expected data structure

## License

MIT

## Credits

Built with:
- [FastMCP](https://gofastmcp.com/) - MCP server framework
- [httpx](https://www.python-httpx.org/) - Async HTTP client
- [Pydantic](https://docs.pydantic.dev/) - Data validation
- [InvoiceNinja](https://invoiceninja.com/) - Invoice management platform

## Contributing

Contributions welcome! Please ensure:
- All tests pass
- Code follows existing patterns
- Documentation is updated
- Security best practices are followed

## Security

- Never commit `.env` file
- Keep API tokens secure
- Use HTTPS for InvoiceNinja instance
- Review API token permissions regularly