@bli-cockpit/mcp 0.1.76 → 0.1.78
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/careers-tools.js +19 -2
- package/dist/server.js +5 -0
- package/dist/sources-tools.d.ts +30 -0
- package/dist/sources-tools.js +137 -0
- package/dist/tool-catalog.d.ts +2 -2
- package/dist/tool-catalog.js +2 -0
- package/dist/verb-census.js +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ about what an agent can reach.
|
|
|
103
103
|
|
|
104
104
|
<!-- BEGIN GENERATED verb census — `npm run mcp:readme` -->
|
|
105
105
|
|
|
106
|
-
**
|
|
106
|
+
**116 of 121 Tower verbs have an MCP twin.**
|
|
107
107
|
Each tool goes through the SAME door its CLI verb calls, with the same
|
|
108
108
|
collector device token — never a second route and never a service-role
|
|
109
109
|
reader. `src/verb-census.test.ts` fails when a verb is in none of the
|
|
@@ -129,6 +129,7 @@ three tables below.
|
|
|
129
129
|
| `cockpit cal today` | `cal_today` | `GET /api/cal/today` |
|
|
130
130
|
| `cockpit cal week` | `cal_week` | `GET /api/cal/week` |
|
|
131
131
|
| `cockpit careers decide` | `careers_decide` | `POST /api/careers/applications/[id]/decide` |
|
|
132
|
+
| `cockpit careers followup` | `careers_followup` | `POST /api/careers/applications/[id]/followup` |
|
|
132
133
|
| `cockpit careers grade` | `careers_grade` | `POST /api/careers/applications/[id]/grade` |
|
|
133
134
|
| `cockpit careers send-takehome` | `careers_send_takehome` | `POST /api/careers/applications/[id]/invite` |
|
|
134
135
|
| `cockpit careers list` | `careers_list` | `GET /api/careers/applications` |
|
package/dist/careers-tools.js
CHANGED
|
@@ -123,6 +123,20 @@ export function registerCareersTools(server, deps) {
|
|
|
123
123
|
const expires = typeof body.expires_in === 'number' && body.expires_in > 0 ? body.expires_in : 60;
|
|
124
124
|
return dataResult(`Resume link, valid for ${expires} seconds:`, { url: body.url, expires_in_seconds: expires });
|
|
125
125
|
}));
|
|
126
|
+
// BLI-12183: the second-chance email, or a record that one went out by hand.
|
|
127
|
+
// Same door the drawer's Send follow-up and Already followed up controls call.
|
|
128
|
+
register('careers_followup', {
|
|
129
|
+
title: 'Send or record the follow-up',
|
|
130
|
+
description: 'Super-admin only. With no record_sent_at, sends the one second-chance email now, in the take-home thread, from the configured mailbox: only a row at `invited` with no follow-up yet. With record_sent_at, records a follow-up sent outside Tower at that instant and sends nothing. Either way a second follow-up is refused.',
|
|
131
|
+
inputSchema: {
|
|
132
|
+
id: APPLICATION_ID,
|
|
133
|
+
record_sent_at: z.string().min(1).max(40).optional().describe('ISO-8601 instant with an offset, e.g. 2026-10-03T08:00:00Z. Records a follow-up already sent outside Tower; sends nothing.'),
|
|
134
|
+
},
|
|
135
|
+
}, async (args) => withSession(deps, async (session) => {
|
|
136
|
+
const body = args.record_sent_at === undefined ? {} : { record_sent_at: args.record_sent_at };
|
|
137
|
+
const answer = await callAgentDoor(session, deps.fetchImpl, 'POST', `/api/careers/applications/${encodeURIComponent(String(args.id))}/followup`, body, 60_000);
|
|
138
|
+
return answer.ok ? dataResult(args.record_sent_at === undefined ? 'Follow-up sent' : 'Follow-up recorded', answer.body) : errorResult(doorFailureText('careers_followup', answer));
|
|
139
|
+
}));
|
|
126
140
|
register('careers_decide', {
|
|
127
141
|
title: 'Decide on an application',
|
|
128
142
|
description: 'Super-admin only. Records the call on one application: `passed_on`, `archived`, or `reopen` to put it back at `screened`.',
|
|
@@ -157,7 +171,7 @@ export function registerCareersTools(server, deps) {
|
|
|
157
171
|
}));
|
|
158
172
|
register('careers_settings_show', {
|
|
159
173
|
title: 'Show the careers settings',
|
|
160
|
-
description: 'Super-admin only. The automatic-invite bar, the attention threshold, whether automatic sending is on,
|
|
174
|
+
description: 'Super-admin only. The automatic-invite bar, the attention threshold, whether automatic sending is on, which mailbox it goes out from, the booking link and grader model, and the three follow-up settings (followupEnabled, followupAfterDays, takehomeWindowHours).',
|
|
161
175
|
inputSchema: {},
|
|
162
176
|
}, async () => withSession(deps, async (session) => {
|
|
163
177
|
const answer = await callAgentDoor(session, deps.fetchImpl, 'GET', '/api/careers/settings');
|
|
@@ -165,7 +179,7 @@ export function registerCareersTools(server, deps) {
|
|
|
165
179
|
}));
|
|
166
180
|
register('careers_settings_set', {
|
|
167
181
|
title: 'Change the careers settings',
|
|
168
|
-
description: 'Super-admin only. Writes any of the
|
|
182
|
+
description: 'Super-admin only. Writes any of the careers settings. Each takes effect on the next use, with no deploy.',
|
|
169
183
|
inputSchema: {
|
|
170
184
|
auto_invite_enabled: z.boolean().optional().describe('Send the take-home automatically to applicants over the auto-invite bar.'),
|
|
171
185
|
auto_invite_min_score: z.number().min(0).max(100).optional().describe('The screening score (0-100) at or over which the take-home goes out automatically.'),
|
|
@@ -173,6 +187,9 @@ export function registerCareersTools(server, deps) {
|
|
|
173
187
|
invite_account_id: z.string().uuid().nullable().optional().describe('The mailbox id (mail_accounts) invitations are sent from; null clears it.'),
|
|
174
188
|
booking_link: z.string().url().max(2000).nullable().optional().describe('The interview booking link sent after a passing grade; null clears it.'),
|
|
175
189
|
grader_model: z.string().min(3).max(200).optional().describe('The take-home grader model, provider:model (models_list ids).'),
|
|
190
|
+
followup_enabled: z.boolean().optional().describe('Send the one second-chance follow-up automatically to overdue invited candidates. Off by default.'),
|
|
191
|
+
followup_after_days: z.number().int().min(1).max(60).optional().describe('Whole days after the invitation before the follow-up is due (1-60).'),
|
|
192
|
+
takehome_window_hours: z.number().int().min(1).max(720).optional().describe('Hours a candidate has: the first deadline and the second-chance deadline (1-720).'),
|
|
176
193
|
},
|
|
177
194
|
}, async (args) => withSession(deps, async (session) => {
|
|
178
195
|
const answer = await callAgentDoor(session, deps.fetchImpl, 'PUT', '/api/careers/settings', args);
|
package/dist/server.js
CHANGED
|
@@ -32,6 +32,7 @@ import { registerTeamWriteTools } from "./team-write-tools.js";
|
|
|
32
32
|
import { registerWorkTools } from "./work-tools.js";
|
|
33
33
|
import { registerCareersTools } from "./careers-tools.js";
|
|
34
34
|
import { registerUsageTools } from "./usage-tools.js";
|
|
35
|
+
import { registerSourcesTools } from "./sources-tools.js";
|
|
35
36
|
export const PACKAGE_NAME = "@bli-cockpit/mcp";
|
|
36
37
|
/**
|
|
37
38
|
* The version this server tells a client in `initialize`, read from the
|
|
@@ -483,5 +484,9 @@ export function createServer(deps) {
|
|
|
483
484
|
// BLI-3728: one search over documents, messages, issues, meeting notes and
|
|
484
485
|
// memory. Same door, same ranking, same snippets the browser bar shows.
|
|
485
486
|
registerSearchTool(server, { fetchImpl: deps.fetchImpl });
|
|
487
|
+
// BLI-11122: which local transcripts Tower holds, read-only, by running
|
|
488
|
+
// `cockpit sources check` on this machine. No delete twin, ever: Tower never
|
|
489
|
+
// deletes a person's transcripts and an agent must not do it through a tool.
|
|
490
|
+
registerSourcesTools(server);
|
|
486
491
|
return server;
|
|
487
492
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ToolResult } from "./tool-result.js";
|
|
2
|
+
declare const VERDICTS: readonly ["stored_full", "stored_partial", "not_stored"];
|
|
3
|
+
export interface CliRunOutcome {
|
|
4
|
+
exitCode: number | null;
|
|
5
|
+
stdout: string;
|
|
6
|
+
/** A reason label when the CLI could not be run at all. */
|
|
7
|
+
spawnError?: string;
|
|
8
|
+
}
|
|
9
|
+
export type RunCockpitCli = (args: string[]) => Promise<CliRunOutcome>;
|
|
10
|
+
export interface SourcesToolDeps {
|
|
11
|
+
runCli?: RunCockpitCli;
|
|
12
|
+
}
|
|
13
|
+
export declare function registerSourcesTools(server: {
|
|
14
|
+
registerTool: (...args: never[]) => unknown;
|
|
15
|
+
}, deps?: SourcesToolDeps): void;
|
|
16
|
+
interface SourcesCheckArgs {
|
|
17
|
+
older_than_days?: number;
|
|
18
|
+
verdict?: (typeof VERDICTS)[number];
|
|
19
|
+
include_files?: boolean;
|
|
20
|
+
limit?: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function sourcesCheck(runCli: RunCockpitCli, args: SourcesCheckArgs): Promise<ToolResult>;
|
|
23
|
+
/**
|
|
24
|
+
* The CLI this server shipped beside (`@bli-cockpit/cli` depends on this
|
|
25
|
+
* package), run with this same Node; `cockpit` on PATH as the fallback.
|
|
26
|
+
*/
|
|
27
|
+
export declare const runInstalledCockpit: RunCockpitCli;
|
|
28
|
+
/** Walk up from this module to the `@bli-cockpit/cli` package that installed it. */
|
|
29
|
+
export declare function findCliEntry(startDir: string, exists?: (file: string) => boolean): string | null;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sources_check` — the twin of `cockpit sources check` (BLI-11122).
|
|
3
|
+
*
|
|
4
|
+
* Does Tower hold each Codex and Claude transcript on this machine: stored in
|
|
5
|
+
* full, stored in part, or not stored, with the reason. An agent asked to free
|
|
6
|
+
* disk calls this BEFORE anything is deleted; on 2026-09-30 one guessed
|
|
7
|
+
* instead and deleted 349 transcripts Tower did not hold.
|
|
8
|
+
*
|
|
9
|
+
* The answer needs the local files, which only this machine can read, so the
|
|
10
|
+
* tool runs the SAME verb on the same machine (`cockpit sources check --json`)
|
|
11
|
+
* rather than a second implementation: one set of verdict rules, one door
|
|
12
|
+
* (`POST /api/ambient/evidence/transcripts`). This server is always spawned on
|
|
13
|
+
* the machine that holds the transcripts.
|
|
14
|
+
*
|
|
15
|
+
* Read-only, and there is deliberately no delete twin: Tower never deletes a
|
|
16
|
+
* person's raw transcripts, and an agent must never delete them through a
|
|
17
|
+
* tool. Deleting one is the person's own act, by their own hand.
|
|
18
|
+
*/
|
|
19
|
+
import { spawn } from "node:child_process";
|
|
20
|
+
import fs from "node:fs";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
import { dataResult, errorResult, registrarFor } from "./tool-result.js";
|
|
25
|
+
/** Redacting a large transcript store to compare hashes takes minutes, not seconds. */
|
|
26
|
+
const CHECK_TIMEOUT_MS = 15 * 60 * 1000;
|
|
27
|
+
const DEFAULT_FILE_LIMIT = 50;
|
|
28
|
+
const MAX_FILE_LIMIT = 500;
|
|
29
|
+
const VERDICTS = ["stored_full", "stored_partial", "not_stored"];
|
|
30
|
+
export function registerSourcesTools(server, deps = {}) {
|
|
31
|
+
const runCli = deps.runCli ?? runInstalledCockpit;
|
|
32
|
+
registrarFor(server)("sources_check", {
|
|
33
|
+
title: "Check which local transcripts Tower holds",
|
|
34
|
+
description: "For every Codex and Claude transcript on THIS machine: does Tower hold it (stored_full, stored_partial, not_stored), with the reason, counts and bytes. Read-only. Call it before deleting any agent transcript to free disk: only a stored_full file is held by Tower in full. Tower never deletes these files, and this server has no tool that does. older_than_days limits the check to files not modified in that many days. The answer names files only when verdict is given or include_files is true (paths, newest verdict first, up to limit). Redacting large stores can take minutes.",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
older_than_days: z.number().int().min(1).max(36_500).optional().describe("Only files not modified in this many days. Default: every transcript."),
|
|
37
|
+
verdict: z.enum(VERDICTS).optional().describe("List only files with this verdict (implies include_files)."),
|
|
38
|
+
include_files: z.boolean().optional().describe("List files with their paths. Off by default: counts only."),
|
|
39
|
+
limit: z.number().int().min(1).max(MAX_FILE_LIMIT).optional().describe(`How many files to list. Default ${DEFAULT_FILE_LIMIT}.`),
|
|
40
|
+
},
|
|
41
|
+
}, async (args) => sourcesCheck(runCli, args));
|
|
42
|
+
}
|
|
43
|
+
export async function sourcesCheck(runCli, args) {
|
|
44
|
+
const cliArgs = ["sources", "check", "--json", ...(args.older_than_days ? ["--older-than", String(args.older_than_days)] : [])];
|
|
45
|
+
const outcome = await runCli(cliArgs);
|
|
46
|
+
if (outcome.spawnError) {
|
|
47
|
+
return errorResult(`sources_check could not run \`cockpit sources check\` on this machine (${outcome.spawnError}). Nothing was read. Install or update the Tower CLI: \`npm i -g @bli-cockpit/cli && cockpit doctor\`.`);
|
|
48
|
+
}
|
|
49
|
+
let report;
|
|
50
|
+
try {
|
|
51
|
+
report = JSON.parse(outcome.stdout);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return errorResult(`sources_check got no answer from \`cockpit sources check\` (cli_unparseable_output, exit ${String(outcome.exitCode)}). An older CLI may lack the verb: \`npm i -g @bli-cockpit/cli\`. Nothing was read.`);
|
|
55
|
+
}
|
|
56
|
+
if (report.status === "unavailable") {
|
|
57
|
+
return errorResult(`sources_check could not ask Tower (${String(report.reason)}). No file is answered; nothing here means "not stored".`);
|
|
58
|
+
}
|
|
59
|
+
const counts = (report.counts ?? {});
|
|
60
|
+
const all = Array.isArray(report.files) ? report.files : [];
|
|
61
|
+
const listFiles = args.include_files === true || args.verdict !== undefined;
|
|
62
|
+
const matching = args.verdict ? all.filter((file) => file.verdict === args.verdict) : all;
|
|
63
|
+
const limit = args.limit ?? DEFAULT_FILE_LIMIT;
|
|
64
|
+
const files = listFiles ? matching.slice(0, limit) : [];
|
|
65
|
+
const structured = {
|
|
66
|
+
status: report.status,
|
|
67
|
+
reason: report.reason,
|
|
68
|
+
older_than_days: report.older_than_days,
|
|
69
|
+
counts: report.counts,
|
|
70
|
+
reasons: report.reasons,
|
|
71
|
+
local: report.local,
|
|
72
|
+
remote: report.remote,
|
|
73
|
+
files,
|
|
74
|
+
files_listed: files.length,
|
|
75
|
+
files_matching: listFiles ? matching.length : null,
|
|
76
|
+
};
|
|
77
|
+
const mb = (bytes) => ((bytes ?? 0) / (1024 * 1024)).toFixed(1);
|
|
78
|
+
const headline = `stored in full ${counts.stored_full?.files ?? 0} (${mb(counts.stored_full?.bytes)} MB), ` +
|
|
79
|
+
`stored in part ${counts.stored_partial?.files ?? 0} (${mb(counts.stored_partial?.bytes)} MB), ` +
|
|
80
|
+
`not stored ${counts.not_stored?.files ?? 0} (${mb(counts.not_stored?.bytes)} MB)` +
|
|
81
|
+
`${report.status === "incomplete" ? `; INCOMPLETE (${String(report.reason)}), not stored may be understated` : ""}.` +
|
|
82
|
+
`${listFiles && matching.length > files.length ? ` Listing ${files.length} of ${matching.length}; pass a larger limit (up to ${MAX_FILE_LIMIT}).` : ""}` +
|
|
83
|
+
" Tower never deletes these files; only a stored_full file is held in full.";
|
|
84
|
+
return dataResult(headline, structured);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The CLI this server shipped beside (`@bli-cockpit/cli` depends on this
|
|
88
|
+
* package), run with this same Node; `cockpit` on PATH as the fallback.
|
|
89
|
+
*/
|
|
90
|
+
export const runInstalledCockpit = async (args) => {
|
|
91
|
+
const entry = findCliEntry(path.dirname(fileURLToPath(import.meta.url)));
|
|
92
|
+
const command = entry ? process.execPath : process.platform === "win32" ? "cockpit.cmd" : "cockpit";
|
|
93
|
+
const commandArgs = entry ? [entry, ...args] : args;
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
let stdout = "";
|
|
96
|
+
let settled = false;
|
|
97
|
+
const finish = (outcome) => {
|
|
98
|
+
if (settled)
|
|
99
|
+
return;
|
|
100
|
+
settled = true;
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
resolve(outcome);
|
|
103
|
+
};
|
|
104
|
+
// A `.cmd` shim needs a shell on Windows; the arguments are fixed words and a number.
|
|
105
|
+
const child = spawn(command, commandArgs, {
|
|
106
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
107
|
+
shell: !entry && process.platform === "win32",
|
|
108
|
+
windowsHide: true,
|
|
109
|
+
});
|
|
110
|
+
const timer = setTimeout(() => {
|
|
111
|
+
child.kill();
|
|
112
|
+
finish({ exitCode: null, stdout, spawnError: "cli_timeout" });
|
|
113
|
+
}, CHECK_TIMEOUT_MS);
|
|
114
|
+
child.stdout?.setEncoding("utf8");
|
|
115
|
+
child.stdout?.on("data", (chunk) => (stdout += chunk));
|
|
116
|
+
child.on("error", (error) => finish({ exitCode: null, stdout, spawnError: error.code ?? "spawn_failed" }));
|
|
117
|
+
child.on("close", (code) => finish({ exitCode: code, stdout }));
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
/** Walk up from this module to the `@bli-cockpit/cli` package that installed it. */
|
|
121
|
+
export function findCliEntry(startDir, exists = fs.existsSync) {
|
|
122
|
+
let dir = startDir;
|
|
123
|
+
for (;;) {
|
|
124
|
+
const beside = path.join(dir, "node_modules", "@bli-cockpit", "cli", "dist", "cli.js");
|
|
125
|
+
if (exists(beside))
|
|
126
|
+
return beside;
|
|
127
|
+
if (path.basename(dir) === "cli" && path.basename(path.dirname(dir)) === "@bli-cockpit") {
|
|
128
|
+
const own = path.join(dir, "dist", "cli.js");
|
|
129
|
+
if (exists(own))
|
|
130
|
+
return own;
|
|
131
|
+
}
|
|
132
|
+
const parent = path.dirname(dir);
|
|
133
|
+
if (parent === dir)
|
|
134
|
+
return null;
|
|
135
|
+
dir = parent;
|
|
136
|
+
}
|
|
137
|
+
}
|
package/dist/tool-catalog.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* from `src/` under Node's type stripping, so it may import nothing.
|
|
11
11
|
*/
|
|
12
12
|
/** Tools that change nothing. The smoke calls every one. */
|
|
13
|
-
export declare const READ_TOOLS: readonly ["get_ticket_timeline", "get_active_tickets", "docs_list", "docs_tree", "docs_read", "docs_sync_status", "msg_channels", "msg_read", "msg_thread", "work_list_issues", "work_get_issue", "work_issue_history", "work_list_projects", "careers_list", "careers_show", "careers_resume_url", "careers_takehome_show", "careers_settings_show", "usage_people", "usage_sessions", "mail_accounts", "mail_inbox", "mail_read", "mail_search", "mail_attachment", "mail_sync_status", "cal_today", "cal_week", "cal_next", "cal_find", "cal_calendars", "cal_sync_status", "jarvis_ask", "jarvis_trace", "jarvis_check", "models_list", "models_card", "models_compare", "brief_read", "brief_history", "brief_status", "brief_missed", "notes_folders", "notes_list", "notes_show", "notes_shelf", "notes_shelves", "notes_place", "notes_accounts", "notes_sync_status", "notes_granola_status", "ops_status", "slack_coverage", "slack_read", "memory_profile", "memory_consolidation", "settings_show", "settings_list", "model_show", "team_members", "team_device_list", "scout_board", "workbook_read", "tower_search"];
|
|
13
|
+
export declare const READ_TOOLS: readonly ["get_ticket_timeline", "get_active_tickets", "docs_list", "docs_tree", "docs_read", "docs_sync_status", "msg_channels", "msg_read", "msg_thread", "work_list_issues", "work_get_issue", "work_issue_history", "work_list_projects", "careers_list", "careers_show", "careers_resume_url", "careers_takehome_show", "careers_settings_show", "usage_people", "usage_sessions", "mail_accounts", "mail_inbox", "mail_read", "mail_search", "mail_attachment", "mail_sync_status", "cal_today", "cal_week", "cal_next", "cal_find", "cal_calendars", "cal_sync_status", "jarvis_ask", "jarvis_trace", "jarvis_check", "models_list", "models_card", "models_compare", "brief_read", "brief_history", "brief_status", "brief_missed", "notes_folders", "notes_list", "notes_show", "notes_shelf", "notes_shelves", "notes_place", "notes_accounts", "notes_sync_status", "notes_granola_status", "ops_status", "slack_coverage", "slack_read", "memory_profile", "memory_consolidation", "settings_show", "settings_list", "model_show", "team_members", "team_device_list", "scout_board", "workbook_read", "tower_search", "sources_check"];
|
|
14
14
|
/**
|
|
15
15
|
* Tools that write, but answer a dry run that writes nothing. The smoke calls
|
|
16
16
|
* each one ONLY with its dry-run flag set (`scripts/smoke.ts` says which).
|
|
@@ -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_takehome_set", "careers_settings_set", "mail_send", "mail_sync", "mail_detach", "cal_create", "cal_share", "cal_sync", "cal_detach", "cal_delete", "jarvis_dispatch", "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"];
|
|
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_takehome_set", "careers_settings_set", "mail_send", "mail_sync", "mail_detach", "cal_create", "cal_share", "cal_sync", "cal_detach", "cal_delete", "jarvis_dispatch", "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"];
|
|
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
|
@@ -75,6 +75,7 @@ export const READ_TOOLS = [
|
|
|
75
75
|
"scout_board",
|
|
76
76
|
"workbook_read",
|
|
77
77
|
"tower_search",
|
|
78
|
+
"sources_check",
|
|
78
79
|
];
|
|
79
80
|
/**
|
|
80
81
|
* Tools that write, but answer a dry run that writes nothing. The smoke calls
|
|
@@ -104,6 +105,7 @@ export const WRITE_TOOLS = [
|
|
|
104
105
|
"careers_invite",
|
|
105
106
|
"careers_grade",
|
|
106
107
|
"careers_decide",
|
|
108
|
+
"careers_followup",
|
|
107
109
|
"careers_takehome_set",
|
|
108
110
|
"careers_settings_set",
|
|
109
111
|
"mail_send",
|
package/dist/verb-census.js
CHANGED
|
@@ -218,6 +218,8 @@ export const MCP_TWINS = {
|
|
|
218
218
|
// README). `cockpit careers invite` and `careers_invite` are the older words
|
|
219
219
|
// for the identical door, and both still work.
|
|
220
220
|
"careers invite": { tool: "careers_send_takehome", door: "POST /api/careers/applications/[id]/invite" },
|
|
221
|
+
// BLI-12183: the second-chance follow-up, sent now or recorded as sent elsewhere.
|
|
222
|
+
"careers followup": { tool: "careers_followup", door: "POST /api/careers/applications/[id]/followup" },
|
|
221
223
|
"careers decide": { tool: "careers_decide", door: "POST /api/careers/applications/[id]/decide" },
|
|
222
224
|
"careers grade": { tool: "careers_grade", door: "POST /api/careers/applications/[id]/grade" },
|
|
223
225
|
// BLI-4462: the drawer's Open resume button, typed. Both twins call the door
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.78",
|
|
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",
|
|
@@ -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.56",
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
35
|
"zod": "^4.3.6"
|
|
36
36
|
},
|