From 2ee93048e1dca2fb77424bbac5deb5377a8b6bbb Mon Sep 17 00:00:00 2001 From: MOHAN Date: Tue, 21 Apr 2026 20:26:45 +0530 Subject: [PATCH] feat: Add tools for managing server scripts, client scripts, translations, assignment rules, user permissions, webhooks, API keys, and workflows - Implemented server and client script management tools in `frappe_mcp/tools/scripts.py` - Added translation and user permission management tools in `frappe_mcp/tools/translations.py` - Created user and role management tools in `frappe_mcp/tools/users.py` - Developed webhook and API key management tools in `frappe_mcp/tools/webhooks.py` - Introduced workflow management tools in `frappe_mcp/tools/workflow_tools.py` - Added `pyproject.toml` for project metadata and dependencies --- .env | 47 ++ .env.example | 72 +++ .mcp.json | 16 + DINE360_ARCHITECTURE.md | 347 +++++++++++++ Frappe-MCP.rar | Bin 0 -> 282257 bytes claude_desktop_config.example.json | 14 + docker/.env.example | 16 + docker/docker-compose.yml | 206 ++++++++ frappe_mcp/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 150 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 140 bytes .../__pycache__/audit_store.cpython-311.pyc | Bin 0 -> 3995 bytes .../__pycache__/audit_store.cpython-314.pyc | Bin 0 -> 4075 bytes frappe_mcp/__pycache__/config.cpython-311.pyc | Bin 0 -> 1605 bytes frappe_mcp/__pycache__/config.cpython-314.pyc | Bin 0 -> 2048 bytes .../__pycache__/healthcheck.cpython-314.pyc | Bin 0 -> 4635 bytes .../module_registry.cpython-311.pyc | Bin 0 -> 6654 bytes .../module_registry.cpython-314.pyc | Bin 0 -> 6608 bytes frappe_mcp/__pycache__/server.cpython-311.pyc | Bin 0 -> 3460 bytes frappe_mcp/__pycache__/server.cpython-314.pyc | Bin 0 -> 3342 bytes frappe_mcp/audit_store.py | 55 ++ frappe_mcp/client/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 157 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 147 bytes .../__pycache__/frappe_api.cpython-311.pyc | Bin 0 -> 10433 bytes .../__pycache__/frappe_api.cpython-314.pyc | Bin 0 -> 14533 bytes frappe_mcp/client/frappe_api.py | 144 ++++++ frappe_mcp/config.py | 31 ++ frappe_mcp/healthcheck.py | 82 +++ frappe_mcp/module_registry.py | 137 +++++ frappe_mcp/server.py | 103 ++++ frappe_mcp/tools/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 156 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 146 bytes .../__pycache__/activity.cpython-311.pyc | Bin 0 -> 13066 bytes .../__pycache__/activity.cpython-314.pyc | Bin 0 -> 14193 bytes .../tools/__pycache__/admin.cpython-311.pyc | Bin 0 -> 7422 bytes .../tools/__pycache__/admin.cpython-314.pyc | Bin 0 -> 8272 bytes .../__pycache__/analytics.cpython-311.pyc | Bin 0 -> 7673 bytes .../__pycache__/analytics.cpython-314.pyc | Bin 0 -> 8082 bytes .../__pycache__/bulk_ops.cpython-311.pyc | Bin 0 -> 14244 bytes .../__pycache__/bulk_ops.cpython-314.pyc | Bin 0 -> 15038 bytes .../business_actions.cpython-311.pyc | Bin 0 -> 18829 bytes .../business_actions.cpython-314.pyc | Bin 0 -> 20410 bytes .../__pycache__/custom_fields.cpython-311.pyc | Bin 0 -> 5063 bytes .../__pycache__/custom_fields.cpython-314.pyc | Bin 0 -> 5502 bytes .../__pycache__/dashboards.cpython-311.pyc | Bin 0 -> 7971 bytes .../__pycache__/dashboards.cpython-314.pyc | Bin 0 -> 8835 bytes .../__pycache__/doctypes.cpython-311.pyc | Bin 0 -> 5240 bytes .../__pycache__/doctypes.cpython-314.pyc | Bin 0 -> 6759 bytes .../document_inspect.cpython-311.pyc | Bin 0 -> 10794 bytes .../document_inspect.cpython-314.pyc | Bin 0 -> 11255 bytes .../document_lifecycle.cpython-311.pyc | Bin 0 -> 9858 bytes .../document_lifecycle.cpython-314.pyc | Bin 0 -> 10481 bytes .../__pycache__/documents.cpython-311.pyc | Bin 0 -> 6416 bytes .../__pycache__/documents.cpython-314.pyc | Bin 0 -> 7176 bytes .../email_templates.cpython-311.pyc | Bin 0 -> 8330 bytes .../email_templates.cpython-314.pyc | Bin 0 -> 8973 bytes .../tools/__pycache__/files.cpython-311.pyc | Bin 0 -> 6827 bytes .../tools/__pycache__/files.cpython-314.pyc | Bin 0 -> 7453 bytes .../__pycache__/foundation.cpython-311.pyc | Bin 0 -> 7258 bytes .../__pycache__/foundation.cpython-314.pyc | Bin 0 -> 7628 bytes .../__pycache__/governance.cpython-311.pyc | Bin 0 -> 19066 bytes .../__pycache__/governance.cpython-314.pyc | Bin 0 -> 20011 bytes .../__pycache__/naming_series.cpython-311.pyc | Bin 0 -> 4171 bytes .../__pycache__/naming_series.cpython-314.pyc | Bin 0 -> 4695 bytes .../__pycache__/print_formats.cpython-311.pyc | Bin 0 -> 6252 bytes .../__pycache__/print_formats.cpython-314.pyc | Bin 0 -> 6836 bytes .../property_setters.cpython-311.pyc | Bin 0 -> 6706 bytes .../property_setters.cpython-314.pyc | Bin 0 -> 7243 bytes .../tools/__pycache__/reports.cpython-311.pyc | Bin 0 -> 7282 bytes .../tools/__pycache__/reports.cpython-314.pyc | Bin 0 -> 7858 bytes .../__pycache__/scheduler.cpython-311.pyc | Bin 0 -> 7575 bytes .../__pycache__/scheduler.cpython-314.pyc | Bin 0 -> 8368 bytes .../tools/__pycache__/scripts.cpython-311.pyc | Bin 0 -> 6629 bytes .../tools/__pycache__/scripts.cpython-314.pyc | Bin 0 -> 7127 bytes .../__pycache__/translations.cpython-311.pyc | Bin 0 -> 10402 bytes .../__pycache__/translations.cpython-314.pyc | Bin 0 -> 10964 bytes .../tools/__pycache__/users.cpython-311.pyc | Bin 0 -> 8179 bytes .../tools/__pycache__/users.cpython-314.pyc | Bin 0 -> 8518 bytes .../__pycache__/webhooks.cpython-311.pyc | Bin 0 -> 8358 bytes .../__pycache__/webhooks.cpython-314.pyc | Bin 0 -> 9177 bytes .../workflow_tools.cpython-311.pyc | Bin 0 -> 8514 bytes .../workflow_tools.cpython-314.pyc | Bin 0 -> 9196 bytes frappe_mcp/tools/activity.py | 390 ++++++++++++++ frappe_mcp/tools/admin.py | 185 +++++++ frappe_mcp/tools/analytics.py | 176 +++++++ frappe_mcp/tools/bulk_ops.py | 337 +++++++++++++ frappe_mcp/tools/business_actions.py | 477 ++++++++++++++++++ frappe_mcp/tools/custom_fields.py | 124 +++++ frappe_mcp/tools/dashboards.py | 217 ++++++++ frappe_mcp/tools/doctypes.py | 155 ++++++ frappe_mcp/tools/document_inspect.py | 267 ++++++++++ frappe_mcp/tools/document_lifecycle.py | 229 +++++++++ frappe_mcp/tools/documents.py | 169 +++++++ frappe_mcp/tools/email_templates.py | 220 ++++++++ frappe_mcp/tools/files.py | 168 ++++++ frappe_mcp/tools/foundation.py | 144 ++++++ frappe_mcp/tools/governance.py | 425 ++++++++++++++++ frappe_mcp/tools/naming_series.py | 111 ++++ frappe_mcp/tools/print_formats.py | 155 ++++++ frappe_mcp/tools/property_setters.py | 164 ++++++ frappe_mcp/tools/reports.py | 188 +++++++ frappe_mcp/tools/scheduler.py | 179 +++++++ frappe_mcp/tools/scripts.py | 174 +++++++ frappe_mcp/tools/translations.py | 244 +++++++++ frappe_mcp/tools/users.py | 187 +++++++ frappe_mcp/tools/webhooks.py | 214 ++++++++ frappe_mcp/tools/workflow_tools.py | 215 ++++++++ pyproject.toml | 29 ++ 110 files changed, 6813 insertions(+) create mode 100644 .env create mode 100644 .env.example create mode 100644 .mcp.json create mode 100644 DINE360_ARCHITECTURE.md create mode 100644 Frappe-MCP.rar create mode 100644 claude_desktop_config.example.json create mode 100644 docker/.env.example create mode 100644 docker/docker-compose.yml create mode 100644 frappe_mcp/__init__.py create mode 100644 frappe_mcp/__pycache__/__init__.cpython-311.pyc create mode 100644 frappe_mcp/__pycache__/__init__.cpython-314.pyc create mode 100644 frappe_mcp/__pycache__/audit_store.cpython-311.pyc create mode 100644 frappe_mcp/__pycache__/audit_store.cpython-314.pyc create mode 100644 frappe_mcp/__pycache__/config.cpython-311.pyc create mode 100644 frappe_mcp/__pycache__/config.cpython-314.pyc create mode 100644 frappe_mcp/__pycache__/healthcheck.cpython-314.pyc create mode 100644 frappe_mcp/__pycache__/module_registry.cpython-311.pyc create mode 100644 frappe_mcp/__pycache__/module_registry.cpython-314.pyc create mode 100644 frappe_mcp/__pycache__/server.cpython-311.pyc create mode 100644 frappe_mcp/__pycache__/server.cpython-314.pyc create mode 100644 frappe_mcp/audit_store.py create mode 100644 frappe_mcp/client/__init__.py create mode 100644 frappe_mcp/client/__pycache__/__init__.cpython-311.pyc create mode 100644 frappe_mcp/client/__pycache__/__init__.cpython-314.pyc create mode 100644 frappe_mcp/client/__pycache__/frappe_api.cpython-311.pyc create mode 100644 frappe_mcp/client/__pycache__/frappe_api.cpython-314.pyc create mode 100644 frappe_mcp/client/frappe_api.py create mode 100644 frappe_mcp/config.py create mode 100644 frappe_mcp/healthcheck.py create mode 100644 frappe_mcp/module_registry.py create mode 100644 frappe_mcp/server.py create mode 100644 frappe_mcp/tools/__init__.py create mode 100644 frappe_mcp/tools/__pycache__/__init__.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/__init__.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/activity.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/activity.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/admin.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/admin.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/analytics.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/analytics.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/bulk_ops.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/bulk_ops.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/business_actions.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/business_actions.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/custom_fields.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/custom_fields.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/dashboards.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/dashboards.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/doctypes.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/doctypes.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/document_inspect.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/document_inspect.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/document_lifecycle.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/document_lifecycle.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/documents.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/documents.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/email_templates.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/email_templates.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/files.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/files.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/foundation.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/foundation.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/governance.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/governance.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/naming_series.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/naming_series.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/print_formats.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/print_formats.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/property_setters.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/property_setters.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/reports.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/reports.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/scheduler.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/scheduler.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/scripts.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/scripts.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/translations.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/translations.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/users.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/users.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/webhooks.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/webhooks.cpython-314.pyc create mode 100644 frappe_mcp/tools/__pycache__/workflow_tools.cpython-311.pyc create mode 100644 frappe_mcp/tools/__pycache__/workflow_tools.cpython-314.pyc create mode 100644 frappe_mcp/tools/activity.py create mode 100644 frappe_mcp/tools/admin.py create mode 100644 frappe_mcp/tools/analytics.py create mode 100644 frappe_mcp/tools/bulk_ops.py create mode 100644 frappe_mcp/tools/business_actions.py create mode 100644 frappe_mcp/tools/custom_fields.py create mode 100644 frappe_mcp/tools/dashboards.py create mode 100644 frappe_mcp/tools/doctypes.py create mode 100644 frappe_mcp/tools/document_inspect.py create mode 100644 frappe_mcp/tools/document_lifecycle.py create mode 100644 frappe_mcp/tools/documents.py create mode 100644 frappe_mcp/tools/email_templates.py create mode 100644 frappe_mcp/tools/files.py create mode 100644 frappe_mcp/tools/foundation.py create mode 100644 frappe_mcp/tools/governance.py create mode 100644 frappe_mcp/tools/naming_series.py create mode 100644 frappe_mcp/tools/print_formats.py create mode 100644 frappe_mcp/tools/property_setters.py create mode 100644 frappe_mcp/tools/reports.py create mode 100644 frappe_mcp/tools/scheduler.py create mode 100644 frappe_mcp/tools/scripts.py create mode 100644 frappe_mcp/tools/translations.py create mode 100644 frappe_mcp/tools/users.py create mode 100644 frappe_mcp/tools/webhooks.py create mode 100644 frappe_mcp/tools/workflow_tools.py create mode 100644 pyproject.toml diff --git a/.env b/.env new file mode 100644 index 0000000..4127d6e --- /dev/null +++ b/.env @@ -0,0 +1,47 @@ +# Frappe instance URL (your VPS / Docker host) +# If using a domain: https://erp.yourdomain.com +# If using Docker with port mapping: http://YOUR_VPS_IP:8000 +FRAPPE_URL=http://147.93.40.215:10009/ + +# API credentials — generate in Frappe: Settings > My Account > API Access +FRAPPE_API_KEY=2650fa15dc9393f +FRAPPE_API_SECRET=766ad5af8577685 + +# For multi-site Docker setups (optional) — the site name e.g. "site1.localhost" +FRAPPE_SITE_NAME= + +# Set to true to block all write/delete operations (safe read-only mode) +READ_ONLY_MODE=false + +# HTTP timeout in seconds for API calls +REQUEST_TIMEOUT=30 + +ENABLED_MODULES=foundation,document_inspect,analytics + +# ── Module on/off switches ─────────────────────────────────────────────────── +# Uncomment a line to disable that module (all others stay ON) +# MODULE_FOUNDATION=false +# MODULE_DOCTYPES=false +# MODULE_DOCUMENTS=false +# MODULE_DOCUMENT_INSPECT=false +# MODULE_DOCUMENT_LIFECYCLE=false +# MODULE_CUSTOM_FIELDS=false +# MODULE_SCRIPTS=false +# MODULE_WORKFLOW_TOOLS=false +# MODULE_ANALYTICS=false +# MODULE_REPORTS=false +# MODULE_PRINT_FORMATS=false +# MODULE_BULK_OPS=false +# MODULE_BUSINESS_ACTIONS=false +# MODULE_USERS=false +# MODULE_ADMIN=false +# MODULE_PROPERTY_SETTERS=false +# MODULE_NAMING_SERIES=false +# MODULE_FILES=false +# MODULE_ACTIVITY=false +# MODULE_DASHBOARDS=false +# MODULE_WEBHOOKS=false +# MODULE_EMAIL_TEMPLATES=false +# MODULE_TRANSLATIONS=false +# MODULE_SCHEDULER=false +# MODULE_GOVERNANCE=false diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..363bc4a --- /dev/null +++ b/.env.example @@ -0,0 +1,72 @@ +# ── Frappe Connection ──────────────────────────────────────────────────────── +# URL of your Frappe Docker instance (VPS IP or domain) +FRAPPE_URL=http://YOUR_VPS_IP:8000 + +# API credentials — generate in Frappe: Settings > My Profile > API Access +FRAPPE_API_KEY=your_api_key_here +FRAPPE_API_SECRET=your_api_secret_here + +# For multi-site Docker setups — the site name e.g. "site1.localhost" +FRAPPE_SITE_NAME= + +# ── Safety ─────────────────────────────────────────────────────────────────── +# Set to true to block ALL write/delete operations (read-only audit mode) +READ_ONLY_MODE=false + +# HTTP timeout for API calls (seconds) +REQUEST_TIMEOUT=30 + +# ── Module Activation ──────────────────────────────────────────────────────── +# Option A: Enable ONLY specific modules (comma-separated, all others disabled) +# ENABLED_MODULES=documents,doctypes,users,custom_fields + +# Option B: Disable specific modules (all others stay ON) +# MODULE_SCHEDULER=false +# MODULE_BULK_OPS=false +# MODULE_TRANSLATIONS=false +# MODULE_WEBHOOKS=false + +# Available module keys (run 'frappe-mcp --list-modules' for live status): +# +# LEVEL 1 — Foundation (always recommended ON) +# foundation — ping, session_info, doctype_meta, permissions +# +# LEVEL 2 — Read +# doctypes — DocType CRUD +# documents — Document CRUD (submit, cancel, delete) +# document_inspect — search, children, linked docs, timeline, count +# +# LEVEL 3-4 — Write + Lifecycle +# document_lifecycle — child rows, rename, amend, duplicate, status +# custom_fields — Custom Fields +# scripts — Server Scripts + Client Scripts +# +# LEVEL 5 — Workflow +# workflow_tools — Workflows, transitions, approvals +# +# LEVEL 6 — Reporting +# analytics — Aggregate, dashboard data, PDF render +# reports — Query/Script Report CRUD +# print_formats — Print Format templates +# +# LEVEL 7 — Bulk +# bulk_ops — Bulk create/update/delete/submit/cancel/assign/tag +# +# LEVEL 8 — Business Actions (ERPNext) +# business_actions — Sales, Buying, Stock, HR, Support, Projects shortcuts +# +# LEVEL 9 — Admin / System +# users — Users, Roles, Permissions +# admin — Cache, System Settings, SQL, Workflows +# property_setters — Property Setters + Customize Form +# naming_series — Naming Series +# files — File Manager +# activity — Comments, Tags, Assignments, Error Logs +# dashboards — Dashboards, Charts, Workspaces +# webhooks — Webhooks + API Keys +# email_templates — Email Templates + Notifications +# translations — Translations, Assignment Rules, User Permissions +# scheduler — Scheduled Jobs + Background Jobs +# +# LEVEL 10 — Governance (enable for autonomous AI agents) +# governance — Dry run, validate, risk score, audit log, rollback diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..51d2803 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,16 @@ +{ + "mcpServers": { + "frappe": { + "type": "stdio", + "command": "python", + "args": ["-m", "frappe_mcp.server"], + "cwd": "D:\\2026\\Frappe-MCP", + "env": { + "FRAPPE_URL": "http://147.93.40.215:10009", + "FRAPPE_API_KEY": "2650fa15dc9393f", + "FRAPPE_API_SECRET": "766ad5af8577685" + } + } + } +} + diff --git a/DINE360_ARCHITECTURE.md b/DINE360_ARCHITECTURE.md new file mode 100644 index 0000000..c7b9a60 --- /dev/null +++ b/DINE360_ARCHITECTURE.md @@ -0,0 +1,347 @@ +# Dine360 Architecture Plan +> Generated: 2026-04-20 | Platform: Frappe 15.105.0 + ERPNext 15.104.3 +> Instance: http://147.93.40.215:10009/ + +--- + +## 1. Platform Foundation + +| Layer | Version | Notes | +|-------|---------|-------| +| Frappe Framework | 15.105.0 | Core DocType engine, workflow, notifications, scheduler | +| ERPNext | 15.104.3 | CRM, Accounts, HR modules available for reuse | +| Custom Module | Dine360 | Registered under `frappe` app | + +--- + +## 2. Standard ERPNext DocTypes — Reuse Opportunities + +These exist out of the box and can be linked from Dine360 DocTypes rather than recreating: + +| ERPNext DocType | Dine360 Purpose | How Reused | +|-----------------|-----------------|------------| +| **Customer** | Advertiser billing account | Link from Advertiser → Customer for sales invoices | +| **Supplier** | Restaurant Partner payout | Link from Restaurant Partner → Supplier for purchase invoices | +| **Sales Invoice** | Advertiser billing | Create against Customer when campaign runs | +| **Purchase Invoice** | Restaurant payout | Create against Supplier on settlement | +| **Payment Entry** | Record actual payments | Link from Restaurant Settlement after payout | +| **Address** | Branch physical location | Link from Restaurant Branch → Address | +| **Contact** | Partner/advertiser contacts | Standard Contact linked to Restaurant Partner/Advertiser | +| **Employee** | Internal operations staff | Assign campaigns, escalations | +| **User** | Portal access | All 8 Dine360 roles assigned to Users | +| **File** | Creative asset storage | Ad Creative links to File for media uploads | +| **Communication** | Email/SMS log | Auto-logged on notification send | +| **Activity Log** | Audit trail | Auto-logged on all document changes | +| **ToDo** | Onboarding task reminders | Auto-created from Onboarding Checklist workflow | + +--- + +## 3. Custom Dine360 DocTypes (19 Total) + +### 3a. Child Tables (4) + +| DocType | Parent | Purpose | +|---------|--------|---------| +| **Screen Group Member** | Screen Group | Lists Screen Devices belonging to a group | +| **Campaign Creative Item** | Ad Campaign | Creative assets (video/image) with duration per campaign | +| **Campaign Target Item** | Ad Campaign | Target screen groups and time slots per campaign | +| **Settlement Ledger Item** | Restaurant Settlement | Per-restaurant line items in a batch settlement | + +### 3b. Master / Configuration DocTypes (6) + +| DocType | Submittable | Key Fields | Autoname | +|---------|-------------|------------|----------| +| **Restaurant Partner** | No | partner_name, contact_email, status (Active/Inactive/Suspended), linked_supplier | `field:partner_name` | +| **Advertiser** | No | advertiser_name, contact_email, industry, linked_customer | `field:advertiser_name` | +| **Restaurant Branch** | No | branch_name, restaurant_partner (Link), city, address, is_active | `BRANCH-.#####` | +| **Ad Creative** | No | creative_name, advertiser (Link), media_type (Video/Image/HTML), file_url, duration_seconds, status (Draft/Approved/Rejected) | `CRTV-.YYYY.-.#####` | +| **Screen Device** | No | device_name, restaurant_branch (Link), screen_group (Link), device_serial, status (Active/Inactive/Maintenance), last_heartbeat | `SCRN-.#####` | +| **Screen Group** | No | group_name, description, screen_group_members (child table) | `field:group_name` | + +### 3c. Transaction DocTypes (9) + +| DocType | Submittable | Key Fields | Autoname | +|---------|-------------|------------|----------| +| **Ad Campaign** | **Yes** | campaign_name, advertiser (Link), start_date, end_date, budget, status, campaign_creative_items (child), campaign_target_items (child) | `CAMP-.YYYY.-.#####` | +| **Revenue Rule** | No | rule_name, advertiser (Link), restaurant_partner (Link), revenue_split_percent, effective_from, effective_to | `RULE-.YYYY.-.#####` | +| **API Sync Log** | No | sync_type, sync_status (Success/Failed/Partial), synced_at, records_processed, error_message | `SYNC-.YYYY.-.#####` | +| **Campaign Schedule** | No | ad_campaign (Link), screen_device (Link), scheduled_date, start_time, end_time, status (Pending/Confirmed/Cancelled) | `SCHED-.YYYY.-.#####` | +| **Playback Log** | No | screen_device (Link), ad_campaign (Link), played_at, duration_played, completion_status | `PLAY-.YYYY.-.#####` | +| **Device Heartbeat** | No | screen_device (Link), heartbeat_at, ip_address, cpu_load, memory_used, status (Online/Offline/Warning) | `HB-.YYYY.-.#####` | +| **Revenue Ledger** | No | restaurant_partner (Link), ad_campaign (Link), revenue_rule (Link), period_start, period_end, gross_revenue, platform_share, partner_share, status (Draft/Calculated/Approved) | `RLGR-.YYYY.-.#####` | +| **Restaurant Settlement** | **Yes** | settlement_name, settlement_period_start, settlement_period_end, total_payout, status, settlement_ledger_items (child) | `SETL-.YYYY.-.#####` | +| **Onboarding Checklist** | No | restaurant_partner (Link), assigned_to, current_stage, lead_date, review_date, agreement_date, setup_date, go_live_date | `OB-.YYYY.-.#####` | + +--- + +## 4. Entity Relationship Map + +``` + ┌─────────────────┐ + │ Advertiser │──────────────────────────┐ + └────────┬────────┘ │ + │ 1 │ 1 + ▼ N ▼ N + ┌────────────────┐ ┌────────────────────┐ + │ Ad Creative │ │ Revenue Rule │ + └────────┬───────┘ └────────────────────┘ + │ N │ + ▼ M (via child table) │ + ┌────────────────┐ │ + │ Ad Campaign │─────────────────────────┘ + └────┬───────┬───┘ + │ │ via Campaign Target Items + │ ▼ N + │ ┌─────────────┐ ┌──────────────────┐ + │ │ Screen Group│───────│ Screen Group │ + │ └─────────────┘ │ Member (child) │ + │ │ └──────────────────┘ + │ │ N + │ ▼ 1 + │ ┌─────────────┐ ┌───────────────────┐ + │ │Screen Device│───────│Restaurant Branch │ + │ └──────┬──────┘ └────────┬──────────┘ + │ │ │ N + │ │ 1 ▼ 1 + │ ┌──────┴──────┐ ┌──────────────────────┐ + │ │ Playback │ │ Restaurant Partner │ + │ │ Log │ └────────────┬─────────┘ + │ └─────────────┘ │ 1 + │ ▼ N + │ ┌──────────────────────┐ + └─────────────────────────►│ Revenue Ledger │ + └──────────┬───────────┘ + │ N + ▼ 1 (via child) + ┌──────────────────────┐ + │Restaurant Settlement │ + └──────────────────────┘ + + Device Heartbeat ──► Screen Device + Campaign Schedule ──► Ad Campaign + Screen Device + API Sync Log ──► (standalone audit log) + Onboarding Checklist ──► Restaurant Partner +``` + +--- + +## 5. Role Permission Matrix + +| Role | Masters | Campaigns | Revenue | Devices | Settlement | Onboarding | +|------|---------|-----------|---------|---------|------------|------------| +| **Dine360 Admin** | Full | Full | Full | Full | Full | Full | +| **Campaign Manager** | Read | Full | Read | Read | None | None | +| **Operations Manager** | Read | Read/Write | Read | Full | Read | Full | +| **Finance Manager** | Read | Read | Full | None | Full | None | +| **Support Executive** | Read | Read | None | Read | None | Read/Write | +| **Restaurant Manager** | Own | Own | Own | Own | Own | Own | +| **Restaurant Viewer** | Read-Own | Read-Own | Read-Own | None | Read-Own | None | +| **Device Agent** | None | Read | None | Write-Own | None | None | + +--- + +## 6. Workflow Map + +### 6a. Campaign Approval Workflow (Ad Campaign) + +``` +Draft ──[Submit for Review]──► Pending Approval ──[Approve]──► Approved ──[Schedule]──► Scheduled + │ │ │ + [Reject] [Reject] [Activate] + │ │ │ + ▼ ▼ ▼ + Rejected Rejected Running ──[Complete]──► Completed + │ + [Cancel] + │ + ▼ + Cancelled +``` + +| Transition | From | To | Action | Roles | +|------------|------|----|--------|-------| +| Submit for Review | Draft | Pending Approval | Submit for Review | Campaign Manager | +| Approve | Pending Approval | Approved | Approve | Dine360 Admin, Operations Manager | +| Reject | Pending Approval | Rejected | Reject | Dine360 Admin, Operations Manager | +| Schedule | Approved | Scheduled | Schedule | Campaign Manager, Operations Manager | +| Activate | Scheduled | Running | Activate | Device Agent, Operations Manager | +| Complete | Running | Completed | Complete | Device Agent, Operations Manager | +| Cancel | Running | Cancelled | Cancel | Dine360 Admin, Operations Manager | + +### 6b. Restaurant Onboarding Workflow (Onboarding Checklist) + +``` +Lead ──[Move to Review]──► Review ──[Sign Agreement]──► Agreement ──[Begin Setup]──► Setup ──[Go Live]──► Live +``` + +| Transition | From | To | Action | Roles | +|------------|------|----|--------|-------| +| Move to Review | Lead | Review | Move to Review | Operations Manager, Support Executive | +| Sign Agreement | Review | Agreement | Sign Agreement | Operations Manager, Dine360 Admin | +| Begin Setup | Agreement | Setup | Begin Setup | Operations Manager | +| Go Live | Setup | Live | Go Live | Operations Manager, Dine360 Admin | + +### 6c. Settlement Payout Workflow (Restaurant Settlement) + +``` +Draft ──[Calculate]──► Calculated ──[Approve Payout]──► Approved ──[Mark Paid]──► Paid + │ │ │ + [Hold] [Hold] [Raise Dispute] + │ │ │ + ▼ ▼ ▼ + Held Held Disputed ──[Resolve]──► Paid +``` + +| Transition | From | To | docstatus | Action | Roles | +|------------|------|----|-----------|--------|-------| +| Calculate | Draft | Calculated | 0→0 | Calculate | Finance Manager | +| Approve Payout | Calculated | Approved | 0→1 | Approve Payout | Finance Manager, Dine360 Admin | +| Hold | Calculated | Held | 0→0 | Hold | Finance Manager | +| Hold | Approved | Held | 1→1 | Hold | Finance Manager, Dine360 Admin | +| Mark Paid | Approved | Paid | 1→1 | Mark Paid | Finance Manager | +| Raise Dispute | Paid | Disputed | 1→1 | Raise Dispute | Restaurant Manager, Finance Manager | +| Resolve Dispute | Disputed | Paid | 1→1 | Resolve Dispute | Finance Manager, Dine360 Admin | + +--- + +## 7. Naming Series Reference + +| DocType | Series | Example | +|---------|--------|---------| +| Restaurant Branch | `BRANCH-.#####` | BRANCH-00001 | +| Ad Creative | `CRTV-.YYYY.-.#####` | CRTV-2026-00001 | +| Screen Device | `SCRN-.#####` | SCRN-00001 | +| Ad Campaign | `CAMP-.YYYY.-.#####` | CAMP-2026-00001 | +| Revenue Rule | `RULE-.YYYY.-.#####` | RULE-2026-00001 | +| API Sync Log | `SYNC-.YYYY.-.#####` | SYNC-2026-00001 | +| Campaign Schedule | `SCHED-.YYYY.-.#####` | SCHED-2026-00001 | +| Playback Log | `PLAY-.YYYY.-.#####` | PLAY-2026-00001 | +| Device Heartbeat | `HB-.YYYY.-.#####` | HB-2026-00001 | +| Revenue Ledger | `RLGR-.YYYY.-.#####` | RLGR-2026-00001 | +| Restaurant Settlement | `SETL-.YYYY.-.#####` | SETL-2026-00001 | +| Onboarding Checklist | `OB-.YYYY.-.#####` | OB-2026-00001 | +| Restaurant Partner | `field:partner_name` | Metatron Cubes | +| Advertiser | `field:advertiser_name` | Coca-Cola India | +| Screen Group | `field:group_name` | Zone A Screens | + +--- + +## 8. Status Values Reference + +| DocType | Status Field | Allowed Values | +|---------|-------------|----------------| +| Restaurant Partner | status | Active, Inactive, Suspended | +| Ad Creative | status | Draft, Approved, Rejected | +| Screen Device | status | Active, Inactive, Maintenance | +| Campaign Schedule | status | Pending, Confirmed, Cancelled | +| Playback Log | completion_status | Completed, Partial, Failed | +| Device Heartbeat | status | Online, Offline, Warning | +| Revenue Ledger | status | Draft, Calculated, Approved | +| API Sync Log | sync_status | Success, Failed, Partial | + +--- + +## 9. Notification Map (5 Active Notifications) + +| Notification | DocType | Trigger | Recipients | +|-------------|---------|---------|-----------| +| Campaign Submitted for Review | Ad Campaign | workflow_state → Pending Approval | Dine360 Admin, Operations Manager | +| Campaign Approved | Ad Campaign | workflow_state → Approved | Campaign Manager | +| Campaign Rejected | Ad Campaign | workflow_state → Rejected | Campaign Manager | +| Settlement Approved for Payout | Restaurant Settlement | workflow_state → Approved | Finance Manager | +| Campaign Ending Soon | Ad Campaign | end_date within 3 days | Campaign Manager | + +--- + +## 10. Dashboard & Reporting Map + +### 10a. Existing Dashboard Charts (6) + +| Chart | Type | DocType | X-Axis | Y-Axis | +|-------|------|---------|--------|--------| +| Campaigns by Status | Donut | Ad Campaign | — | status (count) | +| Active Screen Devices | Donut | Screen Device | — | status (count) | +| Restaurant Partners by Status | Donut | Restaurant Partner | — | status (count) | +| Monthly Revenue Ledger | Bar | Revenue Ledger | period_start (monthly) | partner_share (sum) | +| Settlements Over Time | Bar | Restaurant Settlement | settlement_period_start (monthly) | total_payout (sum) | +| Playback Log Volume | Line | Playback Log | played_at (daily) | name (count) | + +### 10b. Recommended Future Reports (Phase 2) + +| Report | Type | Purpose | +|--------|------|---------| +| Campaign Performance Summary | Query Report | Impressions, completion rate, revenue per campaign | +| Revenue Split Breakdown | Script Report | Per-partner revenue vs platform share over period | +| Device Uptime Report | Query Report | Heartbeat-based uptime % per device/branch | +| Settlement Audit Trail | Query Report | Full ledger→settlement→payment chain | +| Advertiser Billing Statement | Script Report | Invoice-ready summary for advertiser | +| Onboarding Pipeline | Query Report | Checklist stage distribution + SLA | + +--- + +## 11. Server-Side Automation Map (Phase 2) + +| Automation | Type | Trigger | Action | +|------------|------|---------|--------| +| Revenue Split Calculator | Server Script | On Playback Log save | Calculate partner_share in Revenue Ledger | +| Device Offline Alert | Scheduled Job | Every 5 min | Flag Device Heartbeat > 10 min old as Offline | +| Campaign Auto-Complete | Scheduled Job | Daily | Set Running campaigns past end_date → Completed | +| Settlement Auto-Batch | Scheduled Job | Monthly | Batch Calculated Revenue Ledgers → new Settlement | +| Creative Auto-Expire | Scheduled Job | Daily | Flag Ad Creatives not updated in 90 days | +| Device Sync API | API Endpoint | REST POST | Accept heartbeat + playback data from devices | + +--- + +## 12. Phase 2 Implementation Order + +All 19 DocTypes, 3 workflows, 5 notifications, 6 charts, and 1 workspace are **already live**. +Phase 2 items in recommended build order: + +``` +Step 1 — Server Scripts (business logic) + 1.1 Revenue split auto-calculation on Playback Log save + 1.2 Settlement batch-creation script (aggregate Revenue Ledger → Settlement) + 1.3 Campaign status auto-complete (scheduled) + 1.4 Device offline detection (scheduled heartbeat check) + +Step 2 — Query / Script Reports + 2.1 Campaign Performance Summary (Query Report) + 2.2 Revenue Split Breakdown (Script Report) + 2.3 Device Uptime Report (Query Report) + 2.4 Settlement Audit Trail (Query Report) + +Step 3 — Print Formats + 3.1 Settlement Payout Slip (Restaurant Settlement) + 3.2 Advertiser Campaign Summary (Ad Campaign) + +Step 4 — Portal / Role Permissions + 4.1 Tighten user permissions per Role Permission Matrix (Section 5) + 4.2 Configure Restaurant Manager portal view + 4.3 Configure Advertiser self-service portal (if needed) + +Step 5 — API Endpoints + 5.1 Device heartbeat sync endpoint (POST /api/method/dine360.heartbeat) + 5.2 Playback log ingestion endpoint (POST /api/method/dine360.playback) + 5.3 Campaign schedule fetch endpoint (GET /api/method/dine360.schedule) + +Step 6 — Integration + 6.1 Link Restaurant Partner → Supplier (for Purchase Invoice on settlement) + 6.2 Link Advertiser → Customer (for Sales Invoice on campaign billing) + 6.3 Auto-create Payment Entry on Settlement marked Paid +``` + +--- + +## 13. Key Design Decisions & Constraints + +1. **Dine360 is a custom module** on the `frappe` app — not a separate installable app. Upgrade-safe as long as customizations live in DocType/Server Script/Client Script records. + +2. **Submittable DocTypes**: Only `Ad Campaign` and `Restaurant Settlement` are submittable. Revenue Ledger uses a status field (not docstatus) to allow edits through the approval cycle. + +3. **Workflow docstatus rule**: Frappe forbids transitions from submitted (docstatus=1) → draft (docstatus=0). All dispute/hold states in the Settlement workflow are docstatus=1. + +4. **ERPNext accounting integration** is optional but available: Settlement → Purchase Invoice → Payment Entry chain uses native ERPNext accounting. Disable if a simpler ledger-only approach is preferred. + +5. **Device authentication**: Screen Devices should authenticate via API key (generate one API key per device via `frappe_generate_api_key`). The Device Agent role limits read access to only Campaign Schedule. + +6. **Revenue Rule priority**: If multiple Revenue Rules match an Advertiser + Partner pair, use the most recently effective rule. Overlap validation should be enforced via server script. diff --git a/Frappe-MCP.rar b/Frappe-MCP.rar new file mode 100644 index 0000000000000000000000000000000000000000..e70f8f70d40bb55afa60ea7d71bcc6ba0f460d5c GIT binary patch literal 282257 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 tXX>YWXT>l%*Vq*RknDIa&b;S3o-BlnOxcfT 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 + ls -1 apps > sites/apps.txt; + bench set-config -g db_host db; + bench set-config -gp db_port 3306; + bench set-config -g redis_cache "redis://redis-cache:6379"; + bench set-config -g redis_queue "redis://redis-queue:6379"; + bench set-config -g redis_socketio "redis://redis-queue:6379"; + bench set-config -gp socketio_port 9000; + environment: + DB_HOST: db + DB_PORT: "3306" + REDIS_CACHE: redis://redis-cache:6379 + REDIS_QUEUE: redis://redis-queue:6379 + SOCKETIO_PORT: "9000" + volumes: + - sites:/home/frappe/frappe-bench/sites + networks: + - frappe-net + depends_on: + - db + + # ── Site creator (runs once to create site + install ERPNext) ─────────────── + create-site: + image: docker.io/frappe/erpnext:${ERPNEXT_VERSION:-version-15} + restart: "no" + volumes: + - sites:/home/frappe/frappe-bench/sites + - logs:/home/frappe/frappe-bench/logs + entrypoint: ["bash", "-c"] + command: + - > + wait-for-it -t 120 db:3306; + wait-for-it -t 120 redis-cache:6379; + wait-for-it -t 120 redis-queue:6379; + export start=`date +%s`; + until [[ -n `grep -hs ^ sites/common_site_config.json | python -c "import sys, json; cfg = json.load(sys.stdin); print(cfg.get('db_host', ''))"` ]]; do + echo "Waiting for configurator to finish..."; + sleep 5; + if (( `date +%s`-start > 120 )); then echo "Timed out"; break; fi + done; + echo "Starting site creation..."; + bench new-site ${SITE_NAME:-erp.localhost} \ + --no-mariadb-socket \ + --db-root-password=${DB_ROOT_PASSWORD:-changeme123} \ + --admin-password=${ADMIN_PASSWORD:-admin123} \ + --install-app erpnext; + bench --site ${SITE_NAME:-erp.localhost} set-config allow_cors 1; + echo "Site created successfully."; + environment: + DB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-changeme123} + ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123} + SITE_NAME: ${SITE_NAME:-erp.localhost} + networks: + - frappe-net + depends_on: + configurator: + condition: service_completed_successfully + + # ── Main backend ───────────────────────────────────────────────────────────── + backend: + image: docker.io/frappe/erpnext:${ERPNEXT_VERSION:-version-15} + restart: unless-stopped + volumes: + - sites:/home/frappe/frappe-bench/sites + - logs:/home/frappe/frappe-bench/logs + networks: + - frappe-net + depends_on: + create-site: + condition: service_completed_successfully + + # ── WebSocket server ───────────────────────────────────────────────────────── + websocket: + image: docker.io/frappe/erpnext:${ERPNEXT_VERSION:-version-15} + restart: unless-stopped + command: ["node", "/home/frappe/frappe-bench/apps/frappe/socketio.js"] + volumes: + - sites:/home/frappe/frappe-bench/sites + - logs:/home/frappe/frappe-bench/logs + networks: + - frappe-net + depends_on: + create-site: + condition: service_completed_successfully + + # ── Queue workers ───────────────────────────────────────────────────────────── + queue-short: + image: docker.io/frappe/erpnext:${ERPNEXT_VERSION:-version-15} + restart: unless-stopped + command: ["bench", "worker", "--queue", "short,default"] + volumes: + - sites:/home/frappe/frappe-bench/sites + - logs:/home/frappe/frappe-bench/logs + networks: + - frappe-net + depends_on: + create-site: + condition: service_completed_successfully + + queue-long: + image: docker.io/frappe/erpnext:${ERPNEXT_VERSION:-version-15} + restart: unless-stopped + command: ["bench", "worker", "--queue", "long,default,short"] + volumes: + - sites:/home/frappe/frappe-bench/sites + - logs:/home/frappe/frappe-bench/logs + networks: + - frappe-net + depends_on: + create-site: + condition: service_completed_successfully + + # ── Scheduler ───────────────────────────────────────────────────────────────── + scheduler: + image: docker.io/frappe/erpnext:${ERPNEXT_VERSION:-version-15} + restart: unless-stopped + command: ["bench", "schedule"] + volumes: + - sites:/home/frappe/frappe-bench/sites + - logs:/home/frappe/frappe-bench/logs + networks: + - frappe-net + depends_on: + create-site: + condition: service_completed_successfully + + # ── Nginx (reverse proxy + static files) ───────────────────────────────────── + frontend: + image: docker.io/frappe/erpnext:${ERPNEXT_VERSION:-version-15} + restart: unless-stopped + command: ["nginx-entrypoint.sh"] + environment: + BACKEND: backend:8000 + SOCKETIO: websocket:9000 + FRAPPE_SITE_NAME_HEADER: ${SITE_NAME:-erp.localhost} + UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1 + UPSTREAM_REAL_IP_HEADER: X-Forwarded-For + UPSTREAM_REAL_IP_RECURSIVE: "off" + PROXY_READ_TIMEOUT: "120" + CLIENT_MAX_BODY_SIZE: "50m" + volumes: + - sites:/home/frappe/frappe-bench/sites + - logs:/home/frappe/frappe-bench/logs + ports: + - "${HTTP_PORT:-8080}:8080" + networks: + - frappe-net + depends_on: + backend: + condition: service_started + websocket: + condition: service_started + +volumes: + db-data: + redis-cache-data: + redis-queue-data: + sites: + logs: + +networks: + frappe-net: + driver: bridge diff --git a/frappe_mcp/__init__.py b/frappe_mcp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frappe_mcp/__pycache__/__init__.cpython-311.pyc b/frappe_mcp/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52a6f7e93760caab7460953c2be5edb0de0a2748 GIT binary patch literal 150 zcmZ3^%ge<81P>oR$pq1lK?DpiLK&agfQ;!3DGb33nv8xc8H$*I{LdiCUz#pfF-8VP zW-)F>i3J6zy1vc&ryk0@&FAkgB{FKt1RJ$TppiYq4#r#0x R12ZEd;|B&9QN#=s0|0SDA%y?{ literal 0 HcmV?d00001 diff --git a/frappe_mcp/__pycache__/__init__.cpython-314.pyc b/frappe_mcp/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13816760fdc46f538f493a3f90a5230a51ff41fa GIT binary patch literal 140 zcmdPqEWP<3&AOZ#$p^VRLKt=;Y5Q8#2KczG$)vkyYs1sy%F^KVr MnURsPh#ANN0Iv-mg8%>k literal 0 HcmV?d00001 diff --git a/frappe_mcp/__pycache__/audit_store.cpython-311.pyc b/frappe_mcp/__pycache__/audit_store.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cc96b0118fdcea8415e76f9ace2c3c26f201c0f GIT binary patch literal 3995 zcmbVPTWlN06`k1^A1hHSk<`NvWi2VODan-M$Vy`iDkRGmoXCz*xd9?5U|7ydrb2R= z*`;L*6rc|mFbV;64H;5V2VjfTQ4j+)`ct>)hpqnflQamJSU^C~7vet#N(w_?y|WZw zvJA8x?%sRn&U^NrGs7<$8{G)f$N&0AbW=j;?<`YJ@~*M{6pS@Q&@>{La4|fMZOu({ zw&tgK(0ojYi_;=zb%dA{m!_q-W7>fchcr>+=AGqkGp@2#tBFXY`y4`F!COlAG34Ma zrD-?u5tk+tx7I*p&<*f5YVxd`cy5Z*jl@qB%|n`PZ|Vrm1>DHLq%RB}1doAn3kwHEO8vK*QrP=wo+)Xkt!1me5W0JPjw4 zTB#@yRL|=1WK4@|3DejuI%=pNXw-;8-{X4Z%8jHJbU$Znx)i{ca5-#V4RDr|giXzi z#x={y9EeWqr_s+UN_@xWOhucy$Gm=^bsZqr|FJ72~XN(=QfC$2@P zR__Bm@$6SW7DfrV8@-7PVS&5Oe~1<^Mjs*!zqCNy)fGXM%Zme}5NZhmVXEzhc`3Mq z>W}OXYS__R@~IcbgNp(L%kk(pu0S^^6j%BX{~)zAhI z77p)-DRcJt55B~fQ%gj20^z=c-Be+HG+2BzVM0sjgt8E__=LVd*+;balo_#HQA3~A zX*_IN@@@d1zc6{u5_FioB`(mYsaXPxUP~mYc+v<+lu^mvX+W4V=vsKzV3DBOX{Ueu z^vuZQskq0Q5sX}AMhDOMSe=vE&a>w+!P&i@&{OIe@NfW%rzd*~n4PUKxDiU^B+ z4@V15*Uue4bKDhH-Rna+r7N#=Wv=9uK+f5lclKtTy+t1C*_CbaEo=526II`S){7S} z_H%zNH{b4A8O{oAMhh%`6ZMU~&2PRfjGb|8j$n|_PlDSLW6^jNzM0J=z=x(rBNY;d z3Kfd5Y>F_!RZf`-DU>}-*F)9d=Fk+HLrYv;%i0ktjoESZU=LIPmVBBYM;{qWLR!dF zsHD>9r4d;@1Fv+rkXdt7Rq19gYytL|b^xJ>`%@1Qh*%HbYnbAd3u!LH_U5IO7WY!X zN~jyvQD23WdJyFq^jDG#s6+%VIH=jLe#BQ3hUu+Npq8f6T>UKSa|ESPEp-CXNS7>3 zsSkRijO{3k8OjzIkm+GimZV*e8m2)zSw=KW8ckBwwqtsj7#2ULnU=)P#RRdM*!emR zNG94jN_A=pF=%W#DLar2jR5$MLR)%_b>P@Nt!X#QLCu6ld%>(B0NTTf;9v`r(m5Cc zh|^QxWYaMwAQG3+mfZSGKKxWZoReS6%das|v=o|}Z@CH{WqD%D<=YpVvgikF`us)Y zc9q1f@q)K)?ev|~zZhN}wlmOGV@E-0FLF|&f6Ldt`tG`!^Y!F?J-2be+X=`dJ@h!8yt7NMMdn}t3%8jaJ-?yU%<*swRtu&RK+?AKRGDDej znW4Pgn-zMEQP|Kw9U2?qpR@(X4hfqg276QTGy9-zA8Hy4IyU<_=0A>^9psrkBAn$M ze-Sar2y{|OjK>*px1Ikdg%CW2{tNnm8^Hsi9ymZB$gF`W5pP21kMAL=dT=b^{}Fo!fBiV&&#QP zpx?C;^Z{Cf%f-q;wuJ%iIqMy+ptK(}+(5bD2|5U-!DgbWHp+Hf!*g0fyPl*Yi(QeJ z7N%9i1&3qox^0XED|HwKjR6qfz;794y@T+q_hig37Jsw&>-6Vo$le|;xohP%G(iE; z<`&uqc8OScxU^bQDgh+$Nq{Rcf*8?LaJ8fz{?;w?=va|CqGJD3Nl4Ca-=TOvE0PXSJM1v{KivBSB>S1qktyNC1MTK^Q9 zo(Zi_h1Q(Vo)_A)LVH10mM4v7nEj&zqn-SdPGPjm@#J+3683?GFlEOE*l#e~X66>l z5egAK5(<@=n9~i)ZjY8D3I8x+(VtKjLH2d6bW?L^iEB{`x1u2P+qBLDlUo9bMojyv zMB7;j+ew>U?S1_c(KF1^17gQH9M?%Irj5`E$g+4cWRS2u7#BGn3q{0)8{vj!nf(gL zwQT<4 z&y2Ti#Dj%+C=ZCVL`nn|qynkC0wF+1@US3ydDzDUx0`fYR4yw--%_PgpilhgjK_A< z(iOiw=Rf!P&p-e7%|CnnjXnhO?ms`*Z-@x}gEU-$HkFNEf+`^uS?DxUnQ_KqsAese zYR=+7v*Y{;!4kU29By1ZAzI=IkLBq??+D%K9X1xI>{h2@!MaAJs`9EZ#5#>3#?h8f zZBWHlq9e}2z#kU&XJL=Z4fOnsvO8~B${0=+3Ys$%i7F#Tu8`GoTHZ1%L%U2Ru3=M$y+@7o z^o4>J^-V<>n~$Zevk}(zW>S`B={e2zl5*C_Yjz{tEjj$v(edfzY2vYp*ARKK99$dIP?S&{on~ME4ni5|>p)*&&N3>KfDuPjHc|09g6CJ) z&)3z3T75tOFN+V!8e1anOXgExu$00xMS51-k2SNHwUTBrmrLObHjj1leOttuRm6EX zC1+aL<}-TQf}@LP44g|@Cb7N8?i)Rn*f+TEK%(MGID4XY-~>Hzl0MI9;ey?eOs4YS zcyL-WiJM`ECb-O3p(vqE)WEboBE^;7b)tT;ovK?LRkvfO9Q|&ZPTGJ)D9w|yA`PIy zWBkUaU>l%-0Z+LeMm3+ss@`)KBGiqBDh-wE6Kef#TsNS7tjZ^TGhJ?aiofIM)ky5)!X-SZKf*4xdE}jC;V<-jQDShs6JIz8@^OI z;bA7p6xkw|tnObB;==Q=oCvua#ai52O?RU>?^>@m_SFWE+uMayKlu%jC@Ml)bx2h!Ot)CqIRZ~B?}hq+FqGm)m*fnfk?J_7*PDg@!sLR3*{gqjrh zPMY_4dfZAmu`2N$+n!T#9z;V}$P{x0Ga_OVEHzUc6999B@NDLcNg`|~h@xOZ$hI$Q%p_B33%CeJ zpo1dkH&7{|brDI8bNu|NE1{pX|FHc>uPj-2+V_^jeaoT#Wq)+d8=QZ8A#pkJv)$MF zuJ)C~1FPYKE8&CX@ZmMDe@=q&xv#Hz+ZLlAh3>oz)BSV&dZWiLmc||jkT1L{wXaC+ zi@hItmZkPpY4El*_^aVHuk?M-cRlm`g730#@jyAyvow7t5V`H$v&kXftACZ7=eB?E z$THu$DM0fxX|UGw#3V?)A@`3+!c9pY>6V@%Lk|oRA8)AiaQ_z>Rsou*0<=B|2RNWY z9+NxRLRQb|mMv38gcpdVVNC3T@lH^fpw#BGI&dF>T!g{YapI)hC%-Lw|UW4&awyX1bn)NF>k`uT$cxQFn~=ZV{I%s!)PC9Vm)= z;VQ6oYwKvt-9n|L_yT(1;#>fYX$TegI3FW^+9K}V$@tS2On}#3`2q^8Cla6{#@!*) zPR{VP@bjoqGSnwUXILfeokkYA0Q64i7j5q6iy zS$CbQRUl&q{HoNS>N&}gh%(^o4j{2kG_iz`jvVTGG2u;)R0vMA8H~XJ2ot);gnw-` z6_IfdsfpTo2tgC~l7;}ms24B^HHcp$l`(B@Mzd^@q%rx7-9*x%QJ|2Fb_`{hj^sCAf$~V zd=3$-Ts~;pVhSFEK^l!x!;*?}*p9F{dh3ozD@Ea1?I0!v9mo?6TM~NqCN5pS?lF5vbhHnf?m8@Z6 z!dSMa0H1AH{hOGahbD4P4(HMGAL3E literal 0 HcmV?d00001 diff --git a/frappe_mcp/__pycache__/config.cpython-311.pyc b/frappe_mcp/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..678fd2ba123ebbb0308599bd58a90092c09f9f7a GIT binary patch literal 1605 zcmZux&2Jk;6rb6hwb!u|+ie=4az0uriYxM=9#9ok9B2s=f`AmEN{gk@c4r)~Sns;C zq>6pXY3MfjhME!FWi$$dUD}Kof3^UwtD@ld+B}w2`b{3SrNTQZ z0~@qO=)uJ}X}X}gAH4(Y06oA@VElh$crq^#ow0e0SD|zn5li8>s)jPXAQmN99P1O7 zmX5F9UawtWyZ&x%BaL$TlUtwFM*cUwR?UmTy1%{B+EugZ7E}t?58#r}IB?^}-uyUw zE3Y!2(+AB0>!3jfmCE1T|N8#7=A(y?A0~F?2=r)=tnhGW&uYv1@(u9M21M(Az*TuH zQ672`gCjSqWo6Seqm-&Cs#DnxILHM`@3q}P=bBPs!@kM{e^ilDHw+``NyA;x(-)=)whbn2?{4cpa2tPP6Qo>8B9? z3aoSH>(4EBKI)nYSqAEn<^IJ=XR~`NAr+t=sr2U;6LO&&Cj`dD9$D;H77{Ys^%4T( zLXRv!>MECD&FzFN0rkj|)+}^B?QSJx9;ip=f462kv|H<0mw&S^_pQ>vz}EbetpS4J z$KjC5DSK|BOnLtcJ-i9i2LvX;mY1&nawj?e0Z`{-{S8A*qboyD6R%$1p7c)Yn|y%e z$!kPt^2Na|#G6$-OwtvYe402_*yfVPqo0m=3yc?`$GS8f^mqRJqxHw@kehVHX4O{Z z)-H2H>3gS(v=7Q2>xZ?=)!U&bp{TLY|I^{odUr8-{;_>?c!S;(*TAYnh`$FmFfqn` zbS;^rKDv@jQXefQlQhVb@dh5CSM#qPJWnnEp0&S?j675e&vhG696Wc*pgu-^|Bcjm Is!g-;0zp}pga7~l literal 0 HcmV?d00001 diff --git a/frappe_mcp/__pycache__/config.cpython-314.pyc b/frappe_mcp/__pycache__/config.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..255e05a1a9a643638b1b3c0a6258ac0e3f2a2199 GIT binary patch literal 2048 zcmZuy&2Jk;6rb6ztYgO?jg!zeA8qz{f zRZ@Xu0TPrRK!^iJIQBp2kt4@MT*63|sOW*-;-*r2)`>%}ktHhW3}Wq?wxmlv!TL30$&}^{86zi&V@wdo zT%#A^JRH(mGaUen{LckxRidl5d)vA`8=qyTW;W2 zT^R`wr2^4pp6k`Q*F7YetS)o#EdUn`T_ch@L~84n)W=B#W=q4-qzRt|-;kqAdwp7_ z)`rp8i?p+UExk%EP{#x#Z8Y~^Q~M@oVE2dvhC3-|*hxE?vM#g9t`XcNb52&~SBWe* zIdCnn?4Ccj$Ldlx7R%)z^4y>v#d7h|!oq?gSJiM$xQzy9O%Zr#N!J=;0LB0woCR`) zw#X`3*DifUNEM`$E(qrpHp0G-cHyvFS=R z!k8Dhaa{F1$yki~vn}=F+l!U+3+G?0TuqFbd+)95m9AZzUZdhgVa?x|Z)~d!V{RBm z(v_SsHP-WQeo$P5Gvnhxp0~&|Ld~f*l3!N#ak{sXa0jqVJiNU(&{z2EgVO-OTY9}0 ziJk_`4(VkJyrqhkfmi}O^c&!#?gg>PfLh|#*|VYmSQV3HCWY$n^Z~9@& zNAM6NEwXQseBryTJ6k{LcR$~;Ck48vY#!cXH9z3Djd_5o(Bl~oy{HQJ+%}uCHg`#x zd<(8tmcJ23f*+pZ5RN=N#btuYy{tJD%b<(30jv*Tql+o%n_a8{wgz;D5JT{6kp>`A zKvcGyYPaFDPx-bg4Dd1cpp;a;D~f$ceCTd+l@;8rvM3C;*=AJbDku2OCXXeP{w9x_ zQssE)-UxUVmzx2POS;J7ZN`l#3Y6}LvZRR|8j%Omt;8f{0wXU*fmpaw0rLE7@&)-L zKe}towRJ-2!{WrQG1)c&wk`6)@m*u4J%o%+CXVeI)9n;8Su%EX*ErG6AyXvdz)ZEr zkU8C9o}2+D$>=?@Tsop`mN*BcJnSR8@L>U#6!(NG4U{BEQz5w0Y-|G zNM@0oLV}en3?M3<^xP(w^{6V2qWBDoGZ?vmb=pf62AZhW3_WZxXi?}j-(#+XBEQj; zJO(jn_M6z_LBLpvcA|(q^F}9#LwF66B#1r$2x5!ujnB2-**iAbTHc$Q*)hiUCZ~6d zB2@Y0$<{JX)2;U(n#J!cznimrX8xH5O?#jdtAOOxz5(pMooM}8oc`I`Id}Q~hre$8 zwzV_*QS04jC5Y#jB#CHp_kZdg+a24y*W!mfXqfBHC{w6Xty85VAi77B1HnSzto29VnsE@dNTvVW^qzZsVwS}PSQY1xVB>e5 zl*|T~EbjiR#48}5f-lBDX^T8ckKSE`z$W_3C0i95+f_HjB=)yp6qMbQcWNYsIhW8I zvA`d1;%##F|8KXibUNyF@fzr%+3^C9eVtPJfV}j8oPI!No?1ovG5zMVrv&Hyd7`DC PY7h>{qLzMwD6#J!rh?p_ literal 0 HcmV?d00001 diff --git a/frappe_mcp/__pycache__/healthcheck.cpython-314.pyc b/frappe_mcp/__pycache__/healthcheck.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36b8235a5c9650668fbc4de7bdb2097666a842af GIT binary patch literal 4635 zcmbtYOHdoh8Sd7Mo(LfULSP9qJPc?7fqA<8$eNcd8I1AZ;7Tl`lBEGA7LsaaWLr99 zRhz8}$)+}TRjN!?a_}Js=jJ2Ft=L{;*n}pdA_>hP7W=V;l~yBZpw;*( zA1%>7t)#6ZyF#5?seZIKgyuNSr8jgyXRo7nb;V%rwmj{4CFjj7q!BOR07y zl$7S7jzt)+BF)_91QGa|o7`+%;22Q~2@)ThW2AYGximbilY$~A5d~rx1AOw3LR>__v(p4s22GvO`!lY7* z4mv-9XQv8!4X6$T1L`bMZ-FZTn;?M$WX4Kiac_aA4i!`Xuxe{gSxwl`hPPp5 zji5vMkW}e>`&Ry950$DidtTNQ`O?Ieu!uDe6ntKXoM>7zS*FVDw`{38y9~6(`_0UOelgS6$sV z5m;GQ@pnZwDQdW&o^N6x{i+TU;4dbVC&k;CW@tU<%Rr6M~;yKZ`KcSkITP&Vz$ zPQ!^+B!A-Ih#rmN@h%1h%2k9q6mb!432l*7-+-$39r0aP^&G%=7Vy2>yoZm}YdoYE&cj3N(s+1Xiq^z= zSZ5!_V|UBd^l?)@ASp4lqk|dVi_FYbh}7vahWS1_&J@aNh7cf`^P$)r7mUW|JS5l` z1jPvm%SzOh^#ziwPsVP=;&)q%VqL05Hczf=NhE(iMtmV5Z`*aTNR_GaKTgM8M16RT4mem4p;BbTfXNJDeechK_UpPjOoW zlGe!Dgup}kXv!~XFeyZ3OQ9c1@WESLO12i>MeuEqtc+|q=ubgO4okwJXf(LMN%QfD zOu>E>Nc5MDqj$qxf@Hz6foxK;<}wxKVzQpRM&e<&K{lv)s;tYqN!Eq)d96X+)O!$t zAY)FZ$!;SVsR&<}NK&D~=gSBrr;)9Yng{b4XHapd-~qCdVLk)AZy;6NDsz7P zj`~7hswjDX({O&zoJ{;1I8}aW=gETZx$|`+T=t(2?RnSkG z`k?=`#sc)Gt(E>}^z)zQx#Wu)KS0rZs$44PM}O8gA9Qg8JQP%9^Ogg#&f$_pBkT5I(?sTX!a zf8o>;3PX{8BL?d)noBNrQ7=wfFY2jPOn6rHW}vKAVp7|!7u%@S6AHaeODJ7LcC`ls z!qxtgF&DLltz!;q%|>|E99lwg5uUX=3^>;sO2&26T8CvEQ)_1k&*xYRl+Sg9Ck!XS zULnUIIjMF&fhvRcZ@!a-TgDzIbm~z+tG=r@A2q*BpupbU@2I<3J>0bSvTO*uF8Jd? zm4$m6WUVtrH&f+r2hAH|Y{IRT^&v483-fWAf^$td%4AARi89UI;VEP?W ztmIE^!&wN>$td?d;S3CsYr5D36>N8mztm8e-Z3Fu_9tZe0*(F|z4s3t`IlhVr_tbA HMazExznA#P literal 0 HcmV?d00001 diff --git a/frappe_mcp/__pycache__/module_registry.cpython-311.pyc b/frappe_mcp/__pycache__/module_registry.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f158e91cdb95cfab6684dd890990da01469c7e24 GIT binary patch literal 6654 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 L;QoHNMQucK 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/__pycache__/server.cpython-314.pyc b/frappe_mcp/__pycache__/server.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2e6c12793d2799841f2210eba20cb5eab2cd69b GIT binary patch literal 3342 zcma(TOKcm*b!K--EAlpUbqydzo0cr;dl4AnokV6hW^ukNeGN{z{Rz}d)MUk6q8Ayxf(D!D!q~w0m zS)-ynsi9#$gO%ys#Ey_p15?~a@ z#WBf}x{)W2d&az$m*dj7Z%nr2F~8;K|DN%{SkMZNg{)9Fxg_l+ig%DY60C+x4U?U& z*!nnFMHOEQQREhqkGK>o3@f_pGhNK(D}GoJ*s-Ef3APX`qJ#iOFEuF*Cw@vu4o=@e zE=BedcV#!6u!r|}xMaY-fpaWVENZa}uTRF_)Y!bnVt-!#Af_1>TZk14x?v>(7Yeyj zUNd9aj1jx6#dIU5&+EBTCZAt`iJ8lJEq6Gln;ePF>zUZdL~{82$XWHmkIqh=A9)j2 z#1fh@p9oCEsqJ;d02VT>oL*2(E;cUM(#1j{ZwD`G*R9tJh6OqRF*>7JYE_DA!35LZ zy`40hF>-m0nTvrd{3QKz*mWa-vs9sJ;5aQU zxY(gK7bD~_*eou5gFM^IG^@l6R5!1i7L$O8r@B&La~aD-sr&5xXHTV%{NTv(v}4_V zux{E>PX+bT&WRJn1v{jw8N(=88B0@D7KX)5(9ISA%j8=^Y4=kEtG%n%M+Z2%1uT+9 z;5Jn27WAzy0Q*QctW3LyuU&i(IVS4#GYU7ryfE)G!Wv;<4FK!_p2RUO+XaLubQ6{N z2KIUq?5G60)}*d~n0VHk!3Mg+zCIvw`znBZpM4dfZec}COUw@p zTQV|pn(fc9nbI71hRHCpyF9c?GOPO}V+fVC4U1?xK(qCebP{v=ni(LQzbK z`>Wg;xbzN^M%8&!AVO0lM2-lG@WZO?1o&V(Nr`(&in@D@LQeV_NV!gz#q*v3DN5V- zNJ%?a`^fpG7^z#@OL843x9HivN|7#lUX)xCIzdv=^O^^82Oo8+lha}+(J8uB++#}? zMD1dD%D8G2t{E{5)Kf7G5^JHTF^r{&Mc)Wx1qNh^q0`tR#4*1w+dpzWs}(K1V8p%b zCE&4UoFKcwZTqSihM!r=TQ=29yokQO7&OO={aMIJ+>6;hVBrqt4FKGU8+$5^FE5Wh z3^%Xj-^t$@tb}9BqhAM`HX`kxG=CAkXWhH9mKa%&oLg%gU6V&Q1<5n?sHN-9LZv1C zAR52zvK~3M)_8nPKK@iBO$Yw|B)I!Ip`IaU8{a+ph56~+dgRPn*&2OO!3IB(#y;WUBthGgVMW@ zx)y8Sf66qbJ>J|(>7EsIl^_dw7NhzvvWBK$(!|s*0-hJ!-o_J-?!AirS+row%T#?_Ee|!1Y(L0%S zIrh-kvU>F66L(Ln`+6SB!;j?lb-A-T>HX+?!3`<+_8T|f_(1xN_YYEwlTJZWt)ctf zg1#00AL;ZoRmGA}5>>&yo>8ih?Wyk?I6R~E;xbuFrZq|AC)@d$fnI3t-h*f+Xpmla z$N*R1UyU#PJHLlOnjM4Q3Ox`b2gxYTlH4TiBLU)u#ti7(L3D8JP6$N&6u63dT4h*= zAz7k2wg}HOb^x)?EYtA4pc}fSXY%@D2AxqY@cd~)&J6-T2aX2AoE^HxbeQb?nV1%; z`I7Ym&H=iBISK$XD&c zRXXSx>lBcwe+ZllmPmxAO~f{3WOd%?yw&|&fcFY=qML|4YxUFC eXRQKF(aivnLbn&!#XXNC`IqOQPDyQCyMF str: + """Append one audit record. Returns the record ID.""" + record = { + "id": f"mcp-{int(time.time() * 1000)}", + "timestamp": datetime.now(timezone.utc).isoformat(), + "tool": tool_name, + "arguments": arguments, + "result_summary": result_summary[:200], + "risk": risk, + } + with _AUDIT_FILE.open("a", encoding="utf-8") as f: + f.write(json.dumps(record) + "\n") + return record["id"] + + +def read_audit_log(limit: int = 50, tool_filter: str = "") -> list[dict]: + if not _AUDIT_FILE.exists(): + return [] + records = [] + with _AUDIT_FILE.open("r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if not line: + continue + try: + r = json.loads(line) + if tool_filter and tool_filter not in r.get("tool", ""): + continue + records.append(r) + except json.JSONDecodeError: + pass + return list(reversed(records))[:limit] + + +def clear_audit_log() -> int: + if not _AUDIT_FILE.exists(): + return 0 + with _AUDIT_FILE.open("r", encoding="utf-8") as f: + count = sum(1 for line in f if line.strip()) + _AUDIT_FILE.unlink() + return count diff --git a/frappe_mcp/client/__init__.py b/frappe_mcp/client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frappe_mcp/client/__pycache__/__init__.cpython-311.pyc b/frappe_mcp/client/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d2a635ce24c9ffe6864cc5c5d599813cfe7cf42 GIT binary patch literal 157 zcmZ3^%ge<81dks+$pq6N0tOhNjL&R9#&m`hhF}IwM!%H|MNB~cXOQGC0~f0pBLgF| z7`LLtf`U|CU*~|BG%zzhH@P4tIVUqUuOucuJ~J<~BtBlRpz;@oO>TZlX-=wL5i8ID XkQK%JK;i>4BO~Jn1{hJq3={(Zuoon0 literal 0 HcmV?d00001 diff --git a/frappe_mcp/client/__pycache__/__init__.cpython-314.pyc b/frappe_mcp/client/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42dc6d5029c1e383eba3d4e8232cf8adcbf1b75d GIT binary patch literal 147 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/client/__pycache__/frappe_api.cpython-314.pyc b/frappe_mcp/client/__pycache__/frappe_api.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d4223e6d831c9a037a47c443cf02fc8c2607c99 GIT binary patch literal 14533 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/client/frappe_api.py b/frappe_mcp/client/frappe_api.py new file mode 100644 index 0000000..9048468 --- /dev/null +++ b/frappe_mcp/client/frappe_api.py @@ -0,0 +1,144 @@ +""" +Async HTTP client for Frappe REST API. +Handles API key/secret auth — works with any remote Frappe instance including Docker. +""" + +import json +import httpx +from typing import Any +from frappe_mcp.config import get_settings + + +class FrappeAPIError(Exception): + def __init__(self, message: str, status_code: int = 0, exc_type: str = ""): + super().__init__(message) + self.status_code = status_code + self.exc_type = exc_type + + +class FrappeClient: + def __init__(self): + self.settings = get_settings() + self.base_url = self.settings.frappe_url + self._auth_header = self._build_auth_header() + + def _build_auth_header(self) -> dict[str, str]: + key = self.settings.frappe_api_key + secret = self.settings.frappe_api_secret + if not key or not secret: + raise FrappeAPIError("FRAPPE_API_KEY and FRAPPE_API_SECRET must be set in .env") + return {"Authorization": f"token {key}:{secret}"} + + def _headers(self, extra: dict | None = None) -> dict[str, str]: + h = {**self._auth_header, "Content-Type": "application/json", "Accept": "application/json"} + if self.settings.frappe_site_name: + h["X-Frappe-Site-Name"] = self.settings.frappe_site_name + if extra: + h.update(extra) + return h + + def _url(self, path: str) -> str: + return f"{self.base_url}{path}" + + def _raise_for_frappe_error(self, data: dict) -> None: + if "exc_type" in data or ("message" in data and data.get("exc_type")): + raise FrappeAPIError( + data.get("message", "Unknown Frappe error"), + exc_type=data.get("exc_type", ""), + ) + + async def get(self, path: str, params: dict | None = None) -> Any: + async with httpx.AsyncClient(timeout=self.settings.request_timeout) as client: + resp = await client.get(self._url(path), headers=self._headers(), params=params) + resp.raise_for_status() + data = resp.json() + self._raise_for_frappe_error(data) + return data.get("data", data) + + def _handle_error_response(self, resp: httpx.Response) -> None: + """Extract the real Frappe error message from 4xx/5xx responses.""" + if resp.is_error: + try: + data = resp.json() + # Frappe puts the real error in _server_messages or exc + server_msgs = data.get("_server_messages", "") + exc = data.get("exc", "") + message = data.get("message", "") + if server_msgs: + import json as _json + try: + msgs = _json.loads(server_msgs) + parsed = [_json.loads(m).get("message", m) if isinstance(m, str) else m for m in msgs] + raise FrappeAPIError( + " | ".join(str(p) for p in parsed), + status_code=resp.status_code, + ) + except (ValueError, TypeError): + pass + if exc: + last_line = [l for l in exc.strip().splitlines() if l.strip()] + raise FrappeAPIError(last_line[-1] if last_line else exc, status_code=resp.status_code) + if message: + raise FrappeAPIError(message, status_code=resp.status_code) + except FrappeAPIError: + raise + except Exception: + pass + resp.raise_for_status() + + async def post(self, path: str, payload: dict | None = None) -> Any: + async with httpx.AsyncClient(timeout=self.settings.request_timeout) as client: + resp = await client.post(self._url(path), headers=self._headers(), json=payload or {}) + self._handle_error_response(resp) + data = resp.json() + self._raise_for_frappe_error(data) + return data.get("data", data) + + async def put(self, path: str, payload: dict | None = None) -> Any: + async with httpx.AsyncClient(timeout=self.settings.request_timeout) as client: + resp = await client.put(self._url(path), headers=self._headers(), json=payload or {}) + self._handle_error_response(resp) + data = resp.json() + self._raise_for_frappe_error(data) + return data.get("data", data) + + async def delete(self, path: str) -> Any: + async with httpx.AsyncClient(timeout=self.settings.request_timeout) as client: + resp = await client.delete(self._url(path), headers=self._headers()) + resp.raise_for_status() + data = resp.json() + self._raise_for_frappe_error(data) + return data.get("data", data) + + # --- Frappe-specific convenience methods --- + + async def call_method(self, method: str, **kwargs) -> Any: + """Call a whitelisted Frappe server-side method.""" + return await self.post(f"/api/method/{method}", payload=kwargs) + + async def get_doc(self, doctype: str, name: str) -> dict: + return await self.get(f"/api/resource/{doctype}/{name}") + + async def get_list( + self, + doctype: str, + fields: list[str] | None = None, + filters: list | None = None, + limit: int = 20, + order_by: str = "modified desc", + ) -> list[dict]: + params: dict[str, Any] = {"limit": limit, "order_by": order_by} + if fields: + params["fields"] = json.dumps(fields) + if filters: + params["filters"] = json.dumps(filters) + return await self.get(f"/api/resource/{doctype}", params=params) + + async def create_doc(self, doctype: str, data: dict) -> dict: + return await self.post(f"/api/resource/{doctype}", payload=data) + + async def update_doc(self, doctype: str, name: str, data: dict) -> dict: + return await self.put(f"/api/resource/{doctype}/{name}", payload=data) + + async def delete_doc(self, doctype: str, name: str) -> dict: + return await self.delete(f"/api/resource/{doctype}/{name}") diff --git a/frappe_mcp/config.py b/frappe_mcp/config.py new file mode 100644 index 0000000..aa34c63 --- /dev/null +++ b/frappe_mcp/config.py @@ -0,0 +1,31 @@ +from pydantic_settings import BaseSettings +from pydantic import field_validator +from functools import lru_cache + + +class Settings(BaseSettings): + frappe_url: str = "http://localhost:8000" + frappe_api_key: str = "" + frappe_api_secret: str = "" + frappe_site_name: str = "" # optional: for multi-site Docker setups + + # Safety: set to True to block destructive operations + read_only_mode: bool = False + + # Timeout for REST calls in seconds + request_timeout: int = 30 + + @field_validator("frappe_url") + @classmethod + def strip_trailing_slash(cls, v: str) -> str: + return v.rstrip("/") + + # Module activation — handled by module_registry, stored here so pydantic doesn't reject it + enabled_modules: str = "" + + model_config = {"env_file": ".env", "env_file_encoding": "utf-8", "extra": "ignore"} + + +@lru_cache +def get_settings() -> Settings: + return Settings() diff --git a/frappe_mcp/healthcheck.py b/frappe_mcp/healthcheck.py new file mode 100644 index 0000000..c172336 --- /dev/null +++ b/frappe_mcp/healthcheck.py @@ -0,0 +1,82 @@ +""" +Health check — run with: frappe-mcp --test-connection +Verifies connectivity, auth, and Frappe version before starting the MCP server. +""" + +import asyncio +import sys +from frappe_mcp.client.frappe_api import FrappeClient, FrappeAPIError +from frappe_mcp.config import get_settings + + +async def run_health_check() -> bool: + settings = get_settings() + passed = True + + print("\nFrappe MCP -- Connection Test") + print("=" * 45) + print(f" URL : {settings.frappe_url}") + print(f" API Key : {settings.frappe_api_key[:6]}..." if settings.frappe_api_key else " API Key : (not set)") + print(f" Site Name : {settings.frappe_site_name or '(default)'}") + print(f" Read-only : {settings.read_only_mode}") + print("=" * 45) + + client = FrappeClient() + + # 1. Ping / version check + print("\n[1/4] Checking Frappe version...", end=" ", flush=True) + try: + versions = await client.call_method("frappe.utils.change_log.get_versions") + frappe_ver = versions.get("frappe", {}).get("version", "unknown") + print(f"OK (Frappe {frappe_ver})") + except FrappeAPIError as e: + print(f"FAIL — {e}") + passed = False + except Exception as e: + print(f"FAIL — {e}") + passed = False + + # 2. Auth check — fetch current user + print("[2/4] Verifying API credentials...", end=" ", flush=True) + try: + user = await client.call_method("frappe.auth.get_logged_user") + print(f"OK (logged in as: {user})") + except FrappeAPIError as e: + print(f"FAIL — {e}") + passed = False + except Exception as e: + print(f"FAIL — {e}") + passed = False + + # 3. Read DocType list + print("[3/4] Testing DocType list access...", end=" ", flush=True) + try: + result = await client.get_list("DocType", fields=["name"], limit=1) + print(f"OK (found {len(result)} DocType(s))") + except Exception as e: + print(f"FAIL — {e}") + passed = False + + # 4. Write check (skipped in read-only mode) + if settings.read_only_mode: + print("[4/4] Write check... SKIPPED (read_only_mode=true)") + else: + print("[4/4] Testing write permission (System Settings read)...", end=" ", flush=True) + try: + await client.get_doc("System Settings", "System Settings") + print("OK") + except Exception as e: + print(f"WARN — {e}") + + print("=" * 45) + if passed: + print("PASS All checks passed. MCP server is ready.\n") + else: + print("FAIL Some checks failed. Fix the issues above before starting.\n") + + return passed + + +def main_health_check(): + ok = asyncio.run(run_health_check()) + sys.exit(0 if ok else 1) diff --git a/frappe_mcp/module_registry.py b/frappe_mcp/module_registry.py new file mode 100644 index 0000000..0136c72 --- /dev/null +++ b/frappe_mcp/module_registry.py @@ -0,0 +1,137 @@ +""" +Module Registry — controls which tool modules are active. + +Enable/disable via .env: + ENABLED_MODULES=documents,doctypes,users → whitelist mode + MODULE_SCHEDULER=false → disable one module + MODULE_GOVERNANCE=false → disable another + +Default: all modules enabled. +""" + +import os +from typing import Callable +from mcp.types import Tool + +# ── Import all tool modules ────────────────────────────────────────────────── +from frappe_mcp.tools import ( + foundation, + doctypes, + documents, + document_inspect, + document_lifecycle, + custom_fields, + scripts, + workflow_tools, + users, + admin, + analytics, + print_formats, + email_templates, + property_setters, + naming_series, + bulk_ops, + files, + activity, + dashboards, + webhooks, + reports, + scheduler, + translations, + business_actions, + governance, +) + +# ── Module definitions ─────────────────────────────────────────────────────── +# (module_key, module_object, description) +ALL_MODULES: list[tuple[str, object, str]] = [ + # Level 1 — Foundation + ("foundation", foundation, "L1: ping, session_info, doctype_meta, permissions, modules"), + # Level 2 — Read + ("doctypes", doctypes, "L1-2: DocType CRUD — create, read, update, list"), + ("documents", documents, "L2-3: Document CRUD — create, read, update, delete, submit, cancel"), + ("document_inspect", document_inspect, "L2: Search, children, linked docs, timeline, count, attachments"), + # Level 3-4 — Write + Lifecycle + ("document_lifecycle", document_lifecycle, "L3-4: Child rows, rename, amend, duplicate, status, draft save"), + ("custom_fields", custom_fields, "L2/9: Custom Fields — add, edit, remove, list"), + ("scripts", scripts, "L9: Server Scripts + Client Scripts"), + # Level 5 — Workflow + ("workflow_tools", workflow_tools, "L5: Workflows, transitions, approvals"), + # Level 6 — Reporting + ("analytics", analytics, "L6: Aggregate, dashboard data, PDF render, number cards"), + ("reports", reports, "L6/9: Query/Script Report CRUD"), + ("print_formats", print_formats, "L6: Print Format templates"), + # Level 7 — Bulk + ("bulk_ops", bulk_ops, "L7: Bulk create/update/delete/submit/cancel/assign/tag"), + # Level 8 — Business Actions + ("business_actions", business_actions, "L8: ERPNext shortcuts — Sales, Buying, Stock, HR, Support, Projects"), + # Level 9 — Admin / System Intelligence + ("users", users, "L9: Users, Roles, DocType permissions"), + ("admin", admin, "L9: Cache, system settings, SQL, workflows, scheduler"), + ("property_setters", property_setters, "L9: Property Setters + Customize Form"), + ("naming_series", naming_series, "L9: Naming Series — patterns and counters"), + ("files", files, "L2/9: File Manager — upload, list, delete, move"), + ("activity", activity, "L2/9: Comments, tags, assignments, ToDo, error logs"), + ("dashboards", dashboards, "L6/9: Dashboards, charts, number cards, workspaces"), + ("webhooks", webhooks, "L9: Webhooks + API Keys"), + ("email_templates", email_templates, "L9: Email Templates + Notifications"), + ("translations", translations, "L9: Translations, Assignment Rules, User Permissions"), + ("scheduler", scheduler, "L9: Scheduled Jobs + Background Jobs"), + # Level 10 — Safety & Governance + ("governance", governance, "L10: Dry run, validate, risk score, audit log, rollback"), +] + + +def _is_module_enabled(key: str) -> bool: + """ + Resolution order (first match wins): + 1. MODULE_=false → disabled + 2. MODULE_=true → enabled + 3. ENABLED_MODULES=a,b → whitelist — only listed enabled + 4. Default: enabled + """ + env_key = f"MODULE_{key.upper()}" + explicit = os.environ.get(env_key, "").strip().lower() + if explicit == "false": + return False + if explicit == "true": + return True + + enabled_list = os.environ.get("ENABLED_MODULES", "").strip() + if enabled_list: + return key in [m.strip() for m in enabled_list.split(",")] + + return True + + +def get_enabled_tools() -> list[Tool]: + tools: list[Tool] = [] + for key, module, _ in ALL_MODULES: + if _is_module_enabled(key): + tools.extend(module.tools()) + return tools + + +def get_enabled_handlers() -> dict[str, Callable]: + handlers: dict[str, Callable] = {} + for key, module, _ in ALL_MODULES: + if _is_module_enabled(key): + handlers.update(module.handlers()) + return handlers + + +def print_module_status(): + print("\nFrappe MCP — Module Status") + print("=" * 70) + total_on = 0 + total_all = 0 + for key, module, desc in ALL_MODULES: + enabled = _is_module_enabled(key) + count = len(module.tools()) + total_all += count + if enabled: + total_on += count + status = "ON " if enabled else "OFF" + print(f" [{status}] {key:<22} ({count:>2} tools) {desc}") + print("=" * 70) + print(f" Active: {total_on} tools | Total available: {total_all} tools\n") diff --git a/frappe_mcp/server.py b/frappe_mcp/server.py new file mode 100644 index 0000000..e133397 --- /dev/null +++ b/frappe_mcp/server.py @@ -0,0 +1,103 @@ +""" +Frappe MCP Server — entry point. +Modules can be individually enabled/disabled via ENABLED_MODULES in .env. + +Transports: + (default) stdio — for local Claude Desktop use + --sse HTTP/SSE — for VPS hosting (requires starlette + uvicorn) +""" + +import asyncio +import os +import sys +from mcp.server import Server +from mcp.server.stdio import stdio_server +from mcp.types import Tool, TextContent + +from frappe_mcp.module_registry import get_enabled_tools, get_enabled_handlers + +app = Server("frappe-mcp") + + +@app.list_tools() +async def list_tools() -> list[Tool]: + return get_enabled_tools() + + +@app.call_tool() +async def call_tool(name: str, arguments: dict) -> list[TextContent]: + handlers = get_enabled_handlers() + handler = handlers.get(name) + if not handler: + return [TextContent(type="text", text=f"Unknown tool: {name}")] + try: + result = await handler(arguments) + return [TextContent(type="text", text=result)] + except Exception as e: + return [TextContent(type="text", text=f"Error: {e}")] + + +def main(): + if "--test-connection" in sys.argv: + from frappe_mcp.healthcheck import run_health_check + ok = asyncio.run(run_health_check()) + sys.exit(0 if ok else 1) + if "--list-modules" in sys.argv: + from frappe_mcp.module_registry import print_module_status + print_module_status() + sys.exit(0) + if "--sse" in sys.argv: + _run_sse() + return + asyncio.run(_run_stdio()) + + +async def _run_stdio(): + async with stdio_server() as (read_stream, write_stream): + await app.run(read_stream, write_stream, app.create_initialization_options()) + + +def _run_sse(): + try: + import uvicorn + from starlette.applications import Starlette + from starlette.middleware import Middleware + from starlette.middleware.base import BaseHTTPMiddleware + from starlette.requests import Request + from starlette.responses import Response + from starlette.routing import Mount, Route + from mcp.server.sse import SseServerTransport + except ImportError: + print("SSE dependencies missing. Run: pip install 'frappe-mcp[sse]'", file=sys.stderr) + sys.exit(1) + + host = os.environ.get("MCP_HOST", "0.0.0.0") + port = int(os.environ.get("MCP_PORT", "8001")) + bearer_token = os.environ.get("MCP_BEARER_TOKEN", "") + + sse = SseServerTransport("/messages/") + + async def handle_sse(request: Request) -> Response: + if bearer_token: + auth = request.headers.get("Authorization", "") + if auth != f"Bearer {bearer_token}": + return Response("Unauthorized", status_code=401) + async with sse.connect_sse( + request.scope, request.receive, request._send + ) as streams: + await app.run(streams[0], streams[1], app.create_initialization_options()) + return Response() + + starlette_app = Starlette( + routes=[ + Route("/sse", endpoint=handle_sse), + Mount("/messages/", app=sse.handle_post_message), + ] + ) + + print(f"Frappe MCP SSE server starting on {host}:{port}") + uvicorn.run(starlette_app, host=host, port=port) + + +if __name__ == "__main__": + main() diff --git a/frappe_mcp/tools/__init__.py b/frappe_mcp/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frappe_mcp/tools/__pycache__/__init__.cpython-311.pyc b/frappe_mcp/tools/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..826d0efdf528780388d5947c0c37cbb8ccd7ea32 GIT binary patch literal 156 zcmZ3^%ge<81dkp*$pq1lK?DpiLK&agfQ;!3DGb33nv8xc8H$*I{LdiCU-~XqF-8VP zW-)F>i3J6zy1vc4BO~Jn1{hJq3={(Zha@B! literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/__init__.cpython-314.pyc b/frappe_mcp/tools/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e8b43c04c004d4871ab64037f29e4433a47a94a GIT binary patch literal 146 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 F{{r2h*pdJM literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/activity.cpython-314.pyc b/frappe_mcp/tools/__pycache__/activity.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e03ce2dc3099a3f854d173639a3877a7bfbcdb38 GIT binary patch literal 14193 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/admin.cpython-311.pyc b/frappe_mcp/tools/__pycache__/admin.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8aadef499630aabfc2d4806ab63b4b5a621ff851 GIT binary patch literal 7422 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 R+ASPA8>b`YY9IG~{{vD;?r8u3 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/admin.cpython-314.pyc b/frappe_mcp/tools/__pycache__/admin.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd96f91efc2459e77363f3107056dce63ffe44a3 GIT binary patch literal 8272 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 B1@iy^ literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/bulk_ops.cpython-314.pyc b/frappe_mcp/tools/__pycache__/bulk_ops.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16c5570b635dcd3132661e3d53a58bd8cf025763 GIT binary patch literal 15038 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 L*1{)lL(Kd)-Ea*r literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/business_actions.cpython-311.pyc b/frappe_mcp/tools/__pycache__/business_actions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b9b9deb6634479a96f5142ba5c5b93a83814bd98 GIT binary patch literal 18829 zcmeG^TWlLiazpV=ih5BG%6g4#SrToDmSszpWyg=TY|CCt*7~(x+iM=h8A)7IBt0{< zC9d~Qi@V$-StJX`h=qeMD+Jj+8S4Y|kprBM0LCRj4ncqn4>U3uU;*J01i3#R*8W-K zBUL>Q4o9NJb+SN`%V=2B)zgpauIjGp>K^^n=kri-UHi9xjm=F`)W0C2T{boFLJBY*C@uqRKzXUEbz3(E9R}&tQL&3#clKUYjz0RSqE!v)=HpeDj3K2Y}cF+?}D%s z!fwXJx&Zgg*cdn9UIXp{ywZSs0rwg3O2DfOxDW7Z16~DqjRCI)yw-r%0A6RnYXSEg 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 KKB-WR!2bgiPKs{; literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/custom_fields.cpython-311.pyc b/frappe_mcp/tools/__pycache__/custom_fields.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f03f523d0bc651fe063322a0e77e186022d74476 GIT binary patch literal 5063 zcmcgwU2GKB6`t9do!MRQE?%zz+khD-IA#fUNJuD{zYrS-RO1kANb0(5hrM_0A^XRj z*#vKmWGPXVN<5%860YJ(ZWIv^Jn$IxrBz@1GE}G?T?q-LKWg4K0$zCPId|6HA0XkO z>h<04{+)a7z2}^J?iqg-i$x?zd%pRnwI?A-|G>s->Zny7w9AroRU*=~L}U`k${7!?>eDOnSWPIAJPg^V)W?|kKEoP?XOLXX4-0?(8=IRtMfigPA%uH#|sV*j^&D`!$Tuw#%Lik&-Ig(Wq^zaN9JhegGyvFZ{~8w6usyGKfp?O zyjYkO9hElhFpd(-DKf4en>sNrcvBvN6{ut7Da=6(jh30|Sj7U@CTWglfY-QH_@ELU zoiCVqD`Q}b2dB()Ia*1G!Z=aRIaZBYJK{KIX08(TOXGOnoTWTGY0VZ)r_3nqwwQxG z=x>=UQzVqPy-lGbQ(N;Cl!e(!Y!X(6O&D)6YQo|$r<`AHg<(~HfNRBb=YcY%@&Ivo zSV$SR2@8s<148D(oOzDsxMn*HS#PQdAM}O_Gn_eU2x92C5#Xwr(?+PBVO9zE!1_Q* zsemZBN(<$D<;}4o6M`5v9FxsbXHBC9j`*S&HVX8koK>KMHZ7STR7#AZ;AjtV-6}YA zmaP7 z+ev?D(T{isv~0&L5R;K~*2)1Jq(G)=!IbqQqZg2oHfIq+3p@-WC+T7#H_z2`#T>bz z_(u-`|FXp>;gJ$6mVkvtZLTp2YAs}d!&-_$O@ZHoe*;{V9PlKo34MtZSZzttE#(?e z-b8AVnrKxLBpT64Xf}Wz1$>E*8h94;D6wUQ#Fv%t<*^R@$kv6+A=d1mBnymp2VomG-Yah#Ztz*>MEy%xrQ6!ukpZy$GrTt6a06l)_b1 zzORxtt57OClbJc1H+fHueYr%ymYP=>IxRYlx?+rC7FTy9{qj-^zY>^DFl^^{wo6dv2=i@ zEsfpp>{{9*Hdj>wI90=`2zmsk0z5>h9^Mhvs?v_2#twiaGVohVL8zhVAM6Eyo4+<7 zNsYJOQR@SaR<9Fz8SheHBBiq3(9QNBATPEL0Ni}yiQC0H(@(h((EAi?ht)x;nRc|LeJQXYuQd48wP_LwECjB> z$j!CUk0Gl2g>pu^s{B&Dq&Rw=_WYV&78Ih~Qk&<2W`Yw>94`seAD}% zp-VDuXd&>Mz~%6y)`n4b{nh4%2OX0v9DWKIrG)s!Q_HtDj!D(zn(nH2XL@(h#$Ki=wz8bImHigXg4z z4A0GjmP7M4k7VEh1P4os8LpnUArcCba=v7PO&&!uEhC~cu9wVt2+0UOfi^tdHZp

V*|H^ZQiul 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 LCoXVW>aG6JC@?at|ErwjHt2r z>Ybq7V@Q7DIyz69cFy+6Jiq6b$U3 zfq(To!y!ddR_bo9F3)|;xvz81J&&uOb#{6=q|#Uass3v($NdL2>PfUKkNyUg>zvG; zL`_S%eYWvadYHD|)9grQ9T%@ZI$D-c1l=Dhj+Asb4(lzO5u8@@z zx|+*MdM=mNV&18FMN^~%QzU&J<*b%V7j#9+s97~r$i$>~Ree5J(CMqDL@lJl$0b#h zX47g`j(H!_xh6j2O>qhnMtIYIlqK?cWi+iSS>1FoMK7>yvT=G;y6_Y5d-UG`*EyX> zVJD)cEU26;-gMk`-U8Mg!n9Hgo=bK=6P`!zkOkSB;$^Swll>~M@^a@*;XCK(}d}pvj`N=tJ&1z>!b5ZazVN-3rmKn; z5lk_g$S9^yRuZuwBnymz zmy(S!ETwa)lp?>NW~G9ru&BgxX+?`lnhL?86{C`prLD_SCMOrHZ?tFoaP~1`(r6-` zhCt!4b1KsyVmS%Yi9_myoy=u*<)R)lpKscvoJ-O%<2YhQwf3tmepKJg*hiEL6yM*R(Np75X|j;7ZF;nz>A4IJp(kUJSc@3cDV$OC#NscGDLNRD$unhM$!h9( zrA|Ugvm|KP+VGsJq-9OI097Fod$4Bxz|vegkwQCXNgpc7c{MFdI-LV8?0f;-r{SP8 zxW2|%X&&U(V$u;!gYc<|v^1;C<)Ei5cHyK_j!BBF>Qu$93*Zv)P4_JHkdnwwMI1Ha zyJclAQAp#Cu%JbKHJeNq}g~mp~vu>jFF`P(98c^t-CuL8ps_L7OCgjpRa&NdIUI0H&bvf#Y}bH zAZli3y!ocuW9x`?Q5acw>vU`@YNlr%vYdt{V>{6t#ZQv}AQ79lt?oRyx@Tl{dlZ$b z&qbuE0h>Oim|!U_*<=(sH^%-Hzem^5`zFV?8O>YxbO*m$y3BWQMSi7ueEkz>bPN4= z8sB~j2j&lj)7*98PsGa(-Pu6oijHPF-vTf0H2A^x6dfx~99bJBJ5;U}cLO{@7F+SM zaF{L&I$a(+oonz}b{2&qe~zeNCE0aTkllAZtust=J2{=$ZKnZ_y?9yNV8#v|j$J(! z#a61wUKsFo^Cul=K&{K-ABmH8d4a#cT@=o67x;*8;>kcwMZ^la3cm`rJDXDCu#_

%e1&Yo zOMr6Qi?60rVJycYUPin$eM#7pI3#pFC!1bXQ?q1SlZq+IYEm}^Y|RwUX>bdiav_t~ zOwVjWQ{q_5>^O2UsnlEs0mclMYJg^RC2M}2r*R_h+D=Vg(P&+Xl64c<^JrRN92Jcm zzW>7C61Upf|7SB-XO@mu9Zu&-zS6(_LI2+S{dOctK;t)?|tyw z*}2kqsyv=DLg$y?yFUH#^vclfky2=1IkfLV=)nEZfzOZMyHpAtFNcmB$eHt3ysLrU ztBWgB_s$w8PnS=|jqnGQ)pJH*p%hps2Nn#^!ndowzADFKH_fm%^8J69{B`hGr~m%6 z5gtSS@@*qi+^}|KC^rcf05q)ws-9uaRU&(b0OTS`>sJ3mh_bbQH0lHKl@j!T;)>k+-phOubXhy>UpTJ%FgqepibE_@jBv;id(q^XMsjZJ zq9@)%WX8#zeSL9oo&Ap;O6Q<1wbxOpqy0WcYvj}|afW}mmT&C8^7fVUSI;y$EW2+y ztssc)0`e2@xE1zDW6B)!qr>zdB92pJ^iW0B5qS8HnL;1V%;iO zri20&>-a26-?}U_ltz3Q2VFGNZHGWChOcZNK%~pEU+e&(yUBpU+hRYWmWNuzAoen0 zze3w~7~6boo(w{4NumOV&VFA z>oXtEtQ@^PRtoJehxQwR_Pq2)#gf1k&(HjS>%aZ(=d&g0)w1*|#IWQaF8haDBvA46Rk}Cb@EU=gm9Ajb&GioKc(rSQRW_~3)^EBC{%-0LreN6X<+139&@df>Ht^S=>G2gb_>#*Oeh ze?4&H;-{BCx%^ij-u$o>-d7IqGmt0WsaQhz%f(;s{f%CFd8+*Klrc1Y+jrY%oSlX+ zmU?H(y))H-XERPfpqmSHSA!mZ@M{j>F#&vV25Zqs!xshv$#<#w*{FOU<6Gop$C`q= zE3~S@7W35Nb)RdPg`z_i@kj;rwdQw2$+;$;Y_e5o%@MPdD8(vPZ2K8+6-^gOT)RkX z7v?izP4el5PD6C8(b5gMme61XGWcop0PS+!+y9_<@P6-L#Unusmd{)ZRGnPkuCFAORsIY5euyphrk0nFPb1QEfzTG9??y_&UA?{w!6&F^wxU!7O_u&dXEy7(8W#?Vj zEtr-&_|?)ga%5dh0FHD_us1MCP3Im*?crYnEP6=WO9?HBC!ja!`&$5}C&%!<6`#Gd zo>vaJ=jjviWj#x-|M=q{w_fZ@aBH=L>)ZJ?=On%WR5>T{Wdi`d z`2W9rnZ+IYK747tEqGGp?uy^VpCY$``|6Zktv809_>NfE-MVgjJlP@>f=;U6PKb>g zuHrf;kYZK1q-0dG-H1r7ev5yr3I7u&)0@(rJ)8>$S^AuX6D`9{3S4b|>+u4701N83mCb-u1}4 zn_}++vKau=-~6=NB&D7vJwkSvHB2fSre;HOBVvg48GtW9Yv(~Ez6(dZsr z(M#@V*&W4`i!+GGvm;muOAo@)`{AhZ!jUg^W9t2X{?G_VOW}Aq95?#f^OYm3f&Lo< ze=;>6AuD!Zf0;_^(Xd*It|R|8wn*8%%d2Ni$!l`%wv9YF+xU-<%8hnMA* z{f2L_%rOovGcVNANPgNJF*24A`ckpHr;XZMdERbxMm059%I()DkDmo z+iWj~x@}uv_ayRaja%;l&w7)zd>`vAiv1q-Sy8#=yGYa|tzz+_?H%ih=TOc2d7Wi} zLSm&I0K|ECzUmNo5lSAw%klmtAN^Ihu+f?;+)GAluDV|0U03wwv8#(!4%K#$=iR(( zc>r&T^sN>2x%rd)vP|74`5RiTd$kER?!ZkE`I1X;(ZjK@3e|`(Bt8U cZeS@CD_99dJJ=N9chM>+XcZJ20?VxZFBkvb5C8xG literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/foundation.cpython-314.pyc b/frappe_mcp/tools/__pycache__/foundation.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..936c1799f6a3b770cc8600a150169b01afa08ab1 GIT binary patch literal 7628 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 BV=4du literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/governance.cpython-311.pyc b/frappe_mcp/tools/__pycache__/governance.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..185da254537cf63427a65cc4dca59f97759bbe9d GIT binary patch literal 19066 zcmbt+eQ*@lwP*KC&-aW*qfhjOT1bEdLdKX6K>!C3GO|Io5n#UT@t|&@fzgcIJp)J? zIokK~65*oCvO^w9LMro8FNqSf^>}Yzc1bE*aqMItwVSQ&p(f?ou4RjTZ@m@oUrO0j zxl*;Y`#ZOLK6)hCe*1c^&h5T^KhM4QoO91Ptse&hUJlod|Mfqk<9j*opXi}*Rhc&* z-{v{)7I%`9xU-zZOSTw)mS?f;tc}I?vvw8>X9dLem}A0u*2z;|A?BKJpLMf)N6a(f zJ?owDo%K!l&-!^T%AF0!RdP_S8nZPOTIJ+w$%(Ge+t7+{UG!FR7k?g!e%|8GcRO&l zM)J!+3AIb8f6OTbzb2fm#eJRZEVVRdld6$kk971v3gNy%sxiw5QZ1ev%bx4-yr%5A z9?wl>&kcBPE_-gob4%It8a%g_JvZUGt?aoO&uhz`Tkzao_S}kRvFy1G&mEF&gzH(G z`YKT3Jt|+3W8#xr#ebZ=DUL+Ob|Er) zSxhQ`DAMQsh-Ps3C%i74KqEc8?jKYllaunHSX7QDd+fRwCBugrD$qGx5pA!djfi3 zj5QyYLMnE-kXFbtO1O#X&1M!wR^?=PG^HjJ6VYjg1HHcZ?h3FGj!Z_w zm*pwF(M&eFFDptyq4B8J=G4@MiDmWJ1_}E@>uHl)?U9eo?-6 zipc}q%WKc0z8Tw$J&FOQRbL3!?qxjIM?lci8(3~oMBq;#^EHzK~_|V96 z;bRA0&>hjFJfZ4dH5pNo>eXm+yhl)4C~qsPV^WdFqStgsj5d!-SQ2sl>B)gT=eKR$ zw)6ZTTSZ%r9eUxsISUh`ljqq!I$zpYeUnqVFHGA<4%1FjHld;o_*1_QFv~q`TKE0- z@3d!|HszW&EpawS69d-+bIy53-dFd5ul<3qJ>$&!Hs*XAmpIPVKo{t)=eAAq-nL7=+x9#DAEAbK zs1|lv>Ig_xlKmnl1%E*0s-+O>szF(6eYLEkhzpihm`nuEd=H%8!+dRR|3;ePXN z+h=07SK5Nv;668BpY+5R%C}W|l16rsX1O%iUj(n)q^H;%Z^y_BWsMpL+NB+AOgp~- zHg-w<$iEwX*lpB=@+-{hMIR``o?%@yw_|~Yg{8zOqkrMa$W$y5k*5FRl*!%1!Ygqs zD#s*MydaNZr;3d7f{cm@MPy``7@2rzTpqowii-SNDyqnmS*F*B#pKkaOuP_$H5QFu zHaKWBIn^f)BxF?_e*T20My|-nFqIsq`gCXF!XDWtiY71W0APgF}cU4Jc$ja`y^Q%Ricx`MSJYZ6G&6G(hGr@08&^kccJsT zcT!1A%1SaSt9ZGcvOmS@iN+^W$&t}LI*fhxI( zC?qjPE)qdaq>?mdhP>m9^Qhw0@kmm9>G_jK2gGrb0^)m$!-7NsBA$vRNz9l)FUoLC zXa(j(#!1yakaDNdP}xYGCc|_ZoU(u!L5`;;rdy5|r>!ulvWRhQ!MH&9cM}4NNMrcW z*O&{5=(b71jW(w*jSxW>ljE`>9x)yvCdPmdLTf@r&rLj-D@wj5kEW6|bJ1jD8xLP?oi7G`qU^ujZ8Tvm)(O2p+Y z$>;=Dc3D0{eiO0c(Qj&d3zPMXj`xWtFgjL~C|{ezf>*_8T->=;RAp2mseR%L5mjY4 zFmfdmg`HE`AB-~w)2{~Ai=nfEqHCY-g#1=w_ z-e3;e5D%GzGa8A-ru&XX;wjurkqnBl&>NB7P^&aaVA3Ta6^n<71BPHg0)r_Xuq~p{ zAp%=c9*bZl{-j(afNV;uGpwd2CL+p|E)cqks|50xDW+mz$%HaBJ#ZYdY@z)v#u_P# z%51f;9-4hs#fb=QbBNW18C6zQY~?=P6^$q5i?Z_3HgwEf)5-zdPqz(5V@X*NF94Y| zV%q2;L?454{KN*?B}(F@k6~(zWiC{(lSWy2V8=y2Y<{oQ1Ubf7z zC=yq(J5zB9uV`(IrW6IjF&m>%jo3|H3Mb;RsW95Z8Ay&taIBEb*3lidQIg|W5aS8( zjh;Tev>`8I`ekMLab$Y(xNN*D>r#aHN#WcgstJ$>tlT)Z13OBgO?H|hVypCtBUo41 zhbF)1WixKD92yBE)-5L0A+*UvB|Sz$I4T*`jR^fTIFi8;86M>V*P}X&>>!BGBV(zE z?m^{YR%>z96pnfYt}u2SXFsF6La_HV=QGuH9Ly6ixFX1wEL+8MUITE z8%?Uq2W9lkj_&=0Q#MmCdjNC?twU8A!98(0^#XFtav!?=`Os5Kf-SUZ$;&l$Do+H00L0xow`)e=V_SncutkpuXI zz~gnCCy)8N$jY_lH$I_#S<=?Z2wxl{J^3|99<34ct8_8Ham(U0Z9_kyd)EtQc=>|XI*PC& zGF$=bKfosh9=EdoZ_clI1+6^V@Wj1?THDhI`QGi?VM*(h5z6r3a1tH}xV%&kz$XMA zH?Vrp+kN?to_jUgn!N}ZCS?Nngur93(HLR=OG{2}-KJv3fQvE$d_v%HHOshxG7g%n zp{;x2*uQ9k_|U<^!rH5~QYcnexu7;}(+;# zF1u$cNaHz4^_o|*pn$}4vbvCp`rM$hC~?>Va@M6J+hK#)(j41@Tsn(@;y))IAQ_4g6PCUd4;w;ZG&;0|d2q-@}fr z54(0g+;ZgMx@~l0!1i7vWvS6mi^Bls0?`Gipw!VaEdobV`{(%k_!<_=H@N3GzCzDn z)iTZSX?|sGl*C`>q%tirPKw+e;YV0r?-ZBVpF>0a75gdfmi_C(j1Bo6C0*|f^bj)w z{v1gM`eR;cTlu2=5f<({#l@>Mq-~jE-GveqOU{d&HE*0p&CVnVndUXejX~$}HFw%M zz*)ywS$4*ic0s%Lk2CJHdzJQ}aj~{H<5@MYw?YRul=h_US9s-c+P-q_X^*vpx-~6i z%C>4@L>RX(?fEVz`B!Z_fVwO=MDD8ZIjr^b9P1IMjIUZx&{{&OzRYGtdEUb%gQfnY z?P*VGhOK3I?#53$S0x)`wk}TTLQhs3X^4%q#@fPnx$g;;t(@U*{P(NeHTxOvDu3gj zlB7{HuS><%kSui)Em~`kYK^g_g|s6{MlsFq5c_$al(#~LvFV9ZPj^6iJ%^p*qJEKGCA{4p1l`wtiCp> z?4Ryh#uEDWvh$LDFmgeT{fX}}i!v>feJGxsb`G-a)6I0y$LS2m@;J_+?9|x{BT;gX zsL(#^5p)M*g1XaC!|3j-5rwoAsva~j7{(c&v{lkaQ~)*~XA}o5Sb2g9&A;v=CKsN- z!7L%^-YB|^@c>jJZc2(ZAYJzh3@x^tA-nREfIM%dz1QfNlGLST%*IJN3v13*M5Rf#>l z3U$A6v6)v+QeFc84S#AI253&{!$8yZS8lvAJD3kOeh}(>5bDgkH|4AA=Fi*==IiS7 zO>IjauBPjcoTJA12?0p_#3NS$vjaw7ARh>#-e73<$cLR<7NwsJWIK1~ zI(KWn19RKo?0=(wesJMn*4Li%wQIiid&=yR=CgNC0wCOze--^j^u9}b@lS|PxMg5sazdh7(wZ|r#(s+&LcR?E$n4F4e1k*`^kuWNnS(w-51?D>J`uY7;) z%eM69T6!U8H_{9Oc)4KplF#7}VSfD~%&$L$7mkoh(&%s2)erRBe$&70@D|5!dwD>M z6y!k`CI#8Bn!bPx1nWzQ{>{K*RxbIF{Y|o$y%#)O8g?|3N3L5{f^xw21`AGF_#uDC zY2^_X$;lBvxtfKKtUuP0GfuQelylhy-uYRrxT0Lz^+VPhNIt7ceU7yIv-_GP+?v;= zqGTs|fDv!n0sDa<`R@d*eIDT;8I&C~APa1p@q9*2RTinELSB-BE449dI|FHS#`C%d zwoDdZr!?t9#fhAUs8bVjLN zbXK7r&>B|0Mp-?FuZ_yg2%+1dhct<>GDjI$g~V?SD*P7Rkv>Xy%h#YY10Nw%y>d~O zblxDr1p+LJ1_?ezMF{*K{HfCrN(`o2qQHUBd^aP-RW*98RB?s5q_eaj&TJ;G5s8kwO2XljS zgP;L*Yi{+w-M=uHIhd{8n5*45HvrYr_h0$WD~r$EJ&|qOlWW_fHCBeXBl-HC1-Dk; zlNkV*8!UE7>l?g3sI_d#wv6OjMzs3MFgN%xSbJkSBi$XlAJj&pTJt3eYElcPvcXg? zn9|&-Papc~Kp8RWa#$+p|9$v^HY#PqaxN@u&0`c2lUne#Z1A;Q@HNd{8Hi4TVxmIa zsB19?KRwuZ$i}^I^B&r7f8W3H&~E$tPw{lW+W}~yb#!uIv~C{LR*?QI^bDQ=8Dv_1 zNw#RkxoXCiETb)H9v%jupzn~-!7k%e2VVjnz6AA<=8JC%IVhGYWNvsPIX_Y1dIsP) z`QfP0+?P4xFyAIco`6de?I z$B9#bKx!6c08Q#HWP!A)mS|>=gHb`|(V>u^Rf!DZ6`dTE?-C$)5>|(D8+R)4B1YE? zlJ*k4B~XiPp?-)PgQOAN@$8Il5-l}{6aZhw;kom%rsAi#d1y1rJ^ncd($!kcsY=>^H)G-V^Ok+u=-J5w_7*XI0f3z4i}%=yLH!;k8l7B=VVH{=U( zO~bA3x4RemGsm+v-MO0XhYig)qi&@Tfgj-aLi56otkCheiX9m8 zp@w2zYsGxyg)eY4Sibm+2Z8^9o-begSRQ49zjd)-ffu(13KuUZLIvyMSv99asu0NU zxU34)8NOUOda1ZMBnartjc4VBTDA}mpL>`!!2`hGl?tcnkSq5h)l|Vl&gBWhiWl-ImuW6Y7O0K4}7&o@uy7u-p zP275SUAA#Yu5m{`5PI|U8>i>T7UXQ8D;Ma(REC^5)`jTU9&*kOJ*I>CjXjyVyF*&T zeuQ^H)P}OYp`33>6NZ-0Ymun^23=WcURRkrIN5!{xmz~NcPhx*YI9e9tVvgpRLrWA z9MA4l9O^)RJj2QmA({BF@+<+OJjxLOOr>Kqk%GPk%+g@+rFnEHgo}!KRQ`e$GbZz2 zzgEW~m;pSvkwdSj8-W-2peEw?2maXc{RdwbefFF7L$imBu2eWlgpPnB9o2-R%V&_-HLbgk zPXH{RK`VOzu8Vtv<1Q72PB^(@MuAZ~OcO{g397)TW0*F9i`jUv3xX!@U^gORrhG5h z6-N1&ZG$`Lngw&`kf0EFmN3_cn*ud#$@<$s5N8iRB>tS~y0_z>iH84(?tjB)AcFb! za})dw&;0o9G24WF#x^0m;bYGN`TNh<**zC?%?Qa5dSYG`h;!Juw7nwD8RceN&|x?- zL>`x2;NKWs$*b6+t8h!sa-DnHoopyoV4VV`2lj@tQ-3KRJRU2~9joN4kn!WZ#1UCc zyCg5M>6%sfx7_ z{1^6+m0!Jwb3WL+s$uW4Roc6Noe;I1c3uh=@PM=$N=tSWQaFy)P`gqM))As?9V^>e zDtoDD62{w=Eg*(!o7bz!!Li(r37gV);ONotvF8U)9z8q)qia+pe<1iDg-wSbD7gKk z1EC`Zsllha41c6PxccF93gaw8e*a`o2{Shz93gaD;$zI6L3Nli!yNn4{%@0qCK3^7e z5f5OdD`ID5rmO8mgxP^Ep%Ptx6DoIlhtU`VSbkZFo)zJy!D@$uPbGfB4`W)1VKOHt zi1~zL<<|hZ9ivd*qeq5n>MtEJ4O}ZE22HR3Oe7av{@jh{vhFoG_ZpU7DqV4B;J!AQ zNJe6Xy~O%ge!Q+NdYa+Giauj!GH*iV@6f^GM>+J;+?9r>nKGsP8)RUp&_m`z4%BY!U0B*)p#$8+w8B^q#=;G$kY8y5^MRg}zYci`Ly2f_~ygtb?r*#nny2QFy`E+Oahwh39C&ctTs7k2j_NYg?f1ViQT#OowI|QuO%zAKJ3B=K!yES zy36rs>D6X~#O2*2}VnHM$RMni9;2^-a|SYZF|K*zx*?!6}O zA)EcZ&iaG+GWMt zf{^ogE6JM?aG({M&=cc&!(Xl?x$eiY)meB~9!ri&>C7tE%{a*SCM{f|KC(;MjxTYX zKj3Fv<&x-}-}K0Vw~e)DR$>f2vU3$lv_k%a#LK~UWrz2U$XJAD#yjJ~NW2+up)cjJ zwN+@Wg|ue|-^RxI8y#u4(gmE^(iLr}O{?|>K0xrV@qW*4?LW^Y8%wQTDoVWQm(|0h zLdPxfh6OS0T+M3MQ#H(_DCK9UA54nKnn4wdny4O^LISu#Jy5C$)Dx%%D1OCAU+cjZ zq)d_nK-jX(NR7=n@Il;^ap0yI{<&H-r^_lBsJg z-W~?)6fK^0Wui%eYyKILXfkXSd_1c5XjOYMk-6lXUwz}N3$Ukj}6lF3~Xd7sBehqpXab{U^FBP39(gWW3Sfi1;4I(0jj1FN&v9<4_+k z7$mEwRKA7h$~9C9NQY?S+yl?gU90F~561wKi!$tz!` zTm(`y5iJY+mrL{L2l0}KJRb%z~A=3 z-=?iSk@cU<`A=&8laE66v(M%m*MUbk$gSH^bKN&*pBtI)pbkEDYwz28-`an3zvhDi zqa)wcI`?eeADTN1F!~6KzLZ&FhBj)(>I^aL>|;`%r}VtU+PVw-?TnH)&LA1@i{6@ z2G+ww*NPJW2(%MRJbzK@nx5FJyM3=hzga8&=Ia& zo-O()d$O!vYk&7~tBeeG;?-Mb-&wCP z#$_HXTcLL=8&{d~-e>V%sj!8U{%JW5P45-qb%A-WATIM>2@D&H_?sB&bmwpaR@LH{ zP3Gr5V^9y{!&!WB7?t2vSX}NE`{e{|uRd~7hhr&y|5SQC2=U?(>Q|@{c%-nc!FEcC z|Ilc4ukjHmb5Hpe75GakVA^GOQv#jSl(z_c8=%rI8>SZozE4|fJ%Fh~4Tv8Ex*i0& zGA9-{=hpAc26p8FyWpeegWDJHxWqI6iiKTT*UR_2?tdls>^a;~XnsYzIIabw*|SiR>&f-(xi|Rk&R-703uNS#+|h8h@zq@8t6Hcs z%sC(W;hmN_mlL1V{M%ULi(U72zT1=g(oyZydF|!V+$l+GhJS~w#S>a^G8>%C1t&H4 zWZ^7xj2)^9(5PYkzwf*k`T2%lZqS+!AV5hTJemz2%>|EY?#fU&-yCCJA?-lfuj>!2 zJJ`>?*Y7>F&Hlb^{h=-P_c!u%zr_LAvrcz~X=1`*gQ^jc(_P~cbvzcmp!|@YJak|u z7gbduf~>cepW~TBXP=pZ|F~iJmKMHdq>{!LqV!#vY}k*8CK=ju58bh2KCK9R#DULL z;mW|g4^;Gst*9=_w28pi35*bUmB2{?rwA~^{b{;8Lx348NafBB?MxbB6q&7|iq9+l zaIsii7_b3MSUXK9_A4=p?e-%~!c zxSd6J53#5cG@Q2a&bj3Lz>Vo8j_y~0SA+oXobOuV5LE(kEg#C@6{1D7ho}-9B1nXSHL5K(Q2D#1~{4HF|RafmW1iz>mFc^7|* arvXw-Bcyl*Sm!l+MKSe?;?IUmlK6k4Tz4n{ literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/governance.cpython-314.pyc b/frappe_mcp/tools/__pycache__/governance.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..124df4b9318393de6a31d6c4415b7cd0630c4990 GIT binary patch literal 20011 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/print_formats.cpython-311.pyc b/frappe_mcp/tools/__pycache__/print_formats.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc4d177c163f4a3fa4c1bd82fa2448738f3f3054 GIT binary patch literal 6252 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 oHUKro;gtK-)&Kwi literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/print_formats.cpython-314.pyc b/frappe_mcp/tools/__pycache__/print_formats.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aaea6ebd8aab5000cd5896a85cdfd4509f985395 GIT binary patch literal 6836 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/property_setters.cpython-311.pyc b/frappe_mcp/tools/__pycache__/property_setters.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa5ceb0d662e918bbc95b951f183ff70842b7201 GIT binary patch literal 6706 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/property_setters.cpython-314.pyc b/frappe_mcp/tools/__pycache__/property_setters.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ea860abc291cc3e3a7bea4ef6447efe5af6de06 GIT binary patch literal 7243 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/reports.cpython-314.pyc b/frappe_mcp/tools/__pycache__/reports.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79f426107186d61a4203597e9755a9820f1c0104 GIT binary patch literal 7858 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 kMR|9WhmU1jCc;RWk|IFC2odlw? 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/scripts.cpython-311.pyc b/frappe_mcp/tools/__pycache__/scripts.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d150779b2470add1e32aa2ca8115c5a16f88c42d GIT binary patch literal 6629 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/scripts.cpython-314.pyc b/frappe_mcp/tools/__pycache__/scripts.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a15168f69734f32ca32d5b4bd73454f30e683235 GIT binary patch literal 7127 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 A1ONa4 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/translations.cpython-311.pyc b/frappe_mcp/tools/__pycache__/translations.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..311727d1add643015a9cc19a44b737d56d649832 GIT binary patch literal 10402 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/translations.cpython-314.pyc b/frappe_mcp/tools/__pycache__/translations.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bd4c42814f73a75a570d35eeb5d500f676ee1d8 GIT binary patch literal 10964 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 L2+6hu#FG9GY%_SY literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/users.cpython-314.pyc b/frappe_mcp/tools/__pycache__/users.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f5f301433532b1b970ee729426902502447775d GIT binary patch literal 8518 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 Nem}&32*i28zW`&wq@n-- literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/webhooks.cpython-314.pyc b/frappe_mcp/tools/__pycache__/webhooks.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55a231bba1972daf9abe9f0a4e4f8b03af864471 GIT binary patch literal 9177 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 F{s$h$Ret~g literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/workflow_tools.cpython-311.pyc b/frappe_mcp/tools/__pycache__/workflow_tools.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b9cfdb031f32c4e9c8c365811a15cb52fa977c3 GIT binary patch literal 8514 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/workflow_tools.cpython-314.pyc b/frappe_mcp/tools/__pycache__/workflow_tools.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39bfbe3f01dcb50b03c2b0b5293d763c5145d014 GIT binary patch literal 9196 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 Imip)a0S9x6fdBvi literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/activity.py b/frappe_mcp/tools/activity.py new file mode 100644 index 0000000..eec556c --- /dev/null +++ b/frappe_mcp/tools/activity.py @@ -0,0 +1,390 @@ +""" +Activity, collaboration, and system log tools. +Covers: Comments, Tags, Assignments, ToDo, Activity Log, Error Log, Document Follow. +""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + # --- Comments --- + Tool( + name="frappe_add_comment", + description="Add a comment to any document.", + inputSchema={ + "type": "object", + "required": ["reference_doctype", "reference_name", "content"], + "properties": { + "reference_doctype": {"type": "string"}, + "reference_name": {"type": "string"}, + "content": {"type": "string"}, + "comment_type": { + "type": "string", + "enum": ["Comment", "Info", "Warning", "Error", "Workflow", "Label"], + "default": "Comment", + }, + }, + }, + ), + Tool( + name="frappe_get_comments", + description="Get all comments for a document.", + inputSchema={ + "type": "object", + "required": ["reference_doctype", "reference_name"], + "properties": { + "reference_doctype": {"type": "string"}, + "reference_name": {"type": "string"}, + }, + }, + ), + # --- Tags --- + Tool( + name="frappe_add_tag", + description="Add a tag to a document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name", "tag"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "tag": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_remove_tag", + description="Remove a tag from a document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name", "tag"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "tag": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_get_tags", + description="Get all tags on a document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + # --- Assignments --- + Tool( + name="frappe_assign_document", + description="Assign a document to one or more users.", + inputSchema={ + "type": "object", + "required": ["doctype", "name", "assign_to"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "assign_to": { + "type": "array", + "items": {"type": "string"}, + "description": "List of user emails to assign", + }, + "description": {"type": "string"}, + "due_date": {"type": "string", "description": "YYYY-MM-DD"}, + "priority": {"type": "string", "enum": ["Low", "Medium", "High"], "default": "Medium"}, + "notify": {"type": "integer", "default": 0}, + }, + }, + ), + Tool( + name="frappe_remove_assignment", + description="Remove assignment of a document from a user.", + inputSchema={ + "type": "object", + "required": ["doctype", "name", "assign_to"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "assign_to": {"type": "string", "description": "User email to unassign"}, + }, + }, + ), + Tool( + name="frappe_get_assignments", + description="Get all active assignments for a document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + # --- ToDo --- + Tool( + name="frappe_create_todo", + description="Create a ToDo item (task) optionally linked to a document.", + inputSchema={ + "type": "object", + "required": ["description"], + "properties": { + "description": {"type": "string"}, + "assigned_by": {"type": "string"}, + "owner": {"type": "string", "description": "Assigned to user"}, + "reference_type": {"type": "string"}, + "reference_name": {"type": "string"}, + "due_date": {"type": "string"}, + "priority": {"type": "string", "enum": ["Low", "Medium", "High"], "default": "Medium"}, + "status": {"type": "string", "enum": ["Open", "Closed"], "default": "Open"}, + }, + }, + ), + # --- Activity Log --- + Tool( + name="frappe_get_activity_log", + description="Get the activity log for a specific document (who changed what and when).", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "limit": {"type": "integer", "default": 20}, + }, + }, + ), + # --- Error Logs --- + Tool( + name="frappe_get_error_logs", + description="Get recent system error logs.", + inputSchema={ + "type": "object", + "properties": { + "limit": {"type": "integer", "default": 20}, + "method": {"type": "string", "description": "Filter by method name"}, + }, + }, + ), + Tool( + name="frappe_clear_error_logs", + description="Clear all error logs.", + inputSchema={"type": "object", "properties": {}}, + ), + # --- Document Follow --- + Tool( + name="frappe_follow_document", + description="Follow a document to receive notifications on changes.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_unfollow_document", + description="Unfollow a document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_add_comment": _add_comment, + "frappe_get_comments": _get_comments, + "frappe_add_tag": _add_tag, + "frappe_remove_tag": _remove_tag, + "frappe_get_tags": _get_tags, + "frappe_assign_document": _assign_document, + "frappe_remove_assignment": _remove_assignment, + "frappe_get_assignments": _get_assignments, + "frappe_create_todo": _create_todo, + "frappe_get_activity_log": _get_activity_log, + "frappe_get_error_logs": _get_error_logs, + "frappe_clear_error_logs": _clear_error_logs, + "frappe_follow_document": _follow_document, + "frappe_unfollow_document": _unfollow_document, + } + + +async def _add_comment(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.client.add_comment", + reference_doctype=args["reference_doctype"], + reference_name=args["reference_name"], + content=args["content"], + comment_email="", + comment_by="", + ) + return json.dumps(result, indent=2) + + +async def _get_comments(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + "Comment", + fields=["name", "comment_type", "comment_by", "content", "creation"], + filters=[ + ["reference_doctype", "=", args["reference_doctype"]], + ["reference_name", "=", args["reference_name"]], + ], + limit=50, + order_by="creation asc", + ) + return json.dumps(result, indent=2) + + +async def _add_tag(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.doctype.tag.tag.add_tag", + tag=args["tag"], + dt=args["doctype"], + dn=args["name"], + ) + return json.dumps(result, indent=2) + + +async def _remove_tag(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.doctype.tag.tag.remove_tag", + tag=args["tag"], + dt=args["doctype"], + dn=args["name"], + ) + return json.dumps(result, indent=2) + + +async def _get_tags(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.doctype.tag.tag.get_tags", + dt=args["doctype"], + dn=args["name"], + ) + return json.dumps(result, indent=2) + + +async def _assign_document(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.form.assign_to.add", + args={ + "doctype": args["doctype"], + "name": args["name"], + "assign_to": args["assign_to"], + "description": args.get("description", ""), + "due_date": args.get("due_date", ""), + "priority": args.get("priority", "Medium"), + "notify": args.get("notify", 0), + }, + ) + return json.dumps(result, indent=2) + + +async def _remove_assignment(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.form.assign_to.remove", + doctype=args["doctype"], + name=args["name"], + assign_to=args["assign_to"], + ) + return json.dumps(result, indent=2) + + +async def _get_assignments(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + "ToDo", + fields=["name", "owner", "description", "due_date", "priority", "status"], + filters=[ + ["reference_type", "=", args["doctype"]], + ["reference_name", "=", args["name"]], + ["status", "=", "Open"], + ], + limit=20, + ) + return json.dumps(result, indent=2) + + +async def _create_todo(args: dict) -> str: + client = FrappeClient() + payload = {k: v for k, v in args.items()} + result = await client.create_doc("ToDo", payload) + return json.dumps(result, indent=2) + + +async def _get_activity_log(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + "Version", + fields=["name", "owner", "creation", "data"], + filters=[ + ["ref_doctype", "=", args["doctype"]], + ["docname", "=", args["name"]], + ], + limit=args.get("limit", 20), + order_by="creation desc", + ) + return json.dumps(result, indent=2) + + +async def _get_error_logs(args: dict) -> str: + client = FrappeClient() + filters = [] + if method := args.get("method"): + filters.append(["method", "like", f"%{method}%"]) + result = await client.get_list( + "Error Log", + fields=["name", "method", "error", "creation"], + filters=filters if filters else None, + limit=args.get("limit", 20), + order_by="creation desc", + ) + return json.dumps(result, indent=2) + + +async def _clear_error_logs(args: dict) -> str: + client = FrappeClient() + result = await client.call_method("frappe.core.doctype.error_log.error_log.clear_error_logs") + return json.dumps(result, indent=2) + + +async def _follow_document(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.form.document_follow.follow_document", + doctype=args["doctype"], + doc_name=args["name"], + ) + return json.dumps(result, indent=2) + + +async def _unfollow_document(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.form.document_follow.unfollow_document", + doctype=args["doctype"], + doc_name=args["name"], + ) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/admin.py b/frappe_mcp/tools/admin.py new file mode 100644 index 0000000..425acad --- /dev/null +++ b/frappe_mcp/tools/admin.py @@ -0,0 +1,185 @@ +"""Admin / bench tools — cache, migrate, app management, system settings.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient +from frappe_mcp.config import get_settings + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_clear_cache", + description="Clear Frappe's server-side cache.", + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_get_installed_apps", + description="List all Frappe apps installed on the site.", + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_get_system_settings", + description="Get the current System Settings document.", + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_update_system_settings", + description="Update System Settings fields.", + inputSchema={ + "type": "object", + "required": ["updates"], + "properties": { + "updates": { + "type": "object", + "description": "Fields to update e.g. {\"language\": \"en\", \"time_zone\": \"Asia/Kolkata\"}", + }, + }, + }, + ), + Tool( + name="frappe_run_report", + description="Run a Script Report or Query Report and return results.", + inputSchema={ + "type": "object", + "required": ["report_name"], + "properties": { + "report_name": {"type": "string"}, + "filters": { + "type": "object", + "description": "Report filters as key-value pairs", + }, + }, + }, + ), + Tool( + name="frappe_execute_query", + description=( + "Execute a read-only SQL query on the Frappe database via frappe.db.sql. " + "Only SELECT statements are allowed." + ), + inputSchema={ + "type": "object", + "required": ["query"], + "properties": { + "query": {"type": "string", "description": "SQL SELECT query"}, + "values": { + "type": "array", + "description": "Parameterized values for %s placeholders", + }, + }, + }, + ), + Tool( + name="frappe_get_site_info", + description="Get basic site information: Frappe version, installed apps, site config.", + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_create_workflow", + description="Create a Workflow for a DocType with states and transitions.", + inputSchema={ + "type": "object", + "required": ["workflow_name", "document_type", "states", "transitions"], + "properties": { + "workflow_name": {"type": "string"}, + "document_type": {"type": "string"}, + "is_active": {"type": "integer", "default": 1}, + "states": { + "type": "array", + "description": "List of workflow state objects with: state, doc_status, allow_edit", + "items": {"type": "object"}, + }, + "transitions": { + "type": "array", + "description": "List of transition objects with: state, action, next_state, allowed", + "items": {"type": "object"}, + }, + "send_email_alert": {"type": "integer", "default": 0}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_clear_cache": _clear_cache, + "frappe_get_installed_apps": _get_installed_apps, + "frappe_get_system_settings": _get_system_settings, + "frappe_update_system_settings": _update_system_settings, + "frappe_run_report": _run_report, + "frappe_execute_query": _execute_query, + "frappe_get_site_info": _get_site_info, + "frappe_create_workflow": _create_workflow, + } + + +async def _clear_cache(args: dict) -> str: + client = FrappeClient() + result = await client.call_method("frappe.sessions.clear") + return json.dumps(result, indent=2) + + +async def _get_installed_apps(args: dict) -> str: + client = FrappeClient() + result = await client.call_method("frappe.utils.change_log.get_versions") + return json.dumps(result, indent=2) + + +async def _get_system_settings(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("System Settings", "System Settings") + return json.dumps(result, indent=2) + + +async def _update_system_settings(args: dict) -> str: + if get_settings().read_only_mode: + return "Error: read_only_mode is enabled." + client = FrappeClient() + result = await client.update_doc("System Settings", "System Settings", args["updates"]) + return json.dumps(result, indent=2) + + +async def _run_report(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.query_report.run", + report_name=args["report_name"], + filters=args.get("filters", {}), + ) + return json.dumps(result, indent=2) + + +async def _execute_query(args: dict) -> str: + query = args["query"].strip().lower() + if not query.startswith("select"): + return "Error: only SELECT queries are allowed." + client = FrappeClient() + result = await client.call_method( + "frappe.client.get_list", + doctype="__query__", + query=args["query"], + values=args.get("values", []), + ) + return json.dumps(result, indent=2) + + +async def _get_site_info(args: dict) -> str: + client = FrappeClient() + result = await client.call_method("frappe.utils.change_log.get_versions") + return json.dumps(result, indent=2) + + +async def _create_workflow(args: dict) -> str: + client = FrappeClient() + payload = { + "workflow_name": args["workflow_name"], + "document_type": args["document_type"], + "is_active": args.get("is_active", 1), + "send_email_alert": args.get("send_email_alert", 0), + "states": args["states"], + "transitions": args["transitions"], + } + result = await client.create_doc("Workflow", payload) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/analytics.py b/frappe_mcp/tools/analytics.py new file mode 100644 index 0000000..c63513b --- /dev/null +++ b/frappe_mcp/tools/analytics.py @@ -0,0 +1,176 @@ +""" +Level 6 — Reporting and analytics tools. +Aggregate queries, dashboard data, PDF rendering. +""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_aggregate_documents", + description=( + "Aggregate document data: sum, avg, min, max, or count grouped by a field. " + "e.g. total sales per customer, count of issues per status." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "function", "field"], + "properties": { + "doctype": {"type": "string"}, + "function": { + "type": "string", + "enum": ["sum", "avg", "min", "max", "count"], + }, + "field": {"type": "string", "description": "Field to aggregate e.g. 'grand_total'"}, + "group_by": {"type": "string", "description": "Field to group by e.g. 'customer'"}, + "filters": {"type": "array"}, + "limit": {"type": "integer", "default": 50}, + }, + }, + ), + Tool( + name="frappe_get_dashboard_data", + description=( + "Pull data for a Dashboard Chart — returns the chart's data points " + "for display as a summary or KPI." + ), + inputSchema={ + "type": "object", + "required": ["chart_name"], + "properties": { + "chart_name": {"type": "string"}, + "filters": {"type": "object"}, + "refresh": {"type": "boolean", "default": False}, + }, + }, + ), + Tool( + name="frappe_render_document_pdf", + description=( + "Generate a PDF for a document using a Print Format. " + "Returns the PDF download URL on the Frappe server." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "print_format": {"type": "string", "description": "Print Format name (optional, uses default if empty)"}, + "letterhead": {"type": "string"}, + "language": {"type": "string", "default": "en"}, + }, + }, + ), + Tool( + name="frappe_get_number_card_value", + description="Get the current value of a Number Card (single KPI metric).", + inputSchema={ + "type": "object", + "required": ["card_name"], + "properties": { + "card_name": {"type": "string"}, + "filters": {"type": "object"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_aggregate_documents": _aggregate_documents, + "frappe_get_dashboard_data": _get_dashboard_data, + "frappe_render_document_pdf": _render_document_pdf, + "frappe_get_number_card_value": _get_number_card_value, + } + + +async def _aggregate_documents(args: dict) -> str: + client = FrappeClient() + func = args["function"].upper() + field = args["field"] + group_by = args.get("group_by") + filters = args.get("filters") + + if group_by: + sql = f"SELECT `{group_by}`, {func}(`{field}`) as value FROM `tab{args['doctype']}`" + conditions = [] + if filters and isinstance(filters, list): + for f in filters: + if len(f) >= 3: + conditions.append(f"`{f[0]}` {f[1]} '{f[2]}'") + if conditions: + sql += " WHERE " + " AND ".join(conditions) + sql += f" GROUP BY `{group_by}` ORDER BY value DESC LIMIT {args.get('limit', 50)}" + result = await client.call_method("frappe.client.get_list", doctype="__query__", query=sql) + else: + sql = f"SELECT {func}(`{field}`) as value FROM `tab{args['doctype']}`" + conditions = [] + if filters and isinstance(filters, list): + for f in filters: + if len(f) >= 3: + conditions.append(f"`{f[0]}` {f[1]} '{f[2]}'") + if conditions: + sql += " WHERE " + " AND ".join(conditions) + result = await client.call_method( + "frappe.desk.query_report.run", + report_name="__inline__", + filters={}, + ) + # Fallback: use get_list with aggregation via API + result = await client.call_method( + "frappe.client.get_value", + doctype=args["doctype"], + filters=filters, + fieldname=f"{func.lower()}({field})", + ) + return json.dumps({"function": func, "field": field, "group_by": group_by, "result": result}, indent=2) + + +async def _get_dashboard_data(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.doctype.dashboard_chart.dashboard_chart.get", + chart_name=args["chart_name"], + filters=json.dumps(args.get("filters", {})), + refresh=args.get("refresh", False), + ) + return json.dumps(result, indent=2) + + +async def _render_document_pdf(args: dict) -> str: + client = FrappeClient() + params = { + "doctype": args["doctype"], + "name": args["name"], + "print_format": args.get("print_format", ""), + "letterhead": args.get("letterhead", ""), + "lang": args.get("language", "en"), + } + pdf_url = ( + f"{client.base_url}/api/method/frappe.utils.pdf.download_pdf" + f"?doctype={params['doctype']}&name={params['name']}" + f"&format={params['print_format']}&lang={params['lang']}" + ) + return json.dumps({ + "pdf_url": pdf_url, + "doctype": args["doctype"], + "name": args["name"], + "print_format": args.get("print_format", "default"), + "note": "Open pdf_url in browser or download directly from Frappe server", + }, indent=2) + + +async def _get_number_card_value(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.doctype.number_card.number_card.get_result", + doc={"name": args["card_name"]}, + filters=json.dumps(args.get("filters", {})), + ) + return json.dumps({"card": args["card_name"], "value": result}, indent=2) diff --git a/frappe_mcp/tools/bulk_ops.py b/frappe_mcp/tools/bulk_ops.py new file mode 100644 index 0000000..dd9ab8c --- /dev/null +++ b/frappe_mcp/tools/bulk_ops.py @@ -0,0 +1,337 @@ +"""Bulk operations — bulk create, update, delete documents and data import/export.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient +from frappe_mcp.config import get_settings + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_bulk_create_documents", + description="Create multiple documents of the same DocType in one call.", + inputSchema={ + "type": "object", + "required": ["doctype", "documents"], + "properties": { + "doctype": {"type": "string"}, + "documents": { + "type": "array", + "items": {"type": "object"}, + "description": "List of document field dicts to create", + }, + }, + }, + ), + Tool( + name="frappe_bulk_update_documents", + description="Update the same field(s) on multiple documents at once.", + inputSchema={ + "type": "object", + "required": ["doctype", "names", "updates"], + "properties": { + "doctype": {"type": "string"}, + "names": { + "type": "array", + "items": {"type": "string"}, + "description": "List of document names to update", + }, + "updates": { + "type": "object", + "description": "Fields and values to apply to all documents", + }, + }, + }, + ), + Tool( + name="frappe_bulk_delete_documents", + description="Delete multiple documents. Blocked in read-only mode.", + inputSchema={ + "type": "object", + "required": ["doctype", "names"], + "properties": { + "doctype": {"type": "string"}, + "names": {"type": "array", "items": {"type": "string"}}, + }, + }, + ), + Tool( + name="frappe_bulk_submit_documents", + description="Submit multiple documents in one call.", + inputSchema={ + "type": "object", + "required": ["doctype", "names"], + "properties": { + "doctype": {"type": "string"}, + "names": {"type": "array", "items": {"type": "string"}}, + }, + }, + ), + Tool( + name="frappe_import_data", + description=( + "Import documents via JSON array. Equivalent to Frappe's Data Import tool. " + "Use 'Insert' to create new records or 'Update' to update existing ones." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "data"], + "properties": { + "doctype": {"type": "string"}, + "data": { + "type": "array", + "items": {"type": "object"}, + "description": "Array of document objects to import", + }, + "import_type": { + "type": "string", + "enum": ["Insert New Records", "Update Existing Records"], + "default": "Insert New Records", + }, + }, + }, + ), + Tool( + name="frappe_export_data", + description="Export documents from a DocType as a JSON list.", + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + "fields": { + "type": "array", + "items": {"type": "string"}, + "description": "Fields to export. Empty = all fields.", + }, + "filters": {"type": "array"}, + "limit": {"type": "integer", "default": 500}, + }, + }, + ), + Tool( + name="frappe_bulk_cancel_documents", + description="Cancel multiple submitted documents.", + inputSchema={ + "type": "object", + "required": ["doctype", "names"], + "properties": { + "doctype": {"type": "string"}, + "names": {"type": "array", "items": {"type": "string"}}, + }, + }, + ), + Tool( + name="frappe_bulk_assign_documents", + description="Assign multiple documents to a user.", + inputSchema={ + "type": "object", + "required": ["doctype", "names", "assign_to"], + "properties": { + "doctype": {"type": "string"}, + "names": {"type": "array", "items": {"type": "string"}}, + "assign_to": {"type": "array", "items": {"type": "string"}, "description": "List of user emails"}, + "description": {"type": "string"}, + "due_date": {"type": "string"}, + "priority": {"type": "string", "enum": ["Low", "Medium", "High"], "default": "Medium"}, + }, + }, + ), + Tool( + name="frappe_bulk_add_tags", + description="Add the same tag to multiple documents.", + inputSchema={ + "type": "object", + "required": ["doctype", "names", "tag"], + "properties": { + "doctype": {"type": "string"}, + "names": {"type": "array", "items": {"type": "string"}}, + "tag": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_bulk_add_comment", + description="Add the same comment to multiple documents.", + inputSchema={ + "type": "object", + "required": ["doctype", "names", "content"], + "properties": { + "doctype": {"type": "string"}, + "names": {"type": "array", "items": {"type": "string"}}, + "content": {"type": "string"}, + "comment_type": {"type": "string", "default": "Comment"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_bulk_create_documents": _bulk_create, + "frappe_bulk_update_documents": _bulk_update, + "frappe_bulk_delete_documents": _bulk_delete, + "frappe_bulk_submit_documents": _bulk_submit, + "frappe_import_data": _import_data, + "frappe_export_data": _export_data, + "frappe_bulk_cancel_documents": _bulk_cancel, + "frappe_bulk_assign_documents": _bulk_assign, + "frappe_bulk_add_tags": _bulk_add_tags, + "frappe_bulk_add_comment": _bulk_add_comment, + } + + +async def _bulk_create(args: dict) -> str: + client = FrappeClient() + results = [] + errors = [] + for doc in args["documents"]: + try: + data = {"doctype": args["doctype"], **doc} + result = await client.create_doc(args["doctype"], data) + results.append(result.get("name") if isinstance(result, dict) else result) + except Exception as e: + errors.append({"doc": doc, "error": str(e)}) + return json.dumps({"created": results, "errors": errors}, indent=2) + + +async def _bulk_update(args: dict) -> str: + client = FrappeClient() + results = [] + errors = [] + for name in args["names"]: + try: + result = await client.update_doc(args["doctype"], name, args["updates"]) + results.append(name) + except Exception as e: + errors.append({"name": name, "error": str(e)}) + return json.dumps({"updated": results, "errors": errors}, indent=2) + + +async def _bulk_delete(args: dict) -> str: + if get_settings().read_only_mode: + return "Error: read_only_mode is enabled. Deletion blocked." + client = FrappeClient() + results = [] + errors = [] + for name in args["names"]: + try: + await client.delete_doc(args["doctype"], name) + results.append(name) + except Exception as e: + errors.append({"name": name, "error": str(e)}) + return json.dumps({"deleted": results, "errors": errors}, indent=2) + + +async def _bulk_submit(args: dict) -> str: + client = FrappeClient() + results = [] + errors = [] + for name in args["names"]: + try: + await client.update_doc(args["doctype"], name, {"docstatus": 1}) + results.append(name) + except Exception as e: + errors.append({"name": name, "error": str(e)}) + return json.dumps({"submitted": results, "errors": errors}, indent=2) + + +async def _import_data(args: dict) -> str: + client = FrappeClient() + results = [] + errors = [] + is_update = args.get("import_type", "Insert New Records") == "Update Existing Records" + for doc in args["data"]: + try: + name = doc.get("name") + if is_update and name: + result = await client.update_doc(args["doctype"], name, doc) + else: + data = {"doctype": args["doctype"], **doc} + result = await client.create_doc(args["doctype"], data) + results.append(result.get("name") if isinstance(result, dict) else str(result)) + except Exception as e: + errors.append({"doc": doc.get("name", str(doc)[:40]), "error": str(e)}) + return json.dumps({"imported": len(results), "names": results, "errors": errors}, indent=2) + + +async def _export_data(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + args["doctype"], + fields=args.get("fields") or ["*"], + filters=args.get("filters"), + limit=args.get("limit", 500), + ) + return json.dumps(result, indent=2) + + +async def _bulk_cancel(args: dict) -> str: + client = FrappeClient() + results, errors = [], [] + for name in args["names"]: + try: + await client.update_doc(args["doctype"], name, {"docstatus": 2}) + results.append(name) + except Exception as e: + errors.append({"name": name, "error": str(e)}) + return json.dumps({"cancelled": results, "errors": errors}, indent=2) + + +async def _bulk_assign(args: dict) -> str: + client = FrappeClient() + results, errors = [], [] + for name in args["names"]: + try: + await client.call_method( + "frappe.desk.form.assign_to.add", + args={ + "doctype": args["doctype"], + "name": name, + "assign_to": args["assign_to"], + "description": args.get("description", ""), + "due_date": args.get("due_date", ""), + "priority": args.get("priority", "Medium"), + "notify": 0, + }, + ) + results.append(name) + except Exception as e: + errors.append({"name": name, "error": str(e)}) + return json.dumps({"assigned": results, "errors": errors}, indent=2) + + +async def _bulk_add_tags(args: dict) -> str: + client = FrappeClient() + results, errors = [], [] + for name in args["names"]: + try: + await client.call_method( + "frappe.desk.doctype.tag.tag.add_tag", + tag=args["tag"], dt=args["doctype"], dn=name, + ) + results.append(name) + except Exception as e: + errors.append({"name": name, "error": str(e)}) + return json.dumps({"tagged": results, "errors": errors}, indent=2) + + +async def _bulk_add_comment(args: dict) -> str: + client = FrappeClient() + results, errors = [], [] + for name in args["names"]: + try: + await client.call_method( + "frappe.client.add_comment", + reference_doctype=args["doctype"], + reference_name=name, + content=args["content"], + comment_email="", + comment_by="", + ) + results.append(name) + except Exception as e: + errors.append({"name": name, "error": str(e)}) + return json.dumps({"commented": results, "errors": errors}, indent=2) diff --git a/frappe_mcp/tools/business_actions.py b/frappe_mcp/tools/business_actions.py new file mode 100644 index 0000000..5f8af45 --- /dev/null +++ b/frappe_mcp/tools/business_actions.py @@ -0,0 +1,477 @@ +""" +Level 8 — Business action tools (ERPNext shortcuts). +High-value operations: convert documents, approve, transfer stock, etc. +These call ERPNext's built-in mapper/make functions. +""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + # ── Sales / CRM ────────────────────────────────────────────────────── + Tool( + name="erpnext_create_quotation_from_opportunity", + description="Create a Quotation from an Opportunity.", + inputSchema={ + "type": "object", + "required": ["opportunity_name"], + "properties": {"opportunity_name": {"type": "string"}}, + }, + ), + Tool( + name="erpnext_create_sales_order_from_quotation", + description="Create a Sales Order from a Quotation.", + inputSchema={ + "type": "object", + "required": ["quotation_name"], + "properties": {"quotation_name": {"type": "string"}}, + }, + ), + Tool( + name="erpnext_create_sales_invoice_from_sales_order", + description="Create a Sales Invoice from a Sales Order.", + inputSchema={ + "type": "object", + "required": ["sales_order_name"], + "properties": {"sales_order_name": {"type": "string"}}, + }, + ), + Tool( + name="erpnext_create_delivery_note_from_sales_order", + description="Create a Delivery Note from a Sales Order.", + inputSchema={ + "type": "object", + "required": ["sales_order_name"], + "properties": {"sales_order_name": {"type": "string"}}, + }, + ), + Tool( + name="erpnext_create_payment_entry_for_invoice", + description="Create a Payment Entry for a Sales Invoice or Purchase Invoice.", + inputSchema={ + "type": "object", + "required": ["invoice_doctype", "invoice_name"], + "properties": { + "invoice_doctype": {"type": "string", "enum": ["Sales Invoice", "Purchase Invoice"]}, + "invoice_name": {"type": "string"}, + }, + }, + ), + # ── Buying ─────────────────────────────────────────────────────────── + Tool( + name="erpnext_create_purchase_order_from_supplier_quotation", + description="Create a Purchase Order from a Supplier Quotation.", + inputSchema={ + "type": "object", + "required": ["supplier_quotation_name"], + "properties": {"supplier_quotation_name": {"type": "string"}}, + }, + ), + Tool( + name="erpnext_create_purchase_receipt_from_purchase_order", + description="Create a Purchase Receipt from a Purchase Order.", + inputSchema={ + "type": "object", + "required": ["purchase_order_name"], + "properties": {"purchase_order_name": {"type": "string"}}, + }, + ), + Tool( + name="erpnext_create_purchase_invoice_from_purchase_receipt", + description="Create a Purchase Invoice from a Purchase Receipt.", + inputSchema={ + "type": "object", + "required": ["purchase_receipt_name"], + "properties": {"purchase_receipt_name": {"type": "string"}}, + }, + ), + # ── Stock ──────────────────────────────────────────────────────────── + Tool( + name="erpnext_get_item_stock_balance", + description="Get current stock balance for an item, optionally at a specific warehouse.", + inputSchema={ + "type": "object", + "required": ["item_code"], + "properties": { + "item_code": {"type": "string"}, + "warehouse": {"type": "string"}, + "posting_date": {"type": "string", "description": "YYYY-MM-DD (defaults to today)"}, + }, + }, + ), + Tool( + name="erpnext_transfer_stock", + description="Transfer stock between warehouses via a Material Transfer Stock Entry.", + inputSchema={ + "type": "object", + "required": ["item_code", "qty", "from_warehouse", "to_warehouse"], + "properties": { + "item_code": {"type": "string"}, + "qty": {"type": "number"}, + "from_warehouse": {"type": "string"}, + "to_warehouse": {"type": "string"}, + "posting_date": {"type": "string"}, + "remarks": {"type": "string"}, + }, + }, + ), + Tool( + name="erpnext_create_stock_entry", + description=( + "Create a Stock Entry of any type: " + "Material Issue, Material Receipt, Material Transfer, Manufacture, Repack." + ), + inputSchema={ + "type": "object", + "required": ["stock_entry_type", "items"], + "properties": { + "stock_entry_type": { + "type": "string", + "enum": ["Material Issue", "Material Receipt", "Material Transfer", + "Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor"], + }, + "items": { + "type": "array", + "description": "List of {item_code, qty, s_warehouse, t_warehouse} objects", + "items": {"type": "object"}, + }, + "posting_date": {"type": "string"}, + "remarks": {"type": "string"}, + }, + }, + ), + # ── HR ─────────────────────────────────────────────────────────────── + Tool( + name="erpnext_approve_leave_application", + description="Approve a Leave Application.", + inputSchema={ + "type": "object", + "required": ["leave_application_name"], + "properties": {"leave_application_name": {"type": "string"}}, + }, + ), + Tool( + name="erpnext_reject_leave_application", + description="Reject a Leave Application.", + inputSchema={ + "type": "object", + "required": ["leave_application_name"], + "properties": { + "leave_application_name": {"type": "string"}, + "reason": {"type": "string"}, + }, + }, + ), + Tool( + name="erpnext_mark_attendance", + description="Mark attendance for an employee on a specific date.", + inputSchema={ + "type": "object", + "required": ["employee", "attendance_date", "status"], + "properties": { + "employee": {"type": "string"}, + "attendance_date": {"type": "string", "description": "YYYY-MM-DD"}, + "status": {"type": "string", "enum": ["Present", "Absent", "Half Day", "Work From Home", "On Leave"]}, + "shift": {"type": "string"}, + }, + }, + ), + Tool( + name="erpnext_approve_expense_claim", + description="Approve an Expense Claim.", + inputSchema={ + "type": "object", + "required": ["expense_claim_name"], + "properties": {"expense_claim_name": {"type": "string"}}, + }, + ), + # ── Support ────────────────────────────────────────────────────────── + Tool( + name="erpnext_create_issue", + description="Create a new Support Issue.", + inputSchema={ + "type": "object", + "required": ["subject"], + "properties": { + "subject": {"type": "string"}, + "customer": {"type": "string"}, + "description": {"type": "string"}, + "priority": {"type": "string", "enum": ["Low", "Medium", "High", "Urgent"], "default": "Medium"}, + "issue_type": {"type": "string"}, + "raised_by": {"type": "string"}, + }, + }, + ), + Tool( + name="erpnext_close_issue", + description="Close a Support Issue.", + inputSchema={ + "type": "object", + "required": ["issue_name"], + "properties": { + "issue_name": {"type": "string"}, + "resolution_details": {"type": "string"}, + }, + }, + ), + # ── Projects ───────────────────────────────────────────────────────── + Tool( + name="erpnext_create_task", + description="Create a Task in a Project.", + inputSchema={ + "type": "object", + "required": ["subject"], + "properties": { + "subject": {"type": "string"}, + "project": {"type": "string"}, + "assigned_to": {"type": "string"}, + "exp_start_date": {"type": "string"}, + "exp_end_date": {"type": "string"}, + "priority": {"type": "string", "enum": ["Low", "Medium", "High", "Urgent"]}, + "description": {"type": "string"}, + }, + }, + ), + Tool( + name="erpnext_update_task_status", + description="Update the status of a Task.", + inputSchema={ + "type": "object", + "required": ["task_name", "status"], + "properties": { + "task_name": {"type": "string"}, + "status": {"type": "string", "enum": ["Open", "Working", "Pending Review", "Overdue", "Template", "Cancelled", "Completed"]}, + }, + }, + ), + Tool( + name="erpnext_log_timesheet_entry", + description="Log a timesheet entry for an employee on a task or project.", + inputSchema={ + "type": "object", + "required": ["employee", "from_time", "to_time"], + "properties": { + "employee": {"type": "string"}, + "from_time": {"type": "string", "description": "ISO datetime"}, + "to_time": {"type": "string", "description": "ISO datetime"}, + "project": {"type": "string"}, + "task": {"type": "string"}, + "activity_type": {"type": "string"}, + "description": {"type": "string"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "erpnext_create_quotation_from_opportunity": _make_quotation_from_opportunity, + "erpnext_create_sales_order_from_quotation": _make_so_from_quotation, + "erpnext_create_sales_invoice_from_sales_order": _make_si_from_so, + "erpnext_create_delivery_note_from_sales_order": _make_dn_from_so, + "erpnext_create_payment_entry_for_invoice": _make_payment_entry, + "erpnext_create_purchase_order_from_supplier_quotation": _make_po_from_sq, + "erpnext_create_purchase_receipt_from_purchase_order": _make_pr_from_po, + "erpnext_create_purchase_invoice_from_purchase_receipt": _make_pi_from_pr, + "erpnext_get_item_stock_balance": _get_item_stock_balance, + "erpnext_transfer_stock": _transfer_stock, + "erpnext_create_stock_entry": _create_stock_entry, + "erpnext_approve_leave_application": _approve_leave, + "erpnext_reject_leave_application": _reject_leave, + "erpnext_mark_attendance": _mark_attendance, + "erpnext_approve_expense_claim": _approve_expense_claim, + "erpnext_create_issue": _create_issue, + "erpnext_close_issue": _close_issue, + "erpnext_create_task": _create_task, + "erpnext_update_task_status": _update_task_status, + "erpnext_log_timesheet_entry": _log_timesheet, + } + + +async def _make_quotation_from_opportunity(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "erpnext.crm.doctype.opportunity.opportunity.make_quotation", + source_name=args["opportunity_name"], + ) + return json.dumps(result, indent=2) + +async def _make_so_from_quotation(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "erpnext.selling.doctype.quotation.quotation.make_sales_order", + source_name=args["quotation_name"], + ) + return json.dumps(result, indent=2) + +async def _make_si_from_so(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "erpnext.selling.doctype.sales_order.sales_order.make_sales_invoice", + source_name=args["sales_order_name"], + ) + return json.dumps(result, indent=2) + +async def _make_dn_from_so(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note", + source_name=args["sales_order_name"], + ) + return json.dumps(result, indent=2) + +async def _make_payment_entry(args: dict) -> str: + client = FrappeClient() + method = ( + "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account" + if args["invoice_doctype"] == "Sales Invoice" + else "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_payment_entry" + ) + result = await client.call_method( + "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry", + dt=args["invoice_doctype"], + dn=args["invoice_name"], + ) + return json.dumps(result, indent=2) + +async def _make_po_from_sq(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order", + source_name=args["supplier_quotation_name"], + ) + return json.dumps(result, indent=2) + +async def _make_pr_from_po(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt", + source_name=args["purchase_order_name"], + ) + return json.dumps(result, indent=2) + +async def _make_pi_from_pr(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "erpnext.buying.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice", + source_name=args["purchase_receipt_name"], + ) + return json.dumps(result, indent=2) + +async def _get_item_stock_balance(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "erpnext.stock.utils.get_stock_balance", + item_code=args["item_code"], + warehouse=args.get("warehouse", ""), + posting_date=args.get("posting_date", ""), + ) + return json.dumps({"item_code": args["item_code"], "warehouse": args.get("warehouse"), "balance": result}, indent=2) + +async def _transfer_stock(args: dict) -> str: + client = FrappeClient() + payload = { + "stock_entry_type": "Material Transfer", + "posting_date": args.get("posting_date", ""), + "remarks": args.get("remarks", ""), + "items": [{ + "item_code": args["item_code"], + "qty": args["qty"], + "s_warehouse": args["from_warehouse"], + "t_warehouse": args["to_warehouse"], + }], + } + result = await client.create_doc("Stock Entry", payload) + return json.dumps(result, indent=2) + +async def _create_stock_entry(args: dict) -> str: + client = FrappeClient() + payload = { + "stock_entry_type": args["stock_entry_type"], + "posting_date": args.get("posting_date", ""), + "remarks": args.get("remarks", ""), + "items": args["items"], + } + result = await client.create_doc("Stock Entry", payload) + return json.dumps(result, indent=2) + +async def _approve_leave(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc("Leave Application", args["leave_application_name"], {"status": "Approved"}) + return json.dumps(result, indent=2) + +async def _reject_leave(args: dict) -> str: + client = FrappeClient() + updates = {"status": "Rejected"} + if reason := args.get("reason"): + updates["reason_for_rejection"] = reason + result = await client.update_doc("Leave Application", args["leave_application_name"], updates) + return json.dumps(result, indent=2) + +async def _mark_attendance(args: dict) -> str: + client = FrappeClient() + payload = { + "employee": args["employee"], + "attendance_date": args["attendance_date"], + "status": args["status"], + "shift": args.get("shift", ""), + "docstatus": 1, + } + result = await client.create_doc("Attendance", payload) + return json.dumps(result, indent=2) + +async def _approve_expense_claim(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc("Expense Claim", args["expense_claim_name"], {"approval_status": "Approved"}) + return json.dumps(result, indent=2) + +async def _create_issue(args: dict) -> str: + client = FrappeClient() + payload = { + "subject": args["subject"], + "customer": args.get("customer", ""), + "description": args.get("description", ""), + "priority": args.get("priority", "Medium"), + "issue_type": args.get("issue_type", ""), + "raised_by": args.get("raised_by", ""), + } + result = await client.create_doc("Issue", payload) + return json.dumps(result, indent=2) + +async def _close_issue(args: dict) -> str: + client = FrappeClient() + updates = {"status": "Closed"} + if res := args.get("resolution_details"): + updates["resolution_details"] = res + result = await client.update_doc("Issue", args["issue_name"], updates) + return json.dumps(result, indent=2) + +async def _create_task(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("Task", args) + return json.dumps(result, indent=2) + +async def _update_task_status(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc("Task", args["task_name"], {"status": args["status"]}) + return json.dumps(result, indent=2) + +async def _log_timesheet(args: dict) -> str: + client = FrappeClient() + payload = { + "employee": args["employee"], + "time_logs": [{ + "from_time": args["from_time"], + "to_time": args["to_time"], + "project": args.get("project", ""), + "task": args.get("task", ""), + "activity_type": args.get("activity_type", ""), + "description": args.get("description", ""), + }], + } + result = await client.create_doc("Timesheet", payload) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/custom_fields.py b/frappe_mcp/tools/custom_fields.py new file mode 100644 index 0000000..ffa7599 --- /dev/null +++ b/frappe_mcp/tools/custom_fields.py @@ -0,0 +1,124 @@ +"""Custom Fields tools — add, modify, remove custom fields on any DocType.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +_FIELD_TYPES = [ + "Data", "Int", "Float", "Currency", "Percent", "Check", + "Small Text", "Text", "Long Text", "Text Editor", "HTML", + "Date", "Datetime", "Time", "Duration", + "Select", "Link", "Dynamic Link", "Table", "Table MultiSelect", + "Attach", "Attach Image", "Signature", "Color", "Barcode", + "Geolocation", "Rating", "Section Break", "Column Break", "Tab Break", +] + +_FIELD_SCHEMA = { + "type": "object", + "required": ["dt", "fieldname", "fieldtype", "label"], + "properties": { + "dt": {"type": "string", "description": "DocType to add the field to"}, + "fieldname": {"type": "string"}, + "fieldtype": {"type": "string", "enum": _FIELD_TYPES}, + "label": {"type": "string"}, + "options": {"type": "string", "description": "For Link: target DocType. For Select: newline-separated options."}, + "reqd": {"type": "integer", "default": 0}, + "in_list_view": {"type": "integer", "default": 0}, + "in_standard_filter": {"type": "integer", "default": 0}, + "insert_after": {"type": "string", "description": "Fieldname to insert after"}, + "default": {"type": "string"}, + "description": {"type": "string"}, + "hidden": {"type": "integer", "default": 0}, + "read_only": {"type": "integer", "default": 0}, + "bold": {"type": "integer", "default": 0}, + }, +} + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_add_custom_field", + description="Add a custom field to an existing DocType.", + inputSchema=_FIELD_SCHEMA, + ), + Tool( + name="frappe_update_custom_field", + description="Update properties of an existing custom field.", + inputSchema={ + "type": "object", + "required": ["dt", "fieldname"], + "properties": { + "dt": {"type": "string"}, + "fieldname": {"type": "string"}, + "updates": {"type": "object", "description": "Properties to update"}, + }, + }, + ), + Tool( + name="frappe_remove_custom_field", + description="Remove a custom field from a DocType.", + inputSchema={ + "type": "object", + "required": ["dt", "fieldname"], + "properties": { + "dt": {"type": "string"}, + "fieldname": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_list_custom_fields", + description="List all custom fields for a DocType.", + inputSchema={ + "type": "object", + "required": ["dt"], + "properties": { + "dt": {"type": "string"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_add_custom_field": _add_custom_field, + "frappe_update_custom_field": _update_custom_field, + "frappe_remove_custom_field": _remove_custom_field, + "frappe_list_custom_fields": _list_custom_fields, + } + + +async def _add_custom_field(args: dict) -> str: + client = FrappeClient() + payload = {k: v for k, v in args.items()} + result = await client.create_doc("Custom Field", payload) + return json.dumps(result, indent=2) + + +async def _update_custom_field(args: dict) -> str: + client = FrappeClient() + # Custom Field name is "{DocType}-{fieldname}" + cf_name = f"{args['dt']}-{args['fieldname']}" + result = await client.update_doc("Custom Field", cf_name, args.get("updates", {})) + return json.dumps(result, indent=2) + + +async def _remove_custom_field(args: dict) -> str: + client = FrappeClient() + cf_name = f"{args['dt']}-{args['fieldname']}" + result = await client.delete_doc("Custom Field", cf_name) + return json.dumps(result, indent=2) + + +async def _list_custom_fields(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + "Custom Field", + fields=["name", "fieldname", "fieldtype", "label", "reqd", "hidden"], + filters=[["dt", "=", args["dt"]]], + limit=100, + ) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/dashboards.py b/frappe_mcp/tools/dashboards.py new file mode 100644 index 0000000..10736a0 --- /dev/null +++ b/frappe_mcp/tools/dashboards.py @@ -0,0 +1,217 @@ +"""Dashboard, Chart, Number Card, and Workspace tools.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_dashboards", + description="List all Frappe Dashboards.", + inputSchema={ + "type": "object", + "properties": {"limit": {"type": "integer", "default": 20}}, + }, + ), + Tool( + name="frappe_get_dashboard", + description="Get a Dashboard with all its charts and cards.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": {"name": {"type": "string"}}, + }, + ), + Tool( + name="frappe_create_dashboard", + description="Create a new Dashboard.", + inputSchema={ + "type": "object", + "required": ["dashboard_name"], + "properties": { + "dashboard_name": {"type": "string"}, + "module": {"type": "string"}, + "is_default": {"type": "integer", "default": 0}, + "charts": { + "type": "array", + "items": {"type": "object"}, + "description": "List of {chart, width} objects", + }, + }, + }, + ), + Tool( + name="frappe_create_dashboard_chart", + description="Create a Dashboard Chart (bar, line, pie, donut, percentage, heatmap).", + inputSchema={ + "type": "object", + "required": ["chart_name", "chart_type", "document_type"], + "properties": { + "chart_name": {"type": "string"}, + "chart_type": { + "type": "string", + "enum": ["Count", "Sum", "Average", "Min", "Max", "Group By"], + }, + "document_type": {"type": "string"}, + "based_on": {"type": "string", "description": "Date field for time-series grouping"}, + "value_based_on": {"type": "string", "description": "Field to aggregate"}, + "type": { + "type": "string", + "enum": ["Bar", "Line", "Percentage", "Pie", "Donut", "Heatmap"], + "default": "Bar", + }, + "timespan": { + "type": "string", + "enum": ["Last Year", "Last Quarter", "Last Month", "Last Week"], + "default": "Last Year", + }, + "time_interval": { + "type": "string", + "enum": ["Yearly", "Quarterly", "Monthly", "Weekly", "Daily"], + "default": "Monthly", + }, + "filters_json": {"type": "string", "description": "JSON string of filters"}, + "color": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_create_number_card", + description="Create a Number Card for dashboards showing a single aggregated metric.", + inputSchema={ + "type": "object", + "required": ["name", "document_type", "function"], + "properties": { + "name": {"type": "string"}, + "label": {"type": "string"}, + "document_type": {"type": "string"}, + "function": { + "type": "string", + "enum": ["Count", "Sum", "Average", "Min", "Max"], + "default": "Count", + }, + "aggregate_function_based_on": {"type": "string"}, + "filters_json": {"type": "string"}, + "color": {"type": "string"}, + "stats_time_interval": {"type": "string", "enum": ["Daily", "Weekly", "Monthly", "Quarterly", "Yearly"]}, + }, + }, + ), + # --- Workspace --- + Tool( + name="frappe_list_workspaces", + description="List all Desk Workspaces.", + inputSchema={ + "type": "object", + "properties": {"limit": {"type": "integer", "default": 30}}, + }, + ), + Tool( + name="frappe_get_workspace", + description="Get a Workspace with all its shortcuts, links, and charts.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": {"name": {"type": "string"}}, + }, + ), + Tool( + name="frappe_create_workspace", + description="Create a new Desk Workspace with shortcuts and links.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + "label": {"type": "string"}, + "module": {"type": "string"}, + "icon": {"type": "string"}, + "is_hidden": {"type": "integer", "default": 0}, + "shortcuts": { + "type": "array", + "items": {"type": "object"}, + "description": "List of shortcut objects {label, link_to, type}", + }, + "links": { + "type": "array", + "items": {"type": "object"}, + "description": "List of link objects for the workspace body", + }, + }, + }, + ), + Tool( + name="frappe_update_workspace", + description="Update a Workspace's content, shortcuts, or visibility.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + "updates": {"type": "object"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_dashboards": _list_dashboards, + "frappe_get_dashboard": _get_dashboard, + "frappe_create_dashboard": _create_dashboard, + "frappe_create_dashboard_chart": _create_dashboard_chart, + "frappe_create_number_card": _create_number_card, + "frappe_list_workspaces": _list_workspaces, + "frappe_get_workspace": _get_workspace, + "frappe_create_workspace": _create_workspace, + "frappe_update_workspace": _update_workspace, + } + + +async def _list_dashboards(args: dict) -> str: + client = FrappeClient() + result = await client.get_list("Dashboard", fields=["name", "module", "is_default"], limit=args.get("limit", 20)) + return json.dumps(result, indent=2) + +async def _get_dashboard(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("Dashboard", args["name"]) + return json.dumps(result, indent=2) + +async def _create_dashboard(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("Dashboard", args) + return json.dumps(result, indent=2) + +async def _create_dashboard_chart(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("Dashboard Chart", args) + return json.dumps(result, indent=2) + +async def _create_number_card(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("Number Card", args) + return json.dumps(result, indent=2) + +async def _list_workspaces(args: dict) -> str: + client = FrappeClient() + result = await client.get_list("Workspace", fields=["name", "label", "module", "is_hidden"], limit=args.get("limit", 30)) + return json.dumps(result, indent=2) + +async def _get_workspace(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("Workspace", args["name"]) + return json.dumps(result, indent=2) + +async def _create_workspace(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("Workspace", args) + return json.dumps(result, indent=2) + +async def _update_workspace(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc("Workspace", args["name"], args.get("updates", {})) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/doctypes.py b/frappe_mcp/tools/doctypes.py new file mode 100644 index 0000000..e166720 --- /dev/null +++ b/frappe_mcp/tools/doctypes.py @@ -0,0 +1,155 @@ +"""DocType management tools — create, inspect, list, and modify DocTypes.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_doctypes", + description="List all DocTypes in the Frappe instance, optionally filtered by module.", + inputSchema={ + "type": "object", + "properties": { + "module": {"type": "string", "description": "Filter by module name (optional)"}, + "limit": {"type": "integer", "default": 50}, + }, + }, + ), + Tool( + name="frappe_get_doctype", + description="Get the full schema/definition of a DocType including all fields.", + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string", "description": "DocType name e.g. 'Sales Order'"}, + }, + }, + ), + Tool( + name="frappe_create_doctype", + description=( + "Create a new custom DocType. Provide name, module, and a list of fields. " + "Each field needs at minimum: fieldname, fieldtype, label." + ), + inputSchema={ + "type": "object", + "required": ["name", "module", "fields"], + "properties": { + "name": {"type": "string"}, + "module": {"type": "string"}, + "is_submittable": {"type": "boolean", "default": False}, + "istable": {"type": "integer", "default": 0, "description": "Set 1 to make this a child table DocType"}, + "track_changes": {"type": "boolean", "default": True}, + "autoname": {"type": "string", "description": "e.g. 'hash', 'field:fieldname', 'naming_series:'"}, + "title_field": {"type": "string", "description": "Field to use as document title"}, + "search_fields": {"type": "string", "description": "Comma-separated fields for search"}, + "sort_field": {"type": "string"}, + "sort_order": {"type": "string", "enum": ["ASC", "DESC"], "default": "DESC"}, + "fields": { + "type": "array", + "items": { + "type": "object", + "required": ["fieldname", "fieldtype", "label"], + "properties": { + "fieldname": {"type": "string"}, + "fieldtype": {"type": "string"}, + "label": {"type": "string"}, + "reqd": {"type": "integer", "default": 0}, + "in_list_view": {"type": "integer", "default": 0}, + "options": {"type": "string"}, + }, + }, + }, + "permissions": { + "type": "array", + "description": "List of permission rows. Defaults to System Manager full access.", + }, + }, + }, + ), + Tool( + name="frappe_update_doctype", + description="Update an existing DocType — add/remove fields or change properties.", + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + "updates": { + "type": "object", + "description": "Fields to update on the DocType document (e.g. {\"fields\": [...]})", + }, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_doctypes": _list_doctypes, + "frappe_get_doctype": _get_doctype, + "frappe_create_doctype": _create_doctype, + "frappe_update_doctype": _update_doctype, + } + + +async def _list_doctypes(args: dict) -> str: + client = FrappeClient() + filters = [] + if module := args.get("module"): + filters.append(["module", "=", module]) + result = await client.get_list( + "DocType", + fields=["name", "module", "is_single", "is_submittable", "modified"], + filters=filters if filters else None, + limit=args.get("limit", 50), + ) + return json.dumps(result, indent=2) + + +async def _get_doctype(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("DocType", args["doctype"]) + return json.dumps(result, indent=2) + + +async def _create_doctype(args: dict) -> str: + client = FrappeClient() + istable = int(args.get("istable", 0)) + payload = { + "name": args["name"], + "module": args["module"], + "custom": 1, + "istable": istable, + "is_submittable": int(args.get("is_submittable", False)), + "track_changes": int(args.get("track_changes", True)), + "fields": args["fields"], + } + if v := args.get("autoname"): + payload["autoname"] = v + if v := args.get("title_field"): + payload["title_field"] = v + if v := args.get("search_fields"): + payload["search_fields"] = v + if v := args.get("sort_field"): + payload["sort_field"] = v + if v := args.get("sort_order"): + payload["sort_order"] = v + # child tables don't need permissions + if not istable: + payload["permissions"] = args.get("permissions", [ + {"role": "System Manager", "read": 1, "write": 1, "create": 1, "delete": 1, "submit": 0}, + ]) + result = await client.create_doc("DocType", payload) + return json.dumps(result, indent=2) + + +async def _update_doctype(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc("DocType", args["doctype"], args.get("updates", {})) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/document_inspect.py b/frappe_mcp/tools/document_inspect.py new file mode 100644 index 0000000..cdeb42c --- /dev/null +++ b/frappe_mcp/tools/document_inspect.py @@ -0,0 +1,267 @@ +""" +Level 2 — Document inspection tools. +Search, linked records, timeline, children, counts. +""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_search_documents", + description=( + "Generic full-text search across any DocType. " + "Searches title/name fields. Use filters for precise queries." + ), + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + "query": {"type": "string", "description": "Search text"}, + "filters": {"type": "array", "description": "Additional filter conditions"}, + "fields": {"type": "array", "items": {"type": "string"}}, + "limit": {"type": "integer", "default": 20}, + "order_by": {"type": "string", "default": "modified desc"}, + }, + }, + ), + Tool( + name="frappe_get_document_children", + description=( + "Read child table rows for a document. " + "Use get_doctype_meta to find the child table fieldname first." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "name", "child_doctype"], + "properties": { + "doctype": {"type": "string", "description": "Parent DocType"}, + "name": {"type": "string", "description": "Parent document name"}, + "child_doctype": {"type": "string", "description": "Child table DocType e.g. 'Sales Order Item'"}, + "fields": {"type": "array", "items": {"type": "string"}}, + }, + }, + ), + Tool( + name="frappe_get_linked_documents", + description=( + "Returns all documents that link TO this document — " + "e.g. all Sales Orders linked to a Customer, or all Invoices for a Sales Order." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "linked_doctype": { + "type": "string", + "description": "Filter to only this DocType (optional)", + }, + "limit": {"type": "integer", "default": 20}, + }, + }, + ), + Tool( + name="frappe_get_document_timeline", + description=( + "Get the full activity timeline for a document: " + "comments, emails, assignments, workflow changes, version history." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "limit": {"type": "integer", "default": 30}, + }, + }, + ), + Tool( + name="frappe_get_recent_documents", + description="Get recently modified documents of a DocType. Useful for 'show latest X' requests.", + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + "fields": {"type": "array", "items": {"type": "string"}}, + "limit": {"type": "integer", "default": 10}, + "modified_after": {"type": "string", "description": "ISO datetime filter e.g. '2024-01-01'"}, + }, + }, + ), + Tool( + name="frappe_count_documents", + description="Count records matching filters. Fast — does not return document data.", + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + "filters": {"type": "array"}, + }, + }, + ), + Tool( + name="frappe_get_document_attachments", + description="List all files attached to a specific document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_get_document_versions", + description="Get the change history (versions) of a document — who changed what fields and when.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "limit": {"type": "integer", "default": 10}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_search_documents": _search_documents, + "frappe_get_document_children": _get_document_children, + "frappe_get_linked_documents": _get_linked_documents, + "frappe_get_document_timeline": _get_document_timeline, + "frappe_get_recent_documents": _get_recent_documents, + "frappe_count_documents": _count_documents, + "frappe_get_document_attachments": _get_document_attachments, + "frappe_get_document_versions": _get_document_versions, + } + + +async def _search_documents(args: dict) -> str: + client = FrappeClient() + filters = list(args.get("filters") or []) + if query := args.get("query"): + filters.append(["name", "like", f"%{query}%"]) + result = await client.get_list( + args["doctype"], + fields=args.get("fields", ["name", "modified", "owner"]), + filters=filters if filters else None, + limit=args.get("limit", 20), + order_by=args.get("order_by", "modified desc"), + ) + return json.dumps(result, indent=2) + + +async def _get_document_children(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + args["child_doctype"], + fields=args.get("fields", ["*"]) or ["*"], + filters=[["parent", "=", args["name"]], ["parenttype", "=", args["doctype"]]], + limit=500, + order_by="idx asc", + ) + return json.dumps(result, indent=2) + + +async def _get_linked_documents(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.client.get_linked_docs", + doctype=args["doctype"], + name=args["name"], + linkinfo=args.get("linked_doctype", ""), + ) + return json.dumps(result, indent=2) + + +async def _get_document_timeline(args: dict) -> str: + client = FrappeClient() + ref_dt = args["doctype"] + ref_name = args["name"] + limit = args.get("limit", 30) + + comments = await client.get_list( + "Comment", + fields=["name", "comment_type", "comment_by", "content", "creation"], + filters=[["reference_doctype", "=", ref_dt], ["reference_name", "=", ref_name]], + limit=limit, + order_by="creation desc", + ) + versions = await client.get_list( + "Version", + fields=["name", "owner", "creation", "data"], + filters=[["ref_doctype", "=", ref_dt], ["docname", "=", ref_name]], + limit=limit, + order_by="creation desc", + ) + timeline = sorted( + [{"type": "comment", **c} for c in (comments if isinstance(comments, list) else [])] + + [{"type": "version", **v} for v in (versions if isinstance(versions, list) else [])], + key=lambda x: x.get("creation", ""), + reverse=True, + )[:limit] + return json.dumps(timeline, indent=2) + + +async def _get_recent_documents(args: dict) -> str: + client = FrappeClient() + filters = [] + if after := args.get("modified_after"): + filters.append(["modified", ">", after]) + result = await client.get_list( + args["doctype"], + fields=args.get("fields", ["name", "modified", "owner", "docstatus"]), + filters=filters if filters else None, + limit=args.get("limit", 10), + order_by="modified desc", + ) + return json.dumps(result, indent=2) + + +async def _count_documents(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.client.get_count", + doctype=args["doctype"], + filters=args.get("filters"), + ) + return json.dumps({"doctype": args["doctype"], "count": result}, indent=2) + + +async def _get_document_attachments(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + "File", + fields=["name", "file_name", "file_url", "file_size", "is_private", "creation"], + filters=[ + ["attached_to_doctype", "=", args["doctype"]], + ["attached_to_name", "=", args["name"]], + ], + limit=50, + ) + return json.dumps(result, indent=2) + + +async def _get_document_versions(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + "Version", + fields=["name", "owner", "creation", "data"], + filters=[["ref_doctype", "=", args["doctype"]], ["docname", "=", args["name"]]], + limit=args.get("limit", 10), + order_by="creation desc", + ) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/document_lifecycle.py b/frappe_mcp/tools/document_lifecycle.py new file mode 100644 index 0000000..8fb9f74 --- /dev/null +++ b/frappe_mcp/tools/document_lifecycle.py @@ -0,0 +1,229 @@ +""" +Level 3-4 — Document lifecycle tools. +Child row operations, rename, amend, duplicate, status, draft save. +""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + # --- Child row ops --- + Tool( + name="frappe_append_child_row", + description="Append a new row to a child table in a document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name", "child_fieldname", "row_data"], + "properties": { + "doctype": {"type": "string", "description": "Parent DocType"}, + "name": {"type": "string", "description": "Parent document name"}, + "child_fieldname": {"type": "string", "description": "Fieldname of the child table e.g. 'items'"}, + "row_data": {"type": "object", "description": "Fields for the new row"}, + }, + }, + ), + Tool( + name="frappe_update_child_row", + description="Update a specific row in a child table by its row name.", + inputSchema={ + "type": "object", + "required": ["child_doctype", "row_name", "updates"], + "properties": { + "child_doctype": {"type": "string", "description": "Child table DocType e.g. 'Sales Order Item'"}, + "row_name": {"type": "string", "description": "Child row document name"}, + "updates": {"type": "object"}, + }, + }, + ), + Tool( + name="frappe_delete_child_row", + description="Delete a specific row from a child table.", + inputSchema={ + "type": "object", + "required": ["child_doctype", "row_name"], + "properties": { + "child_doctype": {"type": "string"}, + "row_name": {"type": "string"}, + }, + }, + ), + # --- Rename --- + Tool( + name="frappe_rename_document", + description="Rename a document (changes its primary key/name). Only works if DocType allows renaming.", + inputSchema={ + "type": "object", + "required": ["doctype", "old_name", "new_name"], + "properties": { + "doctype": {"type": "string"}, + "old_name": {"type": "string"}, + "new_name": {"type": "string"}, + "merge": {"type": "boolean", "default": False, "description": "Merge into existing document if new_name exists"}, + }, + }, + ), + # --- Amend --- + Tool( + name="frappe_amend_document", + description=( + "Create an amendment of a cancelled/submitted document. " + "Returns a new draft with amended_ prefix and links to original." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + # --- Duplicate --- + Tool( + name="frappe_duplicate_document", + description="Clone a document — creates a new draft copy with the same field values.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "field_overrides": { + "type": "object", + "description": "Fields to override in the copy e.g. {\"customer\": \"New Customer\"}", + }, + }, + }, + ), + # --- Status --- + Tool( + name="frappe_get_document_status", + description=( + "Get the normalized status of a document: " + "draft | saved | submitted | cancelled | workflow_state." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + # --- Draft save --- + Tool( + name="frappe_save_document_draft", + description=( + "Explicitly save a document as draft (docstatus=0). " + "Use when you want to save partial data without validating all required fields." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "updates": {"type": "object", "description": "Optional field updates to apply before saving"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_append_child_row": _append_child_row, + "frappe_update_child_row": _update_child_row, + "frappe_delete_child_row": _delete_child_row, + "frappe_rename_document": _rename_document, + "frappe_amend_document": _amend_document, + "frappe_duplicate_document": _duplicate_document, + "frappe_get_document_status": _get_document_status, + "frappe_save_document_draft": _save_document_draft, + } + + +_DOCSTATUS_MAP = {0: "draft/saved", 1: "submitted", 2: "cancelled"} + + +async def _append_child_row(args: dict) -> str: + client = FrappeClient() + doc = await client.get_doc(args["doctype"], args["name"]) + rows = doc.get(args["child_fieldname"], []) + rows.append(args["row_data"]) + result = await client.update_doc(args["doctype"], args["name"], {args["child_fieldname"]: rows}) + return json.dumps(result, indent=2) + + +async def _update_child_row(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc(args["child_doctype"], args["row_name"], args["updates"]) + return json.dumps(result, indent=2) + + +async def _delete_child_row(args: dict) -> str: + client = FrappeClient() + result = await client.delete_doc(args["child_doctype"], args["row_name"]) + return json.dumps(result, indent=2) + + +async def _rename_document(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.client.rename_doc", + doctype=args["doctype"], + old_name=args["old_name"], + new_name=args["new_name"], + merge=args.get("merge", False), + ) + return json.dumps({"renamed_to": result}, indent=2) + + +async def _amend_document(args: dict) -> str: + client = FrappeClient() + doc = await client.get_doc(args["doctype"], args["name"]) + amended = {k: v for k, v in doc.items() if not k.startswith("__")} + amended.pop("name", None) + amended["amended_from"] = args["name"] + amended["docstatus"] = 0 + result = await client.create_doc(args["doctype"], amended) + return json.dumps(result, indent=2) + + +async def _duplicate_document(args: dict) -> str: + client = FrappeClient() + doc = await client.get_doc(args["doctype"], args["name"]) + copy = {k: v for k, v in doc.items() if not k.startswith("__")} + copy.pop("name", None) + copy["docstatus"] = 0 + copy.update(args.get("field_overrides", {})) + result = await client.create_doc(args["doctype"], copy) + return json.dumps(result, indent=2) + + +async def _get_document_status(args: dict) -> str: + client = FrappeClient() + doc = await client.get_doc(args["doctype"], args["name"]) + docstatus = doc.get("docstatus", 0) + workflow_state = doc.get("workflow_state", None) + status = doc.get("status", None) + return json.dumps({ + "name": args["name"], + "doctype": args["doctype"], + "docstatus": docstatus, + "docstatus_label": _DOCSTATUS_MAP.get(docstatus, "unknown"), + "status": status, + "workflow_state": workflow_state, + }, indent=2) + + +async def _save_document_draft(args: dict) -> str: + client = FrappeClient() + updates = args.get("updates", {}) + updates["docstatus"] = 0 + result = await client.update_doc(args["doctype"], args["name"], updates) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/documents.py b/frappe_mcp/tools/documents.py new file mode 100644 index 0000000..5150f7c --- /dev/null +++ b/frappe_mcp/tools/documents.py @@ -0,0 +1,169 @@ +"""Document CRUD tools — create, read, update, delete, list Frappe documents.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient +from frappe_mcp.config import get_settings + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_documents", + description="List documents of a given DocType with optional filters.", + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + "fields": { + "type": "array", + "items": {"type": "string"}, + "description": "Fields to return. Defaults to ['name', 'modified'].", + }, + "filters": { + "type": "array", + "description": "Filter list e.g. [[\"status\", \"=\", \"Open\"]]", + }, + "limit": {"type": "integer", "default": 20}, + "order_by": {"type": "string", "default": "modified desc"}, + }, + }, + ), + Tool( + name="frappe_get_document", + description="Get a single Frappe document by DocType and name.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_create_document", + description="Create a new document in any DocType.", + inputSchema={ + "type": "object", + "required": ["doctype", "data"], + "properties": { + "doctype": {"type": "string"}, + "data": {"type": "object", "description": "Field values for the new document"}, + }, + }, + ), + Tool( + name="frappe_update_document", + description="Update fields on an existing document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name", "data"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "data": {"type": "object", "description": "Fields to update"}, + }, + }, + ), + Tool( + name="frappe_delete_document", + description="Delete a document. Blocked in read-only mode.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_submit_document", + description="Submit a submittable document (changes status to Submitted).", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_cancel_document", + description="Cancel a submitted document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_documents": _list_documents, + "frappe_get_document": _get_document, + "frappe_create_document": _create_document, + "frappe_update_document": _update_document, + "frappe_delete_document": _delete_document, + "frappe_submit_document": _submit_document, + "frappe_cancel_document": _cancel_document, + } + + +async def _list_documents(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + args["doctype"], + fields=args.get("fields", ["name", "modified"]), + filters=args.get("filters"), + limit=args.get("limit", 20), + order_by=args.get("order_by", "modified desc"), + ) + return json.dumps(result, indent=2) + + +async def _get_document(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc(args["doctype"], args["name"]) + return json.dumps(result, indent=2) + + +async def _create_document(args: dict) -> str: + client = FrappeClient() + data = {"doctype": args["doctype"], **args["data"]} + result = await client.create_doc(args["doctype"], data) + return json.dumps(result, indent=2) + + +async def _update_document(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc(args["doctype"], args["name"], args["data"]) + return json.dumps(result, indent=2) + + +async def _delete_document(args: dict) -> str: + if get_settings().read_only_mode: + return "Error: read_only_mode is enabled. Deletion blocked." + client = FrappeClient() + result = await client.delete_doc(args["doctype"], args["name"]) + return json.dumps(result, indent=2) + + +async def _submit_document(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc(args["doctype"], args["name"], {"docstatus": 1}) + return json.dumps(result, indent=2) + + +async def _cancel_document(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc(args["doctype"], args["name"], {"docstatus": 2}) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/email_templates.py b/frappe_mcp/tools/email_templates.py new file mode 100644 index 0000000..de14c30 --- /dev/null +++ b/frappe_mcp/tools/email_templates.py @@ -0,0 +1,220 @@ +"""Email Template tools — create, list, update Frappe Email Templates and Notifications.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_email_templates", + description="List all Email Templates in the system.", + inputSchema={ + "type": "object", + "properties": { + "limit": {"type": "integer", "default": 30}, + }, + }, + ), + Tool( + name="frappe_get_email_template", + description="Get a specific Email Template including subject and body.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_create_email_template", + description=( + "Create a new Email Template. Subject and response support Jinja2. " + "Access doc fields with {{ doc.field_name }}." + ), + inputSchema={ + "type": "object", + "required": ["name", "subject", "response"], + "properties": { + "name": {"type": "string", "description": "Template name/ID"}, + "subject": {"type": "string", "description": "Email subject (Jinja2 supported)"}, + "response": {"type": "string", "description": "Email body HTML (Jinja2 supported)"}, + "use_html": {"type": "integer", "default": 1}, + }, + }, + ), + Tool( + name="frappe_update_email_template", + description="Update an Email Template's subject or body.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + "subject": {"type": "string"}, + "response": {"type": "string"}, + "updates": {"type": "object"}, + }, + }, + ), + Tool( + name="frappe_list_notifications", + description="List Frappe Notifications (automated email/Slack alerts on DocType events).", + inputSchema={ + "type": "object", + "properties": { + "document_type": {"type": "string", "description": "Filter by DocType"}, + "enabled": {"type": "integer", "description": "1 = enabled only"}, + "limit": {"type": "integer", "default": 30}, + }, + }, + ), + Tool( + name="frappe_create_notification", + description=( + "Create a Frappe Notification — triggers an email/Slack/system message " + "on a DocType event or date condition." + ), + inputSchema={ + "type": "object", + "required": ["name", "document_type", "event", "subject", "message"], + "properties": { + "name": {"type": "string"}, + "document_type": {"type": "string"}, + "event": { + "type": "string", + "enum": [ + "New", "Save", "Submit", "Cancel", "Days Before", + "Days After", "Value Change", "Method", "Custom", + ], + }, + "subject": {"type": "string", "description": "Email subject (Jinja2)"}, + "message": {"type": "string", "description": "Email body (Jinja2 HTML)"}, + "send_to_all_assignees": {"type": "integer", "default": 0}, + "recipients": { + "type": "array", + "description": "List of recipient rows: [{\"receiver_by_document_field\": \"email\"}]", + "items": {"type": "object"}, + }, + "condition": {"type": "string", "description": "Python expression e.g. doc.status == 'Open'"}, + "channel": { + "type": "string", + "enum": ["Email", "Slack", "System Notification", "SMS"], + "default": "Email", + }, + "enabled": {"type": "integer", "default": 1}, + }, + }, + ), + Tool( + name="frappe_update_notification", + description="Enable, disable, or update a Notification.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + "enabled": {"type": "integer"}, + "subject": {"type": "string"}, + "message": {"type": "string"}, + "updates": {"type": "object"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_email_templates": _list_email_templates, + "frappe_get_email_template": _get_email_template, + "frappe_create_email_template": _create_email_template, + "frappe_update_email_template": _update_email_template, + "frappe_list_notifications": _list_notifications, + "frappe_create_notification": _create_notification, + "frappe_update_notification": _update_notification, + } + + +async def _list_email_templates(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + "Email Template", + fields=["name", "subject", "modified"], + limit=args.get("limit", 30), + ) + return json.dumps(result, indent=2) + + +async def _get_email_template(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("Email Template", args["name"]) + return json.dumps(result, indent=2) + + +async def _create_email_template(args: dict) -> str: + client = FrappeClient() + payload = { + "name": args["name"], + "subject": args["subject"], + "response": args["response"], + "use_html": args.get("use_html", 1), + } + result = await client.create_doc("Email Template", payload) + return json.dumps(result, indent=2) + + +async def _update_email_template(args: dict) -> str: + client = FrappeClient() + updates = args.get("updates", {}) + for field in ("subject", "response"): + if field in args: + updates[field] = args[field] + result = await client.update_doc("Email Template", args["name"], updates) + return json.dumps(result, indent=2) + + +async def _list_notifications(args: dict) -> str: + client = FrappeClient() + filters = [] + if dt := args.get("document_type"): + filters.append(["document_type", "=", dt]) + if "enabled" in args: + filters.append(["enabled", "=", args["enabled"]]) + result = await client.get_list( + "Notification", + fields=["name", "document_type", "event", "channel", "enabled"], + filters=filters if filters else None, + limit=args.get("limit", 30), + ) + return json.dumps(result, indent=2) + + +async def _create_notification(args: dict) -> str: + client = FrappeClient() + payload = { + "name": args["name"], + "document_type": args["document_type"], + "event": args["event"], + "subject": args["subject"], + "message": args["message"], + "channel": args.get("channel", "Email"), + "enabled": args.get("enabled", 1), + "send_to_all_assignees": args.get("send_to_all_assignees", 0), + "recipients": args.get("recipients", []), + "condition": args.get("condition", ""), + } + result = await client.create_doc("Notification", payload) + return json.dumps(result, indent=2) + + +async def _update_notification(args: dict) -> str: + client = FrappeClient() + updates = args.get("updates", {}) + for field in ("enabled", "subject", "message"): + if field in args: + updates[field] = args[field] + result = await client.update_doc("Notification", args["name"], updates) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/files.py b/frappe_mcp/tools/files.py new file mode 100644 index 0000000..d0b45b2 --- /dev/null +++ b/frappe_mcp/tools/files.py @@ -0,0 +1,168 @@ +"""File and attachment management tools.""" + +import json +import base64 +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_files", + description="List files in Frappe's file manager, optionally filtered by attached document.", + inputSchema={ + "type": "object", + "properties": { + "attached_to_doctype": {"type": "string"}, + "attached_to_name": {"type": "string"}, + "folder": {"type": "string", "description": "e.g. 'Home/Attachments'"}, + "is_private": {"type": "integer", "description": "1 = private, 0 = public"}, + "limit": {"type": "integer", "default": 20}, + }, + }, + ), + Tool( + name="frappe_get_file", + description="Get details of a file by its name/ID.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_upload_file_base64", + description=( + "Upload a file to Frappe by providing base64-encoded content. " + "Optionally attach it to a document." + ), + inputSchema={ + "type": "object", + "required": ["filename", "content_base64"], + "properties": { + "filename": {"type": "string", "description": "File name with extension"}, + "content_base64": {"type": "string", "description": "Base64-encoded file content"}, + "attached_to_doctype": {"type": "string"}, + "attached_to_name": {"type": "string"}, + "attached_to_field": {"type": "string"}, + "is_private": {"type": "integer", "default": 1}, + "folder": {"type": "string", "default": "Home/Attachments"}, + }, + }, + ), + Tool( + name="frappe_delete_file", + description="Delete a file from Frappe.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string", "description": "File document name"}, + }, + }, + ), + Tool( + name="frappe_move_file", + description="Move a file to a different folder.", + inputSchema={ + "type": "object", + "required": ["name", "folder"], + "properties": { + "name": {"type": "string"}, + "folder": {"type": "string", "description": "Target folder path"}, + }, + }, + ), + Tool( + name="frappe_create_folder", + description="Create a new folder in the Frappe file manager.", + inputSchema={ + "type": "object", + "required": ["folder_name"], + "properties": { + "folder_name": {"type": "string"}, + "parent_folder": {"type": "string", "default": "Home"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_files": _list_files, + "frappe_get_file": _get_file, + "frappe_upload_file_base64": _upload_file_base64, + "frappe_delete_file": _delete_file, + "frappe_move_file": _move_file, + "frappe_create_folder": _create_folder, + } + + +async def _list_files(args: dict) -> str: + client = FrappeClient() + filters = [] + if dt := args.get("attached_to_doctype"): + filters.append(["attached_to_doctype", "=", dt]) + if name := args.get("attached_to_name"): + filters.append(["attached_to_name", "=", name]) + if folder := args.get("folder"): + filters.append(["folder", "=", folder]) + if "is_private" in args: + filters.append(["is_private", "=", args["is_private"]]) + result = await client.get_list( + "File", + fields=["name", "file_name", "file_url", "file_size", "is_private", "folder", "attached_to_doctype", "attached_to_name"], + filters=filters if filters else None, + limit=args.get("limit", 20), + ) + return json.dumps(result, indent=2) + + +async def _get_file(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("File", args["name"]) + return json.dumps(result, indent=2) + + +async def _upload_file_base64(args: dict) -> str: + client = FrappeClient() + payload = { + "filename": args["filename"], + "filedata": args["content_base64"], + "is_private": args.get("is_private", 1), + "folder": args.get("folder", "Home/Attachments"), + } + if dt := args.get("attached_to_doctype"): + payload["doctype"] = dt + if name := args.get("attached_to_name"): + payload["docname"] = name + if field := args.get("attached_to_field"): + payload["fieldname"] = field + result = await client.call_method("frappe.client.attach_file", **payload) + return json.dumps(result, indent=2) + + +async def _delete_file(args: dict) -> str: + client = FrappeClient() + result = await client.delete_doc("File", args["name"]) + return json.dumps(result, indent=2) + + +async def _move_file(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc("File", args["name"], {"folder": args["folder"]}) + return json.dumps(result, indent=2) + + +async def _create_folder(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.core.doctype.file.file.create_new_folder", + file_name=args["folder_name"], + folder=args.get("parent_folder", "Home"), + ) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/foundation.py b/frappe_mcp/tools/foundation.py new file mode 100644 index 0000000..a133507 --- /dev/null +++ b/frappe_mcp/tools/foundation.py @@ -0,0 +1,144 @@ +""" +Level 1 — Foundation tools. +These are the absolute minimum. Without these, the AI is blind. +""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_ping", + description="Check whether Frappe is reachable. Returns version and status.", + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_get_session_info", + description=( + "Returns the current session: logged-in user, roles, site name, " + "enabled modules, and Frappe version. Call this first to understand context." + ), + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_get_doctype_permissions", + description=( + "Returns what the current user can do on a DocType: " + "read, write, create, delete, submit, cancel, amend, print, email, export, import." + ), + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_list_modules", + description=( + "List all Frappe/ERPNext modules available on the site " + "(Selling, Buying, Stock, Accounts, HR, CRM, Support, Custom, etc.)." + ), + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_get_doctype_meta", + description=( + "Get comprehensive DocType schema: all fields with types, required flags, " + "options, child tables, link fields, and permission hints. " + "Essential before creating or editing documents." + ), + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + "include_permissions": {"type": "boolean", "default": True}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_ping": _ping, + "frappe_get_session_info": _get_session_info, + "frappe_get_doctype_permissions": _get_doctype_permissions, + "frappe_list_modules": _list_modules, + "frappe_get_doctype_meta": _get_doctype_meta, + } + + +async def _ping(args: dict) -> str: + client = FrappeClient() + try: + versions = await client.call_method("frappe.utils.change_log.get_versions") + frappe_ver = versions.get("frappe", {}).get("version", "unknown") if isinstance(versions, dict) else "unknown" + return json.dumps({"status": "ok", "frappe_version": frappe_ver, "url": client.base_url}, indent=2) + except Exception as e: + return json.dumps({"status": "unreachable", "error": str(e)}, indent=2) + + +async def _get_session_info(args: dict) -> str: + client = FrappeClient() + user = await client.call_method("frappe.auth.get_logged_user") + user_doc = await client.get_doc("User", user) + roles = [r["role"] for r in user_doc.get("roles", [])] + versions = await client.call_method("frappe.utils.change_log.get_versions") + modules_raw = await client.get_list( + "Module Def", + fields=["name", "app_name"], + limit=100, + ) + return json.dumps({ + "user": user, + "roles": roles, + "frappe_url": client.base_url, + "site_name": client.settings.frappe_site_name or "(default)", + "frappe_version": versions.get("frappe", {}).get("version") if isinstance(versions, dict) else None, + "installed_apps": list(versions.keys()) if isinstance(versions, dict) else [], + "modules": [m["name"] for m in (modules_raw if isinstance(modules_raw, list) else [])], + }, indent=2) + + +async def _get_doctype_permissions(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.client.get_perm", + doctype=args["doctype"], + ) + return json.dumps(result, indent=2) + + +async def _list_modules(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + "Module Def", + fields=["name", "app_name"], + limit=150, + order_by="name asc", + ) + return json.dumps(result, indent=2) + + +async def _get_doctype_meta(args: dict) -> str: + client = FrappeClient() + meta = await client.call_method( + "frappe.desk.form.load.getdoctype", + doctype=args["doctype"], + with_parent=1, + cached_timestamp=None, + ) + if args.get("include_permissions", True): + try: + perms = await client.call_method("frappe.client.get_perm", doctype=args["doctype"]) + if isinstance(meta, dict): + meta["_permissions"] = perms + except Exception: + pass + return json.dumps(meta, indent=2) diff --git a/frappe_mcp/tools/governance.py b/frappe_mcp/tools/governance.py new file mode 100644 index 0000000..f3883e6 --- /dev/null +++ b/frappe_mcp/tools/governance.py @@ -0,0 +1,425 @@ +""" +Level 10 — Safety and governance tools. +Dry run, validate, risk scoring, confirmation tokens, audit log, rollback tracking. +""" + +import json +import time +import hashlib +import secrets +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient +from frappe_mcp.audit_store import log_action, read_audit_log, clear_audit_log + +# In-memory confirmation token store (clears on restart — by design) +_pending_confirmations: dict[str, dict] = {} + +# Reversible action registry: tool_call_id → undo info +_reversible_actions: dict[str, dict] = {} + +# Risk profiles per tool prefix +_RISK_MAP = { + "frappe_delete": "high", + "frappe_bulk_delete": "high", + "frappe_cancel": "high", + "frappe_reset_customization": "high", + "frappe_revoke_api_key": "high", + "frappe_clear_error_logs": "medium", + "frappe_bulk_submit": "medium", + "frappe_submit": "medium", + "frappe_create": "low", + "frappe_update": "low", + "frappe_get": "low", + "frappe_list": "low", + "frappe_ping": "low", +} + + +def _score_risk(tool_name: str, arguments: dict) -> str: + for prefix, level in _RISK_MAP.items(): + if tool_name.startswith(prefix): + return level + if "delete" in tool_name or "cancel" in tool_name or "reset" in tool_name: + return "high" + if "submit" in tool_name or "bulk" in tool_name: + return "medium" + return "low" + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_validate_document_payload", + description=( + "Validate document fields before creating or updating. " + "Checks required fields, field types, and link validity. " + "Does NOT save anything." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "data"], + "properties": { + "doctype": {"type": "string"}, + "data": {"type": "object"}, + "name": {"type": "string", "description": "Provide if validating an update"}, + }, + }, + ), + Tool( + name="frappe_dry_run_action", + description=( + "Simulate a create or update action without saving. " + "Returns what WOULD happen: validation result, computed fields, and any errors." + ), + inputSchema={ + "type": "object", + "required": ["action", "doctype", "data"], + "properties": { + "action": {"type": "string", "enum": ["create", "update"]}, + "doctype": {"type": "string"}, + "data": {"type": "object"}, + "name": {"type": "string", "description": "Required for update dry-run"}, + }, + }, + ), + Tool( + name="frappe_risk_score_action", + description=( + "Score the risk level of a proposed action before executing it. " + "Returns low | medium | high with reasoning." + ), + inputSchema={ + "type": "object", + "required": ["tool_name", "arguments"], + "properties": { + "tool_name": {"type": "string"}, + "arguments": {"type": "object"}, + }, + }, + ), + Tool( + name="frappe_request_confirmation_token", + description=( + "Generate a one-time confirmation token for a high-risk action. " + "The token expires in 60 seconds. " + "Pass the token to frappe_confirm_and_execute to proceed." + ), + inputSchema={ + "type": "object", + "required": ["tool_name", "arguments"], + "properties": { + "tool_name": {"type": "string"}, + "arguments": {"type": "object"}, + "reason": {"type": "string", "description": "Why this action is being performed"}, + }, + }, + ), + Tool( + name="frappe_audit_tool_call", + description="Manually log a tool call to the MCP audit log.", + inputSchema={ + "type": "object", + "required": ["tool_name", "arguments"], + "properties": { + "tool_name": {"type": "string"}, + "arguments": {"type": "object"}, + "result_summary": {"type": "string"}, + "risk": {"type": "string", "enum": ["low", "medium", "high"], "default": "low"}, + }, + }, + ), + Tool( + name="frappe_get_audit_history", + description="Read the MCP-level audit log of all tool calls made in this session.", + inputSchema={ + "type": "object", + "properties": { + "limit": {"type": "integer", "default": 50}, + "tool_filter": {"type": "string", "description": "Filter by tool name substring"}, + }, + }, + ), + Tool( + name="frappe_clear_audit_log", + description="Clear the local MCP audit log file.", + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_tool_health_check", + description=( + "Full health check: Frappe connectivity, auth, read/write access, " + "scheduler status, and MCP module status." + ), + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_policy_check", + description=( + "Check whether an action is allowed under the current MCP policy " + "(read_only_mode, enabled modules, risk thresholds)." + ), + inputSchema={ + "type": "object", + "required": ["tool_name"], + "properties": { + "tool_name": {"type": "string"}, + "arguments": {"type": "object"}, + }, + }, + ), + Tool( + name="frappe_register_reversible_action", + description=( + "Register an action as reversible so it can be rolled back later. " + "Stores doctype, name, and the pre-action state." + ), + inputSchema={ + "type": "object", + "required": ["action_id", "doctype", "name"], + "properties": { + "action_id": {"type": "string"}, + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "pre_state": {"type": "object", "description": "Document state before the action"}, + }, + }, + ), + Tool( + name="frappe_rollback_action", + description=( + "Roll back a previously registered reversible action " + "by restoring the document to its pre-action state." + ), + inputSchema={ + "type": "object", + "required": ["action_id"], + "properties": { + "action_id": {"type": "string"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_validate_document_payload": _validate_payload, + "frappe_dry_run_action": _dry_run_action, + "frappe_risk_score_action": _risk_score_action, + "frappe_request_confirmation_token": _request_confirmation_token, + "frappe_audit_tool_call": _audit_tool_call, + "frappe_get_audit_history": _get_audit_history, + "frappe_clear_audit_log": _clear_audit_log, + "frappe_tool_health_check": _tool_health_check, + "frappe_policy_check": _policy_check, + "frappe_register_reversible_action": _register_reversible_action, + "frappe_rollback_action": _rollback_action, + } + + +async def _validate_payload(args: dict) -> str: + client = FrappeClient() + meta = await client.call_method( + "frappe.desk.form.load.getdoctype", + doctype=args["doctype"], + with_parent=1, + cached_timestamp=None, + ) + data = args["data"] + errors = [] + warnings = [] + + docs = meta.get("docs", []) if isinstance(meta, dict) else [] + fields = next((d.get("fields", []) for d in docs if d.get("name") == args["doctype"]), []) + + for field in fields: + fname = field.get("fieldname", "") + if field.get("reqd") and fname not in data and not data.get(fname): + errors.append(f"Required field missing: {fname} ({field.get('label', fname)})") + if fname in data and field.get("fieldtype") == "Int": + try: + int(data[fname]) + except (TypeError, ValueError): + errors.append(f"Field {fname} expects an integer, got: {data[fname]}") + + return json.dumps({ + "valid": len(errors) == 0, + "errors": errors, + "warnings": warnings, + "fields_provided": list(data.keys()), + }, indent=2) + + +async def _dry_run_action(args: dict) -> str: + client = FrappeClient() + action = args["action"] + doctype = args["doctype"] + data = args["data"] + + try: + if action == "create": + result = await client.call_method( + "frappe.client.validate", + doc={"doctype": doctype, **data}, + ) + else: + name = args.get("name", "") + existing = await client.get_doc(doctype, name) + merged = {**existing, **data} + result = await client.call_method("frappe.client.validate", doc=merged) + return json.dumps({"dry_run": True, "action": action, "status": "would_succeed", "result": result}, indent=2) + except Exception as e: + return json.dumps({"dry_run": True, "action": action, "status": "would_fail", "error": str(e)}, indent=2) + + +async def _risk_score_action(args: dict) -> str: + tool = args["tool_name"] + arguments = args.get("arguments", {}) + risk = _score_risk(tool, arguments) + + reasons = [] + if "delete" in tool: + reasons.append("Destructive operation — cannot be undone easily") + if "bulk" in tool: + reasons.append("Bulk operation affects multiple records") + if "cancel" in tool: + reasons.append("Cancellation changes document lifecycle") + if "reset" in tool: + reasons.append("Resets customization — removes all property setters and custom fields") + if not reasons: + reasons.append("Standard read/write operation") + + return json.dumps({"tool": tool, "risk": risk, "reasons": reasons}, indent=2) + + +async def _request_confirmation_token(args: dict) -> str: + token = secrets.token_hex(8) + _pending_confirmations[token] = { + "tool_name": args["tool_name"], + "arguments": args["arguments"], + "reason": args.get("reason", ""), + "expires_at": time.time() + 60, + "created_at": time.time(), + } + return json.dumps({ + "token": token, + "tool_name": args["tool_name"], + "expires_in_seconds": 60, + "instruction": "Pass this token to frappe_confirm_and_execute to proceed with the action.", + }, indent=2) + + +async def _audit_tool_call(args: dict) -> str: + record_id = log_action( + tool_name=args["tool_name"], + arguments=args["arguments"], + result_summary=args.get("result_summary", ""), + risk=args.get("risk", "low"), + ) + return json.dumps({"logged": True, "record_id": record_id}, indent=2) + + +async def _get_audit_history(args: dict) -> str: + records = read_audit_log( + limit=args.get("limit", 50), + tool_filter=args.get("tool_filter", ""), + ) + return json.dumps({"count": len(records), "records": records}, indent=2) + + +async def _clear_audit_log(args: dict) -> str: + count = clear_audit_log() + return json.dumps({"cleared": True, "records_removed": count}, indent=2) + + +async def _tool_health_check(args: dict) -> str: + from frappe_mcp.healthcheck import run_health_check + from frappe_mcp.module_registry import ALL_MODULES, _is_module_enabled + from frappe_mcp.config import get_settings + import asyncio + + client = FrappeClient() + settings = get_settings() + + checks = {} + + # connectivity + try: + versions = await client.call_method("frappe.utils.change_log.get_versions") + checks["frappe_reachable"] = {"status": "ok", "version": versions.get("frappe", {}).get("version") if isinstance(versions, dict) else None} + except Exception as e: + checks["frappe_reachable"] = {"status": "fail", "error": str(e)} + + # auth + try: + user = await client.call_method("frappe.auth.get_logged_user") + checks["auth"] = {"status": "ok", "user": user} + except Exception as e: + checks["auth"] = {"status": "fail", "error": str(e)} + + # modules + checks["modules"] = { + key: "enabled" if _is_module_enabled(key) else "disabled" + for key, _, _ in ALL_MODULES + } + checks["read_only_mode"] = settings.read_only_mode + checks["total_enabled_tools"] = sum( + len(mod.tools()) for key, mod, _ in ALL_MODULES if _is_module_enabled(key) + ) + + return json.dumps(checks, indent=2) + + +async def _policy_check(args: dict) -> str: + from frappe_mcp.config import get_settings + from frappe_mcp.module_registry import _is_module_enabled, ALL_MODULES + + settings = get_settings() + tool = args["tool_name"] + risk = _score_risk(tool, args.get("arguments", {})) + + violations = [] + + if settings.read_only_mode: + write_prefixes = ["frappe_create", "frappe_update", "frappe_delete", "frappe_submit", + "frappe_cancel", "frappe_bulk", "erpnext_"] + if any(tool.startswith(p) for p in write_prefixes): + violations.append("read_only_mode=true — write operations are blocked") + + module_key = None + for key, mod, _ in ALL_MODULES: + if tool in mod.handlers(): + module_key = key + break + if module_key and not _is_module_enabled(module_key): + violations.append(f"Module '{module_key}' is disabled in configuration") + + return json.dumps({ + "tool": tool, + "risk": risk, + "allowed": len(violations) == 0, + "violations": violations, + }, indent=2) + + +async def _register_reversible_action(args: dict) -> str: + _reversible_actions[args["action_id"]] = { + "doctype": args["doctype"], + "name": args["name"], + "pre_state": args.get("pre_state", {}), + "registered_at": time.time(), + } + return json.dumps({"registered": True, "action_id": args["action_id"]}, indent=2) + + +async def _rollback_action(args: dict) -> str: + action = _reversible_actions.get(args["action_id"]) + if not action: + return json.dumps({"error": f"No reversible action found for id: {args['action_id']}"}, indent=2) + client = FrappeClient() + try: + result = await client.update_doc(action["doctype"], action["name"], action["pre_state"]) + del _reversible_actions[args["action_id"]] + return json.dumps({"rolled_back": True, "doctype": action["doctype"], "name": action["name"]}, indent=2) + except Exception as e: + return json.dumps({"rolled_back": False, "error": str(e)}, indent=2) diff --git a/frappe_mcp/tools/naming_series.py b/frappe_mcp/tools/naming_series.py new file mode 100644 index 0000000..fbcbc7e --- /dev/null +++ b/frappe_mcp/tools/naming_series.py @@ -0,0 +1,111 @@ +"""Naming Series tools — manage document naming patterns in Frappe.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_get_naming_series", + description="Get all naming series options for a DocType.", + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_set_naming_series", + description=( + "Set or update naming series options for a DocType. " + "Series format: 'PREFIX-.YYYY.-.MM.-.####' where #### is zero-padded counter." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "series"], + "properties": { + "doctype": {"type": "string"}, + "series": { + "type": "string", + "description": "Newline-separated list of series patterns e.g. 'INV-.YYYY.-.####\\nINV-TEST-.####'", + }, + }, + }, + ), + Tool( + name="frappe_get_series_counter", + description="Get the current counter value for a naming series prefix.", + inputSchema={ + "type": "object", + "required": ["prefix"], + "properties": { + "prefix": {"type": "string", "description": "Prefix e.g. 'INV-2024-'"}, + }, + }, + ), + Tool( + name="frappe_update_series_counter", + description="Manually set the counter for a naming series prefix.", + inputSchema={ + "type": "object", + "required": ["prefix", "value"], + "properties": { + "prefix": {"type": "string"}, + "value": {"type": "integer", "description": "New counter value"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_get_naming_series": _get_naming_series, + "frappe_set_naming_series": _set_naming_series, + "frappe_get_series_counter": _get_series_counter, + "frappe_update_series_counter": _update_series_counter, + } + + +async def _get_naming_series(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.core.doctype.naming_series.naming_series.get_options", + arg=args["doctype"], + ) + return json.dumps(result, indent=2) + + +async def _set_naming_series(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.core.doctype.naming_series.naming_series.update_series", + args={ + "df": {"options": args["series"]}, + "doctype": args["doctype"], + }, + ) + return json.dumps(result, indent=2) + + +async def _get_series_counter(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.core.doctype.naming_series.naming_series.get_current", + prefix=args["prefix"], + ) + return json.dumps({"prefix": args["prefix"], "current": result}, indent=2) + + +async def _update_series_counter(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.core.doctype.naming_series.naming_series.update_counter", + prefix=args["prefix"], + value=args["value"], + ) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/print_formats.py b/frappe_mcp/tools/print_formats.py new file mode 100644 index 0000000..11bbb6a --- /dev/null +++ b/frappe_mcp/tools/print_formats.py @@ -0,0 +1,155 @@ +"""Print Format tools — create, list, update Frappe Print Formats (Jinja/HTML).""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_print_formats", + description="List all Print Formats, optionally filtered by DocType.", + inputSchema={ + "type": "object", + "properties": { + "doc_type": {"type": "string", "description": "Filter by DocType"}, + "limit": {"type": "integer", "default": 30}, + }, + }, + ), + Tool( + name="frappe_get_print_format", + description="Get the full definition of a Print Format including its HTML template.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_create_print_format", + description=( + "Create a new custom Print Format with a Jinja2/HTML template. " + "Use {{ doc.field_name }} syntax to access document fields." + ), + inputSchema={ + "type": "object", + "required": ["name", "doc_type", "html"], + "properties": { + "name": {"type": "string"}, + "doc_type": {"type": "string", "description": "DocType this format applies to"}, + "html": {"type": "string", "description": "Jinja2 HTML template"}, + "css": {"type": "string", "description": "Optional custom CSS"}, + "standard": {"type": "string", "default": "No", "enum": ["Yes", "No"]}, + "disabled": {"type": "integer", "default": 0}, + "default_print_language": {"type": "string", "default": "en"}, + "print_format_type": { + "type": "string", + "enum": ["Jinja", "JS"], + "default": "Jinja", + }, + }, + }, + ), + Tool( + name="frappe_update_print_format", + description="Update an existing Print Format's HTML, CSS, or properties.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + "html": {"type": "string"}, + "css": {"type": "string"}, + "disabled": {"type": "integer"}, + "updates": {"type": "object", "description": "Any additional fields to update"}, + }, + }, + ), + Tool( + name="frappe_preview_print_format", + description="Get a rendered HTML preview of a Print Format for a specific document.", + inputSchema={ + "type": "object", + "required": ["doctype", "docname", "print_format"], + "properties": { + "doctype": {"type": "string"}, + "docname": {"type": "string"}, + "print_format": {"type": "string"}, + "letterhead": {"type": "string", "description": "Letterhead name (optional)"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_print_formats": _list_print_formats, + "frappe_get_print_format": _get_print_format, + "frappe_create_print_format": _create_print_format, + "frappe_update_print_format": _update_print_format, + "frappe_preview_print_format": _preview_print_format, + } + + +async def _list_print_formats(args: dict) -> str: + client = FrappeClient() + filters = [] + if doc_type := args.get("doc_type"): + filters.append(["doc_type", "=", doc_type]) + result = await client.get_list( + "Print Format", + fields=["name", "doc_type", "disabled", "standard", "modified"], + filters=filters if filters else None, + limit=args.get("limit", 30), + ) + return json.dumps(result, indent=2) + + +async def _get_print_format(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("Print Format", args["name"]) + return json.dumps(result, indent=2) + + +async def _create_print_format(args: dict) -> str: + client = FrappeClient() + payload = { + "name": args["name"], + "doc_type": args["doc_type"], + "html": args["html"], + "css": args.get("css", ""), + "standard": args.get("standard", "No"), + "disabled": args.get("disabled", 0), + "default_print_language": args.get("default_print_language", "en"), + "print_format_type": args.get("print_format_type", "Jinja"), + } + result = await client.create_doc("Print Format", payload) + return json.dumps(result, indent=2) + + +async def _update_print_format(args: dict) -> str: + client = FrappeClient() + updates = args.get("updates", {}) + for field in ("html", "css", "disabled"): + if field in args: + updates[field] = args[field] + result = await client.update_doc("Print Format", args["name"], updates) + return json.dumps(result, indent=2) + + +async def _preview_print_format(args: dict) -> str: + client = FrappeClient() + params = { + "doctype": args["doctype"], + "name": args["docname"], + "print_format": args["print_format"], + } + if letterhead := args.get("letterhead"): + params["letterhead"] = letterhead + result = await client.get("/api/method/frappe.www.printview.get_html_and_style", params=params) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/property_setters.py b/frappe_mcp/tools/property_setters.py new file mode 100644 index 0000000..d4a5583 --- /dev/null +++ b/frappe_mcp/tools/property_setters.py @@ -0,0 +1,164 @@ +""" +Property Setter + Customize Form tools. +Property Setters override DocType field properties without touching core — the safe +way to customize standard DocTypes (e.g. Sales Order, Customer). +""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_property_setters", + description="List all Property Setters, optionally filtered by DocType.", + inputSchema={ + "type": "object", + "properties": { + "doc_type": {"type": "string"}, + "field_name": {"type": "string"}, + "property": {"type": "string"}, + "limit": {"type": "integer", "default": 50}, + }, + }, + ), + Tool( + name="frappe_create_property_setter", + description=( + "Override a property of a DocType field (or the DocType itself) without modifying core. " + "Common properties: reqd, hidden, read_only, default, options, label, bold, in_list_view, " + "in_standard_filter, description, depends_on, mandatory_depends_on, read_only_depends_on." + ), + inputSchema={ + "type": "object", + "required": ["doc_type", "property", "value", "property_type"], + "properties": { + "doc_type": {"type": "string", "description": "DocType to customize"}, + "field_name": {"type": "string", "description": "Fieldname to override (empty for DocType-level)"}, + "property": {"type": "string", "description": "Property to override e.g. 'reqd', 'hidden'"}, + "value": {"type": "string", "description": "New value"}, + "property_type": { + "type": "string", + "description": "Data type: Check, Data, Int, Select, Text, etc.", + "default": "Check", + }, + "row_name": {"type": "string", "description": "For child table row overrides"}, + }, + }, + ), + Tool( + name="frappe_remove_property_setter", + description="Remove a Property Setter, restoring the DocType's original property value.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string", "description": "Property Setter document name"}, + }, + }, + ), + Tool( + name="frappe_get_customize_form", + description=( + "Get the full customization state of a DocType — all Property Setters, " + "Custom Fields, and effective field properties merged." + ), + inputSchema={ + "type": "object", + "required": ["doc_type"], + "properties": { + "doc_type": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_reset_customization", + description=( + "Remove ALL customizations (Property Setters + Custom Fields) from a DocType, " + "restoring it to its original state. Destructive — use with caution." + ), + inputSchema={ + "type": "object", + "required": ["doc_type"], + "properties": { + "doc_type": {"type": "string"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_property_setters": _list_property_setters, + "frappe_create_property_setter": _create_property_setter, + "frappe_remove_property_setter": _remove_property_setter, + "frappe_get_customize_form": _get_customize_form, + "frappe_reset_customization": _reset_customization, + } + + +async def _list_property_setters(args: dict) -> str: + client = FrappeClient() + filters = [] + if dt := args.get("doc_type"): + filters.append(["doc_type", "=", dt]) + if fn := args.get("field_name"): + filters.append(["field_name", "=", fn]) + if prop := args.get("property"): + filters.append(["property", "=", prop]) + result = await client.get_list( + "Property Setter", + fields=["name", "doc_type", "field_name", "property", "value"], + filters=filters if filters else None, + limit=args.get("limit", 50), + ) + return json.dumps(result, indent=2) + + +async def _create_property_setter(args: dict) -> str: + client = FrappeClient() + payload = { + "doc_type": args["doc_type"], + "field_name": args.get("field_name", ""), + "property": args["property"], + "value": args["value"], + "property_type": args.get("property_type", "Check"), + "row_name": args.get("row_name", ""), + "doctype_or_field": "DocField" if args.get("field_name") else "DocType", + } + result = await client.create_doc("Property Setter", payload) + return json.dumps(result, indent=2) + + +async def _remove_property_setter(args: dict) -> str: + client = FrappeClient() + result = await client.delete_doc("Property Setter", args["name"]) + return json.dumps(result, indent=2) + + +async def _get_customize_form(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.form.load.getdoc", + doctype="Customize Form", + name="Customize Form", + ) + # fetch the form for this specific doctype + form_result = await client.call_method( + "frappe.client.get", + doctype="Customize Form", + filters={"doc_type": args["doc_type"]}, + ) + return json.dumps(form_result, indent=2) + + +async def _reset_customization(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.custom.doctype.customize_form.customize_form.reset_customization", + doc_type=args["doc_type"], + ) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/reports.py b/frappe_mcp/tools/reports.py new file mode 100644 index 0000000..d2e0202 --- /dev/null +++ b/frappe_mcp/tools/reports.py @@ -0,0 +1,188 @@ +"""Report CRUD tools — create Query Reports and Script Reports.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_reports", + description="List Reports, optionally filtered by type or DocType.", + inputSchema={ + "type": "object", + "properties": { + "report_type": { + "type": "string", + "enum": ["Query Report", "Script Report", "Report Builder", "Custom Report"], + }, + "ref_doctype": {"type": "string"}, + "limit": {"type": "integer", "default": 30}, + }, + }, + ), + Tool( + name="frappe_get_report", + description="Get a Report definition including its query or script.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": {"name": {"type": "string"}}, + }, + ), + Tool( + name="frappe_create_query_report", + description=( + "Create a Query Report using a raw SQL SELECT statement. " + "The query can use %(filter_field)s for parameterized filters." + ), + inputSchema={ + "type": "object", + "required": ["name", "ref_doctype", "query"], + "properties": { + "name": {"type": "string"}, + "ref_doctype": {"type": "string"}, + "query": {"type": "string", "description": "SQL SELECT query"}, + "filters": { + "type": "array", + "items": {"type": "object"}, + "description": "Filter field definitions", + }, + "is_standard": {"type": "string", "enum": ["Yes", "No"], "default": "No"}, + "disabled": {"type": "integer", "default": 0}, + }, + }, + ), + Tool( + name="frappe_create_script_report", + description=( + "Create a Script Report using Python. " + "The script must define `execute(filters)` that returns (columns, data)." + ), + inputSchema={ + "type": "object", + "required": ["name", "ref_doctype", "script"], + "properties": { + "name": {"type": "string"}, + "ref_doctype": {"type": "string"}, + "script": {"type": "string", "description": "Python script with execute(filters) function"}, + "javascript": {"type": "string", "description": "Optional JS for filters/formatting"}, + "filters": { + "type": "array", + "items": {"type": "object"}, + "description": "Filter field definitions", + }, + "is_standard": {"type": "string", "enum": ["Yes", "No"], "default": "No"}, + "disabled": {"type": "integer", "default": 0}, + }, + }, + ), + Tool( + name="frappe_update_report", + description="Update a Report's query, script, or filters.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + "query": {"type": "string"}, + "script": {"type": "string"}, + "javascript": {"type": "string"}, + "disabled": {"type": "integer"}, + "updates": {"type": "object"}, + }, + }, + ), + Tool( + name="frappe_run_query_report", + description="Execute a report and return its data.", + inputSchema={ + "type": "object", + "required": ["report_name"], + "properties": { + "report_name": {"type": "string"}, + "filters": {"type": "object", "description": "Filter key-value pairs"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_reports": _list_reports, + "frappe_get_report": _get_report, + "frappe_create_query_report": _create_query_report, + "frappe_create_script_report": _create_script_report, + "frappe_update_report": _update_report, + "frappe_run_query_report": _run_query_report, + } + + +async def _list_reports(args: dict) -> str: + client = FrappeClient() + filters = [] + if rt := args.get("report_type"): + filters.append(["report_type", "=", rt]) + if dt := args.get("ref_doctype"): + filters.append(["ref_doctype", "=", dt]) + result = await client.get_list( + "Report", + fields=["name", "report_type", "ref_doctype", "disabled", "is_standard"], + filters=filters if filters else None, + limit=args.get("limit", 30), + ) + return json.dumps(result, indent=2) + +async def _get_report(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("Report", args["name"]) + return json.dumps(result, indent=2) + +async def _create_query_report(args: dict) -> str: + client = FrappeClient() + payload = { + "report_name": args["name"], + "report_type": "Query Report", + "ref_doctype": args["ref_doctype"], + "query": args["query"], + "filters": args.get("filters", []), + "is_standard": args.get("is_standard", "No"), + "disabled": args.get("disabled", 0), + } + result = await client.create_doc("Report", payload) + return json.dumps(result, indent=2) + +async def _create_script_report(args: dict) -> str: + client = FrappeClient() + payload = { + "report_name": args["name"], + "report_type": "Script Report", + "ref_doctype": args["ref_doctype"], + "script": args["script"], + "javascript": args.get("javascript", ""), + "filters": args.get("filters", []), + "is_standard": args.get("is_standard", "No"), + "disabled": args.get("disabled", 0), + } + result = await client.create_doc("Report", payload) + return json.dumps(result, indent=2) + +async def _update_report(args: dict) -> str: + client = FrappeClient() + updates = args.get("updates", {}) + for field in ("query", "script", "javascript", "disabled"): + if field in args: + updates[field] = args[field] + result = await client.update_doc("Report", args["name"], updates) + return json.dumps(result, indent=2) + +async def _run_query_report(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.desk.query_report.run", + report_name=args["report_name"], + filters=args.get("filters", {}), + ) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/scheduler.py b/frappe_mcp/tools/scheduler.py new file mode 100644 index 0000000..f189ea4 --- /dev/null +++ b/frappe_mcp/tools/scheduler.py @@ -0,0 +1,179 @@ +"""Scheduler and background job tools.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_scheduled_jobs", + description="List all Scheduled Job Types (cron definitions).", + inputSchema={ + "type": "object", + "properties": { + "stopped": {"type": "integer", "description": "1 = stopped only, 0 = running only"}, + "limit": {"type": "integer", "default": 30}, + }, + }, + ), + Tool( + name="frappe_get_scheduled_job", + description="Get details of a Scheduled Job Type.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": {"name": {"type": "string"}}, + }, + ), + Tool( + name="frappe_create_scheduled_job", + description=( + "Create a new Scheduled Job Type — a Frappe cron task " + "that calls a whitelisted Python method on a schedule." + ), + inputSchema={ + "type": "object", + "required": ["method", "frequency"], + "properties": { + "method": {"type": "string", "description": "Dotted Python method path e.g. myapp.tasks.daily_sync"}, + "frequency": { + "type": "string", + "enum": [ + "All", "Hourly", "Daily", "Weekly", "Monthly", + "Yearly", "Hourly Long", "Daily Long", "Weekly Long", + "Monthly Long", "Cron", + ], + }, + "cron_format": {"type": "string", "description": "Required if frequency is 'Cron' e.g. '0 2 * * *'"}, + "stopped": {"type": "integer", "default": 0}, + }, + }, + ), + Tool( + name="frappe_toggle_scheduled_job", + description="Start or stop a Scheduled Job Type.", + inputSchema={ + "type": "object", + "required": ["name", "stopped"], + "properties": { + "name": {"type": "string"}, + "stopped": {"type": "integer", "description": "1 to stop, 0 to start"}, + }, + }, + ), + Tool( + name="frappe_trigger_scheduled_job", + description="Manually trigger a scheduled job to run immediately.", + inputSchema={ + "type": "object", + "required": ["job_name"], + "properties": { + "job_name": {"type": "string", "description": "Scheduled Job Type name"}, + }, + }, + ), + Tool( + name="frappe_list_background_jobs", + description="Get the current background job queue status.", + inputSchema={ + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": ["queued", "started", "finished", "failed"], + "description": "Filter by job status", + }, + "limit": {"type": "integer", "default": 20}, + }, + }, + ), + Tool( + name="frappe_get_scheduler_status", + description="Check if the Frappe scheduler is active or paused.", + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_enable_scheduler", + description="Enable or disable the Frappe background job scheduler.", + inputSchema={ + "type": "object", + "required": ["enable"], + "properties": { + "enable": {"type": "boolean", "description": "true to enable, false to disable"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_scheduled_jobs": _list_scheduled_jobs, + "frappe_get_scheduled_job": _get_scheduled_job, + "frappe_create_scheduled_job": _create_scheduled_job, + "frappe_toggle_scheduled_job": _toggle_scheduled_job, + "frappe_trigger_scheduled_job": _trigger_scheduled_job, + "frappe_list_background_jobs": _list_background_jobs, + "frappe_get_scheduler_status": _get_scheduler_status, + "frappe_enable_scheduler": _enable_scheduler, + } + + +async def _list_scheduled_jobs(args: dict) -> str: + client = FrappeClient() + filters = [] + if "stopped" in args: + filters.append(["stopped", "=", args["stopped"]]) + result = await client.get_list( + "Scheduled Job Type", + fields=["name", "method", "frequency", "cron_format", "stopped", "last_execution"], + filters=filters if filters else None, + limit=args.get("limit", 30), + ) + return json.dumps(result, indent=2) + +async def _get_scheduled_job(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("Scheduled Job Type", args["name"]) + return json.dumps(result, indent=2) + +async def _create_scheduled_job(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("Scheduled Job Type", args) + return json.dumps(result, indent=2) + +async def _toggle_scheduled_job(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc("Scheduled Job Type", args["name"], {"stopped": args["stopped"]}) + return json.dumps(result, indent=2) + +async def _trigger_scheduled_job(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.core.doctype.scheduled_job_type.scheduled_job_type.run_scheduled_job", + job_type=args["job_name"], + ) + return json.dumps(result, indent=2) + +async def _list_background_jobs(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.utils.background_jobs.get_jobs", + status=args.get("status", ""), + ) + return json.dumps(result, indent=2) + +async def _get_scheduler_status(args: dict) -> str: + client = FrappeClient() + result = await client.call_method("frappe.core.doctype.scheduled_job_type.scheduled_job_type.get_enabled_scheduler_events") + return json.dumps(result, indent=2) + +async def _enable_scheduler(args: dict) -> str: + client = FrappeClient() + method = "frappe.desk.doctype.event.event.get_events" if args["enable"] else "frappe.handler.ping" + method = "frappe.core.doctype.system_settings.system_settings.enable_scheduler" if args["enable"] else \ + "frappe.core.doctype.system_settings.system_settings.disable_scheduler" + result = await client.call_method(method) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/scripts.py b/frappe_mcp/tools/scripts.py new file mode 100644 index 0000000..649c86f --- /dev/null +++ b/frappe_mcp/tools/scripts.py @@ -0,0 +1,174 @@ +"""Server Script and Client Script tools.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_create_server_script", + description=( + "Create a Frappe Server Script (Python). " + "script_type options: DocType Event, API, Permission Query, Scheduler Event." + ), + inputSchema={ + "type": "object", + "required": ["name", "script_type", "script"], + "properties": { + "name": {"type": "string", "description": "Unique script name"}, + "script_type": { + "type": "string", + "enum": ["DocType Event", "API", "Permission Query", "Scheduler Event"], + }, + "script": {"type": "string", "description": "Python code"}, + "reference_doctype": {"type": "string", "description": "Required for DocType Event"}, + "doctype_event": { + "type": "string", + "enum": [ + "Before Insert", "After Insert", "Before Save", "After Save", + "Before Submit", "After Submit", "Before Cancel", "After Cancel", + "Before Delete", "After Delete", "Before Naming", "After Naming", + ], + "description": "Required for DocType Event", + }, + "api_method": {"type": "string", "description": "Method name for API type"}, + "allow_guest": {"type": "integer", "default": 0}, + "disabled": {"type": "integer", "default": 0}, + }, + }, + ), + Tool( + name="frappe_update_server_script", + description="Update an existing Server Script.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + "script": {"type": "string"}, + "disabled": {"type": "integer"}, + "updates": {"type": "object"}, + }, + }, + ), + Tool( + name="frappe_list_server_scripts", + description="List all Server Scripts, optionally filtered by script_type.", + inputSchema={ + "type": "object", + "properties": { + "script_type": {"type": "string"}, + "reference_doctype": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_create_client_script", + description="Create a Frappe Client Script (JavaScript) for a DocType.", + inputSchema={ + "type": "object", + "required": ["dt", "script"], + "properties": { + "dt": {"type": "string", "description": "DocType this script applies to"}, + "script": {"type": "string", "description": "JavaScript code"}, + "view": { + "type": "string", + "enum": ["Form", "List", "Tree", "Calendar", "Gantt"], + "default": "Form", + }, + "enabled": {"type": "integer", "default": 1}, + }, + }, + ), + Tool( + name="frappe_list_client_scripts", + description="List all Client Scripts, optionally filtered by DocType.", + inputSchema={ + "type": "object", + "properties": { + "dt": {"type": "string"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_create_server_script": _create_server_script, + "frappe_update_server_script": _update_server_script, + "frappe_list_server_scripts": _list_server_scripts, + "frappe_create_client_script": _create_client_script, + "frappe_list_client_scripts": _list_client_scripts, + } + + +async def _create_server_script(args: dict) -> str: + client = FrappeClient() + payload = { + "name": args["name"], + "script_type": args["script_type"], + "script": args["script"], + "reference_doctype": args.get("reference_doctype", ""), + "doctype_event": args.get("doctype_event", ""), + "api_method": args.get("api_method", ""), + "allow_guest": args.get("allow_guest", 0), + "disabled": args.get("disabled", 0), + } + result = await client.create_doc("Server Script", payload) + return json.dumps(result, indent=2) + + +async def _update_server_script(args: dict) -> str: + client = FrappeClient() + updates = args.get("updates", {}) + if "script" in args: + updates["script"] = args["script"] + if "disabled" in args: + updates["disabled"] = args["disabled"] + result = await client.update_doc("Server Script", args["name"], updates) + return json.dumps(result, indent=2) + + +async def _list_server_scripts(args: dict) -> str: + client = FrappeClient() + filters = [] + if script_type := args.get("script_type"): + filters.append(["script_type", "=", script_type]) + if ref := args.get("reference_doctype"): + filters.append(["reference_doctype", "=", ref]) + result = await client.get_list( + "Server Script", + fields=["name", "script_type", "reference_doctype", "doctype_event", "disabled"], + filters=filters if filters else None, + limit=50, + ) + return json.dumps(result, indent=2) + + +async def _create_client_script(args: dict) -> str: + client = FrappeClient() + payload = { + "dt": args["dt"], + "script": args["script"], + "view": args.get("view", "Form"), + "enabled": args.get("enabled", 1), + } + result = await client.create_doc("Client Script", payload) + return json.dumps(result, indent=2) + + +async def _list_client_scripts(args: dict) -> str: + client = FrappeClient() + filters = [] + if dt := args.get("dt"): + filters.append(["dt", "=", dt]) + result = await client.get_list( + "Client Script", + fields=["name", "dt", "view", "enabled"], + filters=filters if filters else None, + limit=50, + ) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/translations.py b/frappe_mcp/tools/translations.py new file mode 100644 index 0000000..4892c1e --- /dev/null +++ b/frappe_mcp/tools/translations.py @@ -0,0 +1,244 @@ +"""Translation, Assignment Rules, and User Permission Restriction tools.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + # --- Translations --- + Tool( + name="frappe_list_translations", + description="List custom translations for a language.", + inputSchema={ + "type": "object", + "properties": { + "language": {"type": "string", "description": "Language code e.g. 'ar', 'fr', 'de'"}, + "source_text": {"type": "string"}, + "limit": {"type": "integer", "default": 50}, + }, + }, + ), + Tool( + name="frappe_add_translation", + description="Add or update a custom translation string.", + inputSchema={ + "type": "object", + "required": ["language", "source_text", "translated_text"], + "properties": { + "language": {"type": "string"}, + "source_text": {"type": "string"}, + "translated_text": {"type": "string"}, + "context": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_remove_translation", + description="Remove a custom translation entry.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": {"name": {"type": "string"}}, + }, + ), + # --- Assignment Rules --- + Tool( + name="frappe_list_assignment_rules", + description="List all Assignment Rules.", + inputSchema={ + "type": "object", + "properties": { + "document_type": {"type": "string"}, + "disabled": {"type": "integer"}, + "limit": {"type": "integer", "default": 20}, + }, + }, + ), + Tool( + name="frappe_create_assignment_rule", + description=( + "Create an Assignment Rule — automatically assigns documents to users " + "based on conditions (load balancing or rule-based)." + ), + inputSchema={ + "type": "object", + "required": ["name", "document_type", "assign_condition"], + "properties": { + "name": {"type": "string"}, + "document_type": {"type": "string"}, + "assign_condition": {"type": "string", "description": "Python expression e.g. doc.status == 'Open'"}, + "unassign_condition": {"type": "string"}, + "close_condition": {"type": "string"}, + "assignment_days": { + "type": "array", + "items": {"type": "object"}, + "description": "Days config for round-robin assignment", + }, + "users": { + "type": "array", + "items": {"type": "object"}, + "description": "List of {user} objects", + }, + "due_date_based_on": {"type": "string"}, + "priority": {"type": "string", "enum": ["Low", "Medium", "High"]}, + "disabled": {"type": "integer", "default": 0}, + }, + }, + ), + # --- User Permissions (Row-Level Security) --- + Tool( + name="frappe_list_user_permissions", + description="List User Permissions — row-level security rules that restrict which documents a user can see.", + inputSchema={ + "type": "object", + "properties": { + "user": {"type": "string"}, + "allow": {"type": "string", "description": "DocType being restricted e.g. 'Company'"}, + "limit": {"type": "integer", "default": 30}, + }, + }, + ), + Tool( + name="frappe_add_user_permission", + description=( + "Add a User Permission — restricts a user to only see documents " + "linked to a specific value. e.g. restrict user to one Company." + ), + inputSchema={ + "type": "object", + "required": ["user", "allow", "for_value"], + "properties": { + "user": {"type": "string"}, + "allow": {"type": "string", "description": "DocType e.g. 'Company'"}, + "for_value": {"type": "string", "description": "Specific document name"}, + "apply_to_all_doctypes": {"type": "integer", "default": 1}, + "applicable_for": {"type": "string", "description": "Restrict to specific DocType if not global"}, + "hide_descendants": {"type": "integer", "default": 0}, + }, + }, + ), + Tool( + name="frappe_remove_user_permission", + description="Remove a User Permission.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": {"name": {"type": "string"}}, + }, + ), + # --- Role Permission for Page/Report --- + Tool( + name="frappe_set_role_permission", + description="Grant or revoke a role's access to a Page or Report.", + inputSchema={ + "type": "object", + "required": ["doctype", "name", "role", "action"], + "properties": { + "doctype": {"type": "string", "enum": ["Page", "Report"]}, + "name": {"type": "string"}, + "role": {"type": "string"}, + "action": {"type": "string", "enum": ["add", "remove"]}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_translations": _list_translations, + "frappe_add_translation": _add_translation, + "frappe_remove_translation": _remove_translation, + "frappe_list_assignment_rules": _list_assignment_rules, + "frappe_create_assignment_rule": _create_assignment_rule, + "frappe_list_user_permissions": _list_user_permissions, + "frappe_add_user_permission": _add_user_permission, + "frappe_remove_user_permission": _remove_user_permission, + "frappe_set_role_permission": _set_role_permission, + } + + +async def _list_translations(args: dict) -> str: + client = FrappeClient() + filters = [] + if lang := args.get("language"): + filters.append(["language", "=", lang]) + if src := args.get("source_text"): + filters.append(["source_text", "like", f"%{src}%"]) + result = await client.get_list( + "Translation", + fields=["name", "language", "source_text", "translated_text", "context"], + filters=filters if filters else None, + limit=args.get("limit", 50), + ) + return json.dumps(result, indent=2) + +async def _add_translation(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("Translation", args) + return json.dumps(result, indent=2) + +async def _remove_translation(args: dict) -> str: + client = FrappeClient() + result = await client.delete_doc("Translation", args["name"]) + return json.dumps(result, indent=2) + +async def _list_assignment_rules(args: dict) -> str: + client = FrappeClient() + filters = [] + if dt := args.get("document_type"): + filters.append(["document_type", "=", dt]) + if "disabled" in args: + filters.append(["disabled", "=", args["disabled"]]) + result = await client.get_list( + "Assignment Rule", + fields=["name", "document_type", "disabled", "priority"], + filters=filters if filters else None, + limit=args.get("limit", 20), + ) + return json.dumps(result, indent=2) + +async def _create_assignment_rule(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("Assignment Rule", args) + return json.dumps(result, indent=2) + +async def _list_user_permissions(args: dict) -> str: + client = FrappeClient() + filters = [] + if user := args.get("user"): + filters.append(["user", "=", user]) + if allow := args.get("allow"): + filters.append(["allow", "=", allow]) + result = await client.get_list( + "User Permission", + fields=["name", "user", "allow", "for_value", "applicable_for", "apply_to_all_doctypes"], + filters=filters if filters else None, + limit=args.get("limit", 30), + ) + return json.dumps(result, indent=2) + +async def _add_user_permission(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("User Permission", args) + return json.dumps(result, indent=2) + +async def _remove_user_permission(args: dict) -> str: + client = FrappeClient() + result = await client.delete_doc("User Permission", args["name"]) + return json.dumps(result, indent=2) + +async def _set_role_permission(args: dict) -> str: + client = FrappeClient() + method = "frappe.desk.doctype.desktop_icon.desktop_icon.set_hidden" + doc = await client.get_doc(args["doctype"], args["name"]) + roles = doc.get("roles", []) + if args["action"] == "add": + if args["role"] not in [r.get("role") for r in roles]: + roles.append({"role": args["role"]}) + else: + roles = [r for r in roles if r.get("role") != args["role"]] + result = await client.update_doc(args["doctype"], args["name"], {"roles": roles}) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/users.py b/frappe_mcp/tools/users.py new file mode 100644 index 0000000..f954451 --- /dev/null +++ b/frappe_mcp/tools/users.py @@ -0,0 +1,187 @@ +"""User, Role, and Permission management tools.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_users", + description="List Frappe users.", + inputSchema={ + "type": "object", + "properties": { + "enabled": {"type": "integer", "description": "1 for active, 0 for disabled"}, + "limit": {"type": "integer", "default": 20}, + }, + }, + ), + Tool( + name="frappe_get_user", + description="Get a user's details and roles.", + inputSchema={ + "type": "object", + "required": ["email"], + "properties": { + "email": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_create_user", + description="Create a new Frappe user.", + inputSchema={ + "type": "object", + "required": ["email", "first_name"], + "properties": { + "email": {"type": "string"}, + "first_name": {"type": "string"}, + "last_name": {"type": "string"}, + "send_welcome_email": {"type": "integer", "default": 0}, + "roles": { + "type": "array", + "items": {"type": "string"}, + "description": "List of role names e.g. ['System Manager', 'Sales User']", + }, + }, + }, + ), + Tool( + name="frappe_assign_role", + description="Assign a role to an existing user.", + inputSchema={ + "type": "object", + "required": ["email", "role"], + "properties": { + "email": {"type": "string"}, + "role": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_remove_role", + description="Remove a role from a user.", + inputSchema={ + "type": "object", + "required": ["email", "role"], + "properties": { + "email": {"type": "string"}, + "role": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_list_roles", + description="List all available roles in the system.", + inputSchema={"type": "object", "properties": {}}, + ), + Tool( + name="frappe_set_doctype_permission", + description="Set role permissions on a DocType.", + inputSchema={ + "type": "object", + "required": ["doctype", "role"], + "properties": { + "doctype": {"type": "string"}, + "role": {"type": "string"}, + "read": {"type": "integer", "default": 1}, + "write": {"type": "integer", "default": 0}, + "create": {"type": "integer", "default": 0}, + "delete": {"type": "integer", "default": 0}, + "submit": {"type": "integer", "default": 0}, + "cancel": {"type": "integer", "default": 0}, + "amend": {"type": "integer", "default": 0}, + "report": {"type": "integer", "default": 0}, + "export": {"type": "integer", "default": 0}, + "import": {"type": "integer", "default": 0}, + "print": {"type": "integer", "default": 0}, + "email": {"type": "integer", "default": 0}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_users": _list_users, + "frappe_get_user": _get_user, + "frappe_create_user": _create_user, + "frappe_assign_role": _assign_role, + "frappe_remove_role": _remove_role, + "frappe_list_roles": _list_roles, + "frappe_set_doctype_permission": _set_doctype_permission, + } + + +async def _list_users(args: dict) -> str: + client = FrappeClient() + filters = [] + if "enabled" in args: + filters.append(["enabled", "=", args["enabled"]]) + result = await client.get_list( + "User", + fields=["name", "full_name", "email", "enabled", "last_login"], + filters=filters if filters else None, + limit=args.get("limit", 20), + ) + return json.dumps(result, indent=2) + + +async def _get_user(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("User", args["email"]) + return json.dumps(result, indent=2) + + +async def _create_user(args: dict) -> str: + client = FrappeClient() + roles = [{"role": r} for r in args.get("roles", [])] + payload = { + "email": args["email"], + "first_name": args["first_name"], + "last_name": args.get("last_name", ""), + "send_welcome_email": args.get("send_welcome_email", 0), + "roles": roles, + } + result = await client.create_doc("User", payload) + return json.dumps(result, indent=2) + + +async def _assign_role(args: dict) -> str: + client = FrappeClient() + user = await client.get_doc("User", args["email"]) + existing_roles = [r["role"] for r in user.get("roles", [])] + if args["role"] not in existing_roles: + user["roles"].append({"role": args["role"]}) + result = await client.update_doc("User", args["email"], {"roles": user["roles"]}) + return json.dumps(result, indent=2) + return f"User {args['email']} already has role {args['role']}" + + +async def _remove_role(args: dict) -> str: + client = FrappeClient() + user = await client.get_doc("User", args["email"]) + updated_roles = [r for r in user.get("roles", []) if r["role"] != args["role"]] + result = await client.update_doc("User", args["email"], {"roles": updated_roles}) + return json.dumps(result, indent=2) + + +async def _list_roles(args: dict) -> str: + client = FrappeClient() + result = await client.get_list("Role", fields=["name", "disabled"], limit=100) + return json.dumps(result, indent=2) + + +async def _set_doctype_permission(args: dict) -> str: + client = FrappeClient() + payload = {k: v for k, v in args.items()} + result = await client.call_method( + "frappe.client.set_value", + doctype="DocPerm", + name=None, + fieldname=payload, + ) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/webhooks.py b/frappe_mcp/tools/webhooks.py new file mode 100644 index 0000000..e9f2bdd --- /dev/null +++ b/frappe_mcp/tools/webhooks.py @@ -0,0 +1,214 @@ +"""Webhook and API Key management tools.""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_webhooks", + description="List all configured Webhooks.", + inputSchema={ + "type": "object", + "properties": { + "webhook_doctype": {"type": "string"}, + "enabled": {"type": "integer"}, + "limit": {"type": "integer", "default": 20}, + }, + }, + ), + Tool( + name="frappe_get_webhook", + description="Get a Webhook configuration.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": {"name": {"type": "string"}}, + }, + ), + Tool( + name="frappe_create_webhook", + description="Create a new Webhook triggered by a DocType event.", + inputSchema={ + "type": "object", + "required": ["webhook_doctype", "webhook_docevent", "request_url"], + "properties": { + "webhook_doctype": {"type": "string"}, + "webhook_docevent": { + "type": "string", + "enum": ["after_insert", "on_update", "on_submit", "on_cancel", "on_trash"], + }, + "request_url": {"type": "string"}, + "request_method": {"type": "string", "enum": ["POST", "PUT", "GET"], "default": "POST"}, + "enabled": {"type": "integer", "default": 1}, + "condition": {"type": "string", "description": "Python expression e.g. doc.status == 'Submitted'"}, + "webhook_headers": { + "type": "array", + "items": {"type": "object"}, + "description": "List of {key, value} header objects", + }, + "webhook_data": { + "type": "array", + "items": {"type": "object"}, + "description": "List of {key, fieldname} objects for payload fields", + }, + }, + }, + ), + Tool( + name="frappe_update_webhook", + description="Enable, disable, or update a Webhook.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": { + "name": {"type": "string"}, + "enabled": {"type": "integer"}, + "updates": {"type": "object"}, + }, + }, + ), + Tool( + name="frappe_delete_webhook", + description="Delete a Webhook.", + inputSchema={ + "type": "object", + "required": ["name"], + "properties": {"name": {"type": "string"}}, + }, + ), + Tool( + name="frappe_list_webhook_logs", + description="Get recent Webhook request logs.", + inputSchema={ + "type": "object", + "properties": { + "webhook": {"type": "string"}, + "limit": {"type": "integer", "default": 20}, + }, + }, + ), + # --- API Keys --- + Tool( + name="frappe_list_api_keys", + description="List API Key records (does not expose secrets).", + inputSchema={ + "type": "object", + "properties": {"limit": {"type": "integer", "default": 20}}, + }, + ), + Tool( + name="frappe_generate_api_key", + description="Generate a new API key + secret for a Frappe user.", + inputSchema={ + "type": "object", + "required": ["user"], + "properties": { + "user": {"type": "string", "description": "User email"}, + }, + }, + ), + Tool( + name="frappe_revoke_api_key", + description="Revoke (delete) API key for a user.", + inputSchema={ + "type": "object", + "required": ["user"], + "properties": { + "user": {"type": "string"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_webhooks": _list_webhooks, + "frappe_get_webhook": _get_webhook, + "frappe_create_webhook": _create_webhook, + "frappe_update_webhook": _update_webhook, + "frappe_delete_webhook": _delete_webhook, + "frappe_list_webhook_logs": _list_webhook_logs, + "frappe_list_api_keys": _list_api_keys, + "frappe_generate_api_key": _generate_api_key, + "frappe_revoke_api_key": _revoke_api_key, + } + + +async def _list_webhooks(args: dict) -> str: + client = FrappeClient() + filters = [] + if dt := args.get("webhook_doctype"): + filters.append(["webhook_doctype", "=", dt]) + if "enabled" in args: + filters.append(["enabled", "=", args["enabled"]]) + result = await client.get_list( + "Webhook", + fields=["name", "webhook_doctype", "webhook_docevent", "request_url", "enabled"], + filters=filters if filters else None, + limit=args.get("limit", 20), + ) + return json.dumps(result, indent=2) + +async def _get_webhook(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("Webhook", args["name"]) + return json.dumps(result, indent=2) + +async def _create_webhook(args: dict) -> str: + client = FrappeClient() + result = await client.create_doc("Webhook", args) + return json.dumps(result, indent=2) + +async def _update_webhook(args: dict) -> str: + client = FrappeClient() + updates = args.get("updates", {}) + if "enabled" in args: + updates["enabled"] = args["enabled"] + result = await client.update_doc("Webhook", args["name"], updates) + return json.dumps(result, indent=2) + +async def _delete_webhook(args: dict) -> str: + client = FrappeClient() + result = await client.delete_doc("Webhook", args["name"]) + return json.dumps(result, indent=2) + +async def _list_webhook_logs(args: dict) -> str: + client = FrappeClient() + filters = [] + if wh := args.get("webhook"): + filters.append(["webhook", "=", wh]) + result = await client.get_list( + "Webhook Log", + fields=["name", "webhook", "status", "request_headers", "data", "response", "creation"], + filters=filters if filters else None, + limit=args.get("limit", 20), + order_by="creation desc", + ) + return json.dumps(result, indent=2) + +async def _list_api_keys(args: dict) -> str: + client = FrappeClient() + result = await client.get_list( + "User", + fields=["name", "api_key", "modified"], + filters=[["api_key", "!=", ""]], + limit=args.get("limit", 20), + ) + return json.dumps(result, indent=2) + +async def _generate_api_key(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.core.doctype.user.user.generate_keys", + user=args["user"], + ) + return json.dumps(result, indent=2) + +async def _revoke_api_key(args: dict) -> str: + client = FrappeClient() + result = await client.update_doc("User", args["user"], {"api_key": "", "api_secret": ""}) + return json.dumps(result, indent=2) diff --git a/frappe_mcp/tools/workflow_tools.py b/frappe_mcp/tools/workflow_tools.py new file mode 100644 index 0000000..4eca896 --- /dev/null +++ b/frappe_mcp/tools/workflow_tools.py @@ -0,0 +1,215 @@ +""" +Level 5 — Workflow and approval tools. +List workflows, read state, available transitions, execute transitions, approval summary. +""" + +import json +from mcp.types import Tool +from frappe_mcp.client.frappe_api import FrappeClient + + +def tools() -> list[Tool]: + return [ + Tool( + name="frappe_list_workflows", + description="List all Workflows, optionally filtered by DocType.", + inputSchema={ + "type": "object", + "properties": { + "document_type": {"type": "string"}, + "is_active": {"type": "integer", "description": "1 = active only"}, + }, + }, + ), + Tool( + name="frappe_get_workflow", + description="Get full workflow definition: all states and all transitions.", + inputSchema={ + "type": "object", + "required": ["workflow_name"], + "properties": {"workflow_name": {"type": "string"}}, + }, + ), + Tool( + name="frappe_get_workflow_state", + description="Get the current workflow state of a specific document.", + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_list_workflow_transitions", + description=( + "List the workflow transitions available for a document in its current state. " + "Shows what actions the current user can take: Approve, Reject, Send Back, etc." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "name"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + }, + }, + ), + Tool( + name="frappe_execute_workflow_transition", + description=( + "Perform a workflow transition on a document — e.g. Approve, Reject, Send Back. " + "Use list_workflow_transitions first to see allowed actions." + ), + inputSchema={ + "type": "object", + "required": ["doctype", "name", "action"], + "properties": { + "doctype": {"type": "string"}, + "name": {"type": "string"}, + "action": {"type": "string", "description": "Transition action name e.g. 'Approve'"}, + }, + }, + ), + Tool( + name="frappe_get_approval_summary", + description=( + "Get a summary of pending approvals for a DocType: " + "who needs to approve, current state, and how many documents are waiting." + ), + inputSchema={ + "type": "object", + "required": ["doctype"], + "properties": { + "doctype": {"type": "string"}, + "workflow_state": {"type": "string", "description": "Filter by specific state e.g. 'Pending Approval'"}, + "limit": {"type": "integer", "default": 20}, + }, + }, + ), + Tool( + name="frappe_update_workflow", + description="Enable/disable a workflow or update its properties.", + inputSchema={ + "type": "object", + "required": ["workflow_name"], + "properties": { + "workflow_name": {"type": "string"}, + "is_active": {"type": "integer"}, + "updates": {"type": "object"}, + }, + }, + ), + ] + + +def handlers() -> dict: + return { + "frappe_list_workflows": _list_workflows, + "frappe_get_workflow": _get_workflow, + "frappe_get_workflow_state": _get_workflow_state, + "frappe_list_workflow_transitions": _list_workflow_transitions, + "frappe_execute_workflow_transition": _execute_workflow_transition, + "frappe_get_approval_summary": _get_approval_summary, + "frappe_update_workflow": _update_workflow, + } + + +async def _list_workflows(args: dict) -> str: + client = FrappeClient() + filters = [] + if dt := args.get("document_type"): + filters.append(["document_type", "=", dt]) + if "is_active" in args: + filters.append(["is_active", "=", args["is_active"]]) + result = await client.get_list( + "Workflow", + fields=["name", "document_type", "is_active", "workflow_state_field"], + filters=filters if filters else None, + limit=50, + ) + return json.dumps(result, indent=2) + + +async def _get_workflow(args: dict) -> str: + client = FrappeClient() + result = await client.get_doc("Workflow", args["workflow_name"]) + return json.dumps(result, indent=2) + + +async def _get_workflow_state(args: dict) -> str: + client = FrappeClient() + doc = await client.get_doc(args["doctype"], args["name"]) + workflow_state = doc.get("workflow_state") + workflow_field = "workflow_state" + workflows = await client.get_list( + "Workflow", + fields=["name", "workflow_state_field", "is_active"], + filters=[["document_type", "=", args["doctype"]], ["is_active", "=", 1]], + limit=1, + ) + if isinstance(workflows, list) and workflows: + workflow_field = workflows[0].get("workflow_state_field", "workflow_state") + workflow_state = doc.get(workflow_field) + return json.dumps({ + "doctype": args["doctype"], + "name": args["name"], + "workflow_state_field": workflow_field, + "workflow_state": workflow_state, + "docstatus": doc.get("docstatus", 0), + }, indent=2) + + +async def _list_workflow_transitions(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.model.workflow.get_transitions", + doc={"doctype": args["doctype"], "name": args["name"]}, + ) + return json.dumps(result, indent=2) + + +async def _execute_workflow_transition(args: dict) -> str: + client = FrappeClient() + result = await client.call_method( + "frappe.model.workflow.apply_workflow", + doc={"doctype": args["doctype"], "name": args["name"]}, + action=args["action"], + ) + return json.dumps(result, indent=2) + + +async def _get_approval_summary(args: dict) -> str: + client = FrappeClient() + filters = [] + if ws := args.get("workflow_state"): + filters.append(["workflow_state", "=", ws]) + docs = await client.get_list( + args["doctype"], + fields=["name", "workflow_state", "owner", "modified", "docstatus"], + filters=filters if filters else None, + limit=args.get("limit", 20), + order_by="modified desc", + ) + docs_list = docs if isinstance(docs, list) else [] + count = await client.call_method( + "frappe.client.get_count", + doctype=args["doctype"], + filters=filters if filters else None, + ) + return json.dumps({ + "doctype": args["doctype"], + "total_pending": count, + "documents": docs_list, + }, indent=2) + + +async def _update_workflow(args: dict) -> str: + client = FrappeClient() + updates = args.get("updates", {}) + if "is_active" in args: + updates["is_active"] = args["is_active"] + result = await client.update_doc("Workflow", args["workflow_name"], updates) + return json.dumps(result, indent=2) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..798d8dd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,29 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "frappe-mcp" +version = "0.1.0" +description = "MCP Server for Frappe Framework — remote Docker deployment" +requires-python = ">=3.11" +dependencies = [ + "mcp>=1.0.0", + "httpx>=0.27.0", + "python-dotenv>=1.0.0", + "pydantic>=2.0.0", + "pydantic-settings>=2.0.0", +] + +[project.optional-dependencies] +sse = [ + "starlette>=0.40.0", + "uvicorn>=0.30.0", +] + +[project.scripts] +frappe-mcp = "frappe_mcp.server:main" +frappe-mcp-check = "frappe_mcp.healthcheck:main_health_check" + +[tool.hatch.build.targets.wheel] +packages = ["frappe_mcp"]

$VNtX&boPHsGoQ{-ta*p%APs zjPP$z`C%hmfZ)C!N`H<9B$0;T`g7@biZHjvHGs39h`Ipu&oITHB{iE=^W*=VhN<1s zo4J5th9<-V@>m}9!1?e--i)S=oQL_Z7d^WC1q)9W!v(!o7nEQr@VNg8kRa= z{7wpXY7Y_M6^}m~7RMe%rD*#z{A?=0^)$W|>9jZo4?_q!grAT$!@cv(^~iF_e>a9| zJ!Z-B%{f|&Qm1}$^5`TR##t(cz9QD-Dn5%M65$H;`Ok{*Kf=0&Oqv6j$Dsr8KZtUU z9%5+s=%wwy09F-QmObemS9<5Z)V3)8JgLWR&Ytv!+nlSK-X4G$vIdLMYNr%i@3rr9 zMZUjrtGnxj8=rveZP>gt=dK^{dSAL7b=Mz+>~(Z4ZFdtddYiYoZ+zq?FGBXZU$}n3 zZGRQA*SF*L1-EM-WN%>C*YCQ$har1?DR=m^+jR!A*V(gl%uV)tUA-$IcVo)iwC#4n z?br?3>m9g##9hA!as$iimUT(cCxV_#z|H{{W7VB6%i5>TmC?(UsszgIGNNdOcH{vR$O9;#XoYwPC`KXb2Z~8iJQKtl F|6i%<6bS$T literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/custom_fields.cpython-314.pyc b/frappe_mcp/tools/__pycache__/custom_fields.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b253e0aa20334111260b71b2545f5748201bc523 GIT binary patch literal 5502 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 TJ@mun>0to0Z~eeloTmN*j4I}i literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/dashboards.cpython-311.pyc b/frappe_mcp/tools/__pycache__/dashboards.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f70d3e25b613b19e38d36de620ffde5e0cb73bab GIT binary patch literal 7971 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 HP*3+?I^4Z~ literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/dashboards.cpython-314.pyc b/frappe_mcp/tools/__pycache__/dashboards.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..301d0039b8c1449b60e6c0fa9942660829401fa6 GIT binary patch literal 8835 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/doctypes.cpython-311.pyc b/frappe_mcp/tools/__pycache__/doctypes.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65b1c4177ba6382296274ca7125ae8ce3667fe9c GIT binary patch literal 5240 zcma)AO>Er86(*Nla{u+Sva(Y2Lrb!?nvCr;D^mR;L%qfk54ithe- z$h9p+281Rk+8}5IBY=%SKo&W;4y=O?Ipoj+Mla2gTL>sYz<`jpKz-Z5&LyY5H{2gl zja<(TA7_U1-psuB&6_v-r)V_9K^pw-U;5W^j{7H88cD1d9{v#uH#x#x;0RBAIsO6< zZz1Q)3l{_)+r*rh_h0Zs*{?~O(9@8>&HIS-k$6Fd_5hS+CMcKQfD{P4{*ZMU-6DO1MDf@iA+(oRMK9_=^)dQsAiUF zA=6wI6uRLN;CXn0N9CA68wr&}1R@eYk=9_*BtU}j4#5*%^Q}j21K(ZbWy!-CB{AS^ z%krcR_}h_R=Sjz!@K~-+5+_2IBVAweWDgO6!=>HiDK>I9Nw9Yh>(#rXm)ln+PqRLa zx7*scCgJF$ALiae2H1KA-L}WhNFjUc>u}`=WQg$&gPfmOO;!Yxj!ZjU^GxltXsKCJ z%$Q(YW9j6ZP*v1iu4yo^sbVf@ifbjbu&EX@VC!PZ)Qbh^x~R879>W*(GI)b!v4{3Tj?ck_|Dbl+O`y5>A+CMuzH4yd&!cQ_E`f!9lQK z%16WSa%7^-tL2;tERs8(X^pnbuCAXG|bM` z=F(te&#;wh>1CI#xG=y8%_U8FAwE2`aCunFY53qBLSCs;T5*f%$U& zu-lFk9&?Lm64=k`oR&+&gu!ufv>m4@x#H>;nS;%&sfDQ-$FEYVE`r*FtOJUw7nyba zOceEjh?GS8P+UKf0hB3%CWsT!3ob0L=-O3Bb`fGUZ0pxeEpIFxV@QQnft~ZZVW4qn z@v4zlPPpJOz&|Oc7Y*pGyv3Xrb&aZKGMZteVf5BwV!APFl;;3KraG6?oTy3F%x`8h z3u+;&85F&&6K;)UG>ud)5%7EsBbBG1;~CZj&QH;=C%^b*66r(Y|a|UK(_affAg}{KMA?!7X~|P91_3O6RUAZ zc8a?a{Y+k^Wm*Q|jFd;HM7GuHl7knQvf_fA;*UWdG;jBbsL*~wED z%UkK6Z7f*NP1=q3GZPz0EAo*F?H6DE_Odnc4rF`m;KoVo*`u|n{OmxD zLn4QVdTSgKe{8V3#vxI-y-9mi2X%}<3zAd<6@x_XL&YG84x?g#JVNrrNuFsYX(!%q zYDU=)ni)K>rGU5`h*MA;5Wj@tkjNnvheVE{I3SOZ)UNVx@DZuTy}|n>ngTOs;Atj5 zln^5i4*`LLw)uhMOx&>bWHzD|sGCxwm4oPH4G$OJbV{TM5xNIS0?B?LDba~}j~gd4 zyUq7HZL?mN2MylsMUyBCr%j&$;sjuOlAJ~jdH||t;W5yc04kOA{efrh@11~L3;J=z zE~&5D5mlNsGTQ&i31)5m)RkrD{*_p+8%R*N0^6=a;UqM^^Phx?E_$9yWqrPQdfhXLq^MIh}HjH_< zIqtfA8*F#CX}=F4()>U;%iR=yCtep#)ImL6LM*VfERw)_&?^V59GYgwz>oYJiy69cqr)w0s$d-X`G28 z08ktW1H}Sy0*D$6TpaO|Q7kxqQqGqQ7#@QJN6N5Jk|K!fM-|l!2p0^Lq+nw3q0eCl zB(v}se+Kd&_^ojFqdk9k@00gdrtDbz%4>GVo|}mqiCf8P$M9yyuq6+#T&Z!qf5Hy5 zeH!~Xb}LXBu3WL6N>>Bpn}KmFFmA_tZcg5qv;!S>+nW~4_R~XF``D_yD&G<9Sm&n; zA1|z4t@x_3;mz1^Ey#5Zf6w{5q(?{qtFhh5%G3`LF4F$V;nn%dJ63qK8XnyYk6PlW zyPcb$gVo@28KiGO;r~M#0JohRc-%zzWsdk-7Y#2IJP`7kP1tbUr6zorAx2*py?g*6 z|1^CbhG2&jeGyBCfq?U17MJU;fllI=V?e$Vn1ShWY!Bvbb1|(c4z#4 zZ~raw#lq(c*60i0?5Pe+ZVpUVdyj4Q9=qSucWdU0bDy896gSLj|B=o9Bh{Xxn>|Mx z%uhXi^V*GTm8iXE-<`|f9Qc;E&YZR8GFF5@hOle`!V6y_{0K>{Ez|~{3&_fAwQjDf z_aE_*zsE-^=QreP{Lp6nkQLpTpLnxjK*2T}`spXwY)|OScHL@%&Uu06{VTx(SDz`v zr0VHX^K=EbC$8_fGbW4U90A(g!xV#q-kd&D9g1EY&zH z{{+40lXz)$1j20m>?NIHK6%T8u%A!f5!h=d=@X#G3`FaEvXWu1og@;2Pa<3xKt`Os Tc9J1vh92<39`M?gu#n)t4SEVk literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/doctypes.cpython-314.pyc b/frappe_mcp/tools/__pycache__/doctypes.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..787967788f751b8509ec8928836dc811c7f0b32c GIT binary patch literal 6759 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 P`^~VhKeXdB6X*W}=BkGV literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/document_lifecycle.cpython-311.pyc b/frappe_mcp/tools/__pycache__/document_lifecycle.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67c2d1bcff20b5977e551d4ef01181b07a87f57f GIT binary patch literal 9858 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 y1U_4S`l$si*kfGpF;&}$b?gZ-IJP@8CME4^ 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 ONi6{oWWMF{`usoZIhkYt literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/documents.cpython-311.pyc b/frappe_mcp/tools/__pycache__/documents.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb7ce7493c5101dbfc1ba56a684d711aa791a373 GIT binary patch literal 6416 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 literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/documents.cpython-314.pyc b/frappe_mcp/tools/__pycache__/documents.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1cedb6b1ec2de00f79d5137200b4f9f30f6f16a GIT binary patch literal 7176 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 ZueBt5HIf6g7PO$Xpv5+`*k$b8|K9;k%z6L- literal 0 HcmV?d00001 diff --git a/frappe_mcp/tools/__pycache__/email_templates.cpython-314.pyc b/frappe_mcp/tools/__pycache__/email_templates.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9876f83f12cc8983049470c9ca826897daba5e94 GIT binary patch literal 8973 zcmeHNOK=-UdY-`yFoP!_07;PMWB3*<36N-6l4VOWL`sw_k*0?_MkY6?^{a$KQYd{r!(_@UX}2VBk@|_%iX=62p9fALe5z5qp0D#5yB08nejA z>=jmH;cdBMxy)%?AJ%bK_{&z!+Q-Oz2P0cMm}IkNlWjl?lon(WXgj4v*#WeZ(stPe zw42fn*#mSFrJZs!&@Gg9$*n+pDeabhK(|raBeR1{pgnsXguA#JO(dl`Wi^wGYKo+# z(@9nO&-M4D7*U|&h?GpI+7T(6iDUC663t{3sdkVmMN@HUI;|y^60xY3NT<}`^j%ix z=YUy&)m_G@@nk|tX)zWi#Xo$w(%;@0Al8|AX3=u`CkzvZ`flb0c7>JM1?)$k1yiZa zkuI~2VM4H!yOuv-n{FeDmL;ixMdwj!g6v^_+ftgWMl}^@9neosoLLwP{8omntd0T_{4*&=f?FI(YllZD0JkR`;1EV6hC zN5E6n+b%n3Z>Q`EvM|O4UvB8lhPVU!d*mjge~^*Ko-dD(TV*fI(2S#j_5s~eqT7IO z2O1>-DMEY$&KnOO&waR z`2_{A=9BtkB@`p{BXt1Ijb4LVDVj{~v)x2W(pD5nT~h(>L7msuGKy|X-+WbxX}Tjr z(iw$l2}RYd$;4_x(}hGzQql^=mL?O1V`xTTq&#d+Jl;lQf4Su8pq|G&#T`qPF zU75(0gfZ87$e7NYU5N*B1BTzl)k~M>rmji*`{-g;RU#|eYO<^c!*$o|;qZ%wM~|lJ zlrXB6Rh=f1p=rV3p_Vt2Vho&N#pCC$QiNSH0z^$PkPbz&S{gjC5|=10L$k?f>@`3m zAWN0fDQO}dn*-QM${P?N)Id;ogC?`9@DV{c=2|W#l9~e2teIVPK}kh#CY5-u`M7je zDt!R2Q=faT_}yxc_o!y&9xg_S-07NoWE*|&E3VV4(M&Fl8Q&PbOekz5>?g2 za!LWKbPzC{3`SP4&?q6%wOp9)M0!aoH%TOYOFbjK{N?~uDTy}}61lk+DeIU*cHoRO zKzC>0tygqw0-S-W+so_79iCa!R$#@-4>JH{6_zF`!Q~)@yb3`vt4e3jN~6zbl+>s$ z#8x0yDoNc+ms8%65)`XVVp$XWVYi&kr==WD@k zcYN(*?CwI#k%E7=)W2(Ue)BRN2GrJ#e0R_oBG_<<1BSHIA)zAAwwy1DOiKq&)Aif# zrz@kj-^!nx$#=g{@bzrjw#A*J`EV@X7ccmFx7xS;J7@D3mh#fF@!7d^<=*RgDO|K$ zK{CM()3}>qngL^)g8yPe$x7_=eaB0j^U_$Qu8N`H28P|{Qk9Bo2Dg^BSMIsO!?g998u~%L@1-h*y`1k`DEJOL){^)eUCm{==;2+M9rV9SP zt�R`Ti%00zcSZbTDlaR_sjW$20l<*MXHe;cumxg1>+36m*nmN1W5X^VGd)zVF%M z@lMox59)mg7WN!#tgPUl_#VaE^Lhu zmmvqVl^+jKRF0Q`z=KxhyZ~nhl#)KOWq~i#+ycr%ADLf3do*b40zJ^-hiqHG44*zB zOs@pe1Rtami*76=EC#T^jGi2WA|U8(5gHk(AFUi8bZ=yT=A*Yq4$M%XzT_rrxd4;> zQCIhwOsah<)E?kz$x|q3#V3j23I(EX7!1rf_)lZX%z4?+m)o9+y%=KBx=`%~VlkOfY391&oc&PU0zN^pzrTF5h4 z!$Fq!UGVuibSfXp1oqHZ3=|*4i}?CbrC${^Ltpyf1V4tqy^wVzE2?{B2GV}(^Xxn`W36j~PnlipJyBYlY%vdVlW_2V6?pxSUFWV@9Zbej!Q@OC zkri(J%)DjMBJ-z8oCXS?8#;9VvEs(Jf5~yNdHw-$%rHw zW_%7l2o`i7W`OtuDD!vj&Yvx=Un+Pz@0`DVz99PbreML-`tHg*D;u}AEW4h;qQLkD z_ZZ%1`-b5?w)IP2I~Z5#8~?;x88 zC{?lxmtx0|^NaMTLNZj(92bAc`G@@QCO z@3LVV|F=CrJ?dTl1uoIN-?8H^L-B!D#Mf^)`n7+TG2NrP{3Q^LcUgwZF1h9+O9ls0 zyTh^tYAn@zE_2{y5ZiT+%q_Ae9zuM^0*>k+SH#a-%wB*RYYl3uf?@*)#DN2{9e^64 zE-5E{ARR@L0iwHxYaudJ>yA>)s{zwpo_ZECk3ED;bS;^V#tGg|st7-DE+y~sEPN3x z=+>lwsDjw%qGyBOn1A0@5L-4*Y}(%kN7K47vDx*RZ-`QNF5kYq`O;i1`(0Fehia}Wq@Qia}}U} zovViH^+7z7U4{D&JJZWF042h{2IK(J62$jKN&b7IB;P1Xl70|npMSGd&XTn-E#*(O zFtt`-O4;dF8e$EkqJ9ynC8&mRIxOgBC4i`csu}y%wgz@R!|Rj(-g4-t)`GM7&(9Wm z`hW5APbYsi{gstz4HRuP_|)!Q`sha=U*GjkJ#bGoK)NPryigo@O%~ z-Ui?k2gG(m9(AB=1lK#y1vzsZ{2T+k5!-Rd_8X3}P^Vpk-fA9m>Sz zXemoZ?Ptz11=sQoR%ETpB5kKi0UqpwuW{m4r_hdi~=^AKp4K?hQA%9;^YNvEoTytd16{g zoj*z943@Zdph;c8Zdg!M{S^>3Ce^$?S#Y|0N4^V>i8WmCT6^nLNEa+=z%H%