"""
Contact management tools.
Provides CRUD operations for contacts (res.partner).
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from ..constants import OdooModel
from ..decorators import handle_odoo_errors
from ..formatters import MarkdownBuilder
from .base import extract_name, get_odoo_client, normalize_pagination
if TYPE_CHECKING:
from mcp.server.fastmcp import FastMCP
def register_tools(mcp: "FastMCP") -> None:
"""Register contact tools with the MCP server."""
@mcp.tool()
@handle_odoo_errors
def list_contacts(
customer: bool | None = None,
supplier: bool | None = None,
company: bool | None = None,
limit: int = 50,
offset: int = 0,
) -> str:
"""
List contacts (partners) in Odoo.
Args:
customer: Filter customers (True/False/None for all)
supplier: Filter suppliers (True/False/None for all)
company: Filter companies (True) or individuals (False)
limit: Maximum number of contacts (default: 50)
offset: Offset for pagination (default: 0)
Returns:
List of contacts with their information
"""
limit, offset = normalize_pagination(limit, offset)
client = get_odoo_client()
domain: list = []
if customer is not None:
domain.append(("customer_rank", ">" if customer else "=", 0))
if supplier is not None:
domain.append(("supplier_rank", ">" if supplier else "=", 0))
if company is not None:
domain.append(("is_company", "=", company))
contacts = client.search_read(
OdooModel.PARTNER,
domain,
["name", "email", "phone", "city", "country_id", "is_company"],
limit=limit,
offset=offset,
order="name asc",
)
if not contacts:
return "No contacts found."
builder = MarkdownBuilder("Contacts")
rows = []
for c in contacts:
contact_type = "Company" if c.get("is_company") else "Individual"
rows.append([
c["id"],
c["name"],
c.get("email") or "-",
c.get("phone") or "-",
c.get("city") or "-",
extract_name(c.get("country_id"), "-"),
contact_type,
])
builder.add_table(
["ID", "Name", "Email", "Phone", "City", "Country", "Type"],
rows,
alignments=["right", "left", "left", "left", "left", "left", "center"],
)
builder.add_pagination(len(contacts), limit, offset)
return builder.build()
@mcp.tool()
@handle_odoo_errors
def get_contact(contact_id: int) -> str:
"""
Get details of a specific contact.
Args:
contact_id: Contact ID
Returns:
Complete contact details
"""
client = get_odoo_client()
contact = client.get_record(
OdooModel.PARTNER,
contact_id,
[
"name",
"email",
"phone",
"mobile",
"street",
"street2",
"city",
"zip",
"state_id",
"country_id",
"website",
"vat",
"is_company",
"parent_id",
"child_ids",
"user_id",
"comment",
],
)
builder = MarkdownBuilder(f"Contact: {contact['name']}")
# Basic info
builder.add_heading("General Information", 2)
builder.add_key_value({
"ID": contact["id"],
"Name": contact["name"],
"Type": "Company" if contact.get("is_company") else "Individual",
"Parent Company": extract_name(contact.get("parent_id")),
})
# Contact info
builder.add_heading("Contact Details", 2)
builder.add_key_value({
"Email": contact.get("email"),
"Phone": contact.get("phone"),
"Mobile": contact.get("mobile"),
"Website": contact.get("website"),
})
# Address
builder.add_heading("Address", 2)
address_parts = [
contact.get("street"),
contact.get("street2"),
f"{contact.get('zip', '')} {contact.get('city', '')}".strip(),
extract_name(contact.get("state_id")),
extract_name(contact.get("country_id")),
]
address = ", ".join(p for p in address_parts if p)
builder.add_text(address or "No address")
# Additional info
if contact.get("vat"):
builder.add_heading("Tax Information", 2)
builder.add_key_value({"VAT": contact.get("vat")})
if contact.get("comment"):
builder.add_heading("Notes", 2)
builder.add_text(contact["comment"])
return builder.build()
@mcp.tool()
@handle_odoo_errors
def create_contact(
name: str,
email: str | None = None,
phone: str | None = None,
is_company: bool = False,
customer: bool = True,
supplier: bool = False,
street: str | None = None,
city: str | None = None,
zip_code: str | None = None,
parent_id: int | None = None,
) -> str:
"""
Create a new contact.
Args:
name: Contact name
email: Email address (optional)
phone: Phone number (optional)
is_company: True if it's a company, False for individual
customer: Mark as customer (default: True)
supplier: Mark as supplier (default: False)
street: Address (optional)
city: City (optional)
zip_code: Zip code (optional)
parent_id: Parent company ID for contacts (optional)
Returns:
Confirmation with created contact ID
"""
client = get_odoo_client()
values = {
"name": name,
"is_company": is_company,
}
if email:
values["email"] = email
if phone:
values["phone"] = phone
if street:
values["street"] = street
if city:
values["city"] = city
if zip_code:
values["zip"] = zip_code
if parent_id:
values["parent_id"] = parent_id
# Set customer/supplier rank
if customer:
values["customer_rank"] = 1
if supplier:
values["supplier_rank"] = 1
record_id = client.create(OdooModel.PARTNER, values)
return f"Contact created successfully (ID: {record_id})"