@bli-cockpit/mcp 0.1.54 → 0.1.57

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 CHANGED
@@ -34,7 +34,7 @@ about what an agent can reach.
34
34
 
35
35
  <!-- BEGIN GENERATED verb census — `npm run mcp:readme` -->
36
36
 
37
- **104 of 109 Tower verbs have an MCP twin.**
37
+ **106 of 111 Tower verbs have an MCP twin.**
38
38
  Each tool goes through the SAME door its CLI verb calls, with the same
39
39
  collector device token — never a second route and never a service-role
40
40
  reader. `src/verb-census.test.ts` fails when a verb is in none of the
@@ -91,8 +91,10 @@ three tables below.
91
91
  | `cockpit mail search` | `mail_search` | `GET /api/mail/search` |
92
92
  | `cockpit mail send` | `mail_send` | `POST /api/mail/send` |
93
93
  | `cockpit mail sync` | `mail_sync` | `POST /api/mail/accounts/[id]/sync` |
94
+ | `cockpit memory consolidation` | `memory_consolidation` | `GET /api/memory/consolidation` |
94
95
  | `cockpit memory forget` | `forget_memory` (`bli-memory`) | `POST /api/memory/forget` |
95
96
  | `cockpit memory log` | `memory_experience` | `POST /api/memory/experience` |
97
+ | `cockpit memory profile` | `memory_profile` | `GET /api/memory/profile` |
96
98
  | `cockpit memory save` | `save_memory` (`bli-memory`) | `POST /api/memory/save` |
97
99
  | `cockpit memory search` | `search_memory` (`bli-memory`) | `POST /api/memory/search` |
98
100
  | `cockpit memory update` | `update_memory` (`bli-memory`) | `POST /api/memory/update` |
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `memory_consolidation` — the MCP twin of `cockpit memory consolidation`
3
+ * (BLI-4534).
4
+ *
5
+ * Same door, same device token, same answer: `GET /api/memory/consolidation`.
6
+ * It reads what the nightly BLI Memory tidy-up pass did — clusters looked at,
7
+ * edits by verb, refusals by reason, cost — and it cannot start, stop or
8
+ * change a pass. The pass itself is a Railway cron.
9
+ *
10
+ * It lives on `bli-tower` rather than on the `bli-memory` server that carries
11
+ * `search_memory`/`save_memory`/`update_memory`/`forget_memory`, because this
12
+ * is a read of a JOB's counts rather than a read or a write of the store.
13
+ *
14
+ * No memory text crosses this tool. The door does not send any.
15
+ */
16
+ import { type ToolDeps } from "./tool-result.js";
17
+ export declare function registerMemoryConsolidationTools(server: {
18
+ registerTool: (...args: never[]) => unknown;
19
+ }, deps: ToolDeps): void;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `memory_consolidation` — the MCP twin of `cockpit memory consolidation`
3
+ * (BLI-4534).
4
+ *
5
+ * Same door, same device token, same answer: `GET /api/memory/consolidation`.
6
+ * It reads what the nightly BLI Memory tidy-up pass did — clusters looked at,
7
+ * edits by verb, refusals by reason, cost — and it cannot start, stop or
8
+ * change a pass. The pass itself is a Railway cron.
9
+ *
10
+ * It lives on `bli-tower` rather than on the `bli-memory` server that carries
11
+ * `search_memory`/`save_memory`/`update_memory`/`forget_memory`, because this
12
+ * is a read of a JOB's counts rather than a read or a write of the store.
13
+ *
14
+ * No memory text crosses this tool. The door does not send any.
15
+ */
16
+ import { z } from "zod";
17
+ import { callAgentDoor } from "./agent-door.js";
18
+ import { doorFailureText, errorResult, queryString, registrarFor, textResult, withSession, } from "./tool-result.js";
19
+ export function registerMemoryConsolidationTools(server, deps) {
20
+ registrarFor(server)("memory_consolidation", {
21
+ title: "Read the nightly memory consolidation runs",
22
+ description: "What the nightly BLI Memory consolidation pass did: clusters, merges/rewrites/dated corrections by verb, refusals by reason, model and cost. With a container tag it answers that space's recent runs; without one, the newest run per container. Read-only — the pass never deletes a memory and this tool never starts one. An empty list means no run is visible to this account (the receipts are super_admin-readable), not an outage.",
23
+ inputSchema: {
24
+ container: z
25
+ .string()
26
+ .min(1)
27
+ .max(128)
28
+ .optional()
29
+ .describe("One container tag. Omit for the newest run per container."),
30
+ limit: z
31
+ .number()
32
+ .int()
33
+ .min(1)
34
+ .max(200)
35
+ .optional()
36
+ .describe("How many run rows to read. Default 50."),
37
+ },
38
+ }, async (args) => withSession(deps, async (session) => {
39
+ const params = new URLSearchParams();
40
+ if (args.container)
41
+ params.set("container", String(args.container));
42
+ if (args.limit !== undefined)
43
+ params.set("limit", String(args.limit));
44
+ const response = await callAgentDoor(session, deps.fetchImpl, "GET", `/api/memory/consolidation${queryString(params)}`);
45
+ if (!response.ok)
46
+ return errorResult(doorFailureText("memory_consolidation", response));
47
+ const runs = Array.isArray(response.body.runs) ? response.body.runs : [];
48
+ const reason = typeof response.body.reason === "string" ? response.body.reason : "runs_read";
49
+ return textResult(runs.length === 0
50
+ ? `No consolidation runs are visible (${reason}). The pass is a nightly cron and its receipts are readable by a super_admin; this is the access rule answering, not an outage.`
51
+ : `${runs.length} consolidation run${runs.length === 1 ? "" : "s"}. report = proposals only, nothing on the shelf changed; apply = accepted edits written as supersessions, which never delete anything.`, response.body);
52
+ }));
53
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `memory_profile` (BLI-4533) — the compiled one-page briefing for a memory
3
+ * space, over the same `GET /api/memory/profile` door `cockpit memory profile`
4
+ * calls.
5
+ *
6
+ * Every session already opens with this page injected by the session-start
7
+ * hook. This tool exists for the turn AFTER that: an agent that wants to know
8
+ * what a DIFFERENT project's memory says about itself, or one that wants to
9
+ * re-read its own briefing without trusting a context window that has since
10
+ * been compacted.
11
+ *
12
+ * It lives on `bli-tower` rather than on `bli-memory` for one reason: the four
13
+ * store tools on that server are the vendor-compatible set the Claude Code
14
+ * plugin expects, and this is ours. The DOOR is the same either way.
15
+ *
16
+ * Read-only, and there is no compile argument on purpose. The page is written
17
+ * once a day by the `memory-profile` Railway service; a tool that could trigger
18
+ * a compile would put a paid model call behind a sentence an agent types.
19
+ */
20
+ import { type ToolDeps } from "./tool-result.js";
21
+ export declare function registerMemoryProfileTools(server: {
22
+ registerTool: (...args: never[]) => unknown;
23
+ }, deps: ToolDeps): void;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `memory_profile` (BLI-4533) — the compiled one-page briefing for a memory
3
+ * space, over the same `GET /api/memory/profile` door `cockpit memory profile`
4
+ * calls.
5
+ *
6
+ * Every session already opens with this page injected by the session-start
7
+ * hook. This tool exists for the turn AFTER that: an agent that wants to know
8
+ * what a DIFFERENT project's memory says about itself, or one that wants to
9
+ * re-read its own briefing without trusting a context window that has since
10
+ * been compacted.
11
+ *
12
+ * It lives on `bli-tower` rather than on `bli-memory` for one reason: the four
13
+ * store tools on that server are the vendor-compatible set the Claude Code
14
+ * plugin expects, and this is ours. The DOOR is the same either way.
15
+ *
16
+ * Read-only, and there is no compile argument on purpose. The page is written
17
+ * once a day by the `memory-profile` Railway service; a tool that could trigger
18
+ * a compile would put a paid model call behind a sentence an agent types.
19
+ */
20
+ import { z } from "zod";
21
+ import { callAgentDoor } from "./agent-door.js";
22
+ import { doorFailureText, errorResult, queryString, registrarFor, textResult, withSession, } from "./tool-result.js";
23
+ export function registerMemoryProfileTools(server, deps) {
24
+ registrarFor(server)("memory_profile", {
25
+ title: "Read a memory space's compiled profile",
26
+ description: "The one-page briefing BLI Memory compiles daily for a container: who works here, what is going on now, "
27
+ + "standing rules and preferences, how to communicate. The same page a session start injects. Read-only — "
28
+ + "the compile runs on a cron, never on demand. A container with no page yet answers `not_compiled_yet`, "
29
+ + "which means nothing has been compiled, NOT that the store is down.",
30
+ inputSchema: {
31
+ container: z
32
+ .string()
33
+ .min(1)
34
+ .max(128)
35
+ .describe("The container tag, e.g. repo_bli_cockpit__12e241cdbc00adfd."),
36
+ },
37
+ }, async (args) => withSession(deps, async (session) => {
38
+ const container = String(args.container);
39
+ const response = await callAgentDoor(session, deps.fetchImpl, "GET", `/api/memory/profile${queryString(new URLSearchParams({ container }))}`);
40
+ if (!response.ok)
41
+ return errorResult(doorFailureText("memory_profile", response));
42
+ const body = response.body;
43
+ const profile = body.profile ?? null;
44
+ const lines = profile?.lines ?? [];
45
+ if (lines.length === 0) {
46
+ // Named, not blank: the difference between "not compiled yet" and an
47
+ // empty page is the whole reason the door returns a reason at all.
48
+ return textResult(`No profile is compiled for ${container} yet (${String(body.reason ?? "unknown")}). `
49
+ + "The daily compile writes one once this space has memories.", { ok: true, containerTag: container, reason: body.reason ?? "unknown", lines: 0 });
50
+ }
51
+ return textResult([
52
+ ...lines,
53
+ "",
54
+ `Compiled ${String(profile?.compiledAt ?? "—")} from ${String(profile?.sourceRows ?? 0)} memories by ${String(profile?.model ?? "—")}.`,
55
+ ].join("\n"), {
56
+ ok: true,
57
+ containerTag: container,
58
+ reason: body.reason ?? "profile_read",
59
+ lines: lines.length,
60
+ compiledAt: profile?.compiledAt ?? null,
61
+ sourceRows: profile?.sourceRows ?? 0,
62
+ model: profile?.model ?? null,
63
+ });
64
+ }));
65
+ }
package/dist/server.js CHANGED
@@ -15,7 +15,9 @@ import { registerMailTools } from "./mail-tools.js";
15
15
  import { registerNotesAccountTools } from "./notes-account-tools.js";
16
16
  import { registerNotesTools } from "./notes-tools.js";
17
17
  import { registerNotesWriteTools } from "./notes-write-tools.js";
18
+ import { registerMemoryConsolidationTools } from "./memory-consolidation-tools.js";
18
19
  import { registerMemoryExperienceTools } from "./memory-experience-tools.js";
20
+ import { registerMemoryProfileTools } from "./memory-profile-tools.js";
19
21
  import { registerModelsTools } from "./models-tools.js";
20
22
  import { registerOpsTools } from "./ops-tools.js";
21
23
  import { registerPagesTools } from "./pages-tools.js";
@@ -423,6 +425,11 @@ export function createServer(deps) {
423
425
  registerNotesTools(server, { fetchImpl: deps.fetchImpl });
424
426
  registerOpsTools(server, { fetchImpl: deps.fetchImpl });
425
427
  registerMemoryExperienceTools(server, { fetchImpl: deps.fetchImpl });
428
+ // BLI-4533: the compiled one-page profile, the twin of `cockpit memory profile`.
429
+ registerMemoryProfileTools(server, { fetchImpl: deps.fetchImpl });
430
+ // BLI-4534: the nightly consolidation receipts, twin of `cockpit memory
431
+ // consolidation`. Read-only, and the pass it reports on never deletes.
432
+ registerMemoryConsolidationTools(server, { fetchImpl: deps.fetchImpl });
426
433
  registerSettingsTools(server, { fetchImpl: deps.fetchImpl });
427
434
  registerPagesTools(server, { fetchImpl: deps.fetchImpl });
428
435
  // BLI-3756 batch 2: the WRITES. Same doors, same device token, same refusal
@@ -65,6 +65,7 @@ export function registerSettingsWriteTools(server, deps) {
65
65
  chat_model: z.string().min(3).max(200).optional().describe("personal/models: provider:model."),
66
66
  brief_model: z.string().min(3).max(200).optional().describe("personal: provider:model."),
67
67
  memory_model: z.string().min(1).max(200).optional().describe("models: the memory compiler's model id."),
68
+ note_reader_model: z.string().min(3).max(200).optional().describe("models: the meeting-note reader, provider:model from its allowlist."),
68
69
  key: z.string().min(1).max(200).optional().describe("switches: which switch."),
69
70
  value: z.string().min(1).max(200).optional().describe("switches: its new value."),
70
71
  version: z.string().min(1).max(50).optional().describe("cli-floor: the version every collector is pulled to."),
@@ -113,8 +114,10 @@ export function registerSettingsWriteTools(server, deps) {
113
114
  }
114
115
  if (args.memory_model)
115
116
  body.memoryModel = String(args.memory_model);
117
+ if (args.note_reader_model)
118
+ body.noteReaderModel = String(args.note_reader_model);
116
119
  if (Object.keys(body).length === 0) {
117
- return errorResult("Refused (nothing_to_set): name chat_model or memory_model. Nothing was changed.");
120
+ return errorResult("Refused (nothing_to_set): name chat_model, memory_model or note_reader_model. Nothing was changed.");
118
121
  }
119
122
  return write(deps, session, "settings_set", "POST", "/api/settings/model-routing", body, (answer) => {
120
123
  const saved = Array.isArray(answer.saved) ? answer.saved.join(", ") : "";
@@ -300,6 +300,14 @@ export const MCP_TWINS = {
300
300
  "memory save": { tool: "save_memory", door: "POST /api/memory/save", server: "bli-memory" },
301
301
  "memory update": { tool: "update_memory", door: "POST /api/memory/update", server: "bli-memory" },
302
302
  "memory forget": { tool: "forget_memory", door: "POST /api/memory/forget", server: "bli-memory" },
303
+ // BLI-4533: the compiled one-page profile. Its twin is on `bli-tower`, not
304
+ // `bli-memory`: that server's four tools are the vendor-compatible set the
305
+ // Claude Code plugin expects, and this page is ours.
306
+ "memory profile": { tool: "memory_profile", door: "GET /api/memory/profile" },
307
+ // BLI-4534: the nightly consolidation receipts. This twin lives on
308
+ // `bli-tower` rather than `bli-memory`, because it is a read of a JOB s
309
+ // counts and not a read or write of the store itself.
310
+ "memory consolidation": { tool: "memory_consolidation", door: "GET /api/memory/consolidation" },
303
311
  "ops status": { tool: "ops_status", door: "GET /api/ops/status" },
304
312
  "ops recompile": { tool: "ops_recompile", door: "POST /api/ops/recompile" },
305
313
  "slack coverage": { tool: "slack_coverage", door: "GET /api/ops/slack/coverage" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/mcp",
3
- "version": "0.1.54",
3
+ "version": "0.1.57",
4
4
  "private": false,
5
5
  "description": "bli-tower: an MCP server over BLI Cockpit's agent doors: JARVIS (jarvis_*), documents (docs_*), channels (msg_*), issues (work_*), the daily page (brief_*), meeting notes (notes_*), the ops board (ops_status/slack_*), settings/team/model, Scout and the workbook, plus the legacy event-stream tools (emit_event, get_ticket_timeline, get_active_tickets).",
6
6
  "type": "module",