Skip to content

Alerts & subscriptions#42

Merged
alxnddr merged 1 commit into
mainfrom
alerts-subscriptions
Jul 15, 2026
Merged

Alerts & subscriptions#42
alxnddr merged 1 commit into
mainfrom
alerts-subscriptions

Conversation

@alxnddr

@alxnddr alxnddr commented Jul 14, 2026

Copy link
Copy Markdown
Member

Description

We're building alerts and subscriptions in MCPv2 but they were missing in the CLI too so this PR adds new commands and skills to use alerts & subscriptions.

Summary by CodeRabbit

  • New Features

    • Added commands for creating, listing, viewing, updating, archiving, and sending question alerts.
    • Added commands for managing dashboard subscriptions, including filtering by dashboard.
    • Added shortcuts to list alerts for a card and subscriptions for a dashboard.
    • Added guidance for choosing and configuring alert and subscription notifications.
  • Documentation

    • Expanded command reference documentation and refreshed the bundled-skills catalog.
  • Bug Fixes

    • Improved update behavior to preserve archived status and other existing notification settings.
  • Chores

    • Updated the CLI package version to 0.2.2.

@alxnddr alxnddr requested a review from ranquild July 14, 2026 18:37
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@alxnddr, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b3df27dc-fad4-4df2-8591-b8c071d209db

📥 Commits

Reviewing files that changed from the base of the PR and between 8e124bf and 5e96027.

📒 Files selected for processing (36)
  • README.md
  • missing-skills.md
  • package.json
  • skill-data/core/SKILL.md
  • skill-data/notification/SKILL.md
  • src/commands/alert/archive.ts
  • src/commands/alert/create.ts
  • src/commands/alert/get.ts
  • src/commands/alert/index.ts
  • src/commands/alert/list.ts
  • src/commands/alert/patch.test.ts
  • src/commands/alert/patch.ts
  • src/commands/alert/send.ts
  • src/commands/alert/summary.ts
  • src/commands/alert/update.ts
  • src/commands/card/alerts.ts
  • src/commands/card/index.ts
  • src/commands/dashboard/index.ts
  • src/commands/dashboard/subscriptions.ts
  • src/commands/subscription/archive.ts
  • src/commands/subscription/create.ts
  • src/commands/subscription/get.ts
  • src/commands/subscription/index.ts
  • src/commands/subscription/list.ts
  • src/commands/subscription/patch.test.ts
  • src/commands/subscription/patch.ts
  • src/commands/subscription/update.ts
  • src/domain/cron.ts
  • src/domain/notification.ts
  • src/domain/pulse.ts
  • src/domain/transform-job.ts
  • src/main.ts
  • src/runtime/command-help.test.ts
  • tests/e2e/alert.e2e.test.ts
  • tests/e2e/skills.e2e.test.ts
  • tests/e2e/subscription.e2e.test.ts
📝 Walkthrough

Walkthrough

The CLI adds alert and dashboard subscription commands backed by new notification and pulse schemas, API merge logic, command registration, end-to-end coverage, documentation, specialized skills, and a package version bump.

Changes

Notification management

Layer / File(s) Summary
Notification and subscription contracts
src/domain/cron.ts, src/domain/notification.ts, src/domain/pulse.ts, src/domain/transform-job.ts
Adds typed notification and pulse schemas, compact views, rendering helpers, input validation, and shared cron display types.
Question alert commands
src/commands/alert/*, src/commands/card/alerts.ts, src/commands/card/index.ts
Adds alert CRUD, send, archive, card filtering, card-alert validation, and merge-safe update behavior.
Dashboard subscription commands
src/commands/subscription/*, src/commands/dashboard/subscriptions.ts, src/commands/dashboard/index.ts
Adds subscription CRUD, archive, dashboard filtering, and state-preserving update behavior.
CLI registration and end-to-end validation
src/main.ts, src/runtime/command-help.test.ts, tests/e2e/*
Registers the new command groups, expands command-tree checks, and tests alert and subscription workflows.
Documentation and release metadata
README.md, skill-data/core/SKILL.md, skill-data/notification/SKILL.md, package.json
Documents commands and notification contracts, refreshes bundled skills, and bumps the package version to 0.2.2.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Command
  participant MetabaseAPI
  participant Renderer
  CLI->>Command: Execute alert or subscription command
  Command->>MetabaseAPI: Request resource operation
  MetabaseAPI-->>Command: Return validated resource
  Command->>Renderer: Render result or summary
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the change, but it is too generic to clearly describe the main update. Use a more specific title such as "Add CLI support for alerts and subscriptions".
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch alerts-subscriptions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@alxnddr alxnddr force-pushed the alerts-subscriptions branch from 8e124bf to f6f5541 Compare July 14, 2026 18:51

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (6)
src/commands/alert/list.ts (2)

1-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate NotificationApiList schema across two command files. Both files define the identical z.array(Notification) schema locally to parse the same /api/notification list response; centralize it once.

  • src/commands/alert/list.ts#L1-L15: export a shared array-parse schema (e.g. NotificationList = z.array(Notification)) from src/domain/notification.ts and import it here instead of the local NotificationApiList.
  • src/commands/card/alerts.ts#L1-L11: import the same shared schema instead of redeclaring NotificationApiList locally.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/alert/list.ts` around lines 1 - 15, Centralize the shared
notification-list schema by exporting a single z.array(Notification) schema from
src/domain/notification.ts. In src/commands/alert/list.ts and
src/commands/card/alerts.ts, import that shared schema and remove each local
NotificationApiList declaration, updating references to use the shared symbol.

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

NotificationApiList duplicated with src/commands/card/alerts.ts.

This exact schema (z.array(Notification)) is redefined identically in src/commands/card/alerts.ts. See consolidated comment.

Also applies to: 15-15

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/alert/list.ts` at line 1, Remove the duplicate
NotificationApiList schema definition from the alert list command and reuse the
existing shared definition from the card alerts module. Update imports and
references as needed so both commands use the single z.array(Notification)
schema.
src/commands/card/alerts.ts (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

NotificationApiList duplicated with src/commands/alert/list.ts.

Same schema redefined verbatim in both files. See consolidated comment.

Also applies to: 11-11

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/card/alerts.ts` at line 1, Remove the duplicated
NotificationApiList schema from the alerts command and reuse the existing shared
definition from the alert list implementation. Update imports and references as
needed so both commands use the single canonical schema.
src/commands/subscription/list.ts (1)

10-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated PulseApiList schema across two command files. Both files declare an identical, unexported z.array(Pulse) validator for the raw /api/pulse list response; centralize it once to avoid drift.

  • src/commands/subscription/list.ts#L10: move PulseApiList into src/domain/pulse.ts (export it alongside Pulse/PulseCompact) and import it here instead of re-declaring locally.
  • src/commands/dashboard/subscriptions.ts#L11: drop the local PulseApiList declaration and import the same exported schema from src/domain/pulse.ts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/subscription/list.ts` at line 10, Centralize the duplicated
PulseApiList schema by exporting the z.array(Pulse) validator from
src/domain/pulse.ts alongside Pulse and PulseCompact. In
src/commands/subscription/list.ts:10 and
src/commands/dashboard/subscriptions.ts:11, remove the local declarations and
import the shared PulseApiList export.
src/domain/pulse.ts (1)

187-199: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

No conditional validation tying schedule_hour/schedule_day/schedule_frame to schedule_type.

PulseChannelInput leaves all schedule fields optional regardless of schedule_type, so e.g. a "monthly" channel can be submitted with only schedule_frame and no schedule_hour/schedule_day. That still fails fast in the sense of hitting the server, but the CLI loses the chance to give a precise, local error message the way it already does for invalid channel_type. Consider a .superRefine() (or discriminated union keyed on schedule_type) enforcing the per-type required fields documented in create.ts's details string.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/pulse.ts` around lines 187 - 199, Add conditional validation to
PulseChannelInput based on schedule_type, using superRefine or a discriminated
union. Require the schedule_hour, schedule_day, and schedule_frame fields
appropriate to each schedule type according to the documented rules in
create.ts, and attach precise validation errors to the missing fields while
preserving the existing field constraints.
src/commands/alert/patch.ts (1)

43-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a more specific type instead of object.

Using object bypasses type checking for the incoming patch payload. Since patch.payload originates from NotificationUpdateInput, consider utilizing its derived type to ensure better type safety during the spread operation.

♻️ Proposed refactor
-function mergePayload(current: Notification, patch: object): object {
+function mergePayload(
+  current: Notification,
+  patch: NonNullable<NotificationUpdateInput["payload"]>
+): object {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/alert/patch.ts` at line 43, Update mergePayload to replace the
generic object type for patch with the derived payload type from
NotificationUpdateInput, preserving type safety when spreading patch.payload and
merging it with current notification data.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 1099-1255: Update the flag tables for mb subscription list and mb
alert list to match the CLI examples: use --dashboard-id, --card-id,
--creator-id, and --recipient-id instead of the shortened names. Leave the
surrounding descriptions and examples unchanged.

In `@src/commands/alert/get.ts`:
- Around line 9-21: The get command advertises an unsupported --full option.
Update the command definition around defineMetabaseCommand and its run
implementation to either define and implement --full so it returns the hydrated
alert response, or remove all --full references from details and examples; keep
help text and runtime behavior consistent.

In `@src/commands/alert/send.ts`:
- Around line 11-45: Make the alert send command a CLI-only wrapper by moving
AlertSendResult and alertSendResultView into a dedicated result helper such as
send-result.ts, then reuse those exports from send.ts. Extract the
client.requestRaw call into an API helper such as sendAlert in patch.ts or a new
alert API module, and have run call that helper instead of performing HTTP
directly.

---

Nitpick comments:
In `@src/commands/alert/list.ts`:
- Around line 1-15: Centralize the shared notification-list schema by exporting
a single z.array(Notification) schema from src/domain/notification.ts. In
src/commands/alert/list.ts and src/commands/card/alerts.ts, import that shared
schema and remove each local NotificationApiList declaration, updating
references to use the shared symbol.
- Line 1: Remove the duplicate NotificationApiList schema definition from the
alert list command and reuse the existing shared definition from the card alerts
module. Update imports and references as needed so both commands use the single
z.array(Notification) schema.

In `@src/commands/alert/patch.ts`:
- Line 43: Update mergePayload to replace the generic object type for patch with
the derived payload type from NotificationUpdateInput, preserving type safety
when spreading patch.payload and merging it with current notification data.

In `@src/commands/card/alerts.ts`:
- Line 1: Remove the duplicated NotificationApiList schema from the alerts
command and reuse the existing shared definition from the alert list
implementation. Update imports and references as needed so both commands use the
single canonical schema.

In `@src/commands/subscription/list.ts`:
- Line 10: Centralize the duplicated PulseApiList schema by exporting the
z.array(Pulse) validator from src/domain/pulse.ts alongside Pulse and
PulseCompact. In src/commands/subscription/list.ts:10 and
src/commands/dashboard/subscriptions.ts:11, remove the local declarations and
import the shared PulseApiList export.

In `@src/domain/pulse.ts`:
- Around line 187-199: Add conditional validation to PulseChannelInput based on
schedule_type, using superRefine or a discriminated union. Require the
schedule_hour, schedule_day, and schedule_frame fields appropriate to each
schedule type according to the documented rules in create.ts, and attach precise
validation errors to the missing fields while preserving the existing field
constraints.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 22084c83-b398-4e4b-a898-3a4cf9067311

📥 Commits

Reviewing files that changed from the base of the PR and between 45f3efe and 8e124bf.

📒 Files selected for processing (35)
  • README.md
  • missing-skills.md
  • package.json
  • skill-data/core/SKILL.md
  • skill-data/notification/SKILL.md
  • src/commands/alert/archive.ts
  • src/commands/alert/create.ts
  • src/commands/alert/get.ts
  • src/commands/alert/index.ts
  • src/commands/alert/list.ts
  • src/commands/alert/patch.test.ts
  • src/commands/alert/patch.ts
  • src/commands/alert/send.ts
  • src/commands/alert/summary.ts
  • src/commands/alert/update.ts
  • src/commands/card/alerts.ts
  • src/commands/card/index.ts
  • src/commands/dashboard/index.ts
  • src/commands/dashboard/subscriptions.ts
  • src/commands/subscription/archive.ts
  • src/commands/subscription/create.ts
  • src/commands/subscription/get.ts
  • src/commands/subscription/index.ts
  • src/commands/subscription/list.ts
  • src/commands/subscription/patch.test.ts
  • src/commands/subscription/patch.ts
  • src/commands/subscription/update.ts
  • src/domain/cron.ts
  • src/domain/notification.ts
  • src/domain/pulse.ts
  • src/domain/transform-job.ts
  • src/main.ts
  • src/runtime/command-help.test.ts
  • tests/e2e/alert.e2e.test.ts
  • tests/e2e/subscription.e2e.test.ts
💤 Files with no reviewable changes (1)
  • missing-skills.md

Comment thread README.md
Comment thread src/commands/alert/get.ts
Comment on lines +9 to +21
export default defineMetabaseCommand({
meta: { name: "get", description: "Get a question alert by id" },
details:
"`--full` includes the hydrated card the alert watches, alongside its schedules and handlers.",
capabilities: { minVersion: 58 },
args: {
...outputFlags,
...profileFlag,
...connectionFlags,
id: { type: "positional", description: "Alert id", required: true },
},
outputSchema: Notification,
examples: ["mb alert get 9", "mb alert get 9 --full --json"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm no "full" flag/hydration exists anywhere in the alert command chain
rg -n '\bfull\b' src/commands/alert src/commands/card --type=ts

Repository: metabase/metabase-cli

Length of output: 381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== get.ts ==\n'
cat -n src/commands/alert/get.ts

printf '\n== alert directory refs to full ==\n'
rg -n '\bfull\b' src/commands/alert src/domain src/core --type=ts || true

printf '\n== defineMetabaseCommand ==\n'
fd -a 'defineMetabaseCommand' src || true
rg -n 'function defineMetabaseCommand|const defineMetabaseCommand|export .*defineMetabaseCommand' src --type=ts || true

printf '\n== notification schema ==\n'
cat -n src/domain/notification.ts

printf '\n== fetchAlert / related alert commands ==\n'
rg -n 'fetchAlert\(|NotificationCompact|NotificationCardPayload|hydrate|card_id' src/commands/alert src/domain --type=ts || true

Repository: metabase/metabase-cli

Length of output: 29172


--full is advertised but not implemented.
details and examples mention --full, but args never defines it and run always returns the compact alert payload. Either wire up a real full response or remove the flag from the help text/example.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/alert/get.ts` around lines 9 - 21, The get command advertises an
unsupported --full option. Update the command definition around
defineMetabaseCommand and its run implementation to either define and implement
--full so it returns the hydrated alert response, or remove all --full
references from details and examples; keep help text and runtime behavior
consistent.

Comment on lines +11 to +45
export const AlertSendResult = z.object({
id: z.number().int(),
sent: z.boolean(),
});
export type AlertSendResultJson = z.infer<typeof AlertSendResult>;

const alertSendResultView: ResourceView<AlertSendResultJson> = {
compactPick: AlertSendResult,
tableColumns: [
{ key: "id", label: "ID" },
{ key: "sent", label: "Sent" },
],
};

export default defineMetabaseCommand({
meta: { name: "send", description: "Send a question alert now, off-schedule" },
details:
"Delivers to every handler on the alert, ignoring its send condition and schedule. Requires the channel to be configured on the server (email needs SMTP set up).",
capabilities: { minVersion: 58 },
args: {
...outputFlags,
...profileFlag,
...connectionFlags,
id: { type: "positional", description: "Alert id", required: true },
},
outputSchema: AlertSendResult,
examples: ["mb alert send 9", "mb alert send 9 --json"],
async run({ args, ctx, getClient }) {
const id = parseId(args.id);
const client = await getClient();
await fetchAlert(client, id);
await client.requestRaw(`/api/notification/${id}/send`, {
method: "POST",
expectContentType: "binary",
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract HTTP, parsing, and formatting logic from the command file.

The send.ts command file contains a Zod schema (AlertSendResult), view formatting (alertSendResultView), and direct HTTP logic (client.requestRaw). As per coding guidelines, command files must be strictly CLI shell only: no HTTP, no parsing, and no formatting logic.

Please extract the schema and view to a helper file (e.g., send-result.ts) and move the HTTP call to an API helper function (e.g., sendAlert inside patch.ts or a new API file).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/alert/send.ts` around lines 11 - 45, Make the alert send command
a CLI-only wrapper by moving AlertSendResult and alertSendResultView into a
dedicated result helper such as send-result.ts, then reuse those exports from
send.ts. Extract the client.requestRaw call into an API helper such as sendAlert
in patch.ts or a new alert API module, and have run call that helper instead of
performing HTTP directly.

Source: Coding guidelines

@alxnddr alxnddr force-pushed the alerts-subscriptions branch from f6f5541 to 44f5a85 Compare July 14, 2026 19:11
@alxnddr alxnddr force-pushed the alerts-subscriptions branch from 44f5a85 to 5e96027 Compare July 14, 2026 19:29
@alxnddr alxnddr merged commit dc3e6c8 into main Jul 15, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants