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
- Overview
- Getting Started
- Authentication
- Architecture
- API Tools Reference
- Request & Response Format
- Error Handling
- Best Practices
- Code Examples
- 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
- Active Quento Account: Sign up at https://quento.app
- 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
- 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: /mcp → quento → Authenticate; 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
| Purpose | URL |
|---|---|
| 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 |
| Authorization | https://quento.app/oauth/authorize |
| Token | https://quento.app/oauth/token |
| Revocation | https://quento.app/oauth/revoke |
- PKCE with
S256is 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
- The client calls
POST /mcpwithout a token and receives401 UnauthorizedwithWWW-Authenticate: Bearer resource_metadata="…". - It fetches the metadata above, registers itself (or uses an existing
client_id), and opens the authorization URL in your browser. - You sign in to Quento and click Authorize.
- The client exchanges the code for an access token (valid 2 hours) and a refresh token, and renews silently from then on.
- 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
create_invoice- Create new invoicesupdate_invoice- Modify existing invoiceslist_invoices- Query invoices with filtersget_invoice- Get detailed invoice informationmark_invoice_paid- Record payment receivedchange_invoice_status- Issue, send, or cancel invoicessend_invoice_email- Email invoices to clientsget_invoice_pdf_link- Get PDF download link
👥 Client Management
create_client- Add new clients to your accountupdate_client- Update client informationlist_clients- Search and list all clientsget_client- Get detailed client information
📦 Product Catalog
create_product- Create a new product or serviceupdate_product- Update product details (useactive: falseto deactivate)get_product- Get product detailslist_products- List products and services
🏦 Bank Accounts
create_bank_account- Add a bank account to a companyupdate_bank_account- Update bank account details (useactive: falseto deactivate)list_bank_accounts- List bank accounts
🏢 Company Management
list_companies- List your companies (sellers)create_company- Create a new companyupdate_company- Update company detailsget_company- Get detailed company informationlookup_company- Look up Polish company by NIP (VAT registry)
🇵🇱 KSeF (Polish e-Invoice System)
submit_invoice_to_ksef- Submit invoice to KSeFget_ksef_status- Check KSeF submission statuslist_ksef_submissions- List KSeF submissionslist_ksef_payables- List incoming purchase invoices received via KSeF
⏱️ 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 rangeupdate_work_log- Edit an entry or mark it billed/unbilledsummarize_unbilled_work- Unbilled hours and amounts per clientcreate_invoice_draft_from_work_logs- Turn selected unbilled entries into a draft invoice and mark them billed
📊 Analytics & Reporting
get_statistics- Get invoice analytics and statistics
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
| Code | Meaning | Example |
|---|---|---|
-32700 | Parse error | Invalid JSON |
-32600 | Invalid Request | Missing jsonrpc field |
-32601 | Method not found | Unknown tool name |
-32602 | Invalid params | Missing required field |
-32603 | Internal error | Database error |
401 | Unauthorized | Missing, expired, or revoked OAuth token (re-authenticate in your client) |
403 | Forbidden | The authorized user lacks permission for this operation |
404 | Not Found | Resource doesn't exist |
422 | Unprocessable | Validation 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
destructiveHintfor 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: /mcp → quento → Authenticate; 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
- MCP Specification: https://modelcontextprotocol.io/specification
- This page: https://quento.app/docs/mcp
- Agent skills and setup guide: https://github.com/DeliveristsIO/quento-skills
Support
- Email Support: support@quento.app
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_payablesfor incoming purchase invoices - ✅ Tool annotations: every tool declares
readOnlyHint,destructiveHint, andopenWorldHint - ✅
create_invoiceaccepts drafts without a client;update_invoicecan change a draft's currency - ✅
change_invoice_statusgainedunmark_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)