Skip to main content
Glama
blesta-mcp

blesta-mcp

by shukiv

blesta-mcp

An MCP server that exposes the Blesta billing API to LLM agents (Claude Code, Claude Desktop, Hermes, or any MCP client).

It ships a small set of purpose-built, read-only tools for the common support workflow (find a customer, look at their invoices, services and payments, hand them a pay link) plus a generic blesta_call escape hatch that can reach every public model method Blesta exposes.

Tools

Tool

What it does

Blesta methods

search_clients

Find customers by email, name, company, client number or phone number

Clients.search, Clients.getSearchCount, phone index over Contacts.getNumbers

get_client

Full client record, optionally with effective settings

Clients.get

search_invoices

Free-text invoice search, or list a client's invoices by status

Invoices.search, Invoices.getList, getListCount

get_invoice

Invoice details, line items, totals, paid/due

Invoices.get

get_invoice_payments

Transactions applied to an invoice and their status

Transactions.getApplied, Transactions.get

get_client_services

A client's services with status, renewal date and price; or one service in full

Services.getList, Services.get, Services.getRenewalPrice

create_invoice_payment_link

Build a no-login "pay now" URL for one invoice

Invoices.createPayHash, Encryption.systemEncrypt

verify_invoice_payment_link

Validate a hash or sid token against client + invoice and report if payment is still due

Invoices.verifyPayHash, Encryption.systemDecrypt, Invoices.get

get_client_contacts

Contacts under a client with phone numbers

Contacts.getAll, Contacts.getNumbers

get_client_notes

Staff notes and sticky notes

Clients.getNoteList, getNoteListCount, getAllStickyNotes

get_client_balance

Amount due per currency and invoice counts by status

Invoices.amountDue, Invoices.getStatusCount

get_client_transactions

Payment history with filters

Transactions.getList, getListCount

get_transaction

One payment plus applied invoices

Transactions.get, Transactions.getApplied

get_payment_accounts

Masked cards/bank accounts on file

Accounts.getAllCcByClient, Accounts.getAllAchByClient

search_services

Find a service by domain/username/number across clients

Services.search

get_service_actions

Operations Blesta allows on a service right now

Services.getActions

list_packages / get_package

Product catalog with prices

Packages.getAll, Packages.getAllGroups, Packages.get

list_quotations / get_quotation

Quotes

Quotations.getList, get, getLineItems, getInvoices

lookup_coupon

Validate a promo code

Coupons.getByCode, Coupons.getForPackages

add_client_note (write)

Log an interaction on the account

Clients.addNote

update_client_status (write)

active / inactive / fraud

Clients.edit

update_client_profile (write)

Names, email, company, address of a contact; keeps the login in step with the email; dry_run validates

Contacts.edit, Contacts.validateContact, Users.edit

set_contact_number (write)

Add, change, remove phone/fax numbers

Contacts.addNumber, editNumber, deleteNumber

add_client_contact (write)

Add a billing or other contact

Contacts.add

delete_client_contact (write)

Remove an additional contact

Contacts.delete

update_client_settings (write)

Language, currency, tax ID, tax exemption, invoice method, invoice contact

Clients.setSettings

create_invoice (write)

New invoice with line items

Invoices.add

send_invoice (write)

Queue (re)delivery by email

Invoices.addDelivery

update_invoice (write)

Header fields incl. void

Invoices.edit

edit_invoice_lines (write)

Add, update, remove line items

Invoices.edit

delete_invoice (write)

Delete a draft invoice

Invoices.deleteDraft

merge_invoices (write)

Combine open invoices into one

Invoices.merge

split_invoice (write)

Move lines to a new invoice

Invoices.split

append_services_to_invoice (write)

Add service lines priced by Blesta

Invoices.appendServices

set_invoice_closed (write)

Recalculate closed state

Invoices.setClosed

suspend_service / unsuspend_service / cancel_service (write)

Service lifecycle

Services.suspend, unsuspend, cancel

get_service

Full service view: pricing info, options, next invoice date, children, actions, pending changes

Services.get, getOptions, getPricingInfo, getNextInvoiceDate, getAllChildren, getActions, ServiceChanges.getAll

list_compatible_packages

Upgrade/downgrade targets with pricing terms

Packages.getCompatiblePackages

create_service (write)

Add a service, optionally provision on the module and invoice it; dry_run validates

Services.add, Services.validate, Invoices.createFromServices

delete_service (write)

Delete a pending/in-review/canceled service

Services.delete

change_service_package (write)

Upgrade, downgrade or term change; dry_run validates

Services.edit, Services.validateServiceEdit

update_service (write)

Status (activate), dates, qty, price override, coupon, module fields

Services.edit

uncancel_service (write)

Reactivate a canceled service

Services.unCancel

move_service (write)

Transfer a service to another client

Services.move

invoice_service (write)

Invoice new or renewing services

Invoices.createFromServices

set_service_field (write)

Store a module field on the service

Services.editField, Services.addField

manage_service_change (write)

Process or cancel a queued service change

ServiceChanges.process, ServiceChanges.cancel

record_manual_payment (write)

Record offline payment and apply it

Transactions.add, Transactions.apply

download_invoice_pdf

Render invoices to PDF with Blesta's template and save the file (needs the Component API plugin)

ComponentApi.ComponentApiCaller.call -> InvoiceDelivery.downloadInvoices

record_invoice_payment (write)

Record offline payment for one invoice by invoice ID

Invoices.get, Transactions.add, Transactions.apply

apply_transaction (write)

Apply existing credit to invoices

Transactions.apply

process_payment (write, off by default)

Charge a stored payment account

Payments.processPayment

blesta_call

Call any {model}/{method} with named parameters

anything in source-docs.blesta.com

Write tools are refused when BLESTA_READ_ONLY=1. process_payment additionally needs BLESTA_ALLOW_PAYMENTS=1.

The payment link is built exactly the way Blesta's own payment-reminder emails build it:

{install}/client/pay/method/{invoice_id}/?sid=rawurlencode(systemEncrypt("c={client_id}|h={hash}"))

so the resulting URL opens Blesta's native client-area payment page without a login.

Related MCP server: WHMCS MCP Server

Requirements

  • Node.js 20 or newer

  • A Blesta API user + key (Blesta admin: Settings > System > API Access)

  • HTTPS access to the Blesta install (the API key travels in every request)

Install

git clone <this repo> blesta-mcp
cd blesta-mcp
npm install
npm run build

Configuration

All configuration is by environment variable.

Variable

Required

Meaning

BLESTA_URL

yes

Install URL, e.g. https://billing.example.com or https://billing.example.com/api/ (both accepted)

BLESTA_API_USER

yes

API user name

BLESTA_API_KEY

yes

API key

BLESTA_READ_ONLY

no

1 blocks every non-GET call (safe default for support agents)

BLESTA_SYSTEM_KEY

on IonCube installs

Blesta.system_key from config/blesta.php. Needed by the payment-link tools: systemEncrypt/systemDecrypt live in the IonCube-encoded AppModel, and omitted optional arguments fail there with Failed to retrieve the default value. With the key set, key/iv are passed explicitly, which is exactly what the PHP defaults do.

BLESTA_SYSTEM_KEY_FILE

alternative

Path to a file containing only the system key (preferred over the env var: keep the file chmod 600, outside the repo, so the key never appears in MCP client config or shell history)

BLESTA_STAFF_ID

no

Staff member ID recorded on notes, suspensions and payments when a tool call gives none

BLESTA_COMPANY_ID

no

Company for catalog calls; default is the first company Blesta reports

BLESTA_ALLOW_PAYMENTS

no

1 enables process_payment (charges stored payment accounts)

BLESTA_DOWNLOAD_DIR

no

Where download_invoice_pdf saves files; default is blesta-mcp/ under the OS temp dir

BLESTA_PHONE_INDEX_FILE

no

Cache file for the phone index used by search_clients; default ~/.cache/blesta-mcp/phone-index-<hash>.json (mode 0600, holds customer phone numbers). off keeps the index in memory only

BLESTA_PHONE_INDEX_TTL_HOURS

no

Age after which the phone index is rebuilt in the background; default 24

BLESTA_INDEX_CONCURRENCY

no

Parallel read calls while building the phone index; default 8, max 32

BLESTA_PUBLIC_URL

no

Base URL used in customer-facing links when it differs from BLESTA_URL, e.g. https://www.example.com/clients while the API is called at https://clients.example.com

BLESTA_CLIENT_URI

no

Client-area path used in payment links, default client/

BLESTA_TIMEOUT_MS

no

HTTP timeout, default 30000

BLESTA_ALLOW_HTTP

no

1 permits plain http:// URLs (local testing only)

Security note. Blesta's API has no scopes: any valid key can call every public model method, which is full administrative access. Keep the key out of shell history and prompts, prefer BLESTA_READ_ONLY=1 unless the agent must write, and never point this server at an install over plain HTTP.

Add to Claude Code

claude mcp add blesta \
  -e BLESTA_URL=https://billing.example.com \
  -e BLESTA_API_USER=apiuser \
  -e BLESTA_API_KEY=your-key \
  -e BLESTA_READ_ONLY=1 \
  -e BLESTA_SYSTEM_KEY_FILE=/home/you/.config/blesta-mcp/system_key \
  -- node /absolute/path/to/blesta-mcp/dist/index.js

Or in a project .mcp.json / Claude Desktop claude_desktop_config.json:

{
  "mcpServers": {
    "blesta": {
      "command": "node",
      "args": ["/absolute/path/to/blesta-mcp/dist/index.js"],
      "env": {
        "BLESTA_URL": "https://billing.example.com",
        "BLESTA_API_USER": "apiuser",
        "BLESTA_API_KEY": "your-key",
        "BLESTA_READ_ONLY": "1"
      }
    }
  }
}

How requests are made

The client mirrors the official PHP SDK (phillipsdata/blesta_sdk):

  • URL: {BLESTA_URL}/api/{model}/{method}.json

  • Auth headers: BLESTA-API-USER, BLESTA-API-KEY

  • Parameters are passed by name and encoded like PHP's http_build_query, so nested arrays become vars[lines][0][amount]=5.99. GET/DELETE use the query string, POST/PUT a form-encoded body.

  • A 200 response is unwrapped from {"response": ...}; any other status is turned into a tool error that lists Blesta's per-field validation messages.

blesta_call infers the HTTP verb from the method name (get*/search* -> GET, add* -> POST, edit*/set* -> PUT, delete* -> DELETE) and accepts http_method to override it. Plugin models are addressed as plugin.model.

Two Blesta quirks worth knowing:

  • Timestamps sent to Blesta must include a timezone (2026-01-31T12:00:00Z); Blesta otherwise assumes the company's local time.

  • On IonCube-encoded installs a call can fail with Failed to retrieve the default value when an optional argument is omitted. The curated tools omit a few trailing optionals (transactions/getApplied without transaction_id, services/getList without filters), so get_invoice_payments and get_client_services are the first tools to try against a real install; if they return that error, open an issue and the calls will be made fully explicit.

  • create_invoice_payment_link and verify_invoice_payment_link still work under BLESTA_READ_ONLY=1: Encryption.systemEncrypt/systemDecrypt are side-effect free and are the only POST calls allowed in that mode.

  • Verified against a live IonCube-encoded 5.x install: all 27 read tools pass, including download_invoice_pdf and the dry_run paths of create_service, change_service_package and update_service (they run Blesta's validation without writing). The 30 write tools were verified against a mock only (request paths, names and http_build_query encoding checked against the Blesta model sources); they have not been executed against a live install. A generated payment link opens Blesta's payment-method page without a login once BLESTA_SYSTEM_KEY/BLESTA_SYSTEM_KEY_FILE is set (see Configuration). Passing an empty key is not equivalent to the default: Blesta uses the empty string literally, so the resulting sid is rejected.

Invoice PDFs

Stock Blesta renders invoice PDFs in the InvoiceDelivery component, which the API does not route. The free Component API plugin from Blesta fixes that: install it once (unzip into plugins/, then Settings > Company > Plugins > Available > Install) and download_invoice_pdf calls InvoiceDelivery.downloadInvoices through it and saves the PDF locally. Without the plugin the tool fails with an explanatory message. No invoice caching setting is needed.

Documentation

Development

npm run dev      # tsc --watch
npm run build
node dist/index.js   # speaks MCP over stdio

License

GPL-3.0-only. See LICENSE.

Available Tools

33 tools
add_client_noteAdd client noteA

Record a staff note on a client account (e.g. summary of a support interaction). Wraps Clients.addNote. staff_id defaults to BLESTA_STAFF_ID.

Inputs: title (required), client_id (required), sticky, staff_id, +1 more.

apply_transactionApply transaction to invoicesA

Apply an existing transaction's unapplied balance (client credit) to one or more invoices in the same currency. Wraps Transactions.apply.

Inputs: amounts (required), transaction_id (required).

blesta_callCall any Blesta API methodA
Destructive

Escape hatch: call any public model method of the Blesta API as {model}/{method}. Parameters are passed BY NAME and must match the PHP method's argument names (see https://source-docs.blesta.com/classes/{Model}.html). Nested arrays/objects are supported (e.g. params: {"vars": {"client_id": 1, "lines": [{"description": "x", "amount": "5.00"}]}}). Plugin models use "plugin.model" (e.g. "support_manager.support_manager_tickets"). If http_method is omitted it is inferred from the method name (get*/search* -> GET, add* -> POST, edit*/set* -> PUT, delete* -> DELETE). Timestamps must include a timezone (e.g. 2026-01-31T12:00:00Z). If Blesta answers HTTP 500 'Failed to retrieve the default value', resend with every optional argument explicitly set. Any valid API key has full administrative power; prefer the purpose-built tools when one exists.

Inputs: model (required), method (required), params, max_chars, +1 more.

cancel_serviceCancel serviceA
Destructive

Cancel a service now, at end of the current term, or on a given date. Wraps Services.cancel. when: 'end_of_term' (default, safest), 'now' (immediate, module deprovisions if use_module), or an ISO 8601 date with timezone. Scheduled cancellations are executed by Blesta's cron and can be undone before then with blesta_call services/unCancel.

Inputs: service_id (required), when, reason, use_module, +1 more.

create_invoiceCreate invoiceA

Create a new invoice for a client with one or more line items. Wraps Invoices.add. Dates default to now (billed) and now+7 days (due). Set deliver_by_email to queue the invoice email (sent by Blesta's cron). Set draft to create it unissued. Returns the new numeric invoice ID and the invoice as stored.

Inputs: lines (required), currency (required), client_id (required), draft, +5 more.

get_clientGet clientA
Read-onlyIdempotent

Retrieve one customer by numeric client ID: identity, primary contact details (name, email, address, phone numbers), status (active/inactive/fraud), client group and, optionally, effective settings (currency, language, autodebit, ...). Wraps Clients.get.

Inputs: client_id (required), include_settings.

get_client_balanceGet client balanceA
Read-onlyIdempotent

Amount the client owes in a currency (sum of open invoices) plus invoice counts by status (open, past_due, closed, ...). Wraps Invoices.amountDue and Invoices.getStatusCount. Use get_client with include_settings to learn the client's default currency.

Inputs: currency (required), client_id (required).

get_client_contactsGet client contactsB
Read-onlyIdempotent

All contacts under a client (primary, billing, other) with name, email, address and phone/fax numbers. Wraps Contacts.getAll and Contacts.getNumbers.

Inputs: client_id (required), contact_type, include_numbers.

get_client_notesGet client notesA
Read-onlyIdempotent

Staff notes on a client account, newest first, plus pinned (sticky) notes. Read these before acting on an account. Wraps Clients.getNoteList, Clients.getNoteListCount and Clients.getAllStickyNotes.

Inputs: client_id (required), page.

get_client_servicesGet client servicesA
Read-onlyIdempotent

List a customer's services (hosting accounts, domains, add-ons) with status, package/pricing, renewal date and price. Wraps Services.getList (paged, default status 'active') and, when include_renewal_price is set, Services.getRenewalPrice per service. Pass service_id instead of client_id to fetch a single service (Services.get), including module fields such as domain/username. Results are compacted by default; set full for the raw Blesta objects.

Inputs: full, page, status, client_id, +3 more.

get_client_transactionsGet client transactionsA
Read-onlyIdempotent

Payment history for a client (or all clients): amount, currency, type (cc/ach/other), status, gateway, reference and which invoices each payment was applied to. Wraps Transactions.getList / getListCount. Filters: payment_type, reference_id, date and amount ranges, applied_status (fully_applied, partially_applied, not_applied).

Inputs: page, status, end_date, client_id, +6 more.

get_invoiceGet invoiceA
Read-onlyIdempotent

Read one invoice by numeric invoice ID: displayed number (id_code), client_id, status, date_billed, date_due, date_closed, currency, subtotal, total, paid, due (total minus paid), taxes and line_items with descriptions, quantities and amounts. Wraps Invoices.get. Use search_invoices first if you only know the displayed invoice number.

Inputs: invoice_id (required).

get_invoice_paymentsGet invoice paymentsA
Read-onlyIdempotent

List payments (transactions) applied to an invoice: applied amount and date, transaction status (approved/declined/void/error/pending/returned), payment type, gateway, reference and transaction number. Wraps Transactions.getApplied(invoice_id). Set include_transaction_details to also fetch each full transaction record (Transactions.get). Also returns the invoice's current paid/due summary.

Inputs: invoice_id (required), include_transaction_details.

get_packageGet packageB
Read-onlyIdempotent

One package in full: pricing per term, description, module, configurable option groups, groups. Wraps Packages.get.

Inputs: package_id (required).

get_payment_accountsGet payment accountsA
Read-onlyIdempotent

Cards and bank accounts a client has on file (masked: last4, type, expiry, holder name, gateway reference). Never returns full numbers. Wraps Accounts.getAllCcByClient and Accounts.getAllAchByClient. Useful for autodebit failures and expired cards.

Inputs: client_id (required).

get_quotationGet quotationA
Read-onlyIdempotent

One quote with its line items and any invoices generated from it. Wraps Quotations.get, getLineItems, getInvoices.

Inputs: quotation_id (required).

get_service_actionsGet service actionsA
Read-onlyIdempotent

Which operations Blesta allows on a service in its current state (e.g. suspend, unsuspend, cancel, uncancel, change_renew_date, ...). Wraps Services.get + Services.getActions(current_status).

Inputs: service_id (required).

get_transactionGet transactionA
Read-onlyIdempotent

One payment in full plus the invoices it was applied to. Wraps Transactions.get and Transactions.getApplied(transaction_id).

Inputs: transaction_id (required).

list_packagesList packagesA
Read-onlyIdempotent

Product catalog: packages and package groups. Wraps Packages.getAll (compact view) and Packages.getAllGroups. Packages.getAll does not return prices; set include_pricing (max 25 packages per call, so filter by name or group first) to fetch Packages.get for each and attach pricing per term/period/currency. Pass full for raw objects incl. descriptions.

Inputs: full, name, type, status, +4 more.

list_quotationsList quotationsA
Read-onlyIdempotent

Quotes (estimates) for a client or across all clients, by status. Wraps Quotations.getList / getListCount. Statuses: draft, pending, approved, invoiced, expired, dead, lost, all.

Inputs: page, status, client_id.

lookup_couponLookup couponA
Read-onlyIdempotent

Validate a promo code: existence, status, start/end dates, usage limits, discount amounts per currency, eligible packages. Wraps Coupons.getByCode; with package_ids, also Coupons.getForPackages to check applicability.

Inputs: code (required), package_ids.

process_paymentCharge payment account on fileA
Destructive

Charge a client's stored card or bank account through the payment gateway and apply the result to invoices. Wraps Payments.processPayment with account_id only; raw card data is never accepted. Sends the client a receipt unless disabled. Moves real money. DISABLED: set BLESTA_ALLOW_PAYMENTS=1 to enable.

Inputs: type (required), amount (required), currency (required), client_id (required), +4 more.

record_manual_paymentRecord manual paymentA

Record an offline payment (bank transfer, cash, check) and apply it to invoices. Wraps Transactions.add then Transactions.apply. Does not charge anything; it only records money already received. Omit apply_to to leave the amount as unapplied credit.

Inputs: amount (required), currency (required), client_id (required), message, +3 more.

search_clientsSearch clientsA
Read-onlyIdempotent

Find customers matching a free-text query (email, first/last name, company, client ID code such as '1234'). Wraps Clients.search. Returns a page of client records including the primary contact's name, email and status. Use get_client with the returned id for full details.

Inputs: query (required), page.

search_invoicesSearch invoicesA
Read-onlyIdempotent

Find invoices two ways. (1) query: free-text search by displayed invoice number, client name/email or line description (wraps Invoices.search). (2) client_id: list a customer's invoices filtered by status (wraps Invoices.getList; default status 'open'). Provide at least one of query or client_id. Each result includes numeric id, displayed id_code, status, dates, total, paid and due amounts.

Inputs: page, query, status, currency, +2 more.

search_servicesSearch servicesA
Read-onlyIdempotent

Find services across all clients by domain, hostname, username, package name or service number. Wraps Services.search (with search_fields to include module field values such as the domain). Returns compact service records with client_id.

Inputs: query (required), full, page, search_fields.

send_invoiceSend invoiceA

Queue (re)delivery of an invoice to the client by email or another configured method. Wraps Invoices.addDelivery. Blesta's cron performs the actual send within a few minutes; the tool also returns the delivery log so you can see prior sends.

Inputs: invoice_id (required), method.

suspend_serviceSuspend serviceB
Destructive

Suspend a service (and, when use_module is true, tell the provisioning module to suspend the account). Wraps Services.suspend.

Inputs: service_id (required), reason, staff_id, use_module.

unsuspend_serviceUnsuspend serviceA

Reactivate a suspended service (and on the module when use_module is true). Wraps Services.unsuspend.

Inputs: service_id (required), staff_id, use_module.

update_client_statusUpdate client statusA
Destructive

Set a client's status to active, inactive or fraud. Wraps Clients.edit(client_id, {status}). Inactive/fraud clients cannot log in; fraud also blocks orders.

Inputs: status (required), client_id (required).

update_invoiceUpdate invoiceA
Destructive

Edit invoice header fields: status (active/draft/proforma/void), due date, billed date, notes. Wraps Invoices.edit. Use status: "void" to void an unpaid invoice (irreversible in practice). Line items are not editable here; use blesta_call invoices/edit with lines for that.

Inputs: invoice_id (required), status, date_due, date_billed, +2 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.8/5.0

    Scored across 33 tools

    Disambiguation5/5

    Each tool targets a distinct resource+action combination, even in dense areas like payments (record_manual_payment vs apply_transaction vs process_payment are clearly separated by funding source and behavior). The only generic tool, blesta_call, is explicitly an escape hatch rather than a competing operation.

    Naming Consistency4/5

    Tool names overwhelmingly follow a snake_case verb_noun pattern (get_client, create_invoice, suspend_service). Minor deviations like lookup_coupon instead of get_coupon and list_quotations alongside get_quotation are predictable but not perfectly uniform.

    Tool Count3/5

    33 tools is heavy, but the breadth of Blesta's domain (clients, invoices, services, payments, packages, quotes, coupons) justifies most of them. The count sits above the typical heavy range and some getters could potentially be consolidated, yet no tool feels truly redundant.

    Completeness3/5

    Core client, invoice, service, and payment operations are well covered, including search, retrieve, and common state changes. Notable gaps exist (no create_client, no package/quote creation, no direct line-item editing), though blesta_call provides a workaround for advanced operations.

    Maintenance

    ActivitySlowing
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      C
      maintenance
      Enables LLMs to interact with Billforward's billing and subscription management API, providing tools for accounts, subscriptions, invoices, payments, and more with read-only safety by default.
      23
      31 npm
      3
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI agents to administrate WHMCS installations through the External API, providing ~50 tools for clients, billing, orders, services, domains, support, and aggregators with safety features and governance.
      58 npm
      3
      ISC
    • A
      license
      B
      quality
      B
      maintenance
      Enables AI assistants to access the Billingo v3 invoicing API through MCP, providing 49 read/write tools for managing invoices, partners, and related data over stdio or HTTP transports.
      22
      63 npm
      3
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI assistants to query and manage a web hosting billing platform through its External API — clients, tickets, invoices, domains, orders, products, and system data. It defaults to read-only with per-domain opt-in for writes, two-step confirmation for billing and destructive actions, and automatic redaction of secrets, card data, and PII.
      1
      MIT