vessels-mcp 0.4.0 → 0.5.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.
- package/dist/index.js +66 -27
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -75,7 +75,7 @@ async function ask(vessel, prompt, build, expectedType) {
|
|
|
75
75
|
if (!r.hasMore) await sleep(2e3);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
var server = new McpServer({ name: "vessels", version: "0.
|
|
78
|
+
var server = new McpServer({ name: "vessels", version: "0.5.0" });
|
|
79
79
|
server.registerTool(
|
|
80
80
|
"list_vessels",
|
|
81
81
|
{
|
|
@@ -87,16 +87,17 @@ server.registerTool(
|
|
|
87
87
|
}
|
|
88
88
|
},
|
|
89
89
|
async ({ limit, archived }) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
let vessels;
|
|
91
|
+
try {
|
|
92
|
+
vessels = await v.listVessels({ archived: !!archived });
|
|
93
|
+
} catch (e) {
|
|
94
|
+
return ok(`error: ${e.message}`);
|
|
95
|
+
}
|
|
96
|
+
const rows = vessels.slice(0, limit ?? 30);
|
|
96
97
|
if (!rows.length) return ok("(no vessels)");
|
|
97
98
|
const lines = rows.map((x) => {
|
|
98
99
|
const labels = (x.labels ?? []).length ? ` [${x.labels.join(",")}]` : "";
|
|
99
|
-
return `${x.
|
|
100
|
+
return `${x.externalId} \xB7${x.status}${x.pinned ? " \xB7pinned" : ""} "${x.title ?? ""}"${labels}`;
|
|
100
101
|
});
|
|
101
102
|
return ok(lines.join("\n"));
|
|
102
103
|
}
|
|
@@ -142,13 +143,23 @@ server.registerTool(
|
|
|
142
143
|
"send",
|
|
143
144
|
{
|
|
144
145
|
title: "Send a message or artifact",
|
|
145
|
-
description: 'Post a message to a vessel. For a chat reply pass `message`. For a full-width artifact (proposal, report, diff, review) pass `body` (block markdown: headings/lists/tables/quotes) and optionally `title` + `card`. Also supports `
|
|
146
|
+
description: 'Post a message to a vessel. For a chat reply pass `message`. For a full-width artifact (proposal, report, diff, review) pass `body` (block markdown: headings/lists/tables/quotes) and optionally `title` + `card`. Also supports `details` (persistent vessel reference record), `labels`, `suggestions` (quick-reply chips), `previewUrl` (a link card), and `status` to set the vessel state. Returns the new message id as "mid:<id>".',
|
|
146
147
|
inputSchema: {
|
|
147
148
|
message: z.string().optional().describe("Chat-bubble text (use this OR body)."),
|
|
148
149
|
body: z.string().optional().describe("Surface artifact body \u2014 block markdown."),
|
|
149
150
|
title: z.string().optional().describe("Surface heading."),
|
|
150
151
|
card: z.object({ title: z.string().optional(), fields: z.array(z.object({ label: z.string(), value: z.string() })) }).optional().describe("Glance-facts card (label/value rows)."),
|
|
151
|
-
|
|
152
|
+
details: z.object({
|
|
153
|
+
fields: z.array(
|
|
154
|
+
z.object({
|
|
155
|
+
label: z.string(),
|
|
156
|
+
value: z.string(),
|
|
157
|
+
url: z.string().optional(),
|
|
158
|
+
tone: z.enum(["default", "success", "warning", "danger"]).optional(),
|
|
159
|
+
copyable: z.boolean().optional()
|
|
160
|
+
})
|
|
161
|
+
)
|
|
162
|
+
}).optional().describe("Persistent vessel reference record (CRM-style identity: name, contact, links) shown in the top bar. Replaces wholesale."),
|
|
152
163
|
labels: z.array(z.string()).optional().describe("Triage labels (replace semantics)."),
|
|
153
164
|
suggestions: z.array(z.string()).optional().describe("Quick-reply suggestion chips."),
|
|
154
165
|
previewUrl: z.string().optional().describe("A single URL rendered as a link card."),
|
|
@@ -156,13 +167,13 @@ server.registerTool(
|
|
|
156
167
|
vessel: z.string().optional().describe(`Vessel external_id (default "${defaultVessel}").`)
|
|
157
168
|
}
|
|
158
169
|
},
|
|
159
|
-
async ({ message, body, title, card,
|
|
170
|
+
async ({ message, body, title, card, details, labels, suggestions, previewUrl, status, vessel }) => {
|
|
160
171
|
const common = {
|
|
161
172
|
vessel: vessel ?? defaultVessel,
|
|
162
173
|
vesselTitle,
|
|
163
174
|
...title ? { title } : {},
|
|
164
175
|
...card ? { card } : {},
|
|
165
|
-
...
|
|
176
|
+
...details ? { details } : {},
|
|
166
177
|
...labels ? { labels } : {},
|
|
167
178
|
...suggestions ? { suggestions } : {},
|
|
168
179
|
...previewUrl ? { previewUrl } : {},
|
|
@@ -173,6 +184,35 @@ server.registerTool(
|
|
|
173
184
|
return ok(`mid:${res.messageId}`);
|
|
174
185
|
}
|
|
175
186
|
);
|
|
187
|
+
server.registerTool(
|
|
188
|
+
"mark",
|
|
189
|
+
{
|
|
190
|
+
title: "Mark (action receipt)",
|
|
191
|
+
description: 'Record that you COMMITTED a discrete action, as a slim timeline chip \u2014 an icon + a short `label`, optional small `subtext`, optional deep-link `url`. Use it instead of narrating the action in prose (e.g. after a tool sends an email or records a payment): the chip is the durable record, so just keep working. Does not wait, fires no webhook. Returns the chip id as "mid:<id>".',
|
|
192
|
+
inputSchema: {
|
|
193
|
+
label: z.string().describe("The chip label \u2014 what happened (max 120 chars)."),
|
|
194
|
+
type: z.enum(["email_sent", "payment_recorded", "status_changed", "booking_created", "invoice_voided", "generic"]).optional().describe('Semantic kind \u2014 picks a default icon + tone. Defaults to "generic".'),
|
|
195
|
+
subtext: z.string().optional().describe("Small secondary line (an amount, an id, a target; max 160 chars)."),
|
|
196
|
+
icon: z.string().optional().describe('Override the default glyph with a literal emoji (e.g. "\u2709\uFE0F").'),
|
|
197
|
+
tone: z.enum(["neutral", "success", "warning", "danger"]).optional().describe('Accent colour. Defaults to "neutral".'),
|
|
198
|
+
url: z.string().optional().describe("Deep-link into your own UI \u2014 renders a right chevron. NOT an interaction."),
|
|
199
|
+
vessel: z.string().optional().describe(`Vessel external_id (default "${defaultVessel}").`)
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
async ({ label, type, subtext, icon, tone, url, vessel }) => {
|
|
203
|
+
const res = await v.mark({
|
|
204
|
+
vessel: vessel ?? defaultVessel,
|
|
205
|
+
vesselTitle,
|
|
206
|
+
label,
|
|
207
|
+
...type ? { type } : {},
|
|
208
|
+
...subtext ? { subtext } : {},
|
|
209
|
+
...icon ? { icon } : {},
|
|
210
|
+
...tone ? { tone } : {},
|
|
211
|
+
...url ? { url } : {}
|
|
212
|
+
});
|
|
213
|
+
return ok(`mid:${res.markId}`);
|
|
214
|
+
}
|
|
215
|
+
);
|
|
176
216
|
server.registerTool(
|
|
177
217
|
"ask_human",
|
|
178
218
|
{
|
|
@@ -327,21 +367,20 @@ server.registerTool(
|
|
|
327
367
|
}
|
|
328
368
|
},
|
|
329
369
|
async ({ vessel, title, labels, status, archived, pinned }) => {
|
|
330
|
-
const
|
|
370
|
+
const patch = {
|
|
331
371
|
...title !== void 0 ? { title } : {},
|
|
332
372
|
...labels !== void 0 ? { labels } : {},
|
|
333
373
|
...status !== void 0 ? { vesselStatus: status } : {},
|
|
334
374
|
...archived !== void 0 ? { archived } : {},
|
|
335
375
|
...pinned !== void 0 ? { pinned } : {}
|
|
336
376
|
};
|
|
337
|
-
if (!Object.keys(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
return ok(res.ok ? "ok" : `error: ${data.error ?? res.status}`);
|
|
377
|
+
if (!Object.keys(patch).length) return ok("error: nothing to update");
|
|
378
|
+
try {
|
|
379
|
+
await v.updateVessel(vessel, patch);
|
|
380
|
+
return ok("ok");
|
|
381
|
+
} catch (e) {
|
|
382
|
+
return ok(`error: ${e.message}`);
|
|
383
|
+
}
|
|
345
384
|
}
|
|
346
385
|
);
|
|
347
386
|
server.registerTool(
|
|
@@ -352,12 +391,12 @@ server.registerTool(
|
|
|
352
391
|
inputSchema: { vessel: z.string().describe("Vessel external_id to delete.") }
|
|
353
392
|
},
|
|
354
393
|
async ({ vessel }) => {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
})
|
|
359
|
-
|
|
360
|
-
|
|
394
|
+
try {
|
|
395
|
+
await v.deleteVessel(vessel);
|
|
396
|
+
return ok(`deleted ${vessel}`);
|
|
397
|
+
} catch (e) {
|
|
398
|
+
return ok(`error: ${e.message}`);
|
|
399
|
+
}
|
|
361
400
|
}
|
|
362
401
|
);
|
|
363
402
|
server.registerTool(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vessels-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.
|
|
23
|
+
"vessels-sdk": "^0.26.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"tsup": "^8.5.1",
|