community-pulse — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited community-pulse (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Surface the signal in a busy Discord server. Who joined, which channels are hot, who's contributing, what's getting asked, and what's left hanging — in one digest you can act on in 15 minutes.
| Job | Primary | Also answers it | What differs |
|---|---|---|---|
| Members, joins, channel activity, contributors | Discord | — | Only Discord has membership and channels |
| Themes and unanswered questions | Discord (get_channel_messages) | Crisp (list_conversations, get_conversation_messages), YouTube (list_comment_threads) | Crisp is 1:1 support, so "unanswered" means filter_state: "unresolved"; YouTube marks owner_replied per thread |
| Volume trend | Discord message counts | Crisp get_website_statistics, YouTube comment counts | Report each source in its own row; never add them up |
Discord is the primary source and drives steps 2-6. Crisp and YouTube are optional extra sources (step 7b). If Discord isn't connected but one of the others is, run steps 7, 7b, 8 and 9 for that source only and say so in the header.
/community-pulse — weekly digest of the connected Discord server (plus Crisp/YouTube if connected).
Pass a guild ID if the bot is in more than one server:
/community-pulse 123456789012345678
If no mcp__cogny__* tools exist (no list_integrations, no cogny_status), print this and stop:
Community Pulse needs your marketing data through Cogny's MCP gateway.
1. Sign up at https://cogny.com and connect Discord (plus Crisp or YouTube if you use them).
2. Add Cogny to .mcp.json in your project:
{"mcpServers": {"cogny": {"type": "http", "url": "https://app.cogny.com/mcp"}}}
3. Restart Claude Code; it opens a browser for the OAuth login on first use.
Nothing to run without Cogny.If Cogny is connected but Discord isn't, say "connect Discord in Cogny" (Crisp and YouTube alone still give a partial digest).
The Discord MCP needs two privileged intents enabled in the Discord Developer Portal → Bot tab → Privileged Gateway Intents:
list_guild_members returns onlythe bot itself.
get_channel_messages returnsauthors and timestamps but every content field comes back empty.
If the report comes back with a 1-element member list or empty message content, this is almost always why. The skill flags the gap loudly at the top of the output rather than silently producing misleading numbers.
The bot also needs View Channel + Read Message History in each channel you want analysed — these are channel-level permissions, not intents.
Call mcp__cogny__list_integrations to see which of Discord, Crisp and YouTube are connected. Its health_status misses revoked tokens, so make one cheap read per connected source: Discord get_current_user (also confirms the bot identity), Crisp get_website_statistics, YouTube get_channel. A 401, Forbidden or scope error = 🔴 "reconnect <integration> in Cogny": name it in the header and don't retry. For Discord that means the bot token stored in Cogny is bad: "reconnect Discord in Cogny".
If a tool you expect is missing, call mcp__cogny__refresh_mcp_discovery once (never loop). New tools only load after the client reconnects, so skip that source this run and mark it ⚠ "needs reconnect".
Call list_guilds. If exactly one guild, use it. If multiple, prefer the guild ID passed as an argument. If none was passed and there's more than one, list them and ask the user to specify.
Once chosen, call get_guild(guild_id, with_counts=True) for the baseline:
approximate_member_count (this is the "members" stat)approximate_presence_count (online right now)Call list_guild_channels(guild_id). Bucket channels into:
intro, welcome, feedback,support, showcase, hiring, general (these usually hold the signal for community health)
permission_overwrites (or its parent category's) has anentry with id == guild_id that denies VIEW_CHANNEL (bit 1024). Per-customer channels are usually private. Analyse them for counts and anonymised themes only: never quote them, name their members, or list their messages in Top Themes or Unanswered Questions.
Cap the analysis at 30 channels to bound runtime.
Call list_guild_members(guild_id, limit=1000), paginating with after=<last user id> until exhausted or 5000 members fetched.
For each member, parse joined_at and bucket into:
Compute:
For each text channel from Step 3 (cap 30, prioritize high-signal):
get_channel_messages(channel_id, limit=25) — most recent batch. Each message carries the fullauthor object, so keep limit at 20–25 (50 returned 55–105KB per channel in live runs). A Forbidden means the bot can't read that channel: list it as "not readable" and continue with the next one. Exclude author.bot and staff ids from every count, and exclude system messages (any type other than the default 0 or reply 19, e.g. type 7 member joins, pins, boosts): they are not conversation.
timestamp in the last 7 dayswith before=<earliest message id> (still limit=25). Stop after 200 messages per channel. For channels outside the high-signal bucket, keep only counts (timestamps, author ids), not message content.
| Class | Criteria |
|---|---|
| Hot | ≥20 messages this week, ≥5 unique authors |
| Steady | 5–19 messages, ≥3 unique authors |
| Quiet | 1–4 messages |
| Dead | 0 this week, last activity >14 days ago |
Aggregate the message samples from Step 5 by author across all channels:
reactions[].count per message)Then surface:
zero messages in days 8–14. These are lurkers who just turned the corner.
Going quiet — worth a check-in.
If content is non-empty (Message Content Intent is on):
≥5 messages or in ≥2 channels.
(anonymize as "@member said: …"), sentiment (positive / neutral / mixed / concerning). Quotes come from public channels only; private channels (step 3) feed the theme counts but are never quoted.
the sampled thread. These are unmet needs.
If content is empty everywhere, skip this step and write a one-line note: "Topic analysis unavailable — enable Message Content Intent on the bot in the Discord Developer Portal."
Crisp (support conversations). All Crisp tools need website_id (the Crisp website UUID: Crisp → Settings → Website settings → Setup). Ask the user once if you don't have it.
mcp__cogny__mcp__Crisp__get_website_statistics (website_id) for the volume baseline.mcp__cogny__mcp__Crisp__list_conversations (website_id, page_number, 50 per page, newest first). Keep the last 14 days; stop after 4 pages.get_conversation_messages (website_id, session_id). Group by topic the same way as step 7.list_conversations with filter_state: "unresolved" (or "pending"). List those older than 24h.YouTube (comments on your channel). Gate this on the tools existing, not on list_integrations: if YouTube is connected but list_comment_threads isn't loaded, follow step 1 (refresh once, skip, mark ⚠).
mcp__cogny__mcp__YouTube__list_comment_threads with no video/channel (uses the connected channel), order: "time", max_results: 50; page with page_token until comments are older than 14 days.owner_replied is false and the comment is a question. Link each with its url.Merge themes across sources only when the same topic shows up in more than one (e.g. "pricing confusion" in Discord #help and in Crisp). That cross-source overlap is the strongest signal in the digest; mark it.
Each action must:
Useful action shapes:
Print the report in this shape:
Community Pulse — <guild-name>
Week of <YYYY-MM-DD> – <YYYY-MM-DD>
⚠️ Intent gap: Message Content Intent off — topic analysis disabled.
(Skip this line if intents are configured.)
Pulse
- Members: 1,247 (+18 WoW · +1.4%)
- Active authors: 34 (+6 WoW)
- Total messages sampled: 412 (+71 WoW)
- Online ratio: 11% (last week 9%)
Channel Activity
| Channel | Msgs | Authors | Status |
|----------------|------|---------|--------|
| #introductions | 28 | 19 | Hot |
| #general | 142 | 22 | Hot |
| #showcase | 8 | 5 | Steady |
| #help | 3 | 2 | Quiet |
| #off-topic | 0 | 0 | Dead |
Top Contributors
1. @member (24 messages, 31 reactions, 4 channels)
…
Newly Active (lurker → contributor this week)
- @member joined 2026-02-14, 5 messages this week, top channel #showcase
…
At-Risk Regulars (going quiet)
- @member was 18/wk last week → 1/wk this week, top channel #general
…
Top Themes (Message Content Intent required)
1. …
Unanswered Questions
- Discord #help "How do I …" — @member, 2 days ago
- Crisp "Can I export …" — unresolved 3 days
- YouTube "Does this work with …" — on "<video title>", no owner reply
Other Sources (if connected)
| Source | This week | Last week | Unanswered |
|---------|-----------|-----------|------------|
| Crisp | 41 convs | 35 | 6 |
| YouTube | 58 comments | 70 | 9 |
Recommended Actions
1. Welcome @member1, @member2, @member3 in #general
Why: 3 lurkers became active this week
Owner: community manager · Done when: posted by EOW
2. …File each recommended action in the action queue. Use create_ticket if it exists (Cloud), otherwise create_finding (Solo), mapping fields per the table in section 2 of ../marketing-engineer/references/mcp-routing.md. On create_ticket, expected_outcome is required, the action type goes in body ("Type: community_engagement") and urgency prefixes the title. The create_finding shape:
{
"title": "Welcome 3 newly-active members in #general",
"body": "Lurkers @x, @y, @z each posted ≥3 messages this week after >30 days quiet. Acknowledge to lock them in.",
"action_type": "community_engagement",
"expected_outcome": "Each replies or reacts within 48h",
"estimated_impact_usd": 0,
"priority": "medium"
}Action types for Discord:
community_engagement — welcomes, mentions, reaching out to at-risk regularscontent_response — answering unanswered questions, posting summarieschannel_management — archiving dead channels, reorganising, permission fixesintent_configuration — re-enabling Server Members or Message Content Intentbot_permission — re-installing the bot with a wider permission setUse create_ticket when the workspace has it (Cloud) and create_finding otherwise (Solo). The fields differ; see section 2 of ../marketing-engineer/references/mcp-routing.md.
fully — don't dilute the report.
list_guild_members returns 1 row or allcontent fields are empty, flag it loudly at the top before any other numbers — the rest of the report will be misleading.
messages newest-first; verify each timestamp against the 7-day window.
members. The output is more useful than a complete one.
Crisp conversations are private support chats: never show emails or full names from them, only the paraphrased question. Don't paste full message bodies unless the message is already pinned or the author is the workspace owner. 5b. Never quote secrets. If a message in any channel appears to contain a secret (API key, client secret, password, token pattern such as sk_live_…, ghp_…, xoxb-…, a JWT, or password=), never quote or paraphrase the value. Report "possible exposed secret in #<channel> on <date>; rotate it" without the value, and make it a top action.
send_message or Crispsend_message_in_conversation / update_conversation_state (none are in the allowed-tools list). The digest is for the user to act on, not for the bot to broadcast. If the user asks for a reply to be posted, show the draft and ask first.
deltas are signal. Always include the comparison.
wrote 8 paragraphs of analysis without 3 clear next steps, you over- analyzed — cut.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.