Install
$ agentstack add mcp-soumyasethy-lazychat-erpnext ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
lazychat-erpnext
Talk to ERPNext like a senior consultant.
95 permission-scoped tools · two-phase mutations · composer-critic verification · BYO LLM
↑ 78-second flagship walkthrough — stakeholder ask → tool dispatch → report URL → BYO LLM in one shot. (Want HD? ▶ download the MP4.)
↑ Still hero — the post-dispatch state of the panel, mid-conversation.
From stakeholder request to delivered report — in minutes
> "Hey, I need to verify which December purchase invoices have payment entries against them. Can we get a report by EOD?"
That ask used to mean opening 3 Frappe doctypes, writing a custom Query Report, debugging joins, fixing field names, and probably a meeting. With lazychat, the consultant types the stakeholder's words verbatim into the chat panel.
Step 1 — Type the stakeholder's request The consultant pastes the verbatim ask into the chat composer.
Step 2 — Lazychat dispatches tools, returns data describedoctype → runsql_select → inline result table. Real data. No copy-pasting from /api/method.
Step 3 — Stage the report, click Apply preparecreatereport validates the SQL via execute-probe, shows sample rows, the critic LLM grades it. One click commits.
Step 4 — Share the URL with the stakeholder Copy the report URL. Done. Time elapsed: ~2 minutes.
The same flow works for cross-doctype reconciliations, variance reports, ad-hoc audits, and bulk operations. 95 permission-scoped tools back the chat — see the catalog below for what each can do.
Bring your own LLM in 30 seconds
Self-hosted LM Studio? Anthropic? NVIDIA NIM? OpenRouter? Together? Groq? You don't fill out a form. You paste a curl snippet from the provider's own docs — and lazychat parses it into endpoint, model, auth, headers, streaming flag, even provider-specific payload extras. The API key stays in the browser (browser-LLM path); no server-side credential storage, no shared org-key risk.
The 4-step flow
- Open the model pickerFrom the chat composer, click the model chip (bottom-left) → + Add custom model.
- Paste any provider's curl into the right-hand panelEndpoint, model, auth, format, streaming, maxtokens, temperature, topp, extra headers, extra payload — all auto-fill on the left.
- Click Test connection → green ✓ → Add modelTest connection makes a 1-token probe request, shows the HTTP status + first 800 chars of the response. Green check = ready to ship.
- Switch instantly, per sessionCustom models appear under a CUSTOM section in the picker. Click to switch — the chat composer's model chip updates immediately. Mix free / paid / local on different chats.
Tested provider snippets (paste these verbatim)
NVIDIA NIM — auto-detects OpenAI format, picks up chattemplatekwargs for thinking-mode
curl -X POST "https://integrate.api.nvidia.com/v1/chat/completions" \
-H "Authorization: Bearer nvapi-..." \
-H "Accept: text/event-stream" \
-H "Content-Type: application/json" \
-d '{
"model": "moonshotai/kimi-k2.6",
"messages": [{"role":"user","content":""}],
"max_tokens": 16384,
"temperature": 1.0,
"stream": true,
"chat_template_kwargs": {"thinking": true}
}'
Auto-fills label Kimi K2 (NVIDIA), endpoint, format=openai, streaming=✓, bearer token, max_tokens, temperature, plus chat_template_kwargs lands in the Extra payload field verbatim.
Anthropic (direct, no Frappe LLM Provider needed) — auto-detects messages format + x-api-key auth
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: sk-ant-..." \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-haiku-4-5",
"max_tokens": 4096,
"stream": true,
"messages": [{"role":"user","content":""}]
}'
Auth type → API Key, header name → x-api-key, format → anthropic, response parser → anthropic-sse. The custom anthropic-version header lands in Extra headers.
OpenAI
curl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5","messages":[{"role":"user","content":""}],"stream":true}'
OpenRouter — gateway to 200+ models
curl https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer sk-or-..." \
-H "HTTP-Referer: https://your-site.example.com" \
-H "X-Title: Lazychat" \
-d '{"model":"meta-llama/llama-3.3-70b-instruct","stream":true}'
The HTTP-Referer and X-Title extras are required by OpenRouter and land in Extra headers.
LM Studio (localhost) — fully local, no API key
curl http://localhost:1234/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"meta-llama-3-8b-instruct","stream":true}'
Auth type → none, format → openai, streaming → ✓. Browser-LLM path makes localhost work because requests originate from the user's browser, not the Frappe server.
What the parser auto-fills from the curl
| Curl part | Form field | |---|---| | https://api.anthropic.com/v1/messages | URL ends /messages → Format = anthropic | | https://*/v1/chat/completions | URL ends /chat/completions → Format = openai | | -H "Authorization: Bearer XYZ" | Auth type = Bearer; Token = XYZ | | -H "x-api-key: XYZ" | Auth type = API Key; Header name = x-api-key; Token = XYZ | | -H "Accept: text/event-stream" | Streaming = ✓ | | Any other -H "X: Y" (e.g. anthropic-version, HTTP-Referer) | Extra headers (kv list) | | Payload "model":"X" | Model = X | | Payload "max_tokens":N / "temperature":N / "top_p":N | Defaults block | | Payload "stream":true | Streaming = ✓ | | Any payload key not in the above | Extra payload (preserved as JSON, merged into request body verbatim) | | Hostname (api.anthropic.com) | Label auto-generated as Anthropic Direct-style title |
> Test connection sends one messages: [{"role":"user","content":""}] request to the configured endpoint with the parsed auth + extras. Returns the HTTP status + first 800 chars of the response body in a green/red panel — instant proof the credential and endpoint work before you start using the model in real chats.
> Same auto-fill works for requests.post(...) snippets too — paste a Python snippet from a provider's quickstart and it parses identically.
Charts and dashboards rendered in-chat
Tools like make_chart, dashboard_chart_data, and number_card_value don't return raw JSON to the user — the chat-ui's ChartBlock renders them inline as proper Vega charts and KPI cards, in the panel, alongside the conversation. The consultant asks "chart of paid PIs by month" and the chart appears as the answer. No tab-switching, no exporting to a BI tool, no Frappe Dashboard form-filling.
> Bar / line / area / donut / scatter all map to Frappe Charts via the make_chart tool. Multi-chart dashboards (with cards + charts grouped) ship via prepare_create_dashboard — you ask, lazychat stages, you click Apply, the dashboard appears at /app/dashboard-view/ ready to bookmark.
How tool results render in the chat
Tool calls don't return raw JSON dumps to the user. Lazychat's response renderer formats the LLM's reply using the full markdown stack — GitHub-flavored tables, syntax-highlighted code with shiki, mermaid diagrams, KaTeX math, blockquote callouts, structured headings — so a "tool result" reads like an answer, not a debug log.
Markdown tables — currency-aligned columns + status badges The LLM emits a plain markdown table; the renderer auto-aligns numerics, bolds important columns, and inlines emoji status pills (🟢 OK / 🟠 Watch / 🔴 Overdue).
Code blocks — full shiki highlighting + Copy button SQL / Python / TypeScript / JSON all light up with the same theme tokens as the surrounding panel. Language tag in the corner, Copy button inset, inline code spans (like this) keep the same palette.
Mermaid diagrams — relationships + flows in-chat The LLM ships a ```mermaid fence; the renderer lazy-loads mermaid, applies the panel's brand-orange theme, and you get a real ER diagram or flowchart inline. Better than 200 words of "see, the join is..."
Structured prose — headings, lists, blockquote callouts For analytical answers the LLM uses real document structure: H3 sub-headings, ordered lists with bold labels, italic emphasis, blockquote callouts for the recommended next action. The renderer applies the typographic hierarchy.
> Plus: KaTeX math (for reconciliation formulas), JSON tree expanders for raw tool outputs, automatic link-card previews for any URL the LLM cites, attachment renders for extract_file_content PDF/DOCX/CSV outputs, and a streaming markdown parser that keeps tables and code blocks structurally valid mid-token. The tool gives the data; the renderer makes it readable.
Install
Frappe Cloud: install from the marketplace in one click.
Self-hosted bench: see [INSTALL.md](INSTALL.md) — the standard bench install (the pre-built chat-ui ships with the app, so no build step needed), building from source, and HMR dev are all documented there.
After install, the app seeds disabled-by-default LLM Provider rows (OpenAI, Anthropic, NVIDIA, OpenRouter, Vercel AI, LM Studio) — enable one and add your API key from /app/llm-provider, or skip server-side config entirely and let users bring their own keys via the chat-ui's model picker (browser-LLM path).
Why lazychat-erpnext
- Built for ERPNext, not bolted on. Every tool runs as
frappe.session.user— Frappe permissions, role checks, workflow guards, and the audit trail apply automatically. No god-mode bypass; no separate auth surface. - Mutations require explicit Apply. The LLM stages every write to a Redis token; you click Apply (or the 3-second auto-Apply countdown for low-risk actions) to commit inside
frappe.db.savepoint. A 30-second composer-critic LLM second-opinion shows up as an amber strip when it disagrees with the staged action. - Bring any model. Anthropic Claude, OpenAI, NVIDIA NIM, OpenRouter, Vercel AI Gateway, Together, Groq, LM Studio. Same 95-tool registry; the API key never has to leave the browser if you don't want it to.
What you get
Live tool dispatchWatch the agent fetch real data with elapsed timers and inline result tables. No copy-pasting from /api/method.
Mutations always Apply-gatedThe LLM stages writes to a Redis token; you click Apply. Audit-safe by default.
Critic catches misalignmentA second LLM grades every staged action; mismatches show as an amber warning. You still get to decide.
Plan modeFor multi-step tasks: emit a numbered plan first, you Approve, then it executes the steps in order.
Built-in: schema-aware SQL retry on Unknown column, two chat paths (server-orchestrated or browser-LLM), real-execution probe before staging Query Reports, structured form prefill for HTTP-414-defying URLs, knowledge bases with reindex, scheduled jobs, dashboards, custom fields, client scripts, and an admin panel that moves all configuration into the chat-ui itself.
Token + cost tracking NEW
Every LLM turn writes a row to the Lazychat Usage Log doctype — model · provider · input/output tokens · cost estimate (USD) · session id · path (backend / browser). Cost auto-computed from LLM Model.input_price_per_mtok / output_price_per_mtok first, falls back to a built-in rate table for common Claude / GPT models, zero when both miss.
Open the chat-ui's Command Palette → Account & usage… to see:
- 4-tile totals (calls / input / output / cost)
- By-model rollup — sorted by total tokens, shows provider + cost per model
- Daily breakdown for the last 14 days
- Range picker (Today / 7d / 30d / 90d)
- System Manager users see ALL users' usage; everyone else sees their own
No setup required — works for Claude / OpenAI / NVIDIA / OpenRouter / Vercel / Anthropic / Together / Groq / LM Studio, regardless of whether you're on the backend-LLM or browser-LLM path. Set per-model rates on /app/llm-model/ if you want exact billing.
Architecture
The Frappe app ships a 280-line vanilla-JS shim ([public/js/lazychat_panel.bundle.js](lazychaterpnext/public/js/lazychatpanel.bundle.js)) loaded via app_include_js on every Desk page. The shim mounts the chat-ui (a React app, sibling repo lazychat.ai, bundled into public/lazychat_dist/) as a same-origin iframe, sets up the postMessage protocol, and intercepts /commit slash commands to call the server.
Tool dispatch goes through one of two paths, both backed by the same 95-tool registry:
| Path | LLM lives | Tool dispatch | Best when | |---|---|---|---| | Backend-LLM | Frappe (LLM Provider doctype) | run_agentic_turn calls execute_tool in-process | Org deployments, shared keys, central audit | | Browser-LLM | chat-ui (BYO key in localStorage) | chat-ui calls mcp.handle JSONRPC per tool_use | Single-user / power-user; key never touches server |
Default chat_path = auto: chat-ui inspects the active model — built-in → backend; custom-added → browser. Both paths run with frappe.session.user's permissions, both write to Claude Conversation, both share tools.py. Zero drift, one implementation.
Configuration
Server Config dialog inside the chat panel. Open via Command Palette → Server config. System Manager only.
Primary admin surface (in-app): Command Palette → Server config → 3 tabs:
- General — gates (
allow_email,allow_dangerous_tools,cycle9_enabled…),chat_pathradio,iframe_base_url,llm_proxy_allowed_hostschip list. - LLM Providers — Anthropic / OpenAI-compatible CRUD with Test Connection + Reveal API Key.
- LLM Models — model picker contents;
is_defaulttoggle (single-default invariant enforced).
Or edit the Lazychat Settings doctype directly. All defaults are allow-on for self-hosted single-org installs; defense-in-depth is preserved (System Manager role check at tool dispatch + /commit confirmation per call).
| Field | Default | What it does | |---|---|---| | enabled | true | Master switch — mount the panel at all | | iframe_base_url | /assets/lazychat_erpnext/lazychat_dist/index.html | Where chat-ui loads from. Override for HMR (http://127.0.0.1:5173) or remote chat-ui | | iframe_query_params | ?frame=sidebar | Appended to base_url | | chat_path | auto | auto / browser / backend — see Architecture above | | mcp_endpoint | /api/method/lazychat_erpnext.desk_assistant.mcp.handle | Read-only; browser-LLM path uses this | | legacy_widget_enabled | false | Mount the OLD vanilla-JS widget INSTEAD of the iframe (mutually exclusive) | | allow_email | true | Enable prepare_send_email | | allow_dangerous_tools | true | Enable prepare_run_sql + prepare_run_python (still gated by System Manager role + /commit) | | allow_email_setup | true | Enable prepare_create_email_account | | cycle9_enabled | true | Enable composer-critic verdict, verification briefs, exemplar memory | | bulk_update_max_rows | 500 | Ceiling for prepare_bulk_update blast radius |
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: soumyasethy
- Source: soumyasethy/lazychat-erpnext
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.