Skip to main content
Glama
tools_read.py12 kB
"""GitHub read tools for MCP server.""" import json from typing import Any, Dict, List from github_client import GitHubClient from utils import summarize_diff, format_file_size, is_binary_file, is_text async def get_repo(owner: str, repo: str) -> Dict[str, Any]: """Get repository information from GitHub. Args: owner: Repository owner (username or organization) repo: Repository name Returns: Dictionary containing repository summary and full data """ try: client = GitHubClient() repository = client.get_repository(owner, repo) # Create summary summary = f"""Repository: {repository.full_name} Description: {repository.description or "No description"} Language: {repository.language or "Not specified"} Stars: {repository.stargazers_count} Forks: {repository.forks_count} Open Issues: {repository.open_issues_count} Created: {repository.created_at.strftime("%Y-%m-%d")} Updated: {repository.updated_at.strftime("%Y-%m-%d")} Default Branch: {repository.default_branch} License: {repository.license.name if repository.license else "Not specified"} Homepage: {repository.homepage or "Not specified"}""" # Full repository data as JSON string repo_data = { "id": repository.id, "name": repository.name, "full_name": repository.full_name, "description": repository.description, "language": repository.language, "stargazers_count": repository.stargazers_count, "forks_count": repository.forks_count, "open_issues_count": repository.open_issues_count, "created_at": repository.created_at.isoformat(), "updated_at": repository.updated_at.isoformat(), "default_branch": repository.default_branch, "license": repository.license.name if repository.license else None, "homepage": repository.homepage, "archived": repository.archived, "disabled": repository.disabled, "private": repository.private, "fork": repository.fork, "size": repository.size, "topics": list(repository.get_topics()), "url": repository.html_url, "clone_url": repository.clone_url, "ssh_url": repository.ssh_url, } return { "summary": summary, "data": json.dumps(repo_data, indent=2), "success": True, } except ValueError as e: return { "summary": f"Error: {str(e)}", "data": "", "success": False, "error": str(e), } except Exception as e: return { "summary": f"Unexpected error: {str(e)}", "data": "", "success": False, "error": str(e), } async def list_pull_requests( owner: str, repo: str, state: str = "open" ) -> Dict[str, Any]: """List pull requests for a repository. Args: owner: Repository owner (username or organization) repo: Repository name state: PR state filter (open, closed, all) Returns: Dictionary containing PR list summary and data """ try: client = GitHubClient() repository = client.get_repository(owner, repo) # Get pull requests pull_requests = repository.get_pulls(state=state) pr_list = [] for pr in pull_requests: pr_data = { "number": pr.number, "title": pr.title, "user": pr.user.login, "state": pr.state, "created_at": pr.created_at.isoformat(), "updated_at": pr.updated_at.isoformat(), "merged_at": pr.merged_at.isoformat() if pr.merged_at else None, "closed_at": pr.closed_at.isoformat() if pr.closed_at else None, "draft": pr.draft, "mergeable": pr.mergeable, "mergeable_state": pr.mergeable_state, "comments": pr.comments, "commits": pr.commits, "additions": pr.additions, "deletions": pr.deletions, "changed_files": pr.changed_files, "url": pr.html_url, } pr_list.append(pr_data) # Create summary summary = f"""Pull Requests ({state}): {len(pr_list)} found Repository: {repository.full_name} """ for pr in pr_list[:5]: # Show first 5 PRs summary += ( f"#{pr['number']}: {pr['title']} by @{pr['user']} ({pr['state']})\n" ) if len(pr_list) > 5: summary += f"\n... and {len(pr_list) - 5} more PRs" return { "summary": summary, "data": json.dumps(pr_list, indent=2), "success": True, "count": len(pr_list), } except ValueError as e: return { "summary": f"Error: {str(e)}", "data": "", "success": False, "error": str(e), } except Exception as e: return { "summary": f"Unexpected error: {str(e)}", "data": "", "success": False, "error": str(e), } async def get_pr_diff(owner: str, repo: str, number: int) -> Dict[str, Any]: """Get diff for a specific pull request. Args: owner: Repository owner (username or organization) repo: Repository name number: Pull request number Returns: Dictionary containing PR diff summary and data """ try: client = GitHubClient() repository = client.get_repository(owner, repo) # Get pull request pull_request = repository.get_pull(number) # Get diff diff = pull_request.get_files() diff_data = [] total_additions = 0 total_deletions = 0 for file in diff: file_data = { "filename": file.filename, "status": file.status, "additions": file.additions, "deletions": file.deletions, "changes": file.changes, "patch": file.patch[:1000] if file.patch else None, # Limit patch size "raw_url": file.raw_url, } diff_data.append(file_data) total_additions += file.additions total_deletions += file.deletions # Create summary summary = f"""Pull Request #{number}: {pull_request.title} Repository: {repository.full_name} Author: @{pull_request.user.login} State: {pull_request.state} Files changed: {len(diff_data)} Total additions: +{total_additions} Total deletions: -{total_deletions} Changed files: """ for file in diff_data[:10]: # Show first 10 files summary += f" {file['filename']} ({file['status']}) +{file['additions']} -{file['deletions']}\n" if len(diff_data) > 10: summary += f"\n... and {len(diff_data) - 10} more files" return { "summary": summary, "data": json.dumps(diff_data, indent=2), "success": True, "file_count": len(diff_data), "total_additions": total_additions, "total_deletions": total_deletions, } except ValueError as e: return { "summary": f"Error: {str(e)}", "data": "", "success": False, "error": str(e), } except Exception as e: return { "summary": f"Unexpected error: {str(e)}", "data": "", "success": False, "error": str(e), } async def get_file( owner: str, repo: str, path: str, ref: str = "HEAD" ) -> Dict[str, Any]: """Get file content from a repository. Args: owner: Repository owner (username or organization) repo: Repository name path: File path in repository ref: Git reference (branch, tag, or commit SHA) Returns: Dictionary containing file content and metadata """ try: client = GitHubClient() repository = client.get_repository(owner, repo) # Get file content file_content = repository.get_contents(path, ref=ref) if isinstance(file_content, list): # Directory files = [] for item in file_content: file_data = { "name": item.name, "path": item.path, "type": item.type, "size": item.size, "size_formatted": format_file_size(item.size), "url": item.html_url, "download_url": item.download_url, } files.append(file_data) summary = f"""Directory: {path} Repository: {repository.full_name} Reference: {ref} Files: {len(files)} Contents: """ for file in files[:10]: # Show first 10 files summary += ( f" {file['name']} ({file['type']}, {file['size_formatted']})\n" ) if len(files) > 10: summary += f"\n... and {len(files) - 10} more items" return { "summary": summary, "data": json.dumps(files, indent=2), "success": True, "type": "directory", "file_count": len(files), } else: # Single file content = file_content.decoded_content is_text_content = is_text(content) file_data = { "name": file_content.name, "path": file_content.path, "type": file_content.type, "size": file_content.size, "size_formatted": format_file_size(file_content.size), "encoding": file_content.encoding, "url": file_content.html_url, "download_url": file_content.download_url, "is_text": is_text_content, "is_binary": is_binary_file(file_content.name), } if is_text_content and file_content.size < 1024 * 1024: # Less than 1MB try: file_data["content"] = content.decode("utf-8") except UnicodeDecodeError: file_data["content"] = content.decode("utf-8", errors="replace") else: file_data["content"] = None if file_content.size >= 1024 * 1024: file_data["content_note"] = "File too large to display (>1MB)" elif not is_text_content: file_data["content_note"] = "Binary file - content not displayed" summary = f"""File: {path} Repository: {repository.full_name} Reference: {ref} Size: {file_data["size_formatted"]} Type: {"Text" if is_text_content else "Binary"} """ if file_data["content"]: summary += ( f"Content preview (first 200 chars):\n{file_data['content'][:200]}" ) if len(file_data["content"]) > 200: summary += "..." else: summary += file_data.get("content_note", "No content preview available") return { "summary": summary, "data": json.dumps(file_data, indent=2), "success": True, "type": "file", "file_size": file_content.size, } except ValueError as e: return { "summary": f"Error: {str(e)}", "data": "", "success": False, "error": str(e), } except Exception as e: return { "summary": f"Unexpected error: {str(e)}", "data": "", "success": False, "error": 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/J-nowcow/github-MCP-practice'

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