Skip to content

Commit c4631ca

Browse files
feat(v0.4.5): sort_order="asc"|"desc" sur leexi_list_calls (defaut "asc" hardcoded)
L'orchestrateur cote skill (leexi-routine v0.8.0) demandait a Claude de trier ASC via prompt Markdown — pas fiable, Claude pouvait l'oublier au runtime, et le user voyait des batches dans le mauvais ordre. Fix : hardcoder le tri dans le code du tool MCP. Garanti, peu importe ce que fait Claude au runtime. - Nouveau param sort_order: "asc" | "desc" (defaut "asc") - Tri local par performed_at (string ISO-8601, comparison directe) - Applique AVANT only_unprocessed filter et AVANT fields strip - Critique pour la reprise historique : les fiches client se construisent dans l'ordre logique (R1 -> R2 -> kickoff -> steerco) au lieu de l'inverse. Tests : 60/60 (was 58) avec 2 nouveaux tests qui valident le re-sort quand l'API retourne dans l'ordre inverse. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7fec80d commit c4631ca

11 files changed

Lines changed: 171 additions & 11 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
},
77
"metadata": {
88
"description": "Leexi MCP plugin — read calls and transcripts from Claude.",
9-
"version": "0.4.4"
9+
"version": "0.4.5"
1010
},
1111
"plugins": [
1212
{
1313
"name": "leexi-mcp",
14-
"version": "0.4.4",
14+
"version": "0.4.5",
1515
"source": "./",
1616
"description": "Read Leexi calls and transcripts from Claude. Built for post-call automation routines.",
1717
"category": "productivity",

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "leexi-mcp",
33
"description": "Read Leexi calls and transcripts from Claude. Built for post-call automation routines.",
4-
"version": "0.4.4",
4+
"version": "0.4.5",
55
"author": {
66
"name": "DonkeyCode",
77
"email": "hello@donkeycode.com"

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to `@donkeycode/leexi-mcp` will be documented here. Format i
44

55
## [Unreleased]
66

7+
## [0.4.5] — 2026-05-26
8+
9+
### Added
10+
11+
- **`sort_order: "asc" | "desc"`** param on `leexi_list_calls` (default `"asc"`). Tri par `performed_at` appliqué CÔTÉ TOOL (après réception de l'API), pour garantir l'ordre chronologique indépendamment de ce que renvoie l'API Leexi. Critique pour la reprise historique : les fiches client se construisent dans l'ordre logique (R1 → R2 → kickoff → steerco) au lieu de l'inverse.
12+
13+
### Why
14+
15+
- L'orchestrateur côté skill (`leexi-routine` v0.8.0) demandait à Claude de trier ASC via prompt Markdown — pas fiable, Claude pouvait l'oublier au runtime. Hardcoder le tri dans le code du tool garantit le comportement. Le param `sort_order` reste configurable pour les cas où on veut le contraire (polling continu newest-first).
16+
17+
### Tests
18+
19+
- 60 tests (was 58). +2 couvrant sort ASC default + sort DESC explicit, avec API qui retourne dans l'ordre inverse pour valider le re-sort.
20+
721
## [0.4.4] — 2026-05-26
822

923
### Added

dist/index.js

Lines changed: 23 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/tools/list-calls.d.ts

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/tools/list-calls.js

Lines changed: 34 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/tools/list-calls.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@donkeycode/leexi-mcp",
3-
"version": "0.4.4",
3+
"version": "0.4.5",
44
"description": "DonkeyCode MCP server for Leexi (AI Notetaker) — exposes calls, transcripts, and processing state.",
55
"license": "MIT",
66
"author": "DonkeyCode <hello@donkeycode.com>",

src/tools/list-calls.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ export const ListCallsInputSchema = z.object({
2323
* Utile uniquement pour debug ou exports massifs.
2424
*/
2525
fields: z.enum(["summary", "full"]).default("summary"),
26+
/**
27+
* v0.4.5 — tri par performed_at appliqué CÔTÉ TOOL (après réception API).
28+
* - "asc" (défaut) : du plus ancien au plus récent. Critique pour la reprise
29+
* historique : les fiches client se construisent dans l'ordre logique
30+
* (R1 → R2 → kickoff → steerco) au lieu de l'inverse.
31+
* - "desc" : du plus récent au plus ancien. Utile pour "qu'est-ce qui s'est
32+
* passé hier" ou polling continu.
33+
*
34+
* Important : le tri est appliqué AVANT le filter only_unprocessed et AVANT
35+
* le strip fields, sur l'ensemble retourné par l'API pour cette page.
36+
*/
37+
sort_order: z.enum(["asc", "desc"]).default("asc"),
2638
});
2739

2840
// Raw (pre-default) input type: what callers actually pass in (e.g. {}).
@@ -65,7 +77,10 @@ export function createListCallsTool(
6577
return {
6678
name: "leexi_list_calls",
6779
description:
68-
"List Leexi calls (paginated). Default fields='summary' returns lightweight metadata only (uuid, title, performed_at, duration, locale, owner, speakers, leexi_url, summary text) — recommended for any listing/filtering use case. Pass fields='full' to include simple_transcript, chapters, tasks, prompts (~30KB extra per call, only for debug/export). Use only_unprocessed=true to skip calls already marked processed by this MCP. Pagination uses { page, items, count }.",
80+
"List Leexi calls (paginated, sorted ASC by performed_at by default). " +
81+
"Default sort_order='asc' (oldest first) is critical for historical backfills so that client timelines build in chronological order (R1 → R2 → kickoff). Pass 'desc' for newest-first polling. " +
82+
"Default fields='summary' returns lightweight metadata only (uuid, title, performed_at, duration, locale, owner, speakers, leexi_url, summary text) — recommended for any listing/filtering. Pass fields='full' to include simple_transcript, chapters, tasks, prompts (~30KB extra per call, only for debug/export). " +
83+
"Use only_unprocessed=true to skip calls already marked processed by this MCP. Pagination uses { page, items, count }.",
6984
inputSchema: ListCallsInputSchema,
7085
handler: async (rawInput) => {
7186
// Parse to apply Zod defaults before using the values.
@@ -81,6 +96,11 @@ export function createListCallsTool(
8196

8297
let calls = list.calls;
8398

99+
// v0.4.5 — Sort by performed_at locally. ASC by default for historical
100+
// backfill correctness. The Leexi API may return calls in arbitrary
101+
// order (typically DESC), so we re-sort here to guarantee the contract.
102+
calls = sortCalls(calls, input.sort_order);
103+
84104
if (input.only_unprocessed) {
85105
const filtered: CallSummary[] = [];
86106
for (const call of calls) {
@@ -103,6 +123,19 @@ export function createListCallsTool(
103123
};
104124
}
105125

126+
// ---------------------------------------------------------------------------
127+
// sortCalls — guarantees chronological order regardless of API behavior.
128+
// performedAt is ISO-8601 so string comparison is correct.
129+
// ---------------------------------------------------------------------------
130+
function sortCalls(calls: CallSummary[], order: "asc" | "desc"): CallSummary[] {
131+
const dir = order === "asc" ? 1 : -1;
132+
return [...calls].sort((a, b) => {
133+
if (a.performedAt < b.performedAt) return -1 * dir;
134+
if (a.performedAt > b.performedAt) return 1 * dir;
135+
return 0;
136+
});
137+
}
138+
106139
// ---------------------------------------------------------------------------
107140
// stripHeavyFields — drop transcript/chapters/tasks/prompts/scorecards.
108141
// Keeps the call object structurally valid (CallSummary type), just emptied.

0 commit comments

Comments
 (0)