NexWave MCP
Allows connecting to an ERPNext site and provides read-only tools for viewing the current user, companies, customers, items, sales orders, and allowlisted sales and purchase documents.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@NexWave MCPlist my recent sales orders"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
NexWave MCP
NexWave MCP is a small, self-hosted gateway that connects AI clients such as ChatGPT, Claude, and Codex to a NexWave or ERPNext site.
It uses standard MCP with OAuth 2 for interactive users and Frappe API tokens for non-interactive service clients. It does not need a custom Frappe app and does not use frappe_assistant_core.
Live POC
Service | URL |
MCP endpoint |
|
Service MCP endpoint |
|
Site setup |
|
Health check |
|
The setup page requires the private ADMIN_TOKEN Worker secret. Registered site names and URLs remain private.
Related MCP server: ERPNext MCP Server
Architecture
flowchart LR
ChatGPT["ChatGPT"]
Claude["Claude"]
Codex["Codex"]
Service["Non-interactive service client"]
MCP["NexWave MCP Server<br/>Cloudflare Worker"]
Site["NexWave / ERPNext Site<br/>Frappe REST API"]
D1[("Cloudflare D1<br/>Site registry, encrypted credentials,<br/>service token hashes, and audit events")]
KV[("Cloudflare KV<br/>OAuth state, grants, and tokens")]
Admin["NexWave administrator"]
ChatGPT -->|"Remote MCP over HTTPS<br/>OAuth 2"| MCP
Claude -->|"Remote MCP over HTTPS<br/>OAuth 2"| MCP
Codex -->|"Remote MCP over HTTPS<br/>OAuth 2"| MCP
Service -->|"Remote MCP over HTTPS<br/>Service token"| MCP
MCP -->|"Frappe REST API<br/>User OAuth token"| Site
MCP -->|"Frappe REST API<br/>Service API token"| Site
MCP --> D1
MCP --> KV
Admin -->|"Registers site credentials"| MCPThe Worker is the protocol and security boundary between each AI client and each NexWave site.
Component | Responsibility |
MCP client | Discovers the server, authenticates, and calls tools |
Cloudflare Worker | Handles MCP, OAuth, tool validation, and Frappe REST requests |
D1 | Stores the private site registry, encrypted OAuth or API-token credentials, service token hashes, and sign-in audit events |
KV | Stores short-lived OAuth state, grants, and MCP tokens |
NexWave site | Authenticates the user and applies normal Frappe permissions |
Authentication and request flow
The MCP client discovers the protected resource and OAuth metadata from the Worker.
The Worker asks the user for their exact NexWave site URL. It does not show a list of registered sites.
The Worker matches the URL origin against its private D1 registry.
The browser moves to the selected NexWave site for Frappe OAuth approval with PKCE S256.
After approval, the Worker returns control to the MCP client and issues an MCP token with the
nexwave:readscope.Each tool call uses the signed-in user's Frappe access token. Frappe applies that user's roles and document permissions.
Non-interactive clients can use the separate /service/mcp route. When an administrator registers a fixed API-token site, the Worker verifies the Frappe credentials and returns a generated service token once. It stores only the service-token hash and encrypted Frappe credentials in D1. A service client sends the generated token in the X-NexWave-Service-Token header. This route exposes the same read-only tool catalogue as /mcp.
Legacy JSON-RPC service tool calls receive finite application/json replies. Standard MCP tool failures retain isError: true by default. Clients that discard these errors can opt in with X-MCP-Error-Format: result. In that mode only, tool failures are delivered as normal MCP results containing status: "error", ok: false, and a safe error object with code, message, and retryable. The client must check this envelope before using any data. Authentication failures and protocol errors keep their normal semantics. The OAuth /mcp route does not use this option.
Each MCP data tool has a shared 50-second upstream request budget, including any lookups and bounded retries of retryable reads. The service response handler allows 60 seconds to deliver the result or a safe error. Configure Retell's MCP timeout_ms as 90000 so it does not stop waiting before the server can return that error. OAuth sign-in and token-exchange timeouts are unchanged.
Connect a client
Use this remote MCP endpoint in a client that supports OAuth-enabled remote MCP servers:
https://mcp.highflyertech.co.nz/mcpFor Codex CLI and the Codex desktop app:
codex mcp add nexwave --url https://mcp.highflyertech.co.nz/mcp
codex mcp login nexwave
codex mcp get nexwaveThe browser will ask for the NexWave site URL and then show that site's normal Frappe sign-in and access approval screens.
Connect a non-interactive service client
Use the service endpoint when a client cannot complete an interactive OAuth flow:
https://mcp.highflyertech.co.nz/service/mcpConfigure this request header:
X-NexWave-Service-Token: <generated-service-token>Add the site at /admin, select Fixed API token, and enter the API key and secret of a dedicated NexWave user. Copy the generated service token when the connection is saved. The same site URL can have one OAuth connection and one fixed-token connection. The gateway token is not a Frappe credential. The Worker uses its hash to select the registered site, decrypts the stored Frappe API token, and applies that service user's normal roles and permissions.
Available tools
All tools are read-only. List results and report output are bounded to keep MCP responses manageable.
Connection and master data
Tool | Purpose |
| Show the connected site and user |
| List accessible companies |
| Search customers |
| Search suppliers |
| Search items |
| List company warehouses |
| List company ledger accounts |
| List company cost centres |
| List company projects |
| List enabled fiscal years |
Transactions
Tool | Purpose |
| List sales orders |
| Search and filter sales invoices |
| Search and filter purchase orders |
| Search and filter purchase invoices |
| Search and filter payment entries |
| Search and filter imported bank transactions |
| Get an allowlisted business document by exact name |
get_document supports Customer, Item, Sales Order, Sales Invoice, Purchase Order, Purchase Invoice, Payment Entry, and Bank Transaction.
Reports
Tool | Standard ERPNext report |
| Stock Balance |
| Stock Ledger |
| Profit and Loss Statement |
| Trial Balance |
| General Ledger |
| Accounts Receivable |
| Accounts Receivable totals, confirmed overdue, and top customer balances |
| Accounts Payable |
| Bank Reconciliation Statement |
Report names and filters are allowlisted. Detailed reports have date-range limits and all report responses have row limits.
Search and voice queries
Directory tools search record IDs and display names. If the full phrase has no exact match, they make one bounded token search and compare normalized names, including common legal suffixes for companies and parties. Fallbacks preserve explicit filters and permissions. Partial or ambiguous names require confirmation. Empty search strings and the existing text-array list responses remain supported; structuredContent adds match status, candidates, and truncation metadata.
Invoice and order tools accept customer_query or supplier_query for spoken names. Exact customer and supplier filters must use confirmed record IDs. Do not combine a spoken query and its exact filter. search is document text, not an unrestricted filter language. SQL wildcard characters in text are escaped.
Use unpaid_only: true for submitted positive invoice balances, including partly paid and overdue invoices. Use overdue_as_of for due dates strictly before a date. Neither can be combined with status. Purchase orders support pending_receipt: true for submitted, open orders with less than 100 percent received. List tools expose bounded, allowlisted sorting; monetary invoice/order ranking uses base_grand_total with one company, or a native amount with an explicit currency filter.
Tool | Purpose |
| Resolve a spoken customer/supplier name and return its company-currency balance; use |
| Complete payable totals, credits, overdue balances and top parties |
| Net invoice sales by customer, item, item group or month, calculated before limiting displayed groups |
| Partial stock check against per-warehouse reorder settings, with explicit missing-setting and coverage warnings |
Date-range reports accept either the existing explicit from_date/to_date pair or period plus as_of_date (the user's current local calendar date). Presets are current_fiscal_year, last_month, and last_90_days. Fiscal years are resolved by NexWave for the specified company. Do not mix presets with explicit dates. Ledger and sales-summary ranges remain bounded to 366 days.
Tool execution shares a 50-second upstream budget for service-token and OAuth clients. Responses are bounded to 8 MiB. Upstream timeouts, invalid JSON, permission failures and oversized results return safe MCP errors; none mean that no records exist. Read-only upstream requests get at most one retry inside the same 50-second budget when the failure is retryable. Authentication routes, stored credentials and null-argument normalization are unchanged. No database migration is needed.
The stock-risk report covers existing item/warehouse stock rows only. It does not implement missing-Bin replenishment, warehouse-group reorder rules, demand forecasts or lead-time forecasts. A zero returned risk count is not evidence that all stock is safe.
The deployment-ready voice prompt and evaluation cases describe the intended client behavior. Deploy the MCP changes, refresh the client's tool catalogue, and then publish the updated voice configuration. Keep all existing tools selected.
The receivable summary adds totals.overdue when overdue_complete is true. It uses positive document balances due strictly before report_date, includes the overdue part of the first ageing bucket, and does not deduct unallocated credits. Missing due dates fall back to posting dates, as in the standard report. If dates or per-customer detail reconciliation are incomplete, the overdue field is omitted rather than reported as zero. Existing totals, ageing buckets and filters are unchanged, and no extra upstream report request is required. The voice prompt also supports sequential, customer-scoped summaries across balances, sales and relevant invoice/order tools, with explicit partial-result handling.
Party and payable summaries also expose overdue_complete and omit overdue amounts when positive documents lack valid dates, while preserving their other balance totals. The prompt requires this confirmation on all three summaries. It can be published safely before the MCP update, but will report overdue as unavailable until the new field is deployed.
Local development
Requirements:
Node.js 22 or newer
A Cloudflare account for deployment
A Frappe v15, ERPNext, or NexWave site
Install dependencies, create local storage, and start the Worker:
npm ci
cp .dev.vars.example .dev.vars
npm run db:local
npm run devOpen http://localhost:8787/admin.
For an OAuth site, create an OAuth Client on the Frappe site with these settings:
Field | Value |
App Name | NexWave MCP |
Scopes |
|
Redirect URIs |
|
Default Redirect URI |
|
Grant Type | Authorization Code |
Response Type | Code |
Skip Authorization | Off |
Copy the generated client ID and secret into the local setup page and select OAuth 2. A local Frappe bench can use http://localhost:8000 when the required site is the bench default.
For a non-interactive service connection, create an API key and secret for a dedicated NexWave user. In the setup page, select Fixed API token. The page shows the generated service token once after it verifies and saves the connection.
Cloudflare deployment
The Worker needs one KV namespace and one D1 database. Replace the account and resource IDs in wrangler.jsonc before deploying a fork to another Cloudflare account.
Set the Worker secrets. Do not store them in source control:
openssl rand -base64 32 | wrangler secret put ADMIN_TOKEN
openssl rand -base64 32 | wrangler secret put CONFIG_ENCRYPTION_KEYApply migrations and deploy:
npm run db:remote
npm run deployFor a Worker at https://nexwave-mcp.example.workers.dev, add this redirect URI to the Frappe OAuth Client:
https://nexwave-mcp.example.workers.dev/oauth/frappe/callbackRegister the site at /admin, then give users the /mcp endpoint.
GitHub Actions validates each push and pull request to main. It does not deploy automatically.
Security model
Site OAuth client secrets and fixed API-token credentials are encrypted with AES-256-GCM before D1 storage.
The OAuth Provider library encrypts upstream tokens and refresh properties in KV.
OAuth approval state expires after ten minutes and is bound to the same browser with an HTTP-only cookie.
Frappe sign-in uses PKCE S256 and the confidential client secret.
The admin API requires a separate bearer token.
The service MCP route accepts a generated gateway token and maps its stored hash to one registered fixed-token site.
A generated service token is returned only once. D1 stores its hash, not the token value.
Fixed Frappe API credentials are encrypted in D1 and are never returned to the MCP client.
Production site URLs must use HTTPS. Plain HTTP is accepted only for local development hosts.
Tools cannot call arbitrary Frappe methods, reports, DocTypes, fields, or URLs.
The public consent page does not enumerate registered site names or URLs.
Frappe remains the source of truth for user permissions.
The POC records successful and failed sign-in events. It does not yet include a full audit viewer, write tools, per-tool scopes, automatic deployment, or managed secret rotation.
Quality checks
Run the same validation used by CI:
npm run check
npm test
npx wrangler deploy --dry-runThe protected main branch requires a pull request, resolved review conversations, and a passing Validate check. Contributors need one approval; the current repository administrators can bypass the approval requirement.
See AGENTS.md for contributor and coding-agent rules.
This server cannot be deployed
Maintenance
Related MCP Connectors
Authenticated, user-scoped MCP connectors for 30+ business systems.
Connect MCP clients to 2,000+ AI models without managing provider API keys.
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
Native MCP access to CRM contacts, organizations, deals and tasks with user permissions.
1223
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables ERPNext management, file operations, read-only database access, and ERPNext API integration through a standardized MCP server.4MIT
- AlicenseNot gradedqualityDmaintenanceA comprehensive MCP server for ERPNext providing generic, doctype-agnostic access to any ERPNext document type with robust permission controls, audit logging, and enterprise-grade security.MIT
- AlicenseAqualityAmaintenanceConnects MCP clients to Frappe/ERPNext sites via REST API, enabling document CRUD, search, and remote method calls.7MIT
- AlicenseNot gradedqualityBmaintenanceEnables MCP-compatible clients to connect to any Frappe or Frappe HRMS site, exposing schema discovery, document CRUD and workflow operations, and HR helpers such as leave and attendance management through standardized tools.MIT