Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .craft.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ targets:
- name: npm
id: "@sentry/junior-github"
includeNames: /^sentry-junior-github-\d.*\.tgz$/
- name: npm
id: "@sentry/junior-hex"
includeNames: /^sentry-junior-hex-\d.*\.tgz$/
- name: npm
id: "@sentry/junior-linear"
includeNames: /^sentry-junior-linear-\d.*\.tgz$/
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jobs:
pnpm --filter @sentry/junior-agent-browser pack --pack-destination artifacts
pnpm --filter @sentry/junior-datadog pack --pack-destination artifacts
pnpm --filter @sentry/junior-github pack --pack-destination artifacts
pnpm --filter @sentry/junior-hex pack --pack-destination artifacts
pnpm --filter @sentry/junior-linear pack --pack-destination artifacts
pnpm --filter @sentry/junior-notion pack --pack-destination artifacts
pnpm --filter @sentry/junior-sentry pack --pack-destination artifacts
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ This repo uses Craft for manual lockstep npm releases of:
- `@sentry/junior-agent-browser`
- `@sentry/junior-datadog`
- `@sentry/junior-github`
- `@sentry/junior-hex`
- `@sentry/junior-linear`
- `@sentry/junior-notion`
- `@sentry/junior-sentry`
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Start here:
| `@sentry/junior-agent-browser` | Agent Browser plugin package for browser automation |
| `@sentry/junior-datadog` | Datadog plugin package for observability workflows (**non-functional**: Datadog has DCR locked down, see the [package README](./packages/junior-datadog/README.md)) |
| `@sentry/junior-github` | GitHub plugin package for issue workflows |
| `@sentry/junior-hex` | Hex plugin package for data warehouse query workflows |
| `@sentry/junior-linear` | Linear plugin package for issue workflows |
| `@sentry/junior-notion` | Notion plugin package for page search workflows |
| `@sentry/junior-sentry` | Sentry plugin package for issue workflows |
2 changes: 1 addition & 1 deletion apps/example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ It demonstrates:
- one local skill (`/example-local`)
- one plugin-bundled skill (`/example-bundle-help`)
- one bundle-only plugin (`app/plugins/example-bundle/plugin.yaml`) with no credential broker config
- installed plugin packages (`@sentry/junior-agent-browser`, `@sentry/junior-github`, `@sentry/junior-linear`, `@sentry/junior-notion`, `@sentry/junior-sentry`)
- installed plugin packages (`@sentry/junior-agent-browser`, `@sentry/junior-github`, `@sentry/junior-hex`, `@sentry/junior-linear`, `@sentry/junior-notion`, `@sentry/junior-sentry`)

## Run

Expand Down
1 change: 1 addition & 0 deletions apps/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@sentry/junior-agent-browser": "workspace:*",
"@sentry/junior-datadog": "workspace:*",
"@sentry/junior-github": "workspace:*",
"@sentry/junior-hex": "workspace:*",
"@sentry/junior-linear": "workspace:*",
"@sentry/junior-notion": "workspace:*",
"@sentry/junior-sentry": "workspace:*",
Expand Down
1 change: 1 addition & 0 deletions apps/example/plugin-packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const examplePluginPackages = [
"@sentry/junior-agent-browser",
"@sentry/junior-datadog",
"@sentry/junior-github",
"@sentry/junior-hex",
"@sentry/junior-linear",
"@sentry/junior-notion",
"@sentry/junior-sentry",
Expand Down
1 change: 1 addition & 0 deletions packages/docs/src/content/docs/contribute/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Junior uses lockstep package releases for:
- `@sentry/junior-agent-browser`
- `@sentry/junior-datadog`
- `@sentry/junior-github`
- `@sentry/junior-hex`
- `@sentry/junior-linear`
- `@sentry/junior-notion`
- `@sentry/junior-sentry`
Expand Down
82 changes: 82 additions & 0 deletions packages/docs/src/content/docs/extend/hex-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: Hex Plugin
description: Configure the hosted Hex MCP server for data warehouse query workflows.
type: tutorial
prerequisites:
- /extend/
related:
- /concepts/credentials-and-oauth/
- /operate/security-hardening/
---

The Hex plugin uses Hex's hosted MCP server so Slack users can run analytical queries against the data warehouse from Junior.

Junior exposes only Hex's `create_thread` and `get_thread` MCP tools. These support creating new analysis threads and polling for results — the core primitives needed for data retrieval workflows.

Requires a Hex Team or Enterprise plan.

## Install

Install the plugin package alongside `@sentry/junior`:

```bash
pnpm add @sentry/junior @sentry/junior-hex
```

## Runtime setup

List the plugin in `juniorNitro({ pluginPackages: [...] })`:

```ts title="nitro.config.ts"
juniorNitro({
pluginPackages: ["@sentry/junior-hex"],
});
```

## Auth model

- No `HEX_API_TOKEN` or shared secret is required.
- Each user completes OAuth the first time Junior calls a Hex MCP tool on their behalf.
- Junior sends the authorization link privately, then resumes the same thread automatically after the user authorizes.

## Region configuration

The default MCP endpoint is `https://app.hex.tech/mcp`. For non-standard deployments, set `HEX_MCP_URL` in your environment:

| Deployment | `HEX_MCP_URL` |
| --------------- | ----------------------------------- |
| Standard | (leave unset — uses default) |
| Single-tenant | `https://your-company.hex.tech/mcp` |
| EU multi-tenant | `https://eu.hex.tech/mcp` |
| HIPAA | `https://hc.hex.tech/mcp` |

## Skills

The plugin ships one skill:

- **hex** — Creates a Hex thread, polls for completion, and extracts results against a caller-provided pattern.

## What users can do

- Query data from the Hex data warehouse using natural language or raw SQL.
- Get structured output suitable for downstream processing or display.

## Verify

Confirm a real user can connect and query successfully:

1. Ask Junior to check usage data for a known Sentry customer.
2. Complete the private OAuth flow when Junior prompts for it.
3. Confirm the thread resumes automatically and includes Hex query results.
4. Open Junior App Home and confirm Hex appears under `Connected accounts`.

## Failure modes

- No auth prompt or no resume: the user still needs to complete the OAuth flow. Retry the request and finish the private authorization flow when prompted.
- Query timeout: Hex threads can take time to process. The `hex` skill polls up to 10 times with 20-second intervals. Complex queries may need to be simplified.
- No data returned: verify the entity identifier in the prompt matches what's in the warehouse. Narrow the query and try again.
- Wrong Hex workspace: verify `HEX_MCP_URL` points to the correct deployment if your org uses a custom Hex domain.

## Next step

Review [Credentials & OAuth](/concepts/credentials-and-oauth/) and [Security Hardening](/operate/security-hardening/).
4 changes: 3 additions & 1 deletion packages/docs/src/content/docs/extend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ prerequisites:
related:
- /extend/datadog-plugin/
- /extend/github-plugin/
- /extend/hex-plugin/
- /extend/linear-plugin/
- /extend/notion-plugin/
- /extend/sentry-plugin/
Expand Down Expand Up @@ -49,7 +50,7 @@ my-junior-plugin/
For reuse across apps or teams, package plugin manifests + skills as npm packages and install them next to `@sentry/junior`.

```bash
pnpm add @sentry/junior @sentry/junior-datadog @sentry/junior-github @sentry/junior-linear @sentry/junior-notion @sentry/junior-sentry
pnpm add @sentry/junior @sentry/junior-datadog @sentry/junior-github @sentry/junior-hex @sentry/junior-linear @sentry/junior-notion @sentry/junior-sentry
```

List the plugin packages in `juniorNitro` so they are bundled at build time and available at runtime:
Expand All @@ -65,6 +66,7 @@ export default defineConfig({
pluginPackages: [
"@sentry/junior-datadog",
"@sentry/junior-github",
"@sentry/junior-hex",
"@sentry/junior-linear",
"@sentry/junior-notion",
"@sentry/junior-sentry",
Expand Down
3 changes: 2 additions & 1 deletion packages/docs/src/content/docs/start-here/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ The initializer creates local `app/plugins` and `app/skills` directories, so you
If you want to use npm-distributed plugins, install them explicitly:

```bash
pnpm add @sentry/junior-datadog @sentry/junior-github @sentry/junior-linear @sentry/junior-notion @sentry/junior-sentry
pnpm add @sentry/junior-datadog @sentry/junior-github @sentry/junior-hex @sentry/junior-linear @sentry/junior-notion @sentry/junior-sentry
```

List the plugin packages in `juniorNitro` so they are bundled and available at runtime:
Expand All @@ -97,6 +97,7 @@ export default defineConfig({
pluginPackages: [
"@sentry/junior-datadog",
"@sentry/junior-github",
"@sentry/junior-hex",
"@sentry/junior-linear",
"@sentry/junior-notion",
"@sentry/junior-sentry",
Expand Down
29 changes: 29 additions & 0 deletions packages/junior-hex/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# @sentry/junior-hex

`@sentry/junior-hex` adds Hex data warehouse query workflows to Junior through Hex's hosted MCP server.

## Install

```bash
pnpm add @sentry/junior @sentry/junior-hex
```

## Configure

List the plugin in `juniorNitro({ pluginPackages: [...] })`:

```ts
juniorNitro({
pluginPackages: ["@sentry/junior-hex"],
});
```

No API token is needed. Each user completes OAuth the first time Junior calls a Hex MCP tool on their behalf.

For non-standard Hex deployments, set `HEX_MCP_URL` in your environment:

- Single-tenant: `https://your-company.hex.tech/mcp`
- EU multi-tenant: `https://eu.hex.tech/mcp`
- HIPAA: `https://hc.hex.tech/mcp`

Requires a Hex Team or Enterprise plan.
13 changes: 13 additions & 0 deletions packages/junior-hex/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "@sentry/junior-hex",
"version": "0.34.0",
"private": false,
"publishConfig": {
"access": "public"
},
"type": "module",
"files": [
"plugin.yaml",
"skills"
]
}
13 changes: 13 additions & 0 deletions packages/junior-hex/plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: hex
description: Hex analytics platform — run data warehouse queries for customer usage data

env-vars:
HEX_MCP_URL:
default: https://app.hex.tech/mcp

mcp:
url: ${HEX_MCP_URL}
allowed-tools:
- create_thread
- get_thread
Comment thread
cursor[bot] marked this conversation as resolved.
- continue_thread
122 changes: 122 additions & 0 deletions packages/junior-hex/skills/hex/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
name: hex
type: atomic
description: >
Internal data access primitive. Executes a Hex query and returns structured
results. Called by core skills — not intended for direct use. Invoke when you
need to run a Hex query on behalf of a core skill that has provided a query
and pattern.
Comment thread
cursor[bot] marked this conversation as resolved.
---

# Query Hex (Atomic)

Single responsibility: execute a Hex query via MCP, poll for completion, and return results matched against the caller-provided pattern. Hex Threads take a few minutes to run — this skill owns the full create → poll → extract cycle.

## Input

Receive all three from the calling core skill:

- **query** — the complete, self-contained prompt to send to Hex (see Prompt Construction below)
- **pattern** — what the target data looks like (field names, expected shape — provided by caller; do not assume)
- **context** — any identifying context for the query (e.g. account name, entity ID, time range)

## Prompt Construction (Critical — Read Before Creating a Thread)

Hex's Threads Agent decides which tables to query and what SQL to run based entirely on your prompt. A vague prompt causes broad table exploration and significantly slower responses. A precise prompt can shave several minutes off query time.

### Rules

1. **Batch everything into one prompt.** Include every metric and data point the caller needs in a single `create_thread` call. Do NOT use `continue_thread` to fetch additional fields — each call re-triggers the full agent pipeline and adds multiple minutes of latency. Treat `continue_thread` as a last resort only when a genuinely requested data point is absent from the initial response.

2. **Anchor on a specific entity identifier.** Always include the primary key or identifier for the entity being queried (e.g. account ID, org slug, user ID, opportunity ID). Never rely on Hex to infer the entity from a name or description alone.

3. **Use exact field and table terminology.** Vague terms force Hex to guess. Use the canonical column names, table names, and metric names from the caller's data model. If the caller provides known column names, include them in the prompt.

4. **Specify the time window explicitly.** Always state the period: `"for the last 28 days"`, `"from [start date] to today"`, `"monthly for the last 6 months"`.

5. **Request structured output.** End every prompt with: `"Return results in a structured list or table."`

6. **Pass raw SQL when available.** When the caller provides a specific SQL query, pass it directly to `create_thread` — the agent will execute it as-is, which is faster than natural language exploration.

### Example: Natural Language Query

> "For account ID '12345': return (1) total revenue for the last 90 days, (2) number of active users this month, and (3) any plan or subscription changes in the last 30 days. Return results in a structured list."

### Example: Raw SQL Query

> "Run this SQL: SELECT id, name, revenue, created_at FROM \`my-project.dataset.orders\` WHERE account_id = '12345' AND created_at >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY) ORDER BY created_at DESC"

## Steps

1. **Create a Hex thread.**
Call `create_thread` with the fully constructed prompt.
If the call fails (network error, auth error), return immediately with `status: "error"` and the error message.

2. **Poll for completion.**
Call `get_thread` to check thread status.
- Wait approximately **20 seconds** between polls.
- Retry up to **10 times** before giving up.
- Continue polling while status is not `IDLE` (i.e., still processing).
- If still not `IDLE` after 10 retries, return:
```json
{
"status": "timeout",
"value": null,
"source": "Hex",
"raw": "Hex query did not complete after 10 polling attempts."
}
```

3. **Extract the result.**
Once the thread reaches `IDLE`, read the response content. Reason against the returned data using the caller-provided **pattern** to locate and extract the target value(s).

4. **Use `continue_thread` only as a last resort.**
If a genuinely requested data point is completely absent from the response (not just unlabeled or formatted differently), use `continue_thread` once to ask for it specifically. Re-extract after. Do not use it to request additional data the caller didn't include in the original query — that is a caller-side error.

5. **Return structured output.**
Return one of:
- **Match found:**
```json
{
"status": "found",
"value": "<extracted value(s) matching the pattern>",
"source": "Hex",
"raw": "<full thread response for debugging>"
}
```
- **No match:**
```json
{
"status": "not_found",
"value": null,
"source": "Hex",
"raw": "<full thread response for debugging>"
}
```

## Constraints

- Do not hardcode data formats or field names — patterns come from the caller.
- Do not poll beyond the 10-attempt cap defined here.
- Do not return partial results as successes — if the pattern match fails, return `not_found`.
- Do not invoke other skills or data sources — this is a single-source primitive.
- Do not fabricate data. If Hex returns an empty or ambiguous result, return `not_found` with the raw output so the caller can inspect it.
- Do not use `continue_thread` as a standard follow-up mechanism — it is expensive and should be used at most once per query, only when a requested data point is genuinely absent from the initial response.

## Guardrails

### Auth and error handling

- **If `create_thread` returns an auth error** (401/403 or OAuth failure), stop immediately and return `status: "error"` — do not retry auth failures.
- **If `create_thread` returns a transient error** (network timeout, 5xx), retry once. If the retry also fails, return an error status.
- **Never expose raw Hex API error messages to end users.** Summarize the failure mode (auth, timeout, not found) without leaking internal details.

### Query safety

- **Do not execute write operations.** Hex threads are read-only analytics queries. If a prompt implies mutation (INSERT, UPDATE, DELETE), refuse and return an error.
- **Do not pass raw user input directly as the Hex prompt.** Always template input into the structured format defined by the caller's query.

### Rate limits

- **Cap `get_thread` polling to 10 calls per thread.** Do not poll beyond this — return `status: "timeout"`.
- **Do not chain this skill recursively.** One invocation handles one query.
5 changes: 5 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scripts/bump-release-versions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const files = [
"packages/junior-agent-browser/package.json",
"packages/junior-datadog/package.json",
"packages/junior-github/package.json",
"packages/junior-hex/package.json",
"packages/junior-linear/package.json",
"packages/junior-notion/package.json",
"packages/junior-sentry/package.json",
Expand Down
Loading