MCP-Frappe/frappe_mcp/module_registry.py
MOHAN 2ee93048e1 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
2026-04-21 20:26:45 +05:30

138 lines
5.6 KiB
Python

"""
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_<KEY>=false → disabled
2. MODULE_<KEY>=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")