Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Capture And Slack

БесплатноНе проверен

Relays a result screenshot to Slack so you can review a coding agent's output without opening the app.

GitHubEmbed

Описание

Relays a result screenshot to Slack so you can review a coding agent's output without opening the app.

README

An MCP server that relays a result screenshot to Slack so you can review a coding agent's output without opening the app.

You hand an agent a task on your desktop app (e.g. Clonify, a Qt/VTK CAD tool), and end it with "…and send me the result via the capture-and-slack MCP." The agent renders the result to an image and this server posts it to your Slack DM or a channel. You glance at Slack instead of launching the app.

This server does not drive any GUI — it only relays images.

How it fits the loop

agent finishes a task
  ├─ (A, preferred)  runs a repro/test that offscreen-renders the result → /tmp/result.png
  │                     → send_to_slack(["/tmp/result.png"], "3mm gaussian emboss done")
  └─ (B, fallback)   the app window is on screen (X11)
                        → capture_window_and_post("result", window="Clonify")
        → Slack files_upload_v2 → you see the image in your Slack DM/channel

Path A is the robust one: the app's own headless render (VTK SetOffScreenRendering(1) + vtkWindowToImageFilter, optionally under QT_QPA_PLATFORM=offscreen) writes a PNG, and this server just posts it. Path B screenshots the live window and is for when no offscreen render exists.

Tools

Tool What it does
send_to_slack(images, message="", channel=None, title=None, status=None, checks=None, links=None, thread_ts=None, echo_image=False) Post image(s) to Slack as a review: a root anchor message (a Block Kit task card when title/status/checks/links are given) with the image(s) threaded beneath it, and 👍/👎 seeded for one-tap review. Returns a JSON status with review_id and the permalink(s). Primary tool.
capture_window_and_post(message="", window=None, channel=None, title=None, trim=True, allow_fullscreen=False, thread_ts=None, echo_image=False) Screenshot the running window (X11/KDE) and post it as a review. window defaults to CAPTURE_WINDOW_TITLE; use "active" or "full".
capture_window(window=None, trim=True, allow_fullscreen=False) Screenshot without posting and return it for inspection. Compose with send_to_slack. No Slack token needed.

Safety: when a specific window title can't be matched to an on-screen window, capture returns NO_WINDOW_MATCH rather than silently grabbing the focused window or the whole desktop (which would leak unrelated apps/secrets to Slack). Pass allow_fullscreen=True (or window="full") to opt into the whole-screen fallback deliberately. | wait_for_review(review_id, channel=None, timeout_seconds=50, poll_seconds=5) | Block until the human reacts (👍/👎/👀/✏️) or replies in the review thread. Returns approved/rejected/changes/seen, or still_awaiting on timeout — call again to keep waiting. | | check_review(review_id, channel=None) | Non-blocking single read of the current verdict. Use in your own polling loop. | | ask_review(review_id, question, channel=None, timeout_seconds=50, poll_seconds=5) | Ask the reviewer a follow-up question in Slack (e.g. why they rejected) and wait for their typed answer. Posts + broadcasts the question so an away reviewer sees it. Returns answered+reply, or still_awaiting+ask_id. | | wait_for_reply(review_id, since_ts, channel=None, timeout_seconds=50, poll_seconds=5) | Block until a thread reply appears after since_ts (e.g. the ask_id from ask_review). Returns answered+reply or still_awaiting. |

Image paths must be absolute (the server's working directory differs from the agent's). Errors come back as a JSON envelope {"code","message","hint"} in an isError result.

Closing the review loop

send_to_slack returns a review_id (the thread's root message ts). The agent seeds a task card, the human taps 👍/👎 on their phone (or replies "make it 5mm"), and wait_for_review(review_id) returns the verdict to the agent:

send_to_slack(["/tmp/result.png"], title="3mm emboss", status="pass",
              checks={"volume Δ": "+2.1%", "watertight": "yes"})   → {"review_id": "1712…", …}
wait_for_review("1712…", channel="C…")                            → {"status": "approved", …}

Re-render into the same thread by passing thread_ts=review_id to send_to_slack; add notify=True to broadcast the re-render back to the channel (🔁) so a watching reviewer is re-pinged.

If the agent needs to ask something (e.g. the reviewer rejected and the agent wants to know why), it must ask in Slack, not in the terminal — the reviewer is watching Slack. ask_review posts the question into the thread, pings the reviewer, and returns their typed answer:

wait_for_review("1712…", "C…")                                   → {"status": "rejected"}
ask_review("1712…", "Why — emboss too sharp, or wrong face?")    → {"status": "answered", "reply": "too sharp"}

Requirements

  • uv and Python ≥ 3.12.
  • A Slack bot token (see Slack setup — image uploads need files_upload_v2; webhooks can't attach files).
  • For live capture only: an X11 session with ImageMagick import or KDE spectacle installed. Precise per-window targeting uses xdotool if present, else xwininfo (already on most X11 desktops) — no install needed. Match by window title or WM_CLASS substring; a class like QClonifyApp disambiguates from an editor tab that merely has "Clonify" in its title.

Slack setup (one time)

  1. Go to https://api.slack.com/appsCreate New App → From an app manifest, pick your workspace, and paste:
    {
      "display_information": { "name": "Capture and Slack Bot" },
      "features": { "bot_user": { "display_name": "capture-and-slack", "always_online": true } },
      "oauth_config": { "scopes": { "bot": [
        "files:write", "chat:write", "im:write",
        "reactions:write", "reactions:read",
        "channels:history", "groups:history", "im:history"
      ] } },
      "settings": { "org_deploy_enabled": false, "socket_mode_enabled": false }
    }
    
    Scopes: files:write (upload), chat:write (the caption/card), im:write (open a DM when the destination is a user ID). For the review loop: reactions:write (seed 👍/👎), reactions:read (read the verdict), and history (channels:history for public channels, groups:history for private, im:history for DMs) to read threaded replies. Drop the loop scopes if you only ever post one-way. If you add scopes to an existing app, reinstall it so the new grants take effect.
  2. Install to Workspace → authorize → copy the Bot User OAuth Token (xoxb-…) from OAuth & Permissions.
  3. Pick a destination ID:
    • DM to yourself: your member ID U… (Slack profile → ⋮ → Copy member ID). The bot can DM you without an invite.
    • A channel: the channel ID C… (channel → View details), and run /invite @capture-and-slack in it.
  4. Provide the token to the server (do not commit it): cp .env.example .env and fill in SLACK_BOT_TOKEN and SLACK_DEFAULT_CHANNEL, or export them in your shell.
  5. (Optional, for precise window targeting) sudo pacman -S xdotool.

Install & test

uv sync
uv run pytest

Register with Claude Code

Project-scoped (.mcp.json is already in this repo):

{
  "mcpServers": {
    "capture-and-slack": {
      "command": "uv",
      "args": ["run", "--directory", "/home/baho/Desktop/capture-and-slack-mcp", "capture-and-slack-mcp"]
    }
  }
}

The server reads SLACK_BOT_TOKEN / SLACK_DEFAULT_CHANNEL from the environment or a git-ignored .env. (Keep the token out of .mcp.json, which is committed.)

Configuration

Env var Default Purpose
SLACK_BOT_TOKEN Bot token (xoxb-…). Required to post.
SLACK_DEFAULT_CHANNEL Default destination: U… (DM), C… (channel), or D… (DM channel).
CAPTURE_WINDOW_TITLE Clonify Default window title substring for live captures.
CAPTURE_SLACK_MAX_FILE_MB 25 Soft cap: larger images are auto-shrunk (downscale → JPEG) to fit before uploading.
CAPTURE_SLACK_HARD_MAX_MB 200 Hard ceiling: files bigger than this are rejected (FILE_TOO_LARGE) instead of shrunk.
CAPTURE_SLACK_SCRATCH_DIR <tmp>/capture_and_slack_mcp Where captured/shrunk PNGs are written (created 0700, files 0600).
CAPTURE_SLACK_SCRATCH_TTL_MIN 60 Reap scratch files older than this (minutes) on access; 0 disables.
CAPTURE_SLACK_KEEP_SCRATCH (unset) Set (1/true) to never delete/reap scratch files — for debugging.
LOG_LEVEL INFO Server log level, written to stderr (DEBUG, INFO, WARNING, …).
SLACK_MAX_RETRIES 3 Auto-retries for rate-limited (429, honors Retry-After) / connection-error Slack calls.
SLACK_HTTP_TIMEOUT 30 Per-request Slack HTTP timeout (seconds).

Development

Module map — only server.py imports the MCP SDK:

  • server.py — the 3 tools + main().
  • slack_client.pyslack_sdk wrapper: files_upload_v2, DM resolution, error translation.
  • capture.py — X11/KDE window capture. Layered fallback: resolve window id (xdotool → xwininfo) and grab it directly → active window (spectacle) → fullscreen.
  • images.py — Pillow: path/content validation, auto-trim, PNG byte reads.
  • config.py — env-driven settings (with a tiny .env loader).
  • errors.pyErrorCode + CaptureSlackError (JSON envelope).

License

MIT

from github.com/baho0/capture-and-slack-mcp

Установка Capture And Slack

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/baho0/capture-and-slack-mcp

FAQ

Capture And Slack MCP бесплатный?

Да, Capture And Slack MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Capture And Slack?

Нет, Capture And Slack работает без API-ключей и переменных окружения.

Capture And Slack — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Capture And Slack в Claude Desktop, Claude Code или Cursor?

Открой Capture And Slack на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Capture And Slack with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории communication