Install
$ agentstack add skill-mugwork-mug-workflow Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 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 Used
- ✓ 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.
About
Create a Workflow
Build a multi-step workflow that queries data, applies AI logic, and takes action (notify, update records, etc.).
For full API reference (all ctx methods, WorkflowResult, ops database schema, production execution), see .mug/docs/workflows.md. For cross-cutting API reference (ctx.params shape, error handling, data patterns), see .mug/docs/api.md.
Input
Workflow name or description: $ARGUMENTS
If no argument provided, ask the user what the workflow should do. Clarify:
- What data sources does it need? (which databases/tables)
- What logic or AI classification should it apply?
- What actions should it take? (SMS, email, Slack, update records, write back to external APIs via
ctx.action()) - Does it need to write back to external systems? (requires a bidirectional
connector()— see/connectorskill) - Should it run on a schedule or only manually?
Step 1 — Name and plan
Pick a kebab-case name for the workflow (e.g., invoice-followup, lead-scoring, daily-report).
Present a brief plan:
- Data queries needed
- AI classification steps (if any)
- Actions/notifications
- Estimated step count
Wait for user confirmation.
Step 2 — Write the workflow
Create the file at workflows/.ts:
import { workflow } from "@mugwork/mug";
workflow("", async (ctx) => {
// Fetch all open items from the workspace database
const rows = await ctx.query("SELECT ...");
// Classify each item using AI with smart routing
for (const row of rows) {
const result = await ctx.ai("fast", {
prompt: ``,
system: "Reply with exactly one word.",
maxTokens: 10,
});
}
// Notify the relevant person about the results
await ctx.notify.sms({ to: "...", message: "..." });
// Return a summary of what happened
return { summary: "..." };
}, {
description: "",
// webhook: true, // or { auth: "hmac", secret: "WEBHOOK_SECRET" }
// inbound: "sms", // or "email" or "slack"
// trigger: { source: "quickbooks", table: "invoices", on: "insert" },
});
Descriptions
Every workflow must have a description in the options object — a plain English sentence explaining what the workflow does and why.
Every ctx.* call and return statement must have a // comment on the line above describing what it does. These comments appear in the workspace explorer as human-readable step descriptions.
// Check for overdue invoices
const overdue = await ctx.query("SELECT * FROM invoices WHERE ...");
// Send a reminder SMS to the customer
await ctx.notify.sms({ to: inv.phone, message: "..." });
// No overdue invoices found, nothing to do
return { skipped: true };
ctx API reference
ctx.query(sql, params?)— query unified workspace database (table names auto-resolved across sources).ctx.query("source", sql, params?)for scoped queries.ctx.exec(sql, params?)— write to workspace database. Always use the one-arg form for your own tables. The two-arg formctx.exec("source", sql)is only for writing back to a synced connector source — never pass a made-up database name. Tables are auto-created at deploy time — if your workflow INSERTs into a table with a column list, the platform creates it automatically (TEXT columns). No separate CREATE TABLE needed. Schema evolution is automatic — adding columns to a CREATE TABLE IF NOT EXISTS will add them to the existing table without losing data.ctx.ai(model, { prompt, system, maxTokens?, routing?, billing? })— returns{ text, model, usage, routing? }- Use tier names:
"fast"(cheap),"balanced"(mid),"powerful"(best). For multi-provider config and BYOK, see the/aiskill. ctx.notify.email({ to, message, subject?, fromName?, cta? })— send styled email with optional CTA button. For templates, surface links, and BYOK, use the/notifyskill.ctx.notify.sms({ to, message })— send SMSctx.notify.slack({ to, message })— send Slack messagectx.surfaceUrl(surfaceId, path?)— generate URL to a surface (dev/prod-aware). Use in notification CTAs.ctx.file(path)— read a file fromfiles/asArrayBuffer(local in dev, R2 in production)ctx.fileText(path)— read a file as UTF-8 string. Use for templates, CSV data, JSON configs.ctx.collect(options)— create a form that collects data from users. Returns the form URL. For the full form schema walkthrough (field types, conditionals, pages, access modes), use the/formskill.ctx.secret(name)— read a workspace secret by name (from.mug/secrets). Throws if not found.ctx.credential(name)is an alias — both work identically in workflows. Use for external API keys, tokens, or credentials.ctx.waitFor(eventName, { timeout?, message? })— pause workflow until external event. Returns{ payload, type, timedOut }. See Step 5.ctx.waitForUrl(eventName)— generate a one-time callback URL for embedding in notifications. See Step 5.ctx.agent(name, { goal, context?, sessionKey?, caps? })— invoke a custom AI agent. See the/agentsskill.ctx.http(url, options?)— outbound HTTP request. Returns{ status, headers, body, json, ok }. Throws on non-2xx by default. Options:{ method?, headers?, body?, throwOnError?: false, retry?: { attempts? } | false, timeout?, sign?: { secret, header? } }. Auto-retries connection errors and 429 with exponential backoff.ctx.respond(body, status?)— set a custom HTTP response for webhook-triggered workflows. First call wins. Use for Slack URL verification, Twilio TwiML, etc.ctx.search(query, { source?, limit? })— semantic search across synced data. Returns{ score, table, primaryKey, row }[]. Requires deployed workspace.ctx.ask(question, { source?, limit?, model?, system? })— full RAG: searches data, feeds to LLM, returns{ answer, sources, usage }. Requires deployed workspace.
Drop files in the files/ directory and run mug push to upload them to production. Check .mug/manifest.json for what's available remotely.
Every ctx.* call is automatically logged with timing, input/output, and token usage.
Step 3 — Test locally
mug dev # start the dev server (if not already running — auto-detects new workflows, no restart needed)
mug run # execute the workflow
Workflows in workflows/ are auto-discovered — no import or restart needed.
Check the output:
[+]= success,[x]= error- Each step shows name, duration, and any errors
- AI steps show token usage
If the workflow errors, read the error message and fix. Common issues:
- Table doesn't exist yet → add
ctx.exec()to create it, or sync the source first - AI prompt returns unexpected format → adjust the prompt or add parsing
No credentials for ""→ checkmug secret setandmug.jsonconnection configAI not configured: missing MUG_AI service binding→ workspace needs to be deployed first, or checkmug devis running
All ctx.* methods throw on failure. Use try/catch when you want to handle errors gracefully (e.g., continue processing other items if one notification fails). See .mug/docs/api.md for the full error handling reference.
Workflows can also be triggered by webhooks — see .mug/docs/api.md for webhook config.
Step 4 — (Optional) Pause for external events
Use ctx.waitFor() to pause a workflow until an external event arrives (approval, payment confirmation, reply). Zero cost while waiting.
// Send an approval request with a callback URL
const callbackUrl = await ctx.waitForUrl("approval");
await ctx.notify.email({
to: manager,
message: "New request needs approval.",
cta: { label: "Approve", url: `${callbackUrl}?action=approve` },
});
// Pause until the manager clicks or 48 hours pass
const event = await ctx.waitFor("approval", { timeout: "48 hours" });
if (event.timedOut) {
await ctx.notify.sms({ to: submitter, message: "Your request timed out." });
} else if (event.payload?.action === "approve") {
await ctx.exec("UPDATE requests SET status = ? WHERE id = ?", ["approved", requestId]);
}
ctx.waitForUrl(eventName)— generates a one-time callback URL. When visited (GET or POST), it delivers the event.ctx.waitFor(eventName, { timeout?, message? })— pauses until the event arrives or timeout. Returns{ payload, type, timedOut }.- Callback URLs expire after 7 days.
- In local dev,
waitForresolves immediately with an empty payload for testing.
For full signatures and options, see .mug/docs/api.md.
Step 5 — View execution log
mug logs # dev server or auto-fallback to production
mug logs --production # force production logs
Shows every run with per-step details: timing, inputs, outputs, token counts. If no dev server is running, automatically fetches production logs.
Step 6 — (Optional) Add a schedule
Add a schedule option to your workflow. Use a cron string for simple patterns, or a structured object for complex ones:
// Simple: cron string
workflow("daily-report", handler, {
schedule: "0 9 * * 1-5", // weekdays at 9am
});
// Complex: structured schedule
workflow("board-meeting-prep", handler, {
schedule: {
weekday: "tuesday",
nth: 3, // 3rd Tuesday of the month
time: "09:00",
skipHolidays: true, // skip workspace holidays
},
});
Cron strings (simple)
"*/15 * * * *"— every 15 minutes"0 9 * * 1-5"— weekdays at 9am"0 0 * * *"— daily at midnight"0 9 * * 1"— Mondays at 9am
Schedule object (complex)
| Field | Type | Description | |-------|------|-------------| | weekday | "monday" .. "sunday" | Day of week | | nth | 1-5 or -1 | Nth occurrence of weekday in month (-1 = last) | | time | "HH:MM" | Time in 24h format | | interval | "15m", "1h", "6h" | Repeat interval | | between | ["14:00", "16:00"] | Time window for interval | | skipHolidays | true or "US" | Skip workspace holidays (true) or a specific country | | skipDates | ["2026-12-24"] | Skip specific dates | | cron | "0 9 1 * *" | Base cron (use with modifiers like skipHolidays) | | timezone | "America/New_York" | IANA timezone |
Holiday calendar
Configure workspace holidays in mug.json:
{
"holidays": {
"country": "US",
"include": [
"christmas-eve",
"black-friday",
"easter-monday",
{ "name": "Company Retreat", "rule": "3rd wednesday of september" }
],
"exclude": ["columbus-day-indigenous-peoples-day"]
}
}
Built-in holiday slugs: christmas-eve, black-friday, easter-monday. Custom rules use "Nth weekday of month" patterns. Exclude holidays by slug.
Step 7 — Deploy
mug validate # type-checks workspace code and runs 40+ validation rules
mug deploy # runs validation, bundles, and deploys to production
mug deploy runs validation automatically — TypeScript type errors appear as warnings but don't block deploy. Scheduled workflows run automatically in production.
When a workflow errors in production, the workspace owner and admins receive an email with the error details, failed step, and duration. Duplicate emails for the same workflow are suppressed for 5 minutes. Errors are also visible in the explorer (mug dev → Errors tab).
To trigger manually in production:
mug run --production
mug status
Inbound message routing
Receive SMS replies, inbound emails, and Slack interactions by routing them to a workflow. Configure via workflow options:
workflow("handle-sms", handler, { inbound: "sms" });
workflow("handle-email", handler, { inbound: "email" });
workflow("handle-slack", handler, { inbound: "slack" });
Each inbound channel delivers different ctx.params:
- SMS:
{ from: "+1234567890", body: "Yes, approved" } - Email:
{ from: "user@example.com", subject: "Re: Invoice #42", body: "Looks good" } - Slack:
{ userId: "U12345", actionId: "approve_btn", actionValue: "yes" }
Webhook URLs are shown after mug deploy: https://api.mug.work/inbound/sms/, etc.
Send-and-wait pattern: Workflow 1 sends a notification and sets a status field in the database. Workflow 2 (the inbound handler) catches the reply and checks the status field to determine what to do.
For full inbound routing reference, see .mug/docs/api.md.
For collecting data from users (forms with submissions), see the /form skill. For displaying data and approval UIs (list+detail pages with action buttons), see the /portal skill. For email/SMS notifications with CTA buttons and surface links, see the /notify skill.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: mugwork
- Source: mugwork/mug
- License: MIT
- Homepage: https://mug.work
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.