@bli-cockpit/mcp 0.1.23 → 0.1.25

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
- **76 of 79 Tower verbs have an MCP twin.**
37
+ **77 of 80 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
@@ -81,6 +81,7 @@ three tables below.
81
81
  | `cockpit memory log` | `memory_experience` | `POST /api/memory/experience` |
82
82
  | `cockpit model set` | `model_set` | `POST /api/settings/jarvis-model` |
83
83
  | `cockpit model show` | `model_show` | `GET /api/settings/jarvis-model` |
84
+ | `cockpit models compare` | `models_compare` | `GET /api/models/cards?compare=` |
84
85
  | `cockpit models list` | `models_list` | `GET /api/models/cards` |
85
86
  | `cockpit models show` | `models_card` | `GET /api/models/cards?id=` |
86
87
  | `cockpit msg channels` | `msg_channels` | `GET /api/msg/channels` |
@@ -86,4 +86,51 @@ export function registerModelsTools(server, deps) {
86
86
  ...(sections ? { sections } : {}),
87
87
  });
88
88
  }));
89
+ register("models_compare", {
90
+ title: "Compare two to four model cards",
91
+ description: "Two to four models side by side — models are columns, facts are rows — over the same door and the same "
92
+ + "builder the page at /settings/models/compare renders and `cockpit models compare` prints. Groups: "
93
+ + "overview, price (including `$ per 100 JARVIS turns`, our own measured tokens a turn times the "
94
+ + "provider's price, labelled with the window it was measured over — `not measured` where no profile "
95
+ + "exists, never a per-million price relabelled), our measurements, the bench and the quirks. The bench "
96
+ + "rows appear only when every model was judged in the SAME run against the SAME baseline; otherwise the "
97
+ + "group says `benched separately — not a head-to-head` rather than subtracting two win rates. A group "
98
+ + "with nothing in it for one column says so once, in that column; a single missing cell is `—`, and a "
99
+ + "measured one with no rows behind it is `not measured` — the two are never blurred. Read-only.",
100
+ inputSchema: {
101
+ ids: z
102
+ .array(z.string().min(1).max(200))
103
+ .min(2)
104
+ .max(4)
105
+ .describe("Two to four card ids, `provider:model` (e.g. openai:gpt-5.6-luna), in column order."),
106
+ highlight: z
107
+ .boolean()
108
+ .optional()
109
+ .describe("Mark the best cell in each scored row (lowest wins on price and latency, highest on tokens/s, "
110
+ + "most wins on the bench). Off by default; a row where any column is missing or unmeasured is "
111
+ + "never scored, and says why."),
112
+ },
113
+ }, async (args) => withSession(deps, async (session) => {
114
+ const ids = args.ids.map((id) => String(id));
115
+ const params = new URLSearchParams();
116
+ params.set("compare", ids.join(","));
117
+ if (args.highlight === true)
118
+ params.set("highlight", "1");
119
+ const response = await callAgentDoor(session, deps.fetchImpl, "GET", `/api/models/cards${queryString(params)}`);
120
+ // The door's own refusal names the id nothing answers to, or the column
121
+ // count it would not draw — both more useful than anything composed here.
122
+ if (!response.ok)
123
+ return errorResult(doorFailureText("models_compare", response));
124
+ const body = response.body;
125
+ const compare = (body.compare ?? {});
126
+ const lines = (Array.isArray(body.compare_lines) ? body.compare_lines : []);
127
+ // Every column names its PROVIDER as well as its model, because the
128
+ // same weights are reachable through two doors and two columns headed
129
+ // alike would be indistinguishable.
130
+ const headers = (compare.columns ?? [])
131
+ .map((column) => column.header ?? column.id ?? "")
132
+ .filter((header) => header.length > 0);
133
+ return textResult(lines.join("\n")
134
+ || `Compared ${ids.length} model cards: ${(headers.length > 0 ? headers : ids).join(", ")}.`, { compare });
135
+ }));
89
136
  }
@@ -241,6 +241,8 @@ export const MCP_TWINS = {
241
241
  // the org setting that picks from it.
242
242
  "models list": { tool: "models_list", door: "GET /api/models/cards" },
243
243
  "models show": { tool: "models_card", door: "GET /api/models/cards?id=" },
244
+ // BLI-3919: two to four cards side by side, on the same door.
245
+ "models compare": { tool: "models_compare", door: "GET /api/models/cards?compare=" },
244
246
  "scout board": { tool: "scout_board", door: "GET /api/cockpit/scout" },
245
247
  "scout start": { tool: "scout_start", door: "POST /api/cockpit/scout (start)" },
246
248
  "scout dismiss": { tool: "scout_dismiss", door: "POST /api/cockpit/scout (dismiss)" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/mcp",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "private": false,
5
5
  "description": "bli-tower \u2014 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",
@@ -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.37",
33
+ "@bli-cockpit/telemetry-core": "0.1.39",
34
34
  "@modelcontextprotocol/sdk": "^1.29.0",
35
35
  "zod": "^4.3.6"
36
36
  },