Install
$ agentstack add mcp-ahmed-mansy-mansico-mcp-llm-server-for-erpnext ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
MCP Task - Universal ERPNext MCP Connector
A comprehensive Model Context Protocol (MCP) connector for ERPNext that enables LLMs to interact seamlessly with any ERPNext instance through standardized tools and advanced permission controls.
[](https://opensource.org/licenses/MIT) [](https://www.python.org/downloads/) [](https://github.com/frappe/frappe)
🎯 Overview
MCP Task is a general-purpose MCP connector that transforms any ERPNext instance into an AI-accessible business system. It provides:
- 🔌 Universal DocType Support: Works with any ERPNext DocType without configuration
- 🛡️ Advanced Permission Model: Field-level, operation-level, and conditional access controls
- 🚀 Complete CRUD Operations: Create, Read, Update, Delete with full validation
- 🎨 Web Chat Interface: Built-in chat widget for direct LLM interaction
- 📊 Comprehensive Auditing: Complete activity logging and permission tracking
- ⚡ Production Ready: Enterprise-grade security, error handling, and performance
🌟 Key Features
🔧 Complete ERPNext Integration
- Full CRUD Operations: Create, Read, Update, Delete any document type
- Intelligent Search: Text-based search across all document fields
- Flexible Filtering: Query documents with complex filter conditions
- DocType Agnostic: Works with standard and custom DocTypes automatically
🛡️ Advanced Security & Permissions
- Multi-Level Access Control: User roles, DocType permissions, field restrictions
- Conditional Access: Python-based rules for dynamic permission evaluation
- Audit Trail: Complete logging of all LLM interactions and data access
- Field-Level Security: Hide sensitive fields from LLM access
🎨 User Experience
- Web Chat Interface: Floating chat widget integrated into ERPNext UI
- Real-time Responses: Instant feedback with loading indicators
- Conversation History: Persistent chat sessions per user
- Mobile Responsive: Works seamlessly on desktop and mobile devices
🏗️ Architecture
System Overview
graph TB
LLM[LLM ClientClaude, GPT, etc.] --> MCP[MCP Protocol Handler]
MCP --> TR[Tool Registry]
TR --> TOOLS[Document ToolsCRUD Operations]
TOOLS --> PERM[Permission Engine]
PERM --> ERP[ERPNext Database]
PERM --> CONFIG[Permission Configuration]
CONFIG --> FIELD[Field Restrictions]
CONFIG --> OP[Operation Controls]
CONFIG --> COND[Condition Scripts]
MCP --> AUDIT[Audit Logger]
MCP --> CHAT[Chat Interface]
subgraph "Security Layers"
PERM
CONFIG
AUDIT
end
Core Components
1. MCP Protocol Handler (api/__init__.py)
- JSON-RPC 2.0 Compliance: Full MCP protocol implementation
- Request Routing: Handles initialize, tools/list, tools/call methods
- Error Management: Comprehensive error handling with proper status codes
- Authentication: Integration with ERPNext session management
2. Tool Registry System (core/)
- Dynamic Discovery: Automatic tool loading and registration
- Permission Integration: Built-in permission validation for each tool
- Extensible Architecture: Easy addition of custom tools
- Metadata Management: Tool descriptions and input schemas
3. Document Tools (tools/)
create_document: Create any ERPNext document with validationget_document: Retrieve document data with field filteringlist_documents: Query documents with flexible filterssearch_documents: Full-text search across document typesupdate_document: Modify document fields with validationdelete_document: Remove documents with dependency checks
4. Advanced Permission Engine
MCP Permission Configuration: Configurable access control DocType- Multi-Level Security: Role, DocType, field, and operation controls
- Condition Scripts: Python-based dynamic permission evaluation
- Field Filtering: Automatic removal of restricted fields from responses
5. Web Interface (public/js/mcp_task.js)
- Floating Chat Widget: Non-intrusive interface integrated into ERPNext
- Real-time Communication: WebSocket-based chat with loading indicators
- Conversation Persistence: User-specific chat history management
- Responsive Design: Mobile and desktop compatibility
6. Audit & Logging System
MCP Chat Log: Complete interaction logging for compliance- Permission Tracking: Detailed logs of access control decisions
- Performance Monitoring: Request timing and error rate tracking
- Security Auditing: Failed access attempts and policy violations
🚀 Installation & Setup
Prerequisites
- ERPNext v15+ or Frappe Framework v15+
- Python 3.10+
- Node.js 18+ (for building assets)
- Redis (for background jobs and caching)
Step 1: Install the App
# Clone the repository
cd frappe-bench
bench get-app https://github.com/yourusername/mcp_task
# Install on your site
bench --site your-site.com install-app mcp_task
# Migrate database
bench --site your-site.com migrate
# Build and restart
bench build --app mcp_task
bench restart
Step 2: Configure Permissions
Basic Setup (All Users)
# Enable MCP access for all users (basic setup)
bench --site your-site.com execute "frappe.db.set_single_value('System Settings', 'enable_mcp_chat', 1)"
Advanced Setup (Role-Based)
- Create MCP Roles (via Setup > Users and Permissions > Role):
MCP Admin: Full access to configuration and toolsMCP User: Limited access to standard operations
- Assign Roles to users through User management
Step 3: Environment Variables
Create a .env file in your bench directory:
# MCP Configuration
MCP_ENABLED=1
MCP_DEBUG=0
MCP_LOG_LEVEL=INFO
# Security Settings
MCP_RATE_LIMIT=100 # Requests per minute per user
MCP_SESSION_TIMEOUT=3600 # Session timeout in seconds
# Optional: External LLM Integration
OPENAI_API_KEY=your_openai_key_here
CLAUDE_API_KEY=your_claude_key_here
Step 4: Basic Configuration Test
# Test MCP endpoint
curl -X POST http://your-site.com/api/method/mcp_task.api.handle_mcp_request \
-H "Content-Type: application/json" \
-H "Authorization: token your_api_key:your_api_secret" \
-d '{
"jsonrpc": "2.0",
"method": "initialize",
"params": {"protocolVersion": "2024-11-05"},
"id": 1
}'
Step 5: Chat Interface Setup
The chat interface is automatically available after installation. Users will see a chat icon in the navbar when logged in.
Troubleshooting: If chat icon doesn't appear:
# Clear cache and rebuild
bench --site your-site.com clear-cache
bench build --app mcp_task
bench restart
🔒 Permission Model & Access Control
Overview
MCP Task implements a multi-layered security model that allows granular control over LLM access to your ERPNext data:
- User Authentication: Standard ERPNext login required
- Role-Based Access: ERPNext role system integration
- DocType Permissions: Native ERPNext permission system
- MCP-Specific Controls: Advanced field and operation restrictions
Permission Configuration
Creating Permission Rules
Navigate to Setup > MCP Task > MCP Permission Configuration to create custom access rules:
# Example: Restrict Sales User to read-only Customer data
{
"title": "Sales User Customer Access",
"doctype_name": "Customer",
"user_roles": "Sales User",
"operation_restrictions": {
"allowed_operations": ["read", "list", "search"],
"blocked_operations": ["create", "update", "delete"]
},
"field_restrictions": {
"blocked_fields": ["credit_limit", "payment_terms"]
}
}
Field-Level Restrictions
Control which fields are accessible to the LLM:
{
"field_restrictions": {
"allowed_fields": ["customer_name", "customer_group", "territory"],
"blocked_fields": ["credit_limit", "outstanding_amount", "payment_terms"]
}
}
Condition-Based Access
Use Python scripts for dynamic permission evaluation:
# Condition Script Example: Only allow access to own records
if doc and doc.get("owner") == user:
result = True
else:
result = False
# Complex example: Time-based restrictions
import datetime
current_hour = datetime.datetime.now().hour
if current_hour 17: # Business hours only
result = False
else:
result = True
Permission Examples
Example 1: Basic User Restrictions
{
"title": "Standard User Access",
"user_roles": "Employee, Desk User",
"operation_restrictions": {
"allowed_operations": ["read", "search", "list"]
},
"field_restrictions": {
"blocked_fields": ["base_amount", "outstanding_amount", "credit_limit"]
}
}
Example 2: Department-Specific Access
{
"title": "HR Department Access",
"doctype_name": "Employee",
"user_roles": "HR User, HR Manager",
"field_restrictions": {
"allowed_fields": ["employee_name", "department", "designation", "reports_to"]
},
"condition_script": "result = frappe.get_roles(user).contains('HR Manager') or doc.get('department') == frappe.db.get_value('Employee', {'user_id': user}, 'department')"
}
Example 3: Financial Data Protection
{
"title": "Accounts Restricted Access",
"tool_names": "get_document, list_documents",
"operation_restrictions": {
"conditions": {
"financial_doctypes": ["Sales Invoice", "Purchase Invoice", "Payment Entry"],
"require_role": "Accounts User"
}
},
"condition_script": """
# Only accounts users can access financial documents
if doctype in ['Sales Invoice', 'Purchase Invoice', 'Payment Entry']:
result = 'Accounts User' in frappe.get_roles(user)
else:
result = True
"""
}
Security Best Practices
1. Principle of Least Privilege
- Start with minimal permissions and add as needed
- Use role-based restrictions rather than user-specific rules
- Regularly audit permission configurations
2. Field Sensitivity Classification
# High Sensitivity (Always Block)
HIGH_SENSITIVITY = [
"bank_account", "iban", "swift_number",
"salary", "ctc", "password", "api_key"
]
# Medium Sensitivity (Role-Based Access)
MEDIUM_SENSITIVITY = [
"credit_limit", "outstanding_amount",
"employee_id", "phone", "email"
]
3. Audit Configuration
- Enable detailed logging for all permission decisions
- Monitor failed access attempts
- Regular review of permission effectiveness
🎯 API Usage Examples
LLM Prompt Examples
Document Creation
"Create a new Customer with the name 'Tech Solutions Inc', customer group 'Corporate', and territory 'India'. Set the customer type to 'Company'."
MCP Request:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "create_document",
"arguments": {
"doctype": "Customer",
"data": {
"customer_name": "Tech Solutions Inc",
"customer_group": "Corporate",
"territory": "India",
"customer_type": "Company"
}
}
},
"id": 1
}
Document Retrieval with Field Filtering
"Show me the customer details for CUST-001, but hide any financial information."
MCP Response (with field restrictions applied):
{
"jsonrpc": "2.0",
"result": {
"content": [{
"type": "text",
"text": {
"customer_name": "Tech Solutions Inc",
"customer_group": "Corporate",
"territory": "India",
"customer_type": "Company"
// Sensitive fields like credit_limit filtered out
}
}]
},
"id": 1
}
Complex Searches
"Find all Sales Orders from the last 30 days where the customer is from Mumbai territory and the order value is above 50,000."
MCP Request:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "list_documents",
"arguments": {
"doctype": "Sales Order",
"filters": {
"creation": [">=", "2024-12-15"],
"territory": "Mumbai",
"grand_total": [">", 50000]
},
"fields": ["name", "customer", "grand_total", "delivery_date"]
}
},
"id": 1
}
⚠️ Security Considerations
Data Protection
- Encryption in Transit: All MCP communications use HTTPS/WSS
- Session Management: ERPNext session tokens with configurable timeouts
- API Rate Limiting: Configurable request limits per user/IP
- Audit Logging: Complete trail of all LLM interactions
Failure Modes & Mitigation
1. Permission Bypass Attempts
- Risk: LLM attempts to access restricted data
- Mitigation: Multi-layer validation, default-deny policies
- Monitoring: Alert on permission failures
2. Data Leakage via Field Filtering
- Risk: Sensitive data exposed through related fields
- Mitigation: Deep field analysis, cascading restrictions
- Example: Block
customer.credit_limitwhen customer data is filtered
3. Condition Script Vulnerabilities
- Risk: Malicious Python code in condition scripts
- Mitigation: Sandboxed execution, restricted imports
- Validation: Syntax checking, security reviews
4. Large Data Extraction
- Risk: LLM queries return excessive data volumes
- Mitigation: Result size limits, pagination controls
- Configuration:
MAX_RESULTS_PER_QUERY = 1000
Security Monitoring
# Example monitoring alerts
SECURITY_ALERTS = {
"permission_failures": {
"threshold": 10, # failures per hour
"action": "notify_admin"
},
"large_queries": {
"threshold": 5000, # records per query
"action": "log_and_limit"
},
"sensitive_field_access": {
"fields": ["salary", "credit_limit", "bank_account"],
"action": "audit_log"
}
}
🛠️ Development & Customization
Adding Custom Tools
- Create Tool Class (
tools/my_custom_tool.py):
from typing import Any
from mcp_task.core.base_tool import BaseTool
import frappe
class MyCustomTool(BaseTool):
def __init__(self):
super().__init__()
self.name = "my_custom_tool"
self.description = "Custom business logic tool"
self.requires_permission = "My Custom DocType"
self.inputSchema = {
"type": "object",
"properties": {
"param1": {"type": "string", "description": "First parameter"},
"param2": {"type": "integer", "description": "Second parameter"}
},
"required": ["param1"]
}
def execute(self, arguments: dict[str, Any]) -> dict[str, Any]:
try:
# Your custom logic here
result = self.perform_custom_operation(arguments)
return {"success": True, "data": result}
except Exception as e:
return {"success": False, "error": str(e)}
def perform_custom_operation(self, args):
# Implement your business logic
pass
- Register Tool (in
core/tool_registry.py):
from mcp_task.tools.my_custom_tool import MyCustomTool
# Add to load_tools method
self.register_tool(MyCustomTool())
Extending Permission System
- Custom Permission Validators:
# mcp_task/permissions/custom_validators.py
def validate_customer_territory(user, doc_data):
"""Only allow access to customers in user's territory"""
user_territory = frappe.db.get_value("Employee",
{"user_id": user}, "territory")
return doc_data.get("territory") == user_territory
- Plugin-Based Architecture:
# mcp_task/plugins/custom_business_rules.py
class CustomBusinessRules:
def apply_restrictions(self, tool_name, user, doc_data):
# Custom business logic
pass
…
## Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [Ahmed-Mansy-Mansico](https://github.com/Ahmed-Mansy-Mansico)
- **Source:** [Ahmed-Mansy-Mansico/MCP_LLM_Server_FOR_ERPNext](https://github.com/Ahmed-Mansy-Mansico/MCP_LLM_Server_FOR_ERPNext)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.