Skip to main content
Glama

FastMCP Demo Server

by apoojary94
server.py•15.4 kB
"""GitHub MCP Server - Download and analyze GitHub repository files.""" import os import re import requests import json from typing import Dict, List, Any, Optional from urllib.parse import urlparse, unquote from fastmcp import FastMCP from dotenv import load_dotenv # Load environment variables load_dotenv() APP_NAME = "github-file-analyzer" APP_DESCRIPTION = "MCP server for downloading and analyzing GitHub repository files." app = FastMCP(name=APP_NAME, instructions=APP_DESCRIPTION) # API configuration GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") GITHUB_API_BASE = "https://api.github.com" RAW_CONTENT_BASE = "https://raw.githubusercontent.com" OPENAI_API_BASE = "https://api.openai.com/v1" # Set up headers for GitHub API HEADERS = { "Accept": "application/vnd.github.v3+json", "User-Agent": "github-file-analyzer/1.0" } if GITHUB_TOKEN: HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" # Set up headers for OpenAI API OPENAI_HEADERS = { "Content-Type": "application/json", "Authorization": f"Bearer {OPENAI_API_KEY}" } def parse_github_url(url: str) -> Optional[Dict[str, str]]: """Parse GitHub URL to extract repository information. Args: url: GitHub URL (blob, raw, or api format) Returns: Dictionary with parsed URL components or None if invalid """ try: # Handle raw.githubusercontent.com URLs if "raw.githubusercontent.com" in url: parts = url.replace("https://raw.githubusercontent.com/", "").split("/") if len(parts) >= 3: return { "owner": parts[0], "repo": parts[1], "branch": parts[2], "file_path": "/".join(parts[3:]) if len(parts) > 3 else "", "url_type": "raw" } # Handle github.com/blob URLs elif "github.com" in url and "/blob/" in url: # Extract path after github.com path_match = re.search(r'github\.com/([^/]+)/([^/]+)/blob/([^/]+)/(.+)', url) if path_match: return { "owner": path_match.group(1), "repo": path_match.group(2), "branch": path_match.group(3), "file_path": path_match.group(4), "url_type": "blob" } # Handle github.com API URLs elif "api.github.com" in url and "/contents/" in url: # Extract from API URL pattern path_match = re.search(r'api\.github\.com/repos/([^/]+)/([^/]+)/contents/(.+)', url) if path_match: return { "owner": path_match.group(1), "repo": path_match.group(2), "file_path": path_match.group(3), "url_type": "api" } return None except Exception: return None def get_raw_file_url(owner: str, repo: str, branch: str, file_path: str) -> str: """Convert GitHub URL components to raw content URL.""" return f"{RAW_CONTENT_BASE}/{owner}/{repo}/{branch}/{file_path}" def download_file_content(url: str) -> Dict[str, Any]: """Download content from a GitHub file URL. Args: url: GitHub file URL Returns: Dictionary containing file content and metadata """ try: # Parse the URL parsed = parse_github_url(url) if not parsed: return { "success": False, "error": f"Invalid GitHub URL format: {url}", "url": url } # Get raw content URL if parsed["url_type"] == "raw": raw_url = url else: raw_url = get_raw_file_url( parsed["owner"], parsed["repo"], parsed["branch"], parsed["file_path"] ) # Download the file content response = requests.get(raw_url, headers=HEADERS, timeout=30) response.raise_for_status() # Get file information filename = parsed["file_path"].split("/")[-1] if parsed["file_path"] else "unknown" file_extension = filename.split(".")[-1] if "." in filename else "" return { "success": True, "content": response.text, "metadata": { "filename": filename, "file_path": parsed["file_path"], "owner": parsed["owner"], "repo": parsed["repo"], "branch": parsed["branch"], "file_extension": file_extension, "size_bytes": len(response.content), "size_chars": len(response.text), "url": url, "raw_url": raw_url } } except requests.exceptions.RequestException as e: return { "success": False, "error": f"Failed to download file: {str(e)}", "url": url } except Exception as e: return { "success": False, "error": f"Unexpected error: {str(e)}", "url": url } @app.tool() def download_github_files(urls: List[str]) -> Dict[str, Any]: """Download content from multiple GitHub file URLs. Args: urls: List of GitHub file URLs (supports blob, raw, and API formats) Returns: Dictionary containing downloaded files with content and metadata """ if not urls: return {"error": "No URLs provided"} if len(urls) > 20: # Reasonable limit return {"error": "Too many URLs provided (max 20)"} results = [] successful_downloads = 0 failed_downloads = 0 for url in urls: if not url.strip(): continue result = download_file_content(url.strip()) results.append(result) if result["success"]: successful_downloads += 1 else: failed_downloads += 1 return { "summary": { "total_urls": len(urls), "successful_downloads": successful_downloads, "failed_downloads": failed_downloads, "success_rate": f"{(successful_downloads / len(urls) * 100):.1f}%" if urls else "0%" }, "files": results } # Style-based code review functionality ACCEPTED_STYLES = { "trump": "Speak like Donald Trump - use superlatives constantly ('the best', 'tremendous', 'nobody knows more than me'), be extremely confident and boastful, frequently mention how great things are, use simple but emphatic language, and occasionally go off on tangents about how amazing everything you do is.", "shakespeare": "Write in Shakespearean English - use thee, thou, thy, wherefore, hither, and other Early Modern English terms. Speak in iambic pentameter when possible, use elaborate metaphors and poetic flourishes, reference the stars and fate, and express everything with dramatic theatrical flair.", "pirate": "Talk like a stereotypical pirate - use 'arr', 'matey', 'ye', 'aye', mention the seven seas, treasure, plunder, and sailing. Pepper speech with nautical terms, threaten to make people walk the plank, and be generally rowdy and swashbuckling.", "chaucer": "Write in the style of Geoffrey Chaucer's Middle English - use archaic spellings like 'whan', 'tellen', 'maken', frequent use of 'y-' prefix for past participles. Be verbose and tell everything as if it's part of a grand Canterbury Tale, with moral undertones and references to pilgrims, knights, and medieval life.", "cowboy": "Speak like an Old West cowboy - use 'partner', 'reckon', 'yonder', 'howdy', reference cattle, horses, and the dusty trail. Use folksy wisdom, be laconic and direct, and pepper speech with western idioms like 'rode hard and put away wet', 'this ain't my first rodeo'.", "yoda": "Speak like Yoda from Star Wars - invert sentence structure consistently, placing verbs and objects before subjects. Use wise and cryptic phrasing, reference the Force frequently, speak slowly and thoughtfully, use 'hmm' and 'yes' often. Example: 'Strong with the Force, this code is.'", "tupac": "Write in Tupac Shakur's style - use 90s hip-hop slang, be poetic yet street-smart, reference the struggle and keeping it real. Be introspective and philosophical while maintaining street credibility. Reference thug life, the streets, and staying true to yourself. Use rhythmic, almost rap-like phrasing.", "venture-capitalist": "Speak like a Silicon Valley venture capitalist - constantly talk about disruption, synergy, paradigm shifts, hockey stick growth, and unicorns. Obsess over market size, scalability, and ROI. Ask about the moat, the go-to-market strategy, and whether this will 10x. Use phrases like 'circle back', 'double-click on that', and 'move the needle'.", "corporate-speak": "Use maximum corporate jargon - leverage synergies, circle back, touch base, move the needle, shift paradigms, think outside the box, drill down, get our ducks in a row, low-hanging fruit, boil the ocean. Make everything sound important and strategic while saying very little of actual substance. Schedule follows ups and action items for everything.", "gen-z-slang": "Talk in Gen Z internet slang - use 'no cap', 'fr fr', 'slaps', 'hits different', 'bussin', 'sheesh', 'based', 'mid', 'ate and left no crumbs', 'understood the assignment', 'main character energy', 'it's giving...', 'the way that...', and skull emoji energy. Be extremely online and reference TikTok culture." } def generate_style_review(code: str, style: str = "trump") -> str: """Generate a code review in the specified style using OpenAI API. Args: code: The code to review style: The speaking style to use Returns: A styled code review """ if style.lower() not in ACCEPTED_STYLES: return f"Invalid style '{style}'. Available styles: {', '.join(ACCEPTED_STYLES.keys())}" if not OPENAI_API_KEY: return "āŒ OpenAI API key not configured. Please add OPENAI_API_KEY to your .env file." style_description = ACCEPTED_STYLES[style.lower()] try: # Prepare the OpenAI API request payload = { "model": "gpt-4o-mini", "messages": [ { "role": "system", "content": f"""You are a code reviewer. {style_description} Review and comment on the provided code while maintaining this speaking style throughout. Be entertaining and stay fully in character. Provide: 1. Overall assessment of the code 2. Specific feedback on code quality, structure, and best practices 3. Suggestions for improvement 4. Any potential issues or bugs you notice Keep the review detailed but concise, and maintain the character style consistently.""" }, { "role": "user", "content": f"Please review this code:\n\n```\n{code}\n```" } ], "max_tokens": 1000, "temperature": 0.7 } # Make the API request response = requests.post( f"{OPENAI_API_BASE}/chat/completions", headers=OPENAI_HEADERS, json=payload, timeout=30 ) if response.status_code == 200: data = response.json() review_content = data["choices"][0]["message"]["content"] # Format the review with metadata formatted_review = f"""šŸŽ­ **Code Review in {style.title()} Style** šŸŽ­ **Style Applied:** {style.title()} **AI Model:** GPT-4o-mini **Review:** {review_content} **Technical Details:** - File Length: {len(code.splitlines())} lines - Character Count: {len(code)} characters - Complexity: {'High' if len(code) > 1000 else 'Medium' if len(code) > 500 else 'Low'} """ return formatted_review else: error_msg = f"OpenAI API error: {response.status_code} - {response.text}" return f"āŒ Failed to generate review: {error_msg}" except requests.exceptions.RequestException as e: return f"āŒ Network error calling OpenAI API: {str(e)}" except Exception as e: return f"āŒ Error generating review: {str(e)}" @app.tool() def download_and_review_github_files(urls: List[str], style: str = "trump") -> Dict[str, Any]: """Download GitHub files and generate styled code reviews for each file. Args: urls: List of GitHub file URLs to download and review style: Speaking style for the reviews (default: "trump") Returns: Dictionary containing downloaded files with styled reviews """ if not urls: return {"error": "No URLs provided"} if len(urls) > 10: # Reasonable limit for reviews return {"error": "Too many URLs provided (max 10 for reviews)"} # Validate style if style.lower() not in ACCEPTED_STYLES: return { "error": f"Invalid style '{style}'. Available styles: {', '.join(ACCEPTED_STYLES.keys())}" } # Download files using existing functionality download_result = download_github_files(urls) if "error" in download_result: return download_result # Generate reviews for each successfully downloaded file reviews = [] for file_data in download_result["files"]: if file_data["success"]: # Generate review for this file review = generate_style_review(file_data["content"], style) reviews.append({ "filename": file_data["metadata"]["filename"], "file_path": file_data["metadata"]["file_path"], "repository": f"{file_data['metadata']['owner']}/{file_data['metadata']['repo']}", "file_extension": file_data["metadata"]["file_extension"], "size_bytes": file_data["metadata"]["size_bytes"], "review": review, "style_used": style }) else: reviews.append({ "filename": "unknown", "error": file_data["error"], "review": f"āŒ Could not review this file due to download error: {file_data['error']}" }) return { "summary": { "total_files": len(urls), "successful_reviews": len([r for r in reviews if "error" not in r]), "failed_reviews": len([r for r in reviews if "error" in r]), "style_used": style, "available_styles": list(ACCEPTED_STYLES.keys()) }, "reviews": reviews } @app.tool() def list_review_styles() -> Dict[str, Any]: """List all available speaking styles for code reviews. Returns: Dictionary containing all available styles with descriptions """ return { "available_styles": ACCEPTED_STYLES, "total_styles": len(ACCEPTED_STYLES), "default_style": "trump" } if __name__ == "__main__": app.run()

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/apoojary94/mcp_hackathon'

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