From be6266b2c843406def617e6ab0400e94ac332e80 Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 30 Mar 2026 20:06:54 -0500 Subject: [PATCH] Add search_knowledge_templates, get_knowledge_template, list_knowledge_template_categories --- server/odoo_mcp.py | 51 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/server/odoo_mcp.py b/server/odoo_mcp.py index 3dc9ccb..2890503 100644 --- a/server/odoo_mcp.py +++ b/server/odoo_mcp.py @@ -3,7 +3,7 @@ Odoo MCP Server for MPM Connects to mpmedia.odoo.com via XML-RPC and exposes tools for Products, Knowledge, Contacts, Sales, CRM, Project, Helpdesk, -Purchase, Inventory, and Employees. +Purchase, Inventory, Employees, and Knowledge Templates. """ import os @@ -162,6 +162,55 @@ def update_knowledge_article(article_id: int, name: str = "", body: str = "") -> return False return _write("knowledge.article", article_id, vals) +@mcp.tool() +def search_knowledge_templates(query: str = "", category: str = "", limit: int = 50) -> list: + """Search Knowledge Base article templates. + Optionally filter by template name or category name (e.g. 'Productivity', 'Sales', + 'Marketing', 'Company Organization', 'Product Management'). + Returns id, template_name, template_description, category, and sequence.""" + domain = [["is_template", "=", True]] + if query: + domain.append(["template_name", "ilike", query]) + if category: + domain.append(["template_category_id.name", "ilike", category]) + return _search_read("knowledge.article", domain, + ["id", "name", "template_name", "template_description", + "template_category_id", "template_sequence", "is_published"], + limit=limit, order="template_category_sequence asc, template_sequence asc") + +@mcp.tool() +def get_knowledge_template(template_id: int) -> dict: + """Get full details of a Knowledge Base article template by ID, including + the template body content. Inline base64 images are replaced with + [embedded image] placeholders.""" + r = _read("knowledge.article", [template_id], + ["id", "name", "template_name", "template_description", + "template_body", "template_preview", "template_category_id", + "template_sequence", "is_published"]) + if not r: + return {} + template = r[0] + if template.get("template_body"): + template["template_body"] = re.sub( + r'src="data:image/[^;]+;base64,[A-Za-z0-9+/=]+"', + 'src="[embedded image]"', + template["template_body"] + ) + if template.get("template_preview"): + template["template_preview"] = re.sub( + r'src="data:image/[^;]+;base64,[A-Za-z0-9+/=]+"', + 'src="[embedded image]"', + template["template_preview"] + ) + return template + +@mcp.tool() +def list_knowledge_template_categories() -> list: + """List all Knowledge Base article template categories with their IDs and names.""" + return _search_read("knowledge.article.template.category", [], + ["id", "name", "sequence"], + limit=50, order="sequence asc") + # ════════════════════════════════════════════════════════════════════════════ # CONTACTS