Synapse
Provides a permission-aware MCP server for ERPNext, enabling LLM clients to manage ERPNext documents (Sales Invoices, etc.) under the authenticated user's permissions, with OAuth and audit trails.
Provides a permission-aware MCP server for Frappe, allowing LLM clients to read and write Frappe site data under user permissions, with OAuth authentication and full audit logging.
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., "@Synapselist the last 10 sales invoices for customer Acme"
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.
Synapse
Synapse is an MCP server for Frappe and ERPNext. It lets an AI client read and write a site over OAuth. The client acts as a real Frappe user and stays within that user's permissions. Every call is written to an audit log.
POST https://<your-site>/api/method/synapse.mcp.handle_mcpWhat makes it different
Many Frappe MCP servers run with full privileges and give the model raw SQL or document access with permissions turned off. That is fine for a personal sandbox. It is not safe on a business system. Synapse works the other way.
It never turns off permissions. Every tool runs as the signed in user. DocType permissions, User Permissions, share rules and submit or cancel rights all apply. Writes go through the normal insert, save, submit and cancel path, so validations, hooks and workflows run the same as they do in the desk.
It adds a second layer above permissions. "This user may edit Sales Invoices in the desk" and "an AI agent holding this user's token may edit Sales Invoices" are two different decisions. That second decision is the Synapse Profile.
It logs every call, including ones that are refused.
It has no external dependencies and creates no roles. A plain
bench install-appis the whole install.
Related MCP server: Frappe Assistant Core
Install
bench get-app https://github.com/dxbitz-technology/synapse
bench --site <your-site> install-app synapseCheck the current state of a site at any time:
bench --site <your-site> execute synapse.mcp_tools.check.reportIt prints what is set up and what is missing, in the order to fix it. A fresh install is fully closed. Nothing is reachable until you create a profile.
How access works
Access is granted by Synapse Profile records. A profile lists a set of roles and the DocTypes and actions those roles may use. A user's access is the sum of every enabled profile whose roles they hold.
With no matching profile, nothing is reachable.
A tick in a profile is a ceiling, not a grant. The user still needs the matching Frappe permission on the record. That is checked when the document is touched.
Full Access on a profile grants every action on every DocType and ignores the grid. The user's own Frappe permissions become the working limit. Use it only for a user whose Frappe permissions are already scoped the way you want.
Allow SQL on a profile turns on the raw SQL tool for its users. Read the SQL section first.
Two fixed rules sit above every profile and cannot be overridden:
Some DocTypes are never reachable. These hold tokens, credentials and the records that hand them out, plus Synapse's own settings, profiles and log. Reading them is how a read-only user could turn into a writer, or edit the gate that controls them.
Some DocTypes are read only and can never be written. These define the schema, the code and the permission model, for example DocType, Custom Field, Server Script, Custom DocPerm, Role and User. A user who could edit Custom DocPerm could grant themselves anything.
The read-only rule has one opt-in exception. Tick Allow System Manager Config Writes in Synapse Settings and a caller who holds the System Manager role can create and update those config, schema and permission DocTypes through MCP, since they can already do so in the desk. It is off by default. Only create and update are lifted; delete and run_operation on them stay blocked. The token and credential DocTypes above are never affected: they stay blocked for everyone, System Manager included.
Synapse Settings also has a site wide Blocked DocTypes list. Use it to block something that a profile would otherwise allow.
Tools
Tool | Action needed |
| read |
| read |
| write |
| write |
| write |
| submit |
| cancel |
| delete |
| operate |
| a profile with Allow SQL (see below) |
The write tools fall into three bands, all under the write action:
Document level:
create_docmakes a document,update_docchanges fields on one (and replaces a whole child table if pointed at one),set_valuesets a single field.Row level:
add_childappends a row and returns its name and idx,set_child_valueedits one row (one or more fields, optionalexpect),set_child_rowsedits many rows atomically,delete_childremoves a row. Rows are addressed byrow.name, taken from a priorget_doc, never by position or content, so the log always names the row it touched. Each runs the parent's real save, so totals and tax recompute the same as in the desk.Field text:
replace_in_fieldchanges part of a long text field. It counts how many times the text appears and refuses unless that count matches the number you expected, so it cannot rewrite the wrong part.
For a child table, prefer the row-level tools. Use update_doc on a table
only to replace the whole thing on purpose. Reaching for update_doc to change
one price would silently discard every other row.
set_child_value, set_child_rows and delete_child take an optional expect
of current values. When given, the write is refused if the row no longer holds
those values, so an edit cannot land on a row that changed since it was read. A
submitted parent is refused, the same as a desk edit would be. run_operation
runs a document's own method (see below).
Dates come back in the format set in Synapse Settings, ISO by default. Writes accept ISO or DD-MM-YYYY, so a read then write round trip cannot swap the day and the month.
The operate action
run_operation runs a document's own method by name. This is the behaviour
behind a desk button, for example a Sales Invoice reposting its accounting
entries. Because it can run code, it has its own action, operate, which is
granted per DocType in a profile. That grant is what makes it safe to offer. A
profile has to say, for this DocType, that operations may run.
It still runs as the signed in user, under Frappe permissions, and every call is logged. Methods that already have their own tool (save, submit, cancel, delete and so on) are refused here, and so is anything private. So operate cannot be used to get around the other tools.
Custom tools
Any installed app can add its own tools to the Synapse endpoint, so an app can expose the specific jobs it knows how to do rather than only the generic document tools. It is all done through one hook. The app declares each tool as data and needs no import of synapse:
# in myapp/hooks.py
synapse_tools = [
{"method": "myapp.synapse_tools.open_tasks_for", "read_only": True},
]# in myapp/synapse_tools.py
import frappe
def open_tasks_for(project: str) -> dict:
"""Return the open tasks on a project.
The description and arguments the model sees come from this docstring and
the function signature.
"""
rows = frappe.get_list(
"Task",
filters={"project": project, "status": ["!=", "Completed"]},
fields=["name", "subject", "status"],
)
return {"project": project, "open_tasks": rows}An app that prefers to keep the flags next to the function can instead mark it
with @synapse.tool(read_only=True) and list the module path as a plain string
in the same hook (synapse_tools = ["myapp.synapse_tools"]). Both forms may be
mixed.
A custom tool runs the same way the built-in tools do. It runs as the signed in user, with Frappe permissions on, and every call is written to the Synapse Log. The app author is responsible for what the function does, so it should read and write through the normal Frappe document API and never with permissions off.
A registered tool is not reachable on its own. Two more things must be true:
Enable Custom Tools is ticked in Synapse Settings.
A Synapse Profile the caller holds lists the tool by its exact name, in the Custom Tools table.
This is the same explicit grant model as the rest of Synapse. Full Access does not include custom tools, because a custom tool can run any code its app wrote, so each one is granted by name. A tool whose name clashes with a built-in is refused at load, so an app can never replace a core tool. The readiness report lists every registered tool and whether a profile grants it:
bench --site <your-site> execute synapse.mcp_tools.check.reportModel provider
Synapse Settings has a Model Provider choice. Claude is the only provider that is wired up. The other options are placeholders. The setting records which model family the site presents Synapse for and does not change how tools run.
Setting it up
1. OAuth. Frappe 16 can publish OAuth server metadata and support dynamic client registration. This is what lets an MCP client connect without someone creating an OAuth Client record by hand. It is off by default. In OAuth Settings turn on Show Auth Server Metadata, Show Protected Resource Metadata and Enable Dynamic Client Registration. Synapse does not change these settings. They affect the whole site's OAuth behaviour.
A Frappe OAuth token is not limited to MCP. It authorises the whole /api
surface as that user, so scope the user accordingly.
2. Create a Synapse Profile. Add the roles the agent's user holds, then the DocTypes and actions those roles may use. Reading needs only a read tick.
3. Fill in Synapse Settings. Tick Enable Synapse Endpoint and Enable Read Tools. Reads work at this point. For writes, also tick Enable Write Tools. If that switch is off, the endpoint stays read only whatever a profile grants.
Connecting a client
claude mcp add --transport http mysite https://<your-site>/api/method/synapse.mcp.handle_mcpThen sign in. A browser opens on the site login page. Any MCP client that speaks Streamable HTTP with OAuth works the same way. In Claude Desktop it is Settings, Connectors, Add custom connector, with the same URL.
The endpoint answers an unauthenticated call with a 401 and a WWW-Authenticate header pointing at the site's OAuth metadata, so a client can discover how to authenticate on its own. If a client reports it cannot determine the server settings, the usual cause is the three OAuth Settings switches above being off, so the metadata is not published.
Raw SQL
run_sql_query does not use Frappe's permission system. A user in a profile with
Allow SQL can read every table on the site, whatever their DocType permissions
are. Grant it only to users who already have full database access.
It is off until Enable Read-Only SQL Tool is ticked in Synapse Settings and the
user holds a profile with Allow SQL. It does not use the profile's DocType
grants, because it never names a DocType. Prefer get_list and get_doc. Use
SQL only for a join or an aggregate they cannot express. If an agent keeps
reaching for SQL, the document tools are probably missing something it needs.
Two layers protect it:
A read only database user, enforced by MariaDB. A query that gets past the text filter still cannot write.
mcp_tools/guard.py. It checks the statement type, blocks comments, blocks more than one statement, blocks a list of keywords and tables, and caps the length. This is text matching, so treat it as a backup, not the main line of defence.
Set up the database user per site. As MariaDB root:
CREATE USER 'mcp_ro'@'localhost' IDENTIFIED BY '<STRONG_PASSWORD>';
GRANT SELECT ON `<DB_NAME>`.* TO 'mcp_ro'@'localhost';
REVOKE FILE ON *.* FROM 'mcp_ro'@'localhost';
FLUSH PRIVILEGES;Then in site_config.json (never in the repo):
{
"mcp_ro_db_user": "mcp_ro",
"mcp_ro_db_password": "<STRONG_PASSWORD>"
}Without those keys, the tool falls back to the site's normal read write connection and rolls back after every query. It works, but then the text guard is the only boundary. On hosted platforms where a second database user is not possible, that fallback is the only option. Decide before you enable SQL there.
Add more blocked tables per site with mcp_sql_blocked_tables in
site_config.json. MariaDB only. connection.py raises NotImplementedError on
other backends.
Audit
Every call writes a Synapse Log row: success, refusal or error. Each row records the tool, the user, how they signed in, the IP address, the document touched, the row counts and the timing. Writes also record the values sent and the before and after of each changed field. Calls that are refused before the tool runs are logged too.
If a tool looks blocked and there is no log row for it, the block is in the client, usually its own tool permission prompt. Check that first.
Log rows are written with their own commit after any rollback, so a failed or refused write still leaves a record. System Manager can read and report on the log but cannot create or edit rows from the desk. A daily job drops rows past the retention window. Untick Log Field Values if the data itself must not be copied into the log. Password fields are masked either way.
Tests
bench --site <your-site> run-tests --app synapseThe access model, the SQL guard, the tool schemas and the value conversion do not import anything from frappe, so they also run without a site:
python -m unittest discover -s apps/synapse -p 'test_mcp_*.py'Licence
GNU Affero General Public License v3.0 or later. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Supervised API-write gateway for AI agents with policy, human approval and execution receipts.
- odooOAuthcom.odooconsole
Odoo ERP for AI agents: hosted OAuth endpoint, gated writes, one endpoint for every instance.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables LLMs to interact with any ERPNext instance through comprehensive CRUD operations, advanced permissions, and a web chat interface.1MIT
- 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.303AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables AI models to securely interact with Frappe Framework/ERPNext instances, supporting document CRUD, RPC methods, file management, workflows, reporting, and more via the Model Context Protocol.293ISC
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to interact with ERPNext data and functionality through the Model Context Protocol, including document CRUD, report running, and API method calls.MIT