Skip to main content
Glama
decorators.py7.15 kB
"""Decorator utilities to reduce boilerplate in tool handlers.""" import functools import logging from typing import Any, Awaitable, Callable, Dict, Optional, TypeVar import gitlab from mcp import types from .gitlab_client import GitLabClient logger = logging.getLogger(__name__) T = TypeVar('T', bound=Callable[..., Awaitable[types.CallToolResult]]) def gitlab_tool( requires_auth: bool = True, paginated: bool = False, max_per_page: int = 100, ) -> Callable[[T], T]: """ Decorator to handle common GitLab tool patterns. Args: requires_auth: Whether the tool requires GitLab authentication paginated: Whether the tool supports pagination max_per_page: Maximum items per page for paginated requests Returns: Decorated function with common error handling and client setup """ def decorator(func: T) -> T: @functools.wraps(func) async def wrapper( client: GitLabClient, request: types.CallToolRequest, ) -> types.CallToolResult: try: arguments = request.params.arguments or {} # Check authentication if required if requires_auth and not client.is_authenticated(): return types.CallToolResult( content=[ types.TextContent( type="text", text="GitLab authentication required. Please configure your GitLab token." ) ] ) # Handle pagination parameters if supported if paginated: page = arguments.get('page', 1) per_page = arguments.get('per_page', 20) if not isinstance(page, int) or page < 1: return types.CallToolResult( content=[ types.TextContent( type="text", text="Page must be a positive integer" ) ] ) if not isinstance(per_page, int) or per_page < 1 or per_page > max_per_page: return types.CallToolResult( content=[ types.TextContent( type="text", text=f"Per page must be an integer between 1 and {max_per_page}" ) ] ) # Call the original function return await func(client, request) except gitlab.GitlabAuthenticationError: logger.error("GitLab authentication failed") return types.CallToolResult( content=[ types.TextContent( type="text", text="GitLab authentication failed. Please check your token and permissions." ) ] ) except gitlab.GitlabGetError as e: logger.error(f"GitLab API error: {e}") if e.response_code == 404: return types.CallToolResult( content=[ types.TextContent( type="text", text="Resource not found. Please check the ID or path provided." ) ] ) elif e.response_code == 403: return types.CallToolResult( content=[ types.TextContent( type="text", text="Access denied. You don't have permission to access this resource." ) ] ) else: return types.CallToolResult( content=[ types.TextContent( type="text", text=f"GitLab API error: {str(e)}" ) ] ) except Exception as e: logger.error(f"Unexpected error in {func.__name__}: {e}") return types.CallToolResult( content=[ types.TextContent( type="text", text=f"An unexpected error occurred: {str(e)}" ) ] ) return wrapper return decorator def format_response( data: Any, template: Optional[str] = None, max_length: int = 10000, ) -> types.CallToolResult: """ Format response data consistently. Args: data: Data to format template: Optional template string for formatting max_length: Maximum response length before truncation Returns: Formatted CallToolResult """ try: if template: text = template.format(data=data) elif isinstance(data, (list, tuple)): if not data: text = "No results found." else: text = "\n".join(str(item) for item in data[:50]) # Limit to 50 items if len(data) > 50: text += f"\n\n... and {len(data) - 50} more items" elif isinstance(data, dict): # Format dict as readable text lines = [] for key, value in data.items(): if isinstance(value, (str, int, float, bool, type(None))): lines.append(f"{key}: {value}") elif isinstance(value, (list, tuple)) and len(value) <= 3: lines.append(f"{key}: {', '.join(str(v) for v in value)}") else: lines.append(f"{key}: {type(value).__name__}") text = "\n".join(lines) else: text = str(data) # Truncate if too long if len(text) > max_length: text = text[:max_length - 50] + "\n\n[Response truncated due to length]" return types.CallToolResult( content=[ types.TextContent( type="text", text=text ) ] ) except Exception as e: logger.error(f"Error formatting response: {e}") return types.CallToolResult( content=[ types.TextContent( type="text", text=f"Error formatting response: {str(e)}" ) ] )

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/Vijay-Duke/mcp-gitlab'

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