## Summary
Adds an MCP (Model Context Protocol) server to the HyperDX API, enabling AI assistants (Claude, Cursor, OpenCode, etc.) to query observability data, manage dashboards, and explore data sources directly via standardized tool calls.
Key changes:
- **MCP server** (`packages/api/src/mcp/`) — Streamable HTTP transport at `/api/mcp`, authenticated via Personal API Access Key
- **Tools** — `hyperdx_list_sources`, `hyperdx_query`, `hyperdx_get_dashboard`, `hyperdx_save_dashboard`, `hyperdx_delete_dashboard`, `hyperdx_query_tile`
- **Dashboard prompts** — Detailed prompt templates that guide LLMs in generating valid, high-quality dashboards
- **Shared logic** — Refactored dashboard validation/transformation out of the external API router into reusable utils (`packages/api/src/routers/external-api/v2/utils/dashboards.ts`)
- **Documentation** — `MCP.md` with setup instructions for Claude Code, OpenCode, Cursor, MCP Inspector, and other clients
- **Tests** — Unit tests for dashboard tools, query tools, tracing, and response trimming
### Screenshots
https://github.com/user-attachments/assets/8c5aa582-c79e-47e0-8f75-e03feabdf8a6
### How to test locally
1. Start the dev stack: `yarn dev`
2. Connect an MCP client (e.g. MCP Inspector):
```bash
cd packages/api && yarn dev:mcp
```
Then configure the inspector:
- **Transport Type:** Streamable HTTP
- **URL:** `http://localhost:8080/api/mcp`
- **Header:** `Authorization: Bearer <your-personal-access-key>`
- Click **Connect**
3. Alternatively, connect via Claude Code or OpenCode:
```bash
claude mcp add --transport http hyperdx http://localhost:8080/api/mcp \
--header "Authorization: Bearer <your-personal-access-key>"
```
4. Try listing sources, querying data, or creating/updating a dashboard through the connected AI assistant.
5. Run unit tests:
```bash
cd packages/api && yarn ci:unit
```
### References
- Linear Issue: HDX-3710
## Summary
This PR implements alerting on Raw SQL-based line/bar charts.
- This is only available for line/bar charts with this change. Number charts will be added in a future PR.
- The threshold is compared to the _last_ numeric column in each result.
- The interval parameter must be used. This is required for line charts to function, and is used for zero-fill and other functionality within the alerts task. This limitation will be removed for Number chart alerts when those are implemented.
- Start and and end date should be used, but are not required because there are some potential use-cases where they may not be desirable.
### Screenshots or video
https://github.com/user-attachments/assets/e2d0cd6c-b040-4490-89af-6a51a7380647
Logs from Check-Alerts evaluating a raw-sql alert
<img width="1241" height="908" alt="Screenshot 2026-04-09 at 3 01 14 PM" src="https://github.com/user-attachments/assets/dbed4e5f-bf27-4179-b8e0-897cc19f3d3a" />
### How to test locally or on Vercel
This must be tested locally, as alerts are not enabled in the preview environment.
<details>
<summary>Query for the "anomaly detection" example</summary>
```sql
WITH buckets AS (
SELECT
$__timeInterval(Timestamp) AS ts,
count() AS bucket_count
FROM $__sourceTable
WHERE TimestampTime >= fromUnixTimestamp64Milli({startDateMilliseconds:Int64}) - toIntervalSecond($__interval_s * 30) -- Fetch 30 intervals back
AND TimestampTime < fromUnixTimestamp64Milli({endDateMilliseconds:Int64})
AND SeverityText = 'error'
GROUP BY ts
ORDER BY ts
WITH FILL STEP toIntervalSecond($__interval_s)
),
anomaly_detection as (
SELECT
ts,
bucket_count,
avg(bucket_count) OVER (ORDER BY ts ROWS BETWEEN 30 PRECEDING AND 1 PRECEDING) as previous_30_avg, -- avg of previous 30 intervals
stddevPop(bucket_count) OVER (ORDER BY ts ROWS BETWEEN 30 PRECEDING AND 1 PRECEDING) as previous_30_stddev, -- standard deviation of previous 30 intervals
greatest(bucket_count - (previous_30_avg + 2 * previous_30_stddev), 0) as excess_over_2std -- compare bucket to avg + 2 stddev. clamp at 0.
FROM buckets
)
SELECT ts, excess_over_2std
FROM anomaly_detection
WHERE ts >= fromUnixTimestamp64Milli({startDateMilliseconds:Int64}) AND ts < fromUnixTimestamp64Milli({endDateMilliseconds:Int64})
```
</details>
### References
- Linear Issue: HDX-1605
- Related PRs:
* Adds support for auto-openapi doc generation
* Adds a swagger route for local development
* Adds a script to manually generate the openapi doc (to be used later if we publish to our site, etc...)
Ref: HDX-1661
small dev ex improvement:
Before: `import { ... } from '../../../fixtures';`
After: `import { ... } from '@/fixtures';`
after checking out, rebuild `api` and `task-check-alerts` containers:
```sh
docker compose -f docker-compose.dev.yml up -d --no-deps --build task-check-alerts api
```