Skip to main content
Glama
server.py12.8 kB
"""PrestaShop Documentation MCP Server.""" import logging from typing import List, Optional from fastmcp import FastMCP from .ingest import get_hook, list_hooks, search_hooks from .ingest_v2 import index_documentation_v2 from .search_v2 import search_documentation, get_document, list_documents, get_stats logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Initialize FastMCP server mcp = FastMCP("PrestaShop Docs") @mcp.tool() def search_prestashop_hooks( queries: List[str], hook_type: Optional[str] = None, origin: Optional[str] = None, ) -> str: """Search PrestaShop hooks using full-text search. Args: queries: List of search terms (maximum 3) hook_type: Filter by hook type (display, action) origin: Filter by origin (core, module, theme) Returns: Formatted search results with hook details """ if not queries: return "Error: Please provide at least one search query" # Limit to 3 queries queries = queries[:3] logger.info(f"Searching hooks for: {queries}") results = search_hooks(queries, hook_type=hook_type, origin=origin, limit=10) if not results: filters = [] if hook_type: filters.append(f"type={hook_type}") if origin: filters.append(f"origin={origin}") filter_str = f" (filters: {', '.join(filters)})" if filters else "" return f"No results found for: {', '.join(queries)}{filter_str}" # Format results output = [f"Found {len(results)} hooks for: {', '.join(queries)}\n"] for i, hook in enumerate(results, 1): output.append(f"{i}. **{hook['name']}** ({hook['type']} hook)") output.append(f" Origin: {hook['origin']}") output.append(f" Locations: {hook['locations']}") if hook.get("description"): desc = hook["description"][:150] if len(hook["description"]) > 150: desc += "..." output.append(f" Description: {desc}") if hook.get("aliases"): output.append(f" Aliases: {', '.join(hook['aliases'])}") if hook.get("snippet"): output.append(f" {hook['snippet']}") output.append("") return "\n".join(output) @mcp.tool() def get_prestashop_hook(hook_name: str) -> str: """Get complete documentation for a specific PrestaShop hook. Args: hook_name: Name of the hook (e.g., 'displayHeader', 'actionProductAdd') Returns: Complete hook documentation including description, parameters, examples """ logger.info(f"Getting hook: {hook_name}") hook = get_hook(hook_name) if not hook: return f"Hook '{hook_name}' not found. Use list_prestashop_hooks() to see available hooks." # Format hook documentation output = [f"# {hook['name']}\n"] output.append(f"**Type:** {hook['type']}") output.append(f"**Origin:** {hook['origin']}") output.append(f"**Locations:** {hook['locations']}\n") if hook.get("description"): output.append(f"## Description\n") output.append(f"{hook['description']}\n") if hook.get("aliases"): output.append(f"## Aliases\n") for alias in hook["aliases"]: output.append(f"- {alias}") output.append("") if hook.get("github_refs"): output.append(f"## Source Files\n") for ref in hook["github_refs"]: output.append(f"- {ref}") output.append("") if hook.get("code_examples"): output.append(f"## Code Examples\n") for i, example in enumerate(hook["code_examples"], 1): output.append(f"### Example {i}\n") output.append(f"```php\n{example}\n```\n") # Add full markdown content if hook.get("content"): output.append("## Full Documentation\n") output.append(hook["content"]) return "\n".join(output) @mcp.tool() def list_prestashop_hooks( hook_type: Optional[str] = None, origin: Optional[str] = None ) -> str: """List all available PrestaShop hooks. Args: hook_type: Filter by type (display, action) origin: Filter by origin (core, module, theme) Returns: List of all hooks organized by type and origin """ logger.info(f"Listing hooks (type={hook_type}, origin={origin})") hooks = list_hooks(hook_type=hook_type, origin=origin) if not hooks: return "No hooks found matching the filters" # Organize hooks by type display_hooks = [h for h in hooks if h["type"] == "display"] action_hooks = [h for h in hooks if h["type"] == "action"] other_hooks = [h for h in hooks if h["type"] not in ["display", "action"]] output = [f"Available PrestaShop Hooks ({len(hooks)} total)\n"] if display_hooks: output.append(f"## Display Hooks ({len(display_hooks)})\n") for hook in display_hooks[:20]: # Limit to first 20 desc = hook.get("description", "") if desc: desc = desc[:80] + "..." if len(desc) > 80 else desc output.append(f"- **{hook['name']}** ({hook['origin']}) - {desc}") else: output.append(f"- **{hook['name']}** ({hook['origin']})") if len(display_hooks) > 20: output.append(f"\n ... and {len(display_hooks) - 20} more display hooks") output.append("") if action_hooks: output.append(f"## Action Hooks ({len(action_hooks)})\n") for hook in action_hooks[:20]: # Limit to first 20 desc = hook.get("description", "") if desc: desc = desc[:80] + "..." if len(desc) > 80 else desc output.append(f"- **{hook['name']}** ({hook['origin']}) - {desc}") else: output.append(f"- **{hook['name']}** ({hook['origin']})") if len(action_hooks) > 20: output.append(f"\n ... and {len(action_hooks) - 20} more action hooks") output.append("") if other_hooks: output.append(f"## Other Hooks ({len(other_hooks)})\n") for hook in other_hooks: output.append(f"- **{hook['name']}** ({hook['origin']}, {hook['type']})") output.append("") output.append( "\nUse get_prestashop_hook('hook_name') to get detailed documentation for a specific hook." ) return "\n".join(output) @mcp.tool() def search_prestashop_docs( query: str, doc_type: Optional[str] = None, category: Optional[str] = None ) -> str: """Search ALL PrestaShop documentation (guides, tutorials, API docs, hooks, etc.). Args: query: Search query (e.g., "install", "Mac", "deployment") doc_type: Filter by type: hook, guide, tutorial, api, reference, component, faq, general category: Filter by category: basics, development, modules, themes, etc. Returns: Search results with snippets and metadata """ logger.info(f"Searching documentation for: {query} (type={doc_type}, category={category})") results = search_documentation(query, doc_type=doc_type, category=category, limit=10) if not results: filters = [] if doc_type: filters.append(f"type={doc_type}") if category: filters.append(f"category={category}") filter_str = f" (filters: {', '.join(filters)})" if filters else "" return f"No results found for: {query}{filter_str}" # Format results output = [f"Found {len(results)} documents for: {query}\n"] for i, doc in enumerate(results, 1): output.append(f"{i}. **{doc['title']}** ({doc['doc_type']})") output.append(f" Category: {doc['category']}") if doc.get('subcategory'): output.append(f" Subcategory: {doc['subcategory']}") output.append(f" Path: {doc['path']}") if doc.get('snippet'): output.append(f" {doc['snippet']}") output.append("") output.append("\nUse get_prestashop_doc('path') to get the full document content.") return "\n".join(output) @mcp.tool() def get_prestashop_doc(path: str) -> str: """Get the full content of a specific PrestaShop documentation file. Args: path: Document path (e.g., 'basics/installation/environments/macos-specific.md') Returns: Full document content with metadata """ logger.info(f"Getting document: {path}") doc = get_document(path) if not doc: return f"Document '{path}' not found. Use search_prestashop_docs() to find documents." # Format document output = [f"# {doc['title']}\n"] output.append(f"**Type:** {doc['doc_type']}") output.append(f"**Category:** {doc['category']}") if doc.get('subcategory'): output.append(f"**Subcategory:** {doc['subcategory']}") if doc.get('version'): output.append(f"**Version:** {doc['version']}") output.append(f"**Path:** {doc['path']}\n") output.append("---\n") output.append(doc['content']) return "\n".join(output) @mcp.tool() def list_prestashop_docs( doc_type: Optional[str] = None, category: Optional[str] = None ) -> str: """List PrestaShop documentation files. Args: doc_type: Filter by type: hook, guide, tutorial, api, reference, component, faq, general category: Filter by category: basics, development, modules, themes, etc. Returns: List of available documents """ logger.info(f"Listing documents (type={doc_type}, category={category})") docs = list_documents(doc_type=doc_type, category=category, limit=50) if not docs: return "No documents found matching the filters" # Group by category by_category = {} for doc in docs: cat = doc['category'] if cat not in by_category: by_category[cat] = [] by_category[cat].append(doc) output = [f"Available PrestaShop Documentation ({len(docs)} documents)\n"] for category, category_docs in sorted(by_category.items()): output.append(f"## {category.title()} ({len(category_docs)} docs)\n") for doc in category_docs[:20]: # Limit to 20 per category subcat = f" / {doc['subcategory']}" if doc.get('subcategory') else "" output.append(f"- **{doc['title']}** ({doc['doc_type']}){subcat}") output.append(f" Path: {doc['path']}") if len(category_docs) > 20: output.append(f"\n ... and {len(category_docs) - 20} more documents") output.append("") return "\n".join(output) @mcp.tool() def get_prestashop_stats() -> str: """Get statistics about indexed PrestaShop documentation. Returns: Statistics about documents, types, categories, and specialized data """ logger.info("Getting documentation statistics") stats = get_stats() output = ["# PrestaShop Documentation Statistics\n"] output.append(f"**Total Documents:** {stats['total_documents']}\n") output.append("## By Document Type\n") for doc_type, count in sorted(stats['by_type'].items(), key=lambda x: x[1], reverse=True): output.append(f"- {doc_type}: {count}") output.append("") output.append("## By Category\n") for category, count in sorted(stats['by_category'].items(), key=lambda x: x[1], reverse=True): output.append(f"- {category}: {count}") output.append("") output.append("## Specialized Data\n") output.append(f"- Domain References (CQRS): {stats['domain_references']}") output.append(f"- UI Components: {stats['components']}") return "\n".join(output) def main(): """Main entry point for the MCP server.""" import os # Index documentation on startup if not already indexed logger.info("Initializing PrestaShop Documentation MCP Server...") try: count = index_documentation_v2(force=False) logger.info(f"Documentation indexed: {count} documents") except Exception as e: logger.error(f"Error indexing documentation: {e}") logger.info("Server will start but search may not work properly") # Check for transport configuration transport = os.getenv("PRESTASHOP_TRANSPORT", "stdio").lower() if transport in ["http", "sse"]: # HTTP/SSE transport for remote deployment host = os.getenv("PRESTASHOP_HOST", "0.0.0.0") port = int(os.getenv("PRESTASHOP_PORT", "8765")) logger.info(f"Starting MCP server with {transport.upper()} transport on {host}:{port}") mcp.run(transport=transport, host=host, port=port) else: # Default stdio transport for local/CLI usage logger.info("Starting MCP server with STDIO transport") mcp.run() if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/florinel-chis/prestashop-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server