Skip to main content
Glama
server.py66.4 kB
import os import json import logging from typing import Any, List, Optional try: from mcp.server import Server, NotificationOptions from mcp.server.models import InitializationOptions import mcp.server.stdio import mcp.types as types except ImportError as e: import sys print(f"Error importing MCP: {e}", file=sys.stderr) raise ImportError(f"Failed to import MCP SDK. Make sure 'mcp' is installed: {e}") from pydantic import AnyUrl from dotenv import load_dotenv import gitlab.exceptions try: from .gitlab_client import GitLabClient, GitLabConfig from .git_detector import GitDetector from .utils import GitLabClientManager, sanitize_error, truncate_response from .constants import ( DEFAULT_GITLAB_URL, DEFAULT_PAGE_SIZE, SMALL_PAGE_SIZE, MAX_PAGE_SIZE, DEFAULT_MAX_BODY_LENGTH, MAX_RESPONSE_SIZE, LOG_LEVEL, LOG_FORMAT, JSON_LOGGING, ERROR_NO_TOKEN, ERROR_AUTH_FAILED, ERROR_NOT_FOUND, ERROR_RATE_LIMIT, ERROR_GENERIC, ERROR_NO_PROJECT, ERROR_INVALID_INPUT, TOOL_LIST_PROJECTS, TOOL_GET_PROJECT, TOOL_GET_CURRENT_PROJECT, TOOL_LIST_ISSUES, TOOL_LIST_MRS, TOOL_GET_MR_NOTES, TOOL_LIST_BRANCHES, TOOL_LIST_PIPELINES, TOOL_LIST_COMMITS, TOOL_LIST_REPOSITORY_TREE, TOOL_LIST_TAGS, TOOL_LIST_USER_EVENTS, TOOL_LIST_PROJECT_MEMBERS, TOOL_LIST_PROJECT_HOOKS, TOOL_LIST_RELEASES, TOOL_GET_CURRENT_USER, TOOL_GET_USER, TOOL_LIST_GROUPS, TOOL_GET_GROUP, TOOL_LIST_GROUP_PROJECTS, TOOL_LIST_SNIPPETS, TOOL_GET_SNIPPET, TOOL_CREATE_SNIPPET, TOOL_UPDATE_SNIPPET, TOOL_LIST_PIPELINE_JOBS, TOOL_DOWNLOAD_JOB_ARTIFACT, TOOL_LIST_PROJECT_JOBS, TOOL_SEARCH_USER, TOOL_GET_USER_DETAILS, TOOL_GET_MY_PROFILE, TOOL_GET_USER_CONTRIBUTIONS_SUMMARY, TOOL_GET_USER_ACTIVITY_FEED, TOOL_GET_USER_OPEN_MRS, TOOL_GET_USER_REVIEW_REQUESTS, TOOL_GET_USER_OPEN_ISSUES, TOOL_GET_USER_REPORTED_ISSUES, TOOL_GET_USER_RESOLVED_ISSUES, TOOL_GET_USER_COMMITS, TOOL_GET_USER_MERGE_COMMITS, TOOL_GET_USER_CODE_CHANGES_SUMMARY, TOOL_GET_USER_SNIPPETS, TOOL_GET_USER_ISSUE_COMMENTS, TOOL_GET_USER_MR_COMMENTS, TOOL_GET_USER_DISCUSSION_THREADS, TOOL_GET_USER_RESOLVED_THREADS ) from .tool_handlers import TOOL_HANDLERS, get_project_id_or_detect from . import tool_descriptions as desc except ImportError as e: # Fallback imports for development/testing when package is not installed import sys import os from pathlib import Path # Add the parent directory to sys.path to allow imports src_path = Path(__file__).resolve().parent.parent sys.path.insert(0, str(src_path)) try: from mcp_gitlab.gitlab_client import GitLabClient, GitLabConfig from mcp_gitlab.git_detector import GitDetector from mcp_gitlab.utils import GitLabClientManager, sanitize_error, truncate_response from mcp_gitlab.constants import ( DEFAULT_GITLAB_URL, DEFAULT_PAGE_SIZE, SMALL_PAGE_SIZE, MAX_PAGE_SIZE, DEFAULT_MAX_BODY_LENGTH, MAX_RESPONSE_SIZE, LOG_LEVEL, LOG_FORMAT, JSON_LOGGING, ERROR_NO_TOKEN, ERROR_AUTH_FAILED, ERROR_NOT_FOUND, ERROR_RATE_LIMIT, ERROR_GENERIC, ERROR_NO_PROJECT, ERROR_INVALID_INPUT, TOOL_LIST_PROJECTS, TOOL_GET_PROJECT, TOOL_GET_CURRENT_PROJECT, TOOL_LIST_ISSUES, TOOL_LIST_MRS, TOOL_GET_MR_NOTES, TOOL_LIST_BRANCHES, TOOL_LIST_PIPELINES, TOOL_LIST_COMMITS, TOOL_LIST_REPOSITORY_TREE, TOOL_LIST_TAGS, TOOL_LIST_USER_EVENTS, TOOL_LIST_PROJECT_MEMBERS, TOOL_LIST_PROJECT_HOOKS, TOOL_LIST_RELEASES, TOOL_GET_CURRENT_USER, TOOL_GET_USER, TOOL_LIST_GROUPS, TOOL_GET_GROUP, TOOL_LIST_GROUP_PROJECTS, TOOL_LIST_SNIPPETS, TOOL_GET_SNIPPET, TOOL_CREATE_SNIPPET, TOOL_UPDATE_SNIPPET, TOOL_LIST_PIPELINE_JOBS, TOOL_DOWNLOAD_JOB_ARTIFACT, TOOL_LIST_PROJECT_JOBS, TOOL_SEARCH_USER, TOOL_GET_USER_DETAILS, TOOL_GET_MY_PROFILE, TOOL_GET_USER_CONTRIBUTIONS_SUMMARY, TOOL_GET_USER_ACTIVITY_FEED, TOOL_GET_USER_OPEN_MRS, TOOL_GET_USER_REVIEW_REQUESTS, TOOL_GET_USER_OPEN_ISSUES, TOOL_GET_USER_REPORTED_ISSUES, TOOL_GET_USER_RESOLVED_ISSUES, TOOL_GET_USER_COMMITS, TOOL_GET_USER_MERGE_COMMITS, TOOL_GET_USER_CODE_CHANGES_SUMMARY, TOOL_GET_USER_SNIPPETS, TOOL_GET_USER_ISSUE_COMMENTS, TOOL_GET_USER_MR_COMMENTS, TOOL_GET_USER_DISCUSSION_THREADS, TOOL_GET_USER_RESOLVED_THREADS ) from mcp_gitlab.tool_handlers import TOOL_HANDLERS, get_project_id_or_detect import mcp_gitlab.tool_descriptions as desc except ImportError: # If mcp_gitlab package doesn't exist, try direct imports from gitlab_client import GitLabClient, GitLabConfig from git_detector import GitDetector from utils import GitLabClientManager, sanitize_error, truncate_response from constants import ( DEFAULT_GITLAB_URL, DEFAULT_PAGE_SIZE, SMALL_PAGE_SIZE, MAX_PAGE_SIZE, DEFAULT_MAX_BODY_LENGTH, MAX_RESPONSE_SIZE, LOG_LEVEL, LOG_FORMAT, JSON_LOGGING, ERROR_NO_TOKEN, ERROR_AUTH_FAILED, ERROR_NOT_FOUND, ERROR_RATE_LIMIT, ERROR_GENERIC, ERROR_NO_PROJECT, ERROR_INVALID_INPUT, TOOL_LIST_PROJECTS, TOOL_GET_PROJECT, TOOL_GET_CURRENT_PROJECT, TOOL_LIST_ISSUES, TOOL_LIST_MRS, TOOL_GET_MR_NOTES, TOOL_LIST_BRANCHES, TOOL_LIST_PIPELINES, TOOL_LIST_COMMITS, TOOL_LIST_REPOSITORY_TREE, TOOL_LIST_TAGS, TOOL_LIST_USER_EVENTS, TOOL_LIST_PROJECT_MEMBERS, TOOL_LIST_PROJECT_HOOKS, TOOL_LIST_RELEASES, TOOL_GET_CURRENT_USER, TOOL_GET_USER, TOOL_LIST_GROUPS, TOOL_GET_GROUP, TOOL_LIST_GROUP_PROJECTS, TOOL_LIST_SNIPPETS, TOOL_GET_SNIPPET, TOOL_CREATE_SNIPPET, TOOL_UPDATE_SNIPPET, TOOL_LIST_PIPELINE_JOBS, TOOL_DOWNLOAD_JOB_ARTIFACT, TOOL_LIST_PROJECT_JOBS, TOOL_SEARCH_USER, TOOL_GET_USER_DETAILS, TOOL_GET_MY_PROFILE, TOOL_GET_USER_CONTRIBUTIONS_SUMMARY, TOOL_GET_USER_ACTIVITY_FEED, TOOL_GET_USER_OPEN_MRS, TOOL_GET_USER_REVIEW_REQUESTS, TOOL_GET_USER_OPEN_ISSUES, TOOL_GET_USER_REPORTED_ISSUES, TOOL_GET_USER_RESOLVED_ISSUES, TOOL_GET_USER_COMMITS, TOOL_GET_USER_MERGE_COMMITS, TOOL_GET_USER_CODE_CHANGES_SUMMARY, TOOL_GET_USER_SNIPPETS, TOOL_GET_USER_ISSUE_COMMENTS, TOOL_GET_USER_MR_COMMENTS, TOOL_GET_USER_DISCUSSION_THREADS, TOOL_GET_USER_RESOLVED_THREADS ) from tool_handlers import TOOL_HANDLERS, get_project_id_or_detect import tool_descriptions as desc load_dotenv() # Configure logging based on environment settings if JSON_LOGGING: # Use python-json-logger for structured logging from pythonjsonlogger import jsonlogger handler = logging.StreamHandler() formatter = jsonlogger.JsonFormatter( fmt='%(timestamp)s %(level)s %(name)s %(message)s', rename_fields={'timestamp': 'asctime', 'level': 'levelname'} ) handler.setFormatter(formatter) # Configure root logger logging.root.handlers = [handler] logging.root.setLevel(getattr(logging, LOG_LEVEL.upper())) else: # Use traditional logging format logging.basicConfig( level=getattr(logging, LOG_LEVEL.upper()), format=LOG_FORMAT ) logger = logging.getLogger(__name__) server = Server("mcp-gitlab") client_manager = GitLabClientManager() def get_gitlab_client() -> GitLabClient: """Get GitLab client using singleton manager""" config = GitLabConfig( url=os.getenv("GITLAB_URL", DEFAULT_GITLAB_URL), private_token=os.getenv("GITLAB_PRIVATE_TOKEN"), oauth_token=os.getenv("GITLAB_OAUTH_TOKEN") ) return client_manager.get_client(config) @server.list_tools() async def handle_list_tools() -> List[types.Tool]: """List all available GitLab tools""" return [ # Project Management types.Tool( name=TOOL_LIST_PROJECTS, description=desc.DESC_LIST_PROJECTS, inputSchema={ "type": "object", "properties": { "owned": {"type": "boolean", "description": desc.DESC_OWNED_PROJECTS, "default": False}, "search": {"type": "string", "description": desc.DESC_SEARCH_TERM + " for projects"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_GET_PROJECT, description=desc.DESC_GET_PROJECT, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID_REQUIRED} }, "required": ["project_id"] } ), types.Tool( name=TOOL_GET_CURRENT_PROJECT, description=desc.DESC_GET_CURRENT_PROJECT, inputSchema={ "type": "object", "properties": { "path": {"type": "string", "description": desc.DESC_GIT_PATH} } } ), # Authentication & User Info types.Tool( name=TOOL_GET_CURRENT_USER, description=desc.DESC_GET_CURRENT_USER, inputSchema={ "type": "object", "properties": {} } ), types.Tool( name=TOOL_GET_USER, description=desc.DESC_GET_USER, inputSchema={ "type": "object", "properties": { "user_id": {"type": "integer", "description": desc.DESC_USER_ID}, "username": {"type": "string", "description": desc.DESC_USERNAME} } } ), # Group Management types.Tool( name=TOOL_LIST_GROUPS, description=desc.DESC_LIST_GROUPS, inputSchema={ "type": "object", "properties": { "search": {"type": "string", "description": desc.DESC_SEARCH_TERM + " for groups"}, "owned": {"type": "boolean", "description": desc.DESC_OWNED_GROUPS, "default": False}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_GET_GROUP, description=desc.DESC_GET_GROUP, inputSchema={ "type": "object", "properties": { "group_id": {"type": "string", "description": desc.DESC_GROUP_ID}, "with_projects": {"type": "boolean", "description": desc.DESC_WITH_PROJECTS, "default": False} }, "required": ["group_id"] } ), types.Tool( name=TOOL_LIST_GROUP_PROJECTS, description=desc.DESC_LIST_GROUP_PROJECTS, inputSchema={ "type": "object", "properties": { "group_id": {"type": "string", "description": desc.DESC_GROUP_ID}, "search": {"type": "string", "description": desc.DESC_SEARCH_TERM + " for projects"}, "include_subgroups": {"type": "boolean", "description": desc.DESC_INCLUDE_SUBGROUPS, "default": False}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["group_id"] } ), # Issues types.Tool( name=TOOL_LIST_ISSUES, description=desc.DESC_LIST_ISSUES, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "state": {"type": "string", "description": desc.DESC_STATE_ISSUE, "enum": ["opened", "closed", "all"], "default": "opened"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name="gitlab_get_issue", description=desc.DESC_GET_ISSUE, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "issue_iid": {"type": "integer", "description": desc.DESC_ISSUE_IID} }, "required": ["issue_iid"] } ), # Merge Requests types.Tool( name=TOOL_LIST_MRS, description=desc.DESC_LIST_MRS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "state": {"type": "string", "description": desc.DESC_STATE_MR, "enum": ["opened", "closed", "merged", "all"], "default": "opened"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name="gitlab_get_merge_request", description=desc.DESC_GET_MR, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID} }, "required": ["mr_iid"] } ), types.Tool( name=TOOL_GET_MR_NOTES, description=desc.DESC_GET_MR_NOTES, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": SMALL_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1}, "sort": {"type": "string", "description": desc.DESC_SORT_ORDER, "enum": ["asc", "desc"], "default": "asc"}, "order_by": {"type": "string", "description": desc.DESC_ORDER_BY, "enum": ["created_at", "updated_at"], "default": "created_at"}, "max_body_length": {"type": "integer", "description": desc.DESC_MAX_BODY_LENGTH, "default": DEFAULT_MAX_BODY_LENGTH, "minimum": 0} }, "required": ["mr_iid"] } ), # Repository Files types.Tool( name="gitlab_get_file_content", description=desc.DESC_GET_FILE_CONTENT, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "file_path": {"type": "string", "description": desc.DESC_FILE_PATH}, "ref": {"type": "string", "description": desc.DESC_REF} }, "required": ["file_path"] } ), types.Tool( name=TOOL_LIST_REPOSITORY_TREE, description=desc.DESC_LIST_TREE, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "path": {"type": "string", "description": desc.DESC_TREE_PATH, "default": ""}, "ref": {"type": "string", "description": desc.DESC_REF}, "recursive": {"type": "boolean", "description": desc.DESC_RECURSIVE, "default": False} } } ), # Snippets types.Tool( name=TOOL_LIST_SNIPPETS, description=desc.DESC_LIST_SNIPPETS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_GET_SNIPPET, description=desc.DESC_GET_SNIPPET, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "snippet_id": {"type": "integer", "description": desc.DESC_SNIPPET_ID} }, "required": ["snippet_id"] } ), types.Tool( name=TOOL_CREATE_SNIPPET, description=desc.DESC_CREATE_SNIPPET, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "title": {"type": "string", "description": desc.DESC_SNIPPET_TITLE}, "file_name": {"type": "string", "description": desc.DESC_SNIPPET_FILE_NAME}, "content": {"type": "string", "description": desc.DESC_SNIPPET_CONTENT}, "description": {"type": "string", "description": desc.DESC_SNIPPET_DESCRIPTION}, "visibility": {"type": "string", "description": desc.DESC_SNIPPET_VISIBILITY, "enum": ["private", "internal", "public"], "default": "private"} }, "required": ["title", "file_name", "content"] } ), types.Tool( name=TOOL_UPDATE_SNIPPET, description=desc.DESC_UPDATE_SNIPPET, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "snippet_id": {"type": "integer", "description": desc.DESC_SNIPPET_ID}, "title": {"type": "string", "description": desc.DESC_SNIPPET_TITLE}, "file_name": {"type": "string", "description": desc.DESC_SNIPPET_FILE_NAME}, "content": {"type": "string", "description": desc.DESC_SNIPPET_CONTENT}, "description": {"type": "string", "description": desc.DESC_SNIPPET_DESCRIPTION}, "visibility": {"type": "string", "description": desc.DESC_SNIPPET_VISIBILITY, "enum": ["private", "internal", "public"]} }, "required": ["snippet_id"] } ), # Commits types.Tool( name=TOOL_LIST_COMMITS, description=desc.DESC_LIST_COMMITS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "ref_name": {"type": "string", "description": desc.DESC_REF.replace("commit SHA", "tag name")}, "since": {"type": "string", "description": desc.DESC_DATE_SINCE}, "until": {"type": "string", "description": desc.DESC_DATE_UNTIL}, "path": {"type": "string", "description": desc.DESC_PATH_FILTER}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name="gitlab_get_commit", description=desc.DESC_GET_COMMIT, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "commit_sha": {"type": "string", "description": desc.DESC_COMMIT_SHA}, "include_stats": {"type": "boolean", "description": desc.DESC_INCLUDE_STATS, "default": False} }, "required": ["commit_sha"] } ), types.Tool( name="gitlab_get_commit_diff", description=desc.DESC_GET_COMMIT_DIFF, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "commit_sha": {"type": "string", "description": "Commit SHA"} }, "required": ["commit_sha"] } ), # Search types.Tool( name="gitlab_search_projects", description=desc.DESC_SEARCH_PROJECTS, inputSchema={ "type": "object", "properties": { "search": {"type": "string", "description": desc.DESC_SEARCH_TERM}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["search"] } ), types.Tool( name="gitlab_search_in_project", description=desc.DESC_SEARCH_IN_PROJECT, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "scope": {"type": "string", "description": desc.DESC_SEARCH_SCOPE, "enum": ["issues", "merge_requests", "milestones", "notes", "wiki_blobs", "commits", "blobs"]}, "search": {"type": "string", "description": desc.DESC_SEARCH_TERM}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["scope", "search"] } ), # Repository Info types.Tool( name=TOOL_LIST_BRANCHES, description=desc.DESC_LIST_BRANCHES, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": "Project ID or path (optional - auto-detects from git)"} } } ), types.Tool( name=TOOL_LIST_PIPELINES, description=desc.DESC_LIST_PIPELINES, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "ref": {"type": "string", "description": desc.DESC_BRANCH_TAG_REF} } } ), # User Events types.Tool( name=TOOL_LIST_USER_EVENTS, description=desc.DESC_LIST_USER_EVENTS, inputSchema={ "type": "object", "properties": { "username": {"type": "string", "description": desc.DESC_USERNAME}, "action": {"type": "string", "description": desc.DESC_ACTION_FILTER, "enum": ["commented", "pushed", "created", "closed", "opened", "merged", "joined", "left", "destroyed", "expired", "removed", "deleted", "approved", "updated", "uploaded", "downloaded"]}, "target_type": {"type": "string", "description": desc.DESC_TARGET_TYPE_FILTER, "enum": ["Note", "Issue", "MergeRequest", "Commit", "Project", "Snippet", "User", "WikiPage", "Milestone", "Discussion", "DiffNote"]}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1}, "after": {"type": "string", "description": desc.DESC_DATE_AFTER}, "before": {"type": "string", "description": desc.DESC_DATE_BEFORE} }, "required": ["username"] } ), # MR Lifecycle Tools types.Tool( name="gitlab_update_merge_request", description=desc.DESC_UPDATE_MR, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID}, "title": {"type": "string", "description": desc.DESC_TITLE}, "description": {"type": "string", "description": desc.DESC_DESCRIPTION}, "assignee_id": {"type": "integer", "description": desc.DESC_ASSIGNEE_ID}, "assignee_ids": {"type": "array", "items": {"type": "integer"}, "description": desc.DESC_ASSIGNEE_IDS}, "reviewer_ids": {"type": "array", "items": {"type": "integer"}, "description": desc.DESC_REVIEWER_IDS}, "labels": {"type": "string", "description": desc.DESC_LABELS}, "milestone_id": {"type": "integer", "description": desc.DESC_MILESTONE_ID}, "state_event": {"type": "string", "description": desc.DESC_STATE_EVENT, "enum": ["close", "reopen"]}, "remove_source_branch": {"type": "boolean", "description": desc.DESC_REMOVE_SOURCE_BRANCH}, "squash": {"type": "boolean", "description": desc.DESC_SQUASH}, "discussion_locked": {"type": "boolean", "description": desc.DESC_DISCUSSION_LOCKED}, "allow_collaboration": {"type": "boolean", "description": desc.DESC_ALLOW_COLLABORATION}, "target_branch": {"type": "string", "description": desc.DESC_TARGET_BRANCH} }, "required": ["mr_iid"] } ), types.Tool( name="gitlab_close_merge_request", description=desc.DESC_CLOSE_MR, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID} }, "required": ["mr_iid"] } ), types.Tool( name="gitlab_merge_merge_request", description=desc.DESC_MERGE_MR, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID}, "merge_when_pipeline_succeeds": {"type": "boolean", "description": desc.DESC_MERGE_WHEN_PIPELINE_SUCCEEDS, "default": False}, "should_remove_source_branch": {"type": "boolean", "description": desc.DESC_REMOVE_SOURCE_BRANCH}, "merge_commit_message": {"type": "string", "description": desc.DESC_MERGE_COMMIT_MESSAGE}, "squash_commit_message": {"type": "string", "description": desc.DESC_SQUASH_COMMIT_MESSAGE}, "squash": {"type": "boolean", "description": desc.DESC_SQUASH} }, "required": ["mr_iid"] } ), # Comment Tools types.Tool( name="gitlab_add_issue_comment", description=desc.DESC_ADD_ISSUE_COMMENT, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "issue_iid": {"type": "integer", "description": desc.DESC_ISSUE_IID}, "body": {"type": "string", "description": desc.DESC_COMMENT_BODY} }, "required": ["issue_iid", "body"] } ), types.Tool( name="gitlab_add_merge_request_comment", description=desc.DESC_ADD_MR_COMMENT, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID}, "body": {"type": "string", "description": desc.DESC_COMMENT_BODY} }, "required": ["mr_iid", "body"] } ), # Approval Tools types.Tool( name="gitlab_approve_merge_request", description=desc.DESC_APPROVE_MR, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID} }, "required": ["mr_iid"] } ), types.Tool( name="gitlab_get_merge_request_approvals", description=desc.DESC_GET_MR_APPROVALS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID} }, "required": ["mr_iid"] } ), # Repository Tools types.Tool( name=TOOL_LIST_TAGS, description=desc.DESC_LIST_TAGS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "order_by": {"type": "string", "description": desc.DESC_ORDER_BY_TAG, "enum": ["name", "updated", "version", "semver"], "default": "updated"}, "sort": {"type": "string", "description": desc.DESC_SORT_ORDER, "enum": ["asc", "desc"], "default": "desc"} } } ), types.Tool( name="gitlab_create_commit", description=desc.DESC_CREATE_COMMIT, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "branch": {"type": "string", "description": desc.DESC_BRANCH}, "commit_message": {"type": "string", "description": desc.DESC_COMMIT_MESSAGE}, "actions": { "type": "array", "description": desc.DESC_ACTIONS, "items": { "type": "object", "properties": { "action": {"type": "string", "enum": ["create", "update", "delete", "move"]}, "file_path": {"type": "string"}, "content": {"type": "string"}, "previous_path": {"type": "string"}, "encoding": {"type": "string", "enum": ["text", "base64"], "default": "text"} }, "required": ["action", "file_path"] } }, "author_email": {"type": "string", "description": desc.DESC_AUTHOR_EMAIL}, "author_name": {"type": "string", "description": desc.DESC_AUTHOR_NAME} }, "required": ["branch", "commit_message", "actions"] } ), types.Tool( name="gitlab_compare_refs", description=desc.DESC_COMPARE_REFS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "from_ref": {"type": "string", "description": desc.DESC_FROM_REF}, "to_ref": {"type": "string", "description": desc.DESC_TO_REF}, "straight": {"type": "boolean", "description": desc.DESC_STRAIGHT, "default": False} }, "required": ["from_ref", "to_ref"] } ), # Release and Member Tools types.Tool( name=TOOL_LIST_RELEASES, description=desc.DESC_LIST_RELEASES, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "order_by": {"type": "string", "description": desc.DESC_ORDER_BY, "enum": ["released_at", "created_at"], "default": "released_at"}, "sort": {"type": "string", "description": desc.DESC_SORT_ORDER, "enum": ["asc", "desc"], "default": "desc"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_LIST_PROJECT_MEMBERS, description=desc.DESC_LIST_PROJECT_MEMBERS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "query": {"type": "string", "description": desc.DESC_QUERY}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_LIST_PROJECT_HOOKS, description=desc.DESC_LIST_PROJECT_HOOKS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID} } } ), # MR Advanced Tools types.Tool( name="gitlab_get_merge_request_discussions", description=desc.DESC_GET_MR_DISCUSSIONS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["mr_iid"] } ), types.Tool( name="gitlab_resolve_discussion", description=desc.DESC_RESOLVE_DISCUSSION, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID}, "discussion_id": {"type": "string", "description": desc.DESC_DISCUSSION_ID} }, "required": ["mr_iid", "discussion_id"] } ), types.Tool( name="gitlab_get_merge_request_changes", description=desc.DESC_GET_MR_CHANGES, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID} }, "required": ["mr_iid"] } ), # MR Operations Tools types.Tool( name="gitlab_rebase_merge_request", description=desc.DESC_REBASE_MR, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID} }, "required": ["mr_iid"] } ), types.Tool( name="gitlab_cherry_pick_commit", description=desc.DESC_CHERRY_PICK, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "commit_sha": {"type": "string", "description": desc.DESC_COMMIT_SHA}, "branch": {"type": "string", "description": desc.DESC_BRANCH} }, "required": ["commit_sha", "branch"] } ), # AI Helper Tools types.Tool( name="gitlab_summarize_merge_request", description=desc.DESC_SUMMARIZE_MR, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "mr_iid": {"type": "integer", "description": desc.DESC_MR_IID}, "max_length": {"type": "integer", "description": desc.DESC_MAX_LENGTH, "default": 500} }, "required": ["mr_iid"] } ), types.Tool( name="gitlab_summarize_issue", description=desc.DESC_SUMMARIZE_ISSUE, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "issue_iid": {"type": "integer", "description": desc.DESC_ISSUE_IID}, "max_length": {"type": "integer", "description": desc.DESC_MAX_LENGTH, "default": 500} }, "required": ["issue_iid"] } ), types.Tool( name="gitlab_summarize_pipeline", description=desc.DESC_SUMMARIZE_PIPELINE, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "pipeline_id": {"type": "integer", "description": desc.DESC_PIPELINE_ID}, "max_length": {"type": "integer", "description": desc.DESC_MAX_LENGTH, "default": 500} }, "required": ["pipeline_id"] } ), # Advanced Diff Tools types.Tool( name="gitlab_smart_diff", description=desc.DESC_SMART_DIFF, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "from_ref": {"type": "string", "description": desc.DESC_FROM_REF}, "to_ref": {"type": "string", "description": desc.DESC_TO_REF}, "context_lines": {"type": "integer", "description": desc.DESC_CONTEXT_LINES, "default": 3}, "max_file_size": {"type": "integer", "description": desc.DESC_MAX_FILE_SIZE, "default": 50000} }, "required": ["from_ref", "to_ref"] } ), types.Tool( name="gitlab_safe_preview_commit", description=desc.DESC_SAFE_PREVIEW_COMMIT, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "branch": {"type": "string", "description": desc.DESC_BRANCH}, "commit_message": {"type": "string", "description": desc.DESC_COMMIT_MESSAGE}, "actions": { "type": "array", "description": desc.DESC_ACTIONS, "items": { "type": "object", "properties": { "action": {"type": "string", "enum": ["create", "update", "delete", "move"]}, "file_path": {"type": "string"}, "content": {"type": "string"}, "previous_path": {"type": "string"}, "encoding": {"type": "string", "enum": ["text", "base64"], "default": "text"} }, "required": ["action", "file_path"] } } }, "required": ["branch", "commit_message", "actions"] } ), # Batch Operations Tool types.Tool( name="gitlab_batch_operations", description=desc.DESC_BATCH_OPERATIONS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "operations": { "type": "array", "description": desc.DESC_OPERATIONS, "items": { "type": "object", "properties": { "name": {"type": "string", "description": "Operation name for reference"}, "tool": {"type": "string", "description": "GitLab tool name to execute"}, "arguments": {"type": "object", "description": "Arguments for the tool"} }, "required": ["name", "tool", "arguments"] } }, "stop_on_error": {"type": "boolean", "description": desc.DESC_STOP_ON_ERROR, "default": True} }, "required": ["operations"] } ), # Job and Artifact Tools types.Tool( name=TOOL_LIST_PIPELINE_JOBS, description=desc.DESC_LIST_PIPELINE_JOBS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "pipeline_id": {"type": "integer", "description": desc.DESC_PIPELINE_ID}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["pipeline_id"] } ), types.Tool( name=TOOL_DOWNLOAD_JOB_ARTIFACT, description=desc.DESC_DOWNLOAD_JOB_ARTIFACT, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "job_id": {"type": "integer", "description": desc.DESC_JOB_ID}, "artifact_path": {"type": "string", "description": desc.DESC_ARTIFACT_PATH} }, "required": ["job_id"] } ), types.Tool( name=TOOL_LIST_PROJECT_JOBS, description=desc.DESC_LIST_PROJECT_JOBS, inputSchema={ "type": "object", "properties": { "project_id": {"type": "string", "description": desc.DESC_PROJECT_ID}, "scope": {"type": "string", "description": desc.DESC_JOB_SCOPE, "enum": ["created", "pending", "running", "failed", "success", "canceled", "skipped", "waiting_for_resource", "manual"]}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), # User & Profile Tools types.Tool( name=TOOL_SEARCH_USER, description=desc.DESC_SEARCH_USER, inputSchema={ "type": "object", "properties": { "search": {"type": "string", "description": "Search query (name, username, or email fragment)"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["search"] } ), types.Tool( name=TOOL_GET_USER_DETAILS, description=desc.DESC_GET_USER_DETAILS, inputSchema={ "type": "object", "properties": { "user_id": {"type": "string", "description": "Numeric user ID"}, "username": {"type": "string", "description": "Username string"} } } ), types.Tool( name=TOOL_GET_MY_PROFILE, description=desc.DESC_GET_MY_PROFILE, inputSchema={ "type": "object", "properties": {} } ), types.Tool( name=TOOL_GET_USER_CONTRIBUTIONS_SUMMARY, description=desc.DESC_GET_USER_CONTRIBUTIONS_SUMMARY, inputSchema={ "type": "object", "properties": { "user_id": {"type": "string", "description": "Numeric user ID"}, "username": {"type": "string", "description": "Username string"}, "since": {"type": "string", "description": "Start date for analysis (YYYY-MM-DD)"}, "until": {"type": "string", "description": "End date for analysis (YYYY-MM-DD)"}, "project_id": {"type": "string", "description": "Optional project scope filter"} } } ), types.Tool( name=TOOL_GET_USER_ACTIVITY_FEED, description=desc.DESC_GET_USER_ACTIVITY_FEED, inputSchema={ "type": "object", "properties": { "user_id": {"type": "string", "description": "Numeric user ID"}, "username": {"type": "string", "description": "Username string"}, "action": {"type": "string", "description": "Filter by action type"}, "target_type": {"type": "string", "description": "Filter by target type"}, "after": {"type": "string", "description": "Events after this date (YYYY-MM-DD)"}, "before": {"type": "string", "description": "Events before this date (YYYY-MM-DD)"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), # User's Issues & MRs Tools types.Tool( name=TOOL_GET_USER_OPEN_MRS, description=desc.DESC_GET_USER_OPEN_MRS, inputSchema={ "type": "object", "properties": { "user_id": {"type": "string", "description": "Numeric user ID"}, "username": {"type": "string", "description": "Username string"}, "sort": {"type": "string", "description": "Sort order", "enum": ["updated", "created", "priority"], "default": "updated"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_GET_USER_REVIEW_REQUESTS, description=desc.DESC_GET_USER_REVIEW_REQUESTS, inputSchema={ "type": "object", "properties": { "user_id": {"type": "string", "description": "Numeric user ID"}, "username": {"type": "string", "description": "Username string"}, "priority": {"type": "string", "description": "Filter by priority", "enum": ["high", "medium", "low"]}, "sort": {"type": "string", "description": "Sort order", "enum": ["urgency", "age", "project"], "default": "urgency"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_GET_USER_OPEN_ISSUES, description=desc.DESC_GET_USER_OPEN_ISSUES, inputSchema={ "type": "object", "properties": { "user_id": {"type": "string", "description": "Numeric user ID"}, "username": {"type": "string", "description": "Username string"}, "severity": {"type": "string", "description": "Filter by severity level"}, "sla_status": {"type": "string", "description": "Filter by SLA compliance", "enum": ["at_risk", "overdue", "ok"]}, "sort": {"type": "string", "description": "Sort order", "enum": ["priority", "due_date", "updated"], "default": "priority"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_GET_USER_REPORTED_ISSUES, description=desc.DESC_GET_USER_REPORTED_ISSUES, inputSchema={ "type": "object", "properties": { "user_id": {"type": "string", "description": "Numeric user ID"}, "username": {"type": "string", "description": "Username string"}, "state": {"type": "string", "description": "Filter by state", "enum": ["opened", "closed", "all"], "default": "opened"}, "since": {"type": "string", "description": "Issues created after date (YYYY-MM-DD)"}, "until": {"type": "string", "description": "Issues created before date (YYYY-MM-DD)"}, "sort": {"type": "string", "description": "Sort order", "enum": ["created", "updated", "closed"], "default": "created"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_GET_USER_RESOLVED_ISSUES, description=desc.DESC_GET_USER_RESOLVED_ISSUES, inputSchema={ "type": "object", "properties": { "user_id": {"type": "string", "description": "Numeric user ID"}, "username": {"type": "string", "description": "Username string"}, "since": {"type": "string", "description": "Resolved after date (YYYY-MM-DD)"}, "until": {"type": "string", "description": "Resolved before date (YYYY-MM-DD)"}, "complexity": {"type": "string", "description": "Filter by resolution complexity"}, "sort": {"type": "string", "description": "Sort order", "enum": ["closed", "complexity", "impact"], "default": "closed"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), # User's Code & Commits Tools types.Tool( name=TOOL_GET_USER_COMMITS, description=desc.DESC_GET_USER_COMMITS, inputSchema={ "type": "object", "properties": { "user_id": {"type": "string", "description": "Numeric user ID"}, "username": {"type": "string", "description": "Username string"}, "project_id": {"type": "string", "description": "Optional project scope filter"}, "branch": {"type": "string", "description": "Filter by specific branch"}, "since": {"type": "string", "description": "Commits after date (YYYY-MM-DD)"}, "until": {"type": "string", "description": "Commits before date (YYYY-MM-DD)"}, "include_stats": {"type": "boolean", "description": "Include file change statistics", "default": False}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} } } ), types.Tool( name=TOOL_GET_USER_MERGE_COMMITS, description=desc.DESC_GET_USER_MERGE_COMMITS, inputSchema={ "type": "object", "properties": { "username": {"type": "string", "description": "Username string"}, "project_id": {"type": "string", "description": "Optional project scope filter"}, "since": {"type": "string", "description": "Commits after date (YYYY-MM-DD)"}, "until": {"type": "string", "description": "Commits before date (YYYY-MM-DD)"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["username"] } ), types.Tool( name=TOOL_GET_USER_CODE_CHANGES_SUMMARY, description=desc.DESC_GET_USER_CODE_CHANGES_SUMMARY, inputSchema={ "type": "object", "properties": { "username": {"type": "string", "description": "Username string"}, "project_id": {"type": "string", "description": "Optional project scope filter"}, "since": {"type": "string", "description": "Commits after date (YYYY-MM-DD)"}, "until": {"type": "string", "description": "Commits before date (YYYY-MM-DD)"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE} }, "required": ["username"] } ), types.Tool( name=TOOL_GET_USER_SNIPPETS, description=desc.DESC_GET_USER_SNIPPETS, inputSchema={ "type": "object", "properties": { "username": {"type": "string", "description": "Username string"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["username"] } ), types.Tool( name=TOOL_GET_USER_ISSUE_COMMENTS, description=desc.DESC_GET_USER_ISSUE_COMMENTS, inputSchema={ "type": "object", "properties": { "username": {"type": "string", "description": "Username string"}, "project_id": {"type": "string", "description": "Optional project scope filter"}, "since": {"type": "string", "description": "Comments after date (YYYY-MM-DD)"}, "until": {"type": "string", "description": "Comments before date (YYYY-MM-DD)"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["username"] } ), types.Tool( name=TOOL_GET_USER_MR_COMMENTS, description=desc.DESC_GET_USER_MR_COMMENTS, inputSchema={ "type": "object", "properties": { "username": {"type": "string", "description": "Username string"}, "project_id": {"type": "string", "description": "Optional project scope filter"}, "since": {"type": "string", "description": "Comments after date (YYYY-MM-DD)"}, "until": {"type": "string", "description": "Comments before date (YYYY-MM-DD)"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["username"] } ), types.Tool( name=TOOL_GET_USER_DISCUSSION_THREADS, description=desc.DESC_GET_USER_DISCUSSION_THREADS, inputSchema={ "type": "object", "properties": { "username": {"type": "string", "description": "Username string"}, "project_id": {"type": "string", "description": "Optional project scope filter"}, "thread_status": {"type": "string", "description": "Filter by thread status", "enum": ["resolved", "unresolved"]}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["username"] } ), types.Tool( name=TOOL_GET_USER_RESOLVED_THREADS, description=desc.DESC_GET_USER_RESOLVED_THREADS, inputSchema={ "type": "object", "properties": { "username": {"type": "string", "description": "Username string"}, "project_id": {"type": "string", "description": "Optional project scope filter"}, "since": {"type": "string", "description": "Threads resolved after date (YYYY-MM-DD)"}, "until": {"type": "string", "description": "Threads resolved before date (YYYY-MM-DD)"}, "per_page": {"type": "integer", "description": desc.DESC_PER_PAGE, "default": DEFAULT_PAGE_SIZE, "minimum": 1, "maximum": MAX_PAGE_SIZE}, "page": {"type": "integer", "description": desc.DESC_PAGE_NUMBER, "default": 1, "minimum": 1} }, "required": ["username"] } ) ] @server.call_tool() async def handle_call_tool( name: str, arguments: dict | None ) -> List[types.TextContent | types.ImageContent | types.EmbeddedResource]: """Handle tool execution with comprehensive error handling""" try: client = get_gitlab_client() # Check if tool exists handler = TOOL_HANDLERS.get(name) if not handler: raise ValueError(f"Unknown tool: {name}") # Execute the handler result = handler(client, arguments) # Truncate response if too large result = truncate_response(result, MAX_RESPONSE_SIZE) return [types.TextContent( type="text", text=json.dumps(result, indent=2) )] except gitlab.exceptions.GitlabAuthenticationError as e: logger.error(f"Authentication failed: {e}") error_response = sanitize_error(e, ERROR_AUTH_FAILED) return [types.TextContent(type="text", text=json.dumps(error_response, indent=2))] except gitlab.exceptions.GitlabGetError as e: response_code = getattr(e, 'response_code', None) if response_code == 404: logger.warning(f"Resource not found: {e}") error_response = sanitize_error(e, ERROR_NOT_FOUND) elif response_code == 429: logger.warning(f"Rate limit exceeded: {e}") error_response = sanitize_error(e, ERROR_RATE_LIMIT) else: logger.error(f"GitLab API error: {e}") error_response = sanitize_error(e) return [types.TextContent(type="text", text=json.dumps(error_response, indent=2))] except gitlab.exceptions.GitlabError as e: logger.error(f"General GitLab error: {e}") error_response = sanitize_error(e) return [types.TextContent(type="text", text=json.dumps(error_response, indent=2))] except ValueError as e: logger.warning(f"Invalid input: {e}") error_response = sanitize_error(e, ERROR_INVALID_INPUT) return [types.TextContent(type="text", text=json.dumps(error_response, indent=2))] except Exception as e: logger.error(f"Unexpected error: {e}") error_response = sanitize_error(e, ERROR_GENERIC) return [types.TextContent(type="text", text=json.dumps(error_response, indent=2))] async def main(): """Run the robust MCP GitLab server""" try: logger.info("Starting robust MCP GitLab server...") if not (os.getenv("GITLAB_PRIVATE_TOKEN") or os.getenv("GITLAB_OAUTH_TOKEN")): logger.warning(ERROR_NO_TOKEN) async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): logger.info("Server streams initialized") await server.run( read_stream, write_stream, InitializationOptions( server_name="mcp-gitlab-robust", server_version="2.0.0", capabilities=server.get_capabilities( notification_options=NotificationOptions(), experimental_capabilities={}, ), ), ) except Exception as e: logger.error(f"Server error: {e}") import traceback logger.error(traceback.format_exc()) raise if __name__ == "__main__": import asyncio try: asyncio.run(main()) except KeyboardInterrupt: logger.info("Server stopped by user") except Exception as e: logger.error(f"Fatal error: {e}") sys.exit(1)

Implementation Reference

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