Labsco
webflow logo

webflow-mcp:review-comments

โ˜… 100

by webflow ยท part of webflow/webflow-skills

Review open comment threads on a Webflow site and triage each one.

๐Ÿงฉ One of 7 skills in the webflow/webflow-skills package โ€” works on its own, and pairs well with its siblings.

This is the playbook your agent receives when the skill activates โ€” you don't need to read it to use the skill, but it's here to audit before installing.

Review open comment threads on a Webflow site and triage each one.

Input: $ARGUMENTS โ€” a site name (e.g. "Workhaus"), site ID (e.g. 6808fd4eff835ee3af009d6f), or either with the -reply flag (e.g. Workhaus -reply).

Flags:

  • -reply โ€” in addition to writing the report, post a bot reply to each non-open thread. Without this flag, the skill runs in report-only mode (read-only).

Parse $ARGUMENTS at the start: strip -reply from the input to get the site identifier, and set replyMode = true if -reply was present, false otherwise.


Step 1 โ€” Resolve the site

If the site identifier is empty or blank โ€” cross-site comment survey

  1. Call data_sites_tool > list_sites to get all sites. Page through until all are collected.
  2. For each site, call data_comments_tool > list_comment_threads with isResolved: false and limit: 100. Page through until all unresolved threads are collected. If the API does not support isResolved filtering, fetch all threads and filter client-side to isResolved === false. Process sites in batches of 20 (batch multiple actions in a single tool call). After each batch completes, log a progress line: Batch {N}/{total} done (sites {start}โ€“{end}): {summary of findings, e.g. "all 0 threads" or "SiteName has X threads, rest 0"}.
  3. For each site, compute:
    • unresolvedCount = total unresolved threads
    • newestDate = the maximum lastUpdated value across all unresolved threads for that site (ISO โ†’ human-readable date, e.g. "Apr 3, 2026"). If no threads, show โ€”.
    • oldestDate = the minimum lastUpdated value across all unresolved threads for that site (ISO โ†’ human-readable date). If no threads, show โ€”.
  4. Sort sites by unresolvedCount descending. Take the top 10.
  5. Above the table, show a heading line: ### Checked {totalSiteCount} sites โ€” {sitesWithUnresolved} have unresolved comments.
  6. Display a table in this format (link just the site name to https://webflow.com/design/{siteId}):
| Site | Unresolved Comments | Newest / Oldest |
|------|---------------------|-----------------|
| [Site Name](https://webflow.com/design/{siteId}) ({siteId}) | {N} | {newestDate} / {oldestDate} |
  1. After the table, tell the user: "Run /webflow-mcp:review-comments <site name or ID> to review a specific site."
  2. Write the survey output to a file:
    • Ensure comment-reviews/ exists (create with mkdir comment-reviews if not).
    • Filename: comment-reviews/triage-report-{YYYY-MM-DD-HH-MM}.md using the current local time (zero-padded).
    • File contents: a bold H1 title # **Webflow Comment Review**, then a blank line, then an H3 line ### Report created on: {human-readable date, time, and timezone, e.g. "April 16, 2026 at 10:39 AM PDT"}, then a blank line, then the H3 heading line (### Checked โ€ฆ) and the full table from steps 5โ€“6 above, in markdown.
    • Log the path after writing, e.g. Report written to comment-reviews/triage-report-2026-04-16-14-30.md.
  3. Stop โ€” do not proceed to Step 2.

If the site identifier looks like a Webflow site ID (24-char hex), use it directly.

Otherwise call data_sites_tool > list_sites and find the site whose displayName matches the identifier (case-insensitive). If no match, tell the user and stop.


Step 2 โ€” Fetch all open threads

Always make a fresh API call here โ€” never reuse thread data from earlier in the conversation. The user may have added or resolved comments since the last run.

Call data_comments_tool > list_comment_threads with isResolved: false and limit: 100. Page through results until all threads are collected.

Log: Site: {displayName} and Found {N} open thread(s).


Step 3 โ€” Build page-level element map

From the already-fetched thread list, build a frequency map of elementId.element โ†’ Set<pageId> across all threads. Any elementId.element that appears on 2 or more distinct pages is almost certainly the page root/body element (a real element ID would be page-scoped; only shared structural roots repeat across pages).

No API calls needed โ€” this is a local computation on the thread data.


Step 4 โ€” Triage each thread

For each thread:

4a โ€” Fetch replies

Call data_comments_tool > list_comment_replies for this thread.

4b โ€” Dedup check

Look for replies whose content includes the string โ€” ๐Ÿค– Comment Review Agent.

If found, note the most recent one (lastAgentReply). If no human reply exists with a createdOn after lastAgentReply.createdOn, skip this thread (increment skipped count, continue to next thread).

4c โ€” Compute element context

  • elementId = thread.elementId?.element
  • isPageLevel = elementId appears on 2 or more distinct pageIds in the frequency map from Step 3

4d โ€” Classify the thread

Use the following criteria:

noise โ€” No real design or engineering value:

  • Test/placeholder text ("hello world", "testing", "asdf", random characters)
  • Casual reactions with no ask ("looks nice", "nice!", "hey hey")
  • Duplicate sentiments that add nothing

stale โ€” Real concern, but old and likely handled:

  • Substantive comments older than 14 days with no replies and no follow-up
  • Questions that are probably resolved ("beta for how long?", "is this good contrast?")
  • Action items that normal review would have caught

open โ€” Real, actionable concern needing attention:

  • Explicit tasks ("Should be sentence case", "fix image", "Look at name wrt L10N")
  • Design decisions still required
  • Specific and concrete concerns

page-level โ€” Comment is on the page root, not a specific element:

  • Use this when isPageLevel is true
  • The comment is not anchored to any specific element โ€” it may be intentional or may be an orphan from a deleted element

4e โ€” Compose reply

Always compose the reply text (it appears in the report regardless of mode):

  • noise: one sentence confirming it's safe to resolve. E.g. "Looks like test text โ€” safe to resolve."
  • stale: state the age in days, suggest resolving, invite reopen. E.g. "This is 302 days old with no follow-up. Safe to resolve โ€” reply here if it's still relevant."
  • open: no reply text โ€” surface in report only.
  • page-level: one sentence noting it's not attached to a specific element. E.g. "This comment is on the page root rather than a specific element โ€” it may be an orphan from a deleted element. Safe to resolve if no longer relevant."

Append \n\nโ€” ๐Ÿค– Comment Review Agent to every reply text.

4f โ€” Post reply (only if replyMode = true)

If replyMode is true and verdict is not open, call data_comments_tool > create_reply with the composed reply content.

Log each thread as:

{VERDICT_EMOJI} {verdict}  "{preview (60 chars)}"
                {one-sentence classification reason}
                โ†ณ {reply posted | no reply โ€” report only | skipped}

Verdict emojis: noise = ๐Ÿ—‘, stale = ๐Ÿ•ฐ, open = ๐Ÿ”ด, page-level = ๐Ÿ“„


Step 5 โ€” Write the report

After processing all threads:

  1. Check whether a comment-reviews/ directory exists at the top level of the working directory. If it does not exist, create it with mkdir comment-reviews.
  2. Write a markdown report to comment-reviews/{slugified-site-name}-comments-triage-report.md (lowercase, hyphens, no special chars).

Report format:

# Comment Review โ€” [{siteName} ({siteId})](https://webflow.com/design/{siteId})

**Run:** {human-readable date and time}
**Mode:** {Report only | Report + replies posted}
**Threads:** {total} total | ๐Ÿ”ด {open} open | ๐Ÿ•ฐ {stale} stale | ๐Ÿ“„ {page-level} page-level | ๐Ÿ—‘ {noise} noise | โญ {skipped} skipped

---

## ๐Ÿ”ด Needs Attention ({count})

| Comment | Author | Age | Link |
|---------|--------|-----|------|
| "{first 80 chars of content}" | {author.name} | {age in days}d | [Open โ†—](https://github.com/webflow/webflow-skills/blob/main/plugins/webflow-skills/skills/review-comments/{thread.url}) |

## ๐Ÿ•ฐ Stale โ€” Candidates to Resolve ({count})

| Comment | Author | Age | Suggested Reply | Link |
|---------|--------|-----|-----------------|------|
| "{first 80 chars of content}" | {author.name} | {age in days}d | {composed reply text, without the `โ€” ๐Ÿค– Comment Review Agent` suffix} | [Open โ†—](https://github.com/webflow/webflow-skills/blob/main/plugins/webflow-skills/skills/review-comments/{thread.url}) |

## ๐Ÿ“„ Page-level โ€” Not Anchored to a Specific Element ({count})

| Comment | Author | Age | Suggested Reply | Link |
|---------|--------|-----|-----------------|------|
...

---

**๐Ÿ—‘ Noise:** {count} thread(s) โ€” {if replyMode: "replied to" | if report-only: "suggested replies in report"}. Safe to bulk-resolve.

Use _None._ for any section with no entries.

Log a summary line and confirm the report path.