setup-slack
Use this skill when the user asks to set up Slack messaging for mu.
Goal: get /mu ... working end-to-end in Slack, with the agent doing all local setup and asking the user only for Slack-console actions/secrets the agent cannot perform.
Contents
Required user-provided inputs
- Public webhook base URL reachable by Slack (for example
https://mu.example.com) - Slack app Signing Secret
- Slack app Bot User OAuth Token (
xoxb-...) for outbound replies/media
Agent-first workflow
0) Verify local prerequisites (agent)
bashcommand -v mu >/dev/null && echo "mu: ok" command -v python3 >/dev/null && echo "python3: ok (required for config patching)" command -v curl >/dev/null && echo "curl: ok (required for API checks)" command -v jq >/dev/null && echo "jq: ok (required for filtered JSON checks)"
If any required command is missing, stop and ask the user to install it before proceeding.
1) Preflight local state
bashmu control status --pretty mu store paths --pretty mu control identities --all --pretty
If no running server is available when reload/route checks are attempted, ask the user to run mu serve in another terminal, then continue.
2) Drive user through Slack console steps (minimal ask)
Ask the user to do only these actions in Slack API UI:
- Create/install a Slack app in target workspace.
- Configure request URL for all inbound surfaces to:
https://<public-base>/webhooks/slack
- Enable at least:
- Slash command
/mu - Event Subscriptions (
app_mention) - Interactivity
- Slash command
- Ensure bot scopes include:
app_mentions:readchat:writefiles:readfiles:write
- Return
signing_secretandbot_tokento the agent.
3) Agent patches mu config (do not ask user to edit JSON)
Use this canonical patch snippet (preserves unrelated keys):
bashexport MU_SLACK_SIGNING_SECRET='<SLACK_SIGNING_SECRET>' export MU_SLACK_BOT_TOKEN='<SLACK_BOT_TOKEN>' config_path="$(mu control status --json | python3 -c 'import json,sys; print(json.load(sys.stdin)["config_path"])')" python3 - "$config_path" <<'PY' import json import os import sys from pathlib import Path path = Path(sys.argv[1]) if path.exists(): data = json.loads(path.read_text()) else: data = {"version": 1, "control_plane": {}} cp = data.setdefault("control_plane", {}) adapters = cp.setdefault("adapters", {}) slack = adapters.setdefault("slack", {}) slack["signing_secret"] = os.environ["MU_SLACK_SIGNING_SECRET"] slack["bot_token"] = os.environ["MU_SLACK_BOT_TOKEN"] path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(data, indent=2) + "\n") PY
Replace placeholder values with secrets from the user, then unset MU_SLACK_SIGNING_SECRET MU_SLACK_BOT_TOKEN after patching.
4) Reload and verify adapter status
bashmu control reload mu control status --json --pretty curl -sS http://localhost:3000/api/control-plane/channels | jq '.channels[] | select(.channel=="slack")'
Verify:
configured: trueactive: true(when server is running)- route
/webhooks/slack
5) Identity link with least user effort
Preferred flow:
- Ask user to send one Slack turn (for example
/mu status). - Extract actor + tenant IDs from audit.
- Link identity as operator.
bashmu store tail cp_adapter_audit --limit 50 --json \ | jq -r '[.[] | select(.channel=="slack")] | last | "actor=\(.actor_id) tenant=\(.channel_tenant_id)"' mu control link --channel slack --actor-id <actor-id> --tenant-id <tenant-id> --role operator mu control identities --pretty
If audit rows are unavailable, ask user for Slack IDs (U... user id, T... workspace id).
6) Smoke test + forensic confirmation
bashmu control status --pretty mu store tail cp_adapter_audit --limit 20 --pretty mu store tail cp_outbox --limit 20 --pretty
Ask user to run /mu status again and confirm response delivery.
Evaluation scenarios
-
Happy path onboarding
- Inputs: valid public base URL,
signing_secret,bot_token, runningmu serve. - Expected:
/api/control-plane/channelsreports Slackconfigured=trueandactive=true; Slack/mu statusreturns a response.
- Inputs: valid public base URL,
-
Signature validation failure
- Inputs: wrong
signing_secretconfigured. - Expected: inbound command is rejected;
cp_adapter_auditshows deterministic signature/timestamp reason code; skill proposes secret rotation and reload as next step.
- Inputs: wrong
-
Outbound token missing/invalid
- Inputs: webhook ingress works but
bot_tokenmissing or invalid. - Expected: ingress may ACK but delivery fails;
cp_outbox/adapter audit expose concrete failure reason; skill guides user to update token and re-run smoke test.
- Inputs: webhook ingress works but
Safety and UX requirements
- Never expose secrets in chat logs unnecessarily; redact in summaries.
- Do not overwrite unrelated
config.jsonfields. - Keep the user ask short and sequential (one external-console step at a time).
- If setup fails, report concrete reason codes from audit/outbox and propose the next smallest recovery step.


