@bli-cockpit/mcp 0.1.108 → 0.1.110
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/brain-tools.js +17 -0
- package/dist/docs-msg-tools.js +2 -1
- package/dist/docs-notion-tools.js +3 -1
- package/dist/setup-tools.js +2 -2
- package/dist/tool-catalog.d.ts +1 -1
- package/dist/tool-catalog.js +1 -0
- package/dist/usage-tools.d.ts +1 -0
- package/dist/usage-tools.js +3 -2
- package/dist/verb-census.js +1 -0
- package/dist/work-tools.js +3 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -109,7 +109,7 @@ about what an agent can reach.
|
|
|
109
109
|
|
|
110
110
|
<!-- BEGIN GENERATED verb census — `npm run mcp:readme` -->
|
|
111
111
|
|
|
112
|
-
**
|
|
112
|
+
**139 of 144 Tower verbs have an MCP twin.**
|
|
113
113
|
Each tool goes through the SAME door its CLI verb calls, with the same
|
|
114
114
|
collector device token — never a second route and never a service-role
|
|
115
115
|
reader. `src/verb-census.test.ts` fails when a verb is in none of the
|
|
@@ -117,6 +117,7 @@ three tables below.
|
|
|
117
117
|
|
|
118
118
|
| CLI verb | MCP tool | Door |
|
|
119
119
|
| --- | --- | --- |
|
|
120
|
+
| `cockpit brain retry` | `brain_retry` | `POST /api/brain/incidents/retry` |
|
|
120
121
|
| `cockpit brain status` | `brain_status` | `GET /api/brain/incidents` |
|
|
121
122
|
| `cockpit brief done` | `brief_done` | `POST /api/jarvis/ticks` |
|
|
122
123
|
| `cockpit brief history` | `brief_history` | `GET /api/jarvis/brief?history=1` |
|
package/dist/brain-tools.js
CHANGED
|
@@ -36,4 +36,21 @@ export function registerBrainTools(server, deps) {
|
|
|
36
36
|
const text = typeof response.body.text === "string" ? response.body.text : "";
|
|
37
37
|
return dataResult(text, response.body);
|
|
38
38
|
}));
|
|
39
|
+
registrarFor(server)("brain_retry", {
|
|
40
|
+
title: "Send incidents back to Tower's brain",
|
|
41
|
+
description: "Retry needs_human or not_actionable incidents through the same door as cockpit brain retry. Only members and super admins. Resets attempts and claims, records who/why/when, and comments once on each incident issue. Other states are skipped.",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
ids: z.array(z.string()).max(100).optional().describe("Incident UUIDs or BLI issue identifiers."),
|
|
44
|
+
all_needs_human: z.boolean().optional().describe("Retry all needs_human incidents, optionally in one project."),
|
|
45
|
+
project: z.string().optional().describe("Narrow every selection to this registry project id."),
|
|
46
|
+
reason: z.string().trim().min(1).max(4000).describe("Why these incidents should be tried again."),
|
|
47
|
+
},
|
|
48
|
+
}, async (args) => withSession(deps, async (session) => {
|
|
49
|
+
const response = await callAgentDoor(session, deps.fetchImpl, "POST", "/api/brain/incidents/retry", args);
|
|
50
|
+
if (!response.ok)
|
|
51
|
+
return errorResult(doorFailureText("brain_retry", response));
|
|
52
|
+
if (response.body.ok === false)
|
|
53
|
+
return { ...dataResult(String(response.body.text ?? "Retry partially failed."), response.body), isError: true };
|
|
54
|
+
return dataResult(String(response.body.text ?? ""), response.body);
|
|
55
|
+
}));
|
|
39
56
|
}
|
package/dist/docs-msg-tools.js
CHANGED
|
@@ -39,7 +39,8 @@ export function registerDocsMsgTools(server, deps) {
|
|
|
39
39
|
const more = args.limit === undefined && all.length > DEFAULT_LIST_LIMIT;
|
|
40
40
|
const rows = more ? all.slice(0, DEFAULT_LIST_LIMIT) : all;
|
|
41
41
|
const lines = rows.map(docListLine).join("\n");
|
|
42
|
-
|
|
42
|
+
const counts = response.body.counts;
|
|
43
|
+
return textResult(`${rows.length} document(s).${counts ? ` ${counts.total} match in all. ${counts.coverage}` : ""}${moreNote(more, DOCS_LIST_MAX)}${lines ? `\n${lines}` : ""}`, { documents: rows.map(docListRow), more, counts });
|
|
43
44
|
}));
|
|
44
45
|
register("docs_tree", {
|
|
45
46
|
title: "Read the Tower document tree",
|
|
@@ -85,8 +85,10 @@ export function registerDocsNotionTools(server, deps) {
|
|
|
85
85
|
const runLine = ["push", "pull"]
|
|
86
86
|
.map((direction) => `${direction} ${runSentence(lastRun?.[direction] ?? null)}`)
|
|
87
87
|
.join(" · ");
|
|
88
|
-
|
|
88
|
+
const coverage = response.body.coverage ? `${response.body.coverage} ` : "";
|
|
89
|
+
return textResult(`${counts?.linked ?? 0} linked: ${summary}. ${coverage}Last run — ${runLine}.`, {
|
|
89
90
|
counts,
|
|
91
|
+
...response.body,
|
|
90
92
|
documents: response.body.documents ?? [],
|
|
91
93
|
last_run: lastRun ?? { push: null, pull: null },
|
|
92
94
|
});
|
package/dist/setup-tools.js
CHANGED
|
@@ -4,7 +4,7 @@ import { dataResult, doorFailureText, errorResult, registrarFor, withSession } f
|
|
|
4
4
|
export function registerSetupTools(server, deps) {
|
|
5
5
|
registrarFor(server)("setup_read", {
|
|
6
6
|
title: "Compare agent setups and outcomes",
|
|
7
|
-
description: "Read machine setup names and windowed (default 30-day) merged PR volume, tokens per merged PR and session yield. Compare exact emails. Differences are observations, not evidence that a setting causes better results. Missing or stale setup and incomplete token coverage are labeled. Members see their own outcomes; team comparisons require super_admin.",
|
|
7
|
+
description: "Read machine setup names and windowed (default 30-day) merged PR volume, tokens per merged PR and session yield, plus per person the tokens by tool and model that really ran (model_use) and the skills they typed or the agent called (skill_use, use not installs). Compare exact emails. Differences are observations, not evidence that a setting causes better results. Missing or stale setup and incomplete token coverage are labeled. Members see their own outcomes; team comparisons require super_admin.",
|
|
8
8
|
inputSchema: { since: z.string().max(40).optional().describe("7d, 30d, or an ISO date before now."), person: z.string().max(200).optional().describe("Only this person, by exact first name, display name or email address."), compare: z.string().max(401).optional().describe("Two people, by exact first name, display name or email address, separated by a comma (for example edward,ian). An ambiguous name is refused with its candidates.") },
|
|
9
9
|
}, async (args) => withSession(deps, async (session) => {
|
|
10
10
|
const query = new URLSearchParams();
|
|
@@ -15,6 +15,6 @@ export function registerSetupTools(server, deps) {
|
|
|
15
15
|
if (args.compare)
|
|
16
16
|
query.set("compare", String(args.compare));
|
|
17
17
|
const response = await callAgentDoor(session, deps.fetchImpl, "GET", `/api/setup?${query}`);
|
|
18
|
-
return response.ok ? dataResult(
|
|
18
|
+
return response.ok ? dataResult(`Agent setups and outcomes over the requested window. ${response.body.read_coverage?.join(" ") ?? ""}`, response.body) : errorResult(doorFailureText("setup_read", response));
|
|
19
19
|
}));
|
|
20
20
|
}
|
package/dist/tool-catalog.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare const DRY_RUN_TOOLS: readonly ["ops_recompile", "memory_requeue"]
|
|
|
21
21
|
* candidates, share calendars. Never called by the smoke; covered by the unit
|
|
22
22
|
* suites (`write-tools.test.ts` and each family's own).
|
|
23
23
|
*/
|
|
24
|
-
export declare const WRITE_TOOLS: readonly ["emit_event", "docs_create", "docs_update", "docs_delete", "docs_link", "msg_create_channel", "msg_dm", "msg_send", "work_create_issue", "work_update_issue", "work_move_issue", "work_comment_issue", "careers_rescreen", "careers_send_takehome", "careers_invite", "careers_grade", "careers_decide", "careers_followup", "careers_interview", "careers_takehome_set", "careers_settings_set", "rename_resolve", "github_merge", "github_retitle", "github_rerun", "mail_send", "mail_draft", "mail_sync", "mail_detach", "cal_create", "cal_share", "cal_sync", "cal_detach", "cal_delete", "jarvis_dispatch", "glossary_add", "glossary_remove", "notes_mkdir", "notes_rmdir", "notes_rename", "notes_upload", "notes_paste", "notes_share", "notes_unshare", "notes_move", "notes_sync", "notes_detach", "memory_experience", "scout_start", "scout_dismiss", "scout_undo", "brief_rewrite", "brief_correct", "brief_done", "brief_undone", "settings_set", "settings_delete", "model_set", "team_invite", "team_role", "team_device_revoke", "team_device_doctor"];
|
|
24
|
+
export declare const WRITE_TOOLS: readonly ["brain_retry", "emit_event", "docs_create", "docs_update", "docs_delete", "docs_link", "msg_create_channel", "msg_dm", "msg_send", "work_create_issue", "work_update_issue", "work_move_issue", "work_comment_issue", "careers_rescreen", "careers_send_takehome", "careers_invite", "careers_grade", "careers_decide", "careers_followup", "careers_interview", "careers_takehome_set", "careers_settings_set", "rename_resolve", "github_merge", "github_retitle", "github_rerun", "mail_send", "mail_draft", "mail_sync", "mail_detach", "cal_create", "cal_share", "cal_sync", "cal_detach", "cal_delete", "jarvis_dispatch", "glossary_add", "glossary_remove", "notes_mkdir", "notes_rmdir", "notes_rename", "notes_upload", "notes_paste", "notes_share", "notes_unshare", "notes_move", "notes_sync", "notes_detach", "memory_experience", "scout_start", "scout_dismiss", "scout_undo", "brief_rewrite", "brief_correct", "brief_done", "brief_undone", "settings_set", "settings_delete", "model_set", "team_invite", "team_role", "team_device_revoke", "team_device_doctor"];
|
|
25
25
|
/** Reads that spend a model turn. The smoke's `--no-model` leaves these out. */
|
|
26
26
|
export declare const MODEL_SPENDING_TOOLS: readonly ["jarvis_ask", "jarvis_check"];
|
|
27
27
|
/**
|
package/dist/tool-catalog.js
CHANGED
package/dist/usage-tools.d.ts
CHANGED
package/dist/usage-tools.js
CHANGED
|
@@ -21,6 +21,7 @@ export function githubWords(github, people) {
|
|
|
21
21
|
`GitHub: ${noun(sum("pull_requests_opened"), "pull request")} opened, ${sum("pull_requests_merged")} merged, ${noun(sum("commits"), "commit")}, ` +
|
|
22
22
|
`from ${noun((github.repositories ?? []).length, "mirrored repository", "mirrored repositories")}${range}.`,
|
|
23
23
|
];
|
|
24
|
+
lines.push(...(github.read_coverage ?? []));
|
|
24
25
|
const unmatched = github.unattributed;
|
|
25
26
|
if (unmatched && unmatched.pull_requests_opened + unmatched.pull_requests_merged + unmatched.commits > 0) {
|
|
26
27
|
lines.push(`Not matched to a person: ${noun(unmatched.pull_requests_opened, "pull request")} opened, ${unmatched.pull_requests_merged} merged, ${noun(unmatched.commits, "commit")}.`);
|
|
@@ -80,7 +81,7 @@ export function registerUsageTools(server, deps) {
|
|
|
80
81
|
// agent reading only the text half cannot quote a pull-request count without
|
|
81
82
|
// what it covers and who it left out.
|
|
82
83
|
const github = githubSentence(response.body);
|
|
83
|
-
return dataResult(`${coverage?.sessions_extracted ?? 0} of ${coverage?.sessions_observed ?? 0} sessions extracted.${why} ${coverage?.sessions_counted_at_upload ?? 0} of ${coverage?.sessions_observed ?? 0} sessions counted at upload, ${coverage?.sessions_counted_by_server ?? 0} by server backfill, ${coverage?.sessions_counted_by_ladder ?? 0} by the ladder.${args.groupBy === "task_type" ? ` ${coverage?.sessions_labelled ?? 0} of ${coverage?.sessions_observed ?? 0} sessions labelled by topic.` : ""}${priceLabel}${github}`, response.body);
|
|
84
|
+
return dataResult(`${response.body.read_coverage?.join(" ") ?? ""} ${coverage?.sessions_extracted ?? 0} of ${coverage?.sessions_observed ?? 0} sessions extracted.${why} ${coverage?.sessions_counted_at_upload ?? 0} of ${coverage?.sessions_observed ?? 0} sessions counted at upload, ${coverage?.sessions_counted_by_server ?? 0} by server backfill, ${coverage?.sessions_counted_by_ladder ?? 0} by the ladder.${args.groupBy === "task_type" ? ` ${coverage?.sessions_labelled ?? 0} of ${coverage?.sessions_observed ?? 0} sessions labelled by topic.` : ""}${priceLabel}${github}`, response.body);
|
|
84
85
|
}));
|
|
85
86
|
registrarFor(server)("usage_sessions", {
|
|
86
87
|
title: "Read task or subject sessions",
|
|
@@ -117,6 +118,6 @@ export function registerUsageTools(server, deps) {
|
|
|
117
118
|
return dataResult(`${windows} digest window(s) of session ${String(args.session)}.`, response.body);
|
|
118
119
|
}
|
|
119
120
|
const sessions = Array.isArray(response.body.sessions) ? response.body.sessions.length : 0;
|
|
120
|
-
return dataResult(`${sessions} session(s) for ${args.task ? `task ${String(args.task)}` : `topic "${String(args.subject)}"`}.`, response.body);
|
|
121
|
+
return dataResult(`${response.body.read_coverage?.join(" ") ?? ""} ${sessions} session(s) for ${args.task ? `task ${String(args.task)}` : `topic "${String(args.subject)}"`}.`, response.body);
|
|
121
122
|
}));
|
|
122
123
|
}
|
package/dist/verb-census.js
CHANGED
|
@@ -239,6 +239,7 @@ export const MCP_TWINS = {
|
|
|
239
239
|
// this machine's collector logs and the `vercel` / `railway` CLIs.
|
|
240
240
|
// BLI-12855: a project name (or --stored, or a local CLI that is missing) reads the digests Tower's cron stored, through GET /api/logs.
|
|
241
241
|
"logs": { tool: "logs_digest", door: "LOCAL @bli-cockpit/telemetry-core/log-digest (this machine's logs); `project` reads GET /api/logs (the stored digests)" },
|
|
242
|
+
"brain retry": { tool: "brain_retry", door: "POST /api/brain/incidents/retry" },
|
|
242
243
|
"brain status": { tool: "brain_status", door: "GET /api/brain/incidents" },
|
|
243
244
|
"usage sessions": { tool: "usage_sessions", door: "GET /api/usage/sessions" },
|
|
244
245
|
"setup": { tool: "setup_read", door: "GET /api/setup" },
|
package/dist/work-tools.js
CHANGED
|
@@ -135,10 +135,11 @@ export function registerWorkTools(server, deps) {
|
|
|
135
135
|
+ ` [p${issue.priority ?? 0}${size ? `, size ${size}` : ""}, assignee ${assigneeLabel(issue.assignee_id, directory)}, project ${issue.project_id ?? "none"}]`;
|
|
136
136
|
})
|
|
137
137
|
.join("\n");
|
|
138
|
-
const
|
|
138
|
+
const counts = response.body.counts;
|
|
139
|
+
const totalNote = counts ? ` ${counts.total} match in all. ${counts.coverage}` : completeness?.total != null && completeness.total > issues.length ? ` ${completeness.total} match in all.` : "";
|
|
139
140
|
return textResult(`${issues.length} issue(s).${totalNote}${moreNote(more, ISSUE_LIST_MAX)}${outsideNote(outside)}${lines ? `\n${lines}` : ""}`, {
|
|
140
141
|
issues: issues.map((issue) => withSizeLabel(withAssigneeIdentity(issueListRow(issue), directory))),
|
|
141
|
-
more,
|
|
142
|
+
more, counts,
|
|
142
143
|
...(completeness ? { total: completeness.total } : {}),
|
|
143
144
|
...(outside ? { outside_project: outside } : {}),
|
|
144
145
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.110",
|
|
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",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"readme": "npm run build && node scripts/write-readme-census.mjs"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@bli-cockpit/telemetry-core": "0.1.
|
|
40
|
+
"@bli-cockpit/telemetry-core": "0.1.69",
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
42
42
|
"zod": "^4.3.6"
|
|
43
43
|
},
|