AgentStack
SKILL unreviewed MIT Self-run

Notify

skill-mugwork-mug-notify · by mugwork

Send email or SMS notifications from workflows — styled templates with CTA buttons, surface links, workspace branding. Covers ctx.notify.email(), ctx.surfaceUrl(), and local dev delivery.

No reviews yet
0 installs
7 views
0.0% view→install

Install

$ agentstack add skill-mugwork-mug-notify

Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Dangerous shell/eval execution.

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution Used

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Notify? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Send Notifications

Send email or SMS notifications from workflows. Mug handles delivery (Resend for email, Twilio for SMS), styled HTML templates, and surface link generation. SMS works out of the box — BYOK optional for your own number.

For full API reference (all options, template rendering, BYOK configuration), see .mug/docs/notifications.md.

Input

Notification description: $ARGUMENTS

If no argument provided, ask the user what they need to send. Clarify:

  • What triggers the notification? (form submission, workflow step, schedule)
  • Who receives it? (employee, manager, customer — how do you know their email/phone?)
  • What should the message say?
  • Should it link back to a surface? (portal, form, approval inbox)

Step 1 — Plan the notification

Identify:

  • Which workflow sends it
  • What data is available at send time (from query results, form params, etc.)
  • Whether it needs a CTA button linking to a surface
  • Email or SMS (or both)

Present a brief plan and wait for confirmation.

Step 2 — Write the notification code

Add ctx.notify.email() or ctx.notify.sms() to the workflow:

import { workflow } from "@mugwork/mug";

workflow("handle-request", async (ctx) => {
  const params = ctx.params as Record;

  // Insert the record
  await ctx.exec("INSERT INTO requests (...) VALUES (...)", [...]);

  // Notify the manager with a link to the approval surface
  await ctx.notify.email({
    to: "manager@company.com",
    subject: `New request from ${params.employee_name}`,
    message: `${params.employee_name} submitted a time-off request for ${params.start_date} to ${params.end_date}.`,
    cta: {
      label: "Review Request",
      url: ctx.surfaceUrl("approvals"),
    },
  });

  return { notified: true };
});

ctx.notify.email(options)

await ctx.notify.email({
  to: string;            // recipient email address
  message: string;       // supports: **bold**, *italic*, - lists, 1. lists, [links](url). No headers/code/tables/images.
  subject?: string;      // email subject line (default: "Notification from Mug")
  fromName?: string;     // sender name (default: workspace name, e.g. "Narvick Construction")
  cta?: {
    label: string;       // button text, e.g. "Review Request"
    url: string;         // button URL — use ctx.surfaceUrl() for surface links
  };
});

ctx.notify.sms(options)

await ctx.notify.sms({
  to: string;       // phone number (E.164 format: +1234567890)
  message: string;  // message body (plain text, no markdown)
});

ctx.surfaceUrl(surfaceId, path?)

Generate a URL to a surface. Returns the correct URL for dev (localhost) and production (workspace subdomain).

ctx.surfaceUrl("approvals")
// dev:  "http://localhost:8787/approvals"
// prod: "https://my-workspace.mug.work/approvals"

ctx.surfaceUrl("portal", `/row/${requestId}`)
// dev:  "http://localhost:8787/portal/row/42"
// prod: "https://my-workspace.mug.work/portal/row/42"

Always use ctx.surfaceUrl() instead of hardcoding URLs — it handles dev/prod automatically.

Email templates from files

For complex email bodies, store HTML templates in files/ and load them at runtime:

const template = await ctx.fileText("templates/weekly-report.html");
const body = template.replace("{{name}}", customer.name).replace("{{total}}", total);
await ctx.notify.email({ to: customer.email, message: body, subject: "Weekly Report" });

Drop templates in files/templates/ and run mug push to upload them to production.

Step 3 — Test locally

mug dev          # start the dev server
mug run    # trigger the workflow

Dev email redirect: dev.email in mug.json is auto-set to the logged-in user's email by mug init/mug create/mug update/mug dev. All email notifications redirect to this address in dev mode.

{
  "dev": {
    "email": "developer@example.com"
  }
}

All emails redirect to this address. The subject shows the original recipient: [DEV → manager@company.com] New request. The console logs redirects:

[email] Redirecting: manager@company.com → developer@example.com

SMS sends via Twilio in dev too. Check the console for delivery status.

Step 4 — Verify the email

Check the received email:

  • Subject line is correct
  • Message body renders with proper formatting
  • CTA button links to the right surface URL
  • Sender name shows the workspace name (not "Mug")

Notification Patterns

Common patterns for notifications in workflows.

Form submission notification

Notify someone when a form is submitted. The handler workflow receives form data in ctx.params:

workflow("handle-intake", async (ctx) => {
  const { name, email, issue } = ctx.params as Record;

  await ctx.exec("INSERT INTO tickets (name, email, issue, status) VALUES (?, ?, ?, 'open')",
    [name, email, issue]);

  await ctx.notify.email({
    to: "support@company.com",
    subject: `New ticket from ${name}`,
    message: `**${name}** (${email}) submitted a support ticket:\n\n${issue}`,
    cta: { label: "View Tickets", url: ctx.surfaceUrl("tickets") },
  });
});

Approval result notification

Notify the requester when their request is approved/denied:

workflow("handle-approval", async (ctx) => {
  const params = ctx.params as Record;
  const status = params.action === "approve" ? "approved" : "denied";

  await ctx.exec("UPDATE requests SET status = ? WHERE id = ?", [status, params.id]);

  await ctx.notify.email({
    to: params.employee_email,
    subject: `Your request was ${status}`,
    message: `Your time-off request for ${params.start_date} to ${params.end_date} has been ${status}.`,
    cta: { label: "View Details", url: ctx.surfaceUrl("portal", `/row/${params.id}`) },
  });
});

Scheduled report notification

Send a summary on a schedule:

workflow("weekly-summary", async (ctx) => {
  const openTickets = await ctx.query("SELECT COUNT(*) as count FROM tickets WHERE status = 'open'");
  const resolved = await ctx.query("SELECT COUNT(*) as count FROM tickets WHERE status = 'resolved' AND resolved_at > date('now', '-7 days')");

  await ctx.notify.email({
    to: "owner@company.com",
    subject: `Weekly Summary — ${new Date().toLocaleDateString()}`,
    message: `**This week:**\n- ${resolved[0].count} tickets resolved\n- ${openTickets[0].count} tickets still open`,
    cta: { label: "View Dashboard", url: ctx.surfaceUrl("dashboard") },
  });
});

SMS notification

SMS is best for urgent, time-sensitive notifications. Keep messages short.

await ctx.notify.sms({
  to: "+1234567890",
  message: `New job assigned: ${job.address}. Reply ACCEPT or DECLINE.`,
});

BYOK — bring your own keys

To send from your own email domain or Twilio account, set your own API keys:

mug secret set RESEND_API_KEY=re_xxxxx          # your Resend key
mug secret set TWILIO_ACCOUNT_SID=AC_xxxxx      # your Twilio account
mug secret set TWILIO_AUTH_TOKEN=xxxxx
mug secret set TWILIO_PHONE_NUMBER=+1xxxxx

BYOK sends bypass Mug's notification metering — unlimited sends, your own deliverability reputation.

AI also supports BYOK — mug secret set ai.anthropic= for unlimited AI calls. See the /ai skill for setup.


Complete Example

Full approval flow: form submission triggers manager notification, approval triggers employee notification.

// workflows/handle-request.ts
import { workflow } from "@mugwork/mug";

workflow("handle-request", async (ctx) => {
  const p = ctx.params as Record;

  await ctx.exec(`INSERT INTO requests (id, employee_name, employee_email, type, start_date, end_date, hours, reason, approver_email, status, created_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', datetime('now'))`,
    [crypto.randomUUID(), p.employee_name, p.employee_email, p.type, p.start_date, p.end_date, p.hours, p.reason, "manager@company.com"]);

  await ctx.notify.email({
    to: "manager@company.com",
    subject: `Time-off request from ${p.employee_name}`,
    message: `**${p.employee_name}** is requesting ${p.type} from ${p.start_date} to ${p.end_date} (${p.hours} hours).\n\nReason: ${p.reason}`,
    cta: { label: "Review Request", url: ctx.surfaceUrl("approvals") },
  });

  return { status: "pending", notified: "manager@company.com" };
});

// workflows/handle-approval.ts
import { workflow } from "@mugwork/mug";

workflow("handle-approval", async (ctx) => {
  const p = ctx.params as Record;
  const status = p.action === "approve" ? "approved" : "denied";

  await ctx.exec("UPDATE requests SET status = ?, reviewed_at = datetime('now') WHERE id = ?", [status, p.id]);

  await ctx.notify.email({
    to: p.employee_email,
    subject: `Your time-off request was ${status}`,
    message: `Your ${p.type} request for ${p.start_date} to ${p.end_date} has been **${status}**.`,
    cta: { label: "View My Requests", url: ctx.surfaceUrl("portal") },
  });

  return { id: p.id, status };
});

For form creation (the request form), see the /form skill. For portals (the approval inbox and employee portal), see the /portal skill. For complex workflow logic (AI classification, multi-source queries), see the /workflow skill. For custom branding on email notifications (logo in header, accent color on CTA buttons), add a branding section to mug.json — see .mug/docs/notifications.md.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.