Created
March 12, 2025 11:14
-
-
Save wspruijt/f8ee8a8f4fd2ba7dc040880de5f9a3be to your computer and use it in GitHub Desktop.
Vibecoded Sentry MCP server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| from typing import Dict | |
| from fastmcp import FastMCP | |
| from dotenv import load_dotenv | |
| import requests | |
| load_dotenv() | |
| # Initialize environment variables | |
| SENTRY_AUTH_TOKEN = os.getenv("SENTRY_AUTH_TOKEN") | |
| SENTRY_ORG = os.getenv("SENTRY_ORG") | |
| SENTRY_PROJECT = os.getenv("SENTRY_PROJECT") | |
| if not all([SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT]): | |
| raise ValueError("Missing required Sentry configuration in .env") | |
| # Create MCP server | |
| mcp = FastMCP(host="127.0.0.1", port=7777) | |
| # Configure Sentry API | |
| SENTRY_API_BASE = f"https://sentry.io/api/0/organizations/{SENTRY_ORG}" | |
| HEADERS = { | |
| "Authorization": f"Bearer {SENTRY_AUTH_TOKEN}", | |
| "Content-Type": "application/json" | |
| } | |
| @mcp.tool() | |
| async def get_issue_details(sentry_ref: str) -> Dict: | |
| """ | |
| Get issue details from Sentry using the issue reference. | |
| Args: | |
| sentry_ref: The Sentry issue reference/ID | |
| Returns: | |
| Dict containing issue description, file path, and stack trace | |
| """ | |
| # Remove any 'getsentry/' prefix if present | |
| sentry_ref = sentry_ref.replace("getsentry/", "") | |
| try: | |
| # Get issue details from Sentry API | |
| url = f"{SENTRY_API_BASE}/issues/{sentry_ref}/" | |
| response = requests.get(url, headers=HEADERS) | |
| response.raise_for_status() | |
| issue_data = response.json() | |
| # Get the latest event for this issue to get stack trace | |
| events_url = f"{SENTRY_API_BASE}/issues/{sentry_ref}/events/latest/" | |
| event_response = requests.get(events_url, headers=HEADERS) | |
| event_response.raise_for_status() | |
| event_data = event_response.json() | |
| # Extract stack trace information | |
| stack_trace = None | |
| file_path = None | |
| if event_data.get("entries"): | |
| for entry in event_data["entries"]: | |
| if entry.get("type") == "exception": | |
| values = entry.get("data", {}).get("values", []) | |
| if values: | |
| frames = values[0].get("stacktrace", {}).get("frames", []) | |
| if frames: | |
| # Get the most relevant frame (usually the last one) | |
| frame = frames[-1] | |
| file_path = f"{frame.get('filename')}:{frame.get('lineno')}" | |
| stack_trace = "\n".join([ | |
| f"{f.get('filename')}:{f.get('lineno')} in {f.get('function')}" | |
| for f in frames | |
| ]) | |
| return { | |
| "description": issue_data.get("title", "No description available"), | |
| "file_path": file_path or "File path not available", | |
| "stack_trace": stack_trace or "Stack trace not available" | |
| } | |
| except Exception as e: | |
| return {"error": f"Failed to get issue details: {str(e)}"} | |
| if __name__ == "__main__": | |
| mcp.run() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment