Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/cli-docs/src/content/docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ toolkit/
│ │ │ ├── dsn/ # list
│ │ │ ├── event/ # list, send, view
│ │ │ ├── feedback/ # list, resolve, spam, unresolve, view
│ │ │ ├── games/ # snake
│ │ │ ├── games/ # leaderboard, snake
│ │ │ ├── issue/ # archive, events, explain, link, list, merge, plan, resolve, unlink, unresolve, view
│ │ │ ├── local/ # run, serve
│ │ │ ├── log/ # list, view
Expand Down
21 changes: 21 additions & 0 deletions apps/cli-docs/src/fragments/commands/games.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,24 @@ sentry games snake
Steer with the arrow keys, pause with `p`, retry with `r`, and quit with `esc`
or `q`. The game needs an interactive terminal and is not available when an AI
agent runs the CLI.


### Show the leaderboard

```bash
sentry games leaderboard
```

Shows the top Snake scores from the last 30 days (a rolling window). Your own
row is marked `(you)`. Use `--json` for machine-readable output.

## Anonymous scores

When a Snake game ends, the CLI sends one score and a random player handle such
as `brave-otter-4242`. The handle is generated on your machine and is not linked
to your account, name, email, organization, or installation. The score is sent
in its own trace, apart from the CLI's other telemetry.

Scores are sent only when telemetry is on. To opt out, set
`SENTRY_CLI_NO_TELEMETRY=1` or `DO_NOT_TRACK=1`, or run
`sentry cli defaults telemetry off`.
22 changes: 18 additions & 4 deletions docs/cloudflare/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default new Hono()
```

**Features:**

- OAuth 2.0 flow with Sentry
- Token storage in Cloudflare KV
- Automatic token refresh
Expand All @@ -59,12 +60,13 @@ React-based chat UI with real-time streaming:
export function Chat() {
const { messages, handleSubmit } = useChat({
api: "/api/chat",
headers: { Authorization: `Bearer ${authToken}` }
headers: { Authorization: `Bearer ${authToken}` },
});
}
```

**Features:**

- Message streaming with Vercel AI SDK
- Tool call visualization
- Slash commands (/help, /prompts, /clear)
Expand All @@ -82,12 +84,13 @@ const mcpClient = await experimental_createMCPClient({
transport: {
type: "sse",
url: sseUrl,
headers: { Authorization: `Bearer ${accessToken}` }
}
headers: { Authorization: `Bearer ${accessToken}` },
},
});
```

**Features:**

- Server-sent events (SSE) for MCP communication
- Automatic tool discovery
- Prompt metadata endpoint
Expand All @@ -102,11 +105,12 @@ const result = streamText({
model: openai("gpt-4o"),
messages: processedMessages,
tools: mcpTools,
system: "You are an AI assistant for testing Sentry MCP..."
system: "You are an AI assistant for testing Sentry MCP...",
});
```

**Features:**

- Streaming responses
- Tool execution
- Prompt template processing
Expand All @@ -115,11 +119,13 @@ const result = streamText({
## Data Flow

1. **User Authentication**:

```
User → OAuth Login → Sentry → OAuth Callback → KV Storage
```

2. **Chat Message Flow**:

```
User Input → Chat API → Process Prompts → AI Model → Stream Response
↓
Expand Down Expand Up @@ -151,13 +157,21 @@ COOKIE_SECRET = "..." # For session encryption
OPENAI_API_KEY = "..." # For GPT-4 access
SENTRY_CLIENT_ID = "..." # OAuth app ID
SENTRY_CLIENT_SECRET = "..." # OAuth app secret
SENTRY_GAMES_READ_TOKEN = "..." # Optional: Snake leaderboard (set with `wrangler secret put`)
```

`SENTRY_GAMES_READ_TOKEN` is the Sentry token used by
`GET /api/games/snake/leaderboard`. The token must belong to a bot account that
has only `org:read` and is a member only of the team that owns the CLI project.
Enable "Prevent storing IP addresses" on that project.
Without the token, the leaderboard route returns 503.

### API Routes

- `/api/auth/*` - Authentication endpoints
- `/api/chat` - Main chat endpoint
- `/api/metadata` - MCP metadata endpoint
- `/api/games/snake/leaderboard` - Public Snake leaderboard (rate limited by IP, cached in `MCP_CACHE` for 300 seconds)
- `/sse` - Server-sent events for MCP

## Security Considerations
Expand Down
20 changes: 15 additions & 5 deletions docs/operations/github-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and docs when `SENTRY_CLIENT_ID` is available.
## Workflows

### test.yml

Runs on pushes to `main`, pull requests, and merge queue entries. Discovery
reads pnpm workspace projects and their package scripts. Pull requests check
changed projects, their workspace consumers, and semantic dependencies (the CLI
Expand All @@ -19,6 +20,7 @@ Package-specific exceptions live in `package.json#sentryCi`; the standalone
smoke-test suite remains in its own workflow.

### deploy.yml

Runs after a successful `Test` push run on `main`. Checks out the tested commit
and requires that it is still the tip of `main`. Builds once, records the active
production version, and uploads one new version of `sentry-mcp` from Vite's
Expand All @@ -32,6 +34,7 @@ it restores the exact captured prior version only if the live deployments still
belong to this run.

### recover-cloudflare-deployment.yml

Manual `workflow_dispatch` recovery accepts a deployment run ID and attempt.
It runs trusted current-`main` code in the protected `production` environment,
checks the completed source run, and derives the prior version from contiguous
Expand All @@ -45,15 +48,18 @@ only its 404 response. When the route exists, the smoke test compares its
version ID with the restored version.

### migrate-cloudflare-token.yml

Moves the Cloudflare API token from a repository secret into the protected
`production` environment. Only `main` in the Toolkit repository can run it.
Copy and removal are separate dispatches so a normal production deployment can
prove that the environment copy works before the repository copy is deleted.

### eval.yml

Runs evaluation tests against the MCP server.

### pr-risk-jev.yml

Classifies PR risk with Jev and publishes one `risk: low`, `risk: medium`, or
`risk: high` label. Runs when a non-draft PR is opened, updated with a push,
reopened, marked ready for review, or edited. Manual dispatch accepts a PR number
Expand All @@ -67,6 +73,7 @@ are cleared and the PR stays unclassified. Results are retained as workflow
artifacts for 30 days.

### pr-risk-labels-test.yml

Runs the label publisher's regression tests when its workflow or tests change.
Covers label replacement, stale revisions, failed classifications, and concurrent
label creation.
Expand All @@ -89,23 +96,26 @@ Other configuration:
- **`SENTRY_CLIENT_SECRET`** - Sentry OAuth client secret
- **`COOKIE_SECRET`** - Session cookie encryption secret
- **`OPENAI_API_KEY`** - For AI-powered search features
- **`SENTRY_GAMES_READ_TOKEN`** - Not a GitHub secret. The deploy workflow does not pass Worker secrets; set it with `wrangler secret put SENTRY_GAMES_READ_TOKEN`. The token must belong to a bot account with only `org:read` and membership only in the team that owns the CLI project. Enable "Prevent storing IP addresses" on that project.
- **`AI_GATEWAY_API_KEY`** - Vercel AI Gateway key for Jev PR risk classification

## Deployment Architecture

### Workers

- **`sentry-mcp`** - Production worker at `https://mcp.sentry.dev`
- The candidate is tested on the production Worker at 0% traffic before promotion.

### Resource Isolation

The existing canary Worker has separate resources; exact-version rollout does
not deploy it. The production candidate uses the production bindings:

| Resource | Production | Canary |
|----------|------------|---------|
| KV Namespace | `8dd5e9bafe1945298e2d5ca3b408a553` | `a3fe0d23b2d34416930e284362a88a3b` |
| Rate Limiter IDs | `1001`, `1002`, `1003`, `1004` | `2001`, `2002`, `2003`, `2004` |
| Wrangler Config | `wrangler.jsonc` | `wrangler.canary.jsonc` |
| Resource | Production | Canary |
| ---------------- | ---------------------------------- | ---------------------------------- |
| KV Namespace | `8dd5e9bafe1945298e2d5ca3b408a553` | `a3fe0d23b2d34416930e284362a88a3b` |
| Rate Limiter IDs | `1001`, `1002`, `1003`, `1004` | `2001`, `2002`, `2003`, `2004` |
| Wrangler Config | `wrangler.jsonc` | `wrangler.canary.jsonc` |

### Deployment Flow

Expand Down
45 changes: 31 additions & 14 deletions docs/releases/cloudflare.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Cloudflare Workers deployment configuration and release process.
## Architecture Overview

The deployment consists of:

- **Worker**: Stateless HTTP server with OAuth flow and MCP handler
- **KV Storage**: OAuth token storage
- **Static Assets**: React UI for setup instructions
Expand All @@ -20,33 +21,37 @@ The deployment consists of:
"compatibility_date": "2025-03-21",
"compatibility_flags": [
"nodejs_compat",
"nodejs_compat_populate_process_env"
"nodejs_compat_populate_process_env",
],
"keep_vars": true,

// Bindings
"kv_namespaces": [{
"binding": "OAUTH_KV",
"id": "your-kv-namespace-id"
}],
"kv_namespaces": [
{
"binding": "OAUTH_KV",
"id": "your-kv-namespace-id",
},
],

// SPA configuration
"site": {
"bucket": "./dist/client"
}
"bucket": "./dist/client",
},
}
```

### Environment Variables

Required in production:

```bash
SENTRY_CLIENT_ID=your_oauth_app_id
SENTRY_CLIENT_SECRET=your_oauth_app_secret
COOKIE_SECRET=32_char_random_string
```

Optional overrides for self-hosted deployments:

```bash
# Leave unset to target the SaaS host
SENTRY_HOST=sentry.example.com # Hostname only (self-hosted only)
Expand All @@ -56,7 +61,14 @@ Configure these overrides only when your Cloudflare deployment connects to a
self-hosted Sentry instance; no additional host variables are required for the
SaaS service.

Optional secret for the Snake leaderboard (`GET /api/games/snake/leaderboard`).
Set it with `wrangler secret put SENTRY_GAMES_READ_TOKEN`. Without it, the
route returns 503. The token must belong to a bot account with only `org:read`
and membership only in the team that owns the CLI project. Enable "Prevent
storing IP addresses" on that project.

Development (.dev.vars):

```bash
SENTRY_CLIENT_ID=dev_client_id
SENTRY_CLIENT_SECRET=dev_secret
Expand All @@ -72,7 +84,11 @@ import { experimental_createMcpHandler as createMcpHandler } from "agents/mcp";
import { buildServer } from "@sentry/mcp-server/server";

const mcpHandler: ExportedHandler<Env> = {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext,
): Promise<Response> {
// Extract auth props from ExecutionContext (set by OAuth provider)
const oauthCtx = ctx as OAuthExecutionContext;

Expand All @@ -81,7 +97,7 @@ const mcpHandler: ExportedHandler<Env> = {
userId: oauthCtx.props.userId,
clientId: oauthCtx.props.clientId,
accessToken: oauthCtx.props.accessToken,
grantedSkills, // Primary authorization method
grantedSkills, // Primary authorization method
constraints: verification.constraints,
sentryHost,
mcpUrl: oauthCtx.props.mcpUrl,
Expand Down Expand Up @@ -179,13 +195,15 @@ For feature branches, GitHub Actions automatically uploads new versions without
4. Use Cloudflare dashboard to gradually roll out the version

Manual version upload:

```bash
pnpm cf:versions:upload
```

### Creating Resources

First-time setup:

```bash
# Create KV namespace for OAuth token storage
npx wrangler kv:namespace create OAUTH_KV
Expand All @@ -196,6 +214,7 @@ npx wrangler kv:namespace create OAUTH_KV
## Multi-Region Considerations

Cloudflare Workers run globally, but consider:

- KV is eventually consistent globally
- Workers are stateless and edge-deployed
- Use regional hints for performance
Expand All @@ -205,10 +224,7 @@ Cloudflare Workers run globally, but consider:
### CORS Settings

```typescript
const ALLOWED_ORIGINS = [
"https://sentry.io",
"https://*.sentry.io"
];
const ALLOWED_ORIGINS = ["https://sentry.io", "https://*.sentry.io"];

// Apply to responses
response.headers.set("Access-Control-Allow-Origin", origin);
Expand All @@ -219,7 +235,7 @@ response.headers.set("Access-Control-Allow-Credentials", "true");

```typescript
// Secure cookie settings
"HttpOnly; Secure; SameSite=Lax; Max-Age=2592000"
"HttpOnly; Secure; SameSite=Lax; Max-Age=2592000";
```

## Monitoring
Expand All @@ -245,6 +261,7 @@ export default {
### Worker Analytics

Monitor via Cloudflare dashboard:

- Request rates
- Error rates
- CPU time and memory usage
Expand Down
1 change: 1 addition & 0 deletions packages/cli/plugins/sentry-cli/skills/sentry-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,7 @@ Manage User Feedback

Terminal games

- `sentry games leaderboard` — Show the top Snake scores from the last 30 days
- `sentry games snake` — Play Snake in your terminal

→ Full flags and examples: `references/games.md`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ requires:

Terminal games

### `sentry games leaderboard`

Show the top Snake scores from the last 30 days

**Examples:**

```bash
sentry games leaderboard
```

### `sentry games snake`

Play Snake in your terminal
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/games/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { buildRouteMap } from "../../lib/route-map.js";
import { leaderboardCommand } from "./leaderboard.js";
import { snakeCommand } from "./snake.js";

export const gamesRoute = buildRouteMap({
routes: {
leaderboard: leaderboardCommand,
snake: snakeCommand,
},
docs: {
Expand Down
Loading
Loading