feat(sentry): full Sentry.io integration#184
Merged
Merged
Conversation
added 5 commits
June 1, 2026 12:43
Adds a full Sentry provider mirroring the existing Cloudflare recipe: internal/sentry package (REST client with cursor pagination, 429 backoff honoring Retry-After + X-Sentry-Rate-Limit-Reset, per-org conversation history) plus `clanker sentry` cobra tree (list / get / resolve / ignore / assign / monitor / alert subcommands) and `clanker sentry ask` for natural-language triage. Self-hosted Sentry is supported via a configurable host field (default sentry.io). The ask agent fetches issues / releases / monitors / alerts based on keyword routing and forwards Sentry search syntax verbatim — no client-side parsing — so operators can use `is:unresolved level:error environment:prod`-style queries directly. MCP tools: clanker_sentry_ask, _list_issues, _get_issue, _resolve_issues, _list_releases. Tests cover the happy path, 429 retry, Link-header cursor parsing, error envelope extraction, PUT ?id=... repeated-key encoding, and per-org history round-trip. Closes nothing — net-new integration.
Five fixes from a fresh-eyes review of the Sentry CLI:
- cmd/sentry.go: rename local `context` so it stops shadowing the
imported context package — compiles today but trips up any future
context.WithTimeout call placed below it.
- internal/sentry/issues.go: URL-escape ids in the ?id=A&id=B query of
UpdateIssues via url.Values{}.Add. An id containing & or = (or a
hostile MCP caller payload) could otherwise inject a status= param
into the PUT.
- internal/sentry/conversation.go: introduce safeSlug() to strip
anything outside [A-Za-z0-9_-] from the org slug before building
~/.clanker/sentry-{slug}.json — closes a path-traversal hole where
`../../etc/passwd` would write outside ~/.clanker because
filepath.Join cleans `..` segments.
- internal/sentry/static_commands.go: introduce sentryFlag() helper
that reads via cmd.Flags() so persistent flags registered on the
`sentry` command (not on rootCmd) are reachable from leaf subcommands
three levels deep. Replaces every cmd.Root().PersistentFlags() call
that silently returned "" at depth.
- renderJSON no longer pretends to honour --format — comment clarifies
intent and `format` parameter is renamed to `_` to make it obvious
to readers.
Test coverage:
- TestUpdateIssues_EscapesMaliciousID exercises an id containing
`&status=resolved`, asserting it round-trips as a single escaped id
and does NOT smuggle a second query parameter.
- TestSafeSlug_BlocksPathTraversal covers `../`, leading `/`, empty
string, and dots-only inputs.
Docs: updated the Sentry scope recommendation in .clanker.example.yaml
to drop `project:releases` (an internal-integration-only scope) in
favour of `project:write`, which is the canonical User Auth Token
scope for release management.
Second-pass review surfaced an SSRF vector via the configured sentry.host: NewClient accepted any value and built a Bearer-token request against it, so a hostile sentry.host config or SENTRY_HOST env var could redirect the auth token at 169.254.169.254 or other internal endpoints. - internal/sentry/client.go: introduce validateHost(). Rejects IP literals (catches 169.254.169.254, ::1, etc.), hostnames containing port/path/userinfo characters (`:` `/` `@` `?` `#`), and a small block-list of cloud-metadata DNS names (localhost, metadata.google.internal, instance-data). DNS hostnames matching `[a-z0-9]([a-z0-9\-]*[a-z0-9])?(\.…)*` continue to work for both sentry.io SaaS and self-hosted installs. - internal/sentry/client_test.go: TestValidateHost_BlocksSSRF covers the allowed and blocked shapes; TestNewClient_RejectsHostileHost asserts the guard surfaces in the constructor, not silently. - cmd/mcp_sentry.go: add WithReadOnlyHintAnnotation to clanker_sentry_ask. The tool only calls ListIssues / ListReleases / ListMonitors / ListIssueAlertRules then routes through the LLM — cautious MCP clients (Claude Desktop's safe-tool list) can now invoke it without user confirmation.
… trim narration
CLI cleanup driven by a third review round.
Perf:
- internal/sentry/status.go: run ListProjects + the two ListIssues calls
concurrently via errgroup. The ask command's cold-start was dominated
by these sequential network round-trips.
- cmd/sentry.go (gatherSentryContext): the up-to-4 selected sections
(issues / releases / monitors / alerts) now fetch in parallel via
errgroup, each writing into its own string block that gets stitched
in fixed order. Worst-case ask latency drops ~4x.
Quality:
- internal/sentry/issues.go: introduce IssueStatus type + the three
constants Sentry actually accepts. Status field on IssueUpdate is
typed now, so a typo like "resolve" won't silently no-op.
- internal/sentry/static_commands.go: rename mustClient → buildClient.
Go convention reserves must* for panics; this returns an error.
- internal/sentry/static_commands.go: drop the unused `format` param
from renderJSON. get-resource output is JSON regardless of --format
(varied per-resource shapes), so the symmetry-only parameter was
misleading.
- Trim PR-review-style comments ("Earlier revisions used...", "Renamed
from `context`...") that were useful in the original commit message
but rot in the source.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a first-class Sentry.io provider modeled on the existing Cloudflare integration. Headline feature: `clanker sentry ask "what's blowing up right now?"` — a natural-language agent that fetches issues, releases, and monitors on demand. Plus the full read/write surface for triage automation.
What lands
Decisions
Test plan
Out of scope