Skip to main content
Glama

ConnectWise API Gateway MCP Server

api_db_utils.py12.2 kB
#!/usr/bin/env python3 """ API Database Utility Functions This module provides utility functions to query the SQLite database containing the ConnectWise API endpoint information. """ import sqlite3 import json from typing import Dict, List, Any, Optional, Union, Tuple class APIDatabase: """Class to handle queries to the ConnectWise API SQLite database.""" def __init__(self, db_path: str): """Initialize the database connection.""" self.db_path = db_path self.conn = None self.connect() def connect(self) -> None: """Establish a connection to the SQLite database.""" self.conn = sqlite3.connect(self.db_path) # Enable dictionary access to rows self.conn.row_factory = sqlite3.Row def close(self) -> None: """Close the database connection.""" if self.conn: self.conn.close() def search_endpoints(self, query: str) -> List[Dict[str, Any]]: """ Search for API endpoints matching the query. Args: query: Search string (can match path, description, tags) Returns: List of matching endpoints """ if not self.conn: self.connect() cursor = self.conn.cursor() search_pattern = f"%{query}%" cursor.execute(''' SELECT * FROM endpoints WHERE path LIKE ? OR description LIKE ? OR tags LIKE ? OR summary LIKE ? ORDER BY category, path ''', (search_pattern, search_pattern, search_pattern, search_pattern)) results = [] for row in cursor.fetchall(): results.append(dict(row)) return results def get_endpoint_details(self, endpoint_id: int) -> Dict[str, Any]: """ Get complete details for a specific endpoint. Args: endpoint_id: ID of the endpoint Returns: Dictionary with endpoint details including parameters and response info """ if not self.conn: self.connect() cursor = self.conn.cursor() # Get basic endpoint info cursor.execute('SELECT * FROM endpoints WHERE id = ?', (endpoint_id,)) endpoint = dict(cursor.fetchone() or {}) if not endpoint: return {} # Get parameters cursor.execute('SELECT * FROM parameters WHERE endpoint_id = ?', (endpoint_id,)) endpoint['parameters'] = [dict(row) for row in cursor.fetchall()] # Get request body cursor.execute('SELECT * FROM request_bodies WHERE endpoint_id = ?', (endpoint_id,)) request_body = cursor.fetchone() if request_body: endpoint['request_body'] = dict(request_body) # Parse the JSON schema if endpoint['request_body'].get('schema'): try: endpoint['request_body']['schema'] = json.loads(endpoint['request_body']['schema']) except json.JSONDecodeError: pass # Parse the JSON example if endpoint['request_body'].get('example'): try: endpoint['request_body']['example'] = json.loads(endpoint['request_body']['example']) except json.JSONDecodeError: pass # Get response bodies cursor.execute('SELECT * FROM response_bodies WHERE endpoint_id = ?', (endpoint_id,)) responses = [] for row in cursor.fetchall(): response = dict(row) # Parse the JSON schema if response.get('schema'): try: response['schema'] = json.loads(response['schema']) except json.JSONDecodeError: pass # Parse the JSON example if response.get('example'): try: response['example'] = json.loads(response['example']) except json.JSONDecodeError: pass responses.append(response) endpoint['responses'] = responses return endpoint def find_endpoint_by_path_method(self, path: str, method: str) -> Optional[Dict[str, Any]]: """ Find an endpoint by its path and HTTP method. Args: path: API path (e.g., /service/tickets) method: HTTP method (get, post, put, patch, delete) Returns: Endpoint details or None if not found """ if not self.conn: self.connect() cursor = self.conn.cursor() cursor.execute(''' SELECT id FROM endpoints WHERE path = ? AND method = ? ''', (path, method.lower())) result = cursor.fetchone() if not result: return None return self.get_endpoint_details(result['id']) def get_categories(self) -> List[str]: """ Get a list of all API categories. Returns: List of category names """ if not self.conn: self.connect() cursor = self.conn.cursor() cursor.execute('SELECT DISTINCT category FROM endpoints ORDER BY category') return [row['category'] for row in cursor.fetchall()] def get_endpoints_by_category(self, category: str) -> List[Dict[str, Any]]: """ Get all endpoints for a specific category. Args: category: Category name Returns: List of endpoints in that category """ if not self.conn: self.connect() cursor = self.conn.cursor() cursor.execute(''' SELECT * FROM endpoints WHERE category = ? ORDER BY path ''', (category,)) return [dict(row) for row in cursor.fetchall()] def get_parameter_details(self, endpoint_id: int, param_name: str) -> Optional[Dict[str, Any]]: """ Get details for a specific parameter of an endpoint. Args: endpoint_id: ID of the endpoint param_name: Name of the parameter Returns: Parameter details or None if not found """ if not self.conn: self.connect() cursor = self.conn.cursor() cursor.execute(''' SELECT * FROM parameters WHERE endpoint_id = ? AND name = ? ''', (endpoint_id, param_name)) result = cursor.fetchone() return dict(result) if result else None def search_by_natural_language(self, query: str, limit: int = 10) -> List[Dict[str, Any]]: """ Search for endpoints using natural language queries. Attempts to match keywords to endpoints based on common terms. Args: query: Natural language query limit: Maximum number of results Returns: List of matching endpoints """ if not self.conn: self.connect() # Simple keyword extraction (could be improved with NLP) keywords = query.lower().split() # Remove common words that aren't useful for API searches common_words = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'with', 'by', 'about', 'like', 'from'} keywords = [word for word in keywords if word not in common_words] results = [] if not keywords: return results # Search for each keyword and collect matches for keyword in keywords: if len(keyword) < 3: # Skip very short keywords continue search_pattern = f"%{keyword}%" cursor = self.conn.cursor() cursor.execute(''' SELECT id, path, method, description, category FROM endpoints WHERE path LIKE ? OR description LIKE ? OR tags LIKE ? OR summary LIKE ? LIMIT ? ''', (search_pattern, search_pattern, search_pattern, search_pattern, limit)) for row in cursor.fetchall(): endpoint = dict(row) if endpoint not in results: results.append(endpoint) # Sort results to prioritize endpoints that match more keywords def score_endpoint(endpoint): score = 0 for keyword in keywords: if keyword in endpoint.get('path', '').lower(): score += 3 # Path matches are most important if keyword in endpoint.get('description', '').lower(): score += 2 if keyword in endpoint.get('tags', '').lower(): score += 1 return score results.sort(key=score_endpoint, reverse=True) return results[:limit] def format_endpoint_for_display(self, endpoint: Dict[str, Any]) -> str: """ Format an endpoint for display in a user-friendly way. Args: endpoint: Endpoint dictionary Returns: Formatted string representation """ formatted = [] # Basic endpoint info method = endpoint.get('method', '').upper() path = endpoint.get('path', '') description = endpoint.get('description', '') formatted.append(f"{method} {path}") if description: formatted.append(f"Description: {description}") # Parameters if 'parameters' in endpoint and endpoint['parameters']: formatted.append("\nParameters:") for param in endpoint['parameters']: name = param.get('name', '') location = param.get('location', '') required = "Required" if param.get('required') == 1 else "Optional" param_type = param.get('type', '') param_desc = param.get('description', '') formatted.append(f" - {name} ({location}, {required}, {param_type}): {param_desc}") # Request body if 'request_body' in endpoint and endpoint['request_body']: formatted.append("\nRequest Body:") schema = endpoint['request_body'].get('schema', {}) if isinstance(schema, dict) and schema: formatted.append(f" Schema: {json.dumps(schema, indent=2)}") example = endpoint['request_body'].get('example', {}) if isinstance(example, dict) and example: formatted.append(f" Example: {json.dumps(example, indent=2)}") # Response bodies if 'responses' in endpoint and endpoint['responses']: formatted.append("\nResponses:") for response in endpoint['responses']: status = response.get('status_code', '') desc = response.get('description', '') formatted.append(f" - {status}: {desc}") return "\n".join(formatted) # Example usage if __name__ == "__main__": db = APIDatabase("connectwise_api.db") # Search for endpoints related to tickets results = db.search_endpoints("tickets") for result in results[:5]: # Show first 5 results print(f"{result['method'].upper()} {result['path']}") print(f" {result['description']}") print() # Get details for an endpoint if results: details = db.get_endpoint_details(results[0]['id']) print("Detailed information for first result:") print(db.format_endpoint_for_display(details)) db.close()

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/jasondsmith72/CWM-API-Gateway-MCP'

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