MCP-Frappe/frappe_mcp/tools/documents.py

171 lines
5.8 KiB
Python

"""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",
"items": {},
"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)