Add Sentry triage agent & MCP - #6705
Draft
jpetto wants to merge 1 commit into
Draft
Community-TC Integration / bugbug lint
failed
Aug 24, 2026 in 2m 17s
Community-TC (pull_request)
bugbug lint
Details
View task in Taskcluster | View logs in Taskcluster | View task group in Taskcluster
Task Status
Started: 2026-08-24T21:12:01.117Z
Resolved: 2026-08-24T21:14:17.632Z
Task Execution Time: 2 minutes, 16 seconds, 515 milliseconds
Task Status: failed
Reason Resolved: failed
TaskId: ceUHvGAwQLKl9Sftm8byQQ
RunId: 0
Artifacts
- public/logs/live_backing.log (1.4 MB)
- public/logs/live.log
[taskcluster 2026-08-24T21:12:01.200Z] Worker Type (proj-bugbug/batch) settings:
[taskcluster 2026-08-24T21:12:01.200Z] {
[taskcluster 2026-08-24T21:12:01.200Z] "generic-worker": {
[taskcluster 2026-08-24T21:12:01.200Z] "config": {
[taskcluster 2026-08-24T21:12:01.200Z] "headlessTasks": false
[taskcluster 2026-08-24T21:12:01.200Z] },
[taskcluster 2026-08-24T21:12:01.200Z] "engine": "multiuser",
[taskcluster 2026-08-24T21:12:01.200Z] "go-arch": "amd64",
[taskcluster 2026-08-24T21:12:01.200Z] "go-os": "linux",
[taskcluster 2026-08-24T21:12:01.200Z] "go-version": "go1.26.2",
[taskcluster 2026-08-24T21:12:01.200Z] "release": "https://github.com/taskcluster/taskcluster/releases/tag/v99.1.0",
[taskcluster 2026-08-24T21:12:01.200Z] "revision": "c76d61efe4bdc1a05bcec848739cd41ebf061f01",
[taskcluster 2026-08-24T21:12:01.200Z] "source": "https://github.com/taskcluster/taskcluster/commits/c76d61efe4bdc1a05bcec848739cd41ebf061f01",
[taskcluster 2026-08-24T21:12:01.200Z] "version": "99.1.0"
[taskcluster 2026-08-24T21:12:01.200Z] },
[taskcluster 2026-08-24T21:12:01.200Z] "image": "projects/community-tc-workers/global/images/generic-worker-ubuntu-24-04-aaynirqnxlbwcojgnbeo",
[taskcluster 2026-08-24T21:12:01.200Z] "instance-id": "6376971069628157056",
[taskcluster 2026-08-24T21:12:01.200Z] "instance-type": "projects/757942385826/machineTypes/n2-standard-2",
[taskcluster 2026-08-24T21:12:01.200Z] "local-ipv4": "10.142.0.28",
[taskcluster 2026-08-24T21:12:01.200Z] "machine-setup": {
...(101 lines hidden)...
Found 4 errors (4 fixed, 0 remaining).
ruff format..............................................................Failed
- hook id: ruff-format
- files were modified by this hook
warning: Selection `CPY` has no effect because preview is not enabled.
3 files reformatted, 449 files left unchanged
check python ast.........................................................Passed
check docstring is first.................................................Passed
check that executables have shebangs.....................................Passed
check for merge conflicts................................................Passed
check for broken symlinks............................(no files to check)Skipped
debug statements (python)................................................Passed
trim trailing whitespace.................................................Passed
check yaml...............................................................Passed
mixed line ending........................................................Passed
python tests naming......................................................Passed
check json...............................................................Passed
check vcs permalinks.....................................................Passed
codespell................................................................Passed
taskcluster_yml..........................................................Passed
Strip unnecessary `# noqa`s..............................................Passed
mypy-bugbug..............................................................Passed
mypy-bugbug-http.........................................................Passed
Check for useless excludes...............................................Passed
pre-commit hook(s) made changes.
If you are seeing this message in CI, reproduce locally with: `pre-commit run --all-files`.
To run `pre-commit` as part of git workflow, use `pre-commit install`.
All changes made by hooks:
diff --git a/agents/sentry-triage/hackbot_agents/sentry_triage/__main__.py b/agents/sentry-triage/hackbot_agents/sentry_triage/__main__.py
index 0c1b2c01..8e0f1bca 100644
--- a/agents/sentry-triage/hackbot_agents/sentry_triage/__main__.py
+++ b/agents/sentry-triage/hackbot_agents/sentry_triage/__main__.py
@@ -10,7 +10,7 @@ class AgentInputs(BaseSettings):
model: str | None = None
max_turns: int | None = None
effort: str | None = None
-
+
model_config = SettingsConfigDict(extra="ignore")
diff --git a/agents/sentry-triage/hackbot_agents/sentry_triage/agent.py b/agents/sentry-triage/hackbot_agents/sentry_triage/agent.py
index 92cc033b..643e73f4 100644
--- a/agents/sentry-triage/hackbot_agents/sentry_triage/agent.py
+++ b/agents/sentry-triage/hackbot_agents/sentry_triage/agent.py
@@ -69,7 +69,9 @@ def make_alert_investigator() -> AgentDefinition:
)
-def parse_sentry_alert_url_for_issue_and_event_ids(sentry_alert_url: str) -> tuple[str, str]:
+def parse_sentry_alert_url_for_issue_and_event_ids(
+ sentry_alert_url: str,
+) -> tuple[str, str]:
"""Parses a Sentry alert URL for the issue and event ids."""
# TODO: parse the URL for issue_id and event_id
# e.g. https://mozilla.sentry.io/issues/7325411784/events/16f37d8003bb42f4abff707fb57bfca0/?alert=5775&detection_type=static¬ification_uuid=a672e1f5-d561-4f2d-92f4-441dd86e8fd2&openPeriod=589904722&project=4510958917713920&referrer=metric_alert_slack&statsPeriod=12d
@@ -88,19 +90,20 @@ async def run_alert_triage(
effort: str | None = None,
verbose: bool = False,
log: Path | None = None,
- actions_recorder: ActionsRecorder | None = None
+ actions_recorder: ActionsRecorder | None = None,
) -> SentryTriageResult:
"""Triage a single Sentry alert with a claude-agent-sdk agent.
Returns a :class:`SentryTriageResult` on success; raises :class:`AgentError` if the
agent ends in an error.
"""
-
# load custom claude rules
if rules_dir is None:
rules_dir = HERE / "rules"
- print(f"[sentry_triage] triaging sentry alert at {sentry_alert_url}", file=sys.stderr)
+ print(
+ f"[sentry_triage] triaging sentry alert at {sentry_alert_url}", file=sys.stderr
+ )
# Action-recording MCP server (in-process). Standalone/script runs pass
# actions_recorder=None and get a local recorder that copies attachments
@@ -109,12 +112,19 @@ async def run_alert_triage(
actions_recorder, types=TRIAGE_ACTIONS
)
- sentry_issue_id, sentry_event_id = parse_sentry_alert_url_for_issue_and_event_ids(sentry_alert_url)
+ sentry_issue_id, sentry_event_id = parse_sentry_alert_url_for_issue_and_event_ids(
+ sentry_alert_url
+ )
enabled_action_tools = actions_to_tool_names(TRIAGE_ACTIONS)
system_prompt = render_prompt("system.md", rules_dir=str(rules_dir.resolve()))
- user_prompt = render_prompt("triage.md", issue_id=sentry_issue_id, event_id=sentry_event_id, rules_path=rules_dir)
+ user_prompt = render_prompt(
+ "triage.md",
+ issue_id=sentry_issue_id,
+ event_id=sentry_event_id,
+ rules_path=rules_dir,
+ )
options = ClaudeAgentOptions(
add_dirs=[str(rules_dir.resolve())],
@@ -155,7 +165,9 @@ async def run_alert_triage(
result_msg = msg
if result_msg is None:
- raise AgentError(f"sentry alert url {sentry_alert_url}: agent produced no result message")
+ raise AgentError(
+ f"sentry alert url {sentry_alert_url}: agent produced no result message"
+ )
if result_msg.is_error:
raise AgentError(
diff --git a/agents/sentry-triage/hackbot_agents/sentry_triage/broker.py b/agents/sentry-triage/hackbot_agents/sentry_triage/broker.py
index 7987f6df..7e5bc781 100644
--- a/agents/sentry-triage/hackbot_agents/sentry_triage/broker.py
+++ b/agents/sentry-triage/hackbot_agents/sentry_triage/broker.py
@@ -8,10 +8,10 @@ no credentials:
- Sentry: the `Sentry` MCP tools over `/mcp` (read-only, live during the run).
"""
-import httpx
import logging
from contextlib import asynccontextmanager
+import httpx
import uvicorn
from agent_tools import sentry
from agent_tools.claude_sdk import build_sdk_server
diff --git a/libs/agent-tools/agent_tools/sentry.py b/libs/agent-tools/agent_tools/sentry.py
index 794f2c82..3ee98dfe 100644
--- a/libs/agent-tools/agent_tools/sentry.py
+++ b/libs/agent-tools/agent_tools/sentry.py
@@ -1,9 +1,10 @@
+from dataclasses import dataclass
+from typing import Annotated
+
import httpx
+from pydantic import Field
from agent_tools.registry import ToolError, tool, tools_in
-from dataclasses import dataclass
-from pydantic import Field
-from typing import Annotated, Any
@dataclass
@@ -24,21 +25,28 @@ def _sentry_error(e: Exception, what: str, http_status_error: str) -> ToolError:
@tool
async def get_issue_event(
- ctx: SentryContext,
- issue_id: Annotated[
- str, Field(description="The Sentry issue ID.")
- ],event_id: str):
+ ctx: SentryContext,
+ issue_id: Annotated[str, Field(description="The Sentry issue ID.")],
+ event_id: str,
+):
"""Retrieve an issue event from Sentry."""
try:
- headers = { "Authorization": f"Bearer {ctx.api_token}" }
- resp = await ctx.client.get(f"{ctx.api_url_base}/organizations/{ctx.org_name}/issues/{issue_id}/events/{event_id}/", headers=headers)
+ headers = {"Authorization": f"Bearer {ctx.api_token}"}
+ resp = await ctx.client.get(
+ f"{ctx.api_url_base}/organizations/{ctx.org_name}/issues/{issue_id}/events/{event_id}/",
+ headers=headers,
+ )
resp.raise_for_status()
return resp.json()
except httpx.HTTPStatusError as e:
# get the http error, falling back to a generic "sentry_error"
- http_status_error = {401: "auth_failed", 403: "access_denied", 404: "not_found"}.get(e.response.status_code, "sentry_error")
+ http_status_error = {
+ 401: "auth_failed",
+ 403: "access_denied",
+ 404: "not_found",
+ }.get(e.response.status_code, "sentry_error")
raise _sentry_error(e, "get_issue_event error", http_status_error) from e
-TOOLS = tools_in(__name__)
\ No newline at end of file
+TOOLS = tools_in(__name__)
[taskcluster 2026-08-24T21:14:15.604Z] Exit Code: 1
[taskcluster 2026-08-24T21:14:15.604Z] User Time: 49.268ms
[taskcluster 2026-08-24T21:14:15.604Z] Kernel Time: 45.163ms
[taskcluster 2026-08-24T21:14:15.604Z] Wall Time: 2m11.645465025s
[taskcluster 2026-08-24T21:14:15.604Z] Average Available System Memory: 6.40 GiB
[taskcluster 2026-08-24T21:14:15.604Z] Average System Memory Used: 1.35 GiB
[taskcluster 2026-08-24T21:14:15.604Z] Peak System Memory Used: 1.71 GiB
[taskcluster 2026-08-24T21:14:15.604Z] Total System Memory: 7.75 GiB
[taskcluster 2026-08-24T21:14:15.604Z] Result: FAILED
[taskcluster 2026-08-24T21:14:15.604Z] === Task Finished ===
[taskcluster 2026-08-24T21:14:15.604Z] Task Duration: 2m11.646288791s
[taskcluster:error] exit status 1
Loading