Quento MCP API Documentation

Version: 1.3.0
Protocol: Model Context Protocol (MCP), Streamable HTTP
Server URL: https://quento.app/mcp
Authentication: OAuth 2.1 (authorization code + PKCE), no API keys


Table of Contents

  1. Overview
  2. Getting Started
  3. Authentication
  4. Architecture
  5. API Tools Reference
  6. Request & Response Format
  7. Error Handling
  8. Best Practices
  9. Code Examples
  10. Troubleshooting

Overview

The Quento MCP (Model Context Protocol) API enables AI assistants and applications to interact with your invoicing system through a standardized protocol. Built on the MCP specification created by Anthropic, it allows natural language interactions with your business data.

What is MCP?

Model Context Protocol (MCP) is an open protocol that connects AI applications to external tools and data sources. It provides:

  • Standardized communication between AI models and business applications
  • Type-safe tool definitions with JSON Schema validation
  • Bidirectional streaming for real-time interactions
  • Multi-tool execution for complex workflows

Key Features

  • Natural Language Interface: Interact with invoices using plain language commands
  • Multi-Tenant Architecture: Automatic account isolation and data scoping
  • Auto-Generated CRUD Tools: Dynamically generated API tools from Rails models
  • Real-Time Updates: Server-Sent Events (SSE) support for streaming responses
  • Standard MCP Authorization: OAuth 2.1 with PKCE and dynamic client registration — you sign in once in the browser, no API keys
  • Type Safety: Full JSON Schema validation for all inputs and outputs

Use Cases

  • Invoice Management: Create, update, send, and track invoices via AI
  • Client & Product Management: CRUD operations for clients and products
  • Analytics & Reporting: Query statistics and generate insights
  • Email Automation: Send invoices and payment reminders
  • Multi-Step Workflows: Complex operations like "invoice all overdue clients"

Getting Started

Fastest Setup: Agent Skills

For Claude Code, Codex, and other coding agents, install the ready-made skills from quento-skills — they teach your agent the Quento MCP workflows (invoices, clients, analytics, KSeF) and walk it through configuring the connection:

npx skills add DeliveristsIO/quento-skills

In Claude Code you can install it as a plugin instead, which bundles the skill and the Quento MCP server config in one step:

/plugin marketplace add DeliveristsIO/quento-skills
/plugin install quento@deliverists

Authentication is standard MCP OAuth in the browser — no API keys needed. See the demo video of an agent listing invoices and drafting a new one from the terminal.

The rest of this guide covers connecting any MCP client manually.

Prerequisites

  1. Active Quento Account: Sign up at https://quento.app
  2. An MCP client that supports OAuth: Claude Code, Claude Desktop, ChatGPT, Codex, Cursor, VS Code, OpenCode, Goose, or any client implementing the MCP authorization spec
  3. A browser on the machine where you authorize (once per client)

Quick Start

1. Add the server

One URL, no headers, no secrets:

{
  "mcpServers": {
    "quento": {
      "type": "http",
      "url": "https://quento.app/mcp"
    }
  }
}

Claude Code: claude mcp add --transport http --scope user quento https://quento.app/mcp
Codex: codex mcp add quento --url https://quento.app/mcp
ChatGPT: install the Quento app from the app directory, or add the URL as a custom connector
Other clients: add https://quento.app/mcp as a remote/HTTP MCP server in their config

2. Authorize in the browser

Restart the client. It discovers the OAuth endpoints, registers itself, and opens Quento in your browser (Claude Code: /mcpquentoAuthenticate; Codex: codex mcp login quento). Sign in and click Authorize. Tokens are stored and renewed by the client — you are not asked again on that machine.

3. Verify

Ask the assistant to list your invoices. Real data means the connection works.


Authentication

Quento's MCP server implements the MCP authorization specification: OAuth 2.1 authorization-code flow with PKCE. There are no API keys, tenant-specific URLs, or manually issued client credentials.

Discovery endpoints

PurposeURL
Protected resource metadata (RFC 9728)https://quento.app/.well-known/oauth-protected-resource
Authorization server metadata (RFC 8414)https://quento.app/.well-known/oauth-authorization-server
Dynamic client registration (RFC 7591)POST https://quento.app/oauth/register
Authorizationhttps://quento.app/oauth/authorize
Tokenhttps://quento.app/oauth/token
Revocationhttps://quento.app/oauth/revoke
  • PKCE with S256 is required
  • Grant types: authorization_code, refresh_token
  • Scopes: api (default), read, write
  • Public clients (token_endpoint_auth_method: none) and confidential clients are both accepted

Flow

  1. The client calls POST /mcp without a token and receives 401 Unauthorized with WWW-Authenticate: Bearer resource_metadata="…".
  2. It fetches the metadata above, registers itself (or uses an existing client_id), and opens the authorization URL in your browser.
  3. You sign in to Quento and click Authorize.
  4. The client exchanges the code for an access token (valid 2 hours) and a refresh token, and renews silently from then on.
  5. Every MCP request carries Authorization: Bearer <access token>.

Account scoping

The token is bound to the user who authorized it and to their account. Every tool runs inside that account, and per-user permissions (role, KSeF access) apply exactly as in the web app. Data from other accounts is never reachable, even with known IDs.

Revoking access

Disconnect the server in your MCP client, which revokes the token through the revocation endpoint, or contact support@quento.app to revoke all assistant access to your account.


Architecture

Communication Flow

┌─────────────────┐
│   AI Client     │  (Claude, ChatGPT, Codex, Cursor, …)
│  (MCP Client)   │
└────────┬────────┘
         │ 
         │ HTTPS POST Request
         │ Authorization: Bearer {OAuth access token}
         │ Content-Type: application/json
         │
         ▼
┌─────────────────────────────────┐
│       https://quento.app        │
│         POST /mcp               │
└────────┬────────────────────────┘
         │
         │ JSON-RPC 2.0 Request
         │ {
         │   "jsonrpc": "2.0",
         │   "method": "tools/call",
         │   "params": {...}
         │ }
         │
         ▼
┌─────────────────────────────────┐
│      MCP API Endpoint           │
│  - Validates OAuth token        │
│  - Scopes to account            │
│  - Executes tool                │
│  - Returns JSON-RPC response    │
└────────┬────────────────────────┘
         │
         │ JSON-RPC 2.0 Response
         │ {
         │   "jsonrpc": "2.0",
         │   "result": {...}
         │ }
         │
         ▼
┌─────────────────┐
│   AI Client     │  Receives structured response
│  (MCP Client)   │
└─────────────────┘

Available Tools

The server exposes the tools below. Click a tool name to jump to its reference.

Callable names carry a _tool suffix — the tool documented as list_invoices is called list_invoices_tool. The only exceptions are create_client, get_client, and update_client. Every tool declares MCP annotations (readOnlyHint, destructiveHint, openWorldHint) so clients can distinguish reads from writes; only lookup_company reaches outside your account (the public VAT registry).

📄 Invoice Management

👥 Client Management

📦 Product Catalog

🏦 Bank Accounts

🏢 Company Management

🇵🇱 KSeF (Polish e-Invoice System)

⏱️ Work Logs (time tracking, feature-flagged per account)

  • create_work_log - Log time spent for a client (duration in minutes, optional task items)
  • list_work_logs - List entries by client, billing status, and date range
  • update_work_log - Edit an entry or mark it billed/unbilled
  • summarize_unbilled_work - Unbilled hours and amounts per client
  • create_invoice_draft_from_work_logs - Turn selected unbilled entries into a draft invoice and mark them billed

📊 Analytics & Reporting


Transport Protocol

HTTP Transport: - Endpoint: POST https://quento.app/mcp (Streamable HTTP) - Protocol: JSON-RPC 2.0 over HTTPS - Authentication: OAuth 2.1 access token as Authorization: Bearer … - Content-Type: application/json - Session Management: Stateful sessions maintained per account - Streaming: Server-Sent Events (SSE) support for real-time updates


API Tools Reference

Invoice Tools

create_invoice

Create a new invoice in draft status.

Input Schema:

{
  "client_id": 123,
  "company_id": 456,
  "items": [
    {
      "description": "Web Development",
      "quantity": 10,
      "unit_price": 100.00,
      "vat_rate": 23,
      "unit": "h",
      "product_id": 789
    }
  ],
  "payment_terms": 14,
  "sale_date": "2025-01-15",
  "issue_date": "2025-01-15",
  "payment_method": "transfer",
  "bank_account_id": 1,
  "currency": "EUR",
  "notes": "Notes visible on invoice PDF",
  "internal_notes": "Private notes (not on PDF)",
  "issue_location": "Warsaw",
  "purchase_order_number": "PO-2025-001",
  "split_payment": false,
  "pdf_secondary_language": "en",
  "discount_percentage": 0
}

Required Fields: client_id, company_id, items

Optional Fields: | Field | Type | Description | |-------|------|-------------| | payment_terms | integer | Days until due (default: 14) | | sale_date | string | Sale date in YYYY-MM-DD format | | issue_date | string | Issue date in YYYY-MM-DD format | | payment_method | string | transfer, cash, or card | | bank_account_id | integer | Bank account ID for transfer payments | | currency | string | Currency code (PLN, EUR, USD, GBP) | | notes | string | Notes visible on invoice PDF footer | | internal_notes | string | Private notes (not visible on PDF) | | issue_location | string | Place of issue (e.g., "Warsaw") | | purchase_order_number | string | Client's PO number | | split_payment | boolean | Enable Polish VAT split payment (MPP) | | pdf_secondary_language | string | Bilingual PDF: en, pl, or de (must differ from company's primary language) | | discount_percentage | number | Discount percentage (0-100) |

Response:

✓ Invoice created successfully!

Invoice Details:
- ID: 42
- Number: Not yet assigned (draft)
- Client: Acme Corp
- Total: 1,230.00 EUR
- Status: draft
- Due Date: 2025-01-29
- Items: 1

Items:
  • Web Development: 10 × 100.00 EUR (VAT: 23%)

Next Steps:
- Use GetInvoiceTool (id: 42) to view full details
- Use UpdateInvoiceTool (id: 42) to make changes
- Use ChangeInvoiceStatusTool (id: 42, action: 'issue') to issue the invoice
- Use SendInvoiceEmailTool (id: 42) to send to client


update_invoice

Update an existing invoice (must be in draft status).

Input Schema:

{
  "id": 42,
  "client_id": 123,
  "payment_terms": 30,
  "payment_method": "transfer",
  "bank_account_id": 1,
  "notes": "Updated notes (use empty string to clear)",
  "internal_notes": "Private notes",
  "issue_location": "Kraków",
  "purchase_order_number": "PO-2025-002",
  "split_payment": true,
  "pdf_secondary_language": "de",
  "discount_percentage": 10,
  "items": [
    {
      "description": "Updated service",
      "quantity": 5,
      "unit_price": 200.00,
      "vat_rate": 23
    }
  ]
}

Required Fields: id or invoice_number

Note: Providing items replaces all existing invoice items.

Response:

Invoice updated successfully! Invoice #INV/001/01/2025 for Acme Corp. 
Total: 1,230.00 EUR. Status: draft. Due date: 2025-01-29


list_invoices

List invoices with optional filtering.

Input Schema:

{
  "status": "issued",
  "client_id": 123,
  "currency": "EUR",
  "from_date": "2025-01-01",
  "to_date": "2025-01-31",
  "limit": 10
}

Status Options: draft, issued, sent, paid, overdue, cancelled

Response:

Found 3 invoice(s):

- Invoice #INV/001/01/2025 | Client: Acme Corp | Total: 1,230.00 EUR | Status: issued | Due: 2025-01-29 | Created: 2025-01-15
- Invoice #INV/002/01/2025 | Client: TechStart | Total: 2,500.00 EUR | Status: issued | Due: 2025-02-01 | Created: 2025-01-18
- Invoice #INV/003/01/2025 | Client: SmithCo | Total: 850.00 EUR | Status: paid | Due: 2025-01-25 | Created: 2025-01-11


get_invoice

Get detailed information about a specific invoice.

Input Schema:

{
  "id": 42
}

Response:

Invoice #42:

- Invoice number: INV/001/01/2025
- Client: Acme Corp
- Company: My Company Ltd
- Total cents: 123000
- Currency: EUR
- Aasm state: issued
- Sale date: 2025-01-15
- Issue date: 2025-01-15
- Due date: 2025-01-29
- Payment method: bank_transfer
- Payment terms: 14


mark_invoice_paid

Mark an invoice as paid.

Input Schema:

{
  "id": 42,
  "payment_date": "2025-01-20"
}

Alternative: Use invoice_number instead of id

Response:

Invoice #INV/001/01/2025 marked as paid successfully! 
Total: 1,230.00 EUR. Paid at: 2025-01-20


change_invoice_status

Change invoice status (issue, send, cancel).

Input Schema:

{
  "id": 42,
  "action": "issue"
}

Actions: issue, send_invoice, mark_paid, cancel

Response:

✓ Invoice status changed successfully!

Invoice: #INV/001/01/2025
Previous Status: draft
New Status: issued


send_invoice_email

Send invoice email to client (auto-issues draft invoices).

Input Schema:

{
  "id": 42,
  "recipient_email": "client@example.com",
  "custom_message": "Thank you for your business!"
}

Response:

✓ Invoice sent successfully!

✓ Invoice was automatically issued before sending

Email Details:
- Recipient: client@example.com
- Invoice: #INV/001/01/2025
- Total: 1,230.00 EUR
- Due Date: 2025-01-29
- Status: sent

✓ Custom message included in email

Next Steps:
- Use GetInvoicePdfLinkTool (id: 42) to get preview link
- Use MarkInvoicePaidTool (id: 42) when payment is received


get_invoice_pdf_link

Get a link to view/download the invoice PDF.

Input Schema:

{
  "id": 42
}

Response:

Invoice PDF Link: https://acme.quento.app/invoices/42.pdf

Note: Invoice must be issued or later to generate PDF


Client Tools

create_client

Create a new client.

Input Schema:

{
  "name": "Acme Corporation",
  "email": "contact@acme.com",
  "tax_id": "1234567890",
  "country": "PL",
  "street": "ul. Główna 1",
  "city": "Warsaw",
  "postal_code": "00-001",
  "currency": "EUR",
  "default_payment_terms": 30
}

Required Fields: name, country

Response:

Client created successfully! ID: 123


update_client

Update an existing client.

Input Schema:

{
  "id": 123,
  "email": "newemail@acme.com",
  "default_payment_terms": 45
}

Required Fields: id

Response:

Client updated successfully! ID: 123


get_client

Get client details.

Input Schema:

{
  "id": 123
}

Response:

Client #123:

- Name: Acme Corporation
- Email: contact@acme.com
- Tax id: 1234567890
- Street: ul. Główna 1
- City: Warsaw
- Postal code: 00-001
- Country: PL
- Currency: EUR
- Default payment terms: 30


list_clients

List all clients with optional search.

Input Schema:

{
  "search": "acme",
  "limit": 50,
  "offset": 0
}

Response:

Found 1 client(s):

Client: Acme Corporation
  Email: contact@acme.com
  Tax ID: 1234567890
  Address: ul. Główna 1, Warsaw, 00-001, PL
  Currency: EUR | Payment Terms: 30 days
  ID: 123


Product Tools

create_product

Create a new product or service.

Input Schema:

{
  "name": "Web Development",
  "unit_price": 150.00,
  "vat_rate": "23",
  "unit": "h",
  "sku": "WEB-DEV-01",
  "description": "Hourly web development services",
  "currency": "EUR"
}

Required Fields: name, unit_price

VAT Rates: 23, 8, 5, 0, zw. (exempt), np. (not applicable)

Units: pcs, kg, g, l, ml, m, m2, m3, h, day, month, year

Response:

Product created successfully!

- Name: Web Development
- Price: 150.00 EUR per h
- VAT: 23%
- SKU: WEB-DEV-01
- ID: 1


update_product

Update an existing product.

Input Schema:

{
  "id": 1,
  "name": "Web Development (Premium)",
  "unit_price": 200.00,
  "active": true
}

Required Fields: id

Response:

Product updated successfully!

- Name: Web Development (Premium)
- Price: 200.00 EUR per h
- VAT: 23%
- SKU: WEB-DEV-01
- Active: Yes
- ID: 1


get_product

Get product details.

Input Schema:

{
  "id": 1
}

Response:

Product #1:

- Name: Web Development
- Price: 150.00 EUR per h
- VAT: 23%
- SKU: WEB-DEV-01
- Description: Hourly web development services
- Active: Yes
- Used on invoices: 12 time(s)
- Created: 2025-01-15


list_products

List all products/services.

Input Schema:

{
  "search": "development",
  "limit": 50
}

Response:

Found 2 products:

- Software Development (Hourly) | Price: 150.00 EUR | VAT: 23% | ID: 1
- Web Development (Project) | Price: 5000.00 EUR | VAT: 23% | ID: 2


Bank Account Tools

create_bank_account

Create a new bank account for a company. Bank accounts are used on invoices for payment instructions.

Input Schema:

{
  "company_id": 1,
  "bank_name": "mBank",
  "account_number": "12345678901234567890123456",
  "currency": "PLN",
  "iban": "PL61109010140000071219812874",
  "swift_code": "BREXPLPW",
  "default": true,
  "active": true
}

Required Fields: company_id, bank_name, account_number, currency

Currencies: PLN, EUR, USD, GBP

Response:

Bank account created successfully!

ID: 1
Company: My Company Ltd
Bank: mBank
Account: 12345678901234567890123456
Currency: PLN
IBAN: PL61109010140000071219812874
SWIFT: BREXPLPW
Default: Yes
Status: Active


update_bank_account

Update an existing bank account. Changes do NOT affect already-issued invoices.

Input Schema:

{
  "id": 1,
  "bank_name": "mBank S.A.",
  "default": true,
  "active": true
}

Required Fields: id

Response:

Bank account updated successfully!

ID: 1
Company: My Company Ltd
Bank: mBank S.A.
Account: 12345678901234567890123456
Currency: PLN
Default: Yes
Status: Active

Note: Already-issued invoices retain their original bank details.


list_bank_accounts

List bank accounts for a company.

Input Schema:

{
  "company_id": 1,
  "currency": "PLN",
  "active_only": true
}

Response:

Bank Accounts:

## My Company Ltd
  - ID: 1 | mBank | 12345678901234567890123456 | PLN [DEFAULT]
    IBAN: PL61109010140000071219812874
    SWIFT: BREXPLPW
  - ID: 2 | Santander | 98765432109876543210987654 | EUR
    IBAN: PL27114020040000300201355387


Company Tools

list_companies

List companies (sellers) in your account.

Input Schema:

{
  "search": "acme",
  "limit": 10
}

Response:

Found 2 company(ies):

- Acme Corp | Tax ID: 5260250274 | Currency: PLN | ID: 1
- Acme International | Tax ID: DE123456789 | Currency: EUR | ID: 2


create_company

Create a new company (seller) for issuing invoices.

Input Schema:

{
  "name": "LUX MED Sp. z o.o.",
  "country": "PL",
  "currency": "PLN",
  "locale": "pl",
  "nip": "5272523421",
  "regon": "017370029",
  "street": "ul. Postępu 21C",
  "city": "Warsaw",
  "postal_code": "02-676",
  "email": "kontakt@luxmed.pl",
  "phone": "+48 22 332 28 00",
  "website": "https://luxmed.pl",
  "default_payment_terms": 14,
  "invoice_number_pattern": "FV/{num}/{month}/{year}",
  "default": false,
  "active": true
}

Required Fields: name, country, currency

Country Codes: PL, DE, US, GB, FR, IT, ES, NL, BE, AT, CH

Tax IDs: - For Polish companies: use nip (10 digits) and optionally regon - For other countries: use tax_id

Response:

Company created successfully!

ID: 1
Name: LUX MED Sp. z o.o.
Country: PL
Currency: PLN
Locale: pl
NIP: 5272523421
REGON: 017370029
Address: ul. Postępu 21C, Warsaw, 02-676, PL
Email: kontakt@luxmed.pl
Phone: +48 22 332 28 00
Website: https://luxmed.pl
Payment terms: 14 days
Invoice pattern: FV/{num}/{month}/{year}
Status: Active

Next steps: Use create_bank_account to add a bank account for this company.


update_company

Update an existing company's details.

Input Schema:

{
  "id": 1,
  "name": "LUX MED Sp. z o.o.",
  "email": "nowy-email@luxmed.pl",
  "default_payment_terms": 30,
  "default": true
}

Required Fields: id

Note: Use empty string "" to clear optional text fields.

Response:

Company updated successfully!

ID: 1
Name: LUX MED Sp. z o.o.
Country: PL
Currency: PLN
Locale: pl
NIP: 5272523421
Address: ul. Postępu 21C, Warsaw, 02-676, PL
Email: nowy-email@luxmed.pl
Payment terms: 30 days
Default company: Yes
Status: Active


get_company

Get detailed information about a company.

Input Schema:

{
  "id": 1
}

Alternative: Use name (partial match) or nip instead of id

Response:

=== LUX MED Sp. z o.o. ===

Basic Information:
  ID: 1
  Country: PL
  Currency: PLN
  Locale: pl
  Status: Active
  Default: Yes

Tax Information:
  NIP: 5272523421
  REGON: 017370029

Address:
  Street: ul. Postępu 21C
  City: Warsaw
  Postal Code: 02-676

Contact:
  Email: kontakt@luxmed.pl
  Phone: +48 22 332 28 00
  Website: https://luxmed.pl

Invoice Settings:
  Payment Terms: 14 days
  Number Pattern: FV/{num}/{month}/{year}

Bank Accounts:
  - mBank: 12345678901234567890123456 (PLN) (default) [ID: 1]

KSeF (Polish e-Invoice):
  Status: Enabled (token)
  Environment: production


lookup_company

Look up company data from the Polish VAT Registry using NIP (tax ID). Returns company name, address, VAT status, REGON, KRS, registered bank accounts, email, and website. Use this to auto-fill client data.

Input Schema:

{
  "nip": "5260250274",
  "date": "2025-01-15"
}

Required Fields: nip

NIP Format: Can include dashes (e.g., "526-025-02-74") or just digits

Response:

# Company Found

**Name:** Example Company Sp. z o.o.
**NIP:** 526-025-02-74
**VAT Status:** Active (Czynny) ✓
**REGON:** 012345678
**KRS:** 0000123456

## Address
**Street:** ul. Przykładowa 1
**City:** Warsaw
**Postal Code:** 00-001
**Country:** PL

## Contact
**Email:** contact@example.com
**Website:** https://example.com

## Registered Bank Accounts
- PL 61 1090 1014 0000 0712 1981 2874
- PL 27 1140 2004 0000 3002 0135 5387

---
_Data from Polish VAT Registry (date: 2025-01-15)_


KSeF Tools (Polish e-Invoice System)

KSeF (Krajowy System e-Faktur) is Poland's national e-invoicing system. These tools allow you to submit invoices to KSeF and track their status.

Prerequisites: - Company must be Polish (country: PL) - Company must have KSeF configured with a valid authorization token - Invoice must be issued before submitting to KSeF

submit_invoice_to_ksef

Submit an invoice to Poland's KSeF system.

Input Schema:

{
  "id": 42
}

Alternative: Use invoice_number instead of id

Response:

KSeF submission initiated successfully!

Submission Details:
- Submission ID: 1
- Invoice: INV/001/01/2025
- Company: My Company Ltd (NIP: 5260250274)
- Environment: production
- Status: pending

The submission is being processed in the background. This typically takes 10-60 seconds.

Next Steps:
- Use GetKsefStatusTool (invoice_id: 42) to check submission status
- Once accepted, use GetKsefUpoTool (invoice_id: 42) to get the official confirmation (UPO)


get_ksef_status

Check the KSeF submission status for an invoice.

Input Schema:

{
  "invoice_id": 42
}

Alternative: Use invoice_number or submission_id

Response:

KSeF Status for Invoice INV/001/01/2025
==================================================

Submission #1 (ID: 1)
------------------------------
Status: ACCEPTED
Created: 2025-01-15 10:30:00
Submitted: 2025-01-15 10:30:15
KSeF Reference: 1234567890-20250115-ABC123DEF456

Next Steps:
- Use GetKsefUpoTool to get the official confirmation (UPO)
- The KSeF reference number can be used for official correspondence

Status Values: | Status | Description | |--------|-------------| | pending | Waiting to be processed | | submitted | Sent to KSeF, awaiting response | | accepted | Successfully accepted by KSeF | | rejected | Rejected by KSeF (check error message) | | error | Technical error (can retry) |


list_ksef_submissions

List recent KSeF submissions with their statuses.

Input Schema:

{
  "status": "accepted",
  "company_id": 1,
  "limit": 20
}

Status Filter: pending, submitted, accepted, rejected, error

Response:

KSeF Submissions
============================================================

Summary: 25 total submissions
  - pending: 1
  - submitted: 2
  - accepted: 20
  - rejected: 1
  - error: 1

Recent Submissions (showing 20):
------------------------------------------------------------

[OK]  #20 | Invoice: INV/020/01/2025 | Company: My Company Ltd | 2025-01-20 14:30
       KSeF Ref: 1234567890-20250120-XYZ789

[OK]  #19 | Invoice: INV/019/01/2025 | Company: My Company Ltd | 2025-01-19 11:15
       KSeF Ref: 1234567890-20250119-DEF456

[X]   #18 | Invoice: INV/018/01/2025 | Company: My Company Ltd | 2025-01-18 09:00
       Error: Invalid VAT rate for item 2

Use GetKsefStatusTool (submission_id: ID) for detailed information about a specific submission.


Statistics Tools

get_statistics

Get invoice statistics and analytics.

Input Schema:

{
  "period": "current_month",
  "currency": "EUR"
}

Period Options: current_month, last_month, current_year, last_year, all_time

Response:

Invoice Statistics (Current month) (EUR only):

Overview:
- Total Invoices: 15
- Draft: 2
- Issued: 5
- Sent: 3
- Paid: 4
- Overdue: 1
- Cancelled: 0

Revenue:
- Total Revenue (Paid): 12,500.00 EUR
- Outstanding Amount: 8,300.00 EUR

Top 5 Clients by Revenue:
  - Acme Corp: 5,000.00 EUR
  - TechStart: 3,500.00 EUR
  - SmithCo: 2,000.00 EUR


Request & Response Format

JSON-RPC 2.0 Protocol

All MCP communication uses JSON-RPC 2.0 over HTTP.

Request Structure:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_invoice",
    "arguments": {
      "client_id": 123,
      "company_id": 456,
      "items": [...]
    }
  }
}

Success Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Invoice created successfully! ID: 42"
      }
    ]
  }
}

Error Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32600,
    "message": "Invalid Request",
    "data": {
      "details": "Missing required parameter: client_id"
    }
  }
}

MCP Tool Response Format

All tools return responses in a consistent format with an array of content objects:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Human-readable result message"
      }
    ]
  }
}

Future Support (not yet implemented): - type: "image" - Embedded images - type: "resource" - File attachments


Error Handling

Common Error Codes

CodeMeaningExample
-32700Parse errorInvalid JSON
-32600Invalid RequestMissing jsonrpc field
-32601Method not foundUnknown tool name
-32602Invalid paramsMissing required field
-32603Internal errorDatabase error
401UnauthorizedMissing, expired, or revoked OAuth token (re-authenticate in your client)
403ForbiddenThe authorized user lacks permission for this operation
404Not FoundResource doesn't exist
422UnprocessableValidation failed

Tool-Level Errors

Tools return user-friendly error messages in the response text:

Example - Client Not Found:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Client not found"
      }
    ]
  }
}

Example - Validation Error:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Failed to create invoice: Client can't be blank, Items can't be empty"
      }
    ]
  }
}

Error Handling Best Practices

1. Check HTTP Status First:

if (response.status === 401) {
  // Token expired or revoked — let the MCP client refresh or re-run the OAuth flow
}

2. Parse JSON-RPC Error:

const data = await response.json();
if (data.error) {
  console.error(`MCP Error: ${data.error.message}`);
}

3. Handle Tool Errors:

const text = data.result.content[0].text;
if (text.includes("not found") || text.includes("Failed")) {
  console.error(`Tool Error: ${text}`);
}


Best Practices

Security

  • Never paste tokens into configs. OAuth tokens are obtained and stored by your MCP client; a Quento MCP configuration contains only the server URL.
  • Authorize per machine. Each client instance registers and authorizes separately, so revoking one does not affect the others.
  • Least privilege. The authorized user's role applies to every tool call; use a member account rather than the owner when an assistant only needs to read data.
  • Review write actions. Clients surface destructiveHint for issuing, sending, cancelling, marking paid, and KSeF submission — keep confirmations on for those.

Performance

1. Use Pagination:

{
  "method": "tools/call",
  "params": {
    "name": "list_invoices",
    "arguments": {
      "limit": 20,
      "offset": 0
    }
  }
}

2. Filter Server-Side:

//  Good: Filter in database
{"status": "paid", "currency": "EUR"}

//  Bad: Fetch all and filter client-side
{"limit": 1000}

3. Cache Tool List:

// Cache tools/list response for 1 hour
const tools = await fetchAndCache('/mcp', 'tools/list', 3600);

Data Integrity

1. Validate Before Sending:

// ✅ Good: Validate required fields
if (!client_id || !items.length) {
  throw new Error("Missing required fields");
}

2. Handle Partial Failures:

// When creating multiple invoices, handle each error separately
const results = await Promise.allSettled(
  invoices.map(inv => createInvoice(inv))
);


Code Examples

Client configuration

Claude Code

claude mcp add --transport http --scope user quento https://quento.app/mcp

Codex

codex mcp add quento --url https://quento.app/mcp
codex mcp login quento

Cursor / VS Code (.cursor/mcp.json / .vscode/mcp.json)

{
  "mcpServers": {
    "quento": { "type": "http", "url": "https://quento.app/mcp" }
  }
}

OpenCode (opencode.json)

{
  "mcp": {
    "quento": { "type": "remote", "url": "https://quento.app/mcp" }
  }
}

Clients that only support stdio servers

npx mcp-remote https://quento.app/mcp

mcp-remote proxies stdio to HTTP and runs the same browser OAuth flow.

Your own application

Use an MCP SDK with its OAuth client provider — the TypeScript SDK and Python SDK both implement discovery, dynamic registration, PKCE, and token refresh. Point the client at https://quento.app/mcp; the SDK opens the authorization URL for your user.

Raw JSON-RPC (with a token your client obtained)

curl -X POST https://quento.app/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "list_invoices_tool",
      "arguments": { "status": "paid", "limit": 10 }
    }
  }'

Troubleshooting

Common Issues

Issue: 401 Unauthorized or tools not showing up

Cause: The client has not completed OAuth authorization, or its token was revoked

Solution: - Restart the client after adding the server, then complete authorization in its MCP settings (Claude Code: /mcpquentoAuthenticate; Codex: codex mcp login quento) - Make sure the configured URL is exactly https://quento.app/mcp with no headers - If your client cannot run an OAuth flow, use the mcp-remote shim or a different client — there is no API-key fallback


Issue: "Client not found"

Cause: Client ID doesn't exist or belongs to different account

Solution:

# List all clients
curl -X POST https://quento.app/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "list_clients_tool",
      "arguments": {}
    }
  }'


Issue: "Failed to create invoice: validation errors"

Cause: Missing required fields or invalid data

Solution: Check the error message for specific validation failures: - Client can't be blank - Missing client_id - Items can't be empty - Missing items array - VAT rate is invalid - Check VAT rate format (use number, not string)

Example Fix:

//  Bad
{
  "vat_rate": "23%"
}

//  Good
{
  "vat_rate": 23
}


Issue: "Method not found"

Cause: Tool name is incorrect

Solution: List all available tools:

curl -X POST https://quento.app/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {}
  }'


Issue: "Invoice cannot be marked as paid"

Cause: Invoice is in draft or cancelled status

Solution: Issue the invoice first:

{
  "name": "change_invoice_status",
  "arguments": {
    "id": 42,
    "action": "issue"
  }
}

Then mark as paid:

{
  "name": "mark_invoice_paid",
  "arguments": {
    "id": 42
  }
}


Rate Limits

Current Limits: - 100 requests/minute per IP address - 100 requests/minute and 1000 requests/hour per access token - 5 registrations/minute per IP on /oauth/register

Rate Limit Response:

{
  "error": {
    "code": 429,
    "message": "Rate limit exceeded. Try again in 60 seconds."
  }
}

Best Practice: Implement exponential backoff:

async function callWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.code === 429 && i < maxRetries - 1) {
        await sleep(Math.pow(2, i) * 1000);
        continue;
      }
      throw error;
    }
  }
}


Resources

Official Documentation

Support


Changelog

Version 1.3.0 (Current)

Released: 2026-09-10

  • Standard MCP OAuth: authorization-code flow with PKCE, RFC 9728/8414 discovery, and dynamic client registration on the single URL https://quento.app/mcp. API-key and per-subdomain MCP access are retired.
  • Work logs: create_work_log, list_work_logs, update_work_log, summarize_unbilled_work, create_invoice_draft_from_work_logs (feature-flagged per account)
  • KSeF payables: list_ksef_payables for incoming purchase invoices
  • Tool annotations: every tool declares readOnlyHint, destructiveHint, and openWorldHint
  • create_invoice accepts drafts without a client; update_invoice can change a draft's currency
  • change_invoice_status gained unmark_paid

Version 1.2.0

Released: 2025-12-05

New Features: - ✅ Company Management: create_company, update_company, get_company, list_companies - Create companies with Polish NIP/REGON or international tax IDs - Configure invoice number patterns, payment terms, locale - Automatic NIP validation for Polish companies

Security Improvements: - ❌ Removed all delete_* tools and get_ksef_upo from the MCP API - Deletions must be performed through the web UI - Use update_* with active: false to deactivate records instead

Version 1.1.0

Released: 2025-12-04

New Features: - ✅ Bank Account Management: Full CRUD for bank accounts (create, update, get, delete, list) - ✅ KSeF Integration: Submit invoices to Poland's e-invoice system (submit, status, UPO, list) - ✅ Product Management: Full CRUD for products (create, update, get, delete) - ✅ Company Lookup: Look up Polish companies by NIP from VAT Registry - ✅ Client Deletion: Delete clients (with invoice protection)

Enhanced Invoice Tools: - ✅ bank_account_id - Assign bank account for transfer payments - ✅ internal_notes - Private notes not visible on PDF - ✅ issue_location - Place of issue for invoices - ✅ purchase_order_number - Client's PO number reference - ✅ split_payment - Polish VAT split payment (MPP) support - ✅ pdf_secondary_language - Bilingual PDF support (en/pl/de)

Security Improvements: - ✅ Safe date parsing (ISO 8601 only) - ✅ Error message sanitization - ✅ Input filtering for CRUD operations

Version 1.0.0

Released: 2025-01-28

Features: - ✅ Full invoice CRUD operations - ✅ Client management (create, update, list, get) - ✅ Product listing - ✅ Statistics and analytics - ✅ Email automation - ✅ PDF generation - ✅ Multi-tenant account scoping - ✅ JSON-RPC 2.0 over HTTPS


License

Quento MCP API is proprietary software.
© 2026 Quento. All rights reserved.

For licensing inquiries, contact: support@quento.app


Last Updated: September 10, 2026
API Version: 1.3.0
Protocol Version: MCP 2025-03-26 and later (negotiated per client)