@opvs-ai/mcp 2.6.1 → 2.7.1

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/dist/fleet.js ADDED
@@ -0,0 +1,474 @@
1
+ /**
2
+ * Fleet tools — the MACHINE and SLOT surface (OSB-119, BYO plan row 3.2).
3
+ *
4
+ * Until this module the MCP package had 11 tools and NONE of them reached the
5
+ * environment layer, so the owner's "dashboard or MCP" was dashboard-only.
6
+ *
7
+ * Lives outside `index.ts` because that file ends in a top-level
8
+ * `await server.connect(transport)` — importing it to unit-test a handler would
9
+ * start a server. See `http.ts` for the same reasoning.
10
+ */
11
+ import { err, getJson, ok, postJson } from "./http.js";
12
+ export const FLEET_TOOLS = [
13
+ //
14
+ // VOCABULARY IS RULED, NOT FREE. `MACHINE-AND-SLOT.md` §3 (owner ruling
15
+ // 2026-09-07) settles the product surface on exactly two nouns: a MACHINE is
16
+ // the host you connect, a SLOT is the worker sitting at a desk on it. "box"
17
+ // and "desk" are retired from new writing, and this is new writing. The wire
18
+ // keeps saying `environments` — that disagreement is deliberate and is
19
+ // recorded there, not re-litigated here.
20
+ //
21
+ // Every description below therefore names the wire word and the retired
22
+ // synonyms explicitly, so an agent that was told "list my environments" or
23
+ // "check my box" still finds the right tool. Discoverability is paid for in
24
+ // the description; the NAME follows the ruling.
25
+ {
26
+ name: "fleet_list",
27
+ description: "List every SLOT this brand owns (a slot = one worker desk = one row of the " +
28
+ "`environments` table; also called an environment, box, desk, machine or " +
29
+ "workstation elsewhere), together with the machine allowance it spends. ONE " +
30
+ "call replaces list + allowance + a per-slot fetch. Slots are also grouped " +
31
+ "under their MACHINE (the physical host, keyed by `machine_id`) so co-tenancy " +
32
+ "is visible. Slots whose machine has not identified itself are listed under " +
33
+ "`unplaced` and are NEVER grouped together — two boxes that both decline to " +
34
+ "say where they run are not thereby on the same host. Brand is pinned " +
35
+ "server-side from the PAT — never pass a brand_id; `brand_id` is echoed back " +
36
+ "so an empty fleet says WHOSE fleet is empty. Read-only. `detail=compact` " +
37
+ "(default) emits id, short_id, name, status, dormant, runtime, machine_id, " +
38
+ "sidecar_port, wg_address, sidecar_version and the three freshness fields; " +
39
+ "`full` passes every server field through untouched.",
40
+ inputSchema: {
41
+ type: "object",
42
+ properties: {
43
+ detail: {
44
+ type: "string",
45
+ enum: ["compact", "full"],
46
+ description: "Row depth. Default `compact` (the decision fields, token-cheapest). " +
47
+ "`full` returns each slot exactly as the server sent it.",
48
+ },
49
+ },
50
+ },
51
+ },
52
+ {
53
+ name: "slot_get",
54
+ description: "Fetch ONE slot (environment / box / desk / workstation) plus its live " +
55
+ "ONBOARDING CHECKLIST — the four steps run_installer → wg_register → " +
56
+ "sidecar_up → ready, each with its OWN state (pending | done | failed | " +
57
+ "blocked), a machine-readable `reason` and an operator `hint`. This is the " +
58
+ "tool that answers 'why is my machine stuck?': it also returns " +
59
+ "`next_action`, the single step to act on, so a stalled desk reads as one " +
60
+ "line rather than four competing ones. Accepts ANY ref shape — the full " +
61
+ "UUID, an 8+ hex prefix, or the `env_…` short code — and canonicalizes it " +
62
+ "for you. Read-only; touches no box. Use `slot_health` for a live probe.",
63
+ inputSchema: {
64
+ type: "object",
65
+ properties: {
66
+ slot: {
67
+ type: "string",
68
+ description: "The slot ref: full UUID, an 8-or-more hex prefix, or the `env_…` " +
69
+ "short code. From `fleet_list`.",
70
+ },
71
+ },
72
+ required: ["slot"],
73
+ },
74
+ },
75
+ {
76
+ name: "slot_health",
77
+ description: "LIVE health probe of one slot's sidecar — this call reaches out to the " +
78
+ "actual box over WireGuard, so it is slower than `slot_get` and can time " +
79
+ "out when the machine is down. Returns the stored status alongside " +
80
+ "`sidecar_reachable` and the version + code digest the sidecar reports " +
81
+ "about ITSELF right now (which may differ from the copy stored on the row). " +
82
+ "`sidecar_reachable: false` is a real answer, not a failure — it means the " +
83
+ "box did not respond. Accepts any ref shape (UUID, 8+ hex prefix, `env_…`).",
84
+ inputSchema: {
85
+ type: "object",
86
+ properties: {
87
+ slot: {
88
+ type: "string",
89
+ description: "The slot ref — UUID, 8+ hex prefix, or `env_…` short code.",
90
+ },
91
+ },
92
+ required: ["slot"],
93
+ },
94
+ },
95
+ {
96
+ name: "fleet_logins",
97
+ description: "Which logins have lapsed across the whole fleet, on BOTH credential lanes " +
98
+ "— they are different lanes and are reported separately ON PURPOSE. " +
99
+ "`lease` is the SpiderGate BUILD credential (when it lapses a build falls " +
100
+ "back to metered pricing); `desk` is what the box's TERMINAL actually opens " +
101
+ "(when it lapses the desk will not start). A box can be perfect on one and " +
102
+ "unusable on the other. Desk state is FOUR-valued (healthy | " +
103
+ "not_applicable | unknown | lapsed) and must be rendered as four — " +
104
+ "'nobody asked OPVS to manage this desk' and 'OPVS was asked and cannot " +
105
+ "vouch for it' are different sentences. The two lanes' attention counts are " +
106
+ "returned separately and are never summed: a single total cannot say which " +
107
+ "lane broke. `checked` is the denominator; `dormant` counts boxes skipped " +
108
+ "because nothing has been heard from them. Read-only, fleet-wide, no params.",
109
+ inputSchema: {
110
+ type: "object",
111
+ properties: {},
112
+ },
113
+ },
114
+ {
115
+ name: "slot_order",
116
+ description: "ORDER A NEW SLOT — the self-serve onboarding door (the MCP equivalent of " +
117
+ "the dashboard's '+ HIRE'). ⚠️ THIS WRITES: it spends one unit of the " +
118
+ "brand's machine allowance (check `fleet_list.allowance` first — a full " +
119
+ "allowance is refused with 402/403) and it mints a ONE-SHOT install PAT. " +
120
+ "That PAT and the `install_command` one-liner that embeds it are returned " +
121
+ "EXACTLY ONCE, here, and are never retrievable again — hand them straight " +
122
+ "to the operator to paste onto the target VPS. Nothing is provisioned by " +
123
+ "this call; it creates the record and the installer command. The box goes " +
124
+ "`provisioning` → `ready` on its own once the one-liner has run and the " +
125
+ "sidecar registers. Poll `slot_get` to watch the checklist advance.",
126
+ inputSchema: {
127
+ type: "object",
128
+ properties: {
129
+ name: {
130
+ type: "string",
131
+ description: "Human name for the slot, 1-128 chars. E.g. 'frontend-1'.",
132
+ },
133
+ runtime: {
134
+ type: "string",
135
+ enum: ["claude_code", "codex", "custom"],
136
+ description: "Agent runtime installed on the slot. Default `claude_code`. " +
137
+ "`custom` installs the sidecar but auto-launches nothing.",
138
+ },
139
+ region: {
140
+ type: "string",
141
+ description: "Preferred region hint (advisory only — nothing enforces it).",
142
+ },
143
+ description: {
144
+ type: "string",
145
+ description: "Optional free-text note, max 1000 chars.",
146
+ },
147
+ },
148
+ required: ["name"],
149
+ },
150
+ },
151
+ ];
152
+ // ── Fleet handlers — MACHINES and SLOTS (OSB-119) ─────────────────────
153
+ /** The slot fields a compact fleet row carries.
154
+ *
155
+ * AN ENUMERATION IS ITSELF A CLAIM, so this list is exported into the
156
+ * `fleet_list` description verbatim and `detail: "full"` exists to escape it.
157
+ * A trim that nobody can see is indistinguishable from a field the server
158
+ * stopped sending.
159
+ */
160
+ export const COMPACT_SLOT_FIELDS = [
161
+ "id",
162
+ "short_id",
163
+ "name",
164
+ "status",
165
+ "dormant",
166
+ "runtime",
167
+ "machine_id",
168
+ "sidecar_port",
169
+ "wg_address",
170
+ "sidecar_version",
171
+ "last_health_check",
172
+ "last_sync_at",
173
+ "pending_sync_count",
174
+ ];
175
+ export function compactSlot(row) {
176
+ const out = {};
177
+ for (const f of COMPACT_SLOT_FIELDS) {
178
+ if (row[f] !== undefined)
179
+ out[f] = row[f];
180
+ }
181
+ return out;
182
+ }
183
+ /** Group slots under the machine that reported hosting them.
184
+ *
185
+ * 🔴 A NULL `machine_id` is NOT a group. The column is written by the health
186
+ * poller from `/etc/machine-id` and is NULL whenever the sidecar is too old to
187
+ * report it — which is still the common case. Two boxes that both decline to
188
+ * say where they run are not thereby co-tenants, and collapsing them would
189
+ * invent a co-tenancy the platform has no evidence for
190
+ * (`schemas/environment.py`, `machine_id`). They go to `unplaced`, one entry
191
+ * each, in list order.
192
+ */
193
+ export function groupByMachine(rows) {
194
+ const byMachine = new Map();
195
+ const unplaced = [];
196
+ for (const row of rows) {
197
+ const label = typeof row.name === "string" && row.name ? row.name : String(row.id ?? "?");
198
+ const ref = `${String(row.id ?? "?")} (${label})`;
199
+ const mid = row.machine_id;
200
+ if (typeof mid === "string" && mid.length > 0) {
201
+ const bucket = byMachine.get(mid);
202
+ if (bucket)
203
+ bucket.push(ref);
204
+ else
205
+ byMachine.set(mid, [ref]);
206
+ }
207
+ else {
208
+ unplaced.push(ref);
209
+ }
210
+ }
211
+ return {
212
+ machines: [...byMachine.entries()].map(([machine_id, slots]) => ({
213
+ machine_id,
214
+ slot_count: slots.length,
215
+ slots,
216
+ })),
217
+ unplaced,
218
+ };
219
+ }
220
+ export async function handleFleetList(args) {
221
+ if (args.detail != null && !["compact", "full"].includes(args.detail)) {
222
+ return err(`invalid detail '${args.detail}'`, "#audit.error:input=bad-detail — detail must be compact | full. " +
223
+ "#next:omit it for the default (compact).");
224
+ }
225
+ const full = args.detail === "full";
226
+ let fleet;
227
+ try {
228
+ fleet = await getJson("/api/v1/environments");
229
+ }
230
+ catch (e) {
231
+ const error = e;
232
+ if (error.status === 401 || error.status === 403) {
233
+ return err(`fleet_list forbidden: ${error.detail ?? "not authorized for this brand"}`, "#audit.error:auth #next:whoami to confirm which brand this PAT is pinned to.");
234
+ }
235
+ return err(`fleet_list failed: ${error.status ?? "?"} ${error.detail ?? ""}`, "#audit.error:upstream #next:whoami to verify auth; check the API is reachable.");
236
+ }
237
+ const rows = Array.isArray(fleet.environments) ? fleet.environments : [];
238
+ // The allowance is a SECOND upstream read and is allowed to fail on its own.
239
+ // A fleet listing is still useful without the cap, so a failure here is
240
+ // REPORTED rather than swallowed into a null: `allowance: null` alone would
241
+ // be indistinguishable from an uncapped brand, which is the one reading that
242
+ // must not be invented (`EnvironmentAllowance` carries `unlimited` explicitly
243
+ // for exactly this reason).
244
+ let allowance = null;
245
+ let allowanceError = null;
246
+ try {
247
+ allowance = await getJson("/api/v1/environments/allowance");
248
+ }
249
+ catch (e) {
250
+ const error = e;
251
+ allowanceError = `${error.status ?? "?"} ${error.detail ?? "allowance read failed"}`;
252
+ }
253
+ const grouped = groupByMachine(rows);
254
+ const body = {
255
+ // Echoed from the SERVER, never from a client guess: `get_current_brand`
256
+ // may settle on a different brand than the caller assumed, and an empty
257
+ // list has to be able to say whose fleet is empty (OSB-164).
258
+ brand_id: fleet.brand_id ?? null,
259
+ total: fleet.total ?? rows.length,
260
+ slots: full ? rows : rows.map(compactSlot),
261
+ machines: grouped.machines,
262
+ unplaced: grouped.unplaced,
263
+ allowance,
264
+ };
265
+ if (allowanceError) {
266
+ body.allowance_error = allowanceError;
267
+ body._guidance =
268
+ "#audit.partial:allowance — the fleet listing is complete; the machine " +
269
+ "allowance could not be read, so `allowance` is null because it is UNKNOWN, " +
270
+ "not because the brand is uncapped. Do not tell the operator they have room.";
271
+ }
272
+ return ok(body);
273
+ }
274
+ /** Resolve any slot ref (UUID | 8+ hex prefix | `env_…` short code) to the
275
+ * canonical environment row.
276
+ *
277
+ * 🔑 THIS EXISTS BECAUSE THE TWO ROUTES DISAGREE ABOUT REF SHAPES.
278
+ * `GET /environments/{ref}` funnels through `_resolve_env`, which accepts all
279
+ * three. `GET /workstations/{id}` gates on `_is_uuid` and returns 404 for
280
+ * anything else — so an agent that reads `short_id` out of `fleet_list` and
281
+ * hands it to the checklist route gets "not found" for a slot that plainly
282
+ * exists. Resolving here, once, and then using the row's own `id` for the
283
+ * second hop is what keeps `slot_get` honest about which refs it accepts.
284
+ */
285
+ export async function resolveSlot(ref) {
286
+ try {
287
+ const row = await getJson(`/api/v1/environments/${encodeURIComponent(ref)}`);
288
+ return { ok: true, row };
289
+ }
290
+ catch (e) {
291
+ const error = e;
292
+ if (error.status === 404) {
293
+ return {
294
+ ok: false,
295
+ result: err(`slot '${ref}' not found (or not owned by this brand)`, "#audit.error:slot=missing #next:fleet_list to see this brand's slots; " +
296
+ "whoami to confirm which brand the PAT is pinned to. Any of the full " +
297
+ "UUID, an 8+ hex prefix or the `env_…` short code is accepted."),
298
+ };
299
+ }
300
+ if (error.status === 409) {
301
+ return {
302
+ ok: false,
303
+ result: err(`slot ref '${ref}' is ambiguous: ${error.detail ?? "matches more than one slot"}`, "#audit.error:slot=ambiguous #next:lengthen the hex prefix, or use the " +
304
+ "full UUID / `env_…` short code from fleet_list. Retrying the same ref fails identically."),
305
+ };
306
+ }
307
+ if (error.status === 401 || error.status === 403) {
308
+ return {
309
+ ok: false,
310
+ result: err(`slot '${ref}' forbidden: ${error.detail ?? "not authorized for this brand"}`, "#audit.error:auth #next:whoami to confirm the PAT's brand."),
311
+ };
312
+ }
313
+ return {
314
+ ok: false,
315
+ result: err(`slot lookup failed: ${error.status ?? "?"} ${error.detail ?? ""}`, "#audit.error:upstream #next:whoami to verify auth; check the API is reachable."),
316
+ };
317
+ }
318
+ }
319
+ /** The one step an operator should act on: the first that is not `done`.
320
+ *
321
+ * Everything after it is `blocked_by:<key>` by construction (OSB-113), so
322
+ * picking the first non-done step is what turns four competing lines into one.
323
+ * Returns null when every step is done — a genuinely ready slot.
324
+ */
325
+ export function nextAction(steps) {
326
+ const pending = steps.find((s) => s.state !== "done");
327
+ if (!pending)
328
+ return null;
329
+ return {
330
+ key: pending.key ?? null,
331
+ state: pending.state ?? null,
332
+ reason: pending.reason ?? null,
333
+ hint: pending.hint ?? null,
334
+ };
335
+ }
336
+ export async function handleSlotGet(args) {
337
+ if (!args.slot || typeof args.slot !== "string") {
338
+ return err("slot is required", "#audit.error:input=missing — slot_get needs a slot ref (UUID, 8+ hex " +
339
+ "prefix, or `env_…` short code). #next:fleet_list to find one.");
340
+ }
341
+ const resolved = await resolveSlot(args.slot);
342
+ if (!resolved.ok)
343
+ return resolved.result;
344
+ const row = resolved.row;
345
+ // Second hop, by CANONICAL UUID — see `resolveSlot`. The onboarding checklist
346
+ // hangs off the workstation view, which is the same `environments` row mapped
347
+ // through `_to_view`; a workstation id IS an env id.
348
+ const canonical = typeof row.id === "string" ? row.id : args.slot;
349
+ const body = { slot: row };
350
+ try {
351
+ const view = await getJson(`/api/v1/workstations/${encodeURIComponent(canonical)}`);
352
+ const steps = Array.isArray(view.onboarding_steps) ? view.onboarding_steps : [];
353
+ body.onboarding = {
354
+ ready: view.ready ?? null,
355
+ steps,
356
+ next_action: nextAction(steps),
357
+ };
358
+ }
359
+ catch (e) {
360
+ // The slot itself resolved, so returning it without the checklist beats
361
+ // failing the whole call — but the gap is NAMED. A missing `onboarding` key
362
+ // that the caller has to notice is how a checklist comes to read as "no
363
+ // steps outstanding".
364
+ const error = e;
365
+ body.onboarding = null;
366
+ body.onboarding_error = `${error.status ?? "?"} ${error.detail ?? "checklist read failed"}`;
367
+ body._guidance =
368
+ "#audit.partial:onboarding — the slot resolved but its checklist could not " +
369
+ "be read. `onboarding: null` means UNKNOWN, not 'nothing outstanding'.";
370
+ }
371
+ return ok(body);
372
+ }
373
+ export async function handleSlotHealth(args) {
374
+ if (!args.slot || typeof args.slot !== "string") {
375
+ return err("slot is required", "#audit.error:input=missing — slot_health needs a slot ref (UUID, 8+ hex " +
376
+ "prefix, or `env_…` short code). #next:fleet_list to find one.");
377
+ }
378
+ try {
379
+ const data = await getJson(`/api/v1/environments/${encodeURIComponent(args.slot)}/health`);
380
+ return ok(data);
381
+ }
382
+ catch (e) {
383
+ const error = e;
384
+ if (error.status === 404) {
385
+ return err(`slot '${args.slot}' not found (or not owned by this brand)`, "#audit.error:slot=missing #next:fleet_list to see this brand's slots.");
386
+ }
387
+ if (error.status === 409) {
388
+ return err(`slot ref '${args.slot}' is ambiguous: ${error.detail ?? "matches more than one slot"}`, "#audit.error:slot=ambiguous #next:lengthen the prefix or use the full UUID.");
389
+ }
390
+ // A DOWN BOX IS NOT AN ERROR OF THIS TOOL. The route probes the sidecar over
391
+ // WireGuard, so a dead machine surfaces as an upstream failure here rather
392
+ // than as `sidecar_reachable: false` — say which of the two it was, because
393
+ // "the API is broken" and "the box is off" call for different actions.
394
+ return err(`slot_health failed: ${error.status ?? "?"} ${error.detail ?? ""}`, "#audit.error:upstream — this call reaches the box itself, so a timeout " +
395
+ "here usually means the MACHINE is unreachable, not that OPVS is down. " +
396
+ "#next:slot_get for the stored status + onboarding checklist, which touches no box.");
397
+ }
398
+ }
399
+ export async function handleFleetLogins() {
400
+ try {
401
+ const data = await getJson("/api/v1/environments/credentials/reauth-status");
402
+ return ok({
403
+ // Passed through by LANE, deliberately un-merged. The two counts answer
404
+ // different questions and the schema is explicit that a rail which adds
405
+ // them cannot say which lane broke.
406
+ lease: {
407
+ flagged: data.environments ?? [],
408
+ total_providers: data.total_providers ?? 0,
409
+ },
410
+ desk: {
411
+ rows: data.desk ?? [],
412
+ needs_attention: data.desk_needs_attention ?? 0,
413
+ },
414
+ checked: data.checked ?? 0,
415
+ // Skipped boxes are COUNTED rather than dropped: `checked` is the
416
+ // denominator every figure above is read against, and shrinking it
417
+ // silently would turn "all fine" into a claim about a fleet that was
418
+ // never examined (OSB-158).
419
+ dormant: data.dormant ?? 0,
420
+ });
421
+ }
422
+ catch (e) {
423
+ const error = e;
424
+ if (error.status === 401 || error.status === 403) {
425
+ return err(`fleet_logins forbidden: ${error.detail ?? "not authorized for this brand"}`, "#audit.error:auth #next:whoami to confirm the PAT's brand.");
426
+ }
427
+ return err(`fleet_logins failed: ${error.status ?? "?"} ${error.detail ?? ""}`, "#audit.error:upstream #next:whoami to verify auth; check the API is reachable.");
428
+ }
429
+ }
430
+ export const SLOT_RUNTIMES = ["claude_code", "codex", "custom"];
431
+ export async function handleSlotOrder(args) {
432
+ if (!args.name || typeof args.name !== "string" || args.name.trim() === "") {
433
+ return err("name is required", "#audit.error:input=missing — slot_order needs a human name for the slot, " +
434
+ "1-128 chars. Nothing is created without it.");
435
+ }
436
+ if (args.runtime != null && !SLOT_RUNTIMES.includes(args.runtime)) {
437
+ // Refused HERE rather than forwarded, because the upstream 422 arrives after
438
+ // the allowance gate has already been consulted and reads as a server fault.
439
+ return err(`invalid runtime '${args.runtime}'`, `#audit.error:input=bad-runtime — runtime must be one of ${SLOT_RUNTIMES.join(" | ")}. #next:omit it for the default (claude_code). Note \`gemini-cli\` and ` +
440
+ "`openhands` are NOT slot runtimes — they exist on other axes.");
441
+ }
442
+ const body = { name: args.name };
443
+ if (args.runtime)
444
+ body.runtime = args.runtime;
445
+ if (args.region)
446
+ body.region = args.region;
447
+ if (args.description)
448
+ body.description = args.description;
449
+ try {
450
+ const data = await postJson("/api/v1/workstations", body);
451
+ return ok({
452
+ ...data,
453
+ _guidance: "#audit.write:slot-ordered — `pat` and `install_command` are ONE-SHOT and " +
454
+ "are not retrievable again from any endpoint. Hand the one-liner to the " +
455
+ "operator now; if it is lost, rotate with a fresh PAT rather than " +
456
+ "re-ordering (a second order spends the allowance again). Nothing is " +
457
+ "installed yet — poll slot_get to watch the checklist advance.",
458
+ });
459
+ }
460
+ catch (e) {
461
+ const error = e;
462
+ if (error.status === 402 || error.status === 403) {
463
+ return err(`slot_order denied: ${error.detail ?? "machine allowance exhausted"}`, "#audit.error:quota — ORDER and ATTACH spend the SAME allowance; there is " +
464
+ "one cap, not two. #next:fleet_list to read `allowance`, then release an " +
465
+ "unused slot or raise the plan's `max_environments_per_brand`.");
466
+ }
467
+ if (error.status === 400 || error.status === 422) {
468
+ return err(`slot_order rejected: ${error.detail ?? "invalid request"}`, "#audit.error:input — check `name` (1-128 chars) and `description` (max 1000).");
469
+ }
470
+ return err(`slot_order failed: ${error.status ?? "?"} ${error.detail ?? ""}`, "#audit.error:upstream — a failure here may still have created the slot. " +
471
+ "#next:fleet_list BEFORE retrying, or you will spend the allowance twice.");
472
+ }
473
+ }
474
+ //# sourceMappingURL=fleet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fleet.js","sourceRoot":"","sources":["../src/fleet.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Shared transport + response helpers for the OPVS MCP server.
3
+ *
4
+ * Carved out of `index.ts` (OSB-119) for one reason: `index.ts` ends in a
5
+ * top-level `await server.connect(transport)`, so importing it to test anything
6
+ * STARTS A SERVER. Any unit test of a handler therefore needs the handler to
7
+ * live somewhere that can be imported without that side effect. This module and
8
+ * `fleet.ts` are that somewhere; the move is pure, with no behaviour change.
9
+ */
10
+ import { createClient } from "@opvs-ai/core";
11
+ export type ToolResult = {
12
+ content: Array<{
13
+ type: "text";
14
+ text: string;
15
+ }>;
16
+ };
17
+ export declare function ok(body: unknown): ToolResult;
18
+ /** Standard error envelope: flat-sibling `_guidance` (W1.A discipline,
19
+ * confirmed with SpiderIQ 2026-05-15 PM). Never wrap. */
20
+ export declare function err(message: string, guidance?: string): ToolResult;
21
+ /**
22
+ * `err` plus machine-readable fields, as FLAT SIBLINGS of `error` — the same
23
+ * W1.A discipline `_guidance` follows. Never wrap: a nested `details: {...}`
24
+ * would make the agent reach one level deeper for exactly the values the
25
+ * refusal exists to hand it.
26
+ *
27
+ * For refusals an agent must ACT on rather than merely report. A 413 whose
28
+ * numbers live only inside an English sentence forces the model to parse prose
29
+ * to decide what to do next, which is the failure this envelope removes
30
+ * (OSB-207). `error` and `_guidance` are reserved and cannot be overwritten.
31
+ */
32
+ export declare function errFields(message: string, fields: Record<string, unknown>, guidance?: string): ToolResult;
33
+ export declare function client(): ReturnType<typeof createClient>;
34
+ export declare function getJson<T = unknown>(path: string): Promise<T>;
35
+ export declare function postJson<T = unknown>(path: string, body?: unknown): Promise<T>;
36
+ /** GET a path and return the raw response body as text (no JSON parse). Used
37
+ * for endpoints that emit Markdown / plain text such as the board export. */
38
+ export declare function getText(path: string): Promise<string>;
39
+ export declare function del204(path: string): Promise<void>;
package/dist/http.js ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Shared transport + response helpers for the OPVS MCP server.
3
+ *
4
+ * Carved out of `index.ts` (OSB-119) for one reason: `index.ts` ends in a
5
+ * top-level `await server.connect(transport)`, so importing it to test anything
6
+ * STARTS A SERVER. Any unit test of a handler therefore needs the handler to
7
+ * live somewhere that can be imported without that side effect. This module and
8
+ * `fleet.ts` are that somewhere; the move is pure, with no behaviour change.
9
+ */
10
+ import { createClient, getResponseText } from "@opvs-ai/core";
11
+ export function ok(body) {
12
+ const text = typeof body === "string" ? body : JSON.stringify(body);
13
+ return { content: [{ type: "text", text }] };
14
+ }
15
+ /** Standard error envelope: flat-sibling `_guidance` (W1.A discipline,
16
+ * confirmed with SpiderIQ 2026-05-15 PM). Never wrap. */
17
+ export function err(message, guidance) {
18
+ const body = { error: message };
19
+ if (guidance)
20
+ body._guidance = guidance;
21
+ return { content: [{ type: "text", text: JSON.stringify(body) }] };
22
+ }
23
+ /**
24
+ * `err` plus machine-readable fields, as FLAT SIBLINGS of `error` — the same
25
+ * W1.A discipline `_guidance` follows. Never wrap: a nested `details: {...}`
26
+ * would make the agent reach one level deeper for exactly the values the
27
+ * refusal exists to hand it.
28
+ *
29
+ * For refusals an agent must ACT on rather than merely report. A 413 whose
30
+ * numbers live only inside an English sentence forces the model to parse prose
31
+ * to decide what to do next, which is the failure this envelope removes
32
+ * (OSB-207). `error` and `_guidance` are reserved and cannot be overwritten.
33
+ */
34
+ export function errFields(message, fields, guidance) {
35
+ const body = { error: message };
36
+ for (const [k, v] of Object.entries(fields)) {
37
+ if (k === "error" || k === "_guidance")
38
+ continue;
39
+ body[k] = v;
40
+ }
41
+ if (guidance)
42
+ body._guidance = guidance;
43
+ return { content: [{ type: "text", text: JSON.stringify(body) }] };
44
+ }
45
+ // ── API client ────────────────────────────────────────────────────────
46
+ // The explicit return type is load-bearing: `createClient` returns an anonymous
47
+ // class with private fields, which TypeScript refuses to export inferentially
48
+ // (TS4094). Naming it via ReturnType keeps the export legal without widening it.
49
+ export function client() {
50
+ return createClient();
51
+ }
52
+ export async function getJson(path) {
53
+ const res = await client().get(path);
54
+ const text = await getResponseText(res);
55
+ try {
56
+ return JSON.parse(text);
57
+ }
58
+ catch {
59
+ return text;
60
+ }
61
+ }
62
+ export async function postJson(path, body) {
63
+ const res = await client().post(path, body);
64
+ const text = await getResponseText(res);
65
+ try {
66
+ return JSON.parse(text);
67
+ }
68
+ catch {
69
+ return text;
70
+ }
71
+ }
72
+ /** GET a path and return the raw response body as text (no JSON parse). Used
73
+ * for endpoints that emit Markdown / plain text such as the board export. */
74
+ export async function getText(path) {
75
+ const res = await client().get(path);
76
+ return getResponseText(res);
77
+ }
78
+ export async function del204(path) {
79
+ // The OPVS DELETE endpoints we hit return 204 No Content; createClient
80
+ // throws on non-2xx, so reaching here = success.
81
+ await client().delete(path);
82
+ }
83
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}