Skip to main content
Glama
matthiashuebner

Xentral MCP HTTP Server

mcp_tools_parser.py9.29 kB
""" Parser for mcp-tools-list.md to automatically generate MCP tool definitions. Extracts tool names, descriptions, and parameters from the markdown documentation. """ import re import logging from typing import List, Dict, Optional from pathlib import Path from mcp_protocol import MCPTool, MCPToolParameter logger = logging.getLogger(__name__) class MCPToolsParser: """Parser for extracting MCP tools from markdown documentation.""" def __init__(self, markdown_file: str = "mcp-tools-list.md"): """ Initialize the parser. Args: markdown_file: Path to the markdown file containing tool definitions """ self.markdown_file = Path(markdown_file) self.tools: List[MCPTool] = [] def parse_tools(self) -> List[MCPTool]: """ Parse all tools from the markdown file. Returns: List[MCPTool]: List of parsed MCP tools """ if not self.markdown_file.exists(): logger.error(f"Markdown file not found: {self.markdown_file}") return [] try: with open(self.markdown_file, 'r', encoding='utf-8') as f: content = f.read() self.tools = self._extract_tools_from_content(content) logger.info(f"Parsed {len(self.tools)} tools from {self.markdown_file}") return self.tools except Exception as e: logger.error(f"Error parsing markdown file: {e}") return [] def _extract_tools_from_content(self, content: str) -> List[MCPTool]: """ Extract tool definitions from markdown content. Args: content: Markdown file content Returns: List[MCPTool]: Extracted tools """ tools = [] # Pattern to match tool definitions # Matches: - **`tool_name`** - Description tool_pattern = r'- \*\*`([^`]+)`\*\* - (.+?)(?=\n -|\n- \*\*`|\n##|\Z)' # Find all tool matches tool_matches = re.finditer(tool_pattern, content, re.DOTALL) for match in tool_matches: tool_name = match.group(1).strip() tool_description = match.group(2).strip() # Extract the full tool block including parameters full_match = self._extract_full_tool_block(content, match.start()) if full_match: parameters = self._extract_parameters(full_match) tool = MCPTool( name=tool_name, description=tool_description, parameters=parameters ) tools.append(tool) logger.debug(f"Parsed tool: {tool_name} with {len(parameters)} parameters") return tools def _extract_full_tool_block(self, content: str, start_pos: int) -> Optional[str]: """ Extract the full tool block starting from the tool name. Args: content: Full markdown content start_pos: Starting position of the tool definition Returns: Optional[str]: Full tool block or None if not found """ # Find the end of this tool block (next tool or section) remaining_content = content[start_pos:] # Look for the next tool or major section next_tool_pattern = r'\n- \*\*`[^`]+`\*\*' next_section_pattern = r'\n##' next_tool_match = re.search(next_tool_pattern, remaining_content[1:]) next_section_match = re.search(next_section_pattern, remaining_content[1:]) end_pos = len(remaining_content) if next_tool_match: end_pos = min(end_pos, next_tool_match.start() + 1) if next_section_match: end_pos = min(end_pos, next_section_match.start() + 1) return remaining_content[:end_pos] def _extract_parameters(self, tool_block: str) -> List[MCPToolParameter]: """ Extract parameters from a tool block. Args: tool_block: Full tool block text Returns: List[MCPToolParameter]: List of parameters """ parameters = [] # Pattern to match parameter line param_pattern = r'Parameter:\s*(.+?)(?=\n|$)' param_match = re.search(param_pattern, tool_block, re.IGNORECASE) if not param_match: return parameters param_text = param_match.group(1).strip() # Parse parameter text parts = [p.strip() for p in param_text.split(',')] for part in parts: clean_param = self._clean_parameter_name(part) if clean_param: is_required = '(required)' in part param_type = self._infer_parameter_type(clean_param) description = self._generate_parameter_description(clean_param) param = MCPToolParameter( name=clean_param, type=param_type, description=description, required=is_required ) parameters.append(param) return parameters def _clean_parameter_name(self, param: str) -> str: """ Clean parameter name by removing markdown and extra text. Args: param: Raw parameter string Returns: str: Clean parameter name """ # Remove backticks, required markers, and extra whitespace clean = re.sub(r'[`\']', '', param) clean = re.sub(r'\s*\(required\)\s*', '', clean) clean = clean.strip() return clean def _infer_parameter_type(self, param_name: str) -> str: """ Infer parameter type from parameter name. Args: param_name: Parameter name Returns: str: Inferred type """ name_lower = param_name.lower() # Type inference based on common patterns if any(keyword in name_lower for keyword in ['_id', 'id']): return "integer" elif any(keyword in name_lower for keyword in ['_number', 'number']): return "string" elif any(keyword in name_lower for keyword in ['_date', 'date']): return "string" elif any(keyword in name_lower for keyword in ['_range', 'range']): return "string" elif any(keyword in name_lower for keyword in ['quantity', 'amount', 'price', 'cost', 'count']): return "number" elif any(keyword in name_lower for keyword in ['email', 'phone', 'name', 'city', 'address']): return "string" elif any(keyword in name_lower for keyword in ['status', 'type', 'category', 'priority', 'level']): return "string" elif any(keyword in name_lower for keyword in ['active', 'enabled', 'required', 'expedite']): return "boolean" else: return "string" # Default to string def _generate_parameter_description(self, param_name: str) -> str: """ Generate a human-readable description for a parameter. Args: param_name: Parameter name Returns: str: Generated description """ # Convert snake_case to readable format words = param_name.replace('_', ' ').split() capitalized = [word.capitalize() for word in words] return ' '.join(capitalized) def get_tools_by_category(self) -> Dict[str, List[MCPTool]]: """ Group tools by category based on the markdown structure. Returns: Dict[str, List[MCPTool]]: Tools grouped by category """ # This would require parsing the markdown headers to categorize tools # For now, return all tools under a general category return {"all": self.tools} def get_priority_tools(self) -> List[MCPTool]: """ Get the priority tools for daily usage. Returns: List[MCPTool]: Priority tools for daily usage """ priority_tool_names = [ "search_customers", "get_order_overview", "get_customer_history", "track_order_progress", "create_ticket_from_call", "check_product_availability", "process_return", "send_customer_update", "quick_quote", "get_pricing_info" ] priority_tools = [] for tool in self.tools: if tool.name in priority_tool_names: priority_tools.append(tool) return priority_tools def get_tools_count(self) -> int: """ Get the total number of parsed tools. Returns: int: Number of tools """ return len(self.tools) # Global parser instance tools_parser = MCPToolsParser()

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/matthiashuebner/xentral-mcp'

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