- 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
145 lines
5.0 KiB
Python
145 lines
5.0 KiB
Python
"""
|
|
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)
|