@bli-cockpit/mcp 0.1.47 → 0.1.49
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 +7 -1
- package/dist/careers-tools.d.ts +10 -0
- package/dist/careers-tools.js +73 -2
- package/dist/ops-tools.js +26 -1
- package/dist/verb-census.js +10 -0
- package/package.json +1 -1
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
|
+
**100 of 105 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
|
|
@@ -55,9 +55,15 @@ three tables below.
|
|
|
55
55
|
| `cockpit cal sync` | `cal_sync` | `POST /api/cal/calendars/[id]/sync` |
|
|
56
56
|
| `cockpit cal today` | `cal_today` | `GET /api/cal/today` |
|
|
57
57
|
| `cockpit cal week` | `cal_week` | `GET /api/cal/week` |
|
|
58
|
+
| `cockpit careers decide` | `careers_decide` | `POST /api/careers/applications/[id]/decide` |
|
|
59
|
+
| `cockpit careers invite` | `careers_invite` | `POST /api/careers/applications/[id]/invite` |
|
|
58
60
|
| `cockpit careers list` | `careers_list` | `GET /api/careers/applications` |
|
|
59
61
|
| `cockpit careers rescreen` | `careers_rescreen` | `POST /api/careers/applications/[id]/rescreen` |
|
|
62
|
+
| `cockpit careers settings set` | `careers_settings_set` | `PUT /api/careers/settings` |
|
|
63
|
+
| `cockpit careers settings show` | `careers_settings_show` | `GET /api/careers/settings` |
|
|
60
64
|
| `cockpit careers show` | `careers_show` | `GET /api/careers/applications/[id]` |
|
|
65
|
+
| `cockpit careers takehome set` | `careers_takehome_set` | `PUT /api/careers/takehomes/[role]` |
|
|
66
|
+
| `cockpit careers takehome show` | `careers_takehome_show` | `GET /api/careers/takehomes/[role]` |
|
|
61
67
|
| `cockpit correct` | `brief_correct` | `POST /api/jarvis/corrections` |
|
|
62
68
|
| `cockpit docs create` | `docs_create` | `POST /api/docs/documents` |
|
|
63
69
|
| `cockpit docs list` | `docs_list` | `GET /api/docs/documents` |
|
package/dist/careers-tools.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { type ToolDeps } from './tool-result.js';
|
|
2
|
+
/**
|
|
3
|
+
* `careers_*` — the MCP twins of `cockpit careers`, over the same doors.
|
|
4
|
+
*
|
|
5
|
+
* BLI-4460 added the pipeline half: inviting, deciding, the role's take-home
|
|
6
|
+
* and the four settings. Nothing here decides policy — super_admin, the stage
|
|
7
|
+
* check and every threshold live on the server, so this surface cannot permit
|
|
8
|
+
* something the browser refuses. `careers_takehome_show` returns the RUBRIC,
|
|
9
|
+
* which is reviewer material: it is readable by a super_admin's agent and is
|
|
10
|
+
* never part of what a candidate receives.
|
|
11
|
+
*/
|
|
2
12
|
export declare function registerCareersTools(server: {
|
|
3
13
|
registerTool: (...args: never[]) => unknown;
|
|
4
14
|
}, deps: ToolDeps): void;
|
package/dist/careers-tools.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { callAgentDoor } from './agent-door.js';
|
|
3
3
|
import { doorFailureText, errorResult, registrarFor, textResult, withSession } from './tool-result.js';
|
|
4
|
+
/**
|
|
5
|
+
* `careers_*` — the MCP twins of `cockpit careers`, over the same doors.
|
|
6
|
+
*
|
|
7
|
+
* BLI-4460 added the pipeline half: inviting, deciding, the role's take-home
|
|
8
|
+
* and the four settings. Nothing here decides policy — super_admin, the stage
|
|
9
|
+
* check and every threshold live on the server, so this surface cannot permit
|
|
10
|
+
* something the browser refuses. `careers_takehome_show` returns the RUBRIC,
|
|
11
|
+
* which is reviewer material: it is readable by a super_admin's agent and is
|
|
12
|
+
* never part of what a candidate receives.
|
|
13
|
+
*/
|
|
4
14
|
export function registerCareersTools(server, deps) {
|
|
5
15
|
const register = registrarFor(server);
|
|
6
|
-
register('careers_list', { title: 'List applications', description: 'Super-admin only. Newest applications, bounded to 100 with total and has_more.', inputSchema: { role: z.string().optional(), min_score: z.number().min(0).max(100).optional(), since: z.string().optional() } }, async (args) => withSession(deps, async (session) => {
|
|
16
|
+
register('careers_list', { title: 'List applications', description: 'Super-admin only. Newest applications, bounded to 100 with total and has_more, plus the two thresholds in force.', inputSchema: { role: z.string().optional(), min_score: z.number().min(0).max(100).optional(), since: z.string().optional(), stage: z.string().optional() } }, async (args) => withSession(deps, async (session) => {
|
|
7
17
|
const query = new URLSearchParams();
|
|
8
|
-
for (const key of ['role', 'min_score', 'since'])
|
|
18
|
+
for (const key of ['role', 'min_score', 'since', 'stage'])
|
|
9
19
|
if (args[key] !== undefined)
|
|
10
20
|
query.set(key, String(args[key]));
|
|
11
21
|
const answer = await callAgentDoor(session, deps.fetchImpl, 'GET', `/api/careers/applications?${query}`);
|
|
@@ -16,4 +26,65 @@ export function registerCareersTools(server, deps) {
|
|
|
16
26
|
const answer = await callAgentDoor(session, deps.fetchImpl, action === 'show' ? 'GET' : 'POST', `/api/careers/applications/${encodeURIComponent(String(args.id))}${action === 'rescreen' ? '/rescreen' : ''}`, undefined, 60_000);
|
|
17
27
|
return answer.ok ? textResult('Application', answer.body) : errorResult(doorFailureText(`careers_${action}`, answer));
|
|
18
28
|
}));
|
|
29
|
+
register('careers_invite', {
|
|
30
|
+
title: 'Send the take-home',
|
|
31
|
+
description: 'Super-admin only. Sends that role\'s take-home invitation from the configured mailbox, whatever the screening scored. Only a row still at the `screened` stage; a row already invited is refused.',
|
|
32
|
+
inputSchema: { id: z.string().uuid() },
|
|
33
|
+
}, async (args) => withSession(deps, async (session) => {
|
|
34
|
+
const answer = await callAgentDoor(session, deps.fetchImpl, 'POST', `/api/careers/applications/${encodeURIComponent(String(args.id))}/invite`, undefined, 60_000);
|
|
35
|
+
return answer.ok ? textResult('Invitation sent', answer.body) : errorResult(doorFailureText('careers_invite', answer));
|
|
36
|
+
}));
|
|
37
|
+
register('careers_decide', {
|
|
38
|
+
title: 'Decide on an application',
|
|
39
|
+
description: 'Super-admin only. Records the call on one application: `passed_on`, `archived`, or `reopen` to put it back at `screened`.',
|
|
40
|
+
inputSchema: { id: z.string().uuid(), decision: z.enum(['passed_on', 'archived', 'reopen']) },
|
|
41
|
+
}, async (args) => withSession(deps, async (session) => {
|
|
42
|
+
const answer = await callAgentDoor(session, deps.fetchImpl, 'POST', `/api/careers/applications/${encodeURIComponent(String(args.id))}/decide`, { decision: args.decision });
|
|
43
|
+
return answer.ok ? textResult('Decision recorded', answer.body) : errorResult(doorFailureText('careers_decide', answer));
|
|
44
|
+
}));
|
|
45
|
+
register('careers_takehome_show', {
|
|
46
|
+
title: 'Show a role take-home',
|
|
47
|
+
description: 'Super-admin only. The subject, body, artifact link, reviewer rubric and pass score for one role. The rubric is reviewer material and never reaches a candidate.',
|
|
48
|
+
inputSchema: { role: z.string().min(1).max(200) },
|
|
49
|
+
}, async (args) => withSession(deps, async (session) => {
|
|
50
|
+
const answer = await callAgentDoor(session, deps.fetchImpl, 'GET', `/api/careers/takehomes/${encodeURIComponent(String(args.role))}`);
|
|
51
|
+
return answer.ok ? textResult('Take-home', answer.body) : errorResult(doorFailureText('careers_takehome_show', answer));
|
|
52
|
+
}));
|
|
53
|
+
register('careers_takehome_set', {
|
|
54
|
+
title: 'Edit a role take-home',
|
|
55
|
+
description: 'Super-admin only. Edits one role\'s take-home. A body must keep the {first_name} and {link} placeholders. A role nobody seeded is refused rather than created.',
|
|
56
|
+
inputSchema: {
|
|
57
|
+
role: z.string().min(1).max(200),
|
|
58
|
+
subject: z.string().min(1).max(998).optional(),
|
|
59
|
+
body_text: z.string().min(1).max(100_000).optional(),
|
|
60
|
+
artifact_link: z.string().url().max(2_000).optional(),
|
|
61
|
+
rubric_md: z.string().min(1).max(100_000).optional(),
|
|
62
|
+
pass_score: z.number().min(0).max(10).optional(),
|
|
63
|
+
},
|
|
64
|
+
}, async (args) => withSession(deps, async (session) => {
|
|
65
|
+
const { role, ...patch } = args;
|
|
66
|
+
const answer = await callAgentDoor(session, deps.fetchImpl, 'PUT', `/api/careers/takehomes/${encodeURIComponent(String(role))}`, patch);
|
|
67
|
+
return answer.ok ? textResult('Take-home saved', answer.body) : errorResult(doorFailureText('careers_takehome_set', answer));
|
|
68
|
+
}));
|
|
69
|
+
register('careers_settings_show', {
|
|
70
|
+
title: 'Show the careers settings',
|
|
71
|
+
description: 'Super-admin only. The automatic-invite bar, the attention threshold, whether automatic sending is on, and which mailbox it goes out from.',
|
|
72
|
+
inputSchema: {},
|
|
73
|
+
}, async () => withSession(deps, async (session) => {
|
|
74
|
+
const answer = await callAgentDoor(session, deps.fetchImpl, 'GET', '/api/careers/settings');
|
|
75
|
+
return answer.ok ? textResult('Careers settings', answer.body) : errorResult(doorFailureText('careers_settings_show', answer));
|
|
76
|
+
}));
|
|
77
|
+
register('careers_settings_set', {
|
|
78
|
+
title: 'Change the careers settings',
|
|
79
|
+
description: 'Super-admin only. Writes any of the four careers settings. Each takes effect on the next screening, with no deploy.',
|
|
80
|
+
inputSchema: {
|
|
81
|
+
auto_invite_enabled: z.boolean().optional(),
|
|
82
|
+
auto_invite_min_score: z.number().min(0).max(100).optional(),
|
|
83
|
+
attention_min_score: z.number().min(0).max(100).optional(),
|
|
84
|
+
invite_account_id: z.string().uuid().nullable().optional(),
|
|
85
|
+
},
|
|
86
|
+
}, async (args) => withSession(deps, async (session) => {
|
|
87
|
+
const answer = await callAgentDoor(session, deps.fetchImpl, 'PUT', '/api/careers/settings', args);
|
|
88
|
+
return answer.ok ? textResult('Careers settings saved', answer.body) : errorResult(doorFailureText('careers_settings_set', answer));
|
|
89
|
+
}));
|
|
19
90
|
}
|
package/dist/ops-tools.js
CHANGED
|
@@ -57,6 +57,8 @@ export function registerOpsTools(server, deps) {
|
|
|
57
57
|
job: z.string().min(1).max(100).optional().describe("One pipeline id instead of the whole board."),
|
|
58
58
|
skips: z.boolean().optional().describe("Also return the open ingest-skip ledgers."),
|
|
59
59
|
memory: z.boolean().optional().describe("Also return memory adoption and observed prompt-hook outcomes, delay bounds and cache reuse. No memory text."),
|
|
60
|
+
turns: z.boolean().optional().describe("Also return 30-day JARVIS turn aggregates: latency, hops, tools, tokens and error classes."),
|
|
61
|
+
tool_router: z.boolean().optional().describe("Also return the seven-day shadow tool-router agreement board."),
|
|
60
62
|
},
|
|
61
63
|
}, async (args) => withSession(deps, async (session) => {
|
|
62
64
|
const params = new URLSearchParams();
|
|
@@ -64,6 +66,8 @@ export function registerOpsTools(server, deps) {
|
|
|
64
66
|
params.set("job", String(args.job));
|
|
65
67
|
if (args.skips)
|
|
66
68
|
params.set("skips", "1");
|
|
69
|
+
if (args.turns)
|
|
70
|
+
params.set("turns", "1");
|
|
67
71
|
const response = await callAgentDoor(session, deps.fetchImpl, "GET", `/api/ops/status${queryString(params)}`);
|
|
68
72
|
if (!response.ok)
|
|
69
73
|
return errorResult(doorFailureText("ops_status", response));
|
|
@@ -100,6 +104,11 @@ export function registerOpsTools(server, deps) {
|
|
|
100
104
|
? await callAgentDoor(session, deps.fetchImpl, "GET", "/api/ops/memory-usage") : null;
|
|
101
105
|
if (memory && !memory.ok)
|
|
102
106
|
return errorResult(doorFailureText("ops_status memory", memory));
|
|
107
|
+
const toolRouter = args.tool_router
|
|
108
|
+
? await callAgentDoor(session, deps.fetchImpl, "GET", "/api/ops/tool-router")
|
|
109
|
+
: null;
|
|
110
|
+
if (toolRouter && !toolRouter.ok)
|
|
111
|
+
return errorResult(doorFailureText("ops_status tool router", toolRouter));
|
|
103
112
|
const experience = memory?.ok ? asRecord(memory.body.experience) : null;
|
|
104
113
|
const experienceLines = experience ? ["EXPERIENCE", experience.summary, ...(Array.isArray(experience.lines) ? experience.lines : [])].join("\n") : "";
|
|
105
114
|
const hooks = memory?.ok ? asRecord(memory.body.hooks) : null;
|
|
@@ -108,6 +117,18 @@ export function registerOpsTools(server, deps) {
|
|
|
108
117
|
const row = asRecord(device);
|
|
109
118
|
return [row.line, row.performanceLine].filter((line) => typeof line === "string").join("\n");
|
|
110
119
|
}).join("\n") : "";
|
|
120
|
+
const turns = asRecord(body.turns);
|
|
121
|
+
const turnLine = args.turns
|
|
122
|
+
? typeof turns.turns === "number"
|
|
123
|
+
? `TURNS ${turns.turns} turns / ${typeof turns.windowDays === "number" ? turns.windowDays : 30}d`
|
|
124
|
+
: "TURNS not read (turns_section_absent)"
|
|
125
|
+
: "";
|
|
126
|
+
const toolRouterBoard = toolRouter?.ok ? asRecord(toolRouter.body.board) : null;
|
|
127
|
+
const toolRouterLine = args.tool_router
|
|
128
|
+
? toolRouterBoard && typeof toolRouterBoard.turnsRouted === "number"
|
|
129
|
+
? `TOOL ROUTER ${toolRouterBoard.turnsRouted} routed`
|
|
130
|
+
: "TOOL ROUTER not read (tool_router_section_absent)"
|
|
131
|
+
: "";
|
|
111
132
|
// Both branches say something. A board that is entirely green and
|
|
112
133
|
// reports nothing cannot answer "did anybody look today?".
|
|
113
134
|
return textResult(`${rows.length} pipeline(s), ${unhealthy.length} unhealthy`
|
|
@@ -115,7 +136,9 @@ export function registerOpsTools(server, deps) {
|
|
|
115
136
|
+ `${lines ? `\n${lines}` : ""}`
|
|
116
137
|
+ `${ladderLine ? `\n${ladderLine}` : ""}`
|
|
117
138
|
+ `${memoryLines ? `\n${memoryLines}` : ""}`
|
|
118
|
-
+ `${experienceLines ? `\n${experienceLines}` : ""}
|
|
139
|
+
+ `${experienceLines ? `\n${experienceLines}` : ""}`
|
|
140
|
+
+ `${turnLine ? `\n${turnLine}` : ""}`
|
|
141
|
+
+ `${toolRouterLine ? `\n${toolRouterLine}` : ""}`,
|
|
119
142
|
// The API owns the wording and the timing boundary on every surface.
|
|
120
143
|
{
|
|
121
144
|
pipelines: rows,
|
|
@@ -125,6 +148,8 @@ export function registerOpsTools(server, deps) {
|
|
|
125
148
|
...(body.skips ? { skips: body.skips } : {}),
|
|
126
149
|
...(body.ladder ? { ladder: body.ladder } : {}),
|
|
127
150
|
...(memory?.ok ? { memory: memory.body.memory, hooks: memory.body.hooks, experience: memory.body.experience } : {}),
|
|
151
|
+
...(args.turns ? { turns: body.turns ?? null } : {}),
|
|
152
|
+
...(toolRouter?.ok ? { toolRouter: toolRouter.body.board ?? null } : {}),
|
|
128
153
|
});
|
|
129
154
|
}));
|
|
130
155
|
register("slack_coverage", {
|
package/dist/verb-census.js
CHANGED
|
@@ -212,6 +212,12 @@ export const MCP_TWINS = {
|
|
|
212
212
|
"careers list": { tool: "careers_list", door: "GET /api/careers/applications" },
|
|
213
213
|
"careers show": { tool: "careers_show", door: "GET /api/careers/applications/[id]" },
|
|
214
214
|
"careers rescreen": { tool: "careers_rescreen", door: "POST /api/careers/applications/[id]/rescreen" },
|
|
215
|
+
"careers invite": { tool: "careers_invite", door: "POST /api/careers/applications/[id]/invite" },
|
|
216
|
+
"careers decide": { tool: "careers_decide", door: "POST /api/careers/applications/[id]/decide" },
|
|
217
|
+
"careers takehome-show": { tool: "careers_takehome_show", door: "GET /api/careers/takehomes/[role]" },
|
|
218
|
+
"careers takehome-set": { tool: "careers_takehome_set", door: "PUT /api/careers/takehomes/[role]" },
|
|
219
|
+
"careers settings-show": { tool: "careers_settings_show", door: "GET /api/careers/settings" },
|
|
220
|
+
"careers settings-set": { tool: "careers_settings_set", door: "PUT /api/careers/settings" },
|
|
215
221
|
"usage sessions": { tool: "usage_sessions", door: "GET /api/usage/sessions" },
|
|
216
222
|
"usage people": { tool: "usage_people", door: "GET /api/usage/people" },
|
|
217
223
|
"docs list": { tool: "docs_list", door: "GET /api/docs/documents" },
|
|
@@ -339,6 +345,10 @@ export const MCP_TWINS = {
|
|
|
339
345
|
* shorter this map is, the closer the census keys are to the product.
|
|
340
346
|
*/
|
|
341
347
|
export const TYPED_SPELLING = {
|
|
348
|
+
"careers takehome-show": "careers takehome show",
|
|
349
|
+
"careers takehome-set": "careers takehome set",
|
|
350
|
+
"careers settings-show": "careers settings show",
|
|
351
|
+
"careers settings-set": "careers settings set",
|
|
342
352
|
"settings show": "settings",
|
|
343
353
|
"settings list": "settings env list",
|
|
344
354
|
"settings set": "settings switches set",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.49",
|
|
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",
|