standup-mr 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,35 @@ All notable changes to standup-mr are recorded here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
5
  [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.1.0] - 2026-10-12
8
+
9
+ ### Added
10
+
11
+ - **A compact chat digest for scheduled posts.** Scheduled and test posts now
12
+ send `toChatDigest` instead of the full Markdown report: one line per piece
13
+ of work from the last active day (repeated pushes collapsed, merges marked,
14
+ branch-deletion and merge-commit noise dropped), ready MRs grouped by
15
+ project, the reason each blocked MR is stuck, and a one-line count of
16
+ drafts, stale MRs and waiting reviews. The CLI's `--markdown` output is
17
+ unchanged.
18
+
19
+ - **Scheduled posting on the hosted connector.** Once a webhook is saved, the
20
+ connect page can post the digest automatically at a chosen time, time zone
21
+ and weekdays, with one section per linked account, and send a test right
22
+ away. A run missed by more than 30 minutes while the server was down is
23
+ skipped rather than posted late.
24
+
25
+ - **A `standup` prompt.** Clients that show MCP prompts offer it as a one-click
26
+ command — `/standup` in Claude Code — that writes the note, optionally for a
27
+ named account.
28
+
29
+ - **Gemini CLI extension.** `gemini extensions install
30
+ https://github.com/Jubstaaa/standup-mr` adds the hosted connector to Gemini
31
+ CLI, with a context file that tells it how to use the tools.
32
+ - The README shows how to add the hosted connector to Claude Code, Cursor,
33
+ VS Code, Gemini CLI, Codex CLI and ChatGPT, with one-click install buttons
34
+ for Cursor and VS Code.
35
+
7
36
  ## [1.0.1] - 2026-10-12
8
37
 
9
38
  ### Fixed
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![standup-mr MCP server](https://glama.ai/mcp/servers/Jubstaaa/standup-mr/badges/score.svg)](https://glama.ai/mcp/servers/Jubstaaa/standup-mr)
4
4
  [![Listed on mcpservers.org](https://mcpservers.org/badge.svg)](https://mcpservers.org/servers/jubstaaa/standup-mr)
5
+ [![smithery badge](https://smithery.ai/badge/ilkerbalcilartr/standup-mr)](https://smithery.ai/servers/ilkerbalcilartr/standup-mr)
5
6
 
6
7
  Standup notes from **merge request state**, not commit logs.
7
8
 
@@ -10,6 +11,8 @@ which is not what anyone asks in a standup. This one reads GitLab or GitHub:
10
11
  what is ready to merge, what is blocked, what is waiting on you — and when a
11
12
  pipeline is red, it opens the job log and tells you **why**.
12
13
 
14
+ ![standup-mr reading a GitLab project: ready, blocked, draft, and the error line from a failed job](assets/demo.gif)
15
+
13
16
  ## What makes it different
14
17
 
15
18
  | | commit-log tools | standup-mr |
@@ -66,17 +69,34 @@ So if you already use `gh` or `glab`, there is nothing to configure.
66
69
  npx standup-mr fetch --markdown | npx standup-mr post --slack "$SLACK_WEBHOOK_URL"
67
70
  ```
68
71
 
69
- ## claude.ai (web and mobile)
72
+ ## Hosted connector: any AI assistant
70
73
 
71
- No token, no config file — sign in instead. In claude.ai open **Settings →
72
- Connectors → Add custom connector** and paste:
74
+ No token, no config file — sign in instead. The hosted server speaks the
75
+ standard MCP OAuth flow, so any assistant that supports remote MCP servers can
76
+ use it: add the URL, and a browser page opens where you sign in.
73
77
 
74
78
  ```
75
79
  https://standup.ilkerbalcilar.com/mcp
76
80
  ```
77
81
 
78
- Press **Connect**. A page opens where you link the accounts the note should
79
- read:
82
+ [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=standup&config=eyJ1cmwiOiJodHRwczovL3N0YW5kdXAuaWxrZXJiYWxjaWxhci5jb20vbWNwIn0%3D)
83
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_standup-0098FF?logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=standup&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fstandup.ilkerbalcilar.com%2Fmcp%22%7D)
84
+
85
+ | Assistant | How |
86
+ | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
87
+ | **claude.ai** (web, mobile, Desktop) | Settings → Connectors → Add custom connector → paste the URL → Connect |
88
+ | **Claude Code** | `claude mcp add --scope user --transport http standup https://standup.ilkerbalcilar.com/mcp`, then `/mcp` to sign in |
89
+ | **Cursor** | The button above, or `"standup": { "url": "https://standup.ilkerbalcilar.com/mcp" }` in `~/.cursor/mcp.json` |
90
+ | **VS Code** (Copilot) | The button above, or `"standup": { "type": "http", "url": "https://standup.ilkerbalcilar.com/mcp" }` in `mcp.json` |
91
+ | **Gemini CLI** | `gemini extensions install https://github.com/Jubstaaa/standup-mr`, then `/mcp auth standup` |
92
+ | **Codex CLI** | `codex mcp add standup --url https://standup.ilkerbalcilar.com/mcp`, then `codex mcp login standup` |
93
+ | **ChatGPT** | With developer mode on: Settings → Apps → Create → paste the URL, OAuth |
94
+
95
+ claude.ai and Claude Code are tested end to end; the others use the same
96
+ standard flow. If one of them doesn't sign in, please
97
+ [open an issue](https://github.com/Jubstaaa/standup-mr/issues).
98
+
99
+ On the sign-in page you link the accounts the note should read:
80
100
 
81
101
  - **GitHub** — sign in through the standup-mr GitHub App. It only sees the
82
102
  repositories it is installed on, so install it on your org too; if the org
@@ -87,18 +107,19 @@ read:
87
107
  Otherwise set it up once: the page shows the exact redirect URI and scopes
88
108
  for an OAuth application on your host, you paste its ID and secret, and
89
109
  everyone at that host can sign in from then on. A read-only token works too.
90
- - **Chat webhook** _(optional)_ — Slack, Discord or Google Chat, so Claude can
91
- post the finished note when you ask it to.
110
+ - **Chat webhook** _(optional)_ — Slack, Discord or Google Chat, so your assistant
111
+ can post the finished note when you ask it to.
92
112
 
93
113
  Link as many as you like — say, GitHub for side projects and your company's
94
114
  GitLab for work — then ask for "my standup" or "my work standup". With
95
- several accounts linked, Claude asks which one you mean.
115
+ several accounts linked, the assistant asks which one you mean.
96
116
 
97
- The connector then works from the claude.ai mobile app as well.
117
+ Added in an assistant with a mobile app — Claude or ChatGPT — it works from
118
+ your phone too: ask for your standup on the way to the meeting.
98
119
 
99
120
  **What is stored.** Your provider tokens and webhook URL, encrypted with
100
121
  AES-256-GCM, and only to read your merge requests and post your note. The
101
- access tokens Claude holds are stored as hashes. Disconnecting the connector
122
+ access tokens your assistant holds are stored as hashes. Disconnecting it
102
123
  deletes everything; connections unused for 30 days are deleted. The server is
103
124
  the code in [`remote/`](remote/).
104
125
 
@@ -114,7 +135,8 @@ below on your own machine — the token never leaves it.
114
135
  or any MCP client that runs a local process. See [`mcp/README.md`](mcp/README.md).
115
136
 
116
137
  **Remote MCP server** (`remote/`) — the same tools over HTTPS with sign-in, for
117
- claude.ai on the web and on mobile. See [above](#claudeai-web-and-mobile).
138
+ any assistant that supports remote MCP servers, including the web and mobile
139
+ apps. See [above](#hosted-connector-any-ai-assistant).
118
140
 
119
141
  **Claude Code plugin** — the note-writing playbook, shipped as the `standup`
120
142
  skill. From inside Claude Code:
@@ -151,9 +151,9 @@ function resolveHost(cliHost, envHost, hostList, labels = GITLAB_LABELS) {
151
151
  `Multiple ${labels.name} hosts found (${[...hosts].sort().join(", ")}). Pick one with --host or ${labels.envHost}.`
152
152
  );
153
153
  }
154
- function resolveToken(host, cliToken, envToken, lookup, labels = GITLAB_LABELS) {
154
+ function resolveToken(host, cliToken, envToken, lookup, labels = GITLAB_LABELS, envHost) {
155
155
  if (cliToken) return cliToken;
156
- if (envToken) return envToken;
156
+ if (envToken && (envHost === void 0 || host === envHost)) return envToken;
157
157
  if (lookup) {
158
158
  const token = lookup(host);
159
159
  if (token) return token;
@@ -1014,6 +1014,7 @@ var GitLabProvider = class {
1014
1014
 
1015
1015
  // src/providers/select.ts
1016
1016
  var DEFAULT_PROBE = { gitlab: glabHosts, github: ghHosts };
1017
+ var GITLAB_DOT_COM = "gitlab.com";
1017
1018
  function hostFrom(cliHost, envHost, probe, fallback, labels) {
1018
1019
  if (cliHost) return cliHost;
1019
1020
  if (envHost) return envHost;
@@ -1088,7 +1089,8 @@ function connect(options = {}) {
1088
1089
  options.token,
1089
1090
  env.GITHUB_TOKEN,
1090
1091
  ghToken,
1091
- GITHUB_LABELS
1092
+ GITHUB_LABELS,
1093
+ env.GITHUB_HOST ?? DOT_COM
1092
1094
  );
1093
1095
  return new GitHubProvider(host2, token2);
1094
1096
  }
@@ -1104,7 +1106,8 @@ function connect(options = {}) {
1104
1106
  options.token,
1105
1107
  env.GITLAB_TOKEN,
1106
1108
  glabToken,
1107
- GITLAB_LABELS
1109
+ GITLAB_LABELS,
1110
+ env.GITLAB_HOST ?? GITLAB_DOT_COM
1108
1111
  );
1109
1112
  return new GitLabProvider(host, token);
1110
1113
  }
@@ -1345,6 +1348,9 @@ export {
1345
1348
  GitLabProvider,
1346
1349
  chooseKind,
1347
1350
  connect,
1351
+ STRINGS,
1352
+ REF_PREFIX,
1353
+ PROVIDER_STRINGS,
1348
1354
  toMarkdown,
1349
1355
  buildReport,
1350
1356
  findPackageRoot,
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  postWebhook,
9
9
  readStandupSkillBody,
10
10
  toMarkdown
11
- } from "./chunk-3C3PBI3T.js";
11
+ } from "./chunk-FOOXHVBP.js";
12
12
 
13
13
  // src/cli/cli.ts
14
14
  import { realpathSync } from "fs";
package/dist/index.d.ts CHANGED
@@ -106,7 +106,7 @@ interface ProviderLabels {
106
106
  declare const GITLAB_LABELS: ProviderLabels;
107
107
  declare const GITHUB_LABELS: ProviderLabels;
108
108
  declare function resolveHost(cliHost?: string, envHost?: string, hostList?: string[], labels?: ProviderLabels): string;
109
- declare function resolveToken(host: string, cliToken?: string, envToken?: string, lookup?: (host: string) => string, labels?: ProviderLabels): string;
109
+ declare function resolveToken(host: string, cliToken?: string, envToken?: string, lookup?: (host: string) => string, labels?: ProviderLabels, envHost?: string): string;
110
110
  declare function parseLoggedInHosts(statusOutput: string): string[];
111
111
  declare const parseGlabHosts: typeof parseLoggedInHosts;
112
112
  declare function glabHosts(): string[];
@@ -279,6 +279,8 @@ declare function connect(options?: SelectOptions): Provider;
279
279
 
280
280
  declare function toChatText(markdown: string, kind: WebhookKind): string;
281
281
 
282
+ declare function toChatDigest(report: StandupReport): string;
283
+
282
284
  declare function toMarkdown(report: StandupReport, lang?: string): string;
283
285
 
284
286
  declare function buildReport(provider: Provider, today: Date, lang?: string, lookbackDays?: number): Promise<StandupReport>;
@@ -287,4 +289,4 @@ declare function readStandupSkillBody(): string;
287
289
 
288
290
  declare function extractErrors(rawTrace: string, limit?: number): string[];
289
291
 
290
- export { type ActiveDay, type ActivityEvent, ApiError, type Blocker, type Bucket, ConfigError, DIAGNOSIS_UNAVAILABLE, type FetchLike, GITHUB_LABELS, GITLAB_LABELS, GitHubProvider, GitLabProvider, type Identity, type MergeRequest, type Provider, type ProviderKind, type ProviderLabels, RETRY_ATTEMPTS, RETRY_BACKOFF_MS, type Review, STALE_DAYS, type SelectOptions, type StandupReport, approvedBy, assertUsable, buildReport, buildUrl, chooseKind, classify, connect, countChangesRequested, degradable, extractErrors, ghHosts, ghToken, glabHosts, glabToken, inferWebhookKind, isoDay, label, latestStateByReviewer, localAt, mapEvent, markMissingPipelines, normalizeChecks, parseGlabHosts, parseLoggedInHosts, postWebhook, previousActiveDays, readStandupSkillBody, repoFromUrl, resolveHost, resolveToken, sendWithRetry, toChatText, toMarkdown, undiagnosed, unreachable };
292
+ export { type ActiveDay, type ActivityEvent, ApiError, type Blocker, type Bucket, ConfigError, DIAGNOSIS_UNAVAILABLE, type FetchLike, GITHUB_LABELS, GITLAB_LABELS, GitHubProvider, GitLabProvider, type Identity, type MergeRequest, type Provider, type ProviderKind, type ProviderLabels, RETRY_ATTEMPTS, RETRY_BACKOFF_MS, type Review, STALE_DAYS, type SelectOptions, type StandupReport, approvedBy, assertUsable, buildReport, buildUrl, chooseKind, classify, connect, countChangesRequested, degradable, extractErrors, ghHosts, ghToken, glabHosts, glabToken, inferWebhookKind, isoDay, label, latestStateByReviewer, localAt, mapEvent, markMissingPipelines, normalizeChecks, parseGlabHosts, parseLoggedInHosts, postWebhook, previousActiveDays, readStandupSkillBody, repoFromUrl, resolveHost, resolveToken, sendWithRetry, toChatDigest, toChatText, toMarkdown, undiagnosed, unreachable };
package/dist/index.js CHANGED
@@ -7,9 +7,12 @@ import {
7
7
  GITLAB_LABELS,
8
8
  GitHubProvider,
9
9
  GitLabProvider,
10
+ PROVIDER_STRINGS,
11
+ REF_PREFIX,
10
12
  RETRY_ATTEMPTS,
11
13
  RETRY_BACKOFF_MS,
12
14
  STALE_DAYS,
15
+ STRINGS,
13
16
  approvedBy,
14
17
  assertUsable,
15
18
  buildReport,
@@ -45,7 +48,155 @@ import {
45
48
  toMarkdown,
46
49
  undiagnosed,
47
50
  unreachable
48
- } from "./chunk-3C3PBI3T.js";
51
+ } from "./chunk-FOOXHVBP.js";
52
+
53
+ // src/render/digest.constants.ts
54
+ var MAX_DAY_LINES = 8;
55
+ var TITLE_LIMIT = 80;
56
+ var ERROR_LIMIT = 120;
57
+ var MERGED_ACTIONS = ["accepted", "merged"];
58
+ var MERGE_COMMIT = /^Merge (branch|remote-tracking branch|pull request) /;
59
+ var DIGEST_STRINGS = {
60
+ standup: "Standup",
61
+ yesterday: "Yesterday",
62
+ daysAgo: "days ago",
63
+ merged: "merged",
64
+ more: "more",
65
+ ready: "Ready to merge",
66
+ blocked: "Blocked",
67
+ pipelineFailed: "pipeline failed",
68
+ also: "Also:",
69
+ drafts: "drafts",
70
+ stale: "stale",
71
+ reviews: "reviews waiting"
72
+ };
73
+
74
+ // src/render/digest.ts
75
+ function shortName(project) {
76
+ return project.split("/").pop() ?? project;
77
+ }
78
+ function truncate(text, limit) {
79
+ return text.length > limit ? `${text.slice(0, limit - 1).trimEnd()}\u2026` : text;
80
+ }
81
+ function squash(text) {
82
+ return text.toLowerCase().replace(/[^a-z0-9]/g, "");
83
+ }
84
+ function isNoise(text, project) {
85
+ if (!text || MERGE_COMMIT.test(text)) return true;
86
+ return !text.includes(":") && squash(text).endsWith(squash(shortName(project)));
87
+ }
88
+ function eventTitle(event, mrs) {
89
+ if (!event.commits && !event.commitTitle) return event.title;
90
+ const mr = mrs.find(
91
+ (mr2) => mr2.project === event.project && mr2.branch === event.branch
92
+ );
93
+ return mr?.title ?? (event.commitTitle || event.branch);
94
+ }
95
+ function dayItems(day, mrs) {
96
+ const items = /* @__PURE__ */ new Map();
97
+ for (const event of day.events) {
98
+ const title = eventTitle(event, mrs);
99
+ if (isNoise(title, event.project)) continue;
100
+ const key = `${event.project}
101
+ ${title}`;
102
+ const item = items.get(key) ?? {
103
+ merged: false,
104
+ project: event.project,
105
+ title
106
+ };
107
+ item.merged ||= MERGED_ACTIONS.includes(event.action);
108
+ items.set(key, item);
109
+ }
110
+ return [...items.values()];
111
+ }
112
+ function hasWeekdayBetween(from, to) {
113
+ const day = /* @__PURE__ */ new Date(`${from}T00:00:00Z`);
114
+ day.setUTCDate(day.getUTCDate() + 1);
115
+ for (; day.toISOString().slice(0, 10) < to; day.setUTCDate(day.getUTCDate() + 1)) {
116
+ const weekday = day.getUTCDay();
117
+ if (weekday >= 1 && weekday <= 5) return true;
118
+ }
119
+ return false;
120
+ }
121
+ function daySection(report) {
122
+ const day = report.previousDays.at(-1);
123
+ if (!day) return [];
124
+ const items = dayItems(day, report.myMrs);
125
+ if (items.length === 0) return [];
126
+ const heading = hasWeekdayBetween(day.date, report.today.date) ? `**${day.label} (${day.gapDays} ${DIGEST_STRINGS.daysAgo})**` : `**${DIGEST_STRINGS.yesterday} (${day.label})**`;
127
+ const lines = items.slice(0, MAX_DAY_LINES).map((item) => {
128
+ const merged = item.merged ? ` (${DIGEST_STRINGS.merged})` : "";
129
+ return `- ${shortName(item.project)} \u2014 ${truncate(item.title, TITLE_LIMIT)}${merged}`;
130
+ });
131
+ const hidden = items.length - MAX_DAY_LINES;
132
+ if (hidden > 0) lines.push(`- +${hidden} ${DIGEST_STRINGS.more}`);
133
+ return [heading, ...lines];
134
+ }
135
+ function readySection(report, ref) {
136
+ const ready = report.myMrs.filter((mr) => mr.bucket === "ready");
137
+ if (ready.length === 0) return [];
138
+ const byProject = /* @__PURE__ */ new Map();
139
+ for (const mr of ready) {
140
+ const name = shortName(mr.project);
141
+ byProject.set(name, [...byProject.get(name) ?? [], `${ref}${mr.iid}`]);
142
+ }
143
+ const groups = [...byProject].map(
144
+ ([name, refs]) => `${name} ${refs.join(", ")}`
145
+ );
146
+ return [`**${DIGEST_STRINGS.ready} (${ready.length})** ${groups.join(" \xB7 ")}`];
147
+ }
148
+ function blockedReason(report, mr, unresolved) {
149
+ const reasons = [];
150
+ const blocker = report.blockers.find(
151
+ (blocker2) => blocker2.project === mr.project && blocker2.mr === mr.iid
152
+ );
153
+ if (blocker) {
154
+ const error = blocker.errors[0];
155
+ reasons.push(
156
+ `${DIGEST_STRINGS.pipelineFailed} (${blocker.job})` + (error ? `: \`${truncate(error, ERROR_LIMIT)}\`` : "")
157
+ );
158
+ } else if (mr.pipeline === "failed") {
159
+ reasons.push(DIGEST_STRINGS.pipelineFailed);
160
+ }
161
+ if (mr.pipelineMissing) reasons.push(STRINGS.en.noPipeline);
162
+ if (mr.unresolved) reasons.push(`${mr.unresolved} ${unresolved}`);
163
+ return reasons.join("; ");
164
+ }
165
+ function blockedSection(report, ref) {
166
+ const blocked = report.myMrs.filter((mr) => mr.bucket === "blocked");
167
+ if (blocked.length === 0) return [];
168
+ const unresolved = PROVIDER_STRINGS[report.provider].en?.unresolved ?? STRINGS.en.unresolved;
169
+ return [
170
+ `**${DIGEST_STRINGS.blocked}**`,
171
+ ...blocked.map((mr) => {
172
+ const reason = blockedReason(report, mr, unresolved);
173
+ const suffix = reason ? ` \u2014 ${reason}` : "";
174
+ return `- ${shortName(mr.project)} ${ref}${mr.iid}${suffix}`;
175
+ })
176
+ ];
177
+ }
178
+ function alsoLine(report) {
179
+ const count = (bucket) => report.myMrs.filter((mr) => mr.bucket === bucket).length;
180
+ const parts = [
181
+ [count("draft"), DIGEST_STRINGS.drafts],
182
+ [count("stale"), DIGEST_STRINGS.stale],
183
+ [report.reviewPendingCount, DIGEST_STRINGS.reviews]
184
+ ];
185
+ const shown = parts.filter(([n]) => n > 0).map(([n, name]) => `${n} ${name}`);
186
+ return shown.length > 0 ? [`**${DIGEST_STRINGS.also}** ${shown.join(" \xB7 ")}`] : [];
187
+ }
188
+ function toChatDigest(report) {
189
+ const ref = REF_PREFIX[report.provider];
190
+ const lines = [
191
+ `**${DIGEST_STRINGS.standup} \u2014 ${report.today.label}** \xB7 ${report.user}`,
192
+ ...daySection(report),
193
+ ...readySection(report, ref),
194
+ ...blockedSection(report, ref),
195
+ ...alsoLine(report)
196
+ ];
197
+ return `${lines.join("\n")}
198
+ `;
199
+ }
49
200
  export {
50
201
  ApiError,
51
202
  ConfigError,
@@ -88,6 +239,7 @@ export {
88
239
  resolveHost,
89
240
  resolveToken,
90
241
  sendWithRetry,
242
+ toChatDigest,
91
243
  toChatText,
92
244
  toMarkdown,
93
245
  undiagnosed,
@@ -137,9 +137,9 @@ function resolveHost(cliHost, envHost, hostList, labels = GITLAB_LABELS) {
137
137
  `Multiple ${labels.name} hosts found (${[...hosts].sort().join(", ")}). Pick one with --host or ${labels.envHost}.`
138
138
  );
139
139
  }
140
- function resolveToken(host, cliToken, envToken, lookup, labels = GITLAB_LABELS) {
140
+ function resolveToken(host, cliToken, envToken, lookup, labels = GITLAB_LABELS, envHost) {
141
141
  if (cliToken) return cliToken;
142
- if (envToken) return envToken;
142
+ if (envToken && (envHost === void 0 || host === envHost)) return envToken;
143
143
  if (lookup) {
144
144
  const token = lookup(host);
145
145
  if (token) return token;
@@ -1046,6 +1046,7 @@ var GitLabProvider = class {
1046
1046
 
1047
1047
  // src/providers/select.ts
1048
1048
  var DEFAULT_PROBE = { gitlab: glabHosts, github: ghHosts };
1049
+ var GITLAB_DOT_COM = "gitlab.com";
1049
1050
  function hostFrom(cliHost, envHost, probe, fallback, labels) {
1050
1051
  if (cliHost) return cliHost;
1051
1052
  if (envHost) return envHost;
@@ -1120,7 +1121,8 @@ function connect(options = {}) {
1120
1121
  options.token,
1121
1122
  env.GITHUB_TOKEN,
1122
1123
  ghToken,
1123
- GITHUB_LABELS
1124
+ GITHUB_LABELS,
1125
+ env.GITHUB_HOST ?? DOT_COM
1124
1126
  );
1125
1127
  return new GitHubProvider(host2, token2);
1126
1128
  }
@@ -1136,7 +1138,8 @@ function connect(options = {}) {
1136
1138
  options.token,
1137
1139
  env.GITLAB_TOKEN,
1138
1140
  glabToken,
1139
- GITLAB_LABELS
1141
+ GITLAB_LABELS,
1142
+ env.GITLAB_HOST ?? GITLAB_DOT_COM
1140
1143
  );
1141
1144
  return new GitLabProvider(host, token);
1142
1145
  }
@@ -1280,6 +1283,28 @@ async function runInstructionsTool() {
1280
1283
  var STANDUP_TOOL_TITLE = "Get standup data";
1281
1284
  var POST_TOOL_TITLE = "Post standup note";
1282
1285
  var INSTRUCTIONS_TOOL_TITLE = "Get note instructions";
1286
+ var STANDUP_PROMPT_TITLE = "Write my standup";
1287
+ var STANDUP_PROMPT_DESCRIPTION = "Writes your standup note from merge request state: what you did, what is ready or blocked today, and why any pipeline is red.";
1288
+ var STANDUP_PROMPT_SCHEMA = {
1289
+ account: z.string().optional().describe(
1290
+ "Which account to read when several are linked: github, gitlab, or a host."
1291
+ )
1292
+ };
1293
+ function standupPrompt(args) {
1294
+ const account = args.account?.trim();
1295
+ const which = account ? ` Read the ${account} account (pass it as provider or host).` : "";
1296
+ return {
1297
+ messages: [
1298
+ {
1299
+ role: "user",
1300
+ content: {
1301
+ type: "text",
1302
+ text: "Write my standup note. First call get_note_instructions and follow its rules, then call get_standup_data." + which + " If several accounts are linked and I did not say which, ask me. Write the note in the language I'm using. Do not post it anywhere unless I ask."
1303
+ }
1304
+ }
1305
+ ]
1306
+ };
1307
+ }
1283
1308
  var STANDUP_TOOL_ANNOTATIONS = {
1284
1309
  readOnlyHint: true,
1285
1310
  destructiveHint: false,
@@ -1345,6 +1370,15 @@ function createServer() {
1345
1370
  },
1346
1371
  async () => await runInstructionsTool()
1347
1372
  );
1373
+ server.registerPrompt(
1374
+ "standup",
1375
+ {
1376
+ title: STANDUP_PROMPT_TITLE,
1377
+ description: STANDUP_PROMPT_DESCRIPTION,
1378
+ argsSchema: STANDUP_PROMPT_SCHEMA
1379
+ },
1380
+ (args) => standupPrompt(args)
1381
+ );
1348
1382
  return server;
1349
1383
  }
1350
1384
  async function main() {
@@ -1375,6 +1409,9 @@ export {
1375
1409
  POST_TOOL_SCHEMA,
1376
1410
  POST_TOOL_TITLE,
1377
1411
  SERVER_ICONS,
1412
+ STANDUP_PROMPT_DESCRIPTION,
1413
+ STANDUP_PROMPT_SCHEMA,
1414
+ STANDUP_PROMPT_TITLE,
1378
1415
  STANDUP_TOOL_ANNOTATIONS,
1379
1416
  STANDUP_TOOL_DESCRIPTION,
1380
1417
  STANDUP_TOOL_SCHEMA,
@@ -1387,5 +1424,6 @@ export {
1387
1424
  runInstructionsTool,
1388
1425
  runPostTool,
1389
1426
  runStandupTool,
1427
+ standupPrompt,
1390
1428
  text
1391
1429
  };
package/mcp/README.md CHANGED
@@ -6,7 +6,7 @@ the same JSON as `standup fetch`.
6
6
 
7
7
  This is the local, stdio server: it runs on your machine and reads tokens from
8
8
  its environment. On claude.ai (web or mobile), use the hosted connector instead
9
- — see [claude.ai (web and mobile)](../README.md#claudeai-web-and-mobile).
9
+ — see the [hosted connector](../README.md#hosted-connector-any-ai-assistant).
10
10
 
11
11
  ## Configure
12
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "standup-mr",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "mcpName": "io.github.Jubstaaa/standup-mr",
5
5
  "description": "Standup notes from merge request state, not commit logs.",
6
6
  "keywords": [