Skip to content

Commit 5f946ed

Browse files
authored
Merge pull request #7 from networkninja/new-servers
Added servers
2 parents 7831c93 + 97e85b9 commit 5f946ed

File tree

5 files changed

+251
-0
lines changed

5 files changed

+251
-0
lines changed

docker-compose.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ services:
88
restart: always
99
ports:
1010
- "8080:80"
11+
mcp-atlassian:
12+
container_name: mcp-atlassian
13+
image: ghcr.io/sooperset/mcp-atlassian:latest
14+
command: --transport sse --port 9000 -vv
15+
ports:
16+
- "9000:9000"
17+
env_file:
18+
- PATH/TO/servers/mcp-atlassian/.env
19+
restart: always
20+
1121

1222
api:
1323
container_name: LibreChat

librechat.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,20 @@ mcpServers:
9393
- "/app/node_modules/.bin/mcp-server-github"
9494
env:
9595
GITHUB_PERSONAL_ACCESS_TOKEN: XXX
96+
sentry:
97+
command: uv
98+
args:
99+
- --directory
100+
- /app/servers/mcp_server_sentry
101+
- run
102+
- server.py
103+
- --sentry_auth_token
104+
- "YOUR AUTH TOKEN HERE"
105+
- --sentry_organization_slug
106+
- "ORG NAME HERE"
107+
- --sentry_organization_id
108+
- "ORG ID HERE"
109+
mcp-atlassian:
110+
url: http://mcp-atlassian:9000/sse
111+
112+

pyproject.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[project]
2+
3+
# These two have to be in a pyproject.toml file
4+
name = "requirements" # the actual name doesn't matter
5+
version = "1" # nor the version
6+
7+
# these make sure you have the correct version of Python / Python packages
8+
requires-python = ">=3.10"
9+
dependencies = [
10+
"markdownify>=0.13.1",
11+
"mcp>=1.1.3",
12+
"protego>=0.3.1",
13+
"pydantic>=2.0.0",
14+
"readabilipy>=0.2.0",
15+
"requests>=2.32.3",
16+
]

servers/mcp-atlassian/.env

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
JIRA_HOST=https://networkninja.atlassian.net
2+
JIRA_URL=https://networkninja.atlassian.net
3+
JIRA_EMAIL=YOUR-EMAIL@networkninja.com
4+
JIRA_USERNAME=YOUR-EMAIL@networkninja.com
5+
JIRA_API_TOKEN="YOUR-API-TOKEN"
6+
JIRA_TOKEN="YOUR-API-TOKEN"
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
from mcp.server.fastmcp import FastMCP
2+
import os
3+
import sys
4+
import requests
5+
import json
6+
from datetime import datetime
7+
import argparse
8+
9+
# Initialize FastMCP server
10+
mcp = FastMCP("sentry_issue_manager")
11+
12+
# Parse command-line arguments
13+
parser = argparse.ArgumentParser(description="Sentry Issue Manager MCP Server")
14+
parser.add_argument('--sentry_auth_token', required=True, help="Sentry authentication token")
15+
parser.add_argument('--sentry_organization_slug', required=True, help="Sentry organization slug")
16+
parser.add_argument('--sentry_organization_id', required=True, help="Sentry organization ID")
17+
args = parser.parse_args()
18+
19+
# Use the parsed arguments
20+
AUTH_TOKEN = args.sentry_auth_token
21+
ORGANIZATION_SLUG = args.sentry_organization_slug
22+
ORGANIZATION_ID = args.sentry_organization_id
23+
24+
# check that the parser arguments are set
25+
if not AUTH_TOKEN or not ORGANIZATION_SLUG or not ORGANIZATION_ID:
26+
print("Error: Sentry authentication token, organization slug, and organization ID must be provided.")
27+
# write to ./logs.txt
28+
with open("logs.txt", "a") as log_file:
29+
log_file.write("Error: Sentry authentication token, organization slug, and organization ID must be provided.\n")
30+
# exit the program
31+
sys.exit(1)
32+
33+
@mcp.tool()
34+
async def sentry_list_projects(cursor: str = None) -> str:
35+
"""List all projects available to the authenticated session.
36+
37+
Args:
38+
cursor: Optional. A pointer to the last object fetched and its sort order;
39+
used to retrieve the next or previous results.
40+
41+
Returns:
42+
str: Formatted list of available Sentry projects
43+
"""
44+
url = "https://sentry.io/api/0/projects/"
45+
headers = {
46+
"Authorization": f"Bearer {AUTH_TOKEN}"
47+
}
48+
49+
params = {}
50+
if cursor:
51+
params["cursor"] = cursor
52+
53+
try:
54+
response = requests.get(url, headers=headers, params=params)
55+
response.raise_for_status() # Raise an exception for bad status codes
56+
57+
try:
58+
projects = response.json()
59+
except json.JSONDecodeError:
60+
return "Error: Unable to parse JSON response"
61+
62+
if not projects:
63+
return "No projects found."
64+
65+
result = "Available Projects:\n"
66+
for project in projects:
67+
org = project.get('organization', {})
68+
org_slug = org.get('slug', 'Unknown')
69+
org_name = org.get('name', 'Unknown')
70+
project_name = project.get('name', 'Unnamed')
71+
project_slug = project.get('slug', 'unknown')
72+
project_id = project.get('id', 'unknown')
73+
platform = project.get('platform', 'Unknown')
74+
75+
result += f"Organization: {org_name} ({org_slug}) | "
76+
result += f"Project: {project_name} (ID: {project_id}, slug: {project_slug}) | "
77+
result += f"Platform: {platform}\n"
78+
79+
# Add pagination information if available
80+
links = response.headers.get('Link')
81+
if links and 'cursor=' in links:
82+
result += "\n(Use the cursor parameter for pagination to see more results)"
83+
84+
return result
85+
except requests.RequestException as e:
86+
return f"Error retrieving projects: {str(e)}"
87+
except Exception as e:
88+
return f"Exception occurred: {str(e)}"
89+
90+
91+
@mcp.tool()
92+
async def sentry_list_project_issues(organization_slug: str, project_slug: str, limit: int = 10) -> str:
93+
"""List recent issues from a specific Sentry project.
94+
Args:
95+
organization_slug: Slug of the Sentry organization
96+
project_slug: Slug of the Sentry project
97+
limit: Maximum number of issues to return (default: 10)
98+
Returns:
99+
str: List of issues with their details for the specified project
100+
"""
101+
url = f"https://sentry.io/api/0/projects/{organization_slug}/{project_slug}/issues/"
102+
headers = {
103+
"Authorization": f"Bearer {AUTH_TOKEN}"
104+
}
105+
params = {"limit": limit}
106+
107+
try:
108+
response = requests.get(url, headers=headers, params=params)
109+
response.raise_for_status() # Raises an HTTPError for bad responses
110+
111+
issues = response.json()
112+
if not issues:
113+
return f"No issues found for project {project_slug}."
114+
115+
result = f"Recent Issues for {project_slug}:\n\n"
116+
for issue in issues:
117+
result += f"ID: {issue['id']}\n"
118+
result += f"Title: {issue['title'][:100]}...\n" # Truncate long titles
119+
result += f"Type: {issue['type']}\n"
120+
result += f"Level: {issue['level']}\n"
121+
result += f"Status: {issue['status']}\n"
122+
result += f"First Seen: {datetime.fromisoformat(issue['firstSeen'][:-1]).strftime('%Y-%m-%d %H:%M:%S')}\n"
123+
result += f"Last Seen: {datetime.fromisoformat(issue['lastSeen'][:-1]).strftime('%Y-%m-%d %H:%M:%S')}\n"
124+
result += f"Count: {issue['count']}\n"
125+
result += f"User Count: {issue['userCount']}\n"
126+
result += f"Culprit: {issue['culprit']}\n"
127+
result += f"Priority: {issue['priority']}\n"
128+
result += f"Permalink: {issue['permalink']}\n"
129+
result += "\n"
130+
131+
return result
132+
133+
except requests.RequestException as e:
134+
return f"Error retrieving project issues: {str(e)}"
135+
except Exception as e:
136+
return f"Exception occurred: {str(e)}"
137+
138+
@mcp.tool()
139+
async def sentry_get_issue_details(issue_id: str) -> str:
140+
"""Get detailed information about a specific issue.
141+
Args:
142+
issue_id: ID of the Sentry issue to retrieve
143+
Returns:
144+
str: Issue details in formatted text
145+
"""
146+
url = f"https://sentry.io/api/0/issues/{issue_id}/"
147+
headers = {
148+
"Authorization": f"Bearer {AUTH_TOKEN}"
149+
}
150+
151+
try:
152+
response = requests.get(url, headers=headers)
153+
if response.status_code == 200:
154+
issue_data = response.json()
155+
return (f"Issue Details:\n"
156+
f"Title: {issue_data['title']}\n"
157+
f"Status: {issue_data['status']}\n"
158+
f"Level: {issue_data['level']}\n"
159+
f"First Seen: {issue_data['firstSeen']}\n"
160+
f"Last Seen: {issue_data['lastSeen']}\n"
161+
f"Count: {issue_data['count']}")
162+
else:
163+
return f"Error retrieving issue details: {response.status_code}\n{response.text}"
164+
except Exception as e:
165+
return f"Exception occurred: {str(e)}"
166+
167+
@mcp.tool()
168+
async def sentry_list_all_org_issues(limit: int = 10) -> str:
169+
"""List recent issues from your Sentry organization.
170+
Args:
171+
limit: Maximum number of issues to return (default: 10)
172+
Returns:
173+
str: List of issues with their IDs and titles
174+
"""
175+
url = f"https://sentry.io/api/0/organizations/{ORGANIZATION_ID}/issues/"
176+
headers = {
177+
"Authorization": f"Bearer {AUTH_TOKEN}"
178+
}
179+
params = {"limit": limit}
180+
181+
try:
182+
response = requests.get(url, headers=headers, params=params)
183+
if response.status_code == 200:
184+
issues = response.json()
185+
if not issues:
186+
return "No issues found."
187+
188+
result = "Recent Issues:\n"
189+
for issue in issues:
190+
result += f"ID: {issue['id']} - {issue['title']}\n"
191+
return result
192+
else:
193+
return f"Error retrieving issues: {response.status_code}\n{response.text}"
194+
except Exception as e:
195+
return f"Exception occurred: {str(e)}"
196+
197+
if __name__ == "__main__":
198+
try:
199+
mcp.run(transport='stdio')
200+
except Exception as e:
201+
print(f"Error starting server: {e}")
202+
sys.exit(1)

0 commit comments

Comments
 (0)