mirror of
https://github.com/latinogino/dolibarr-mcp.git
synced 2026-05-01 13:55:35 +02:00
feat(search): add server-side search tools for products and customers (BLOCKER-1)
This commit is contained in:
@@ -272,6 +272,12 @@ class DolibarrClient:
|
|||||||
# CUSTOMER/THIRD PARTY MANAGEMENT
|
# 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]]:
|
async def get_customers(self, limit: int = 100, page: int = 1) -> List[Dict[str, Any]]:
|
||||||
"""Get list of customers/third parties."""
|
"""Get list of customers/third parties."""
|
||||||
params = {"limit": limit}
|
params = {"limit": limit}
|
||||||
@@ -330,6 +336,12 @@ class DolibarrClient:
|
|||||||
# PRODUCT MANAGEMENT
|
# 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]]:
|
async def get_products(self, limit: int = 100) -> List[Dict[str, Any]]:
|
||||||
"""Get list of products."""
|
"""Get list of products."""
|
||||||
params = {"limit": limit}
|
params = {"limit": limit}
|
||||||
|
|||||||
@@ -44,6 +44,59 @@ async def handle_list_tools():
|
|||||||
description="Get Dolibarr system status and version information",
|
description="Get Dolibarr system status and version information",
|
||||||
inputSchema={"type": "object", "properties": {}, "additionalProperties": False}
|
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
|
# User Management CRUD
|
||||||
Tool(
|
Tool(
|
||||||
@@ -514,6 +567,42 @@ async def handle_call_tool(name: str, arguments: dict):
|
|||||||
elif name == "get_status":
|
elif name == "get_status":
|
||||||
result = await client.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
|
# User Management
|
||||||
elif name == "get_users":
|
elif name == "get_users":
|
||||||
result = await client.get_users(
|
result = await client.get_users(
|
||||||
|
|||||||
Reference in New Issue
Block a user