@tpsdev-ai/flair-mcp 0.48.0 → 0.50.0
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/README.md +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +59 -19
- package/dist/session-start-hook.d.ts +1 -1
- package/dist/session-start-hook.js +1 -1
- package/dist/usage.d.ts +54 -0
- package/dist/usage.js +76 -0
- package/dist/version.d.ts +28 -0
- package/dist/version.js +52 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -41,12 +41,13 @@ Once configured, Claude Code (or any MCP client) gets these tools:
|
|
|
41
41
|
| Tool | Description |
|
|
42
42
|
|------|-------------|
|
|
43
43
|
| `memory_search` | Semantic search across memories. Understands "what happened today". |
|
|
44
|
-
| `memory_store` | Save a memory with type (lesson/decision/fact) and durability. |
|
|
44
|
+
| `memory_store` | Save a memory with type (lesson/decision/fact) and durability. Optional `usedMemoryIds` cites memories that informed the write. |
|
|
45
45
|
| `memory_get` | Retrieve a specific memory by ID. |
|
|
46
46
|
| `memory_delete` | Delete a memory. |
|
|
47
47
|
| `bootstrap` | Cold-start context — soul + recent memories in one call. |
|
|
48
48
|
| `soul_set` | Set personality or project context (included in every bootstrap). |
|
|
49
49
|
| `soul_get` | Get a personality or project context entry. |
|
|
50
|
+
| `record_usage` | Report that recalled memories were actually used (drives `usageCount`). |
|
|
50
51
|
|
|
51
52
|
## Environment Variables
|
|
52
53
|
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* - soul_get — get a personality/context entry
|
|
15
15
|
* - flair_workspace_set — write own WorkspaceState (Office Space coordination)
|
|
16
16
|
* - flair_orgevent — publish an OrgEvent attributed to self (no forging)
|
|
17
|
+
* - record_usage — report that recalled memories were actually used (flair#1147)
|
|
17
18
|
*
|
|
18
19
|
* Auto-presence (flair#598): every tool call above triggers a fire-and-forget,
|
|
19
20
|
* rate-limited `POST /Presence` heartbeat for the calling agent (see
|
|
@@ -38,4 +39,5 @@
|
|
|
38
39
|
* — the silent `npx -y @tpsdev-ai/flair-mcp` failure. The shim checks the Node
|
|
39
40
|
* version FIRST, then dynamically imports this module and calls runMcp().
|
|
40
41
|
*/
|
|
42
|
+
export declare function classifyError(err: unknown, flairUrl: string): string;
|
|
41
43
|
export declare function runMcp(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* - soul_get — get a personality/context entry
|
|
15
15
|
* - flair_workspace_set — write own WorkspaceState (Office Space coordination)
|
|
16
16
|
* - flair_orgevent — publish an OrgEvent attributed to self (no forging)
|
|
17
|
+
* - record_usage — report that recalled memories were actually used (flair#1147)
|
|
17
18
|
*
|
|
18
19
|
* Auto-presence (flair#598): every tool call above triggers a fire-and-forget,
|
|
19
20
|
* rate-limited `POST /Presence` heartbeat for the calling agent (see
|
|
@@ -40,25 +41,27 @@
|
|
|
40
41
|
*/
|
|
41
42
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
42
43
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
43
|
-
import { FlairClient, FlairError } from "@tpsdev-ai/flair-client";
|
|
44
|
+
import { FlairClient, FlairError, formatKeyLookup, inspectKeyLookup } from "@tpsdev-ai/flair-client";
|
|
44
45
|
import { z } from "zod";
|
|
45
46
|
import { deriveActivity, postPresenceSafe, resolveHeartbeatIntervalMs, resolvePresenceTimeoutMs, shouldSendHeartbeat, } from "./presence.js";
|
|
46
47
|
import { readEnvOrUnset, stripInterpolationLiteralsFromEnv } from "./env-guard.js";
|
|
48
|
+
import { buildRecordUsageBody, citationIds, withCiteNudge } from "./usage.js";
|
|
49
|
+
import { serverInfo } from "./version.js";
|
|
47
50
|
// ─── Error helpers ──────────────────────────────────────────────────────────
|
|
48
|
-
function classifyError(err, flairUrl) {
|
|
51
|
+
export function classifyError(err, flairUrl) {
|
|
49
52
|
if (err instanceof FlairError) {
|
|
50
53
|
const { status, body } = err;
|
|
51
54
|
if (status === 400)
|
|
52
55
|
return `validation_error: ${body}`;
|
|
53
56
|
if (status === 401 || status === 403) {
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
// flair#1271: name the agent, the paths that were looked in, and the
|
|
58
|
+
// remedy. A cached-miss / wrong-HOME 401 is not a daemon-restart hint.
|
|
59
|
+
const lookup = err.keyLookup ?? {
|
|
60
|
+
...inspectKeyLookup(readEnvOrUnset("FLAIR_AGENT_ID") ?? "", readEnvOrUnset("FLAIR_KEY_PATH")),
|
|
61
|
+
signed: false,
|
|
62
|
+
authMethod: "none",
|
|
63
|
+
};
|
|
64
|
+
return `auth_error: ${body}\n${formatKeyLookup(lookup)}`;
|
|
62
65
|
}
|
|
63
66
|
if (status === 413)
|
|
64
67
|
return `payload_too_large: ${body}`;
|
|
@@ -212,10 +215,7 @@ export async function runMcp() {
|
|
|
212
215
|
postPresenceSafe(presenceFlair, activity, lastKnownTask, resolvePresenceTimeoutMs()).catch(() => { });
|
|
213
216
|
}
|
|
214
217
|
// ─── MCP Server ──────────────────────────────────────────────────────────────
|
|
215
|
-
const server = new McpServer(
|
|
216
|
-
name: "flair",
|
|
217
|
-
version: "0.1.0",
|
|
218
|
-
});
|
|
218
|
+
const server = new McpServer(serverInfo());
|
|
219
219
|
// ─── Tools ───────────────────────────────────────────────────────────────────
|
|
220
220
|
server.tool("memory_search", "Search memories by meaning. Understands temporal queries like 'what happened today'.", {
|
|
221
221
|
query: z.string().describe("Search query — natural language, semantic matching"),
|
|
@@ -235,7 +235,7 @@ export async function runMcp() {
|
|
|
235
235
|
return `${i + 1}. ${r.content}${meta ? ` (${meta})` : ""}`;
|
|
236
236
|
})
|
|
237
237
|
.join("\n");
|
|
238
|
-
return { content: [{ type: "text", text }] };
|
|
238
|
+
return { content: [{ type: "text", text: withCiteNudge(text) }] };
|
|
239
239
|
}
|
|
240
240
|
catch (err) {
|
|
241
241
|
return errorResult(err, flair.url);
|
|
@@ -254,7 +254,9 @@ export async function runMcp() {
|
|
|
254
254
|
"permanent/persistent -> shared, standard/ephemeral -> private). " +
|
|
255
255
|
"private -- never visible to another agent, even one with a memory grant. " +
|
|
256
256
|
"shared -- visible to the owner and any agent holding a read/search grant."),
|
|
257
|
-
|
|
257
|
+
usedMemoryIds: z.array(z.string()).optional().describe("IDs of memories that informed this write (citation-on-write). Credited via the same " +
|
|
258
|
+
"deduped usage ledger as record_usage. Optional."),
|
|
259
|
+
}, async ({ content, type, durability, tags, visibility, usedMemoryIds }) => {
|
|
258
260
|
heartbeat(); // auto-presence (flair#598) — fire-and-forget, rate-limited
|
|
259
261
|
try {
|
|
260
262
|
const result = await flair.memory.write(content, {
|
|
@@ -264,6 +266,7 @@ export async function runMcp() {
|
|
|
264
266
|
visibility: visibility,
|
|
265
267
|
dedup: true,
|
|
266
268
|
dedupThreshold: 0.95,
|
|
269
|
+
usedMemoryIds: citationIds(usedMemoryIds),
|
|
267
270
|
});
|
|
268
271
|
// The server's conservative dedup gate NEVER suppresses a write
|
|
269
272
|
// (memory-integrity fix, flair#526) — `result.deduplicated` is a
|
|
@@ -307,10 +310,15 @@ export async function runMcp() {
|
|
|
307
310
|
content: z.string().describe("New content"),
|
|
308
311
|
preserveHistory: z.coerce.boolean().optional().default(false)
|
|
309
312
|
.describe("Write a new supersedes-linked version instead of overwriting in place (default false)"),
|
|
310
|
-
|
|
313
|
+
usedMemoryIds: z.array(z.string()).optional().describe("IDs of memories that informed this update (citation-on-write). Credited via the same " +
|
|
314
|
+
"deduped usage ledger as record_usage. Optional."),
|
|
315
|
+
}, async ({ id, content, preserveHistory, usedMemoryIds }) => {
|
|
311
316
|
heartbeat(); // auto-presence (flair#598) — fire-and-forget, rate-limited
|
|
312
317
|
try {
|
|
313
|
-
const result = await flair.memory.update(id, content, {
|
|
318
|
+
const result = await flair.memory.update(id, content, {
|
|
319
|
+
preserveHistory,
|
|
320
|
+
usedMemoryIds: citationIds(usedMemoryIds),
|
|
321
|
+
});
|
|
314
322
|
const text = preserveHistory
|
|
315
323
|
? `Memory updated: new version stored (id: ${result.id}), supersedes ${id}.`
|
|
316
324
|
: `Memory updated (id: ${id}).`;
|
|
@@ -406,7 +414,7 @@ export async function runMcp() {
|
|
|
406
414
|
if (!result.context) {
|
|
407
415
|
return { content: [{ type: "text", text: "No context available." }] };
|
|
408
416
|
}
|
|
409
|
-
return { content: [{ type: "text", text: result.context }] };
|
|
417
|
+
return { content: [{ type: "text", text: withCiteNudge(result.context) }] };
|
|
410
418
|
}
|
|
411
419
|
catch (err) {
|
|
412
420
|
return errorResult(err, flair.url);
|
|
@@ -506,6 +514,38 @@ export async function runMcp() {
|
|
|
506
514
|
return errorResult(err, flair.url);
|
|
507
515
|
}
|
|
508
516
|
});
|
|
517
|
+
// ─── Usage feedback (flair#1147) ─────────────────────────────────────────────
|
|
518
|
+
//
|
|
519
|
+
// POST /RecordUsage already existed; native /mcp already wrapped it. The
|
|
520
|
+
// stdio package did not, so a Claude Code / Cursor client could not close
|
|
521
|
+
// the usageCount loop. Identity is taken from the signed request — the body
|
|
522
|
+
// carries only memory id(s) + optional attribution, never agentId.
|
|
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)"),
|
|
527
|
+
attribution: z.string().optional().describe("Optional one-line note on how it was used (opaque — stored for audit only)"),
|
|
528
|
+
}, async ({ memoryId, memoryIds, attribution }) => {
|
|
529
|
+
heartbeat(); // auto-presence (flair#598) — fire-and-forget, rate-limited
|
|
530
|
+
try {
|
|
531
|
+
const body = buildRecordUsageBody({ memoryId, memoryIds, attribution });
|
|
532
|
+
if (!body) {
|
|
533
|
+
return {
|
|
534
|
+
content: [{ type: "text", text: "record_usage requires memoryId or memoryIds." }],
|
|
535
|
+
isError: true,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
const result = await flair.request("POST", "/RecordUsage", body);
|
|
539
|
+
const text = result?.recorded === true ? "Usage recorded." : "Usage request accepted.";
|
|
540
|
+
return {
|
|
541
|
+
content: [{ type: "text", text }],
|
|
542
|
+
structuredContent: { recorded: result?.recorded === true },
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
catch (err) {
|
|
546
|
+
return errorResult(err, flair.url);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
509
549
|
// ─── Start ───────────────────────────────────────────────────────────────────
|
|
510
550
|
const transport = new StdioServerTransport();
|
|
511
551
|
await server.connect(transport);
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
* "hooks": {
|
|
69
69
|
* "SessionStart": [
|
|
70
70
|
* { "hooks": [ { "type": "command",
|
|
71
|
-
* "command": "sh -c 'out=$(FLAIR_AGENT_ID=me npx -y @tpsdev-ai/flair-mcp flair-session-start 2>/dev/null) && printf %s \"$out\" || true'" } ] }
|
|
71
|
+
* "command": "sh -c 'out=$(FLAIR_AGENT_ID=me npx -y -p @tpsdev-ai/flair-mcp@<version> flair-session-start 2>/dev/null) && printf %s \"$out\" || true'" } ] }
|
|
72
72
|
* ]
|
|
73
73
|
* }
|
|
74
74
|
* }
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
* "hooks": {
|
|
69
69
|
* "SessionStart": [
|
|
70
70
|
* { "hooks": [ { "type": "command",
|
|
71
|
-
* "command": "sh -c 'out=$(FLAIR_AGENT_ID=me npx -y @tpsdev-ai/flair-mcp flair-session-start 2>/dev/null) && printf %s \"$out\" || true'" } ] }
|
|
71
|
+
* "command": "sh -c 'out=$(FLAIR_AGENT_ID=me npx -y -p @tpsdev-ai/flair-mcp@<version> flair-session-start 2>/dev/null) && printf %s \"$out\" || true'" } ] }
|
|
72
72
|
* ]
|
|
73
73
|
* }
|
|
74
74
|
* }
|
package/dist/usage.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* flair#1147 — the usage-feedback loop on the stdio MCP surface.
|
|
3
|
+
*
|
|
4
|
+
* Native `/mcp` already exposes `record_usage` and `memory_store.usedMemoryIds`.
|
|
5
|
+
* The stdio package (`@tpsdev-ai/flair-mcp`) did not, so a Claude Code / Cursor
|
|
6
|
+
* client had no way to reach POST /RecordUsage. These helpers are the thin
|
|
7
|
+
* client-side half: body construction for that endpoint, citation passthrough
|
|
8
|
+
* on write, and the one-line "cite what you use" nudge on recalled ids.
|
|
9
|
+
*
|
|
10
|
+
* Identity is NEVER in the body — RecordUsage attributes from the signed
|
|
11
|
+
* request, same no-forge contract as flair_workspace_set / flair_orgevent.
|
|
12
|
+
*/
|
|
13
|
+
/** One-line instruction on recalled ids (issue ask #3). Search/bootstrap hits are not usage. */
|
|
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
|
+
export declare function withCiteNudge(text: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Build the POST /RecordUsage body from the MCP tool args.
|
|
18
|
+
* Accepts singular `memoryId` and/or `memoryIds`. Returns null when there is
|
|
19
|
+
* nothing to send (the tool should fail locally rather than POST an empty list).
|
|
20
|
+
* Never includes agentId — the server attributes from the signature.
|
|
21
|
+
*
|
|
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.
|
|
40
|
+
*/
|
|
41
|
+
export declare function buildRecordUsageBody(args: {
|
|
42
|
+
memoryId?: string;
|
|
43
|
+
memoryIds?: string[];
|
|
44
|
+
attribution?: string;
|
|
45
|
+
}): {
|
|
46
|
+
memoryIds: string[];
|
|
47
|
+
attribution?: string;
|
|
48
|
+
} | null;
|
|
49
|
+
/**
|
|
50
|
+
* Citation-on-write passthrough. Only returns a list when the caller actually
|
|
51
|
+
* supplied a non-empty array of non-empty strings — omitted/empty is undefined
|
|
52
|
+
* so the write body stays byte-identical to a pre-#1147 write.
|
|
53
|
+
*/
|
|
54
|
+
export declare function citationIds(usedMemoryIds?: string[]): string[] | undefined;
|
package/dist/usage.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* flair#1147 — the usage-feedback loop on the stdio MCP surface.
|
|
3
|
+
*
|
|
4
|
+
* Native `/mcp` already exposes `record_usage` and `memory_store.usedMemoryIds`.
|
|
5
|
+
* The stdio package (`@tpsdev-ai/flair-mcp`) did not, so a Claude Code / Cursor
|
|
6
|
+
* client had no way to reach POST /RecordUsage. These helpers are the thin
|
|
7
|
+
* client-side half: body construction for that endpoint, citation passthrough
|
|
8
|
+
* on write, and the one-line "cite what you use" nudge on recalled ids.
|
|
9
|
+
*
|
|
10
|
+
* Identity is NEVER in the body — RecordUsage attributes from the signed
|
|
11
|
+
* request, same no-forge contract as flair_workspace_set / flair_orgevent.
|
|
12
|
+
*/
|
|
13
|
+
/** One-line instruction on recalled ids (issue ask #3). Search/bootstrap hits are not usage. */
|
|
14
|
+
export 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
|
+
export function withCiteNudge(text) {
|
|
16
|
+
if (!text)
|
|
17
|
+
return text;
|
|
18
|
+
return `${text}\n\n${CITE_USAGE_NUDGE}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build the POST /RecordUsage body from the MCP tool args.
|
|
22
|
+
* Accepts singular `memoryId` and/or `memoryIds`. Returns null when there is
|
|
23
|
+
* nothing to send (the tool should fail locally rather than POST an empty list).
|
|
24
|
+
* Never includes agentId — the server attributes from the signature.
|
|
25
|
+
*
|
|
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.
|
|
44
|
+
*/
|
|
45
|
+
export function buildRecordUsageBody(args) {
|
|
46
|
+
const ids = [];
|
|
47
|
+
if (Array.isArray(args.memoryIds)) {
|
|
48
|
+
for (const id of args.memoryIds) {
|
|
49
|
+
if (typeof id === "string" && id.length > 0)
|
|
50
|
+
ids.push(id);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (typeof args.memoryId === "string" && args.memoryId.length > 0) {
|
|
54
|
+
ids.push(args.memoryId);
|
|
55
|
+
}
|
|
56
|
+
const memoryIds = [...new Set(ids)];
|
|
57
|
+
if (memoryIds.length === 0)
|
|
58
|
+
return null;
|
|
59
|
+
const body = { memoryIds };
|
|
60
|
+
if (typeof args.attribution === "string" && args.attribution.length > 0) {
|
|
61
|
+
body.attribution = args.attribution;
|
|
62
|
+
}
|
|
63
|
+
return body;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Citation-on-write passthrough. Only returns a list when the caller actually
|
|
67
|
+
* supplied a non-empty array of non-empty strings — omitted/empty is undefined
|
|
68
|
+
* so the write body stays byte-identical to a pre-#1147 write.
|
|
69
|
+
*/
|
|
70
|
+
export function citationIds(usedMemoryIds) {
|
|
71
|
+
if (!Array.isArray(usedMemoryIds) || usedMemoryIds.length === 0)
|
|
72
|
+
return undefined;
|
|
73
|
+
if (!usedMemoryIds.every((id) => typeof id === "string" && id.length > 0))
|
|
74
|
+
return undefined;
|
|
75
|
+
return usedMemoryIds;
|
|
76
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* version.ts — the version advertised in MCP `initialize` `serverInfo`.
|
|
3
|
+
*
|
|
4
|
+
* `process.env.npm_package_version` is only populated inside `npm run`, so
|
|
5
|
+
* reading this package's own package.json relative to THIS running module is
|
|
6
|
+
* the only way to report the version of the code that's actually executing
|
|
7
|
+
* (same reason as resources/version.ts). Walk-by-name rather than a fixed
|
|
8
|
+
* number of `..` hops, matching src/lib/mcp-spec.ts: this file compiles to
|
|
9
|
+
* dist/version.js, tests import the .ts next to src/, and a hardcoded depth
|
|
10
|
+
* is one move away from silently advertising the wrong package — or "dev".
|
|
11
|
+
*/
|
|
12
|
+
/** The published package whose version `initialize` must report. */
|
|
13
|
+
export declare const FLAIR_MCP_PACKAGE = "@tpsdev-ai/flair-mcp";
|
|
14
|
+
/** What we advertise when package.json cannot be read. */
|
|
15
|
+
export declare const UNKNOWN_VERSION = "dev";
|
|
16
|
+
/**
|
|
17
|
+
* Walk up from `startDir` looking for `@tpsdev-ai/flair-mcp`'s own package.json.
|
|
18
|
+
* Exported for tests, which need to exercise the not-found path without
|
|
19
|
+
* corrupting a real install.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolvePackageVersionFrom(startDir: string): string;
|
|
22
|
+
/** This package's version — what `initialize` puts in `serverInfo.version`. */
|
|
23
|
+
export declare function resolvePackageVersion(): string;
|
|
24
|
+
/** The `serverInfo` object passed to `McpServer` (and thus returned on initialize). */
|
|
25
|
+
export declare function serverInfo(): {
|
|
26
|
+
name: "flair";
|
|
27
|
+
version: string;
|
|
28
|
+
};
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* version.ts — the version advertised in MCP `initialize` `serverInfo`.
|
|
3
|
+
*
|
|
4
|
+
* `process.env.npm_package_version` is only populated inside `npm run`, so
|
|
5
|
+
* reading this package's own package.json relative to THIS running module is
|
|
6
|
+
* the only way to report the version of the code that's actually executing
|
|
7
|
+
* (same reason as resources/version.ts). Walk-by-name rather than a fixed
|
|
8
|
+
* number of `..` hops, matching src/lib/mcp-spec.ts: this file compiles to
|
|
9
|
+
* dist/version.js, tests import the .ts next to src/, and a hardcoded depth
|
|
10
|
+
* is one move away from silently advertising the wrong package — or "dev".
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
/** The published package whose version `initialize` must report. */
|
|
16
|
+
export const FLAIR_MCP_PACKAGE = "@tpsdev-ai/flair-mcp";
|
|
17
|
+
/** What we advertise when package.json cannot be read. */
|
|
18
|
+
export const UNKNOWN_VERSION = "dev";
|
|
19
|
+
/**
|
|
20
|
+
* Walk up from `startDir` looking for `@tpsdev-ai/flair-mcp`'s own package.json.
|
|
21
|
+
* Exported for tests, which need to exercise the not-found path without
|
|
22
|
+
* corrupting a real install.
|
|
23
|
+
*/
|
|
24
|
+
export function resolvePackageVersionFrom(startDir) {
|
|
25
|
+
let dir = startDir;
|
|
26
|
+
// 8 levels is far more than any real layout needs (dist/ → package root is 1)
|
|
27
|
+
// while still terminating on a pathological symlink loop.
|
|
28
|
+
for (let i = 0; i < 8; i++) {
|
|
29
|
+
try {
|
|
30
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
|
|
31
|
+
if (pkg?.name === FLAIR_MCP_PACKAGE && typeof pkg.version === "string" && pkg.version) {
|
|
32
|
+
return pkg.version;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// No package.json here, or unreadable/malformed — keep walking.
|
|
37
|
+
}
|
|
38
|
+
const parent = dirname(dir);
|
|
39
|
+
if (parent === dir)
|
|
40
|
+
break;
|
|
41
|
+
dir = parent;
|
|
42
|
+
}
|
|
43
|
+
return process.env.npm_package_version ?? UNKNOWN_VERSION;
|
|
44
|
+
}
|
|
45
|
+
/** This package's version — what `initialize` puts in `serverInfo.version`. */
|
|
46
|
+
export function resolvePackageVersion() {
|
|
47
|
+
return resolvePackageVersionFrom(dirname(fileURLToPath(import.meta.url)));
|
|
48
|
+
}
|
|
49
|
+
/** The `serverInfo` object passed to `McpServer` (and thus returned on initialize). */
|
|
50
|
+
export function serverInfo() {
|
|
51
|
+
return { name: "flair", version: resolvePackageVersion() };
|
|
52
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tpsdev-ai/flair-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
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.
|
|
31
|
+
"@tpsdev-ai/flair-client": "0.50.0",
|
|
32
32
|
"zod": "4.3.6"
|
|
33
33
|
},
|
|
34
34
|
"license": "Apache-2.0",
|