Command Palette

Search for a command to run...

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

Daily Standup Digest Bot Server

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

Exposes standup workflow tools (list members, start standup, record replies, list non-responders, generate digest) and skill resources via MCP, enabling AI agen

GitHubEmbed

Описание

Exposes standup workflow tools (list members, start standup, record replies, list non-responders, generate digest) and skill resources via MCP, enabling AI agents to automate standup collection and summarization without direct Slack access.

README

An LLM-powered Slack bot that collects standup replies over DM, summarizes them with an OpenRouter free model, and posts a consolidated digest to a team channel — with a Model Context Protocol (MCP) surface that exposes the same capabilities as standardized tools for AI agents.


Demo overview

The bot lives in a Slack workspace and follows a predictable weekday cadence:

  1. Every weekday morning it DMs each active team member and asks for their standup update.
  2. It listens for DM replies and stores them in SQLite.
  3. If someone hasn't responded, it sends one gentle follow-up DM.
  4. Later that morning it summarizes every reply with an LLM (OpenRouter free model), highlights real blockers, lists non-responders, and posts a formatted digest to a designated Slack channel.

The same workflows are also exposed as MCP tools so an external LLM agent can list team members, kick off a run, record replies, list non-responders, and generate a digest — all without touching Slack.

Synthetic-data-only demo. This project ships with sample members and sample replies. Sample Slack IDs are prefixed SAMPLE_ on purpose. Do not point it at real customer data.


Problem being solved

Distributed teams spend meaningful time on standups. Written standups in a channel are a good middle ground, but they suffer from:

  • Scattered replies. People post at different times; nobody sees the whole picture without scrolling.
  • Buried blockers. The one message with the actual blocker is next to the "no updates" ones.
  • Nagging. Someone has to manually chase people who forgot.
  • Summarization tax. A human ends up rewriting the day's replies into a readable digest.

This bot automates the boring bits (collection, chasing, summarization, formatting) so humans keep the parts that matter (actually unblocking each other).


Core workflow

The bot embodies a small take in → make sense of → act on loop:

  1. Take in data. Slack Bolt (Socket Mode) receives DM replies as message events and persists them to SQLite via db.save_response.
  2. Make sense of it. For each raw reply, agent.summarize_update asks the OpenRouter free model to return strict JSON with yesterday / today / blockers, guided by three reusable Markdown skill files.
  3. Act on it. agent.build_digest assembles a Slack-formatted message that lifts real blockers to the top, groups updates by person, and lists non-responders at the bottom. app.post_digest posts it to the configured TEAM_CHANNEL_ID.

The scheduler (scheduler.py) drives the same three moments (start_standupsend_followupspost_digest) automatically on weekdays.


Features

  • Slack Bolt + Socket Mode. No public HTTPS endpoint needed for the Slack app to receive events; the bot connects outbound to Slack.
  • Slash commands. /join-standup, /start-standup, /send-standup-followups, /post-standup-digest, /schedule-status, and (guarded) /reset-test-standup.
  • DM reply capture. A @app.event("message") listener saves any DM from an active team member as their standup reply.
  • SQLite state. Three-table schema (team_members, standup_runs, responses) with parameterized queries. The path is overridable via STANDUP_DB_PATH (used by tests).
  • APScheduler weekday cron. BackgroundScheduler with three CronTrigger jobs (day_of_week="mon-fri"), timezone-aware, coalesce=True, max_instances=1, misfire grace period. Also a fast interval-based test scheduler for local dev.
  • OpenRouter free-model summarization. openrouter_client.py is a thin wrapper around openai.OpenAI pointed at https://openrouter.ai/api/v1; model comes from OPENROUTER_MODEL and defaults to a free slug. Temperature is pinned low (0.1) for determinism.
  • Reusable Markdown skills. skills/summarize_standup.md, skills/detect_blockers.md, skills/format_digest.md are the instructions the LLM sees, loaded from paths derived from __file__. The same skill files are exposed through MCP as a read-only resource.
  • One-time follow-up tracking. A followup_sent column ensures each person is only nudged once per run.
  • MCP surface. mcp_server.py exposes six tools, one resource, and one prompt via the official mcp Python SDK v1 over Streamable HTTP.
  • LLM-powered MCP agent client. mcp_agent.py discovers the server's tools dynamically, hands them to OpenRouter as OpenAI-compatible function tools, and lets the model plan calls (up to 5 tool-call rounds).
  • Test suite + CI. 54 pytest cases run offline in under a second; GitHub Actions runs them on Python 3.12 and 3.13.
  • Synthetic-data safeguards. Sample IDs prefixed SAMPLE_, a guarded /reset-test-standup command, and an MCP reset_synthetic_standup tool that requires confirm=true.

Architecture

flowchart LR
    subgraph Slack["Slack workspace"]
        SU["Team members"]
    end

    subgraph BotProc["Bot process (app.py)"]
        SB["Slack Bolt / Socket Mode"]
        SCHED["APScheduler weekday cron"]
    end

    subgraph MCPProc["MCP server process (mcp_server.py)"]
        MCP["FastMCP<br/>Streamable HTTP"]
    end

    subgraph Core["Shared core"]
        SVC["standup_service.py<br/>(business logic)"]
        DB[("SQLite<br/>standup.db")]
        AG["agent.py<br/>build_digest / summarize_update"]
        SK["skills/*.md"]
    end

    OR["OpenRouter API<br/>(free model)"]
    AGENT["mcp_agent.py<br/>LLM-driven CLI"]

    SU <--> SB
    SB --> SVC
    SCHED --> SVC
    MCP --> SVC
    SVC --> DB
    SVC --> AG
    AG --> SK
    AG --> OR
    AGENT --> MCP
    AGENT --> OR

Slack, the scheduler, and MCP all funnel into standup_service.py so business logic is defined once. The MCP agent talks to the MCP server and to OpenRouter — it never touches Slack directly.


Technology stack

Layer Choice
Language Python 3.12 / 3.13
Slack slack_bolt + slack_sdk in Socket Mode
Persistence SQLite via the stdlib sqlite3 module
LLM OpenRouter (free model), called through the openai SDK
Scheduling APScheduler 3.x (BackgroundScheduler + CronTrigger)
MCP Official mcp Python SDK v1 (FastMCP, Streamable HTTP)
Config python-dotenv (.env)
Testing pytest
CI GitHub Actions on Ubuntu, Python 3.12 & 3.13

Project structure

.
├── app.py                 # Slack Bolt app: slash commands, DM listener, scheduler wiring
├── db.py                  # SQLite schema and low-level queries
├── agent.py               # LLM summarization + digest formatter
├── openrouter_client.py   # Thin OpenRouter chat-completions client
├── scheduler.py           # Weekday cron + local interval test scheduler
├── standup_service.py     # Slack-free business logic reused by Slack, cron, and MCP
├── mcp_server.py          # MCP tools, resource, and prompt (FastMCP)
├── mcp_agent.py           # LLM-driven MCP agent client
├── test_schedule.py       # Runnable local synthetic scheduler harness
├── test_mcp_tools.py      # Runnable local synthetic service harness
├── test_mcp_client.py     # Runnable local MCP connectivity test
├── skills/                # Markdown instruction files (loaded by agent + MCP resource)
├── sample_data/           # Synthetic team members and sample replies
├── tests/                 # pytest suite (isolated, offline)
├── requirements.txt
├── .env.example
├── pytest.ini
├── .github/workflows/test.yml   # CI: py_compile + pytest on 3.12 and 3.13
├── LICENSE                # MIT
├── CONTRIBUTING.md
├── SECURITY.md
└── README.md

Slack setup

  1. Create a Slack app (from scratch) at api.slack.com/apps and install it in a dev workspace you own.
  2. Enable Socket Mode. Generate an App-Level Token with the connections:write scope → this is SLACK_APP_TOKEN (starts with the app-level prefix).
  3. Under OAuth & Permissions, add these Bot Token Scopes:
    • chat:write — post messages
    • im:history — read DMs the bot is a party to
    • im:read, im:write — open/read DMs
    • commands — slash commands
    • users:read (optional, for nicer display names)
  4. Under Event Subscriptions → Subscribe to bot events, add message.im.
  5. Under Slash Commands, create these six commands. Request URL can be anything valid — Socket Mode ignores it. Point them at: /join-standup, /start-standup, /send-standup-followups, /post-standup-digest, /schedule-status, /reset-test-standup.
  6. Install the app to your dev workspace and copy the Bot User OAuth Token → SLACK_BOT_TOKEN.
  7. Invite the bot to a private test channel and copy the channel ID into TEAM_CHANNEL_ID.

Environment variables

Copy .env.example to .env and fill in your own values. Never commit .env; it is gitignored.

Variable Purpose Default
SLACK_BOT_TOKEN Bot User OAuth token (starts with the bot prefix). — (required for app.py)
SLACK_APP_TOKEN App-level token with connections:write for Socket Mode. — (required for app.py)
TEAM_CHANNEL_ID Channel where the digest is posted. — (required for digest posting)
OPENROUTER_API_KEY OpenRouter API key. — (required for summarization / digest / MCP agent)
OPENROUTER_MODEL OpenRouter model slug. openrouter/free
STANDUP_TIME HH:MM (24h) when the standup DMs go out. 09:00
FOLLOWUP_TIME HH:MM for the one-time follow-up. 09:45
DIGEST_TIME HH:MM for posting the digest. 10:15
SCHEDULE_TIMEZONE IANA timezone for cron. America/Los_Angeles
ENABLE_SCHEDULER Truthy (true / 1 / yes / on) turns on APScheduler. false
ALLOW_TEST_RESET Truthy value enables /reset-test-standup. false
MCP_SERVER_URL Endpoint the MCP client tools connect to. http://localhost:8000/mcp
STANDUP_DB_PATH Override the SQLite path (used by tests). standup.db

Running the Slack bot

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

cp .env.example .env
# edit .env with your own dev credentials — never commit real values

python3 app.py

You should see the bot connect via Socket Mode, and (if ENABLE_SCHEDULER=true) a summary of the scheduled weekday jobs.

Try it out inside Slack:

  • Run /join-standup in any channel where the bot is present to add yourself to the synthetic team.
  • Run /start-standup. The bot DMs every active member and creates pending response rows.
  • Reply to the DM with a short update. The bot confirms with ✅ Thanks! Your test standup update was saved.
  • Run /send-standup-followups to nudge anyone who hasn't replied.
  • Run /post-standup-digest to build the AI-assisted digest and post it to TEAM_CHANNEL_ID.

Running the MCP server

python3 mcp_server.py

The server listens at:

http://localhost:8000/mcp

It uses the streamable-http transport from the official MCP Python SDK v1. The Slack app and APScheduler are not started by mcp_server.py.

Explore the surface with MCP Inspector:

npx -y @modelcontextprotocol/inspector

Set the transport to Streamable HTTP and point it at http://localhost:8000/mcp.

You can also drive the service layer directly (no MCP, no Slack) as a smoke test:

python3 test_mcp_tools.py

Running the MCP agent

mcp_agent.py is an LLM-driven CLI that connects to the MCP server, lists tools, hands them to OpenRouter as OpenAI-compatible function tools, and lets the model plan calls (up to 5 tool-call rounds).

# terminal 1
python3 mcp_server.py

# terminal 2 — one-shot request via CLI argument
python3 mcp_agent.py "List the synthetic standup team."

# or interactive prompt
python3 mcp_agent.py
# Enter an agent request: Who has not responded today?

The agent will never post to Slack — MCP tools in this project do not send Slack messages by design.


Testing

The pytest suite runs entirely offline: no real Slack, no real OpenRouter, no real filesystem outside of pytest's tmp_path.

pytest -q

Under the hood:

  • The temp_db fixture points the app at a per-test SQLite file via STANDUP_DB_PATH and calls init_db() for a fresh schema.
  • An autouse fixture wipes Slack / OpenRouter env vars so no test can accidentally use real credentials.
  • agent.call_llm is monkeypatched in every test that hits the LLM path.

CI (.github/workflows/test.yml) runs a compile-check across all production modules followed by pytest -q on Ubuntu with both Python 3.12 and 3.13.

Full recommended local checklist:

python3 -m py_compile app.py db.py agent.py openrouter_client.py scheduler.py standup_service.py mcp_server.py mcp_agent.py
pytest -q
git status

Available Slack commands

Command Behavior
/join-standup Add the invoking user to the synthetic standup team.
/start-standup Create today's run, DM every active member, and record pending responses.
/send-standup-followups DM one follow-up reminder to each nonresponder who hasn't already been nudged.
/post-standup-digest Build the AI-assisted digest and post it to TEAM_CHANNEL_ID.
/schedule-status Report whether automatic scheduling is enabled and show the configured times / timezone.
/reset-test-standup Synthetic-only. Delete today's run and responses (team members preserved). Guarded by ALLOW_TEST_RESET.

Available MCP tools

All tools delegate to standup_service.py, so their behavior mirrors the Slack and scheduler paths.

Tool What it does
list_team_members Return active synthetic team members.
start_standup_run Create today's run and pending response rows. Does not send Slack DMs.
record_standup_reply Save a synthetic standup reply for a registered member. Raw text is not logged.
list_nonresponders Return today's nonresponders who have not already received a follow-up.
generate_standup_digest Build today's AI-assisted digest string. Does not post to Slack.
reset_synthetic_standup Local-only test reset. Requires confirm=true. Preserves team members.

MCP resource and prompt

Resourcestandup://skills

Read-only combined view of skills/summarize_standup.md, skills/detect_blockers.md, and skills/format_digest.md. Lets an agent understand how the bot reasons without hitting the filesystem itself. Loaded from a path derived from __file__.

Promptstandup_manager

A reusable prompt that instructs an agent to (1) inspect the team, (2) create or inspect today's run, (3) identify nonresponders, (4) generate the digest, (5) never invent updates, (6) work with synthetic data only, and (7) require explicit confirmation before resets.


Synthetic-data and security safeguards

  • Sample IDs are prefixed SAMPLE_ (SAMPLE_U01, SAMPLE_U02, …) so it is obvious these are not real workspace users.
  • No secrets in code. All credentials come from .env, which is gitignored. .env.example is the source of truth for variable names.
  • Guarded destructive actions. The Slack /reset-test-standup command requires ALLOW_TEST_RESET to be truthy; the MCP reset_synthetic_standup tool requires confirm=true.
  • Never logs raw standup text. record_reply in the service and the DM handler in Slack print short confirmation lines and DB identifiers, never the message body.
  • Parameterized SQL everywhere in db.py.
  • MCP tools do not send Slack messages. The agent surface can only manipulate local state and generate text.
  • CI uses dummy env values (no secrets.* context) and tests wipe env vars before every test.
  • See SECURITY.md for the credential-rotation policy.

Known limitations

  • Free-model quality varies. OPENROUTER_MODEL=openrouter/free is intentionally the default; some free slugs on OpenRouter don't fully support function-calling. If mcp_agent.py reports empty tool calls, swap in another free tool-capable slug in .env.
  • DMs from unregistered users are silently dropped. The DM listener uses save_response, which only updates an existing pending row created by /start-standup. That's intentional but worth knowing.
  • SQLite is single-process. The bot and the MCP server can both read/write the same DB fine, but this is not a multi-node design.
  • Timezone / date drift. db.get_or_create_today_run uses the system's local date, while the scheduler runs on SCHEDULE_TIMEZONE. Run the bot in a machine timezone that matches the scheduler tz, or set both to UTC.
  • No auth on the MCP endpoint. mcp_server.py is intended for local localhost:8000 use only. Do not expose it to the public internet.
  • Model summaries are LLM output. Occasional hallucinations are possible; the safety prompt and fallback path minimize this, but a human still owns the standup.

Future improvements

  • Retry policy + exponential backoff around OpenRouter calls.
  • Optional weekly rollup posted on Fridays.
  • Optional blocker-only "escalation" DM to a designated channel.
  • Structured metrics (Prometheus) for run health.
  • Multi-team / multi-channel support driven by DB config rather than env vars.
  • Auth (bearer token or OAuth) in front of the MCP endpoint before exposing it beyond localhost.
  • End-to-end integration tests against a mocked Slack API.

Demo script

A ~5-minute walkthrough you can use verbatim:

  1. Show the architecture. Open the Mermaid diagram above. Explain that Slack, the scheduler, and MCP all share standup_service.py so business logic is defined once.
  2. Show .env.example and .gitignore. Emphasize: real secrets live in .env, which is gitignored; the repo itself has no tokens.
  3. Boot the Slack bot.
    python3 app.py
    
    Point out the ⚡ Daily Standup Digest Bot is connecting to Slack... line and (with ENABLE_SCHEDULER=true) the printed cron schedule.
  4. In Slack:
    • /join-standup — add yourself.
    • /start-standup — the bot DMs you.
    • Reply in DM with a short synthetic update.
    • /post-standup-digest — bot posts the AI-summarized digest to TEAM_CHANNEL_ID.
  5. Boot the MCP server (Ctrl-C the Slack bot first if you're sharing one terminal):
    python3 mcp_server.py
    
  6. Show MCP Inspector.
    npx -y @modelcontextprotocol/inspector
    
    Connect to http://localhost:8000/mcp, walk through the six tools, the standup://skills resource, and the standup_manager prompt.
  7. Run the LLM-driven MCP agent against the running server:
    python3 mcp_agent.py "Who has not responded today?"
    
    Point out that the agent discovered the tools dynamically, that OpenRouter chose which to call, and that MCP tools never posted to Slack.
  8. Run the tests.
    pytest -q
    
    All 54 tests pass offline in under a second. Show the CI badge (or the .github/workflows/test.yml file) to demonstrate the same suite runs on GitHub Actions across Python 3.12 and 3.13.

License

MIT — see LICENSE.

from github.com/SaahasK18/daily-standup-digest-bot

Установка Daily Standup Digest Bot Server

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

▸ github.com/SaahasK18/daily-standup-digest-bot

FAQ

Daily Standup Digest Bot Server MCP бесплатный?

Да, Daily Standup Digest Bot Server MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Daily Standup Digest Bot Server?

Нет, Daily Standup Digest Bot Server работает без API-ключей и переменных окружения.

Daily Standup Digest Bot Server — hosted или self-hosted?

Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.

Как установить Daily Standup Digest Bot Server в Claude Desktop, Claude Code или Cursor?

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

Похожие MCP

Compare Daily Standup Digest Bot Server with

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

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

Автор?

Embed-бейдж для README

Похожее

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