feat(search): add server-side search tools for products and customers (BLOCKER-1)

This commit is contained in:
Benju1
2025-12-05 03:11:45 +01:00
parent 809e8f0c59
commit 987424f6e3
2 changed files with 101 additions and 0 deletions

View File

@@ -272,6 +272,12 @@ class DolibarrClient:
# CUSTOMER/THIRD PARTY MANAGEMENT
# ============================================================================
async def search_customers(self, sqlfilters: str, limit: int = 20) -> List[Dict[str, Any]]:
"""Search customers using SQL filters."""
params = {"limit": limit, "sqlfilters": sqlfilters}
result = await self.request("GET", "thirdparties", params=params)
return result if isinstance(result, list) else []
async def get_customers(self, limit: int = 100, page: int = 1) -> List[Dict[str, Any]]:
"""Get list of customers/third parties."""
params = {"limit": limit}
@@ -330,6 +336,12 @@ class DolibarrClient:
# PRODUCT MANAGEMENT
# ============================================================================
async def search_products(self, sqlfilters: str, limit: int = 20) -> List[Dict[str, Any]]:
"""Search products using SQL filters."""
params = {"limit": limit, "sqlfilters": sqlfilters}
result = await self.request("GET", "products", params=params)
return result if isinstance(result, list) else []
async def get_products(self, limit: int = 100) -> List[Dict[str, Any]]:
"""Get list of products."""
params = {"limit": limit}

View File

@@ -44,6 +44,59 @@ async def handle_list_tools():
description="Get Dolibarr system status and version information",
inputSchema={"type": "object", "properties": {}, "additionalProperties": False}
),
# Search Tools
Tool(
name="search_products_by_ref",
description="Search products by reference prefix (e.g. 'PRJ-123')",
inputSchema={
"type": "object",
"properties": {
"ref_prefix": {"type": "string", "description": "Prefix of the product reference"},
"limit": {"type": "integer", "description": "Maximum number of results", "default": 20}
},
"required": ["ref_prefix"],
"additionalProperties": False
}
),
Tool(
name="search_customers",
description="Search customers by name or alias",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search term for name or alias"},
"limit": {"type": "integer", "description": "Maximum number of results", "default": 20}
},
"required": ["query"],
"additionalProperties": False
}
),
Tool(
name="search_products_by_label",
description="Search products by label/description",
inputSchema={
"type": "object",
"properties": {
"label_search": {"type": "string", "description": "Search term in product label"},
"limit": {"type": "integer", "description": "Maximum number of results", "default": 20}
},
"required": ["label_search"],
"additionalProperties": False
}
),
Tool(
name="resolve_product_ref",
description="Find exactly one product by reference. Returns status 'ok', 'not_found', or 'ambiguous'.",
inputSchema={
"type": "object",
"properties": {
"ref": {"type": "string", "description": "Exact product reference"}
},
"required": ["ref"],
"additionalProperties": False
}
),
# User Management CRUD
Tool(
@@ -514,6 +567,42 @@ async def handle_call_tool(name: str, arguments: dict):
elif name == "get_status":
result = await client.get_status()
# Search Tools
elif name == "search_products_by_ref":
ref_prefix = arguments['ref_prefix']
limit = arguments.get('limit', 20)
sqlfilters = f"(t.ref:like:'{ref_prefix}%')"
result = await client.search_products(sqlfilters=sqlfilters, limit=limit)
elif name == "search_customers":
query = arguments['query']
limit = arguments.get('limit', 20)
sqlfilters = f"((t.nom:like:'%{query}%') OR (t.name_alias:like:'%{query}%'))"
result = await client.search_customers(sqlfilters=sqlfilters, limit=limit)
elif name == "search_products_by_label":
label_search = arguments['label_search']
limit = arguments.get('limit', 20)
sqlfilters = f"(t.label:like:'%{label_search}%')"
result = await client.search_products(sqlfilters=sqlfilters, limit=limit)
elif name == "resolve_product_ref":
ref = arguments['ref']
sqlfilters = f"(t.ref:like:'{ref}')"
products = await client.search_products(sqlfilters=sqlfilters, limit=2)
if not products:
result = {"status": "not_found", "message": f"Product with ref '{ref}' not found"}
elif len(products) == 1:
result = {"status": "ok", "product": products[0]}
else:
# Check if one is exact match
exact_matches = [p for p in products if p.get('ref') == ref]
if len(exact_matches) == 1:
result = {"status": "ok", "product": exact_matches[0]}
else:
result = {"status": "ambiguous", "message": f"Multiple products found for ref '{ref}'", "products": products}
# User Management
elif name == "get_users":
result = await client.get_users(