@bli-cockpit/mcp 0.1.29 → 0.1.30
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 +9 -7
- package/dist/notes-write-tools.js +25 -0
- package/dist/readme-census.js +8 -3
- package/dist/server.js +2 -0
- package/dist/usage-tools.d.ts +4 -0
- package/dist/usage-tools.js +21 -0
- package/dist/verb-census.d.ts +24 -0
- package/dist/verb-census.js +35 -0
- package/package.json +3 -3
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
|
-
**
|
|
37
|
+
**83 of 86 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
|
|
@@ -97,6 +97,7 @@ three tables below.
|
|
|
97
97
|
| `cockpit notes list` | `notes_list` | `GET /api/notes/library` |
|
|
98
98
|
| `cockpit notes move` | `notes_move` | `POST /api/notes/move` |
|
|
99
99
|
| `cockpit notes paste` | `notes_paste` | `POST /api/notes/upload (text)` |
|
|
100
|
+
| `cockpit notes place` | `notes_place` | `POST /api/notes/place` |
|
|
100
101
|
| `cockpit notes share` | `notes_share` | `POST /api/notes/share` |
|
|
101
102
|
| `cockpit notes shelf` | `notes_shelf` | `GET /api/notes/shelf` |
|
|
102
103
|
| `cockpit notes shelves` | `notes_shelves` | `GET /api/notes/library` |
|
|
@@ -111,17 +112,18 @@ three tables below.
|
|
|
111
112
|
| `cockpit scout start` | `scout_start` | `POST /api/cockpit/scout (start)` |
|
|
112
113
|
| `cockpit scout undo` | `scout_undo` | `POST /api/cockpit/scout (undo_dismiss)` |
|
|
113
114
|
| `cockpit search` | `tower_search` | `GET /api/search` |
|
|
114
|
-
| `cockpit settings delete` | `settings_delete` | `DELETE /api/settings/env-blobs` |
|
|
115
|
-
| `cockpit settings list` | `settings_list` | `GET /api/settings/env-blobs` |
|
|
116
|
-
| `cockpit settings set` | `settings_set` | `POST /api/settings/{jarvis-model,switches,model-routing,env-blobs} + PUT /api/settings/cli-floor` |
|
|
117
|
-
| `cockpit settings
|
|
115
|
+
| `cockpit settings env delete` | `settings_delete` | `DELETE /api/settings/env-blobs` |
|
|
116
|
+
| `cockpit settings env list` | `settings_list` | `GET /api/settings/env-blobs` |
|
|
117
|
+
| `cockpit settings switches set` | `settings_set` | `POST /api/settings/{jarvis-model,switches,model-routing,env-blobs} + PUT /api/settings/cli-floor` |
|
|
118
|
+
| `cockpit settings` | `settings_show` | `GET /api/settings/* + /api/team/members` |
|
|
118
119
|
| `cockpit slack coverage` | `slack_coverage` | `GET /api/ops/slack/coverage` |
|
|
119
120
|
| `cockpit slack read` | `slack_read` | `POST /api/ops/slack/read` |
|
|
120
|
-
| `cockpit team device
|
|
121
|
-
| `cockpit team device
|
|
121
|
+
| `cockpit team device list` | `team_device_list` | `GET /api/team/devices` |
|
|
122
|
+
| `cockpit team device revoke` | `team_device_revoke` | `POST /api/ambient/devices/[deviceId]/revoke` |
|
|
122
123
|
| `cockpit team invite` | `team_invite` | `POST /api/team/invite` |
|
|
123
124
|
| `cockpit team members` | `team_members` | `GET /api/team/members` |
|
|
124
125
|
| `cockpit team role` | `team_role` | `PATCH /api/team/members/[userId]/role` |
|
|
126
|
+
| `cockpit usage people` | `usage_people` | `GET /api/usage/people` |
|
|
125
127
|
| `cockpit workbook` | `workbook_read` | `GET /api/cockpit/workbook` |
|
|
126
128
|
|
|
127
129
|
### Owed a twin
|
|
@@ -199,6 +199,31 @@ export function registerNotesWriteTools(server, deps) {
|
|
|
199
199
|
? textResult(text, { ok: true, shelf: body.shelf ?? null })
|
|
200
200
|
: errorResult(text);
|
|
201
201
|
}));
|
|
202
|
+
register("notes_place", {
|
|
203
|
+
title: "Say which shelf a Tower meeting note belongs on",
|
|
204
|
+
description: "Answers where one note files itself and WHY, from the meeting series it is part of, who was in the room "
|
|
205
|
+
+ "and its own name — no model call. Read-only unless `apply: true`, which files it there. It never "
|
|
206
|
+
+ "overrules a shelf a person typed: a note that already has one comes back as `already_shelved`.",
|
|
207
|
+
inputSchema: {
|
|
208
|
+
note_id: z.string().min(1).max(200).describe("A note id."),
|
|
209
|
+
apply: z.boolean().optional().describe("Actually put it there. Default: only say where."),
|
|
210
|
+
},
|
|
211
|
+
}, async (args) => withSession(deps, async (session) => {
|
|
212
|
+
const response = await callAgentDoor(session, deps.fetchImpl, "POST", "/api/notes/place", { note_id: String(args.note_id ?? ""), apply: args.apply === true }, WRITE_DEADLINE_MS);
|
|
213
|
+
if (!response.ok)
|
|
214
|
+
return errorResult(doorFailureText("notes_place", response));
|
|
215
|
+
const body = response.body;
|
|
216
|
+
const text = [body.headline ?? "Tower answered without a sentence.", ...(body.lines ?? [])].join("\n");
|
|
217
|
+
return body.ok === true
|
|
218
|
+
? textResult(text, {
|
|
219
|
+
ok: true,
|
|
220
|
+
shelf: body.shelf ?? null,
|
|
221
|
+
suggested_shelf: body.suggestedShelf ?? null,
|
|
222
|
+
decided_by: body.decidedBy ?? null,
|
|
223
|
+
applied: body.applied === true,
|
|
224
|
+
})
|
|
225
|
+
: errorResult(text);
|
|
226
|
+
}));
|
|
202
227
|
}
|
|
203
228
|
/** Share and unshare are one door and one body; only the boolean differs. */
|
|
204
229
|
async function shareCall(deps, session, args, share) {
|
package/dist/readme-census.js
CHANGED
|
@@ -11,12 +11,17 @@
|
|
|
11
11
|
* and returns a string. No file writing here — that is the script's job, so
|
|
12
12
|
* this can be asserted without touching a disk.
|
|
13
13
|
*/
|
|
14
|
-
import { AWAITING_TWIN, MCP_TWINS, TERMINAL_ONLY, towerVerbs } from "./verb-census.js";
|
|
14
|
+
import { AWAITING_TWIN, MCP_TWINS, TERMINAL_ONLY, towerVerbs, typedSpelling } from "./verb-census.js";
|
|
15
15
|
export const CENSUS_BEGIN = "<!-- BEGIN GENERATED verb census — `npm run mcp:readme` -->";
|
|
16
16
|
export const CENSUS_END = "<!-- END GENERATED verb census -->";
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* `cockpit docs read` → `` `cockpit docs read` ``; `jarvis --trace` keeps its
|
|
19
|
+
* flag. A census key whose grammar differs from the typed command is rendered
|
|
20
|
+
* as the command (`settings show` → `cockpit settings`), because this column is
|
|
21
|
+
* headed "CLI verb" and a reader takes it literally (BLI-4048).
|
|
22
|
+
*/
|
|
18
23
|
function spell(verb) {
|
|
19
|
-
return `\`cockpit ${verb}\``;
|
|
24
|
+
return `\`cockpit ${typedSpelling(verb)}\``;
|
|
20
25
|
}
|
|
21
26
|
export function renderCensusMarkdown() {
|
|
22
27
|
// Every verb the collector's own tables name, plus the one hand-entered mode
|
package/dist/server.js
CHANGED
|
@@ -23,6 +23,7 @@ import { registerSettingsTools } from "./settings-tools.js";
|
|
|
23
23
|
import { registerSettingsWriteTools } from "./settings-write-tools.js";
|
|
24
24
|
import { registerTeamWriteTools } from "./team-write-tools.js";
|
|
25
25
|
import { registerWorkTools } from "./work-tools.js";
|
|
26
|
+
import { registerUsageTools } from "./usage-tools.js";
|
|
26
27
|
export const PACKAGE_NAME = "@bli-cockpit/mcp";
|
|
27
28
|
export const PACKAGE_VERSION = "0.1.4";
|
|
28
29
|
// ---- input schemas (Zod raw shapes) -----------------------------------------
|
|
@@ -390,6 +391,7 @@ export function createServer(deps) {
|
|
|
390
391
|
// the docs/msg tools above, for the same reason: a coding session should
|
|
391
392
|
// file and move a Tower issue the way it files and moves a Linear one.
|
|
392
393
|
registerWorkTools(server, { fetchImpl: deps.fetchImpl });
|
|
394
|
+
registerUsageTools(server, { fetchImpl: deps.fetchImpl });
|
|
393
395
|
// BLI-3708: the mailboxes a person attached. Reads, a send and a sync over
|
|
394
396
|
// the same /api/mail/** doors `cockpit mail` calls; `add-imap` deliberately
|
|
395
397
|
// has no twin (see mail-tools.ts's header — a credential is not a tool
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { callAgentDoor } from "./agent-door.js";
|
|
3
|
+
import { doorFailureText, errorResult, registrarFor, textResult, withSession } from "./tool-result.js";
|
|
4
|
+
export function registerUsageTools(server, deps) {
|
|
5
|
+
registrarFor(server)("usage_people", {
|
|
6
|
+
title: "Read usage by person",
|
|
7
|
+
description: "Per-person Claude Code and Codex token usage, list-price equivalent, and extraction coverage.",
|
|
8
|
+
inputSchema: { since: z.string().optional().describe("30d or an ISO timestamp."), until: z.string().optional(), include_automated: z.boolean().optional() },
|
|
9
|
+
}, async (args) => withSession(deps, async (session) => {
|
|
10
|
+
const query = new URLSearchParams({ since: String(args.since ?? "30d") });
|
|
11
|
+
if (args.until)
|
|
12
|
+
query.set("until", String(args.until));
|
|
13
|
+
if (args.include_automated)
|
|
14
|
+
query.set("include_automated", "1");
|
|
15
|
+
const response = await callAgentDoor(session, deps.fetchImpl, "GET", `/api/usage/people?${query}`);
|
|
16
|
+
if (!response.ok)
|
|
17
|
+
return errorResult(doorFailureText("usage_people", response));
|
|
18
|
+
const coverage = response.body.coverage;
|
|
19
|
+
return textResult(`${coverage?.sessions_extracted ?? 0} of ${coverage?.sessions_observed ?? 0} sessions extracted.`, response.body);
|
|
20
|
+
}));
|
|
21
|
+
}
|
package/dist/verb-census.d.ts
CHANGED
|
@@ -67,6 +67,30 @@ export interface McpTwin {
|
|
|
67
67
|
}
|
|
68
68
|
/** A CLI verb and the MCP tool that does the same thing, over the same door. */
|
|
69
69
|
export declare const MCP_TWINS: Record<string, McpTwin>;
|
|
70
|
+
/**
|
|
71
|
+
* Census key → what a person actually types. (BLI-4048)
|
|
72
|
+
*
|
|
73
|
+
* The keys above are derived MECHANICALLY from each noun's `action` union, and
|
|
74
|
+
* for two nouns that union is not the grammar. `settings`'s positional is a
|
|
75
|
+
* SECTION (`personal|switches|models|env|cli-floor`) and the `action` is what
|
|
76
|
+
* the parser decides afterwards; `team`'s `device-list` is one action word
|
|
77
|
+
* spelled as two typed words, `team device list`. So `cockpit settings show`
|
|
78
|
+
* and `cockpit team device-list` were rendered into the README's "CLI verb"
|
|
79
|
+
* column and into `cli-caller-identity.md`, and the CLI refused all six.
|
|
80
|
+
*
|
|
81
|
+
* This is the display layer and nothing else: the keys, the twin rule and the
|
|
82
|
+
* suite are untouched. Every spelling here is proven typable —
|
|
83
|
+
* `scripts/doc-doors.test.mjs` runs the rendered table through the collector's
|
|
84
|
+
* own parser, so a wrong entry fails the build rather than misleading a
|
|
85
|
+
* stranger. Where one tool serves several sections (`settings_set` writes any
|
|
86
|
+
* of them) the spelling names ONE of them and the door column names the rest.
|
|
87
|
+
*
|
|
88
|
+
* A verb whose key is already typable is absent from here on purpose; the
|
|
89
|
+
* shorter this map is, the closer the census keys are to the product.
|
|
90
|
+
*/
|
|
91
|
+
export declare const TYPED_SPELLING: Record<string, string>;
|
|
92
|
+
/** How a census key is written when a person is being told to type it. */
|
|
93
|
+
export declare function typedSpelling(verb: string): string;
|
|
70
94
|
/** Verbs that can never have an MCP twin, and why. A claim, not a backlog. */
|
|
71
95
|
export declare const TERMINAL_ONLY: Record<string, string>;
|
|
72
96
|
/**
|
package/dist/verb-census.js
CHANGED
|
@@ -209,6 +209,7 @@ export function memoryMcpToolNames() {
|
|
|
209
209
|
}
|
|
210
210
|
/** A CLI verb and the MCP tool that does the same thing, over the same door. */
|
|
211
211
|
export const MCP_TWINS = {
|
|
212
|
+
"usage people": { tool: "usage_people", door: "GET /api/usage/people" },
|
|
212
213
|
"docs list": { tool: "docs_list", door: "GET /api/docs/documents" },
|
|
213
214
|
"docs tree": { tool: "docs_tree", door: "GET /api/docs/tree" },
|
|
214
215
|
"docs read": { tool: "docs_read", door: "GET /api/docs/documents/[id]" },
|
|
@@ -259,6 +260,7 @@ export const MCP_TWINS = {
|
|
|
259
260
|
"notes share": { tool: "notes_share", door: "POST /api/notes/share" },
|
|
260
261
|
"notes unshare": { tool: "notes_unshare", door: "POST /api/notes/share (share=false)" },
|
|
261
262
|
"notes move": { tool: "notes_move", door: "POST /api/notes/move" },
|
|
263
|
+
"notes place": { tool: "notes_place", door: "POST /api/notes/place" },
|
|
262
264
|
"memory log": { tool: "memory_experience", door: "POST /api/memory/experience" },
|
|
263
265
|
// BLI-4047: the memory STORE at a terminal. The twins live on the second
|
|
264
266
|
// server, `bli-memory`, over the same four doors these CLI verbs call.
|
|
@@ -302,6 +304,39 @@ export const MCP_TWINS = {
|
|
|
302
304
|
// header of this file).
|
|
303
305
|
"jarvis --trace": { tool: "jarvis_trace", door: "GET /api/ops/trace/[id]" },
|
|
304
306
|
};
|
|
307
|
+
/**
|
|
308
|
+
* Census key → what a person actually types. (BLI-4048)
|
|
309
|
+
*
|
|
310
|
+
* The keys above are derived MECHANICALLY from each noun's `action` union, and
|
|
311
|
+
* for two nouns that union is not the grammar. `settings`'s positional is a
|
|
312
|
+
* SECTION (`personal|switches|models|env|cli-floor`) and the `action` is what
|
|
313
|
+
* the parser decides afterwards; `team`'s `device-list` is one action word
|
|
314
|
+
* spelled as two typed words, `team device list`. So `cockpit settings show`
|
|
315
|
+
* and `cockpit team device-list` were rendered into the README's "CLI verb"
|
|
316
|
+
* column and into `cli-caller-identity.md`, and the CLI refused all six.
|
|
317
|
+
*
|
|
318
|
+
* This is the display layer and nothing else: the keys, the twin rule and the
|
|
319
|
+
* suite are untouched. Every spelling here is proven typable —
|
|
320
|
+
* `scripts/doc-doors.test.mjs` runs the rendered table through the collector's
|
|
321
|
+
* own parser, so a wrong entry fails the build rather than misleading a
|
|
322
|
+
* stranger. Where one tool serves several sections (`settings_set` writes any
|
|
323
|
+
* of them) the spelling names ONE of them and the door column names the rest.
|
|
324
|
+
*
|
|
325
|
+
* A verb whose key is already typable is absent from here on purpose; the
|
|
326
|
+
* shorter this map is, the closer the census keys are to the product.
|
|
327
|
+
*/
|
|
328
|
+
export const TYPED_SPELLING = {
|
|
329
|
+
"settings show": "settings",
|
|
330
|
+
"settings list": "settings env list",
|
|
331
|
+
"settings set": "settings switches set",
|
|
332
|
+
"settings delete": "settings env delete",
|
|
333
|
+
"team device-list": "team device list",
|
|
334
|
+
"team device-revoke": "team device revoke",
|
|
335
|
+
};
|
|
336
|
+
/** How a census key is written when a person is being told to type it. */
|
|
337
|
+
export function typedSpelling(verb) {
|
|
338
|
+
return TYPED_SPELLING[verb] ?? verb;
|
|
339
|
+
}
|
|
305
340
|
/** Verbs that can never have an MCP twin, and why. A claim, not a backlog. */
|
|
306
341
|
export const TERMINAL_ONLY = {
|
|
307
342
|
"mail add-imap": "carries a Google app password. The CLI reads it from STDIN precisely so it never lands in an argument list; an MCP tool argument travels through a model's context window and whatever transcript store sits behind it, so attaching a mailbox stays a thing a person does at a terminal (BLI-3708)",
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "bli-tower
|
|
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",
|
|
7
7
|
"bin": {
|
|
8
8
|
"bli-cockpit-mcp": "./dist/index.js"
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"readme": "npm run build && node scripts/write-readme-census.mjs"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@bli-cockpit/telemetry-core": "0.1.
|
|
33
|
+
"@bli-cockpit/telemetry-core": "0.1.43",
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
35
|
"zod": "^4.3.6"
|
|
36
36
|
},
|