Frappe MCP Server
This MCP server enables seamless interaction with any Frappe/ERPNext site via its REST API, allowing you to manage documents and invoke server-side methods from MCP-compatible clients. It uses API key/secret for secure token-based authentication.
Check Connectivity (
frappe_ping): Verify the connection to the configured Frappe/ERPNext site.Retrieve Documents (
frappe_get_doc): Fetch a single document by specifying its DocType (e.g.,Sales Invoice,Customer) and name/ID.Search & List Documents (
frappe_search_docs): Search or list documents of any DocType with support for filters, field selection, pagination, and custom ordering.Create Documents (
frappe_create_doc): Create a new document of any DocType by providing the required field values.Update Documents (
frappe_update_doc): Modify an existing document by specifying its DocType, name/ID, and the fields to update.Delete Documents (
frappe_delete_doc): Permanently delete a document by its DocType and name/ID.Run Server-Side Methods (
frappe_run_method): Invoke any whitelisted Python method on the Frappe server using its dotted path (e.g.,frappe.auth.get_logged_user), with optional keyword arguments.
Enables management of ERPNext site data through the Frappe REST API, including document creation, retrieval, update, deletion, and remote method calls.
Allows interaction with a Frappe Framework site via REST API, providing CRUD operations on documents, full-text search, filtered document search, and invocation of server-side methods.
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., "@Frappe MCP ServerShow me the latest 10 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.
Frappe MCP Server
mcp-name: io.github.muthanii/frappe_mcpA Model Context Protocol (MCP) server for Frappe Framework. Connect Claude Desktop, VS Code Copilot, and other MCP clients to any Frappe/ERPNext site via its REST API.
Where to get it
Distribution | Reference | Page |
PyPI |
| |
Docker Hub |
| |
GitHub Container Registry |
| |
MCP Registry |
| |
Glama | — | |
Source | — |
Related MCP server: Frappe MCP
Features
Document CRUD — get, create, update, delete Frappe doctypes
Search — full-text and filtered document search
Remote method calls — invoke any server-side Python method
Authentication — API key + secret (token-based auth)
Docker-first — single
docker runcommand, no Python install needed
Quick start
With uvx (no install):
FRAPPE_URL=https://your-site.com \
FRAPPE_API_KEY=your-api-key \
FRAPPE_API_SECRET=your-api-secret \
uvx frappe-mcp-serverWith pip:
pip install frappe-mcp-server
frappe-mcp-serverWith Docker (Docker Hub):
docker run -i --rm \
-e FRAPPE_URL=https://your-site.com \
-e FRAPPE_API_KEY=your-api-key \
-e FRAPPE_API_SECRET=your-api-secret \
muthanii/frappe-mcpWith Docker (GHCR):
docker run -i --rm \
-e FRAPPE_URL=https://your-site.com \
-e FRAPPE_API_KEY=your-api-key \
-e FRAPPE_API_SECRET=your-api-secret \
ghcr.io/muthanii/frappe-mcpAvailable tools
Every tool ships MCP tool annotations and a declared outputSchema, so a client can tell read tools from destructive ones before calling them.
Tool | Description | Access | Destructive | Idempotent |
| Check connectivity and credentials | read-only | no | yes |
| Retrieve a single document by doctype + name | read-only | no | yes |
| Search/list documents with filters | read-only | no | yes |
| Create a new document | write | no | no |
| Update an existing document | write | yes | yes |
| Delete a document — irreversible | write | yes | no |
| Call a whitelisted server-side method | write | yes | no |
frappe_run_method is marked destructive because its effect is determined entirely by the method you name.
Configuration
Environment variable | Required | Description |
| Yes | Base URL of your Frappe site (e.g. |
| Yes | Frappe API key |
| Yes | Frappe API secret |
| No | Set to |
| No | Request timeout in seconds (default: |
MCP client config
Add this to your claude_desktop_config.json or Copilot config.
Via uvx:
{
"mcpServers": {
"frappe": {
"command": "uvx",
"args": ["frappe-mcp-server"],
"env": {
"FRAPPE_URL": "https://your-site.com",
"FRAPPE_API_KEY": "your-api-key",
"FRAPPE_API_SECRET": "your-api-secret"
}
}
}
}Via Docker:
{
"mcpServers": {
"frappe": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "FRAPPE_URL",
"-e", "FRAPPE_API_KEY",
"-e", "FRAPPE_API_SECRET",
"muthanii/frappe-mcp"
],
"env": {
"FRAPPE_URL": "https://your-site.com",
"FRAPPE_API_KEY": "your-api-key",
"FRAPPE_API_SECRET": "your-api-secret"
}
}
}
}Local development
pip install -e .
frappe-mcpLicense
MIT — see LICENSE.
Available Tools
7 toolsfrappe_create_docCreate a documentA
Create a new document of the given DocType from the supplied field values.
Use to insert a record that does not yet exist. Use frappe_update_doc instead to change a record that already exists — this tool never updates in place, so re-running it creates a second, separately named document.
Behavior: writes to the site and is NOT idempotent. The document is inserted as a draft (docstatus 0) and is not submitted, so submittable DocTypes such as Sales Invoice still need a submit step via frappe_run_method. Frappe assigns the name from the DocType's naming series, so the name is unknown until this call returns. Server-side validation, mandatory-field checks and DocType hooks all run on insert; a missing mandatory field or an invalid Link value raises a Frappe error and nothing is created. Requires create permission on the DocType.
Parameters: data keys are fieldnames (customer_name), not form labels ('Customer Name'); Link fields take the target document's name, not its title. Child tables are passed as a list of row objects under the child field's own fieldname. Omit name — it is assigned by the naming series and any value supplied is ignored unless the DocType prompts for naming.
Returns: {"data": {...}} — the created document, including the assigned name.
| Name | Required | Description | Default |
|---|---|---|---|
| data | Yes | Key-value pairs of document field values to set. Keys are fieldnames (e.g. 'customer_name', 'email_id', 'status', 'items'), not form labels; Link fields take the target document's `name`. Child-table fields take a list of row objects. Must include every field the target DocType marks mandatory, or the insert is rejected. | |
| doctype | Yes | The target DocType name for the new document (e.g. 'Customer', 'Sales Invoice', 'Item', 'ToDo', 'Contact', 'Address'). Case-sensitive and must be a DocType that exists on the target Frappe site. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | A Frappe document. Keys are DocType fieldnames; child tables appear as arrays of row objects. Always includes `name` (the primary key), `owner`, `creation`, `modified` and `docstatus` (0 = draft, 1 = submitted, 2 = cancelled). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well past the annotations: it explains that documents land as draft (docstatus 0) and need a separate submit via frappe_run_method, that the name is server-assigned by naming series and unknown until return, that validation/mandatory-field/hook checks run atomically so a failure creates nothing, and that create permission on the DocType is required. The 'not idempotent' claim is reinforced by annotation idempotentHint=false and adds the real-world consequence.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded and labelled (Behavior / Parameters / Returns), with each sentence carrying operational content. The trailing Returns line is largely redundant given a full output schema exists, and the restated non-idempotency is a minor duplication.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a write tool with nested object parameters, the description covers permissions, validation failure modes, naming behavior, draft state, and the follow-up submit path — everything an agent needs to invoke it correctly and interpret the result, even though the output schema already documents the return shape.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3; the description adds value by stressing fieldnames over form labels and Link fields taking `name` rather than title, and by contributing a detail absent from the schema — omit `name` because the naming series assigns it. Most child-table and mandatory-field guidance duplicates the schema, which keeps it off a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — 'Create a new document of the given DocType from the supplied field values' — and immediately distinguishes itself from the sibling frappe_update_doc. An agent can tell what this does and what it does not do without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('insert a record that does not yet exist'), the named alternative ('Use frappe_update_doc instead'), and the concrete consequence of misuse (re-running creates a second, separately named document). The submittable-DocType caveat further routes the agent to frappe_run_method when a submit step is needed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
frappe_delete_docDelete a document (irreversible)ADestructive
Permanently delete a document by its doctype and name.
Use only for genuinely disposable records such as ToDo or Note, or for a test document created moments earlier — the deletion is irreversible, with no undo and no recycle bin. Prefer frappe_update_doc to set a status such as 'Cancelled' whenever an audit trail matters, which covers most transactional records (Sales Invoice, Sales Order, Payment Entry, Journal Entry).
Behavior: writes to the site. Not idempotent from the caller's point of view — a second call for the same name raises a not-found error. Frappe refuses the delete while other documents link to this one, so a link-exists error means the dependants must be handled first. Deleting a parent removes its child-table rows with it. Submitted documents must be cancelled before they can be deleted. Requires delete permission on the DocType.
Parameters: name is the exact name primary key, not a title, and a wrong value deletes the wrong record with no warning — confirm with frappe_get_doc first when there is any doubt. One document per call: there is no bulk mode and no dry-run, and the deletion is committed as soon as the call is accepted.
Returns: {"message": "ok"} on success.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | The exact `name` primary key of the document to permanently remove (e.g. 'TODO-00001', 'NOTE-00001'). This operation cannot be undone, so verify the value identifies the intended record before calling. | |
| doctype | Yes | The DocType of the document to delete (e.g. 'ToDo', 'Note', 'Contact', 'Address'). WARNING: deleting a transactional or parent document removes its child rows and may be blocked by, or orphan, linked records. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | No | Present instead of `message` when the site answers 202 Accepted; the value is 'accepted'. |
| message | No | Confirmation string returned by Frappe, typically 'ok'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and idempotentHint=false, but the description adds substantial context: no undo or recycle bin, link-exists errors from dependants, cascading child-row removal, the cancel-before-delete rule for submitted documents, and the delete-permission requirement. It also notes the non-idempotent caller experience (second call raises not-found), which is more precise than the annotation alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the cardinal warning, then cleanly segmented into usage, Behavior, Parameters, and Returns. Slightly long and the Parameters block partially restates schema text, but nearly every sentence carries operational weight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers prerequisites, failure modes, cascade behavior, permission requirements, and batching limits for a two-parameter destructive tool; the return value is briefly noted even though an output schema exists. An agent has everything needed to call this safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline would be 3, but the description adds real semantics: `name` is the primary key rather than a title, a wrong value silently deletes the wrong record, and frappe_get_doc should be used to confirm first. It also reinforces the one-document-per-call constraint, though the field enumerations themselves duplicate the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Permanently delete a document by its doctype and name') plus the irreversible scope. It distinguishes itself from siblings by naming frappe_update_doc and frappe_get_doc with the conditions that select them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use (ToDo, Note, freshly created test records) and when-not-to-use with a concrete alternative ('Prefer frappe_update_doc to set a status such as Cancelled'), listing the transaction types where that applies. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
frappe_get_docGet one documentARead-onlyIdempotent
Retrieve one complete document (a single DocType record) by its exact doctype and document name.
Use when the document's name is already known, such as 'SINV-00001'. Use frappe_search_docs instead to find documents by field value, to fetch many records, or to return only a few fields; use frappe_run_method for values that are computed rather than stored on the document.
Behavior: read-only and idempotent, with no side effects. Returns the document in whatever state it is stored — draft, submitted or cancelled — without filtering. Results respect the API user's role permissions: a document that exists but is not permitted raises a permission error, while an unknown doctype or name raises a not-found error.
Parameters: doctype and name are both case-sensitive and must match Frappe exactly. name is the primary key from the list view, not a title — for DocTypes using a naming series it looks like 'SINV-00001', and only for DocTypes named by field does it equal a human-readable value such as a customer name.
Returns: {"data": {...}} containing every stored field, with child tables nested as arrays of row objects.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | The unique document name or ID to retrieve (e.g. 'SINV-00001', 'CUST-00001', 'ITEM-00001', 'Administrator'). This is the `name` primary key shown in the Frappe list view or document form, not a title or description field. | |
| doctype | Yes | The target DocType name (e.g. 'Sales Invoice', 'Customer', 'Item', 'Sales Order', 'Purchase Order', 'User'). Case-sensitive and must match the DocType exactly as defined in Frappe. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | A Frappe document. Keys are DocType fieldnames; child tables appear as arrays of row objects. Always includes `name` (the primary key), `owner`, `creation`, `modified` and `docstatus` (0 = draft, 1 = submitted, 2 = cancelled). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, yet the description adds real context beyond them: unfiltered state return (draft/submitted/cancelled), role-permission enforcement producing a permission error versus a not-found error, and the {"data": {...}} return shape with nested child tables. That error-semantics disclosure is exactly the kind of value structured fields cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and routing in the first two sentences, then cleanly segmented into Behavior/Parameters/Returns. A few sentences repeat schema content ('case-sensitive and must match Frappe exactly', the Returns block despite an output schema), so it is slightly longer than strictly necessary but never padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read tool this covers everything an agent needs: selection criteria, sibling routing, error modes, permission behavior, and return shape. An output schema exists, so the description need not explain return values, yet doing so costs nothing and adds no gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters are already documented, so the baseline is 3. The description adds genuine meaning on top: that `name` is the list-view primary key rather than a title, and that it only looks human-readable for DocTypes named by field — a distinction the schema does not make. Minor redundancy on case sensitivity keeps it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Retrieve one complete document ... by its exact doctype and document name') and explicitly scopes it to a single DocType record. It also names the sibling it is not (frappe_search_docs) and the computed-value alternative (frappe_run_method), so an agent can distinguish it without reading schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit positive trigger ('when the document's name is already known, such as SINV-00001') plus three negative conditions that route to frappe_search_docs (find by field value, fetch many, return few fields) and one that routes to frappe_run_method. This is textbook when/when-not/alternatives coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
frappe_pingCheck Frappe connectionARead-onlyIdempotent
Check connectivity and credentials against the configured Frappe/ERPNext site. Use first when another tool fails, to tell a network or credential problem apart from a bad doctype or document name. This is a diagnostic, not a data tool — it never reads or writes documents. Behavior: read-only and idempotent, with no side effects. A pong proves the site is reachable and that FRAPPE_API_KEY/FRAPPE_API_SECRET authenticate; it does not prove the API user has permission for any particular DocType. Failure surfaces as a connection error (site unreachable, wrong URL, TLS problem) or an auth error (bad key or secret). Parameters: none by design — the target site and credentials come from the server's own environment, never from the caller, so this tool cannot be aimed at a different host. Returns: {"message": "pong"}.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| message | Yes | The literal string 'pong' when the site is reachable and the credentials authenticate. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, but the description adds genuinely new behavioral context: what a pong does and does not prove (reachability/auth yes, per-DocType permission no), and the two failure classes (connection error vs auth error). That is substantive disclosure beyond the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is well front-loaded with purpose and usage before behavior details, and the labeled sections aid scanning. The only waste is the trailing 'Returns: {"message": "pong"},' which duplicates the output schema and could be dropped.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter diagnostic with rich annotations and an output schema, the description covers everything an agent needs: purpose, routing guidance, what success proves, failure modes, and why no arguments exist. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With zero parameters the baseline is 4, but the description earns the extra point by explaining why there are none — the site and credentials come from the server's environment, 'never from the caller' — and the security consequence that the tool 'cannot be aimed at a different host.' That is meaningful parameter-level context not present in the empty schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Check connectivity and credentials against the configured Frappe/ERPNext site.' It explicitly positions itself against the sibling CRUD tools by calling itself 'a diagnostic, not a data tool,' so an agent can distinguish it without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states exactly when to reach for it — 'Use first when another tool fails' — and what decision it supports: separating network/credential problems from bad doctype or document names. That is explicit when-to-use guidance tied to the sibling tools' failure modes.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
frappe_run_methodCall a whitelisted server methodADestructive
Call a whitelisted server-side Python method on the Frappe site.
Use as the escape hatch for anything the CRUD tools cannot express — submitting or cancelling documents, running reports, computing stock balances, or invoking a custom app's API. Use frappe_get_doc, frappe_search_docs, frappe_create_doc or frappe_update_doc for ordinary record work: they validate their arguments and describe their effects, whereas this tool cannot.
Behavior: the effect is determined entirely by the method named and may read, write, delete or enqueue background jobs, so treat every call as potentially destructive and non-idempotent unless the method is known to be safe. The target must be decorated with @frappe.whitelist(); anything else raises a permission error, as does a method whose own permission checks reject the API user. Calls run synchronously and are subject to the server's request timeout (FRAPPE_TIMEOUT, 30s by default).
Parameters: method is the full dotted import path — module path plus function name — not a DocType method name or a REST path. kwargs maps onto that method's named arguments exactly; names come from its signature, and an unexpected or missing key raises a Frappe error rather than being ignored. Values must be JSON-serialisable, with dates passed as 'YYYY-MM-DD' strings. Omitting kwargs calls the method with no arguments.
Returns: {"message": ...} — the method's return value, whose type depends entirely on the method called.
| Name | Required | Description | Default |
|---|---|---|---|
| kwargs | No | Optional keyword arguments passed to the remote method. Keys must match that method's parameter names exactly — they depend entirely on the method being called, and an unknown key raises an error rather than being ignored. Values must be JSON-serialisable; pass dates as 'YYYY-MM-DD' strings. Example for stock balance: {'item_code': 'ITEM-001', 'warehouse': 'Stores - W'}. Defaults to no arguments when omitted. | |
| method | Yes | Full dotted Python path to the whitelisted method — module path plus function name. Examples: 'frappe.auth.get_logged_user', 'frappe.client.submit', 'erpnext.stock.utils.get_stock_balance', or any custom whitelisted method from an installed Frappe app. Not a REST path and not a DocType method name. |
Output Schema
| Name | Required | Description |
|---|---|---|
| message | No | The value returned by the whitelisted method. The type is whatever that method returns — object, array, string, number, boolean or null. Absent when the method returns nothing. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true, openWorldHint=true and idempotentHint=false, and the description reinforces rather than merely repeats this: the effect is determined entirely by the named method, it may read/write/delete/enqueue, and calls should be treated as potentially destructive and non-idempotent unless known safe. It adds auth requirements (@frappe.whitelist() decorator, method-level permission checks) and the synchronous 30s FRAPPE_TIMEOUT constraint — exactly the context annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded and clearly segmented with 'Behavior:', 'Parameters:', 'Returns:' labels, so an agent can scan for the needed section. The Parameters section largely duplicates the schema text, which is the one place sentences do not fully earn their place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a high-variance, open-world mutation tool this covers what an agent needs: generic dispatch semantics, permission preconditions, timeout behavior, argument-mapping failure modes, and the envelope shape. An output schema exists, yet the one-line 'Returns: {"message": ...}' is a useful, non-wasteful addition.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both parameter descriptions already state the dotted-path requirement, the exact-signature match for kwargs keys, the error on unknown keys, and the YYYY-MM-DD date convention. The description's 'Parameters:' paragraph restates nearly all of this, only adding the sharper negative framing 'not a DocType method name or a REST path'. Baseline 3 applies when the schema carries the load and the description adds little.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Call a whitelisted server-side Python method on the Frappe site') and immediately positions itself relative to the CRUD siblings, naming all four by name. An agent can tell from the first two sentences exactly what this tool does and when it is the only option.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly defines the tool as the 'escape hatch' and gives concrete use cases (submit/cancel documents, run reports, compute stock balances, custom app APIs). It also names the alternatives and the condition selecting them: 'Use frappe_get_doc, frappe_search_docs, frappe_create_doc or frappe_update_doc for ordinary record work' because they validate arguments and describe effects.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
frappe_search_docsSearch or list documentsARead-onlyIdempotent
Search or list documents of one DocType, with optional filters, field selection, ordering and pagination.
Use to find documents by field value or to browse a DocType. Use frappe_get_doc instead when the exact document name is known and every field is wanted; use frappe_run_method for aggregates, totals or report output, which this tool cannot compute.
Behavior: read-only and idempotent, with no side effects. Only documents the API user's role permissions allow are returned, so a short result may mean restricted access rather than no matches. An unknown doctype raises a not-found error, and an unknown fieldname in filters, fields or order_by raises a Frappe error rather than being silently ignored.
Parameters: filters entries are ANDed together — there is no OR — and each is a [field, operator, value] triple; in/not in take a list as the value and between takes a two-element list. fields should name only what is needed, since omitting it returns every field of every row and can be very large. limit defaults to 20 and Frappe caps it at 200; there is no offset parameter, so page by ordering on creation and filtering past the last value already seen.
Returns: {"data": [...]} — an array of documents, empty when nothing matches.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results to return (default: 20, maximum: 200). Frappe caps results at 200 per request; there is no offset parameter, so paginate by narrowing `filters` rather than by increasing this. | |
| fields | No | Optional list of fieldnames to return. Limits the response to only the specified fields, which keeps large result sets manageable. Example: ['name', 'status', 'grand_total', 'posting_date']. When omitted, all fields are returned. Child-table fields are not expanded here — fetch the full document with frappe_get_doc for those. | |
| doctype | Yes | The DocType to search within (e.g. 'Sales Invoice', 'Customer', 'Item', 'ToDo', 'Contact'). Case-sensitive; exactly one DocType per call, as this tool cannot search across DocTypes. | |
| filters | No | Optional list of filter conditions, all ANDed together. Each filter is a triple [field, operator, value]. Supported operators: =, !=, >, <, >=, <=, like, not like, in, not in, between. Example: [['status','=','Open'], ['grand_total','>','1000']]. Dates are compared as 'YYYY-MM-DD' strings. | |
| order_by | No | Field to sort by with direction, formatted as 'fieldname direction'. Examples: 'creation desc' (newest first), 'modified asc' (oldest updated first), 'name asc' (alphabetical). The direction is required. Defaults to 'creation desc' when omitted. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | Matching documents, in the requested order. Empty when nothing matched. Each row carries only the requested `fields`, or every field when `fields` was omitted. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, yet the description adds material context: role-permission filtering means a short result can signal restricted access rather than no matches, and unknown doctype/fieldnames raise errors instead of being ignored. These are non-obvious failure modes an agent could not infer from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded purpose sentence followed by clearly labeled Behavior/Parameters/Returns blocks is easy to scan. The explicit Returns line is redundant given a real output schema exists, and the parameter paragraph partially duplicates the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers routing, permission semantics, error behavior, filter composition limits, pagination strategy, and result shape — everything needed to call a 5-parameter search tool correctly. Nothing material is left to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema coverage the baseline is 3; the description adds a concrete pagination recipe (order on `creation`, filter past the last seen value) that the schema's limit text does not give. However, much of the filter/limit/fields text restates the schema descriptions nearly verbatim, so the net addition is modest.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (search or list) and resource (documents of one DocType), plus the scope modifiers (filters, field selection, ordering, pagination). It explicitly distinguishes itself from frappe_get_doc (exact name known) and frappe_run_method (aggregates/reports), so an agent can route without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives both when-to-use (find by field value, browse a DocType) and when-not-to-use with named alternatives for two distinct cases. The exclusions are concrete conditions, not vague hints.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
frappe_update_docUpdate a documentADestructiveIdempotent
Update selected fields of an existing document, identified by its doctype and name.
Use for partial edits: only the keys present in data change and every other field is left alone. Use frappe_create_doc for records that do not exist yet, and prefer this tool over frappe_delete_doc when a document should be voided rather than removed — setting a status such as 'Cancelled' preserves the audit trail.
Behavior: writes to the site, overwriting the previous values of the fields supplied; those old values are not recoverable through this API. Idempotent — repeating an identical call leaves the document in the same state. Validation and DocType hooks run on save, so an update is accepted or rejected as a whole, never partially applied. Documents already submitted (docstatus 1) accept changes only to fields marked 'Allow on Submit'; other edits fail. Requires write permission on the DocType.
Parameters: data keys are fieldnames, not form labels. Supplying a child-table field replaces the entire table rather than merging rows, so to amend one row read the document with frappe_get_doc first and send back the complete row list. Fields omitted from data are untouched — there is no way to clear a field except by setting it explicitly to null or an empty string.
Returns: {"data": {...}} — the whole document as it stands after the update.
| Name | Required | Description | Default |
|---|---|---|---|
| data | Yes | Fieldnames and their new values. Only the fields included here are modified; others retain their current values. Passing a child-table field replaces every row in that table. Example: {'status': 'Cancelled', 'remarks': 'Cancelled per customer request'}. | |
| name | Yes | The `name` primary key of the existing document to modify (e.g. 'SINV-00001', 'CUST-00001', 'ITEM-00001'). The document must already exist; this tool does not create one. | |
| doctype | Yes | The DocType of the document to update (e.g. 'Sales Invoice', 'Customer', 'Item', 'Sales Order'). Case-sensitive and must match the doctype of the existing document. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | A Frappe document. Keys are DocType fieldnames; child tables appear as arrays of row objects. Always includes `name` (the primary key), `owner`, `creation`, `modified` and `docstatus` (0 = draft, 1 = submitted, 2 = cancelled). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only flag write/destructive/idempotent/openWorld; the description adds far more: overwritten values are unrecoverable through this API, DocType validation hooks run and the save is all-or-nothing, submitted documents (docstatus 1) reject edits to fields not marked 'Allow on Submit', and write permission on the DocType is required. This is exactly the behavioural context annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and routing, then cleanly sectioned into Behavior, Parameters and Returns. It is on the long side and the Returns line partly duplicates the output schema, but nearly every sentence carries actionable information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive nested-object write tool, the definition covers atomicity, submitted-document constraints, permissions, child-table replacement and field clearing. Nothing an agent needs in order to call it safely is missing, even though the redundant Returns clause could be dropped since an output schema exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so baseline is 3, but the description adds genuinely new semantics: `data` keys are fieldnames not form labels, child-table fields replace the whole table (with a read-then-resend-rows remedy), and fields can only be cleared by explicitly passing null or an empty string. These go beyond the schema text rather than restating it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource+scope: 'Update selected fields of an existing document, identified by its doctype and name.' It immediately distinguishes partial edit semantics from creation and deletion, so an agent can route correctly without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names alternatives and the conditions that select them: frappe_create_doc for records that don't exist, and this tool over frappe_delete_doc when a document should be voided while preserving the audit trail. Both the when-to-use and the when-to-prefer-a-different-tool cases are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v1.2.0- Changed
frappe_create_doc6 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / data / descriptionPrevious value: -"Key-value pairs of document field values to set. Keys are field names (e.g. 'customer_name', 'email_id', 'status', 'items'), values are the field values. For child table fields, provide a list of dicts. Must include at least all mandatory fields required by the target DocType."New value: +"Key-value pairs of document field values to set. Keys are fieldnames (e.g. 'customer_name', 'email_id', 'status', 'items'), not form labels; Link fields take the target document's `name`. Child-table fields take a list of row objects. Must include every field the target DocType marks mandatory, or the insert is rejected." - added
Input schema / properties / data / minPropertiesAdded value: +1 - changed
Input schema / properties / doctype / descriptionPrevious value: -"The target DocType name for the new document (e.g. 'Customer', 'Sales Invoice', 'Item', 'ToDo', 'Contact', 'Address'). Must be a valid DocType that exists on the target Frappe site."New value: +"The target DocType name for the new document (e.g. 'Customer', 'Sales Invoice', 'Item', 'ToDo', 'Contact', 'Address'). Case-sensitive and must be a DocType that exists on the target Frappe site." - added
Input schema / properties / doctype / minLengthAdded value: +1 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "data": { + "additionalProperties": true, + "description": "A Frappe document. Keys are DocType fieldnames; child tables appear as arrays of row objects. Always includes `name` (the primary key), `owner`, `creation`, `modified` and `docstatus` (0 = draft, 1 = submitted, 2 = cancelled).", + "type": "object" + } + }, + "required": [ + "data" + ], + "type": "object" +}
- Changed
frappe_delete_doc6 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / doctype / descriptionPrevious value: -"The DocType of the document to delete (e.g. 'ToDo', 'Note', 'Contact', 'Address'). WARNING: Deleting transactional or parent documents may affect or orphan linked child records."New value: +"The DocType of the document to delete (e.g. 'ToDo', 'Note', 'Contact', 'Address'). WARNING: deleting a transactional or parent document removes its child rows and may be blocked by, or orphan, linked records." - added
Input schema / properties / doctype / minLengthAdded value: +1 - changed
Input schema / properties / name / descriptionPrevious value: -"The unique document name or ID to permanently remove from the system (e.g. 'TODO-00001', 'NOTE-00001'). This operation cannot be undone."New value: +"The exact `name` primary key of the document to permanently remove (e.g. 'TODO-00001', 'NOTE-00001'). This operation cannot be undone, so verify the value identifies the intended record before calling." - added
Input schema / properties / name / minLengthAdded value: +1 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "data": { + "description": "Present instead of `message` when the site answers 202 Accepted; the value is 'accepted'.", + "type": "string" + }, + "message": { + "description": "Confirmation string returned by Frappe, typically 'ok'.", + "type": "string" + } + }, + "type": "object" +}
- Changed
frappe_get_doc5 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / doctype / minLengthAdded value: +1 - changed
Input schema / properties / name / descriptionPrevious value: -"The unique document name or ID to retrieve (e.g. 'SINV-00001', 'CUST-00001', 'ITEM-00001', 'Administrator'). This is typically the `name` field shown in the Frappe list view or document form."New value: +"The unique document name or ID to retrieve (e.g. 'SINV-00001', 'CUST-00001', 'ITEM-00001', 'Administrator'). This is the `name` primary key shown in the Frappe list view or document form, not a title or description field." - added
Input schema / properties / name / minLengthAdded value: +1 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "data": { + "additionalProperties": true, + "description": "A Frappe document. Keys are DocType fieldnames; child tables appear as arrays of row objects. Always includes `name` (the primary key), `owner`, `creation`, `modified` and `docstatus` (0 = draft, 1 = submitted, 2 = cancelled).", + "type": "object" + } + }, + "required": [ + "data" + ], + "type": "object" +}
- Changed
frappe_ping2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "message": { + "description": "The literal string 'pong' when the site is reachable and the credentials authenticate.", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" +}
- Changed
frappe_run_method6 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / kwargs / descriptionPrevious value: -"Optional dictionary of keyword arguments to pass to the remote method. The keys and expected values depend entirely on the specific method being called. Example for stock balance: {'item_code': 'ITEM-001', 'warehouse': 'Stores - W'}. Defaults to an empty dict when omitted."New value: +"Optional keyword arguments passed to the remote method. Keys must match that method's parameter names exactly — they depend entirely on the method being called, and an unknown key raises an error rather than being ignored. Values must be JSON-serialisable; pass dates as 'YYYY-MM-DD' strings. Example for stock balance: {'item_code': 'ITEM-001', 'warehouse': 'Stores - W'}. Defaults to no arguments when omitted." - changed
Input schema / properties / method / descriptionPrevious value: -"Dotted Python path to the whitelisted method to call. Examples: 'frappe.auth.get_logged_user', 'frappe.utils.get_stock_balance', 'erpnext.stock.utils.get_stock_balance', or any custom whitelisted method from your Frappe app."New value: +"Full dotted Python path to the whitelisted method — module path plus function name. Examples: 'frappe.auth.get_logged_user', 'frappe.client.submit', 'erpnext.stock.utils.get_stock_balance', or any custom whitelisted method from an installed Frappe app. Not a REST path and not a DocType method name." - changed
Input schema / properties / method / examplesPrevious value: -[ - "frappe.auth.get_logged_user", - "frappe.utils.get_stock_balance", - "erpnext.stock.utils.get_stock_balance" -]New value: +[ + "frappe.auth.get_logged_user", + "frappe.client.submit", + "erpnext.stock.utils.get_stock_balance" +] - added
Input schema / properties / method / minLengthAdded value: +1 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "message": { + "description": "The value returned by the whitelisted method. The type is whatever that method returns — object, array, string, number, boolean or null. Absent when the method returns nothing." + } + }, + "type": "object" +}
- Changed
frappe_search_docs9 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / doctype / descriptionPrevious value: -"The DocType to search within (e.g. 'Sales Invoice', 'Customer', 'Item', 'ToDo', 'Contact'). This parameter is required."New value: +"The DocType to search within (e.g. 'Sales Invoice', 'Customer', 'Item', 'ToDo', 'Contact'). Case-sensitive; exactly one DocType per call, as this tool cannot search across DocTypes." - added
Input schema / properties / doctype / minLengthAdded value: +1 - changed
Input schema / properties / fields / descriptionPrevious value: -"Optional list of field names to return. Limits the response to only the specified fields for efficiency. Example: ['name', 'status', 'grand_total', 'posting_date']. When omitted, all fields are returned."New value: +"Optional list of fieldnames to return. Limits the response to only the specified fields, which keeps large result sets manageable. Example: ['name', 'status', 'grand_total', 'posting_date']. When omitted, all fields are returned. Child-table fields are not expanded here — fetch the full document with frappe_get_doc for those." - changed
Input schema / properties / filters / descriptionPrevious value: -"Optional list of filter conditions. Each filter is a triple [field, operator, value]. Supported operators: =, !=, >, <, >=, <=, like, not like, in, not in, between. Example: [['status','=','Open'], ['grand_total','>','1000']]."New value: +"Optional list of filter conditions, all ANDed together. Each filter is a triple [field, operator, value]. Supported operators: =, !=, >, <, >=, <=, like, not like, in, not in, between. Example: [['status','=','Open'], ['grand_total','>','1000']]. Dates are compared as 'YYYY-MM-DD' strings." - changed
Input schema / properties / filters / items / prefixItemsPrevious value: -[ - { - "description": "Field name to filter on", - "type": "string" - }, - { - "description": "Operator: =, !=, >, <, >=, <=, like, not like, in, not in, between", - "type": "string" - }, - { - "description": "Value to compare against", - "type": "string" - } -]New value: +[ + { + "description": "Fieldname to filter on — the DocType's fieldname (e.g. 'grand_total'), not its form label.", + "type": "string" + }, + { + "description": "Operator: =, !=, >, <, >=, <=, like, not like, in, not in, between", + "type": "string" + }, + { + "description": "Value to compare against. Use % as the wildcard with 'like'; pass a list for 'in'/'not in' and a two-element list for 'between'.", + "type": "string" + } +] - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results to return (default: 20, maximum: 200). Use for pagination. Frappe caps results at 200 per request."New value: +"Maximum number of results to return (default: 20, maximum: 200). Frappe caps results at 200 per request; there is no offset parameter, so paginate by narrowing `filters` rather than by increasing this." - changed
Input schema / properties / order_by / descriptionPrevious value: -"Field to sort by with direction. Format: 'fieldname direction'. Examples: 'creation desc' (newest first), 'modified asc' (oldest updated first), 'name asc' (alphabetical). Default ordering is by creation date descending."New value: +"Field to sort by with direction, formatted as 'fieldname direction'. Examples: 'creation desc' (newest first), 'modified asc' (oldest updated first), 'name asc' (alphabetical). The direction is required. Defaults to 'creation desc' when omitted." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "data": { + "description": "Matching documents, in the requested order. Empty when nothing matched. Each row carries only the requested `fields`, or every field when `fields` was omitted.", + "items": { + "additionalProperties": true, + "description": "A Frappe document. Keys are DocType fieldnames; child tables appear as arrays of row objects. Always includes `name` (the primary key), `owner`, `creation`, `modified` and `docstatus` (0 = draft, 1 = submitted, 2 = cancelled).", + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "data" + ], + "type": "object" +}
- Changed
frappe_update_doc8 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / data / descriptionPrevious value: -"Dictionary of field names and their new values. Only the fields included here are modified; other fields retain their current values. Example: {'status': 'Cancelled', 'remarks': 'Cancelled per customer request'}."New value: +"Fieldnames and their new values. Only the fields included here are modified; others retain their current values. Passing a child-table field replaces every row in that table. Example: {'status': 'Cancelled', 'remarks': 'Cancelled per customer request'}." - added
Input schema / properties / data / minPropertiesAdded value: +1 - changed
Input schema / properties / doctype / descriptionPrevious value: -"The DocType of the document to update (e.g. 'Sales Invoice', 'Customer', 'Item', 'Sales Order'). Must match the doctype of the existing document."New value: +"The DocType of the document to update (e.g. 'Sales Invoice', 'Customer', 'Item', 'Sales Order'). Case-sensitive and must match the doctype of the existing document." - added
Input schema / properties / doctype / minLengthAdded value: +1 - changed
Input schema / properties / name / descriptionPrevious value: -"The unique document name or ID of the existing document to modify (e.g. 'SINV-00001', 'CUST-00001', 'ITEM-00001')."New value: +"The `name` primary key of the existing document to modify (e.g. 'SINV-00001', 'CUST-00001', 'ITEM-00001'). The document must already exist; this tool does not create one." - added
Input schema / properties / name / minLengthAdded value: +1 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "data": { + "additionalProperties": true, + "description": "A Frappe document. Keys are DocType fieldnames; child tables appear as arrays of row objects. Always includes `name` (the primary key), `owner`, `creation`, `modified` and `docstatus` (0 = draft, 1 = submitted, 2 = cancelled).", + "type": "object" + } + }, + "required": [ + "data" + ], + "type": "object" +}
6 tool updates
v1.1.0- Changed
frappe_create_doc4 fields changed- changed
Input schema / properties / data / descriptionPrevious value: -"Key-value pairs of fields and their values."New value: +"Key-value pairs of document field values to set. Keys are field names (e.g. 'customer_name', 'email_id', 'status', 'items'), values are the field values. For child table fields, provide a list of dicts. Must include at least all mandatory fields required by the target DocType." - added
Input schema / properties / data / examplesAdded value: +[ + { + "customer_name": "Acme Corp", + "customer_type": "Company", + "email_id": "billing@acme.com" + }, + { + "priority": "Medium", + "status": "Open", + "subject": "Review contract" + } +] - changed
Input schema / properties / doctype / descriptionPrevious value: -"The DocType to create."New value: +"The target DocType name for the new document (e.g. 'Customer', 'Sales Invoice', 'Item', 'ToDo', 'Contact', 'Address'). Must be a valid DocType that exists on the target Frappe site." - added
Input schema / properties / doctype / examplesAdded value: +[ + "Customer", + "Sales Invoice", + "ToDo", + "Contact" +]
- Changed
frappe_delete_doc4 fields changed- changed
Input schema / properties / doctype / descriptionPrevious value: -"The DocType of the document to delete."New value: +"The DocType of the document to delete (e.g. 'ToDo', 'Note', 'Contact', 'Address'). WARNING: Deleting transactional or parent documents may affect or orphan linked child records." - added
Input schema / properties / doctype / examplesAdded value: +[ + "ToDo", + "Note", + "Contact" +] - changed
Input schema / properties / name / descriptionPrevious value: -"The document name/ID to delete."New value: +"The unique document name or ID to permanently remove from the system (e.g. 'TODO-00001', 'NOTE-00001'). This operation cannot be undone." - added
Input schema / properties / name / examplesAdded value: +[ + "TODO-00001", + "NOTE-00001" +]
- Changed
frappe_get_doc4 fields changed- changed
Input schema / properties / doctype / descriptionPrevious value: -"The DocType (e.g. 'Sales Invoice', 'Customer', 'Item')."New value: +"The target DocType name (e.g. 'Sales Invoice', 'Customer', 'Item', 'Sales Order', 'Purchase Order', 'User'). Case-sensitive and must match the DocType exactly as defined in Frappe." - added
Input schema / properties / doctype / examplesAdded value: +[ + "Sales Invoice", + "Customer", + "Item", + "User" +] - changed
Input schema / properties / name / descriptionPrevious value: -"The document name/ID (e.g. 'SINV-00001', 'CUST-001')."New value: +"The unique document name or ID to retrieve (e.g. 'SINV-00001', 'CUST-00001', 'ITEM-00001', 'Administrator'). This is typically the `name` field shown in the Frappe list view or document form." - added
Input schema / properties / name / examplesAdded value: +[ + "SINV-00001", + "CUST-00001", + "Administrator" +]
- Changed
frappe_run_method4 fields changed- changed
Input schema / properties / kwargs / descriptionPrevious value: -"Keyword arguments to pass to the method."New value: +"Optional dictionary of keyword arguments to pass to the remote method. The keys and expected values depend entirely on the specific method being called. Example for stock balance: {'item_code': 'ITEM-001', 'warehouse': 'Stores - W'}. Defaults to an empty dict when omitted." - added
Input schema / properties / kwargs / examplesAdded value: +[ + { + "item_code": "ITEM-001", + "warehouse": "Stores - W" + }, + { + "from_date": "2025-01-01", + "to_date": "2025-12-31" + } +] - changed
Input schema / properties / method / descriptionPrevious value: -"Dotted path to the method (e.g. 'frappe.auth.get_logged_user' or 'erpnext.stock.utils.get_stock_balance')."New value: +"Dotted Python path to the whitelisted method to call. Examples: 'frappe.auth.get_logged_user', 'frappe.utils.get_stock_balance', 'erpnext.stock.utils.get_stock_balance', or any custom whitelisted method from your Frappe app." - added
Input schema / properties / method / examplesAdded value: +[ + "frappe.auth.get_logged_user", + "frappe.utils.get_stock_balance", + "erpnext.stock.utils.get_stock_balance" +]
- Changed
frappe_search_docs13 fields changed- changed
Input schema / properties / doctype / descriptionPrevious value: -"The DocType to search."New value: +"The DocType to search within (e.g. 'Sales Invoice', 'Customer', 'Item', 'ToDo', 'Contact'). This parameter is required." - added
Input schema / properties / doctype / examplesAdded value: +[ + "Sales Invoice", + "Customer", + "Item" +] - changed
Input schema / properties / fields / descriptionPrevious value: -"Optional list of field names to return (e.g. ['name', 'status', 'grand_total'])."New value: +"Optional list of field names to return. Limits the response to only the specified fields for efficiency. Example: ['name', 'status', 'grand_total', 'posting_date']. When omitted, all fields are returned." - added
Input schema / properties / fields / examplesAdded value: +[ + [ + "name", + "status", + "grand_total" + ] +] - changed
Input schema / properties / filters / descriptionPrevious value: -"Optional list of filters, each as [field, operator, value], e.g. [['status','=','Open']]."New value: +"Optional list of filter conditions. Each filter is a triple [field, operator, value]. Supported operators: =, !=, >, <, >=, <=, like, not like, in, not in, between. Example: [['status','=','Open'], ['grand_total','>','1000']]." - added
Input schema / properties / filters / examplesAdded value: +[ + [ + [ + "status", + "=", + "Open" + ] + ], + [ + [ + "grand_total", + ">", + "1000" + ], + [ + "status", + "!=", + "Cancelled" + ] + ] +] - added
Input schema / properties / filters / itemsAdded value: +{ + "prefixItems": [ + { + "description": "Field name to filter on", + "type": "string" + }, + { + "description": "Operator: =, !=, >, <, >=, <=, like, not like, in, not in, between", + "type": "string" + }, + { + "description": "Value to compare against", + "type": "string" + } + ], + "type": "array" +} - added
Input schema / properties / limit / defaultAdded value: +20 - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (default 20)."New value: +"Maximum number of results to return (default: 20, maximum: 200). Use for pagination. Frappe caps results at 200 per request." - added
Input schema / properties / limit / maximumAdded value: +200 - added
Input schema / properties / limit / minimumAdded value: +1 - changed
Input schema / properties / order_by / descriptionPrevious value: -"Field to sort by (e.g. 'creation desc')."New value: +"Field to sort by with direction. Format: 'fieldname direction'. Examples: 'creation desc' (newest first), 'modified asc' (oldest updated first), 'name asc' (alphabetical). Default ordering is by creation date descending." - added
Input schema / properties / order_by / examplesAdded value: +[ + "creation desc", + "modified asc", + "name asc" +]
- Changed
frappe_update_doc6 fields changed- changed
Input schema / properties / data / descriptionPrevious value: -"Key-value pairs of fields to update."New value: +"Dictionary of field names and their new values. Only the fields included here are modified; other fields retain their current values. Example: {'status': 'Cancelled', 'remarks': 'Cancelled per customer request'}." - added
Input schema / properties / data / examplesAdded value: +[ + { + "remarks": "Cancelled per customer request", + "status": "Cancelled" + }, + { + "email_id": "newemail@example.com", + "mobile_no": "+1234567890" + } +] - changed
Input schema / properties / doctype / descriptionPrevious value: -"The DocType of the document."New value: +"The DocType of the document to update (e.g. 'Sales Invoice', 'Customer', 'Item', 'Sales Order'). Must match the doctype of the existing document." - added
Input schema / properties / doctype / examplesAdded value: +[ + "Sales Invoice", + "Customer", + "Item" +] - changed
Input schema / properties / name / descriptionPrevious value: -"The document name/ID."New value: +"The unique document name or ID of the existing document to modify (e.g. 'SINV-00001', 'CUST-00001', 'ITEM-00001')." - added
Input schema / properties / name / examplesAdded value: +[ + "SINV-00001", + "CUST-00001" +]
7 tool updates
v1.0.0- First observed
frappe_create_doc - First observed
frappe_delete_doc - First observed
frappe_get_doc - First observed
frappe_ping - First observed
frappe_run_method - First observed
frappe_search_docs - First observed
frappe_update_doc
TDQS
Scored across 7 tools
Each tool targets a clearly distinct action: ping, get, search, create, update, delete, and an explicit escape-hatch method call. The descriptions actively disambiguate overlapping cases, such as get vs search, create vs update, and delete vs update-to-cancelled. No two tools appear interchangeable.
All tools use the same frappe_ prefix followed by a snake_case verb or verb_noun pattern: frappe_get_doc, frappe_search_docs, frappe_create_doc, etc. The convention is predictable throughout, including the diagnostic frappe_ping and escape hatch frappe_run_method.
Seven tools is well-scoped for a Frappe/ERPNext CRUD surface: five document lifecycle operations, one connectivity diagnostic, and one server-method escape hatch. Each tool earns its place without obvious redundancy.
Core CRUD coverage is complete: create, get, search, update, and delete, plus ping and run_method for submit/cancel/report operations. Minor gaps exist around explicit metadata/schema discovery and bulk operations, but the escape hatch lets agents work around them.
Maintenance
Related MCP Connectors
Connect AI clients to your Dactic work context through a hosted MCP service.
Connect MCP clients to 2,000+ AI models without managing provider API keys.
Host any HTTP API as a standard MCP server; auto-distribute to 12+ mainstream agent platforms.
API-first CRM for LLMs - contacts, companies, deals and activities over a native MCP server.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables interaction with Frappe Framework sites through comprehensive document operations, schema introspection, report generation, and method execution. Provides secure API-based access to create, read, update, and delete Frappe documents while supporting financial reporting and DocType management.2417ISC

Frappe MCPofficial
AlicenseNot gradedqualityFmaintenanceAllows Frappe Framework apps to function as MCP servers, exposing Python-defined tools for LLM interaction.170MIT- AlicenseNot gradedqualityAmaintenanceMCP server that enables LLMs to interact with ERPNext/Frappe sites for document CRUD, search, reports, workflows, and analytics, respecting user permissions and logging all actions.334AGPL 3.0
- AlicenseNot gradedqualityDmaintenanceEnables ERPNext management, file operations, read-only database access, and ERPNext API integration through a standardized MCP server.4MIT