@hvgllc/hvgerp-mcp
v3.4.0
Published
MCP server for ERPNext with interactive UI viewers
Maintainers
Readme
@hvgllc/hvgerp-mcp
MCP server for ERPNext / Frappe ERP — 131 tools across 16 categories, with 7 interactive UI viewers.
Connect any MCP-compatible AI agent (Claude Desktop, Claude Code, VS Code Copilot, custom) to your ERPNext instance via the Model Context Protocol.
Works with self-hosted and ERPNext Cloud (frappe.cloud) instances.
Built on @casys/mcp-server — the MCP server framework (concurrency, auth, MCP Apps, observability) that powers this project.
Forked from @casys/mcp-erpnext by Casys AI, MIT licensed. Everything up to 2.6.0 is their work; this fork renames the package and adds tools of its own.
Screenshots
Interactive viewers rendered inside an MCP host, driven entirely by tool results.
What's New
See the CHANGELOG for the full release history, or the latest release for the current version's highlights.
Documentation
Organised by what you are doing, following Diátaxis:
| | | | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Learning — never used this before | Your first tool call — from nothing to a working response in four steps | | Doing — you have a specific goal | Seed a blank ERPNext instance · Run the HTTP server · Set up OAuth · Migrate to 2026-07-28 | | Looking something up | Tools · Environment variables · DocType coverage | | Understanding why | Concepts — link resolution, transports, MRTR, and which cache does what · ERPNext quirks |
Quick Start
Prerequisites
Generate API credentials in ERPNext:
- Login to ERPNext → top-right menu → My Settings
- Section API Access → Generate Keys
- Copy
API KeyandAPI Secret
Claude Desktop / Claude Code (npm)
{
"mcpServers": {
"erpnext": {
"command": "npx",
"args": ["-y", "@hvgllc/hvgerp-mcp"],
"env": {
"ERPNEXT_URL": "http://localhost:8000",
"ERPNEXT_API_KEY": "your-api-key",
"ERPNEXT_API_SECRET": "your-api-secret"
}
}
}
}Works with ERPNext Cloud — set
ERPNEXT_URLto your Frappe Cloud URL (e.g.https://mycompany.erpnext.comorhttps://mysite.frappe.cloud). API key authentication works the same way on self-hosted and cloud instances.
VS Code Copilot
Add to .vscode/mcp.json:
{
"servers": {
"erpnext": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@hvgllc/hvgerp-mcp"],
"env": {
"ERPNEXT_URL": "http://localhost:8000",
"ERPNEXT_API_KEY": "your-api-key",
"ERPNEXT_API_SECRET": "your-api-secret"
}
}
}
}Deno (stdio)
{
"mcpServers": {
"erpnext": {
"command": "deno",
"args": ["run", "--allow-all", "server.ts"],
"env": {
"ERPNEXT_URL": "http://localhost:8000",
"ERPNEXT_API_KEY": "your-api-key",
"ERPNEXT_API_SECRET": "your-api-secret"
}
}
}
}HTTP mode
For a shared, always-on server rather than one process per client: how to run the HTTP server. Note it is breaking for pre-2026 HTTP clients in 3.0.0.
Category filtering
Load only the categories you need:
npx -y @hvgllc/hvgerp-mcp --categories=sales,inventoryFresh Instance Setup
A blank ERPNext instance has no master data, so business tools fail validation until it exists. See Seed a blank ERPNext instance.
UI Viewers
Seven interactive MCP Apps
viewers, registered as ui://hvgerp-mcp/{name}:
| Viewer | Description | Interactive Features |
| ---------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| doclist-viewer | Generic document table with sort, filter, pagination, CSV export | Row click → inline detail panel with Submit/Cancel + sendMessage navigation. Chip filters for status columns. Max 6 columns, rest in detail panel. |
| invoice-viewer | Sales/Purchase Invoice with parties, items, totals | Item click → stock balance + item info panel. Submit/Cancel/Payment actions. sendMessage to payment entries and customer invoices. |
| stock-viewer | Stock balance table with color-coded qty badges | Row click → item info + recent movements. sendMessage to stock chart, item details, stock entries. |
| chart-viewer | Universal chart renderer (12 types via Recharts) | Click bar/pie/line data points → sendMessage drill-down into underlying documents. |
| kanban-viewer | Read-write kanban for Task, Opportunity, Issue | Drag-and-drop moves, inline edit (priority, progress, dates), sendMessage to Timesheets/Quotations/Related docs. |
| kpi-viewer | Big number card with delta, sparkline, trend | Click number → sendMessage to exception list. Click sparkline → trend chart. |
| funnel-viewer | Trapezoid sales funnel with conversion rates | Click stage → sendMessage to document list at that stage. Stage action buttons. |
Cross-viewer navigation
Viewers communicate via app.sendMessage() — clicking a button in one viewer
injects a message into the conversation, which triggers the AI to call the right
tool and open the appropriate viewer.
The server auto-injects navigation metadata into tool results:
_rowAction— which tool to call when a row is clicked_sendMessageHints— navigation buttons shown in detail panels (e.g. "Orders", "Invoices")_drillDown/_trendDrillDown— sendMessage templates for KPI and chart click-through
Refresh model
All viewers carry a refreshRequest payload for safe revalidation via
app.callServerTool():
kanban-viewerrevalidates after mutations and on focus- All other viewers support focus refresh + manual refresh button
Building UI viewers
cd src/ui
npm install
node build-all.mjsTools (131)
131 tools across 16 categories. Each _list tool returns interactive results
via the doclist-viewer with row click, inline detail, and cross-viewer
navigation.
- Identity (2) —
erpnext_whoami(who the server believes the caller is: User id, roles, linked Employee, and whether the connection is per-caller or a shared service account) anderpnext_my_work(everything currently open for that person). Callerpnext_whoamibefore answering any first-person request: every other tool needs a concrete user or employee id, and this is the only one that produces it. - Sales (17) — Customers, Sales Orders, Invoices, and Quotations with full CRUD, Submit, and Cancel.
- Purchasing (11) — Suppliers, Purchase Orders, Purchase Invoices, Receipts, and Supplier Quotations.
- Inventory (9) — Items, Stock Balance, Warehouses, and Stock Entries.
- Accounting (8) — Chart of Accounts (filterable by
disabled), Journal Entries, and Payment Entries, pluserpnext_gl_entry_list(the posted general ledger, cancelled entries excluded by default) anderpnext_financial_report, which runs a standard financial report (Profit and Loss Statement, Balance Sheet, General Ledger, Trial Balance…) from a closed allowlist and never queues a Prepared Report. - HR (12) — Employees, Attendance, Leave Applications, Salary Slips, Payroll Entries, and Expense Claims.
- Project (9) — Projects, Tasks (with native assignment), and Timesheets.
- Delivery (5) — Delivery Notes and Shipments.
- Manufacturing (7) — BOMs, Work Orders, and Job Cards.
- CRM (8) — Leads, Opportunities, Contacts, and Campaigns.
- Assets (8) — Assets, Movements, Maintenance records, and Categories.
- Operations (12) — Generic CRUD, native assignment, and file upload for any
DocType (
erpnext_doc_*,erpnext_file_upload), pluserpnext_method_callfor allowlisted whitelisted-method calls anderpnext_calendar_events, which reads a date range the way the ERPNext calendar does so a repeating event appears once per occurrence instead of once in total. - Kanban (2) — Read-write boards for Task, Opportunity, and Issue with drag-and-drop.
- Analytics (17) — 11 analytics charts (bar, area, treemap, radar, scatter, P&L…), 5 KPIs with sparklines, and a sales funnel.
- Setup (3) — Company creation and assignable user listing.
- Discovery (1) —
erpnext_doctype_fields: the field schema of any DocType (name, label, type, link target, mandatory, permlevel), gated on the caller's own read permission. Use it instead of inferring a schema from one sample document, which only shows the fields that happen to be filled in.
Full per-tool reference with parameters: docs/tools.md.
Environment Variables
| Variable | Required | Description |
| -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ERPNEXT_URL | Yes | ERPNext base URL — self-hosted (e.g. http://localhost:8000) or cloud (e.g. https://mycompany.erpnext.com) |
| ERPNEXT_API_KEY | stdio | API Key from User Settings. Over HTTP, leave it unset and let each caller's own token decide who the server acts as (see Caller identity) |
| ERPNEXT_API_SECRET | stdio | API Secret from User Settings |
| MCP_CALLER_IDENTITY | No | required | optional | off. HTTP only. Defaults to required when no API key/secret is set, off when they are |
| ERPNEXT_MAX_UPLOAD_BYTES | No | Maximum decoded file-upload size in bytes (positive integer; default: 10 MiB) |
| ERPNEXT_METHOD_ALLOWLIST | No | Comma-separated dotted paths or prefix.* patterns that erpnext_method_call may invoke. Unset means no extra restriction beyond the API key's own ERPNext permissions |
| MCP_MRTR_SIGNING_KEY | No | Exactly 64 lowercase hex characters; enables signed ambiguous-link elicitation. Single-instance deployments only — see below |
Caller identity (HTTP)
Over HTTP the server can act as the user who made the call instead of under
one shared ERPNext account. Each tool call forwards that user's own verified
access token to Frappe as Authorization: HVGKeycloak <token>; Frappe resolves
it to a User and applies that user's roles and row-level permissions. Two
people calling the same tool therefore get different rows, and every write is
attributed to the person who asked for it.
This needs an OAuth/OIDC auth provider (MCP_OAUTH_JWKS_URL) whose tokens carry
an email claim, and an ERPNext side that accepts the scheme. sub is not
accepted as a fallback identity: it maps to no ERPNext user, so a deployment
with the wrong claims fails loudly instead of quietly serving the wrong data.
MCP_CALLER_IDENTITY=required refuses any call that carries no usable identity.
optional binds the identity when present and otherwise falls back to the
static API key. off is the pre-3.1 behaviour. Under required the read cache
is per caller and the startup cache warm is skipped — there is no user to warm
it as.
MRTR is opt-in. Without this key, or when the client does not advertise elicitation, ambiguous links keep returning the existing actionable ambiguity error instead of prompting for a selection.
Do not run MRTR behind a load balancer with this configuration. The signing key proves a retry token is authentic; it does not make it single-use. That is the job of a replay store, and the default one is process-local. Share the key across two instances and the same signed retry validates on both — creating the purchase order, leave application or expense claim twice, irreversibly once submitted.
A multi-instance deployment must pass a shared atomic
mrtr.replayStoretoMcpApp(Redis satisfies the contract withSET key 1 NX EXAT). The framework logs a warning at startup whenever MRTR is enabled without one — that warning is not noise, it is this paragraph.
Architecture
Tools are grouped by business domain under src/tools/, the Frappe REST client
is dependency-free, and each UI viewer is a separate build under src/ui/. Full
layout: repository layout.
npm Package
The npm package (@hvgllc/hvgerp-mcp) is a single self-contained bundle with
zero runtime dependencies. UI viewers are embedded. Requires Node >= 20.
Contributing
Contributions are welcome — see CONTRIBUTING.md to get started, and AGENTS.md for the full architecture and conventions.
License
MIT
