Download server.py from jdewitte/mcp: direct link, hf CLI and curl.
- Browser
- Download file 13.6 kB
-
https://huggingface.co/spaces/jdewitte/mcp/resolve/main/server.py
- Command line
-
hf download hf://spaces/jdewitte/mcp/server.py
-
curl -L -o server.py https://huggingface.co/spaces/jdewitte/mcp/resolve/main/server.py
13.6 kB
| """ | |
| PrestaShop MCP Server | |
| A Model Context Protocol server that provides tools for accessing PrestaShop product data. | |
| """ | |
| import asyncio | |
| import json | |
| import logging | |
| from typing import Any, Dict, List, Optional | |
| from urllib.parse import urlencode | |
| import httpx | |
| from mcp.server import Server | |
| from mcp.types import ( | |
| Resource, | |
| Tool, | |
| TextContent, | |
| ImageContent, | |
| EmbeddedResource, | |
| CallToolRequest, | |
| CallToolResult, | |
| ListToolsRequest, | |
| ListToolsResult, | |
| ListResourcesRequest, | |
| ListResourcesResult, | |
| ReadResourceRequest, | |
| ReadResourceResult, | |
| ) | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Default PrestaShop API configuration | |
| DEFAULT_API_BASE = "https://shop.mcp.integration.ambris.com" | |
| DEFAULT_TIMEOUT = 30 | |
| class PrestaShopMCPServer: | |
| """MCP Server for PrestaShop integration""" | |
| def __init__(self, api_base: str = DEFAULT_API_BASE): | |
| self.api_base = api_base.rstrip('/') | |
| self.server = Server("prestashop-mcp") | |
| self.setup_handlers() | |
| def setup_handlers(self): | |
| """Set up MCP server handlers""" | |
| async def list_tools() -> List[Tool]: | |
| """List available tools""" | |
| return [ | |
| Tool( | |
| name="get_product_details", | |
| description="Get detailed information about a PrestaShop product including name, description, price, and features", | |
| inputSchema={ | |
| "type": "object", | |
| "properties": { | |
| "product_id": { | |
| "type": "integer", | |
| "description": "The ID of the product to retrieve" | |
| }, | |
| "lang_id": { | |
| "type": "integer", | |
| "description": "Language ID for localized content", | |
| "default": 1 | |
| } | |
| }, | |
| "required": ["product_id"] | |
| } | |
| ), | |
| Tool( | |
| name="get_product_features", | |
| description="Get product features and specifications from PrestaShop", | |
| inputSchema={ | |
| "type": "object", | |
| "properties": { | |
| "product_id": { | |
| "type": "integer", | |
| "description": "The ID of the product to get features for" | |
| } | |
| }, | |
| "required": ["product_id"] | |
| } | |
| ), | |
| Tool( | |
| name="get_product_images", | |
| description="Get product images and media from PrestaShop", | |
| inputSchema={ | |
| "type": "object", | |
| "properties": { | |
| "product_id": { | |
| "type": "integer", | |
| "description": "The ID of the product to get images for" | |
| } | |
| }, | |
| "required": ["product_id"] | |
| } | |
| ), | |
| Tool( | |
| name="search_products", | |
| description="Search for products in PrestaShop catalog", | |
| inputSchema={ | |
| "type": "object", | |
| "properties": { | |
| "query": { | |
| "type": "string", | |
| "description": "Search query term" | |
| }, | |
| "limit": { | |
| "type": "integer", | |
| "description": "Maximum number of results to return", | |
| "default": 10 | |
| }, | |
| "category_id": { | |
| "type": "integer", | |
| "description": "Optional category ID to filter results" | |
| } | |
| }, | |
| "required": ["query"] | |
| } | |
| ) | |
| ] | |
| async def call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]: | |
| """Handle tool calls""" | |
| try: | |
| if name == "get_product_details": | |
| return await self._get_product_details(arguments) | |
| elif name == "get_product_features": | |
| return await self._get_product_features(arguments) | |
| elif name == "get_product_images": | |
| return await self._get_product_images(arguments) | |
| elif name == "search_products": | |
| return await self._search_products(arguments) | |
| else: | |
| return [TextContent( | |
| type="text", | |
| text=f"Unknown tool: {name}" | |
| )] | |
| except Exception as e: | |
| logger.error(f"Error calling tool {name}: {e}") | |
| return [TextContent( | |
| type="text", | |
| text=f"Error calling tool {name}: {str(e)}" | |
| )] | |
| async def _make_api_request(self, endpoint: str, params: Dict[str, Any] = None) -> Dict[str, Any]: | |
| """Make HTTP request to PrestaShop API""" | |
| url = f"{self.api_base}/index.php" | |
| # Default parameters | |
| default_params = { | |
| "fc": "module", | |
| "module": "ambaiagent", | |
| "controller": "api" | |
| } | |
| if params: | |
| default_params.update(params) | |
| async with httpx.AsyncClient(timeout=DEFAULT_TIMEOUT) as client: | |
| try: | |
| response = await client.get(url, params=default_params) | |
| response.raise_for_status() | |
| data = response.json() | |
| if not data.get('success', False): | |
| raise Exception(f"API error: {data.get('error', 'Unknown error')}") | |
| return data | |
| except httpx.TimeoutException: | |
| raise Exception("Request timeout - PrestaShop API took too long to respond") | |
| except httpx.HTTPStatusError as e: | |
| raise Exception(f"HTTP error {e.response.status_code}: {e.response.text}") | |
| except json.JSONDecodeError: | |
| raise Exception("Invalid JSON response from PrestaShop API") | |
| async def _get_product_details(self, arguments: Dict[str, Any]) -> List[TextContent]: | |
| """Get detailed product information""" | |
| product_id = arguments.get("product_id") | |
| lang_id = arguments.get("lang_id", 1) | |
| if not product_id: | |
| return [TextContent( | |
| type="text", | |
| text="Error: product_id is required" | |
| )] | |
| try: | |
| data = await self._make_api_request("", { | |
| "action": "get_product", | |
| "product_id": product_id, | |
| "lang_id": lang_id | |
| }) | |
| product = data.get("product", {}) | |
| # Format the product information | |
| result = f"""PRESTASHOP PRODUCT DETAILS (ID: {product_id}) | |
| Name: {product.get('name', 'N/A')} | |
| Description: {product.get('description', 'N/A')} | |
| Short Description: {product.get('description_short', 'N/A')} | |
| Price: {product.get('price', 'N/A')} | |
| Reference: {product.get('reference', 'N/A')} | |
| EAN13: {product.get('ean13', 'N/A')} | |
| Weight: {product.get('weight', 'N/A')} | |
| Quantity: {product.get('quantity', 'N/A')} | |
| Active: {product.get('active', 'N/A')} | |
| Available for Order: {product.get('available_for_order', 'N/A')} | |
| Categories: {json.dumps(product.get('categories', []), indent=2)} | |
| Features: {json.dumps(product.get('features', []), indent=2)} | |
| Images: {json.dumps(product.get('images', []), indent=2)} | |
| Full Product Data: | |
| {json.dumps(product, indent=2)}""" | |
| return [TextContent(type="text", text=result)] | |
| except Exception as e: | |
| return [TextContent( | |
| type="text", | |
| text=f"Error retrieving product {product_id}: {str(e)}" | |
| )] | |
| async def _get_product_features(self, arguments: Dict[str, Any]) -> List[TextContent]: | |
| """Get product features""" | |
| product_id = arguments.get("product_id") | |
| if not product_id: | |
| return [TextContent( | |
| type="text", | |
| text="Error: product_id is required" | |
| )] | |
| try: | |
| data = await self._make_api_request("", { | |
| "action": "get_product", | |
| "product_id": product_id | |
| }) | |
| product = data.get("product", {}) | |
| features = product.get("features", []) | |
| if not features: | |
| result = f"No features found for product {product_id}" | |
| else: | |
| result = f"PRESTASHOP PRODUCT FEATURES (ID: {product_id}):\n\n" | |
| result += json.dumps(features, indent=2) | |
| return [TextContent(type="text", text=result)] | |
| except Exception as e: | |
| return [TextContent( | |
| type="text", | |
| text=f"Error retrieving features for product {product_id}: {str(e)}" | |
| )] | |
| async def _get_product_images(self, arguments: Dict[str, Any]) -> List[TextContent]: | |
| """Get product images""" | |
| product_id = arguments.get("product_id") | |
| if not product_id: | |
| return [TextContent( | |
| type="text", | |
| text="Error: product_id is required" | |
| )] | |
| try: | |
| data = await self._make_api_request("", { | |
| "action": "get_product", | |
| "product_id": product_id | |
| }) | |
| product = data.get("product", {}) | |
| images = product.get("images", []) | |
| if not images: | |
| result = f"No images found for product {product_id}" | |
| else: | |
| result = f"PRESTASHOP PRODUCT IMAGES (ID: {product_id}):\n\n" | |
| result += json.dumps(images, indent=2) | |
| return [TextContent(type="text", text=result)] | |
| except Exception as e: | |
| return [TextContent( | |
| type="text", | |
| text=f"Error retrieving images for product {product_id}: {str(e)}" | |
| )] | |
| async def _search_products(self, arguments: Dict[str, Any]) -> List[TextContent]: | |
| """Search for products""" | |
| query = arguments.get("query") | |
| limit = arguments.get("limit", 10) | |
| category_id = arguments.get("category_id") | |
| if not query: | |
| return [TextContent( | |
| type="text", | |
| text="Error: query is required" | |
| )] | |
| try: | |
| params = { | |
| "action": "search_products", | |
| "query": query, | |
| "limit": limit | |
| } | |
| if category_id: | |
| params["category_id"] = category_id | |
| data = await self._make_api_request("", params) | |
| products = data.get("products", []) | |
| if not products: | |
| result = f"No products found for query: '{query}'" | |
| else: | |
| result = f"PRESTASHOP PRODUCT SEARCH RESULTS for '{query}':\n\n" | |
| result += json.dumps(products, indent=2) | |
| return [TextContent(type="text", text=result)] | |
| except Exception as e: | |
| return [TextContent( | |
| type="text", | |
| text=f"Error searching for products with query '{query}': {str(e)}" | |
| )] | |
| async def run(self, transport_type: str = "stdio"): | |
| """Run the MCP server""" | |
| if transport_type == "stdio": | |
| from mcp.server.stdio import stdio_server | |
| async with stdio_server() as (read_stream, write_stream): | |
| await self.server.run( | |
| read_stream, | |
| write_stream, | |
| self.server.create_initialization_options() | |
| ) | |
| else: | |
| raise ValueError(f"Unsupported transport type: {transport_type}") | |
| def main(): | |
| """Main entry point""" | |
| import argparse | |
| parser = argparse.ArgumentParser(description="PrestaShop MCP Server") | |
| parser.add_argument( | |
| "--api-base", | |
| default=DEFAULT_API_BASE, | |
| help="PrestaShop API base URL" | |
| ) | |
| parser.add_argument( | |
| "--transport", | |
| choices=["stdio"], | |
| default="stdio", | |
| help="Transport mechanism" | |
| ) | |
| args = parser.parse_args() | |
| server = PrestaShopMCPServer(api_base=args.api_base) | |
| try: | |
| asyncio.run(server.run(args.transport)) | |
| except KeyboardInterrupt: | |
| logger.info("Server stopped by user") | |
| except Exception as e: | |
| logger.error(f"Server error: {e}") | |
| raise | |
| if __name__ == "__main__": | |
| main() | |