@tpsdev-ai/flair-mcp 0.50.0 → 0.51.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/dist/index.js CHANGED
@@ -45,7 +45,7 @@ import { FlairClient, FlairError, formatKeyLookup, inspectKeyLookup } from "@tps
45
45
  import { z } from "zod";
46
46
  import { deriveActivity, postPresenceSafe, resolveHeartbeatIntervalMs, resolvePresenceTimeoutMs, shouldSendHeartbeat, } from "./presence.js";
47
47
  import { readEnvOrUnset, stripInterpolationLiteralsFromEnv } from "./env-guard.js";
48
- import { buildRecordUsageBody, citationIds, withCiteNudge } from "./usage.js";
48
+ import { buildRecordUsageBody, citationIds, withCiteNudge, RECORD_USAGE_ID_MERGE_CONTRACT } from "./usage.js";
49
49
  import { serverInfo } from "./version.js";
50
50
  // ─── Error helpers ──────────────────────────────────────────────────────────
51
51
  export function classifyError(err, flairUrl) {
@@ -521,9 +521,10 @@ export async function runMcp() {
521
521
  // the usageCount loop. Identity is taken from the signed request — the body
522
522
  // carries only memory id(s) + optional attribution, never agentId.
523
523
  server.tool("record_usage", "Report that one or more memories were actually USED — cited or relied on to ground an answer or decision. " +
524
- "Distinct from search (surfacing a memory is not usage). Dedup'd (you can only count once per memory) and rate-limited.", {
525
- memoryId: z.string().optional().describe("A single memory id that was used"),
526
- memoryIds: z.array(z.string()).optional().describe("IDs of the memories that were used (max 20 per call)"),
524
+ "Distinct from search (surfacing a memory is not usage). Dedup'd (you can only count once per memory) and rate-limited. " +
525
+ RECORD_USAGE_ID_MERGE_CONTRACT, {
526
+ memoryId: z.string().optional().describe("A single memory id that was used. Merged with memoryIds when both are supplied — not dropped."),
527
+ memoryIds: z.array(z.string()).optional().describe("IDs of the memories that were used (max 20 per call). Merged with memoryId when both are supplied."),
527
528
  attribution: z.string().optional().describe("Optional one-line note on how it was used (opaque — stored for audit only)"),
528
529
  }, async ({ memoryId, memoryIds, attribution }) => {
529
530
  heartbeat(); // auto-presence (flair#598) — fire-and-forget, rate-limited
package/dist/usage.d.ts CHANGED
@@ -13,30 +13,19 @@
13
13
  /** One-line instruction on recalled ids (issue ask #3). Search/bootstrap hits are not usage. */
14
14
  export declare const CITE_USAGE_NUDGE = "Cite memories you actually use via record_usage (or memory_store.usedMemoryIds). A search or bootstrap hit is not usage.";
15
15
  export declare function withCiteNudge(text: string): string;
16
+ /** Stated on the stdio `record_usage` schema so a caller can predict the merge without reading source. */
17
+ export declare const RECORD_USAGE_ID_MERGE_CONTRACT = "When both memoryId and memoryIds are supplied they are merged (union, then deduped) \u2014 a caller who passes both means both.";
16
18
  /**
17
19
  * Build the POST /RecordUsage body from the MCP tool args.
18
20
  * Accepts singular `memoryId` and/or `memoryIds`. Returns null when there is
19
21
  * nothing to send (the tool should fail locally rather than POST an empty list).
20
22
  * Never includes agentId — the server attributes from the signature.
21
23
  *
22
- * MERGE vs PREFER (deliberate, named Sherlock/Kern #1404):
23
- * This helper MERGES `memoryId` + `memoryIds`, then dedupes. Native `/mcp`
24
- * `recordUsage` (resources/mcp-tools.ts) PREFERS `memoryIds` and drops
25
- * `memoryId` when both are supplied. The HTTP endpoint does the same:
26
- * `RecordUsage.post()` is `data?.memoryIds ?? [data?.memoryId]` — PREFER,
27
- * not union. If `memoryIds` is present (even `[]`, which is truthy),
28
- * `memoryId` is never read.
29
- *
30
- * The stdio merge is load-bearing because it flattens first: we send only
31
- * a single `memoryIds` array, so the server's prefer is never exercised
32
- * on two fields. A future path that POSTs both fields through to
33
- * `/RecordUsage` without flattening would silently drop `memoryId`
34
- * (delivered-but-uncounted; empty `memoryIds: []` alongside a real
35
- * `memoryId` would 400 rather than fall through).
36
- *
37
- * Native prefer is a pre-existing delivered-but-uncounted bug on a
38
- * different surface, tracked in flair#1410 — not a regression from this
39
- * PR, and not a blocker for #1147. Do not "align" this helper to prefer.
24
+ * MERGE (flair#1410): this helper unions `memoryId` + `memoryIds`, then
25
+ * dedupes. Native `/mcp` and `POST /RecordUsage` do the same a caller
26
+ * who passes both means both. Do not "align" this helper to prefer
27
+ * `memoryIds` and drop `memoryId`; that is the delivered-but-uncounted
28
+ * bug #1410 closed.
40
29
  */
41
30
  export declare function buildRecordUsageBody(args: {
42
31
  memoryId?: string;
package/dist/usage.js CHANGED
@@ -17,30 +17,19 @@ export function withCiteNudge(text) {
17
17
  return text;
18
18
  return `${text}\n\n${CITE_USAGE_NUDGE}`;
19
19
  }
20
+ /** Stated on the stdio `record_usage` schema so a caller can predict the merge without reading source. */
21
+ export const RECORD_USAGE_ID_MERGE_CONTRACT = "When both memoryId and memoryIds are supplied they are merged (union, then deduped) — a caller who passes both means both.";
20
22
  /**
21
23
  * Build the POST /RecordUsage body from the MCP tool args.
22
24
  * Accepts singular `memoryId` and/or `memoryIds`. Returns null when there is
23
25
  * nothing to send (the tool should fail locally rather than POST an empty list).
24
26
  * Never includes agentId — the server attributes from the signature.
25
27
  *
26
- * MERGE vs PREFER (deliberate, named Sherlock/Kern #1404):
27
- * This helper MERGES `memoryId` + `memoryIds`, then dedupes. Native `/mcp`
28
- * `recordUsage` (resources/mcp-tools.ts) PREFERS `memoryIds` and drops
29
- * `memoryId` when both are supplied. The HTTP endpoint does the same:
30
- * `RecordUsage.post()` is `data?.memoryIds ?? [data?.memoryId]` — PREFER,
31
- * not union. If `memoryIds` is present (even `[]`, which is truthy),
32
- * `memoryId` is never read.
33
- *
34
- * The stdio merge is load-bearing because it flattens first: we send only
35
- * a single `memoryIds` array, so the server's prefer is never exercised
36
- * on two fields. A future path that POSTs both fields through to
37
- * `/RecordUsage` without flattening would silently drop `memoryId`
38
- * (delivered-but-uncounted; empty `memoryIds: []` alongside a real
39
- * `memoryId` would 400 rather than fall through).
40
- *
41
- * Native prefer is a pre-existing delivered-but-uncounted bug on a
42
- * different surface, tracked in flair#1410 — not a regression from this
43
- * PR, and not a blocker for #1147. Do not "align" this helper to prefer.
28
+ * MERGE (flair#1410): this helper unions `memoryId` + `memoryIds`, then
29
+ * dedupes. Native `/mcp` and `POST /RecordUsage` do the same a caller
30
+ * who passes both means both. Do not "align" this helper to prefer
31
+ * `memoryIds` and drop `memoryId`; that is the delivered-but-uncounted
32
+ * bug #1410 closed.
44
33
  */
45
34
  export function buildRecordUsageBody(args) {
46
35
  const ids = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpsdev-ai/flair-mcp",
3
- "version": "0.50.0",
3
+ "version": "0.51.1",
4
4
  "description": "MCP server for Flair — persistent memory for Claude Code, Cursor, and any MCP client.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@modelcontextprotocol/sdk": "1.27.1",
31
- "@tpsdev-ai/flair-client": "0.50.0",
31
+ "@tpsdev-ai/flair-client": "0.51.1",
32
32
  "zod": "4.3.6"
33
33
  },
34
34
  "license": "Apache-2.0",