Skip to content

feat(notification-channels): command group + MCP tools (KLA-485 Phase 1, 76%) - #99

Open
jklaassenjc wants to merge 1 commit into
mainfrom
juergen/notifications-channels-cmd
Open

feat(notification-channels): command group + MCP tools (KLA-485 Phase 1, 76%)#99
jklaassenjc wants to merge 1 commit into
mainfrom
juergen/notifications-channels-cmd

Conversation

@jklaassenjc

Copy link
Copy Markdown
Collaborator

What

Adds jc notification-channels (list/get/create/update/delete) and the five matching MCP tools (notification_channels_*), covering the JumpCloud Console API Notifications Channels area — the last Phase 1 quick-win. Moves the coverage scorecard 75% → 76%.

A notification channel is an alert delivery target: a webhook URL, a slack channel, or an email recipient set.

Contract (verified live against the tenant 2026-07-31)

The empirical gate found two things the spec doesn't tell you:

Aspect Finding
List GET /notifications/channels{channels, count} — wrapped, ResponseKey:"channels"; id is objectId
Envelope single GET, POST, and PATCH all wrap the object in {channel}
type enum CHANNEL_TYPE_WEBHOOK / _EMAIL / _SLACK
Update verb is PATCH but it is not partial — a sparse body 400s "channel name is required", so update read-modify-writes: fetch, strip server-managed createdAt/By,updatedAt/By,organizationObjectId, PATCH the whole {channel} (channel objectId + config.*.objectId kept)
Delete 200, empty

Design

  • Webhook config is flag-expressible: --url + --auth-type/--auth-token/--auth-username/--auth-password/--ssl-verification.
  • Email/Slack carry nested recipient/channel arrays → supplied via --config-file (CLI) / config_json (MCP), the raw JSON for the config object. --config-file also covers advanced webhook.
  • Shared internal/notification package (envelope, CHANNEL_TYPE_* enum, strip list, webhook-config builder) — CLI + MCP call one source of truth (same pattern as internal/command, internal/savedview, internal/serviceaccount, internal/role).
  • Every mutation is plan-first (--plan / execute=false) → flows through the MCP step-up gate automatically.

Tests

  • internal/cmd/notification_channels_test.go + internal/mcp/notification_channels_test.go: list ResponseKey unwrap, --ids, {channel} envelope unwrap, webhook create body, --config-file/config_json path, --type validation, email-needs-config-file, RMW (partial update preserves name/type/config, strips server-managed keys, wraps {channel}), plan-vs-execute on every mutation.
  • internal/mcp/tools_test.go: registration list + count 230 → 235.
  • Full go test ./... green, go vet ./... clean.

Live verification

Full lifecycle through the jc binary against the real tenant: create webhook → get-by-name → update (disable-only; RMW preserved url/type/name) → delete. Tenant left at its 2 pre-existing channels.

Scope note

The sibling tags Webhook Notifications / Slack Notifications (POST .../test) and Webhook Notifications Channels (a GET) remain out of scope — this PR covers the canonical Notifications Channels CRUD area.

🤖 Generated with Claude Code

https://claude.ai/code/session_01B8FxojUvPUc2fM4okCm8Tb

…hase 1, 76%)

Add `jc notification-channels` (list/get/create/update/delete) and the five
matching MCP tools, covering the JumpCloud Console API "Notifications Channels"
area — the last Phase 1 quick-win. Moves the coverage scorecard 75% → 76%.

A notification channel is an alert delivery target: a webhook URL, a Slack
channel, or an email recipient set (type ∈ webhook/email/slack).

Contract verified live against the tenant 2026-07-31:
  - GET /notifications/channels wraps the array in {channels, count}
    (ResponseKey); id is objectId.
  - single GET, POST, and PATCH all wrap the object in {channel}.
  - type is the enum CHANNEL_TYPE_WEBHOOK/_EMAIL/_SLACK.
  - update is PATCH but is NOT partial — a sparse body 400s "channel name is
    required", so update read-modify-writes: fetch, strip server-managed
    createdAt/By,updatedAt/By,organizationObjectId, PATCH the whole {channel}
    (channel objectId and config.*.objectId are kept).

Webhook config is flag-expressible (--url + --auth-*); email/slack carry nested
recipient/channel arrays, supplied via --config-file (CLI) / config_json (MCP).

The wire-contract (envelope, CHANNEL_TYPE_* enum, strip list, webhook config
builder) lives in a shared internal/notification package so the CLI and MCP
surfaces can't drift — same pattern as internal/command, internal/savedview,
internal/serviceaccount, internal/role.

Every mutation is plan-first (execute/--plan) and flows through the MCP step-up
gate. Tests: CLI + MCP cover ResponseKey unwrap, {channel} envelope unwrap,
webhook body, config-file/config_json path, type validation, RMW (partial
update preserves name/type/config, strips server-managed), plan-vs-execute.
Tool count 230 → 235. Full `go test ./...` green, `go vet ./...` clean.

Live-verify: create webhook → get → update (disable-only, RMW preserved
url/type/name) → delete, tenant left at its 2 pre-existing channels.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8FxojUvPUc2fM4okCm8Tb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

3 participants