{"id":14020,"plugin_id":"plugin_asdk_app_6a916657d9ec8191a3fa18146408479c","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:08:39.337Z","digest":"13e36bf09af0358db84c3768d5fbe519791183cfc9269680d6c3304d2fb4972a","against":null,"payload":{"name":"pingroom-mcp","description":"Reach a human through PingRoom — the event platform whose MCP connector this session has. Use this skill whenever the task involves notifying, alerting, or pinging a person; sending them a file, report, markdown, zip, image, or location; sharing a tappable link to their phone; asking a human a question and blocking on the answer; requesting an approve/deny decision before acting; handing a decision off to your authorizing human; or showing live progress on their lock screen while long work runs. Trigger it even when the user doesn't say \"PingRoom\" — phrases like \"let me know when it's done\", \"send this to my phone\", \"ask me before deploying\", \"ping the team\", \"notify me\", \"send me the report\", or \"show progress on my lock screen\" all mean this skill. Also use it when writing scripts or CI that must alert a person, and for managing PingRoom rooms, webhooks, or quick actions via the CLI.","included_files":[{"relative_path":"references/tools.md","size_in_bytes":17799}],"skill_md_contents":"---\nname: pingroom-mcp\ndescription: >-\n  Reach a human through PingRoom — the event platform whose MCP connector this\n  session has. Use this skill whenever the task involves notifying, alerting, or\n  pinging a person; sending them a file, report, markdown, zip, image, or\n  location; sharing a tappable link to their phone; asking a human a question\n  and blocking on the answer; requesting an approve/deny decision before acting;\n  handing a decision off to your authorizing human; or showing live progress on\n  their lock screen while long work runs. Trigger it even when the user doesn't\n  say \"PingRoom\" — phrases like \"let me know when it's done\", \"send this to my\n  phone\", \"ask me before deploying\", \"ping the team\", \"notify me\", \"send me the\n  report\", or \"show progress on my lock screen\" all mean this skill. Also use it\n  when writing scripts or CI that must alert a person, and for managing PingRoom\n  rooms, webhooks, or quick actions via the CLI.\n---\n\n# PingRoom: reach a human, get a real answer\n\nPingRoom turns \"the agent finished / needs input\" into an event on a person's\nphone: a push they feel, a card on their lock screen, a question they answer\nwith one tap. You reach it over MCP (`mcp__pingroom__*` tools); a paired\n`pingroom` CLI may also be available for what MCP doesn't carry.\n\nRead `references/tools.md` for the full 27-tool schema reference when you need\nexact parameters. This file teaches you which tool to reach for and the rules\nthat make the difference between \"sent\" and \"landed\".\n\n## The one rule that matters\n\n**A successful send proves PingRoom accepted delivery work — not that a person\nreceived, saw, or acted.** Never report \"I notified them\" as \"they know\".\nWhen the task needs a human to actually respond, use a primitive that closes\nthe loop, then block on its wait tool:\n\n| You need | Send with | Block with | Resolved states |\n|---|---|---|---|\n| Someone to see + confirm | `broadcast` with `requires_ack: true` | `wait_for_ack` | acked / expired |\n| A choice among 2–4 options | `ask_question` | `wait_for_answer` | answered / expired / cancelled |\n| Permission before acting | `request_approval` | `wait_for_approval` | approved / denied / expired |\n| Your authorizing human, privately | `create_handoff` | `wait_for_handoff` | acked or answered / expired |\n\nPending, timeout, enqueued, and delivery states are **not** answers. If a wait\nexpires, say so plainly and do not proceed as if consent was given.\n\n## Picking a room\n\nCall `list_rooms` first (cache the result for the conversation). Rules:\n\n- Personal rooms (`type: \"personal\"`) refuse `broadcast` — use\n  `trigger_quick_action` there.\n- Public rooms allow 160-char messages; private rooms 120. Titles cap at 40.\n- Sends into single-member rooms are refused (`validation_failed`) — a ping\n  needs a recipient other than the sender.\n- If the user names a room ambiguously, match on name case-insensitively; when\n  several match, ask which one rather than guessing.\n\n## Recipes\n\n### Plain ping\n`broadcast { invite_code, message, title?, action_icon? }`. Keep the message an\nevent, not prose: *what happened, what to do, is it done*. Set a `title` only\nwhen the room name alone wouldn't orient the reader.\n\n### Urgent / must-be-confirmed\n- `is_urgent: true` breaks through Focus/Do Not Disturb. Delivery-only — it\n  asks nothing of the recipient. Reserve it for genuinely time-sensitive events\n  or it trains people to ignore it.\n- `requires_ack: true` keeps the ping open with a lock-screen Acknowledge\n  button until one eligible recipient confirms. Add `ack_timeout_seconds`\n  (60–86400) when the confirmation is only useful for a while, then\n  `wait_for_ack { notification_id }`.\n- The two compose: urgent+ack is \"wake them and hold the door\".\n\n### Location ping\nPut a location in `data.location` — the app renders a tappable map:\n\n```json\n{ \"invite_code\": \"…\", \"message\": \"Meet here at 6\",\n  \"data\": { \"location\": { \"latitude\": 48.8584, \"longitude\": 2.2945,\n            \"label\": \"Eiffel Tower\", \"address\": \"Champ de Mars, Paris\" } } }\n```\n\n`latitude`/`longitude` are required inside `location`; `label` and `address`\nare what the human actually reads, so include at least `label`.\n\n### Link ping\n`data.url` (absolute http/https) turns the ping into a tappable link;\n`data.button_label` (≤26 chars) names the button, `data.label` (≤26) adds a\ncaption. Use for dashboards, PRs, docs — anything better opened than read in\n120 chars.\n\n### Structured data + threading\n`data` carries up to 25 keys / 8 KB of machine-readable context on every send\nprimitive — build numbers, commit SHAs, error codes. Never put secrets in it.\nSet `correlation_id` (your own stable id) so you can find the ping again on\nread surfaces, and `reply_to` (a notification id or correlation id) to thread\na ping as the answer to an earlier one.\n\n### Files and documents (md / txt / html / zip / images — anything)\n\nTwo paths; pick by size:\n\n1. **MCP, files ≤ ~90 KiB** (reports, markdown, logs, small HTML):\n   `upload_attachment { filename, content_base64, mime_type? }` → returns an\n   attachment id → pass `attachment_ids: [\"…\"]` (max 4) on `broadcast` or\n   `ask_question`. Base64 a file with `base64 < file | tr -d '\\n'`. The MCP\n   request envelope caps at 128 KiB, which is why big files don't fit here.\n2. **CLI, anything up to 5 MiB** (zips, images, PDFs):\n   `pingroom ping --room <code> -m \"…\" --attach <path>` — repeat `--attach`\n   for up to 4 files. The CLI is paired to the same account\n   (`~/.pingroom/credentials.json`).\n\nBoth require the account to be Pro (`pro_required` otherwise). Retrieve any\nattachment later with `get_attachment { attachment_id }` (base64 back, small\nfiles) or `pingroom attachment get <id> --out <path>` (any size, binary-safe).\nDelete an unclaimed upload with `delete_attachment`. If `upload_attachment`\nfails with a scope error, the connector's grant predates attachments — fall\nback to the CLI, and mention that reconnecting the MCP connector with the\n`pingroom:attachments:write` scope would enable the direct path.\n\n### Questions\n`ask_question { invite_code, prompt (≤500), options?, text_input?, context?, ttl? }`:\n\n- Omit `options` for a default Approve/Deny; otherwise give 2–4 options in\n  display order. Keep labels short — they're lock-screen buttons.\n- `text_input: { placeholder, max_length ≤60 }` invites a short typed answer,\n  alone or alongside options.\n- `context` (≤40) is the secondary line — a build number, a filename.\n- Then `wait_for_answer { question_id }`. First valid answer wins. Use\n  `cancel_question` if the question became moot; leaving stale questions on\n  someone's lock screen erodes trust.\n- Re-sending on retry? Pass `idempotency_key` so a network blip can't create\n  two questions.\n\n### Approvals\n`request_approval { invite_code, prompt, context?, ttl? }` is the deploy-gate\nprimitive: a dedicated approve/deny card. Block with `wait_for_approval` and\nbranch on `approved` / `denied` / `expired` — treat `expired` as \"no\", never\nas \"probably fine\".\n\n### Handoffs (your authorizing human, privately)\n`create_handoff { kind: \"ack\"|\"question\", prompt, audience: { type: \"user\",\nuser_id: \"me\" }, options? (question only), urgency?, expires_in? }` reaches\nthe one human who authorized this agent — a private loop no room member sees.\nUse it when the decision belongs to *your* human specifically, not to a room.\nBlock with `wait_for_handoff`. If creation fails with recipient-not-ready, the\nhuman's device hasn't enabled the Handoff surface — `activate_agent_inbox`\nsends them a one-tap activation, then retry.\n\n### Live progress on the lock screen\nFor work longer than ~30 seconds, run a live card instead of spamming pings:\n\n1. First `live_status` call with a **new** `correlation_id` starts the stream\n   (one alert). Choose the shape up front — `steps` (labels are immutable after\n   the first ping), `progress` 0..1, `metrics`, `deadline_at`, or a matchup —\n   the template is fixed at creation.\n2. Further calls with the **same** `correlation_id` and `state: \"running\"`\n   update the card silently (`current_step`, `progress`, `message`, `eta_at`).\n3. End with `state: \"done\"` or `\"failed\"` — one completion alert. **Always end\n   the stream**, even on error paths; an orphaned card squats on the lock\n   screen. One stream per correlation_id, ever — a finished id cannot restart.\n4. Free accounts get a daily budget of *new* streams; updates and the final\n   ping are free. Don't burn streams on trivial work.\n5. **Only the room owner can publish a live stream to a room.** In a room the\n   account doesn't own, `live_status` returns `forbidden` — don't retry and\n   don't improvise a burst of regular pings as a substitute (that's noise\n   nobody asked for). Pick a room the account owns, or tell the user the\n   constraint and send ONE ordinary ping when the work finishes.\n\n`get_live_status { invite_code, correlation_id }` reads the current frame.\n\n### Quick actions\n`list_quick_actions` shows a room's 4 configured buttons;\n`trigger_quick_action { invite_code, action_number }` presses one — this is\nalso the only send that works in personal rooms. `is_urgent`/`requires_ack`\nelevate a single press without changing the saved configuration.\n\n### Reading the room\n- `list_notifications { invite_code?, limit?, page? }` /\n  `get_notification { notification_id }` — history, including `data`,\n  `correlation_id`, attachments, and ack state.\n- `wait_for_notification { after? }` — long-poll for *new* pings. Without\n  `after` it returns no history, just the current head cursor: call once to\n  get the cursor, then poll with it. Your own sends are excluded.\n\n## Error handling\n\nTool failures come back as `isError: true` with a JSON `{code, message}`. Act\non the code, don't retry blindly:\n\n| code | Meaning → what to do |\n|---|---|\n| `pro_required` | Attachments/webhooks need Pro. Say so; don't loop. |\n| `room_not_granted` | Room is outside this agent's grant. Ask the human to add it under Connected Agents, or pick a granted room. |\n| `insufficient_scope` / `invalid_credential` | The token predates the permission or has the wrong audience. Reconnect the connector (or use the CLI, which holds its own credential). |\n| `attachment_too_large` | Over the MCP result cap — use `pingroom attachment get <id> --out …`. |\n| `validation_failed` | Read the message; commonly a single-member room or a length cap. |\n| `quota_exceeded` / HTTP 429 | Back off; respect Retry-After. Never hot-loop a wait tool — they long-poll server-side already. |\n\nA tool being listed does not mean this token may call it: `tools/list` is a\nstatic catalog and each call re-checks its OAuth scope.\n\n## CLI companion\n\nThe `pingroom` CLI shares the account and adds what MCP doesn't carry — large\nattachments, room/webhook/quick-action management, CI and hook integration.\nWhen the work is a script, CI job, or a file over ~90 KiB, switch to the\n`pingroom-cli` skill (sibling of this one) instead of forcing it through MCP.\n\n## Tone of what you send\n\nPings land on lock screens. Write them like events: present tense, concrete,\nno fluff (\"Deploy finished — 3 services green\", not \"I have completed the\ndeployment process\"). One ping per event; a thread of five pings is worse than\none ping with an attachment. When you finish a piece of work the human asked\nto be told about, one well-formed ping *is* the deliverable — send it without\nbeing reminded.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}