Skip to main content
Glama
TheBananaStand

leo-proluxe-mcp

leo-proluxe-mcp

Proluxe ERP as a Leo package, over MCP — customers, orders, invoices, AR aging, bills of materials, the product catalog, focus priorities and projects, from the Proluxe API gateway.

The same integration as the compiled leo-proluxe package, reachable as a package the hub installs at runtime rather than one it has to be rebuilt for.

Tools

The compiled package exposed one proluxe tool with a fifteen-value action enum and a nine-field union schema. MCP names tools, so each action is its own tool with only its own parameters — the same fifteen names, the same paths.

Tool

Requires

Upstream

find_customer

query

/CRM/customers/search (limit 10)

customer_details

id

/CRM/customers/{id}/details

find_orders

/CRM/orders (limit 50)

find_opportunities

/CRM/opportunities (limit 25)

find_leads

/CRM/leads (limit 25)

find_invoices

/Finance/invoices (limit 50)

ar_aging

/Finance/aging/ar

income_statement

/Finance/income_statement

bom_inspect

id

/MRP/inspect/{part}, then Main_{part}

find_products

query

/CRM/products/search (limit 10)

product_details

id

/CRM/products/{id}

focus_week

/Common/focus/weeks/current

focus_notes

/Common/focus/notes (limit 20)

projects

/Projects/projects

project_cards

/Projects/projects/{id}, or /Projects/cards

Everything downstream keys on the 18-character Salesforce id, so find_customer comes first and its id goes into find_orders and find_invoices.

bom_inspect is two requests when it has to be: the MRP service keeps some parts under a Main_ prefix and some not, and which is which is not knowable from the part number. The bare form is tried first, the prefix is the fallback, and a failure names both — "SL1266TBAU not found" would send you looking for a part that is right there under a name nobody mentioned.

project_cards branches on absence: with a project id you get that project's cards, without one you get every card. A blank id is an absent one.

Responses are forwarded as the upstream JSON text, unparsed.

Related MCP server: odoo-mcp-server

Authentication

An Auth0 machine-to-machine client-credentials grant. This server POSTs client_id, client_secret, audience and grant_type=client_credentials to https://{domain}/oauth/token, then sends the token as Authorization: Bearer alongside X-User-Email, which is what scopes user-facing requests.

The token is cached and reused for the lifetime Auth0 reports, minus five minutes so none expires in flight. If the gateway answers 401 or 403 the cached token is dropped and the request is retried exactly once with a fresh one — without that, a token that dies early (rotated secret, revoked grant, clock skew) wedges every Proluxe call until the process restarts; with more than one, a genuinely bad credential becomes an unbounded stream of token requests. A second refusal says the credentials and audience are the thing to look at.

Configuration

Six settings. Leo hands them to this process under their settings keys, verbatim and lower-case, so the descriptor's settings_read and process.env.<key> have to agree or the credential silently never arrives.

Key

proluxe_api_url

required — the gateway base URL

proluxe_auth0_client_id

required — from a machine-to-machine app

proluxe_auth0_client_secret

required

proluxe_user_email

required — scopes user-facing requests

proluxe_auth0_domain

optional, defaults to proluxe-portal.auth0.com

proluxe_auth0_audience

optional, defaults to https://portal.proluxe.com

Without the four required ones the server still starts and lists its tools; every call answers with the missing keys named and where to enter them.

Development

npm install
node test.js        # no network needed

The test covers what fails quietly, with an injected fetch and an injected clock:

  • The token body. Omit audience and Auth0 returns a 200 with a real but opaque token, which the gateway then declines — that reads as a wrong secret. clientId for client_id is not a malformed request, it is a request with no client id.

  • The cache and its arithmetic. That one token serves many calls; that it expires when Auth0 said it would; that an unreported expires_in falls back to an hour rather than to forever; that a lifetime shorter than the buffer floors instead of re-minting on every call.

  • A bad token, told apart from an empty result. 401/403 retries once and only once, 500 is not retried at all, an Auth0 refusal and a 200 with no access_token both raise rather than return nothing. Every one of these otherwise arrives as an empty page, indistinguishable from a customer who has no invoices.

  • Path and query building. Absent stays absent — an undeclared filter interpolated into a URL is the literal text undefined, which the gateway honours as a real customer id. Empty means no ? at all; limit is read as the Rust read it (as_u64, so the string "200" silently falls back); the per-action defaults are pinned; / escapes, so an id cannot walk sideways into another endpoint.

  • The Main_ fallback, driven both ways round — a retry that re-requested the bare path would pass on every part that already worked and fail on exactly the parts the fallback exists for.

Faithfulness notes

Two places where this is deliberately not a byte-for-byte port:

  • Non-ASCII percent-encoding. The Rust wrote %{:02X} of the code point, so é went out as Latin-1 %E9 and as the malformed five-character %20AC. Here it is UTF-8. ASCII — which is all a Salesforce id or a part number contains — is byte-identical, including + for a space in path segments as well as query values, which is the Rust's spelling and the one the gateway has been answering.

  • A non-string required argument. The Rust's require_str! refused a numeric query; here the schema asks for a string and a number is stringified, matching the reference servers.

Publishing

./store/publish.sh          # live
./store/publish.sh draft    # stage for review at admin.leoconnect.io

Needs a Cloudflare login with D1:Edit on the leo-store database. The script refuses unless the pinned commit is both real and pushed — a SHA that resolves nowhere installs cleanly and then fails on every hub at first launch.

F
license - not found
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    C
    quality
    A
    maintenance
    MCP server for IQMS / DELMIAworks (EnterpriseIQ) — read manufacturing ERP data via Oracle and post transactional updates via the WebAPI module.
    16
  • F
    license
    Not graded
    quality
    C
    maintenance
    Governed MCP bridge that lets AI clients query live ERP data via SuiteQL, saved searches, and records, with role-based permissions and full audit logging.

View all related MCP servers

Related MCP Connectors

  • Provide seamless access to Appfolio Property Manager Reporting API through a standardized MCP serv…

  • QuickBooks MCP Pack — query customers, invoices, and accounts via QuickBooks Online API.

  • A paid remote MCP for AI SDK data query MCP, built to return verdicts, receipts, usage logs, and aud

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TheBananaStand/leo-proluxe-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server