""" 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)