# standup-mr [Health: Active]

**Category:** 🧠 Knowledge & Memory  
**Repository:** https://github.com/Jubstaaa/standup-mr  
**GitHub Stars:** 0  
**npm Downloads (last month):** 1852  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/standup-mr

## Description
Standup notes from merge request state, with the failed job's error lines.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

```json
"mcpServers": {
  "standup-mr": {
    "command": "npx",
    "args": ["-y","standup-mr"]
  }
}
```

## Documentation & README

# standup-mr

[![standup-mr MCP server](https://glama.ai/mcp/servers/Jubstaaa/standup-mr/badges/score.svg)](https://glama.ai/mcp/servers/Jubstaaa/standup-mr)
[![Listed on mcpservers.org](https://mcpservers.org/badge.svg)](https://mcpservers.org/servers/jubstaaa/standup-mr)

Standup notes from **merge request state**, not commit logs.

Most standup tools read your local `git log`. That answers "what did I type",
which is not what anyone asks in a standup. This one reads GitLab or GitHub:
what is ready to merge, what is blocked, what is waiting on you — and when a
pipeline is red, it opens the job log and tells you **why**.

## What makes it different

|                                               | commit-log tools | standup-mr                                            |
| --------------------------------------------- | ---------------- | ----------------------------------------------------- |
| Source                                        | local `git log`  | GitLab or GitHub API                                  |
| Merge request state                           | ✗                | ready / blocked / draft / stale                       |
| Review queue                                  | ✗                | pending only, approvals filtered out                  |
| Failed pipeline                               | ✗                | error lines from the job trace or the Actions job log |
| Self-hosted (GitLab CE/EE, GitHub Enterprise) | varies           | first class                                           |

## Use

```bash
npx standup-mr fetch                            # JSON, provider auto-detected
npx standup-mr fetch --provider github          # GitHub
npx standup-mr fetch --provider gitlab          # GitLab
npx standup-mr fetch --markdown                 # structured digest
npx standup-mr fetch --lang tr                  # Turkish date labels
```

```bash
npx standup-mr fetch --markdown | npx standup-mr post --google-chat "$URL"
```

### Identity

|             | GitHub                                      | GitLab                                                  |
| ----------- | ------------------------------------------- | ------------------------------------------------------- |
| Flags       | `--host` / `--token`                        | `--host` / `--token`                                    |
| Env         | `GITHUB_HOST` / `GITHUB_TOKEN`              | `GITLAB_HOST` / `GITLAB_TOKEN`                          |
| CLI session | [`gh`](https://cli.github.com/) auth config | [`glab`](https://gitlab.com/gitlab-org/cli) auth config |

GitHub defaults to `github.com` when no host is given. GitLab has no default —
self-hosted is the norm there, so a host must come from a flag, env var, or
`glab`'s own config.

The provider itself is picked in this order:

1. `--provider github` / `--provider gitlab`, if passed
2. a recognizable `--host` (`github.com`, `gitlab.com`, or a hostname
   containing `github`/`gitlab`)
3. `STANDUP_PROVIDER`, or whichever of the `GITHUB_*` / `GITLAB_*` env pairs
   is set
4. whichever of `gh` / `glab` is logged in

If none of these resolve — or both do, ambiguously — the command fails with a
clear error instead of guessing.

So if you already use `gh` or `glab`, there is nothing to configure.

### Post it to chat

```bash
npx standup-mr fetch --markdown | npx standup-mr post --slack "$SLACK_WEBHOOK_URL"
```

## The three surfaces

**CLI** — the core. Emits JSON; zero runtime dependencies.

**MCP server** (`mcp/`) — one tool, `get_standup_data`, for Claude Desktop,
Cursor, or any MCP client. See [`mcp/README.md`](https://github.com/Jubstaaa/standup-mr/blob/HEAD/mcp/README.md).

**Claude Code plugin** — the note-writing playbook, shipped as the `standup`
skill. From inside Claude Code:

```
/plugin marketplace add Jubstaaa/standup-mr
/plugin install standup@standup-mr
```

Then type `/standup`. Updates come with `/plugin marketplace update standup-mr`.

### Using it from Cursor, Codex, or another assistant

If you're not using Claude Code, wire up the MCP server for live data and
paste in the note-writing rules separately.

**Cursor** — add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json`
(project-local):

```json
{
    "mcpServers": {
        "standup": {
            "command": "npx",
            "args": ["-y", "standup-mr", "mcp"],
            "env": {
                "GITHUB_TOKEN": "ghp_..."
            }
        }
    }
}
```

**Codex** — add to `~/.codex/config.toml`:

```toml
[mcp_servers.standup]
command = "npx"
args = ["-y", "standup-mr", "mcp"]

[mcp_servers.standup.env]
GITHUB_TOKEN = "ghp_..."
```

The exact config key and file path are version-dependent for both clients —
if a snippet above doesn't work, check [Cursor's MCP
docs](https://docs.cursor.com/context/mcp) or Codex's own config
documentation for the current format rather than trusting this file blindly.

The server exposes three tools:

| Tool                    | What it does                                                                                 |
| ----------------------- | -------------------------------------------------------------------------------------------- |
| `get_standup_data`      | Reads the provider and returns the report as JSON. Optional `provider`, `host`, `lang`.      |
| `get_note_instructions` | Returns the note-writing rules, so the assistant can write the note the way the skill would. |
| `post_standup_note`     | Posts a finished note to a Slack, Discord or Google Chat webhook.                            |

`post_standup_note` reads the webhook URL from `STANDUP_WEBHOOK_URL` and never
takes it as an argument — anyone holding that URL can post to the channel, so
it belongs with the tokens, not in a transcript. The payload shape is inferred
from the URL host; `kind` is only needed when a proxy hides it.

Three shapes are implemented: `slack` and `google-chat` both post `{"text"}`,
`discord` posts `{"content"}`.

Slack and Google Chat do not render standard Markdown, so the note is rewritten
on the way out: `**bold**` becomes `*bold*` and a `##` heading becomes a bold
line. Inline code, fenced blocks and their contents are left alone. Discord
speaks Markdown natively and is sent untouched. Anything else that accepts a Slack-shaped body —
Mattermost, Rocket.Chat, an n8n or Zapier endpoint — works today by passing
`kind: "slack"`, or `--slack URL` on the CLI.

Outside MCP, the same rules are available on stdout:

```bash
npx standup-mr instructions >> AGENTS.md
```

## `--markdown` is a digest, not a written note

`--markdown` organizes the raw material into readable sections. It does **not**
group events into themes or diagnose blockers — that is the model's work, and it
lives in the MCP client's prompt or in the Claude Code skill.

- Without AI: a structured digest.
- With AI: a note you can read out.

## What the digest looks like

Anonymised output from a real Monday run — note that Friday and Saturday each
get their own section, and that a merge request GitLab has not evaluated is not
called ready:

```markdown
# Monday, 31 August — dev

_Structured digest — not a written note._

## Previous working day: Friday, 28 August

- `acme/ui` pushed to — fix(keyboard): scale keys to viewport (4 commits)
- `acme/ui` accepted — chore(deps): bump @acme/ui to 0.5.18
- `acme/api` opened — feat: package subscription sales

## Previous working day: Saturday, 29 August

- `acme/ui` pushed to — fix(keyboard): close the autofill bar (1 commit)

## Ready to merge (2)

- `acme/api` !196 fix: normalise the +90 trunk prefix
- `acme/web` !194 refactor: loading state — **no pipeline ran**

## Blocked (1)

- `acme/terminal` !49 fix: relative date chips — **1 unresolved comment(s)**

## Reviews (2 pending)

- `acme/mobile` !501 chore: upgrade to RN 0.87 — Teammate

## Blockers

- `acme/mobile` !6 — job `quality`
    - `npm ERR! code E404`
    - `npm ERR! 404 Not Found - GET https://registry.example.com/@acme%2fui`
```

The last section is the point of the tool. Every other standup tool can tell you
that pipeline is red; this one opens the failed job's log and shows you the
404 — and a 404 rather than a 403 usually means the token's scope is wrong, not
that the package is missing.

## Known limits

- **GitHub's events feed is shallow.** It is capped at roughly 300 events over
  the last 90 days, so a very active account or an old gap can silently lose
  the earliest events. GitLab has no comparable documented cap.
- **GitHub activity is only visible to a token belonging to that same
  account**, and private-repository events do not show up for anyone else's
  token, even with otherwise sufficient scopes.
- **On GitHub, CI that reports only through the legacy commit-statuses API**
  — still how some vendors integrate — shows up as `pipelineMissing`. Check
  state is read from check-runs only.
- **A blocker whose diagnosis could not be fetched is still reported**, as
  `job: "unknown"` with a `diagnosis unavailable: …` error line. The merge
  request is blocked either way; only the explanation is missing. Server
  errors are retried twice first, and a rejected token still fails the run.

## Upgrading from 0.1.x

0.2.0 changes the JSON output, the library API, and the MCP options. If you
pipe `standup fetch` into anything, or import the package, read
[`CHANGELOG.md`](https://github.com/Jubstaaa/standup-mr/blob/HEAD/CHANGELOG.md) before upgrading. The short version:

- `previous` and `previousEvents` are replaced by `previousDays[]`, one entry
  per active day, so a weekend no longer swallows Friday. `jq .previous` now
  returns `null` with no error.
- `MergeRequest`, `Review` and `Blocker` carry a required `provider` field, and
  `Provider.getReviews` takes an `Identity` rather than a numeric id.
- The MCP `CollectOptions.provider` is now a provider _name_; inject a
  `Provider` instance through `providerImpl`.

Claude Code plugin users should run `/plugin marketplace update standup-mr` —
the `standup` skill changed along with the report shape.

## Requirements

Node 20 or newer. The CLI core (`fetch`, `post`, `instructions`) pulls no
runtime dependencies. The MCP server (`mcp` command) brings one:
`@modelcontextprotocol/sdk`.

## License

MIT

