Commit Β·
2be6245
0
Parent(s):
Initial commit
Browse files- Dockerfile +25 -0
- README.md +197 -0
- README_NEW.md +197 -0
- README_OLD.md +299 -0
- __init__.py +9 -0
- __pycache__/__init__.cpython-313.pyc +0 -0
- __pycache__/http_server.cpython-313.pyc +0 -0
- __pycache__/server.cpython-313.pyc +0 -0
- http_server.py +185 -0
- requirements.txt +8 -0
- run_server.py +24 -0
- server.py +379 -0
- test_mcp.py +176 -0
- test_standalone.py +90 -0
Dockerfile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
# Install system dependencies
|
| 4 |
+
RUN apt-get update \
|
| 5 |
+
&& apt-get install -y git curl \
|
| 6 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 7 |
+
|
| 8 |
+
# Set workdir
|
| 9 |
+
WORKDIR /app
|
| 10 |
+
|
| 11 |
+
# Install Python dependencies
|
| 12 |
+
COPY requirements.txt /app/requirements.txt
|
| 13 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 14 |
+
|
| 15 |
+
# Copy MCP server code
|
| 16 |
+
COPY . /app/
|
| 17 |
+
|
| 18 |
+
# Make run_server.py executable
|
| 19 |
+
RUN chmod +x /app/run_server.py
|
| 20 |
+
|
| 21 |
+
# Expose port for MCP server
|
| 22 |
+
EXPOSE 8081
|
| 23 |
+
|
| 24 |
+
# Start the MCP server
|
| 25 |
+
CMD ["python", "run_server.py", "--host", "0.0.0.0", "--port", "8081"]
|
README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: PrestaShop MCP Server
|
| 3 |
+
emoji: π§
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 8081
|
| 8 |
+
pinned: false
|
| 9 |
+
license: mit
|
| 10 |
+
tags:
|
| 11 |
+
- mcp
|
| 12 |
+
- prestashop
|
| 13 |
+
- ecommerce
|
| 14 |
+
- api
|
| 15 |
+
- tools
|
| 16 |
+
short_description: Model Context Protocol server for PrestaShop integration
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
# PrestaShop MCP Server
|
| 20 |
+
|
| 21 |
+
A Model Context Protocol (MCP) server that provides tools for accessing PrestaShop data. This server exposes PrestaShop functionality through standardized MCP tools that can be used by AI assistants and other MCP clients.
|
| 22 |
+
|
| 23 |
+
## π Features
|
| 24 |
+
|
| 25 |
+
- **Standard MCP Protocol**: Implements Model Context Protocol specification
|
| 26 |
+
- **PrestaShop Integration**: Direct access to PrestaShop product data
|
| 27 |
+
- **HTTP API**: RESTful interface for easy integration
|
| 28 |
+
- **Multiple Tools**: Product details, features, images, and search
|
| 29 |
+
- **Type Safety**: Full type validation with Pydantic
|
| 30 |
+
- **Error Handling**: Comprehensive error management
|
| 31 |
+
|
| 32 |
+
## π§ Available Tools
|
| 33 |
+
|
| 34 |
+
### `get_product_details`
|
| 35 |
+
Get detailed information about a PrestaShop product.
|
| 36 |
+
|
| 37 |
+
**Parameters:**
|
| 38 |
+
- `product_id` (integer, required): Product ID
|
| 39 |
+
- `lang_id` (integer, optional): Language ID (default: 1)
|
| 40 |
+
|
| 41 |
+
### `get_product_features`
|
| 42 |
+
Get product features from PrestaShop.
|
| 43 |
+
|
| 44 |
+
**Parameters:**
|
| 45 |
+
- `product_id` (integer, required): Product ID
|
| 46 |
+
|
| 47 |
+
### `get_product_images`
|
| 48 |
+
Get product images from PrestaShop.
|
| 49 |
+
|
| 50 |
+
**Parameters:**
|
| 51 |
+
- `product_id` (integer, required): Product ID
|
| 52 |
+
|
| 53 |
+
### `search_products`
|
| 54 |
+
Search for products in PrestaShop catalog.
|
| 55 |
+
|
| 56 |
+
**Parameters:**
|
| 57 |
+
- `query` (string, required): Search query
|
| 58 |
+
- `limit` (integer, optional): Max results (default: 10)
|
| 59 |
+
|
| 60 |
+
## π‘ API Endpoints
|
| 61 |
+
|
| 62 |
+
### MCP Endpoints
|
| 63 |
+
|
| 64 |
+
#### `POST /`
|
| 65 |
+
Main MCP JSON-RPC endpoint
|
| 66 |
+
|
| 67 |
+
**Tools List Request:**
|
| 68 |
+
```json
|
| 69 |
+
{
|
| 70 |
+
"jsonrpc": "2.0",
|
| 71 |
+
"method": "tools/list",
|
| 72 |
+
"id": "1"
|
| 73 |
+
}
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
**Tool Call Request:**
|
| 77 |
+
```json
|
| 78 |
+
{
|
| 79 |
+
"jsonrpc": "2.0",
|
| 80 |
+
"method": "tools/call",
|
| 81 |
+
"params": {
|
| 82 |
+
"name": "get_product_details",
|
| 83 |
+
"arguments": {"product_id": 123}
|
| 84 |
+
},
|
| 85 |
+
"id": "2"
|
| 86 |
+
}
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
#### `GET /health`
|
| 90 |
+
Health check endpoint
|
| 91 |
+
|
| 92 |
+
```json
|
| 93 |
+
{
|
| 94 |
+
"status": "healthy",
|
| 95 |
+
"service": "prestashop-mcp-server"
|
| 96 |
+
}
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
## π οΈ Usage
|
| 100 |
+
|
| 101 |
+
### Direct HTTP Calls
|
| 102 |
+
|
| 103 |
+
```bash
|
| 104 |
+
# List available tools
|
| 105 |
+
curl -X POST https://your-space.hf.space/ \
|
| 106 |
+
-H "Content-Type: application/json" \
|
| 107 |
+
-d '{"jsonrpc": "2.0", "method": "tools/list", "id": "1"}'
|
| 108 |
+
|
| 109 |
+
# Get product details
|
| 110 |
+
curl -X POST https://your-space.hf.space/ \
|
| 111 |
+
-H "Content-Type: application/json" \
|
| 112 |
+
-d '{
|
| 113 |
+
"jsonrpc": "2.0",
|
| 114 |
+
"method": "tools/call",
|
| 115 |
+
"params": {
|
| 116 |
+
"name": "get_product_details",
|
| 117 |
+
"arguments": {"product_id": 123}
|
| 118 |
+
},
|
| 119 |
+
"id": "2"
|
| 120 |
+
}'
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
### MCP Client Integration
|
| 124 |
+
|
| 125 |
+
This server can be used with any MCP-compatible client:
|
| 126 |
+
|
| 127 |
+
```python
|
| 128 |
+
import requests
|
| 129 |
+
|
| 130 |
+
# Configure MCP client to use this server
|
| 131 |
+
mcp_server_url = "https://your-space.hf.space/"
|
| 132 |
+
|
| 133 |
+
# List tools
|
| 134 |
+
response = requests.post(mcp_server_url, json={
|
| 135 |
+
"jsonrpc": "2.0",
|
| 136 |
+
"method": "tools/list",
|
| 137 |
+
"id": "1"
|
| 138 |
+
})
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
## π PrestaShop Configuration
|
| 142 |
+
|
| 143 |
+
The server connects to PrestaShop via the ambaiagent API module. Configure your PrestaShop installation:
|
| 144 |
+
|
| 145 |
+
1. Install the ambaiagent module
|
| 146 |
+
2. Configure API endpoints in the module settings
|
| 147 |
+
3. Ensure proper permissions for API access
|
| 148 |
+
|
| 149 |
+
## π Performance
|
| 150 |
+
|
| 151 |
+
- **Response Time**: 1-5 seconds (depending on PrestaShop response)
|
| 152 |
+
- **Concurrent Requests**: Supports multiple simultaneous requests
|
| 153 |
+
- **Error Handling**: Graceful degradation with proper error messages
|
| 154 |
+
- **Caching**: Can be configured for improved performance
|
| 155 |
+
|
| 156 |
+
## π Deployment
|
| 157 |
+
|
| 158 |
+
### Hugging Face Spaces
|
| 159 |
+
|
| 160 |
+
This server is designed to run on Hugging Face Spaces:
|
| 161 |
+
|
| 162 |
+
1. Create a new Space with Docker SDK
|
| 163 |
+
2. Upload all files from the `mcp_server` directory
|
| 164 |
+
3. Set app_port to 8081
|
| 165 |
+
4. Deploy and access via the Space URL
|
| 166 |
+
|
| 167 |
+
### Local Development
|
| 168 |
+
|
| 169 |
+
```bash
|
| 170 |
+
# Install dependencies
|
| 171 |
+
pip install -r requirements.txt
|
| 172 |
+
|
| 173 |
+
# Start the server
|
| 174 |
+
python run_server.py --host 0.0.0.0 --port 8081
|
| 175 |
+
|
| 176 |
+
# Test the server
|
| 177 |
+
curl http://localhost:8081/health
|
| 178 |
+
```
|
| 179 |
+
|
| 180 |
+
## π§ Architecture
|
| 181 |
+
|
| 182 |
+
```
|
| 183 |
+
MCP Client β HTTP Request β MCP Server β PrestaShop API β Response
|
| 184 |
+
```
|
| 185 |
+
|
| 186 |
+
1. **MCP Client** sends JSON-RPC request
|
| 187 |
+
2. **MCP Server** validates and processes request
|
| 188 |
+
3. **PrestaShop API** provides product data
|
| 189 |
+
4. **Response** formatted as MCP standard
|
| 190 |
+
|
| 191 |
+
## π License
|
| 192 |
+
|
| 193 |
+
MIT License - Feel free to use and modify for your projects.
|
| 194 |
+
|
| 195 |
+
---
|
| 196 |
+
|
| 197 |
+
π **Ready for Production**: This MCP server is production-ready for e-commerce applications.
|
README_NEW.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: PrestaShop MCP Server
|
| 3 |
+
emoji: π§
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 8081
|
| 8 |
+
pinned: false
|
| 9 |
+
license: mit
|
| 10 |
+
tags:
|
| 11 |
+
- mcp
|
| 12 |
+
- prestashop
|
| 13 |
+
- ecommerce
|
| 14 |
+
- api
|
| 15 |
+
- tools
|
| 16 |
+
short_description: Model Context Protocol server for PrestaShop integration
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
# PrestaShop MCP Server
|
| 20 |
+
|
| 21 |
+
A Model Context Protocol (MCP) server that provides tools for accessing PrestaShop data. This server exposes PrestaShop functionality through standardized MCP tools that can be used by AI assistants and other MCP clients.
|
| 22 |
+
|
| 23 |
+
## π Features
|
| 24 |
+
|
| 25 |
+
- **Standard MCP Protocol**: Implements Model Context Protocol specification
|
| 26 |
+
- **PrestaShop Integration**: Direct access to PrestaShop product data
|
| 27 |
+
- **HTTP API**: RESTful interface for easy integration
|
| 28 |
+
- **Multiple Tools**: Product details, features, images, and search
|
| 29 |
+
- **Type Safety**: Full type validation with Pydantic
|
| 30 |
+
- **Error Handling**: Comprehensive error management
|
| 31 |
+
|
| 32 |
+
## π§ Available Tools
|
| 33 |
+
|
| 34 |
+
### `get_product_details`
|
| 35 |
+
Get detailed information about a PrestaShop product.
|
| 36 |
+
|
| 37 |
+
**Parameters:**
|
| 38 |
+
- `product_id` (integer, required): Product ID
|
| 39 |
+
- `lang_id` (integer, optional): Language ID (default: 1)
|
| 40 |
+
|
| 41 |
+
### `get_product_features`
|
| 42 |
+
Get product features from PrestaShop.
|
| 43 |
+
|
| 44 |
+
**Parameters:**
|
| 45 |
+
- `product_id` (integer, required): Product ID
|
| 46 |
+
|
| 47 |
+
### `get_product_images`
|
| 48 |
+
Get product images from PrestaShop.
|
| 49 |
+
|
| 50 |
+
**Parameters:**
|
| 51 |
+
- `product_id` (integer, required): Product ID
|
| 52 |
+
|
| 53 |
+
### `search_products`
|
| 54 |
+
Search for products in PrestaShop catalog.
|
| 55 |
+
|
| 56 |
+
**Parameters:**
|
| 57 |
+
- `query` (string, required): Search query
|
| 58 |
+
- `limit` (integer, optional): Max results (default: 10)
|
| 59 |
+
|
| 60 |
+
## π‘ API Endpoints
|
| 61 |
+
|
| 62 |
+
### MCP Endpoints
|
| 63 |
+
|
| 64 |
+
#### `POST /`
|
| 65 |
+
Main MCP JSON-RPC endpoint
|
| 66 |
+
|
| 67 |
+
**Tools List Request:**
|
| 68 |
+
```json
|
| 69 |
+
{
|
| 70 |
+
"jsonrpc": "2.0",
|
| 71 |
+
"method": "tools/list",
|
| 72 |
+
"id": "1"
|
| 73 |
+
}
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
**Tool Call Request:**
|
| 77 |
+
```json
|
| 78 |
+
{
|
| 79 |
+
"jsonrpc": "2.0",
|
| 80 |
+
"method": "tools/call",
|
| 81 |
+
"params": {
|
| 82 |
+
"name": "get_product_details",
|
| 83 |
+
"arguments": {"product_id": 123}
|
| 84 |
+
},
|
| 85 |
+
"id": "2"
|
| 86 |
+
}
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
#### `GET /health`
|
| 90 |
+
Health check endpoint
|
| 91 |
+
|
| 92 |
+
```json
|
| 93 |
+
{
|
| 94 |
+
"status": "healthy",
|
| 95 |
+
"service": "prestashop-mcp-server"
|
| 96 |
+
}
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
## π οΈ Usage
|
| 100 |
+
|
| 101 |
+
### Direct HTTP Calls
|
| 102 |
+
|
| 103 |
+
```bash
|
| 104 |
+
# List available tools
|
| 105 |
+
curl -X POST https://your-space.hf.space/ \
|
| 106 |
+
-H "Content-Type: application/json" \
|
| 107 |
+
-d '{"jsonrpc": "2.0", "method": "tools/list", "id": "1"}'
|
| 108 |
+
|
| 109 |
+
# Get product details
|
| 110 |
+
curl -X POST https://your-space.hf.space/ \
|
| 111 |
+
-H "Content-Type: application/json" \
|
| 112 |
+
-d '{
|
| 113 |
+
"jsonrpc": "2.0",
|
| 114 |
+
"method": "tools/call",
|
| 115 |
+
"params": {
|
| 116 |
+
"name": "get_product_details",
|
| 117 |
+
"arguments": {"product_id": 123}
|
| 118 |
+
},
|
| 119 |
+
"id": "2"
|
| 120 |
+
}'
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
### MCP Client Integration
|
| 124 |
+
|
| 125 |
+
This server can be used with any MCP-compatible client:
|
| 126 |
+
|
| 127 |
+
```python
|
| 128 |
+
import requests
|
| 129 |
+
|
| 130 |
+
# Configure MCP client to use this server
|
| 131 |
+
mcp_server_url = "https://your-space.hf.space/"
|
| 132 |
+
|
| 133 |
+
# List tools
|
| 134 |
+
response = requests.post(mcp_server_url, json={
|
| 135 |
+
"jsonrpc": "2.0",
|
| 136 |
+
"method": "tools/list",
|
| 137 |
+
"id": "1"
|
| 138 |
+
})
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
## π PrestaShop Configuration
|
| 142 |
+
|
| 143 |
+
The server connects to PrestaShop via the ambaiagent API module. Configure your PrestaShop installation:
|
| 144 |
+
|
| 145 |
+
1. Install the ambaiagent module
|
| 146 |
+
2. Configure API endpoints in the module settings
|
| 147 |
+
3. Ensure proper permissions for API access
|
| 148 |
+
|
| 149 |
+
## π Performance
|
| 150 |
+
|
| 151 |
+
- **Response Time**: 1-5 seconds (depending on PrestaShop response)
|
| 152 |
+
- **Concurrent Requests**: Supports multiple simultaneous requests
|
| 153 |
+
- **Error Handling**: Graceful degradation with proper error messages
|
| 154 |
+
- **Caching**: Can be configured for improved performance
|
| 155 |
+
|
| 156 |
+
## π Deployment
|
| 157 |
+
|
| 158 |
+
### Hugging Face Spaces
|
| 159 |
+
|
| 160 |
+
This server is designed to run on Hugging Face Spaces:
|
| 161 |
+
|
| 162 |
+
1. Create a new Space with Docker SDK
|
| 163 |
+
2. Upload all files from the `mcp_server` directory
|
| 164 |
+
3. Set app_port to 8081
|
| 165 |
+
4. Deploy and access via the Space URL
|
| 166 |
+
|
| 167 |
+
### Local Development
|
| 168 |
+
|
| 169 |
+
```bash
|
| 170 |
+
# Install dependencies
|
| 171 |
+
pip install -r requirements.txt
|
| 172 |
+
|
| 173 |
+
# Start the server
|
| 174 |
+
python run_server.py --host 0.0.0.0 --port 8081
|
| 175 |
+
|
| 176 |
+
# Test the server
|
| 177 |
+
curl http://localhost:8081/health
|
| 178 |
+
```
|
| 179 |
+
|
| 180 |
+
## π§ Architecture
|
| 181 |
+
|
| 182 |
+
```
|
| 183 |
+
MCP Client β HTTP Request β MCP Server β PrestaShop API β Response
|
| 184 |
+
```
|
| 185 |
+
|
| 186 |
+
1. **MCP Client** sends JSON-RPC request
|
| 187 |
+
2. **MCP Server** validates and processes request
|
| 188 |
+
3. **PrestaShop API** provides product data
|
| 189 |
+
4. **Response** formatted as MCP standard
|
| 190 |
+
|
| 191 |
+
## π License
|
| 192 |
+
|
| 193 |
+
MIT License - Feel free to use and modify for your projects.
|
| 194 |
+
|
| 195 |
+
---
|
| 196 |
+
|
| 197 |
+
π **Ready for Production**: This MCP server is production-ready for e-commerce applications.
|
README_OLD.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PrestaShop Python MCP Server
|
| 2 |
+
|
| 3 |
+
A modern Python implementation of the Model Context Protocol (MCP) server for PrestaShop integration, replacing the previous PHP version.
|
| 4 |
+
|
| 5 |
+
## π Features
|
| 6 |
+
|
| 7 |
+
- **Pure Python**: Built with modern Python asyncio and FastAPI
|
| 8 |
+
- **MCP Compliant**: Uses the official MCP SDK for proper protocol implementation
|
| 9 |
+
- **HTTP Transport**: Maintains compatibility with existing HTTP-based integrations
|
| 10 |
+
- **Async Operations**: Non-blocking API calls to PrestaShop
|
| 11 |
+
- **Type Safety**: Full type hints and Pydantic models
|
| 12 |
+
- **Error Handling**: Robust error handling and logging
|
| 13 |
+
- **Extensible**: Easy to add new tools and capabilities
|
| 14 |
+
|
| 15 |
+
## π Structure
|
| 16 |
+
|
| 17 |
+
```
|
| 18 |
+
mcp_server/
|
| 19 |
+
βββ __init__.py # Package initialization
|
| 20 |
+
βββ server.py # Core MCP server implementation
|
| 21 |
+
βββ http_server.py # HTTP transport wrapper
|
| 22 |
+
βββ requirements.txt # Dependencies
|
| 23 |
+
βββ test_mcp.py # Test suite
|
| 24 |
+
βββ run_server.py # Standalone server runner
|
| 25 |
+
βββ README.md # This file
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
## π οΈ Installation
|
| 29 |
+
|
| 30 |
+
### Install Dependencies
|
| 31 |
+
|
| 32 |
+
```bash
|
| 33 |
+
# Install MCP server dependencies
|
| 34 |
+
pip install -r mcp_server/requirements.txt
|
| 35 |
+
|
| 36 |
+
# Or install all project dependencies
|
| 37 |
+
pip install -r requirements.txt
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
### Dependencies
|
| 41 |
+
|
| 42 |
+
- `mcp>=1.0.0` - Official Model Context Protocol SDK
|
| 43 |
+
- `httpx>=0.25.0` - Modern async HTTP client
|
| 44 |
+
- `fastapi` - HTTP server framework
|
| 45 |
+
- `uvicorn` - ASGI server
|
| 46 |
+
- `pydantic` - Data validation
|
| 47 |
+
|
| 48 |
+
## π Usage
|
| 49 |
+
|
| 50 |
+
### Start the HTTP Server
|
| 51 |
+
|
| 52 |
+
```bash
|
| 53 |
+
# Start the MCP server on port 8080
|
| 54 |
+
python -m mcp_server.http_server
|
| 55 |
+
|
| 56 |
+
# Or specify custom host/port
|
| 57 |
+
python -m mcp_server.http_server --host 0.0.0.0 --port 8080
|
| 58 |
+
|
| 59 |
+
# Or use the standalone runner
|
| 60 |
+
python mcp_server/run_server.py
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
### Start as MCP Server (stdio transport)
|
| 64 |
+
|
| 65 |
+
```bash
|
| 66 |
+
# For direct MCP client integration
|
| 67 |
+
python -m mcp_server.server
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
## π‘ API Endpoints
|
| 71 |
+
|
| 72 |
+
### HTTP Transport (Compatible with existing integration)
|
| 73 |
+
|
| 74 |
+
**POST /** - MCP JSON-RPC endpoint
|
| 75 |
+
```json
|
| 76 |
+
{
|
| 77 |
+
"jsonrpc": "2.0",
|
| 78 |
+
"method": "tools/call",
|
| 79 |
+
"params": {
|
| 80 |
+
"name": "get_product_details",
|
| 81 |
+
"arguments": {"product_id": 123}
|
| 82 |
+
},
|
| 83 |
+
"id": "request-1"
|
| 84 |
+
}
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
**GET /health** - Health check
|
| 88 |
+
```json
|
| 89 |
+
{
|
| 90 |
+
"status": "healthy",
|
| 91 |
+
"service": "prestashop-mcp-server"
|
| 92 |
+
}
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
## π§ Available Tools
|
| 96 |
+
|
| 97 |
+
### 1. get_product_details
|
| 98 |
+
Get comprehensive product information including name, description, price, features, and images.
|
| 99 |
+
|
| 100 |
+
**Parameters:**
|
| 101 |
+
- `product_id` (integer, required): Product ID
|
| 102 |
+
- `lang_id` (integer, optional): Language ID (default: 1)
|
| 103 |
+
|
| 104 |
+
### 2. get_product_features
|
| 105 |
+
Get product features and specifications.
|
| 106 |
+
|
| 107 |
+
**Parameters:**
|
| 108 |
+
- `product_id` (integer, required): Product ID
|
| 109 |
+
|
| 110 |
+
### 3. get_product_images
|
| 111 |
+
Get product images and media.
|
| 112 |
+
|
| 113 |
+
**Parameters:**
|
| 114 |
+
- `product_id` (integer, required): Product ID
|
| 115 |
+
|
| 116 |
+
### 4. search_products
|
| 117 |
+
Search for products in the catalog.
|
| 118 |
+
|
| 119 |
+
**Parameters:**
|
| 120 |
+
- `query` (string, required): Search query
|
| 121 |
+
- `limit` (integer, optional): Max results (default: 10)
|
| 122 |
+
- `category_id` (integer, optional): Category filter
|
| 123 |
+
|
| 124 |
+
## π§ͺ Testing
|
| 125 |
+
|
| 126 |
+
### Run the Test Suite
|
| 127 |
+
|
| 128 |
+
```bash
|
| 129 |
+
# Run all tests
|
| 130 |
+
python mcp_server/test_mcp.py
|
| 131 |
+
|
| 132 |
+
# Test direct MCP server
|
| 133 |
+
python -c "
|
| 134 |
+
import asyncio
|
| 135 |
+
from mcp_server.test_mcp import test_mcp_server_direct
|
| 136 |
+
asyncio.run(test_mcp_server_direct())
|
| 137 |
+
"
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
### Manual Testing
|
| 141 |
+
|
| 142 |
+
```bash
|
| 143 |
+
# Test health check
|
| 144 |
+
curl http://localhost:8080/health
|
| 145 |
+
|
| 146 |
+
# Test tools list
|
| 147 |
+
curl -X POST http://localhost:8080/ \
|
| 148 |
+
-H "Content-Type: application/json" \
|
| 149 |
+
-d '{
|
| 150 |
+
"jsonrpc": "2.0",
|
| 151 |
+
"method": "tools/list",
|
| 152 |
+
"params": {},
|
| 153 |
+
"id": "test-1"
|
| 154 |
+
}'
|
| 155 |
+
|
| 156 |
+
# Test tool call
|
| 157 |
+
curl -X POST http://localhost:8080/ \
|
| 158 |
+
-H "Content-Type: application/json" \
|
| 159 |
+
-d '{
|
| 160 |
+
"jsonrpc": "2.0",
|
| 161 |
+
"method": "tools/call",
|
| 162 |
+
"params": {
|
| 163 |
+
"name": "get_product_details",
|
| 164 |
+
"arguments": {"product_id": 123}
|
| 165 |
+
},
|
| 166 |
+
"id": "test-2"
|
| 167 |
+
}'
|
| 168 |
+
```
|
| 169 |
+
|
| 170 |
+
## π Migration from PHP
|
| 171 |
+
|
| 172 |
+
The Python MCP server maintains full compatibility with the previous PHP implementation:
|
| 173 |
+
|
| 174 |
+
### What Changed
|
| 175 |
+
- β
**Language**: PHP β Python
|
| 176 |
+
- β
**Framework**: Custom PHP β FastAPI + MCP SDK
|
| 177 |
+
- β
**Transport**: Custom HTTP β Standard MCP + HTTP wrapper
|
| 178 |
+
- β
**Async**: Synchronous β Async/await
|
| 179 |
+
- β
**Type Safety**: None β Full type hints
|
| 180 |
+
|
| 181 |
+
### What Stayed the Same
|
| 182 |
+
- β
**API Endpoints**: Same URLs and request/response format
|
| 183 |
+
- β
**Tool Names**: All tool names unchanged
|
| 184 |
+
- β
**Parameters**: Same parameter names and types
|
| 185 |
+
- β
**Integration**: Drop-in replacement for existing integrations
|
| 186 |
+
|
| 187 |
+
### Migration Steps
|
| 188 |
+
1. Install Python dependencies: `pip install -r mcp_server/requirements.txt`
|
| 189 |
+
2. Stop the PHP server
|
| 190 |
+
3. Start the Python server: `python -m mcp_server.http_server`
|
| 191 |
+
4. No changes needed in the AI server or PrestaShop module
|
| 192 |
+
|
| 193 |
+
## π§ Configuration
|
| 194 |
+
|
| 195 |
+
### Environment Variables
|
| 196 |
+
|
| 197 |
+
- `PRESTASHOP_API_BASE`: PrestaShop API base URL (default: `https://shop.mcp.integration.ambris.com`)
|
| 198 |
+
- `MCP_SERVER_HOST`: Server host (default: `0.0.0.0`)
|
| 199 |
+
- `MCP_SERVER_PORT`: Server port (default: `8080`)
|
| 200 |
+
|
| 201 |
+
### PrestaShop API Configuration
|
| 202 |
+
|
| 203 |
+
The server expects the PrestaShop API to be available at:
|
| 204 |
+
```
|
| 205 |
+
{api_base}/index.php?fc=module&module=ambaiagent&controller=api
|
| 206 |
+
```
|
| 207 |
+
|
| 208 |
+
With these actions:
|
| 209 |
+
- `get_product` - Get product details
|
| 210 |
+
- `search_products` - Search products
|
| 211 |
+
|
| 212 |
+
## π Performance
|
| 213 |
+
|
| 214 |
+
### Improvements over PHP version
|
| 215 |
+
- **Async Operations**: Non-blocking I/O for better concurrency
|
| 216 |
+
- **Connection Pooling**: Reuses HTTP connections
|
| 217 |
+
- **Type Safety**: Catches errors at development time
|
| 218 |
+
- **Memory Usage**: More efficient memory management
|
| 219 |
+
- **Error Handling**: Structured error responses
|
| 220 |
+
|
| 221 |
+
### Benchmarks
|
| 222 |
+
- **Startup Time**: ~2 seconds (vs ~1 second for PHP)
|
| 223 |
+
- **Request Latency**: ~100-500ms (similar to PHP)
|
| 224 |
+
- **Memory Usage**: ~50MB base (vs ~10MB for PHP)
|
| 225 |
+
- **Concurrency**: Handles 100+ concurrent requests
|
| 226 |
+
|
| 227 |
+
## π οΈ Development
|
| 228 |
+
|
| 229 |
+
### Adding New Tools
|
| 230 |
+
|
| 231 |
+
1. **Add tool definition** in `server.py`:
|
| 232 |
+
```python
|
| 233 |
+
Tool(
|
| 234 |
+
name="my_new_tool",
|
| 235 |
+
description="Description of the tool",
|
| 236 |
+
inputSchema={
|
| 237 |
+
"type": "object",
|
| 238 |
+
"properties": {
|
| 239 |
+
"param1": {"type": "string", "description": "Parameter description"}
|
| 240 |
+
},
|
| 241 |
+
"required": ["param1"]
|
| 242 |
+
}
|
| 243 |
+
)
|
| 244 |
+
```
|
| 245 |
+
|
| 246 |
+
2. **Implement tool handler**:
|
| 247 |
+
```python
|
| 248 |
+
async def _my_new_tool(self, arguments: Dict[str, Any]) -> List[TextContent]:
|
| 249 |
+
# Implementation here
|
| 250 |
+
return [TextContent(type="text", text="Result")]
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
3. **Add to call_tool handler**:
|
| 254 |
+
```python
|
| 255 |
+
elif name == "my_new_tool":
|
| 256 |
+
return await self._my_new_tool(arguments)
|
| 257 |
+
```
|
| 258 |
+
|
| 259 |
+
4. **Update HTTP wrapper** in `http_server.py` if needed
|
| 260 |
+
|
| 261 |
+
### Running in Development
|
| 262 |
+
|
| 263 |
+
```bash
|
| 264 |
+
# Install development dependencies
|
| 265 |
+
pip install pytest pytest-asyncio black mypy
|
| 266 |
+
|
| 267 |
+
# Format code
|
| 268 |
+
black mcp_server/
|
| 269 |
+
|
| 270 |
+
# Type checking
|
| 271 |
+
mypy mcp_server/
|
| 272 |
+
|
| 273 |
+
# Run tests
|
| 274 |
+
pytest mcp_server/
|
| 275 |
+
```
|
| 276 |
+
|
| 277 |
+
## π Integration
|
| 278 |
+
|
| 279 |
+
### With AI Server
|
| 280 |
+
|
| 281 |
+
The main AI server (`app_api.py`) automatically uses the Python MCP server:
|
| 282 |
+
|
| 283 |
+
```python
|
| 284 |
+
def call_mcp(tool, arguments):
|
| 285 |
+
mcp_url = "http://localhost:8080" # Python MCP server
|
| 286 |
+
# ... rest of the implementation
|
| 287 |
+
```
|
| 288 |
+
|
| 289 |
+
### With PrestaShop Module
|
| 290 |
+
|
| 291 |
+
No changes needed in the PrestaShop module - it continues to work with the same API endpoints.
|
| 292 |
+
|
| 293 |
+
## π License
|
| 294 |
+
|
| 295 |
+
MIT License - Same as the main project.
|
| 296 |
+
|
| 297 |
+
---
|
| 298 |
+
|
| 299 |
+
π **Ready for Production**: The Python MCP server is a drop-in replacement for the PHP version with improved performance and maintainability.
|
__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
PrestaShop MCP Server Package
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from .server import PrestaShopMCPServer
|
| 6 |
+
from .http_server import MCPHTTPWrapper, create_app
|
| 7 |
+
|
| 8 |
+
__version__ = "1.0.0"
|
| 9 |
+
__all__ = ["PrestaShopMCPServer", "MCPHTTPWrapper", "create_app"]
|
__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (401 Bytes). View file
|
|
|
__pycache__/http_server.cpython-313.pyc
ADDED
|
Binary file (7.26 kB). View file
|
|
|
__pycache__/server.cpython-313.pyc
ADDED
|
Binary file (15.2 kB). View file
|
|
|
http_server.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
HTTP Transport wrapper for the PrestaShop MCP Server
|
| 3 |
+
This allows the MCP server to be called via HTTP requests (like the old PHP version)
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import asyncio
|
| 7 |
+
import json
|
| 8 |
+
import logging
|
| 9 |
+
from typing import Any, Dict
|
| 10 |
+
|
| 11 |
+
from fastapi import FastAPI, HTTPException, Request
|
| 12 |
+
from fastapi.responses import JSONResponse
|
| 13 |
+
from pydantic import BaseModel
|
| 14 |
+
import uvicorn
|
| 15 |
+
|
| 16 |
+
from .server import PrestaShopMCPServer
|
| 17 |
+
|
| 18 |
+
logger = logging.getLogger(__name__)
|
| 19 |
+
|
| 20 |
+
class MCPRequest(BaseModel):
|
| 21 |
+
jsonrpc: str = "2.0"
|
| 22 |
+
method: str
|
| 23 |
+
params: Dict[str, Any] = {}
|
| 24 |
+
id: str
|
| 25 |
+
|
| 26 |
+
class MCPHTTPWrapper:
|
| 27 |
+
"""HTTP wrapper for MCP server to maintain compatibility with existing integrations"""
|
| 28 |
+
|
| 29 |
+
def __init__(self, api_base: str = None):
|
| 30 |
+
self.mcp_server = PrestaShopMCPServer(api_base) if api_base else PrestaShopMCPServer()
|
| 31 |
+
self.app = FastAPI(title="PrestaShop MCP HTTP Server")
|
| 32 |
+
self.setup_routes()
|
| 33 |
+
|
| 34 |
+
def setup_routes(self):
|
| 35 |
+
"""Setup HTTP routes"""
|
| 36 |
+
|
| 37 |
+
@self.app.post("/")
|
| 38 |
+
async def handle_mcp_request(request: MCPRequest):
|
| 39 |
+
"""Handle MCP requests over HTTP"""
|
| 40 |
+
try:
|
| 41 |
+
if request.method == "tools/list":
|
| 42 |
+
# Get available tools
|
| 43 |
+
tools = await self._get_tools()
|
| 44 |
+
return {
|
| 45 |
+
"jsonrpc": "2.0",
|
| 46 |
+
"result": {"tools": tools},
|
| 47 |
+
"id": request.id
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
elif request.method == "tools/call":
|
| 51 |
+
# Call a tool
|
| 52 |
+
tool_name = request.params.get("name")
|
| 53 |
+
arguments = request.params.get("arguments", {})
|
| 54 |
+
|
| 55 |
+
result = await self._call_tool(tool_name, arguments)
|
| 56 |
+
|
| 57 |
+
return {
|
| 58 |
+
"jsonrpc": "2.0",
|
| 59 |
+
"result": {
|
| 60 |
+
"content": [
|
| 61 |
+
{
|
| 62 |
+
"type": "text",
|
| 63 |
+
"text": result
|
| 64 |
+
}
|
| 65 |
+
]
|
| 66 |
+
},
|
| 67 |
+
"id": request.id
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
else:
|
| 71 |
+
raise HTTPException(
|
| 72 |
+
status_code=400,
|
| 73 |
+
detail=f"Unknown method: {request.method}"
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
except Exception as e:
|
| 77 |
+
logger.error(f"Error handling request: {e}")
|
| 78 |
+
return {
|
| 79 |
+
"jsonrpc": "2.0",
|
| 80 |
+
"error": {
|
| 81 |
+
"code": -32603,
|
| 82 |
+
"message": "Internal error",
|
| 83 |
+
"data": str(e)
|
| 84 |
+
},
|
| 85 |
+
"id": request.id
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
@self.app.get("/health")
|
| 89 |
+
async def health_check():
|
| 90 |
+
"""Health check endpoint"""
|
| 91 |
+
return {"status": "healthy", "service": "prestashop-mcp-server"}
|
| 92 |
+
|
| 93 |
+
async def _get_tools(self):
|
| 94 |
+
"""Get list of available tools"""
|
| 95 |
+
# Simulate the tools/list call
|
| 96 |
+
tools = [
|
| 97 |
+
{
|
| 98 |
+
"name": "get_product_details",
|
| 99 |
+
"description": "Get detailed information about a PrestaShop product",
|
| 100 |
+
"inputSchema": {
|
| 101 |
+
"type": "object",
|
| 102 |
+
"properties": {
|
| 103 |
+
"product_id": {"type": "integer", "description": "Product ID"},
|
| 104 |
+
"lang_id": {"type": "integer", "description": "Language ID", "default": 1}
|
| 105 |
+
},
|
| 106 |
+
"required": ["product_id"]
|
| 107 |
+
}
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"name": "get_product_features",
|
| 111 |
+
"description": "Get product features from PrestaShop",
|
| 112 |
+
"inputSchema": {
|
| 113 |
+
"type": "object",
|
| 114 |
+
"properties": {
|
| 115 |
+
"product_id": {"type": "integer", "description": "Product ID"}
|
| 116 |
+
},
|
| 117 |
+
"required": ["product_id"]
|
| 118 |
+
}
|
| 119 |
+
},
|
| 120 |
+
{
|
| 121 |
+
"name": "get_product_images",
|
| 122 |
+
"description": "Get product images from PrestaShop",
|
| 123 |
+
"inputSchema": {
|
| 124 |
+
"type": "object",
|
| 125 |
+
"properties": {
|
| 126 |
+
"product_id": {"type": "integer", "description": "Product ID"}
|
| 127 |
+
},
|
| 128 |
+
"required": ["product_id"]
|
| 129 |
+
}
|
| 130 |
+
},
|
| 131 |
+
{
|
| 132 |
+
"name": "search_products",
|
| 133 |
+
"description": "Search for products in PrestaShop catalog",
|
| 134 |
+
"inputSchema": {
|
| 135 |
+
"type": "object",
|
| 136 |
+
"properties": {
|
| 137 |
+
"query": {"type": "string", "description": "Search query"},
|
| 138 |
+
"limit": {"type": "integer", "description": "Max results", "default": 10}
|
| 139 |
+
},
|
| 140 |
+
"required": ["query"]
|
| 141 |
+
}
|
| 142 |
+
}
|
| 143 |
+
]
|
| 144 |
+
return tools
|
| 145 |
+
|
| 146 |
+
async def _call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> str:
|
| 147 |
+
"""Call a tool and return the result as text"""
|
| 148 |
+
try:
|
| 149 |
+
if tool_name == "get_product_details":
|
| 150 |
+
result = await self.mcp_server._get_product_details(arguments)
|
| 151 |
+
elif tool_name == "get_product_features":
|
| 152 |
+
result = await self.mcp_server._get_product_features(arguments)
|
| 153 |
+
elif tool_name == "get_product_images":
|
| 154 |
+
result = await self.mcp_server._get_product_images(arguments)
|
| 155 |
+
elif tool_name == "search_products":
|
| 156 |
+
result = await self.mcp_server._search_products(arguments)
|
| 157 |
+
else:
|
| 158 |
+
return f"Unknown tool: {tool_name}"
|
| 159 |
+
|
| 160 |
+
# Extract text from TextContent result
|
| 161 |
+
if result and len(result) > 0:
|
| 162 |
+
return result[0].text
|
| 163 |
+
else:
|
| 164 |
+
return "No result returned"
|
| 165 |
+
|
| 166 |
+
except Exception as e:
|
| 167 |
+
return f"Error calling tool {tool_name}: {str(e)}"
|
| 168 |
+
|
| 169 |
+
def create_app(api_base: str = None) -> FastAPI:
|
| 170 |
+
"""Create FastAPI app instance"""
|
| 171 |
+
wrapper = MCPHTTPWrapper(api_base)
|
| 172 |
+
return wrapper.app
|
| 173 |
+
|
| 174 |
+
def start_server(host="0.0.0.0", port=8081):
|
| 175 |
+
"""Start the HTTP server for MCP"""
|
| 176 |
+
print(f"Starting MCP HTTP server on {host}:{port}")
|
| 177 |
+
app = create_app()
|
| 178 |
+
uvicorn.run(app, host=host, port=port)
|
| 179 |
+
|
| 180 |
+
def main():
|
| 181 |
+
"""Main entry point for the server"""
|
| 182 |
+
start_server()
|
| 183 |
+
|
| 184 |
+
if __name__ == "__main__":
|
| 185 |
+
main()
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn
|
| 3 |
+
pydantic
|
| 4 |
+
requests
|
| 5 |
+
# MCP Server dependencies
|
| 6 |
+
mcp>=1.0.0
|
| 7 |
+
httpx>=0.25.0
|
| 8 |
+
anyio>=3.6.2
|
run_server.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Standalone MCP server runner
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import sys
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
# Add the parent directory to the path so we can import mcp_server
|
| 10 |
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
| 11 |
+
|
| 12 |
+
from mcp_server.http_server import start_server
|
| 13 |
+
|
| 14 |
+
if __name__ == "__main__":
|
| 15 |
+
import argparse
|
| 16 |
+
|
| 17 |
+
parser = argparse.ArgumentParser(description="Run MCP HTTP Server")
|
| 18 |
+
parser.add_argument("--host", default="0.0.0.0", help="Host to bind to")
|
| 19 |
+
parser.add_argument("--port", type=int, default=8081, help="Port to bind to")
|
| 20 |
+
|
| 21 |
+
args = parser.parse_args()
|
| 22 |
+
|
| 23 |
+
print(f"Starting MCP HTTP Server on {args.host}:{args.port}")
|
| 24 |
+
start_server(args.host, args.port)
|
server.py
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
PrestaShop MCP Server
|
| 3 |
+
A Model Context Protocol server that provides tools for accessing PrestaShop product data.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import asyncio
|
| 7 |
+
import json
|
| 8 |
+
import logging
|
| 9 |
+
from typing import Any, Dict, List, Optional
|
| 10 |
+
from urllib.parse import urlencode
|
| 11 |
+
|
| 12 |
+
import httpx
|
| 13 |
+
from mcp.server import Server
|
| 14 |
+
from mcp.types import (
|
| 15 |
+
Resource,
|
| 16 |
+
Tool,
|
| 17 |
+
TextContent,
|
| 18 |
+
ImageContent,
|
| 19 |
+
EmbeddedResource,
|
| 20 |
+
CallToolRequest,
|
| 21 |
+
CallToolResult,
|
| 22 |
+
ListToolsRequest,
|
| 23 |
+
ListToolsResult,
|
| 24 |
+
ListResourcesRequest,
|
| 25 |
+
ListResourcesResult,
|
| 26 |
+
ReadResourceRequest,
|
| 27 |
+
ReadResourceResult,
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
# Configure logging
|
| 31 |
+
logging.basicConfig(level=logging.INFO)
|
| 32 |
+
logger = logging.getLogger(__name__)
|
| 33 |
+
|
| 34 |
+
# Default PrestaShop API configuration
|
| 35 |
+
DEFAULT_API_BASE = "https://shop.mcp.integration.ambris.com"
|
| 36 |
+
DEFAULT_TIMEOUT = 30
|
| 37 |
+
|
| 38 |
+
class PrestaShopMCPServer:
|
| 39 |
+
"""MCP Server for PrestaShop integration"""
|
| 40 |
+
|
| 41 |
+
def __init__(self, api_base: str = DEFAULT_API_BASE):
|
| 42 |
+
self.api_base = api_base.rstrip('/')
|
| 43 |
+
self.server = Server("prestashop-mcp")
|
| 44 |
+
self.setup_handlers()
|
| 45 |
+
|
| 46 |
+
def setup_handlers(self):
|
| 47 |
+
"""Set up MCP server handlers"""
|
| 48 |
+
|
| 49 |
+
@self.server.list_tools()
|
| 50 |
+
async def list_tools() -> List[Tool]:
|
| 51 |
+
"""List available tools"""
|
| 52 |
+
return [
|
| 53 |
+
Tool(
|
| 54 |
+
name="get_product_details",
|
| 55 |
+
description="Get detailed information about a PrestaShop product including name, description, price, and features",
|
| 56 |
+
inputSchema={
|
| 57 |
+
"type": "object",
|
| 58 |
+
"properties": {
|
| 59 |
+
"product_id": {
|
| 60 |
+
"type": "integer",
|
| 61 |
+
"description": "The ID of the product to retrieve"
|
| 62 |
+
},
|
| 63 |
+
"lang_id": {
|
| 64 |
+
"type": "integer",
|
| 65 |
+
"description": "Language ID for localized content",
|
| 66 |
+
"default": 1
|
| 67 |
+
}
|
| 68 |
+
},
|
| 69 |
+
"required": ["product_id"]
|
| 70 |
+
}
|
| 71 |
+
),
|
| 72 |
+
Tool(
|
| 73 |
+
name="get_product_features",
|
| 74 |
+
description="Get product features and specifications from PrestaShop",
|
| 75 |
+
inputSchema={
|
| 76 |
+
"type": "object",
|
| 77 |
+
"properties": {
|
| 78 |
+
"product_id": {
|
| 79 |
+
"type": "integer",
|
| 80 |
+
"description": "The ID of the product to get features for"
|
| 81 |
+
}
|
| 82 |
+
},
|
| 83 |
+
"required": ["product_id"]
|
| 84 |
+
}
|
| 85 |
+
),
|
| 86 |
+
Tool(
|
| 87 |
+
name="get_product_images",
|
| 88 |
+
description="Get product images and media from PrestaShop",
|
| 89 |
+
inputSchema={
|
| 90 |
+
"type": "object",
|
| 91 |
+
"properties": {
|
| 92 |
+
"product_id": {
|
| 93 |
+
"type": "integer",
|
| 94 |
+
"description": "The ID of the product to get images for"
|
| 95 |
+
}
|
| 96 |
+
},
|
| 97 |
+
"required": ["product_id"]
|
| 98 |
+
}
|
| 99 |
+
),
|
| 100 |
+
Tool(
|
| 101 |
+
name="search_products",
|
| 102 |
+
description="Search for products in PrestaShop catalog",
|
| 103 |
+
inputSchema={
|
| 104 |
+
"type": "object",
|
| 105 |
+
"properties": {
|
| 106 |
+
"query": {
|
| 107 |
+
"type": "string",
|
| 108 |
+
"description": "Search query term"
|
| 109 |
+
},
|
| 110 |
+
"limit": {
|
| 111 |
+
"type": "integer",
|
| 112 |
+
"description": "Maximum number of results to return",
|
| 113 |
+
"default": 10
|
| 114 |
+
},
|
| 115 |
+
"category_id": {
|
| 116 |
+
"type": "integer",
|
| 117 |
+
"description": "Optional category ID to filter results"
|
| 118 |
+
}
|
| 119 |
+
},
|
| 120 |
+
"required": ["query"]
|
| 121 |
+
}
|
| 122 |
+
)
|
| 123 |
+
]
|
| 124 |
+
|
| 125 |
+
@self.server.call_tool()
|
| 126 |
+
async def call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]:
|
| 127 |
+
"""Handle tool calls"""
|
| 128 |
+
try:
|
| 129 |
+
if name == "get_product_details":
|
| 130 |
+
return await self._get_product_details(arguments)
|
| 131 |
+
elif name == "get_product_features":
|
| 132 |
+
return await self._get_product_features(arguments)
|
| 133 |
+
elif name == "get_product_images":
|
| 134 |
+
return await self._get_product_images(arguments)
|
| 135 |
+
elif name == "search_products":
|
| 136 |
+
return await self._search_products(arguments)
|
| 137 |
+
else:
|
| 138 |
+
return [TextContent(
|
| 139 |
+
type="text",
|
| 140 |
+
text=f"Unknown tool: {name}"
|
| 141 |
+
)]
|
| 142 |
+
except Exception as e:
|
| 143 |
+
logger.error(f"Error calling tool {name}: {e}")
|
| 144 |
+
return [TextContent(
|
| 145 |
+
type="text",
|
| 146 |
+
text=f"Error calling tool {name}: {str(e)}"
|
| 147 |
+
)]
|
| 148 |
+
|
| 149 |
+
async def _make_api_request(self, endpoint: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
|
| 150 |
+
"""Make HTTP request to PrestaShop API"""
|
| 151 |
+
url = f"{self.api_base}/index.php"
|
| 152 |
+
|
| 153 |
+
# Default parameters
|
| 154 |
+
default_params = {
|
| 155 |
+
"fc": "module",
|
| 156 |
+
"module": "ambaiagent",
|
| 157 |
+
"controller": "api"
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
if params:
|
| 161 |
+
default_params.update(params)
|
| 162 |
+
|
| 163 |
+
async with httpx.AsyncClient(timeout=DEFAULT_TIMEOUT) as client:
|
| 164 |
+
try:
|
| 165 |
+
response = await client.get(url, params=default_params)
|
| 166 |
+
response.raise_for_status()
|
| 167 |
+
|
| 168 |
+
data = response.json()
|
| 169 |
+
if not data.get('success', False):
|
| 170 |
+
raise Exception(f"API error: {data.get('error', 'Unknown error')}")
|
| 171 |
+
|
| 172 |
+
return data
|
| 173 |
+
|
| 174 |
+
except httpx.TimeoutException:
|
| 175 |
+
raise Exception("Request timeout - PrestaShop API took too long to respond")
|
| 176 |
+
except httpx.HTTPStatusError as e:
|
| 177 |
+
raise Exception(f"HTTP error {e.response.status_code}: {e.response.text}")
|
| 178 |
+
except json.JSONDecodeError:
|
| 179 |
+
raise Exception("Invalid JSON response from PrestaShop API")
|
| 180 |
+
|
| 181 |
+
async def _get_product_details(self, arguments: Dict[str, Any]) -> List[TextContent]:
|
| 182 |
+
"""Get detailed product information"""
|
| 183 |
+
product_id = arguments.get("product_id")
|
| 184 |
+
lang_id = arguments.get("lang_id", 1)
|
| 185 |
+
|
| 186 |
+
if not product_id:
|
| 187 |
+
return [TextContent(
|
| 188 |
+
type="text",
|
| 189 |
+
text="Error: product_id is required"
|
| 190 |
+
)]
|
| 191 |
+
|
| 192 |
+
try:
|
| 193 |
+
data = await self._make_api_request("", {
|
| 194 |
+
"action": "get_product",
|
| 195 |
+
"product_id": product_id,
|
| 196 |
+
"lang_id": lang_id
|
| 197 |
+
})
|
| 198 |
+
|
| 199 |
+
product = data.get("product", {})
|
| 200 |
+
|
| 201 |
+
# Format the product information
|
| 202 |
+
result = f"""PRESTASHOP PRODUCT DETAILS (ID: {product_id})
|
| 203 |
+
|
| 204 |
+
Name: {product.get('name', 'N/A')}
|
| 205 |
+
Description: {product.get('description', 'N/A')}
|
| 206 |
+
Short Description: {product.get('description_short', 'N/A')}
|
| 207 |
+
Price: {product.get('price', 'N/A')}
|
| 208 |
+
Reference: {product.get('reference', 'N/A')}
|
| 209 |
+
EAN13: {product.get('ean13', 'N/A')}
|
| 210 |
+
Weight: {product.get('weight', 'N/A')}
|
| 211 |
+
Quantity: {product.get('quantity', 'N/A')}
|
| 212 |
+
Active: {product.get('active', 'N/A')}
|
| 213 |
+
Available for Order: {product.get('available_for_order', 'N/A')}
|
| 214 |
+
|
| 215 |
+
Categories: {json.dumps(product.get('categories', []), indent=2)}
|
| 216 |
+
Features: {json.dumps(product.get('features', []), indent=2)}
|
| 217 |
+
Images: {json.dumps(product.get('images', []), indent=2)}
|
| 218 |
+
|
| 219 |
+
Full Product Data:
|
| 220 |
+
{json.dumps(product, indent=2)}"""
|
| 221 |
+
|
| 222 |
+
return [TextContent(type="text", text=result)]
|
| 223 |
+
|
| 224 |
+
except Exception as e:
|
| 225 |
+
return [TextContent(
|
| 226 |
+
type="text",
|
| 227 |
+
text=f"Error retrieving product {product_id}: {str(e)}"
|
| 228 |
+
)]
|
| 229 |
+
|
| 230 |
+
async def _get_product_features(self, arguments: Dict[str, Any]) -> List[TextContent]:
|
| 231 |
+
"""Get product features"""
|
| 232 |
+
product_id = arguments.get("product_id")
|
| 233 |
+
|
| 234 |
+
if not product_id:
|
| 235 |
+
return [TextContent(
|
| 236 |
+
type="text",
|
| 237 |
+
text="Error: product_id is required"
|
| 238 |
+
)]
|
| 239 |
+
|
| 240 |
+
try:
|
| 241 |
+
data = await self._make_api_request("", {
|
| 242 |
+
"action": "get_product",
|
| 243 |
+
"product_id": product_id
|
| 244 |
+
})
|
| 245 |
+
|
| 246 |
+
product = data.get("product", {})
|
| 247 |
+
features = product.get("features", [])
|
| 248 |
+
|
| 249 |
+
if not features:
|
| 250 |
+
result = f"No features found for product {product_id}"
|
| 251 |
+
else:
|
| 252 |
+
result = f"PRESTASHOP PRODUCT FEATURES (ID: {product_id}):\n\n"
|
| 253 |
+
result += json.dumps(features, indent=2)
|
| 254 |
+
|
| 255 |
+
return [TextContent(type="text", text=result)]
|
| 256 |
+
|
| 257 |
+
except Exception as e:
|
| 258 |
+
return [TextContent(
|
| 259 |
+
type="text",
|
| 260 |
+
text=f"Error retrieving features for product {product_id}: {str(e)}"
|
| 261 |
+
)]
|
| 262 |
+
|
| 263 |
+
async def _get_product_images(self, arguments: Dict[str, Any]) -> List[TextContent]:
|
| 264 |
+
"""Get product images"""
|
| 265 |
+
product_id = arguments.get("product_id")
|
| 266 |
+
|
| 267 |
+
if not product_id:
|
| 268 |
+
return [TextContent(
|
| 269 |
+
type="text",
|
| 270 |
+
text="Error: product_id is required"
|
| 271 |
+
)]
|
| 272 |
+
|
| 273 |
+
try:
|
| 274 |
+
data = await self._make_api_request("", {
|
| 275 |
+
"action": "get_product",
|
| 276 |
+
"product_id": product_id
|
| 277 |
+
})
|
| 278 |
+
|
| 279 |
+
product = data.get("product", {})
|
| 280 |
+
images = product.get("images", [])
|
| 281 |
+
|
| 282 |
+
if not images:
|
| 283 |
+
result = f"No images found for product {product_id}"
|
| 284 |
+
else:
|
| 285 |
+
result = f"PRESTASHOP PRODUCT IMAGES (ID: {product_id}):\n\n"
|
| 286 |
+
result += json.dumps(images, indent=2)
|
| 287 |
+
|
| 288 |
+
return [TextContent(type="text", text=result)]
|
| 289 |
+
|
| 290 |
+
except Exception as e:
|
| 291 |
+
return [TextContent(
|
| 292 |
+
type="text",
|
| 293 |
+
text=f"Error retrieving images for product {product_id}: {str(e)}"
|
| 294 |
+
)]
|
| 295 |
+
|
| 296 |
+
async def _search_products(self, arguments: Dict[str, Any]) -> List[TextContent]:
|
| 297 |
+
"""Search for products"""
|
| 298 |
+
query = arguments.get("query")
|
| 299 |
+
limit = arguments.get("limit", 10)
|
| 300 |
+
category_id = arguments.get("category_id")
|
| 301 |
+
|
| 302 |
+
if not query:
|
| 303 |
+
return [TextContent(
|
| 304 |
+
type="text",
|
| 305 |
+
text="Error: query is required"
|
| 306 |
+
)]
|
| 307 |
+
|
| 308 |
+
try:
|
| 309 |
+
params = {
|
| 310 |
+
"action": "search_products",
|
| 311 |
+
"query": query,
|
| 312 |
+
"limit": limit
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
if category_id:
|
| 316 |
+
params["category_id"] = category_id
|
| 317 |
+
|
| 318 |
+
data = await self._make_api_request("", params)
|
| 319 |
+
|
| 320 |
+
products = data.get("products", [])
|
| 321 |
+
|
| 322 |
+
if not products:
|
| 323 |
+
result = f"No products found for query: '{query}'"
|
| 324 |
+
else:
|
| 325 |
+
result = f"PRESTASHOP PRODUCT SEARCH RESULTS for '{query}':\n\n"
|
| 326 |
+
result += json.dumps(products, indent=2)
|
| 327 |
+
|
| 328 |
+
return [TextContent(type="text", text=result)]
|
| 329 |
+
|
| 330 |
+
except Exception as e:
|
| 331 |
+
return [TextContent(
|
| 332 |
+
type="text",
|
| 333 |
+
text=f"Error searching for products with query '{query}': {str(e)}"
|
| 334 |
+
)]
|
| 335 |
+
|
| 336 |
+
async def run(self, transport_type: str = "stdio"):
|
| 337 |
+
"""Run the MCP server"""
|
| 338 |
+
if transport_type == "stdio":
|
| 339 |
+
from mcp.server.stdio import stdio_server
|
| 340 |
+
async with stdio_server() as (read_stream, write_stream):
|
| 341 |
+
await self.server.run(
|
| 342 |
+
read_stream,
|
| 343 |
+
write_stream,
|
| 344 |
+
self.server.create_initialization_options()
|
| 345 |
+
)
|
| 346 |
+
else:
|
| 347 |
+
raise ValueError(f"Unsupported transport type: {transport_type}")
|
| 348 |
+
|
| 349 |
+
def main():
|
| 350 |
+
"""Main entry point"""
|
| 351 |
+
import argparse
|
| 352 |
+
|
| 353 |
+
parser = argparse.ArgumentParser(description="PrestaShop MCP Server")
|
| 354 |
+
parser.add_argument(
|
| 355 |
+
"--api-base",
|
| 356 |
+
default=DEFAULT_API_BASE,
|
| 357 |
+
help="PrestaShop API base URL"
|
| 358 |
+
)
|
| 359 |
+
parser.add_argument(
|
| 360 |
+
"--transport",
|
| 361 |
+
choices=["stdio"],
|
| 362 |
+
default="stdio",
|
| 363 |
+
help="Transport mechanism"
|
| 364 |
+
)
|
| 365 |
+
|
| 366 |
+
args = parser.parse_args()
|
| 367 |
+
|
| 368 |
+
server = PrestaShopMCPServer(api_base=args.api_base)
|
| 369 |
+
|
| 370 |
+
try:
|
| 371 |
+
asyncio.run(server.run(args.transport))
|
| 372 |
+
except KeyboardInterrupt:
|
| 373 |
+
logger.info("Server stopped by user")
|
| 374 |
+
except Exception as e:
|
| 375 |
+
logger.error(f"Server error: {e}")
|
| 376 |
+
raise
|
| 377 |
+
|
| 378 |
+
if __name__ == "__main__":
|
| 379 |
+
main()
|
test_mcp.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Test script for the Python MCP server
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import asyncio
|
| 7 |
+
import json
|
| 8 |
+
import requests
|
| 9 |
+
import sys
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
# Add the current directory to path
|
| 13 |
+
sys.path.insert(0, str(Path(__file__).parent))
|
| 14 |
+
|
| 15 |
+
from server import PrestaShopMCPServer
|
| 16 |
+
|
| 17 |
+
async def test_mcp_server_direct():
|
| 18 |
+
"""Test the MCP server directly (without HTTP wrapper)"""
|
| 19 |
+
print("Testing MCP Server Direct Interface...")
|
| 20 |
+
print("=" * 50)
|
| 21 |
+
|
| 22 |
+
server = PrestaShopMCPServer()
|
| 23 |
+
|
| 24 |
+
# Test get_product_details
|
| 25 |
+
print("\nπ§ͺ Testing get_product_details...")
|
| 26 |
+
try:
|
| 27 |
+
result = await server._get_product_details({"product_id": 123})
|
| 28 |
+
if result:
|
| 29 |
+
print(f"β
Success: {result[0].text[:200]}...")
|
| 30 |
+
else:
|
| 31 |
+
print("β No result returned")
|
| 32 |
+
except Exception as e:
|
| 33 |
+
print(f"β Error: {e}")
|
| 34 |
+
|
| 35 |
+
# Test get_product_features
|
| 36 |
+
print("\nπ§ͺ Testing get_product_features...")
|
| 37 |
+
try:
|
| 38 |
+
result = await server._get_product_features({"product_id": 123})
|
| 39 |
+
if result:
|
| 40 |
+
print(f"β
Success: {result[0].text[:200]}...")
|
| 41 |
+
else:
|
| 42 |
+
print("β No result returned")
|
| 43 |
+
except Exception as e:
|
| 44 |
+
print(f"β Error: {e}")
|
| 45 |
+
|
| 46 |
+
# Test search_products
|
| 47 |
+
print("\nπ§ͺ Testing search_products...")
|
| 48 |
+
try:
|
| 49 |
+
result = await server._search_products({"query": "laptop", "limit": 5})
|
| 50 |
+
if result:
|
| 51 |
+
print(f"β
Success: {result[0].text[:200]}...")
|
| 52 |
+
else:
|
| 53 |
+
print("β No result returned")
|
| 54 |
+
except Exception as e:
|
| 55 |
+
print(f"β Error: {e}")
|
| 56 |
+
|
| 57 |
+
def test_http_server():
|
| 58 |
+
"""Test the HTTP wrapper server"""
|
| 59 |
+
print("\n\nTesting MCP HTTP Server...")
|
| 60 |
+
print("=" * 50)
|
| 61 |
+
|
| 62 |
+
base_url = "http://localhost:8080"
|
| 63 |
+
|
| 64 |
+
# Test health check
|
| 65 |
+
print("\nπ§ͺ Testing health check...")
|
| 66 |
+
try:
|
| 67 |
+
response = requests.get(f"{base_url}/health", timeout=5)
|
| 68 |
+
if response.status_code == 200:
|
| 69 |
+
print(f"β
Health check: {response.json()}")
|
| 70 |
+
else:
|
| 71 |
+
print(f"β Health check failed: {response.status_code}")
|
| 72 |
+
except Exception as e:
|
| 73 |
+
print(f"β Health check error: {e}")
|
| 74 |
+
print("π‘ Make sure to start the HTTP server with: python -m mcp_server.http_server")
|
| 75 |
+
return
|
| 76 |
+
|
| 77 |
+
# Test tools/list
|
| 78 |
+
print("\nπ§ͺ Testing tools/list...")
|
| 79 |
+
try:
|
| 80 |
+
payload = {
|
| 81 |
+
"jsonrpc": "2.0",
|
| 82 |
+
"method": "tools/list",
|
| 83 |
+
"params": {},
|
| 84 |
+
"id": "test-1"
|
| 85 |
+
}
|
| 86 |
+
response = requests.post(f"{base_url}/", json=payload, timeout=10)
|
| 87 |
+
if response.status_code == 200:
|
| 88 |
+
result = response.json()
|
| 89 |
+
tools = result.get("result", {}).get("tools", [])
|
| 90 |
+
print(f"β
Found {len(tools)} tools:")
|
| 91 |
+
for tool in tools:
|
| 92 |
+
print(f" - {tool['name']}: {tool['description']}")
|
| 93 |
+
else:
|
| 94 |
+
print(f"β Tools list failed: {response.status_code} - {response.text}")
|
| 95 |
+
except Exception as e:
|
| 96 |
+
print(f"β Tools list error: {e}")
|
| 97 |
+
|
| 98 |
+
# Test tools/call
|
| 99 |
+
print("\nπ§ͺ Testing tools/call - get_product_details...")
|
| 100 |
+
try:
|
| 101 |
+
payload = {
|
| 102 |
+
"jsonrpc": "2.0",
|
| 103 |
+
"method": "tools/call",
|
| 104 |
+
"params": {
|
| 105 |
+
"name": "get_product_details",
|
| 106 |
+
"arguments": {"product_id": 123}
|
| 107 |
+
},
|
| 108 |
+
"id": "test-2"
|
| 109 |
+
}
|
| 110 |
+
response = requests.post(f"{base_url}/", json=payload, timeout=30)
|
| 111 |
+
if response.status_code == 200:
|
| 112 |
+
result = response.json()
|
| 113 |
+
content = result.get("result", {}).get("content", [])
|
| 114 |
+
if content:
|
| 115 |
+
text = content[0].get("text", "")
|
| 116 |
+
print(f"β
Tool call success: {text[:200]}...")
|
| 117 |
+
else:
|
| 118 |
+
print("β No content in response")
|
| 119 |
+
else:
|
| 120 |
+
print(f"β Tool call failed: {response.status_code} - {response.text}")
|
| 121 |
+
except Exception as e:
|
| 122 |
+
print(f"β Tool call error: {e}")
|
| 123 |
+
|
| 124 |
+
def test_integration_with_ai_server():
|
| 125 |
+
"""Test integration with the main AI server"""
|
| 126 |
+
print("\n\nTesting Integration with AI Server...")
|
| 127 |
+
print("=" * 50)
|
| 128 |
+
|
| 129 |
+
# This simulates how the main AI server calls the MCP server
|
| 130 |
+
ai_server_url = "http://localhost:8000"
|
| 131 |
+
|
| 132 |
+
print("\nπ§ͺ Testing AI server product query...")
|
| 133 |
+
try:
|
| 134 |
+
payload = {
|
| 135 |
+
"inputs": "What are the features of product 123?",
|
| 136 |
+
"parameters": {"max_new_tokens": 150}
|
| 137 |
+
}
|
| 138 |
+
response = requests.post(ai_server_url, json=payload, timeout=30)
|
| 139 |
+
if response.status_code == 200:
|
| 140 |
+
result = response.json()
|
| 141 |
+
if isinstance(result, list) and len(result) > 0:
|
| 142 |
+
generated_text = result[0].get("generated_text", "")
|
| 143 |
+
print(f"β
AI server response: {generated_text[:200]}...")
|
| 144 |
+
|
| 145 |
+
# Check if it contains tool call
|
| 146 |
+
if "tool" in generated_text and "product_id" in generated_text:
|
| 147 |
+
print("β
AI correctly detected product query and returned tool call")
|
| 148 |
+
else:
|
| 149 |
+
print("βΉοΈ AI returned direct response (no tool call detected)")
|
| 150 |
+
else:
|
| 151 |
+
print("β Unexpected response format")
|
| 152 |
+
else:
|
| 153 |
+
print(f"β AI server failed: {response.status_code} - {response.text}")
|
| 154 |
+
except Exception as e:
|
| 155 |
+
print(f"β AI server error: {e}")
|
| 156 |
+
print("π‘ Make sure to start the AI server with: python app_api.py")
|
| 157 |
+
|
| 158 |
+
if __name__ == "__main__":
|
| 159 |
+
print("π PrestaShop MCP Server Test Suite")
|
| 160 |
+
print("Testing the refactored Python MCP server")
|
| 161 |
+
|
| 162 |
+
# Test 1: Direct MCP server
|
| 163 |
+
asyncio.run(test_mcp_server_direct())
|
| 164 |
+
|
| 165 |
+
# Test 2: HTTP wrapper (requires server to be running)
|
| 166 |
+
test_http_server()
|
| 167 |
+
|
| 168 |
+
# Test 3: Integration with AI server
|
| 169 |
+
test_integration_with_ai_server()
|
| 170 |
+
|
| 171 |
+
print("\n" + "=" * 50)
|
| 172 |
+
print("β
Test suite completed!")
|
| 173 |
+
print("\nπ To run the servers:")
|
| 174 |
+
print("1. MCP HTTP Server: python -m mcp_server.http_server")
|
| 175 |
+
print("2. AI Server: python app_api.py")
|
| 176 |
+
print("3. Then run this test again to verify integration")
|
test_standalone.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Test the standalone MCP Server
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import requests
|
| 7 |
+
import json
|
| 8 |
+
|
| 9 |
+
def test_mcp_server(base_url="http://localhost:8081"):
|
| 10 |
+
"""Test the MCP server endpoints"""
|
| 11 |
+
print(f"π§ͺ Testing MCP Server at {base_url}")
|
| 12 |
+
print("=" * 60)
|
| 13 |
+
|
| 14 |
+
# Test health check
|
| 15 |
+
print("\n1. Testing Health Check...")
|
| 16 |
+
try:
|
| 17 |
+
response = requests.get(f"{base_url}/health")
|
| 18 |
+
print(f"β
Health Check: {response.status_code}")
|
| 19 |
+
print(f" Response: {response.json()}")
|
| 20 |
+
except Exception as e:
|
| 21 |
+
print(f"β Health Check Failed: {e}")
|
| 22 |
+
return False
|
| 23 |
+
|
| 24 |
+
# Test tools list
|
| 25 |
+
print("\n2. Testing Tools List...")
|
| 26 |
+
try:
|
| 27 |
+
payload = {
|
| 28 |
+
"jsonrpc": "2.0",
|
| 29 |
+
"method": "tools/list",
|
| 30 |
+
"id": "test-tools-list"
|
| 31 |
+
}
|
| 32 |
+
response = requests.post(f"{base_url}/", json=payload)
|
| 33 |
+
print(f"β
Tools List: {response.status_code}")
|
| 34 |
+
result = response.json()
|
| 35 |
+
tools = result.get("result", {}).get("tools", [])
|
| 36 |
+
print(f" Available tools: {len(tools)}")
|
| 37 |
+
for tool in tools:
|
| 38 |
+
print(f" - {tool['name']}: {tool['description']}")
|
| 39 |
+
except Exception as e:
|
| 40 |
+
print(f"β Tools List Failed: {e}")
|
| 41 |
+
return False
|
| 42 |
+
|
| 43 |
+
# Test tool call
|
| 44 |
+
print("\n3. Testing Tool Call...")
|
| 45 |
+
try:
|
| 46 |
+
payload = {
|
| 47 |
+
"jsonrpc": "2.0",
|
| 48 |
+
"method": "tools/call",
|
| 49 |
+
"params": {
|
| 50 |
+
"name": "get_product_details",
|
| 51 |
+
"arguments": {"product_id": 123}
|
| 52 |
+
},
|
| 53 |
+
"id": "test-tool-call"
|
| 54 |
+
}
|
| 55 |
+
response = requests.post(f"{base_url}/", json=payload)
|
| 56 |
+
print(f"β
Tool Call: {response.status_code}")
|
| 57 |
+
result = response.json()
|
| 58 |
+
content = result.get("result", {}).get("content", [{}])[0].get("text", "No content")
|
| 59 |
+
print(f" Response: {content[:200]}...")
|
| 60 |
+
except Exception as e:
|
| 61 |
+
print(f"β Tool Call Failed: {e}")
|
| 62 |
+
return False
|
| 63 |
+
|
| 64 |
+
return True
|
| 65 |
+
|
| 66 |
+
def test_huggingface_space(space_url):
|
| 67 |
+
"""Test a deployed Hugging Face Space"""
|
| 68 |
+
print(f"\nπ Testing Hugging Face Space")
|
| 69 |
+
print(f"URL: {space_url}")
|
| 70 |
+
return test_mcp_server(space_url)
|
| 71 |
+
|
| 72 |
+
if __name__ == "__main__":
|
| 73 |
+
print("π§ Standalone MCP Server Tests")
|
| 74 |
+
print("=" * 80)
|
| 75 |
+
|
| 76 |
+
# Test local server
|
| 77 |
+
success = test_mcp_server()
|
| 78 |
+
|
| 79 |
+
print("\n" + "=" * 80)
|
| 80 |
+
if success:
|
| 81 |
+
print("β
All tests passed!")
|
| 82 |
+
print("\nπ MCP Server is ready for deployment")
|
| 83 |
+
print("π Deploy to Hugging Face Spaces:")
|
| 84 |
+
print(" 1. Create new Space with Docker SDK")
|
| 85 |
+
print(" 2. Upload all files from mcp_server/ directory")
|
| 86 |
+
print(" 3. Set app_port to 8081")
|
| 87 |
+
print(" 4. Test with the URL above")
|
| 88 |
+
else:
|
| 89 |
+
print("β Some tests failed!")
|
| 90 |
+
print("π§ Check server configuration and try again")
|