minutes-mcp 0.18.0 → 0.18.3

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/dist/index.d.ts CHANGED
@@ -59,17 +59,36 @@ export declare function meetingSearchItem(meeting: MeetingLike): {
59
59
  content_type: string | undefined;
60
60
  path: string;
61
61
  };
62
+ /**
63
+ * Pull the text of a top-level markdown section (e.g. `## Summary`) out of a
64
+ * meeting body, stopping at the next `## ` heading. Returns undefined when the
65
+ * section is absent or empty. Used to surface the synthesized summary in
66
+ * get_meeting's structuredContent without re-parsing the whole transcript.
67
+ */
68
+ export declare function extractMarkdownSection(body: string | undefined, heading: string): string | undefined;
62
69
  export declare function meetingDetailPayload(input: {
63
70
  path: string;
64
71
  speaker_map?: unknown;
65
72
  recording_health?: unknown;
66
73
  overlay_applied?: boolean;
74
+ title?: unknown;
75
+ summary?: string;
76
+ action_items?: unknown;
77
+ decisions?: unknown;
78
+ intents?: unknown;
79
+ body?: string;
67
80
  }): {
68
81
  path: string;
69
82
  view: "detail";
83
+ title?: unknown;
84
+ summary?: string;
85
+ action_items?: unknown;
86
+ decisions?: unknown;
87
+ intents?: unknown;
70
88
  speaker_map?: unknown;
71
89
  recording_health?: unknown;
72
90
  overlay_applied?: boolean;
91
+ body?: string;
73
92
  };
74
93
  export declare function shouldRunMainEntry(argv1: string | null | undefined, moduleFilename: string): boolean;
75
94
  export declare function parseKnowledgeConfig(configContent: string): KnowledgeConfigStatus | null;
package/dist/index.js CHANGED
@@ -139,11 +139,48 @@ export function meetingSearchItem(meeting) {
139
139
  path: meeting.path,
140
140
  };
141
141
  }
142
+ /**
143
+ * Pull the text of a top-level markdown section (e.g. `## Summary`) out of a
144
+ * meeting body, stopping at the next `## ` heading. Returns undefined when the
145
+ * section is absent or empty. Used to surface the synthesized summary in
146
+ * get_meeting's structuredContent without re-parsing the whole transcript.
147
+ */
148
+ export function extractMarkdownSection(body, heading) {
149
+ if (!body)
150
+ return undefined;
151
+ const lines = body.split(/\r?\n/);
152
+ const start = lines.findIndex((line) => line.trim() === `## ${heading}`);
153
+ if (start === -1)
154
+ return undefined;
155
+ const collected = [];
156
+ for (let i = start + 1; i < lines.length; i++) {
157
+ if (/^##\s/.test(lines[i]))
158
+ break;
159
+ collected.push(lines[i]);
160
+ }
161
+ const text = collected.join("\n").trim();
162
+ return text.length > 0 ? text : undefined;
163
+ }
142
164
  export function meetingDetailPayload(input) {
143
165
  const payload = {
144
166
  path: input.path,
145
167
  view: "detail",
146
168
  };
169
+ if (input.title !== undefined) {
170
+ payload.title = input.title;
171
+ }
172
+ if (input.summary !== undefined) {
173
+ payload.summary = input.summary;
174
+ }
175
+ if (input.action_items !== undefined) {
176
+ payload.action_items = input.action_items;
177
+ }
178
+ if (input.decisions !== undefined) {
179
+ payload.decisions = input.decisions;
180
+ }
181
+ if (input.intents !== undefined) {
182
+ payload.intents = input.intents;
183
+ }
147
184
  if (input.speaker_map !== undefined) {
148
185
  payload.speaker_map = input.speaker_map;
149
186
  }
@@ -153,6 +190,9 @@ export function meetingDetailPayload(input) {
153
190
  if (input.overlay_applied !== undefined) {
154
191
  payload.overlay_applied = input.overlay_applied;
155
192
  }
193
+ if (input.body !== undefined) {
194
+ payload.body = input.body;
195
+ }
156
196
  return payload;
157
197
  }
158
198
  function toolDocsUrl(name) {
@@ -348,7 +388,7 @@ const LIVE_EVENTS_SUPPORTED = hasFeature(CLI_CAPABILITIES, "events_since_seq");
348
388
  // `./version.ts` (see issue #183). Hosted `.mcpb` bundles will run
349
389
  // against CLIs with different minor/patch numbers within the same
350
390
  // major; that is explicitly supported.
351
- const MCP_SERVER_VERSION = "0.18.0";
391
+ const MCP_SERVER_VERSION = "0.18.3";
352
392
  export function parseKnowledgeConfig(configContent) {
353
393
  const knowledgeMatch = configContent.match(/\[knowledge\][\s\S]*?(?=\n\[|$)/);
354
394
  if (!knowledgeMatch) {
@@ -1860,10 +1900,22 @@ registerDocsAppTool(server, "get_meeting", {
1860
1900
  // disk is never mutated — the CLI just layers ~/.minutes/overlays.db on
1861
1901
  // top of the parsed frontmatter. If the CLI is unavailable or the call
1862
1902
  // fails, degrade gracefully to raw content.
1903
+ //
1904
+ // structuredContent mirrors what is on disk: the transcript body plus the
1905
+ // synthesized fields (summary, action_items, decisions, intents). The raw
1906
+ // markdown still rides along in content[0].text, but structured-content
1907
+ // consumers and MCP-App hosts that surface structuredContent over the text
1908
+ // block must not be left with an envelope only (issue #255).
1863
1909
  const rawParsed = reader.parseFrontmatter(rawContent, resolved);
1864
1910
  let structured = meetingDetailPayload({
1865
1911
  path: resolved,
1866
1912
  recording_health: rawParsed?.frontmatter?.recording_health,
1913
+ title: rawParsed?.frontmatter?.title,
1914
+ summary: extractMarkdownSection(rawParsed?.body, "Summary"),
1915
+ action_items: rawParsed?.frontmatter?.action_items ?? [],
1916
+ decisions: rawParsed?.frontmatter?.decisions ?? [],
1917
+ intents: rawParsed?.frontmatter?.intents ?? [],
1918
+ body: rawParsed?.body ?? rawContent,
1867
1919
  });
1868
1920
  if (await isCliAvailable()) {
1869
1921
  try {
@@ -1871,11 +1923,24 @@ registerDocsAppTool(server, "get_meeting", {
1871
1923
  const parsed = parseJsonOutput(stdout);
1872
1924
  if (parsed && typeof parsed === "object" && !parsed.raw) {
1873
1925
  const speakerMap = parsed.frontmatter?.speaker_map;
1926
+ const body = typeof parsed.body === "string" ? parsed.body : rawParsed?.body;
1874
1927
  structured = meetingDetailPayload({
1875
1928
  path: resolved,
1876
1929
  speaker_map: Array.isArray(speakerMap) ? speakerMap : [],
1877
1930
  recording_health: parsed.frontmatter?.recording_health,
1878
1931
  overlay_applied: Boolean(parsed.overlay_applied),
1932
+ title: parsed.frontmatter?.title,
1933
+ summary: extractMarkdownSection(body, "Summary"),
1934
+ action_items: Array.isArray(parsed.frontmatter?.action_items)
1935
+ ? parsed.frontmatter.action_items
1936
+ : [],
1937
+ decisions: Array.isArray(parsed.frontmatter?.decisions)
1938
+ ? parsed.frontmatter.decisions
1939
+ : [],
1940
+ intents: Array.isArray(parsed.frontmatter?.intents)
1941
+ ? parsed.frontmatter.intents
1942
+ : [],
1943
+ body: body ?? rawContent,
1879
1944
  });
1880
1945
  }
1881
1946
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minutes-mcp",
3
- "version": "0.18.0",
3
+ "version": "0.18.3",
4
4
  "description": "MCP server for minutes — conversation memory for AI assistants. Works with Claude Desktop, Mistral Vibe, Cursor, Windsurf, and any MCP client.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -53,5 +53,8 @@
53
53
  "vite": "^7.3.2",
54
54
  "vite-plugin-singlefile": "^2.3.2",
55
55
  "vitest": "^4.1.2"
56
+ },
57
+ "optionalDependencies": {
58
+ "@rollup/rollup-linux-x64-gnu": "^4.59.0"
56
59
  }
57
60
  }