vessels-mcp 0.2.0 → 0.3.0

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.
Files changed (2) hide show
  1. package/dist/index.js +61 -6
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/index.ts
4
4
  import { readFileSync } from "fs";
5
5
  import { homedir } from "os";
6
- import { join } from "path";
6
+ import { join, basename } from "path";
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
9
  import { z } from "zod";
@@ -11,15 +11,22 @@ import { Vessels } from "vessels-sdk";
11
11
  function resolveKey() {
12
12
  const env = process.env.VESSELS_API_KEY;
13
13
  if (env && !env.startsWith("${")) return env;
14
- try {
15
- const k = readFileSync(join(homedir(), ".thru-do-vessels-key"), "utf8").trim();
16
- if (k) return k;
17
- } catch {
14
+ for (const p of [
15
+ join(homedir(), ".config", "vessels-mcp", "key"),
16
+ join(homedir(), ".thru-do-vessels-key")
17
+ // legacy path — kept for back-compat
18
+ ]) {
19
+ try {
20
+ const k = readFileSync(p, "utf8").trim();
21
+ if (k) return k;
22
+ } catch {
23
+ }
18
24
  }
19
25
  return void 0;
20
26
  }
21
27
  var apiKey = resolveKey();
22
- var defaultVessel = process.env.VESSELS_VESSEL ?? "claude-code";
28
+ var cwdName = basename(process.cwd()) || "workspace";
29
+ var defaultVessel = process.env.VESSELS_VESSEL ?? `claude-code-${cwdName}`;
23
30
  var vesselTitle = process.env.VESSELS_VESSEL_TITLE ?? "Claude Code";
24
31
  var baseUrl = process.env.VESSELS_BASE_URL ?? "https://vessels.app";
25
32
  if (!apiKey) {
@@ -305,5 +312,53 @@ server.registerTool(
305
312
  return ok("ok");
306
313
  }
307
314
  );
315
+ server.registerTool(
316
+ "update_vessel",
317
+ {
318
+ title: "Update a vessel",
319
+ description: "Edit vessel-level state WITHOUT posting a message: title, labels (replaces the whole set), status, archived, or pinned (pin to top of the list). Use list_vessels to find the external_id.",
320
+ inputSchema: {
321
+ vessel: z.string().describe("Vessel external_id."),
322
+ title: z.string().optional(),
323
+ labels: z.array(z.string()).optional().describe("Replace the vessel labels (full set)."),
324
+ status: z.enum(["active", "waiting", "resolved"]).optional(),
325
+ archived: z.boolean().optional().describe("Archive (true) or unarchive (false)."),
326
+ pinned: z.boolean().optional().describe("Pin (true) / unpin (false) to the top of the list.")
327
+ }
328
+ },
329
+ async ({ vessel, title, labels, status, archived, pinned }) => {
330
+ const body = {
331
+ ...title !== void 0 ? { title } : {},
332
+ ...labels !== void 0 ? { labels } : {},
333
+ ...status !== void 0 ? { vesselStatus: status } : {},
334
+ ...archived !== void 0 ? { archived } : {},
335
+ ...pinned !== void 0 ? { pinned } : {}
336
+ };
337
+ if (!Object.keys(body).length) return ok("error: nothing to update");
338
+ const res = await fetch(`${baseUrl}/api/v1/vessels/by-external/${encodeURIComponent(vessel)}`, {
339
+ method: "PATCH",
340
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
341
+ body: JSON.stringify(body)
342
+ });
343
+ const data = await res.json();
344
+ return ok(res.ok ? "ok" : `error: ${data.error ?? res.status}`);
345
+ }
346
+ );
347
+ server.registerTool(
348
+ "delete_vessel",
349
+ {
350
+ title: "Delete a vessel (permanent)",
351
+ description: "Permanently delete a vessel and all its messages (cascade). IRREVERSIBLE \u2014 prefer update_vessel with archived:true unless you truly mean to destroy it.",
352
+ inputSchema: { vessel: z.string().describe("Vessel external_id to delete.") }
353
+ },
354
+ async ({ vessel }) => {
355
+ const res = await fetch(`${baseUrl}/api/v1/vessels/by-external/${encodeURIComponent(vessel)}`, {
356
+ method: "DELETE",
357
+ headers: { Authorization: `Bearer ${apiKey}` }
358
+ });
359
+ const data = await res.json();
360
+ return ok(res.ok ? `deleted ${vessel}` : `error: ${data.error ?? res.status}`);
361
+ }
362
+ );
308
363
  await server.connect(new StdioServerTransport());
309
364
  console.error(`[vessels-mcp] ready \u2014 default vessel "${defaultVessel}" @ ${baseUrl}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vessels-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Vessels MCP server — give any MCP client (Claude Code, Cursor, …) a tool to message a human and BLOCK until they answer.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,7 +20,7 @@
20
20
  "dependencies": {
21
21
  "@modelcontextprotocol/sdk": "^1.0.0",
22
22
  "zod": "^3.22",
23
- "vessels-sdk": "0.16.0"
23
+ "vessels-sdk": "^0.18.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "tsup": "^8.5.1",