Skip to content
dsh.fish
Bundle

dsh-sentry

Read-only Sentry issue and event tools for DeepSeek Harness.

Source
maxmilian
stars
2 stars
License
MIT
Updated
Updated 10 days ago

Readme

# dsh-sentry

English | [繁體中文](README.zh-TW.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md)

`dsh-sentry` is a free, open-source, read-only DeepSeek Harness plugin for the Sentry Web API.
Every tool is an HTTP `GET`; the plugin never resolves, assigns, archives, or otherwise changes
Sentry state.

Its main job is not proxying the API — it is **trimming responses so an agent's context survives a
real stacktrace**. A raw `events/latest/` payload is routinely 200KB–2MB. This plugin reduces it to
the frames, source lines, and metadata that actually help debugging, and tells you in `meta.trimmed`
what it had to leave out.

## Tools

| Tool | Purpose |
| --- | --- |
| `sentry_list_projects` | List up to 100 projects in the configured organization. |
| `sentry_search_issues` | Search issues with Sentry search syntax, in one project or the whole organization. |
| `sentry_get_issue` | Read one issue by numeric id or short id, without event bodies. |
| `sentry_get_latest_event` | Read an issue's latest event with a trimmed stacktrace. |
| `sentry_get_event` | Read one event by id within a project, with the same trimming. |

All tools are read-only. Version 0.1 does not modify issues, create releases, or send events.

## Requirements

- DeepSeek Harness with compatible `@deepseek-ai/dsh-tools` APIs
- Node.js 22.19 or newer in the 22.x line, or Node.js 24 or newer
- Bun 1.3.5 or newer when installing from GitHub source or developing locally
- A Sentry **User Auth Token** (`sntryu_`) with read access to the requested organization

## Token scopes

| Scope | Unlocks |
| --- | --- |
| `org:read` | `/organizations/{org}/projects/`, `/organizations/{org}/issues/`, `/organizations/{org}/shortids/{short_id}/` |
| `project:read` | `/projects/{org}/{project}/issues/` |
| `event:read` | `/issues/{id}/`, `/issues/{id}/events/latest/`, `/projects/{org}/{project}/events/{event_id}/` |

Use a **User Auth Token** (`sntryu_`) with `event:read`, `org:read`, and `project:read`. The
simplest safe one comes from `sentry auth login --read-only`, which requests exactly
`project:read`, `org:read`, `event:read`, `member:read`, and `team:read`.

An **Organization Auth Token** (`sntrys_`) does not work: Sentry fixes its scope set to `org:ci`
(Source Map Upload, Release Creation, Code Mappings) with no way to add read scopes, so every read
path here returns HTTP 403. Verified against sentry.io on 2026-08-27 — see
[`docs/live-verification.md`](docs/live-verification.md).

## Configuration

| Field | Environment variable | Default | Notes |
| --- | --- | --- | --- |
| `baseUrl` | `SENTRY_URL` | `https://sentry.io/` | Site root URL. Use `https://de.sentry.io/` for the EU region. A trailing `/api/0` is stripped automatically. |
| `token` | `SENTRY_AUTH_TOKEN` | required | User auth token (`sntryu_`). Organization auth tokens are `org:ci`-only and are rejected with 403. Never returned or logged. |
| `org` | `SENTRY_ORG` | required | Organization slug. Fixed for the whole plugin instance. |
| `locale` | — | `en` | `en`, `zh-TW`, `zh-CN`, or `ja`. Selects the language of tool and parameter descriptions. |
| `includeFrameVars` | `SENTRY_INCLUDE_FRAME_VARS` | `false` | Keep stack frame local variables. Only the literal string `true` enables it. Agents cannot override this. |
| `requestTimeoutMs` | — | `30000` | Deadline for **one whole tool call**, including the extra request a short id costs. Range 1–300000. |
| `maxResponseBytes` | — | `5242880` | Hard cap on a single HTTP response body. Range 1–52428800. |

Plugin configuration always wins over environment variables.

```sh
export SENTRY_AUTH_TOKEN='your-token'
export SENTRY_ORG='your-org'
# self-hosted or EU region only:
export SENTRY_URL='https://sentry.example.com'
```

## Self-hosted and regions

- Self-hosted: point `baseUrl` at the site root, including a sub-path install such as
  `https://example.com/sentry/`.
- Sentry SaaS EU region: `baseUrl` must be `https://de.sentry.io/`. Using `https://sentry.io/` for an
  EU organization surfaces as a 401 or 404, so both of those error messages repeat the region hint.
- Older self-hosted versions simply return fewer fields. The plugin treats every response field as
  optional and never fails because one is missing. Two known behavior differences: `sort=recommended`
  can be rejected (reported as `UNSUPPORTED_BY_INSTANCE`), and `stats_period` is limited to `24h` and
  `14d`.

## What gets trimmed

Removed from every event, unconditionally:

- Request headers, cookies, environment, and body. The request URL keeps only origin and path — the
  query string is dropped whole, because OAuth callbacks and signed URLs carry secrets there.
- Stack frame local variables, unless `includeFrameVars` is on.
- `mechanism.data`, `contexts.state`, `packages`, `modules`, and `_meta`.
- `user.email`, `user.ip_address`, and `user.username`. Only `user.id` survives.
- Any tag whose key looks like a secret or direct PII (`token`, `secret`, `password`, `passwd`,
  `api_key`, `auth`, `cookie`, `session`, `credential`, private/access keys, JWT, DSN, signature,
  email, IP address, or username), plus every `sentry:`-prefixed internal tag.
- Frame fields that leak build paths, such as `absPath`.

Reduced rather than removed:

- **Frames.** Frames run outermost to innermost. When there are more than `max_frames`, the plugin
  prioritizes in-app frames, always includes the two innermost frames, fills from the tail when room
  remains, and never exceeds the cap. Output preserves the original order.
- **Source context.** Kept only for the three innermost in-app frames, at most 11 lines each, each
  line capped at 200 characters.
- **Chained exceptions.** At most the two innermost `exception.values`; `max_frames` applies to each
  stacktrace separately.
- **Breadcrumbs.** The last 20, messages capped at 200 characters.
- **Strings.** Exception values cap at 2000 characters; titles, messages, and culprits at 500.

If the result still exceeds the 200KB tool-result budget, the plugin degrades in fixed steps —
source context, then breadcrumbs, then frames down to at most 10 without raising a lower caller cap. If administrator-enabled frame variables
still make the result too large, they are removed as a final fallback. The last step is reported in
`meta.trimmed.degraded`. Counters such as `omittedFrames` are always "original total minus what you
received", never a running tally.

## Localization

Tool and parameter descriptions follow `locale`. **Tool names are always English and never change**,
because they are the agent's calling identifiers. **Error messages are always English** as well: they
are stable diagnostic strings that tests and reviews compare against.

## Security and error behavior

- Uses `Authorization: Bearer ...` and never returns or logs the token.
- Honors the DSH tool `AbortSignal` and a per-call deadline; a short id costs a second HTTP request
  but shares the same deadline.
- Converts HTTP 401, 403, 404, 429, and 5xx into safe structured errors that never carry a response
  body.
- **One deliberate exception:** on HTTP 400 from an issue search, the plugin reads at most 64KB of
  the body, takes only the structured `detail` or `error` string, drops it entirely if it contains
  the token or looks like it carries a secret, caps it at 200 characters, and appends it as
  `Sentry said: ...`. Without that, an agent can only guess at a search-syntax error. When the body
  is HTML, unparseable, or filtered out, the message falls back to the static form — so
  `INVALID_QUERY` messages come in two shapes.
- Does not support disabling TLS verification or self-signed certificate bypass in v0.1.

## Limitations (v0.1)

- No writes of any kind: no resolve, unresolve, archive, assign, merge, delete, release creation, or
  event ingestion.
- One organization per plugin instance; tools do not accept an organization parameter.
- No Seer AI, Performance, Discover, Metrics, Dashboards, Replay, Trace, or Span endpoints.
- No release, deploy, or issue-tag-distribution queries.
- `stats_period` is limited to `24h` and `14d`; custom `start`/`end` ranges are not supported.
- No automatic pagination. `sentry_search_issues` returns one page plus `meta.nextCursor`;
  `sentry_list_projects` accepts no cursor at all and reports `meta.truncated` instead.
- No local caching, no attachment or source map downloads, and no raw passthrough mode.

## Development

This project uses Bun as its package manager and script runner; the published plugin runtime targets
the Node.js versions listed above:

```sh
bun install --frozen-lockfile
bun run lint
bun run typecheck
bun run test --coverage
bun run build
bun pm pack
```

Tests use Vitest with mocked `fetch` and do not require a live Sentry instance. Coverage gates for
lines, statements, functions, and branches are all set to at least 80%.

Live compatibility against Sentry SaaS and a self-hosted instance has not been recorded for this
release yet. The checklist required before tagging v0.1.0 lives in
[`docs/live-verification.md`](docs/live-verification.md); run it against your own instance with:

```bash
bun run build
SENTRY_TOKEN=... SENTRY_ORG=... node scripts/live-verify.mjs
```

The script is read-only and exits 1 when credentials are missing.

## License

MIT

Install

dsh plugin --profile web add github:maxmilian/dsh-sentry

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source