read only update
This commit is contained in:
+2
-123
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Odoo MCP Server for MPM
|
||||
Connects to mpmedia.odoo.com via XML-RPC and exposes tools for
|
||||
Odoo MCP Server for MPM (read-only)
|
||||
Connects to mpmedia.odoo.com via XML-RPC and exposes read-only tools for
|
||||
Products, Knowledge, Contacts, Sales, CRM, Project, Helpdesk,
|
||||
Purchase, Inventory, Employees, and Knowledge Templates.
|
||||
"""
|
||||
@@ -82,12 +82,6 @@ def _get_credentials() -> tuple[str, str]:
|
||||
api_key = ODOO_API_KEY or _keychain_get("odoo_api_key")
|
||||
return username, api_key
|
||||
|
||||
def _get_credentials() -> tuple[str, str]:
|
||||
"""Resolve username and API key: env vars take priority, then macOS Keychain."""
|
||||
username = ODOO_USERNAME or _keychain_get("odoo_username")
|
||||
api_key = ODOO_API_KEY or _keychain_get("odoo_api_key")
|
||||
return username, api_key
|
||||
|
||||
def _connect():
|
||||
global _uid, _models, _resolved_api_key
|
||||
if _uid is not None:
|
||||
@@ -132,11 +126,6 @@ def _read(model, ids, fields=None):
|
||||
kw["fields"] = fields
|
||||
return _call(model, "read", [ids], kw)
|
||||
|
||||
def _create(model, vals):
|
||||
return _call(model, "create", [vals])
|
||||
|
||||
def _write(model, ids, vals):
|
||||
return _call(model, "write", [[ids] if isinstance(ids, int) else ids, vals])
|
||||
|
||||
# ── FastMCP App ───────────────────────────────────────────────────────────────
|
||||
mcp = FastMCP("Odoo MPM")
|
||||
@@ -274,26 +263,6 @@ def get_knowledge_article(article_id: int) -> dict:
|
||||
)
|
||||
return article
|
||||
|
||||
@mcp.tool()
|
||||
def create_knowledge_article(name: str, body: str, parent_id: int = None) -> int:
|
||||
"""Create a new Knowledge article. body is HTML. Returns new article ID."""
|
||||
vals = {"name": name, "body": body}
|
||||
if parent_id:
|
||||
vals["parent_id"] = parent_id
|
||||
return _create("knowledge.article", vals)
|
||||
|
||||
@mcp.tool()
|
||||
def update_knowledge_article(article_id: int, name: str = "", body: str = "") -> bool:
|
||||
"""Update a Knowledge article's title and/or body (HTML)."""
|
||||
vals = {}
|
||||
if name:
|
||||
vals["name"] = name
|
||||
if body:
|
||||
vals["body"] = body
|
||||
if not vals:
|
||||
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.
|
||||
@@ -375,18 +344,6 @@ def get_contact(contact_id: int) -> dict:
|
||||
"website", "comment", "category_id", "child_ids", "user_id"])
|
||||
return r[0] if r else {}
|
||||
|
||||
@mcp.tool()
|
||||
def create_contact(name: str, email: str = "", phone: str = "",
|
||||
is_company: bool = False, parent_id: int = None,
|
||||
street: str = "", city: str = "") -> int:
|
||||
"""Create a new contact. Returns the new contact ID."""
|
||||
vals = {"name": name, "is_company": is_company}
|
||||
if email: vals["email"] = email
|
||||
if phone: vals["phone"] = phone
|
||||
if parent_id: vals["parent_id"] = parent_id
|
||||
if street: vals["street"] = street
|
||||
if city: vals["city"] = city
|
||||
return _create("res.partner", vals)
|
||||
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
@@ -425,19 +382,6 @@ def get_sales_order(order_id: int) -> dict:
|
||||
order["lines"] = lines
|
||||
return order
|
||||
|
||||
@mcp.tool()
|
||||
def create_sales_order(partner_id: int, lines: list) -> int:
|
||||
"""Create a sales order. lines is a list of dicts with keys:
|
||||
product_id (int), product_uom_qty (float), price_unit (float).
|
||||
Returns new order ID."""
|
||||
order_lines = []
|
||||
for l in lines:
|
||||
order_lines.append((0, 0, {
|
||||
"product_id": l["product_id"],
|
||||
"product_uom_qty": l.get("product_uom_qty", 1),
|
||||
"price_unit": l.get("price_unit", 0),
|
||||
}))
|
||||
return _create("sale.order", {"partner_id": partner_id, "order_line": order_lines})
|
||||
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
@@ -470,28 +414,6 @@ def get_crm_lead(lead_id: int) -> dict:
|
||||
"activity_ids", "date_conversion"])
|
||||
return r[0] if r else {}
|
||||
|
||||
@mcp.tool()
|
||||
def create_crm_lead(name: str, partner_id: int = None, expected_revenue: float = 0,
|
||||
description: str = "", email: str = "", phone: str = "") -> int:
|
||||
"""Create a new CRM opportunity. Returns new lead ID."""
|
||||
vals = {"name": name, "type": "opportunity", "expected_revenue": expected_revenue}
|
||||
if partner_id: vals["partner_id"] = partner_id
|
||||
if description: vals["description"] = description
|
||||
if email: vals["email_from"] = email
|
||||
if phone: vals["phone"] = phone
|
||||
return _create("crm.lead", vals)
|
||||
|
||||
@mcp.tool()
|
||||
def update_crm_lead(lead_id: int, stage_id: int = None, probability: float = None,
|
||||
expected_revenue: float = None, note: str = "") -> bool:
|
||||
"""Update a CRM lead's stage, probability, revenue, or notes."""
|
||||
vals = {}
|
||||
if stage_id is not None: vals["stage_id"] = stage_id
|
||||
if probability is not None: vals["probability"] = probability
|
||||
if expected_revenue is not None: vals["expected_revenue"] = expected_revenue
|
||||
if note: vals["description"] = note
|
||||
return _write("crm.lead", lead_id, vals) if vals else False
|
||||
|
||||
@mcp.tool()
|
||||
def list_crm_stages() -> list:
|
||||
"""List all CRM pipeline stages with their IDs and names."""
|
||||
@@ -543,29 +465,6 @@ def get_task(task_id: int) -> dict:
|
||||
"child_ids", "depend_on_ids", "planned_hours", "effective_hours"])
|
||||
return r[0] if r else {}
|
||||
|
||||
@mcp.tool()
|
||||
def create_task(name: str, project_id: int, description: str = "",
|
||||
date_deadline: str = "", user_ids: list = None) -> int:
|
||||
"""Create a project task. date_deadline format: YYYY-MM-DD. Returns task ID."""
|
||||
vals = {"name": name, "project_id": project_id}
|
||||
if description: vals["description"] = description
|
||||
if date_deadline: vals["date_deadline"] = date_deadline
|
||||
if user_ids: vals["user_ids"] = [(6, 0, user_ids)]
|
||||
return _create("project.task", vals)
|
||||
|
||||
@mcp.tool()
|
||||
def update_task(task_id: int, name: str = "", stage_id: int = None,
|
||||
description: str = "", date_deadline: str = "",
|
||||
kanban_state: str = "") -> bool:
|
||||
"""Update a task. kanban_state: normal, done, blocked."""
|
||||
vals = {}
|
||||
if name: vals["name"] = name
|
||||
if stage_id: vals["stage_id"] = stage_id
|
||||
if description: vals["description"] = description
|
||||
if date_deadline: vals["date_deadline"] = date_deadline
|
||||
if kanban_state: vals["kanban_state"] = kanban_state
|
||||
return _write("project.task", task_id, vals) if vals else False
|
||||
|
||||
@mcp.tool()
|
||||
def list_task_stages(project_id: int = None) -> list:
|
||||
"""List task stages. Optionally filter by project."""
|
||||
@@ -613,26 +512,6 @@ def get_helpdesk_ticket(ticket_id: int) -> dict:
|
||||
"date_last_stage_update", "kanban_state", "tag_ids"])
|
||||
return r[0] if r else {}
|
||||
|
||||
@mcp.tool()
|
||||
def create_helpdesk_ticket(name: str, description: str = "",
|
||||
partner_id: int = None, team_id: int = None) -> int:
|
||||
"""Create a helpdesk ticket. Returns new ticket ID."""
|
||||
vals = {"name": name}
|
||||
if description: vals["description"] = description
|
||||
if partner_id: vals["partner_id"] = partner_id
|
||||
if team_id: vals["team_id"] = team_id
|
||||
return _create("helpdesk.ticket", vals)
|
||||
|
||||
@mcp.tool()
|
||||
def update_helpdesk_ticket(ticket_id: int, stage_id: int = None,
|
||||
user_id: int = None, note: str = "") -> bool:
|
||||
"""Update a helpdesk ticket's stage, assignee, or add a note."""
|
||||
vals = {}
|
||||
if stage_id: vals["stage_id"] = stage_id
|
||||
if user_id: vals["user_id"] = user_id
|
||||
if note: vals["description"] = note
|
||||
return _write("helpdesk.ticket", ticket_id, vals) if vals else False
|
||||
|
||||
@mcp.tool()
|
||||
def list_helpdesk_teams() -> list:
|
||||
"""List all helpdesk teams."""
|
||||
|
||||
Reference in New Issue
Block a user