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
12 changes: 12 additions & 0 deletions .changeset/mcp-alert-saved-search-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@hyperdx/api': minor
---

feat(mcp): add alert, saved search, and webhook MCP tools

Add five new MCP tools for managing alerts, saved searches, and webhooks:
- `hyperdx_get_alert` / `hyperdx_save_alert` for listing, creating, and updating alerts
- `hyperdx_get_webhook` for listing webhook destinations
- `hyperdx_get_saved_search` / `hyperdx_save_saved_search` for listing, creating, and updating saved searches

Also makes `McpContext.userId` required, rejecting MCP requests without a user ID.
21 changes: 13 additions & 8 deletions MCP.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,16 @@ with:

## Available Tools

| Tool | Description |
| -------------------------- | -------------------------------------------------------------------------------------------- |
| `hyperdx_list_sources` | List all data sources and database connections, including column schemas and attribute keys |
| `hyperdx_query` | Query observability data (logs, metrics, traces) using builder mode, search mode, or raw SQL |
| `hyperdx_get_dashboard` | List all dashboards or get full detail for a specific dashboard |
| `hyperdx_save_dashboard` | Create or update a dashboard with tiles (charts, tables, numbers, search, markdown) |
| `hyperdx_delete_dashboard` | Permanently delete a dashboard and its attached alerts |
| `hyperdx_query_tile` | Execute the query for a specific dashboard tile to validate results |
| Tool | Description |
| ----------------------------- | -------------------------------------------------------------------------------------------- |
| `hyperdx_list_sources` | List all data sources and database connections, including column schemas and attribute keys |
| `hyperdx_query` | Query observability data (logs, metrics, traces) using builder mode, search mode, or raw SQL |
| `hyperdx_get_dashboard` | List all dashboards or get full detail for a specific dashboard |
| `hyperdx_save_dashboard` | Create or update a dashboard with tiles (charts, tables, numbers, search, markdown) |
| `hyperdx_delete_dashboard` | Permanently delete a dashboard and its attached alerts |
| `hyperdx_query_tile` | Execute the query for a specific dashboard tile to validate results |
| `hyperdx_get_saved_search` | List all saved searches or get full detail for a specific saved search |
| `hyperdx_save_saved_search` | Create or update a saved search (reusable query against a data source) |
| `hyperdx_get_alert` | List alerts (summary) or get full detail with evaluation history; filter by state |
| `hyperdx_save_alert` | Create a new alert or update an existing one |
| `hyperdx_get_webhook` | List available webhook destinations for use as alert notification channels |
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ dev-int-common-utils:
ci-int:
@mkdir -p $(HDX_CI_LOGS_DIR)
docker compose -p $(HDX_CI_PROJECT) -f ./docker-compose.ci.yml up -d --quiet-pull
bash -c 'set -o pipefail; npx nx run-many -t ci:int --parallel=false 2>&1 | tee $(HDX_CI_LOGS_DIR)/ci-int.log'; ret=$$?; \
bash -c 'set -o pipefail; npx nx run-many -t ci:int --parallel=false --output-style=stream 2>&1 | tee $(HDX_CI_LOGS_DIR)/ci-int.log'; ret=$$?; \
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backporting change to improve logging of int tests - failures previously may not be shown

docker compose -p $(HDX_CI_PROJECT) -f ./docker-compose.ci.yml down; \
$(call archive-int-logs); \
exit $$ret
Expand Down
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"lint": "npx eslint --quiet . --ext .ts",
"lint:fix": "npx eslint . --ext .ts --fix",
"ci:lint": "yarn lint && yarn tsc --noEmit && yarn lint:openapi",
"ci:int": "DOTENV_CONFIG_PATH=.env.test DOTENV_CONFIG_OVERRIDE=true jest --runInBand --ci --forceExit --coverage",
"ci:int": "DOTENV_CONFIG_PATH=.env.test DOTENV_CONFIG_OVERRIDE=true jest --runInBand --ci --forceExit",
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backporting coverage removal to OSS as this PR seems to have crossed the memory threshold and now the int tests are failing

"dev:int": "DOTENV_CONFIG_PATH=.env.test DOTENV_CONFIG_OVERRIDE=true jest --runInBand --coverage",
"dev:migrate-db-create": "ts-node node_modules/.bin/migrate-mongo create -f migrate-mongo-config.ts",
"dev:migrate-db": "ts-node node_modules/.bin/migrate-mongo up -f migrate-mongo-config.ts",
Expand Down
12 changes: 8 additions & 4 deletions packages/api/src/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,19 @@ export const getServer = () => new MockServer();
export const getAgent = (server: MockServer) =>
request.agent(server.getHttpServer());

export const getLoggedInAgent = async (server: MockServer) => {
export const getLoggedInAgent = async (
server: MockServer,
credentials?: { email: string; password: string },
) => {
const agent = getAgent(server);
const creds = credentials ?? MOCK_USER;

await agent
.post('/register/password')
.send({ ...MOCK_USER, confirmPassword: MOCK_USER.password })
.send({ ...creds, confirmPassword: creds.password })
.expect(200);

const user = await findUserByEmail(MOCK_USER.email);
const user = await findUserByEmail(creds.email);
const team = await getTeam(user?.team as any);

if (team === null || user === null) {
Expand All @@ -231,7 +235,7 @@ export const getLoggedInAgent = async (server: MockServer) => {

// login app — 303 See Other so the browser follows the redirect with GET
// (see redirectToDashboard in middleware/auth.ts).
await agent.post('/login/password').send(MOCK_USER).expect(303);
await agent.post('/login/password').send(creds).expect(303);

return {
agent,
Expand Down
Loading
Loading