Files
nocobase-mcp/nocobase_crm_mcp/server_tools_projects.py
T

81 lines
3.8 KiB
Python

from __future__ import annotations
from collections.abc import Callable
from typing import Annotated, Any
from pydantic import Field
SafeResult = Callable[[Callable[[], dict[str, Any]]], dict[str, Any]]
SafeRawResult = Callable[[bool, Callable[[], dict[str, Any]]], dict[str, Any]]
def register_project_tools(server: Any, service: Any, safe_result: SafeResult, safe_raw_result: SafeRawResult) -> None:
@server.tool()
def list_projects(
customer_id: Annotated[Any | None, Field(description="Optional customer ID.")] = None,
team: Annotated[str | None, Field(description="Optional CRM team name.")] = None,
stage: Annotated[str | None, Field(description="Optional project stage label or configured value.")] = None,
limit: Annotated[int, Field(description="Maximum projects to return.")] = 20,
offset: Annotated[int, Field(description="Zero-based result offset.")] = 0,
include_raw: Annotated[bool, Field(description="Include original NocoBase project rows.")] = False,
) -> dict[str, Any]:
"""List CRM projects linked to customers, teams, and project stages."""
return safe_raw_result(
include_raw,
lambda: service.list_projects(
customer_id=customer_id,
team=team,
stage=stage,
limit=limit,
offset=offset,
include_raw=include_raw,
),
)
@server.tool()
def get_project(
project_id: Annotated[Any, Field(description="NocoBase CRM project ID.")],
include_contacts: Annotated[bool, Field(description="Include related contacts.")] = True,
include_followups: Annotated[bool, Field(description="Include related follow-ups.")] = True,
include_updates: Annotated[bool, Field(description="Include project update records.")] = True,
) -> dict[str, Any]:
"""Get a CRM project with its existing NocoBase relations."""
return safe_result(
lambda: service.get_project(
project_id,
include_contacts=include_contacts,
include_followups=include_followups,
include_updates=include_updates,
)
)
@server.tool()
def get_project_timeline(
project_id: Annotated[Any, Field(description="NocoBase CRM project ID.")],
date_from: Annotated[str | None, Field(description="Optional lower event date bound.")] = None,
date_to: Annotated[str | None, Field(description="Optional upper event date bound.")] = None,
) -> dict[str, Any]:
"""Return a chronological CRM project timeline from follow-ups and project updates."""
return safe_result(lambda: service.get_project_timeline(project_id, date_from=date_from, date_to=date_to))
@server.tool()
def list_stale_customers(
team: Annotated[str | None, Field(description="Optional CRM team name.")] = None,
owner: Annotated[str | None, Field(description="Optional customer owner.")] = None,
inactive_days: Annotated[int, Field(description="Days without a valid follow-up.")] = 30,
as_of: Annotated[str | None, Field(description="Reference date in YYYY-MM-DD format.")] = None,
limit: Annotated[int, Field(description="Maximum customers to return.")] = 20,
offset: Annotated[int, Field(description="Zero-based result offset.")] = 0,
) -> dict[str, Any]:
"""List customers whose last valid follow-up is older than the configured threshold."""
return safe_result(
lambda: service.list_stale_customers(
team=team,
owner=owner,
inactive_days=inactive_days,
as_of=as_of,
limit=limit,
offset=offset,
)
)