iflow-mcp_mcp-100_mcp-sentry 0.6.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,229 @@
1
+ Metadata-Version: 2.4
2
+ Name: iflow-mcp_mcp-100_mcp-sentry
3
+ Version: 0.6.8
4
+ Summary: MCP server for retrieving issues from sentry.io
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: mcp>=1.0.0
8
+
9
+
10
+ # mcp-sentry: A Sentry MCP server
11
+
12
+ [![smithery badge](https://smithery.ai/badge/@qianniuspace/mcp-sentry)](https://smithery.ai/server/@qianniuspace/mcp-sentry)
13
+
14
+ ## Overview
15
+
16
+ A Model Context Protocol server for retrieving and analyzing issues from Sentry.io. This server provides tools to inspect error reports, stacktraces, and other debugging information from your Sentry account.
17
+
18
+ ### Tools
19
+
20
+ 1. `get_sentry_issue`
21
+ - Retrieve and analyze a Sentry issue by ID or URL
22
+ - Input:
23
+ - `issue_id_or_url` (string): Sentry issue ID or URL to analyze
24
+ - Returns: Issue details including:
25
+ - Title
26
+ - Issue ID
27
+ - Status
28
+ - Level
29
+ - First seen timestamp
30
+ - Last seen timestamp
31
+ - Event count
32
+ - Full stacktrace
33
+ 2. `get_list_issues`
34
+ - Retrieve and analyze Sentry issues by project slug
35
+ - Input:
36
+ - `project_slug` (string): Sentry project slug to analyze
37
+ - `organization_slug` (string): Sentry organization slug to analyze
38
+ - Returns: List of issues with details including:
39
+ - Title
40
+ - Issue ID
41
+ - Status
42
+ - Level
43
+ - First seen timestamp
44
+ - Last seen timestamp
45
+ - Event count
46
+ - Basic issue information
47
+
48
+ ### Prompts
49
+
50
+ 1. `sentry-issue`
51
+ - Retrieve issue details from Sentry
52
+ - Input:
53
+ - `issue_id_or_url` (string): Sentry issue ID or URL
54
+ - Returns: Formatted issue details as conversation context
55
+
56
+ ## Installation
57
+
58
+ ### Installing via Smithery
59
+
60
+ To install mcp-sentry for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@qianniuspace/mcp-sentry):
61
+
62
+ ```bash
63
+ npx -y @smithery/cli install @qianniuspace/mcp-sentry --client claude
64
+ ```
65
+
66
+ ### Using uv (recommended)
67
+
68
+ When using [`uv`](https://docs.astral.sh/uv/) no specific installation is needed. We will
69
+ use [`uvx`](https://docs.astral.sh/uv/guides/tools/) to directly run *mcp-sentry*.
70
+
71
+ ### Using PIP
72
+
73
+ Alternatively you can install `mcp-sentry` via pip:
74
+
75
+ ```
76
+ pip install mcp-sentry
77
+ ```
78
+
79
+ or use uv
80
+ ```
81
+ uv pip install -e .
82
+ ```
83
+
84
+ After installation, you can run it as a script using:
85
+
86
+ ```
87
+ python -m mcp_sentry
88
+ ```
89
+
90
+ ## Configuration
91
+
92
+ ### Usage with Claude Desktop
93
+
94
+ Add this to your `claude_desktop_config.json`:
95
+
96
+ <details>
97
+ <summary>Using uvx</summary>
98
+
99
+ ```json
100
+ "mcpServers": {
101
+ "sentry": {
102
+ "command": "uvx",
103
+ "args": ["mcp-sentry", "--auth-token", "YOUR_SENTRY_TOKEN","--project-slug" ,"YOUR_PROJECT_SLUG", "--organization-slug","YOUR_ORGANIZATION_SLUG"]
104
+ }
105
+ }
106
+ ```
107
+ </details>
108
+
109
+
110
+ <details>
111
+ <summary>Using docker</summary>
112
+
113
+ ```json
114
+ "mcpServers": {
115
+ "sentry": {
116
+ "command": "docker",
117
+ "args": ["run", "-i", "--rm", "mcp/sentry", "--auth-token", "YOUR_SENTRY_TOKEN","--project-slug" ,"YOUR_PROJECT_SLUG", "--organization-slug","YOUR_ORGANIZATION_SLUG"]
118
+ }
119
+ }
120
+ ```
121
+ </details>
122
+
123
+ <details>
124
+
125
+ <summary>Using pip installation</summary>
126
+
127
+ ```json
128
+ "mcpServers": {
129
+ "sentry": {
130
+ "command": "python",
131
+ "args": ["-m", "mcp_sentry", "--auth-token", "YOUR_SENTRY_TOKEN","--project-slug" ,"YOUR_PROJECT_SLUG", "--organization-slug","YOUR_ORGANIZATION_SLUG"]
132
+ }
133
+ }
134
+ ```
135
+ </details>
136
+
137
+ ### Usage with [Zed](https://github.com/zed-industries/zed)
138
+
139
+ Add to your Zed settings.json:
140
+
141
+ <details>
142
+ <summary>Using uvx</summary>
143
+
144
+ For Example Curson ![mcp.json](.cursor/mcp.json)
145
+
146
+ ```json
147
+ "context_servers": [
148
+ "mcp-sentry": {
149
+ "command": {
150
+ "path": "uvx",
151
+ "args": ["mcp-sentry", "--auth-token", "YOUR_SENTRY_TOKEN","--project-slug" ,"YOUR_PROJECT_SLUG", "--organization-slug","YOUR_ORGANIZATION_SLUG"]
152
+ }
153
+ }
154
+ ],
155
+ ```
156
+ </details>
157
+
158
+ <details>
159
+ <summary>Using pip installation</summary>
160
+
161
+ ```json
162
+ "context_servers": {
163
+ "mcp-sentry": {
164
+ "command": "python",
165
+ "args": ["-m", "mcp_sentry", "--auth-token", "YOUR_SENTRY_TOKEN","--project-slug" ,"YOUR_PROJECT_SLUG", "--organization-slug","YOUR_ORGANIZATION_SLUG"]
166
+ }
167
+ },
168
+ ```
169
+ </details>
170
+
171
+ <details>
172
+ <summary>Using pip installation with custom path</summary>
173
+
174
+ ```json
175
+ "context_servers": {
176
+ "sentry": {
177
+ "command": "python",
178
+ "args": [
179
+ "-m",
180
+ "mcp_sentry",
181
+ "--auth-token",
182
+ "YOUR_SENTRY_TOKEN",
183
+ "--project-slug",
184
+ "YOUR_PROJECT_SLUG",
185
+ "--organization-slug",
186
+ "YOUR_ORGANIZATION_SLUG"
187
+ ],
188
+ "env": {
189
+ "PYTHONPATH": "path/to/mcp-sentry/src"
190
+ }
191
+ }
192
+ },
193
+ ```
194
+
195
+
196
+ </details>
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+ ## Debugging
205
+
206
+ You can use the MCP inspector to debug the server. For uvx installations:
207
+
208
+ ```
209
+ npx @modelcontextprotocol/inspector uvx mcp-sentry --auth-token YOUR_SENTRY_TOKEN --project-slug YOUR_PROJECT_SLUG --organization-slug YOUR_ORGANIZATION_SLUG
210
+ ```
211
+
212
+ Or if you've installed the package in a specific directory or are developing on it:
213
+
214
+ ```
215
+ cd path/to/servers/src/sentry
216
+ npx @modelcontextprotocol/inspector uv run mcp-sentry --auth-token YOUR_SENTRY_TOKEN --project-slug YOUR_PROJECT_SLUG --organization-slug YOUR_ORGANIZATION_SLUG
217
+ ```
218
+ or in term
219
+ ```
220
+ npx @modelcontextprotocol/inspector uv --directory /Volumes/ExtremeSSD/MCP/mcp-sentry/src run mcp_sentry --auth-token YOUR_SENTRY_TOKEN
221
+ --project-slug YOUR_PROJECT_SLUG --organization-slug YOUR_ORGANIZATION_SLUG
222
+ ```
223
+ ![Inspector-tools](./images/Inspector-tools.png)
224
+
225
+ ## Fork From
226
+ - [https://github.com/modelcontextprotocol/servers/tree/main/src/sentr](https://github.com/modelcontextprotocol/servers/tree/main/src/sentry)
227
+ ## License
228
+
229
+ This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.
@@ -0,0 +1,8 @@
1
+ mcp_sentry/__init__.py,sha256=v2UpkufK6E6__uq_OETVdq-g9lYQqtNyAVL4nFhTt_k,215
2
+ mcp_sentry/__main__.py,sha256=Rm8eUEcid2134EYX38mk5QlFHaxNlbkaYb4teFzywz8,74
3
+ mcp_sentry/server.py,sha256=nM8CHylypUhBQilKgq9pKguMJ_ocSquqxN1G9yA9J5Y,13371
4
+ iflow_mcp_mcp_100_mcp_sentry-0.6.8.dist-info/METADATA,sha256=kz0Diaa-CEHVIuK6zmRH8SGJvBEsNda8Z6sadc3HKsU,5721
5
+ iflow_mcp_mcp_100_mcp_sentry-0.6.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ iflow_mcp_mcp_100_mcp_sentry-0.6.8.dist-info/entry_points.txt,sha256=xPJx0xk9fEbYCU93cJRsKyNSV9UEn1JdO2JBWzZCU14,47
7
+ iflow_mcp_mcp_100_mcp_sentry-0.6.8.dist-info/top_level.txt,sha256=ohhxR0Qw654eZ0gW6u9wO7CTPxaLzF1HVmHh_cPXNF4,11
8
+ iflow_mcp_mcp_100_mcp_sentry-0.6.8.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mcp-sentry = mcp_sentry:main
@@ -0,0 +1 @@
1
+ mcp_sentry
mcp_sentry/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ from . import server
2
+ import asyncio
3
+
4
+
5
+ def main():
6
+ """Main entry point for the package."""
7
+ asyncio.run(server.main())
8
+
9
+
10
+ # Optionally expose other important items at package level
11
+ __all__ = ["main", "server"]
mcp_sentry/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from mcp_sentry.server import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
mcp_sentry/server.py ADDED
@@ -0,0 +1,382 @@
1
+ import asyncio
2
+ from dataclasses import dataclass
3
+ from urllib.parse import urlparse
4
+
5
+ import click
6
+ import httpx
7
+ import mcp.types as types
8
+ from mcp.server import NotificationOptions, Server
9
+ from mcp.server.models import InitializationOptions
10
+ from mcp.shared.exceptions import McpError
11
+ import mcp.server.stdio
12
+
13
+ SENTRY_API_BASE = "https://sentry.io/api/0/"
14
+ MISSING_AUTH_TOKEN_MESSAGE = (
15
+ """Sentry authentication token not found. Please specify your Sentry auth token."""
16
+ )
17
+
18
+
19
+ @dataclass
20
+ class SentryIssueData:
21
+ title: str
22
+ issue_id: str
23
+ status: str
24
+ level: str
25
+ first_seen: str
26
+ last_seen: str
27
+ count: int
28
+ stacktrace: str
29
+
30
+ def to_text(self) -> str:
31
+ return f"""
32
+ Sentry Issue: {self.title}
33
+ Issue ID: {self.issue_id}
34
+ Status: {self.status}
35
+ Level: {self.level}
36
+ First Seen: {self.first_seen}
37
+ Last Seen: {self.last_seen}
38
+ Event Count: {self.count}
39
+
40
+ {self.stacktrace}
41
+ """
42
+
43
+ def to_prompt_result(self) -> types.GetPromptResult:
44
+ return types.GetPromptResult(
45
+ description=f"Sentry Issue: {self.title}",
46
+ messages=[
47
+ types.PromptMessage(
48
+ role="user", content=types.TextContent(type="text", text=self.to_text())
49
+ )
50
+ ],
51
+ )
52
+
53
+ def to_tool_result(self) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
54
+ return [types.TextContent(type="text", text=self.to_text())]
55
+
56
+
57
+ class SentryError(Exception):
58
+ pass
59
+
60
+
61
+ def extract_issue_id(issue_id_or_url: str) -> str:
62
+ """
63
+ Extracts the Sentry issue ID from either a full URL or a standalone ID.
64
+
65
+ This function validates the input and returns the numeric issue ID.
66
+ It raises SentryError for invalid inputs, including empty strings,
67
+ non-Sentry URLs, malformed paths, and non-numeric IDs.
68
+ """
69
+ if not issue_id_or_url:
70
+ raise SentryError("Missing issue_id_or_url argument")
71
+
72
+ if issue_id_or_url.startswith(("http://", "https://")):
73
+ parsed_url = urlparse(issue_id_or_url)
74
+ if not parsed_url.hostname or not parsed_url.hostname.endswith(".sentry.io"):
75
+ raise SentryError("Invalid Sentry URL. Must be a URL ending with .sentry.io")
76
+
77
+ path_parts = parsed_url.path.strip("/").split("/")
78
+ if len(path_parts) < 2 or path_parts[0] != "issues":
79
+ raise SentryError(
80
+ "Invalid Sentry issue URL. Path must contain '/issues/{issue_id}'"
81
+ )
82
+
83
+ issue_id = path_parts[-1]
84
+ else:
85
+ issue_id = issue_id_or_url
86
+
87
+ if not issue_id.isdigit():
88
+ raise SentryError("Invalid Sentry issue ID. Must be a numeric value.")
89
+
90
+ return issue_id
91
+
92
+
93
+ def create_stacktrace(latest_event: dict) -> str:
94
+ """
95
+ Creates a formatted stacktrace string from the latest Sentry event.
96
+
97
+ This function extracts exception information and stacktrace details from the
98
+ provided event dictionary, formatting them into a human-readable string.
99
+ It handles multiple exceptions and includes file, line number, and function
100
+ information for each frame in the stacktrace.
101
+
102
+ Args:
103
+ latest_event (dict): A dictionary containing the latest Sentry event data.
104
+
105
+ Returns:
106
+ str: A formatted string containing the stacktrace information,
107
+ or "No stacktrace found" if no relevant data is present.
108
+ """
109
+ stacktraces = []
110
+ for entry in latest_event.get("entries", []):
111
+ if entry["type"] != "exception":
112
+ continue
113
+
114
+ exception_data = entry["data"]["values"]
115
+ for exception in exception_data:
116
+ exception_type = exception.get("type", "Unknown")
117
+ exception_value = exception.get("value", "")
118
+ stacktrace = exception.get("stacktrace")
119
+
120
+ stacktrace_text = f"Exception: {exception_type}: {exception_value}\n\n"
121
+ if stacktrace:
122
+ stacktrace_text += "Stacktrace:\n"
123
+ for frame in stacktrace.get("frames", []):
124
+ filename = frame.get("filename", "Unknown")
125
+ lineno = frame.get("lineNo", "?")
126
+ function = frame.get("function", "Unknown")
127
+
128
+ stacktrace_text += f"{filename}:{lineno} in {function}\n"
129
+
130
+ if "context" in frame:
131
+ context = frame["context"]
132
+ for ctx_line in context:
133
+ stacktrace_text += f" {ctx_line[1]}\n"
134
+
135
+ stacktrace_text += "\n"
136
+
137
+ stacktraces.append(stacktrace_text)
138
+
139
+ return "\n".join(stacktraces) if stacktraces else "No stacktrace found"
140
+
141
+
142
+ async def handle_sentry_issue(
143
+ http_client: httpx.AsyncClient, auth_token: str, issue_id_or_url: str
144
+ ) -> SentryIssueData:
145
+ try:
146
+ issue_id = extract_issue_id(issue_id_or_url)
147
+
148
+ response = await http_client.get(
149
+ f"issues/{issue_id}/", headers={"Authorization": f"Bearer {auth_token}"}
150
+ )
151
+ if response.status_code == 401:
152
+ raise McpError(
153
+ "Error: Unauthorized. Please check your MCP_SENTRY_AUTH_TOKEN token."
154
+ )
155
+ response.raise_for_status()
156
+ issue_data = response.json()
157
+
158
+ # Get issue hashes
159
+ hashes_response = await http_client.get(
160
+ f"issues/{issue_id}/hashes/",
161
+ headers={"Authorization": f"Bearer {auth_token}"},
162
+ )
163
+ hashes_response.raise_for_status()
164
+ hashes = hashes_response.json()
165
+
166
+ if not hashes:
167
+ raise McpError("No Sentry events found for this issue")
168
+
169
+ latest_event = hashes[0]["latestEvent"]
170
+ stacktrace = create_stacktrace(latest_event)
171
+
172
+ return SentryIssueData(
173
+ title=issue_data["title"],
174
+ issue_id=issue_id,
175
+ status=issue_data["status"],
176
+ level=issue_data["level"],
177
+ first_seen=issue_data["firstSeen"],
178
+ last_seen=issue_data["lastSeen"],
179
+ count=issue_data["count"],
180
+ stacktrace=stacktrace
181
+ )
182
+
183
+ except SentryError as e:
184
+ raise McpError(str(e))
185
+ except httpx.HTTPStatusError as e:
186
+ raise McpError(f"Error fetching Sentry issue: {str(e)}")
187
+ except Exception as e:
188
+ raise McpError(f"An error occurred: {str(e)}")
189
+
190
+
191
+ async def handle_list_issues(
192
+ http_client: httpx.AsyncClient,
193
+ auth_token: str,
194
+ project_slug: str,
195
+ organization_slug: str
196
+ ) -> list[SentryIssueData]:
197
+ try:
198
+ response = await http_client.get(
199
+ f"projects/{organization_slug}/{project_slug}/issues/",
200
+ headers={"Authorization": f"Bearer {auth_token}"}
201
+ )
202
+ if response.status_code == 401:
203
+ raise McpError(
204
+ "Error: Unauthorized. Please check your MCP_SENTRY_AUTH_TOKEN token."
205
+ )
206
+ response.raise_for_status()
207
+ issues_data = response.json()
208
+
209
+ result = []
210
+ for issue in issues_data:
211
+ result.append(SentryIssueData(
212
+ title=issue["title"],
213
+ issue_id=issue["id"],
214
+ status=issue["status"],
215
+ level=issue["level"],
216
+ first_seen=issue["firstSeen"],
217
+ last_seen=issue["lastSeen"],
218
+ count=issue["count"],
219
+ stacktrace="Stacktrace not fetched for list view"
220
+ ))
221
+ return result
222
+
223
+ except httpx.HTTPStatusError as e:
224
+ raise McpError(f"Error fetching Sentry issues: {str(e)}")
225
+ except Exception as e:
226
+ raise McpError(f"An error occurred: {str(e)}")
227
+
228
+
229
+ async def serve(auth_token: str, project_slug: str, organization_slug: str) -> Server:
230
+ server = Server("sentry")
231
+ http_client = httpx.AsyncClient(base_url=SENTRY_API_BASE)
232
+
233
+ @server.list_prompts()
234
+ async def handle_list_prompts() -> list[types.Prompt]:
235
+ return [
236
+ types.Prompt(
237
+ name="sentry-issue",
238
+ description="Retrieve a Sentry issue by ID or URL",
239
+ arguments=[
240
+ types.PromptArgument(
241
+ name="issue_id_or_url",
242
+ description="Sentry issue ID or URL",
243
+ required=True,
244
+ )
245
+ ],
246
+ ),
247
+ types.Prompt(
248
+ name="sentry-issues-by-project",
249
+ description="Retrieve Sentry issues by project slug",
250
+ arguments=[
251
+ types.PromptArgument(
252
+ name="project_slug",
253
+ description="Sentry project slug",
254
+ required=True,
255
+ )
256
+ ],
257
+ ),
258
+ ]
259
+
260
+ @server.get_prompt()
261
+ async def handle_get_prompt(
262
+ name: str, arguments: dict[str, str] | None
263
+ ) -> types.GetPromptResult:
264
+ if name != "sentry-issue":
265
+ raise ValueError(f"Unknown prompt: {name}")
266
+
267
+ issue_id_or_url = (arguments or {}).get("issue_id_or_url", "")
268
+ issue_data = await handle_sentry_issue(http_client, auth_token, issue_id_or_url)
269
+ return issue_data.to_prompt_result()
270
+
271
+ @server.list_tools()
272
+ async def handle_list_tools() -> list[types.Tool]:
273
+ return [
274
+ types.Tool(
275
+ name="get_sentry_issue",
276
+ description="""Retrieve and analyze a Sentry issue by ID or URL. Use this tool when you need to:
277
+ - Investigate production errors and crashes
278
+ - Access detailed stacktraces from Sentry
279
+ - Analyze error patterns and frequencies
280
+ - Get information about when issues first/last occurred
281
+ - Review error counts and status""",
282
+ inputSchema={
283
+ "type": "object",
284
+ "properties": {
285
+ "issue_id_or_url": {
286
+ "type": "string",
287
+ "description": "Sentry issue ID or URL to analyze"
288
+ }
289
+ },
290
+ "required": ["issue_id_or_url"]
291
+ }
292
+ ),
293
+ types.Tool(
294
+ name="get_list_issues",
295
+ description="""Retrieve and analyze Sentry issues by project slug. Use this tool when you need to:
296
+ - Investigate production errors and crashes
297
+ - Access detailed stacktraces from Sentry
298
+ - Analyze error patterns and frequencies
299
+ - Get information about when issues first/last occurred
300
+ - Review error counts and status""",
301
+ inputSchema={
302
+ "type": "object",
303
+ "properties": {
304
+ "project_slug": {
305
+ "type": "string",
306
+ "description": "Sentry project slug to analyze"
307
+ },
308
+ "organization_slug": {
309
+ "type": "string",
310
+ "description": "Sentry organization slug to analyze"
311
+ }
312
+ },
313
+ "required": []
314
+ }
315
+ )
316
+ ]
317
+
318
+ @server.call_tool()
319
+ async def handle_call_tool(
320
+ name: str, arguments: dict | None
321
+ ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
322
+ if name == "get_sentry_issue":
323
+ if not arguments or "issue_id_or_url" not in arguments:
324
+ raise ValueError("Missing issue_id_or_url argument")
325
+ issue_data = await handle_sentry_issue(http_client, auth_token, arguments["issue_id_or_url"])
326
+ return issue_data.to_tool_result()
327
+
328
+ elif name == "get_list_issues":
329
+ issues = await handle_list_issues(
330
+ http_client,
331
+ auth_token,
332
+ project_slug,
333
+ organization_slug
334
+ )
335
+ # 将所有issue的信息合并成一个文本返回
336
+ combined_text = "Sentry Issues:\n\n" + "\n---\n".join(
337
+ issue.to_text() for issue in issues
338
+ )
339
+ return [types.TextContent(type="text", text=combined_text)]
340
+
341
+ else:
342
+ raise ValueError(f"Unknown tool: {name}")
343
+
344
+ return server
345
+
346
+ @click.command()
347
+ @click.option(
348
+ "--auth-token",
349
+ envvar="SENTRY_TOKEN",
350
+ required=True,
351
+ help="Sentry authentication token",
352
+ )
353
+ @click.option(
354
+ "--project-slug",
355
+ envvar="SENTRY_PROJECT_SLUG",
356
+ required=True,
357
+ help="Sentry project slug",
358
+ )
359
+ @click.option(
360
+ "--organization-slug",
361
+ envvar="SENTRY_ORGANIZATION_SLUG",
362
+ required=True,
363
+ help="Sentry organization slug",
364
+ )
365
+ def main(auth_token: str, project_slug: str, organization_slug: str):
366
+ async def _run():
367
+ async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
368
+ server = await serve(auth_token, project_slug, organization_slug)
369
+ await server.run(
370
+ read_stream,
371
+ write_stream,
372
+ InitializationOptions(
373
+ server_name="sentry",
374
+ server_version="0.4.1",
375
+ capabilities=server.get_capabilities(
376
+ notification_options=NotificationOptions(),
377
+ experimental_capabilities={},
378
+ ),
379
+ ),
380
+ )
381
+
382
+ asyncio.run(_run())