"""
Project and task management tools.
Provides tools for listing projects and tasks from Odoo.
"""
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 project tools with the MCP server."""
@mcp.tool()
@handle_odoo_errors
def list_projects(
limit: int = 50,
offset: int = 0,
) -> str:
"""
List available projects in Odoo.
Args:
limit: Maximum number of projects to return (default: 50)
offset: Offset for pagination (default: 0)
Returns:
List of projects with their ID and name
"""
limit, offset = normalize_pagination(limit, offset)
client = get_odoo_client()
projects = client.search_read(
OdooModel.PROJECT,
[],
["name", "partner_id", "user_id", "task_count"],
limit=limit,
offset=offset,
order="name asc",
)
if not projects:
return "No projects found."
builder = MarkdownBuilder("Projects")
rows = []
for p in projects:
rows.append([
p["id"],
p["name"],
extract_name(p.get("partner_id"), "-"),
extract_name(p.get("user_id"), "-"),
p.get("task_count", 0),
])
builder.add_table(
["ID", "Name", "Partner", "Manager", "Tasks"],
rows,
alignments=["right", "left", "left", "left", "right"],
)
builder.add_pagination(len(projects), limit, offset)
return builder.build()
@mcp.tool()
@handle_odoo_errors
def list_tasks(
project_id: int | None = None,
limit: int = 50,
offset: int = 0,
) -> str:
"""
List available tasks in Odoo.
Args:
project_id: Filter by project ID (optional)
limit: Maximum number of tasks to return (default: 50)
offset: Offset for pagination (default: 0)
Returns:
List of tasks with their ID, name and project
"""
limit, offset = normalize_pagination(limit, offset)
client = get_odoo_client()
domain: list = []
if project_id:
domain.append(("project_id", "=", project_id))
tasks = client.search_read(
OdooModel.TASK,
domain,
["name", "project_id", "user_ids", "stage_id", "date_deadline"],
limit=limit,
offset=offset,
order="project_id, name asc",
)
if not tasks:
return "No tasks found."
builder = MarkdownBuilder("Tasks")
if project_id:
builder.add_text(f"*Filtered by project ID: {project_id}*\n")
rows = []
for t in tasks:
# Get first assigned user if any
user_ids = t.get("user_ids", [])
assignee = "-"
if user_ids:
# user_ids is a list of IDs, we'd need another call to get names
assignee = f"{len(user_ids)} user(s)"
rows.append([
t["id"],
t["name"][:40] + "..." if len(t["name"]) > 40 else t["name"],
extract_name(t.get("project_id"), "-"),
extract_name(t.get("stage_id"), "-"),
t.get("date_deadline") or "-",
])
builder.add_table(
["ID", "Name", "Project", "Stage", "Deadline"],
rows,
alignments=["right", "left", "left", "left", "center"],
)
builder.add_pagination(len(tasks), limit, offset)
return builder.build()