@coffrify/mcp 0.1.2 → 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.
- package/dist/server.js +375 -84
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
// src/server.ts
|
|
4
4
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
CallToolRequestSchema,
|
|
8
|
+
ListToolsRequestSchema,
|
|
9
|
+
ListResourcesRequestSchema,
|
|
10
|
+
ReadResourceRequestSchema,
|
|
11
|
+
ListPromptsRequestSchema,
|
|
12
|
+
GetPromptRequestSchema
|
|
13
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
+
import { Coffrify, COFFRIFY_EVENT_CATALOG } from "@coffrify/sdk";
|
|
8
15
|
var apiKey = process.env.COFFRIFY_API_KEY;
|
|
9
16
|
var apiUrl = process.env.COFFRIFY_API_URL ?? "https://api.coffrify.com";
|
|
10
17
|
if (!apiKey) {
|
|
@@ -13,33 +20,44 @@ if (!apiKey) {
|
|
|
13
20
|
}
|
|
14
21
|
var coffrify = new Coffrify({ apiKey, apiUrl });
|
|
15
22
|
var server = new Server(
|
|
16
|
-
{ name: "coffrify", version: "0.
|
|
17
|
-
{ capabilities: { tools: {} } }
|
|
23
|
+
{ name: "coffrify", version: "0.3.0" },
|
|
24
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
18
25
|
);
|
|
26
|
+
var disabledBuiltinTools = /* @__PURE__ */ new Set();
|
|
27
|
+
var customActionsByName = /* @__PURE__ */ new Map();
|
|
28
|
+
var eventEnum = COFFRIFY_EVENT_CATALOG.map((e) => e.type);
|
|
19
29
|
var TOOLS = [
|
|
20
30
|
{
|
|
21
31
|
name: "coffrify_list_transfers",
|
|
22
|
-
description: "List recent encrypted file transfers in the workspace.
|
|
32
|
+
description: "List recent encrypted file transfers in the workspace.",
|
|
23
33
|
inputSchema: {
|
|
24
34
|
type: "object",
|
|
25
35
|
properties: {
|
|
26
|
-
limit: { type: "number",
|
|
27
|
-
status: { type: "string", description: "
|
|
36
|
+
limit: { type: "number", default: 20 },
|
|
37
|
+
status: { type: "string", description: "active|expired|deleted" }
|
|
28
38
|
}
|
|
29
39
|
}
|
|
30
40
|
},
|
|
31
41
|
{
|
|
32
|
-
name: "
|
|
33
|
-
description: "
|
|
42
|
+
name: "coffrify_search_transfers",
|
|
43
|
+
description: "Search transfers by title substring or short_code prefix.",
|
|
34
44
|
inputSchema: {
|
|
35
45
|
type: "object",
|
|
36
|
-
properties: {
|
|
37
|
-
|
|
46
|
+
properties: {
|
|
47
|
+
query: { type: "string", description: "Substring (case-insensitive) to match against title or short_code" },
|
|
48
|
+
limit: { type: "number", default: 20 }
|
|
49
|
+
},
|
|
50
|
+
required: ["query"]
|
|
38
51
|
}
|
|
39
52
|
},
|
|
53
|
+
{
|
|
54
|
+
name: "coffrify_get_transfer",
|
|
55
|
+
description: "Get full details of a transfer (files, settings, scan status).",
|
|
56
|
+
inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] }
|
|
57
|
+
},
|
|
40
58
|
{
|
|
41
59
|
name: "coffrify_create_transfer",
|
|
42
|
-
description: "Create a new encrypted file transfer. Returns the transfer + share URL.
|
|
60
|
+
description: "Create a new encrypted file transfer. Returns the transfer + share URL.",
|
|
43
61
|
inputSchema: {
|
|
44
62
|
type: "object",
|
|
45
63
|
properties: {
|
|
@@ -59,119 +77,123 @@ var TOOLS = [
|
|
|
59
77
|
max_downloads: { type: "number" },
|
|
60
78
|
password: { type: "string" },
|
|
61
79
|
transfer_title: { type: "string" },
|
|
62
|
-
burn_after_read: { type: "boolean" }
|
|
80
|
+
burn_after_read: { type: "boolean" },
|
|
81
|
+
encryption_mode: { type: "string", enum: ["server_side", "e2e_v1"], default: "server_side" }
|
|
63
82
|
},
|
|
64
83
|
required: ["files"]
|
|
65
84
|
}
|
|
66
85
|
},
|
|
67
86
|
{
|
|
68
87
|
name: "coffrify_delete_transfer",
|
|
69
|
-
description: "Delete a transfer (
|
|
88
|
+
description: "Delete a transfer (irreversible).",
|
|
89
|
+
inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] }
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "coffrify_bulk_delete_transfers",
|
|
93
|
+
description: "Delete multiple transfers at once. Returns per-id success/failure.",
|
|
70
94
|
inputSchema: {
|
|
71
95
|
type: "object",
|
|
72
|
-
properties: {
|
|
73
|
-
required: ["
|
|
96
|
+
properties: { ids: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 100 } },
|
|
97
|
+
required: ["ids"]
|
|
74
98
|
}
|
|
75
99
|
},
|
|
76
100
|
{
|
|
77
101
|
name: "coffrify_list_webhooks",
|
|
78
|
-
description: "List configured webhook endpoints
|
|
102
|
+
description: "List configured webhook endpoints.",
|
|
79
103
|
inputSchema: { type: "object", properties: {} }
|
|
80
104
|
},
|
|
81
105
|
{
|
|
82
106
|
name: "coffrify_create_webhook",
|
|
83
|
-
description: "Create a webhook subscription. Returns the webhook +
|
|
107
|
+
description: "Create a webhook subscription. Returns the webhook + signing secret (shown once).",
|
|
84
108
|
inputSchema: {
|
|
85
109
|
type: "object",
|
|
86
110
|
properties: {
|
|
87
111
|
name: { type: "string" },
|
|
88
|
-
url: { type: "string"
|
|
89
|
-
events: {
|
|
90
|
-
|
|
91
|
-
items: {
|
|
92
|
-
type: "string",
|
|
93
|
-
enum: ["transfer.created", "transfer.downloaded", "transfer.expired", "transfer.deleted", "transfer.scanned", "transfer.cloned", "transfer.limit_reached"]
|
|
94
|
-
}
|
|
95
|
-
}
|
|
112
|
+
url: { type: "string" },
|
|
113
|
+
events: { type: "array", items: { type: "string", enum: eventEnum } },
|
|
114
|
+
description: { type: "string" }
|
|
96
115
|
},
|
|
97
116
|
required: ["name", "url", "events"]
|
|
98
117
|
}
|
|
99
118
|
},
|
|
100
119
|
{
|
|
101
120
|
name: "coffrify_test_webhook",
|
|
102
|
-
description: "Send a test ping event to a webhook
|
|
103
|
-
inputSchema: {
|
|
104
|
-
type: "object",
|
|
105
|
-
properties: { id: { type: "string" } },
|
|
106
|
-
required: ["id"]
|
|
107
|
-
}
|
|
121
|
+
description: "Send a test ping event to a webhook.",
|
|
122
|
+
inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] }
|
|
108
123
|
},
|
|
109
124
|
{
|
|
110
125
|
name: "coffrify_list_webhook_deliveries",
|
|
111
|
-
description: "List recent delivery attempts for a webhook
|
|
126
|
+
description: "List recent delivery attempts for a webhook.",
|
|
112
127
|
inputSchema: {
|
|
113
128
|
type: "object",
|
|
114
|
-
properties: {
|
|
115
|
-
webhook_id: { type: "string" },
|
|
116
|
-
limit: { type: "number", default: 20 }
|
|
117
|
-
},
|
|
129
|
+
properties: { webhook_id: { type: "string" }, limit: { type: "number", default: 20 } },
|
|
118
130
|
required: ["webhook_id"]
|
|
119
131
|
}
|
|
120
132
|
},
|
|
121
133
|
{
|
|
122
134
|
name: "coffrify_replay_webhook_delivery",
|
|
123
|
-
description: "Replay a past webhook delivery
|
|
135
|
+
description: "Replay a past webhook delivery.",
|
|
136
|
+
inputSchema: { type: "object", properties: { delivery_id: { type: "string" } }, required: ["delivery_id"] }
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "coffrify_list_webhook_event_types",
|
|
140
|
+
description: "Return the catalog of every supported webhook event type with descriptions, families, stability, and required plan.",
|
|
124
141
|
inputSchema: {
|
|
125
142
|
type: "object",
|
|
126
|
-
properties: {
|
|
127
|
-
|
|
143
|
+
properties: {
|
|
144
|
+
family: { type: "string", description: "Filter by family: transfer|workspace|api_key|webhook|scim|saml|audit|gdpr|system|member|api_token" }
|
|
145
|
+
}
|
|
128
146
|
}
|
|
129
147
|
},
|
|
130
148
|
{
|
|
131
149
|
name: "coffrify_list_api_keys",
|
|
132
|
-
description: "List API keys
|
|
150
|
+
description: "List API keys (key values are hashed and never returned).",
|
|
133
151
|
inputSchema: { type: "object", properties: {} }
|
|
134
152
|
},
|
|
135
153
|
{
|
|
136
154
|
name: "coffrify_create_api_key",
|
|
137
|
-
description: "Create a new API key. Returns the key value ONCE \u2014
|
|
155
|
+
description: "Create a new API key. Returns the key value ONCE \u2014 store securely. Specify scopes for least-privilege access.",
|
|
138
156
|
inputSchema: {
|
|
139
157
|
type: "object",
|
|
140
158
|
properties: {
|
|
141
159
|
name: { type: "string" },
|
|
142
160
|
description: { type: "string" },
|
|
143
161
|
environment: { type: "string", enum: ["live", "test"], default: "live" },
|
|
144
|
-
scopes: {
|
|
145
|
-
type: "array",
|
|
146
|
-
items: { type: "string" },
|
|
147
|
-
description: "e.g. ['transfers:read', 'transfers:write', 'webhooks:manage']"
|
|
148
|
-
},
|
|
162
|
+
scopes: { type: "array", items: { type: "string" } },
|
|
149
163
|
expires_in_days: { type: "number" },
|
|
150
|
-
allowed_ips: { type: "array", items: { type: "string" }
|
|
164
|
+
allowed_ips: { type: "array", items: { type: "string" } }
|
|
151
165
|
},
|
|
152
166
|
required: ["name"]
|
|
153
167
|
}
|
|
154
168
|
},
|
|
155
169
|
{
|
|
156
170
|
name: "coffrify_revoke_api_key",
|
|
157
|
-
description: "Revoke an API key (
|
|
171
|
+
description: "Revoke an API key (irreversible).",
|
|
172
|
+
inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] }
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: "coffrify_rotate_api_key",
|
|
176
|
+
description: "Rotate an API key \u2014 mints a new one with same scopes. Old key revokes after grace_days (default 7, max 30).",
|
|
158
177
|
inputSchema: {
|
|
159
178
|
type: "object",
|
|
160
|
-
properties: {
|
|
179
|
+
properties: {
|
|
180
|
+
id: { type: "string" },
|
|
181
|
+
grace_days: { type: "number", default: 7 }
|
|
182
|
+
},
|
|
161
183
|
required: ["id"]
|
|
162
184
|
}
|
|
163
185
|
},
|
|
164
186
|
{
|
|
165
187
|
name: "coffrify_query_audit_log",
|
|
166
|
-
description: "Query the workspace audit log.
|
|
188
|
+
description: "Query the workspace audit log. Filter by action, actor, time range. Supports relative since (e.g. '24h').",
|
|
167
189
|
inputSchema: {
|
|
168
190
|
type: "object",
|
|
169
191
|
properties: {
|
|
170
|
-
action: { type: "string"
|
|
192
|
+
action: { type: "string" },
|
|
171
193
|
actor_id: { type: "string" },
|
|
172
194
|
resource_type: { type: "string" },
|
|
173
|
-
since: { type: "string"
|
|
174
|
-
until: { type: "string"
|
|
195
|
+
since: { type: "string" },
|
|
196
|
+
until: { type: "string" },
|
|
175
197
|
limit: { type: "number", default: 100 }
|
|
176
198
|
}
|
|
177
199
|
}
|
|
@@ -182,22 +204,235 @@ var TOOLS = [
|
|
|
182
204
|
inputSchema: { type: "object", properties: {} }
|
|
183
205
|
}
|
|
184
206
|
];
|
|
185
|
-
server.setRequestHandler(
|
|
186
|
-
|
|
207
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
208
|
+
resources: [
|
|
209
|
+
{ uri: "coffrify://transfers", name: "transfers", description: "All recent transfers (JSON list)", mimeType: "application/json" },
|
|
210
|
+
{ uri: "coffrify://webhooks", name: "webhooks", description: "All webhook endpoints", mimeType: "application/json" },
|
|
211
|
+
{ uri: "coffrify://api-keys", name: "api-keys", description: "All API keys (hashed)", mimeType: "application/json" },
|
|
212
|
+
{ uri: "coffrify://events", name: "events", description: "Catalog of every supported webhook event type", mimeType: "application/json" },
|
|
213
|
+
{ uri: "coffrify://workspace", name: "workspace", description: "Current workspace + scopes", mimeType: "application/json" },
|
|
214
|
+
{ uri: "coffrify://audit", name: "audit", description: "Recent audit log entries (last 100)", mimeType: "application/json" }
|
|
215
|
+
]
|
|
187
216
|
}));
|
|
217
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
|
|
218
|
+
const uri = req.params.uri;
|
|
219
|
+
let data;
|
|
220
|
+
if (uri === "coffrify://transfers") {
|
|
221
|
+
data = await coffrify.transfers.list({ limit: 50 });
|
|
222
|
+
} else if (uri.startsWith("coffrify://transfers/")) {
|
|
223
|
+
data = await coffrify.transfers.get(uri.split("/").pop());
|
|
224
|
+
} else if (uri === "coffrify://webhooks") {
|
|
225
|
+
data = await coffrify.webhooks.list();
|
|
226
|
+
} else if (uri.startsWith("coffrify://webhooks/")) {
|
|
227
|
+
const id = uri.split("/").pop();
|
|
228
|
+
const [hooks, deliveries] = await Promise.all([
|
|
229
|
+
coffrify.webhooks.list(),
|
|
230
|
+
coffrify.webhooks.deliveries(id, { limit: 10 }).catch(() => null)
|
|
231
|
+
]);
|
|
232
|
+
const webhook = (hooks.webhooks ?? hooks.data ?? []).find((w) => w.id === id);
|
|
233
|
+
data = { webhook, recent_deliveries: deliveries };
|
|
234
|
+
} else if (uri === "coffrify://api-keys") {
|
|
235
|
+
data = await coffrify.apiKeys.list();
|
|
236
|
+
} else if (uri === "coffrify://events") {
|
|
237
|
+
data = { catalog: COFFRIFY_EVENT_CATALOG };
|
|
238
|
+
} else if (uri === "coffrify://workspace") {
|
|
239
|
+
data = await coffrify.http.request("GET", "/me");
|
|
240
|
+
} else if (uri === "coffrify://audit") {
|
|
241
|
+
data = await coffrify.audit.list({ limit: 100 });
|
|
242
|
+
} else {
|
|
243
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) }]
|
|
247
|
+
};
|
|
248
|
+
});
|
|
249
|
+
var PROMPTS = [
|
|
250
|
+
{
|
|
251
|
+
name: "send_invoice",
|
|
252
|
+
description: "Compose and send an invoice file as a Coffrify transfer with sensible defaults.",
|
|
253
|
+
arguments: [
|
|
254
|
+
{ name: "recipient_email", description: "Recipient email", required: true },
|
|
255
|
+
{ name: "invoice_filename", description: "Filename of the invoice (e.g. INV-2026-001.pdf)", required: true },
|
|
256
|
+
{ name: "expires_in_days", description: "Expiration window (default 30)", required: false }
|
|
257
|
+
]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: "audit_weekly",
|
|
261
|
+
description: "Pull the last 7 days of audit log and summarize unusual activity.",
|
|
262
|
+
arguments: []
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: "rotate_all_api_keys",
|
|
266
|
+
description: "Rotate every API key in the workspace with a 7-day grace window.",
|
|
267
|
+
arguments: []
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "subscribe_to_security_events",
|
|
271
|
+
description: "Create a webhook subscribed to all security-relevant events (geo_blocked, password_failed, scan_infected, suspicious_usage, saml.login_failed).",
|
|
272
|
+
arguments: [
|
|
273
|
+
{ name: "endpoint_url", description: "HTTPS endpoint to receive events", required: true }
|
|
274
|
+
]
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: "investigate_transfer",
|
|
278
|
+
description: "Pull a transfer + its scan results + recent webhook deliveries to investigate an incident.",
|
|
279
|
+
arguments: [
|
|
280
|
+
{ name: "short_code", description: "Short code or ID of the transfer", required: true }
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
];
|
|
284
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: PROMPTS }));
|
|
285
|
+
server.setRequestHandler(GetPromptRequestSchema, async (req) => {
|
|
286
|
+
const { name, arguments: args = {} } = req.params;
|
|
287
|
+
const a = args;
|
|
288
|
+
switch (name) {
|
|
289
|
+
case "send_invoice":
|
|
290
|
+
return {
|
|
291
|
+
description: "Send an invoice via Coffrify",
|
|
292
|
+
messages: [{
|
|
293
|
+
role: "user",
|
|
294
|
+
content: {
|
|
295
|
+
type: "text",
|
|
296
|
+
text: `Send the file "${a.invoice_filename}" as a Coffrify transfer.
|
|
297
|
+
Recipient: ${a.recipient_email}.
|
|
298
|
+
Expiration: ${a.expires_in_days ?? "30"} days.
|
|
299
|
+
Use the coffrify_create_transfer tool with: title="Invoice ${a.invoice_filename}", expires_in_hours=${parseInt(a.expires_in_days ?? "30", 10) * 24}, max_downloads=3.
|
|
300
|
+
Then share the returned share_url with the recipient.`
|
|
301
|
+
}
|
|
302
|
+
}]
|
|
303
|
+
};
|
|
304
|
+
case "audit_weekly":
|
|
305
|
+
return {
|
|
306
|
+
description: "Weekly audit summary",
|
|
307
|
+
messages: [{
|
|
308
|
+
role: "user",
|
|
309
|
+
content: {
|
|
310
|
+
type: "text",
|
|
311
|
+
text: `Use coffrify_query_audit_log with since="7d" and limit=500 to fetch the last week of activity.
|
|
312
|
+
Summarize:
|
|
313
|
+
1. Top 5 most frequent actions
|
|
314
|
+
2. Any failed/denied actions and what they indicate
|
|
315
|
+
3. Unusual IP addresses or geo distributions
|
|
316
|
+
4. Any api_key.suspicious_usage or saml.login_failed events
|
|
317
|
+
Output a brief markdown report.`
|
|
318
|
+
}
|
|
319
|
+
}]
|
|
320
|
+
};
|
|
321
|
+
case "rotate_all_api_keys":
|
|
322
|
+
return {
|
|
323
|
+
description: "Rotate every API key with 7-day grace",
|
|
324
|
+
messages: [{
|
|
325
|
+
role: "user",
|
|
326
|
+
content: {
|
|
327
|
+
type: "text",
|
|
328
|
+
text: `1. Call coffrify_list_api_keys to get all active keys.
|
|
329
|
+
2. For each key with status=active and not already rotated, call coffrify_rotate_api_key with grace_days=7.
|
|
330
|
+
3. Report: { rotated_count, new_keys: [{ name, new_prefix, grace_until }] }.
|
|
331
|
+
Stress that the new key values are returned ONCE \u2014 they must be saved immediately.`
|
|
332
|
+
}
|
|
333
|
+
}]
|
|
334
|
+
};
|
|
335
|
+
case "subscribe_to_security_events":
|
|
336
|
+
return {
|
|
337
|
+
description: "Wire a webhook to all security events",
|
|
338
|
+
messages: [{
|
|
339
|
+
role: "user",
|
|
340
|
+
content: {
|
|
341
|
+
type: "text",
|
|
342
|
+
text: `Call coffrify_create_webhook with:
|
|
343
|
+
name = "Security events"
|
|
344
|
+
url = "${a.endpoint_url}"
|
|
345
|
+
events = ["transfer.password_failed", "transfer.geo_blocked", "transfer.scan_infected", "transfer.scan_quarantined",
|
|
346
|
+
"api_key.suspicious_usage", "api_key.expired", "api_key.revoked",
|
|
347
|
+
"saml.login_failed", "audit.policy_violated",
|
|
348
|
+
"webhook.endpoint_disabled", "workspace.payment_failed"]
|
|
349
|
+
Print the returned signing secret to the user \u2014 it's shown only once.`
|
|
350
|
+
}
|
|
351
|
+
}]
|
|
352
|
+
};
|
|
353
|
+
case "investigate_transfer":
|
|
354
|
+
return {
|
|
355
|
+
description: "Investigate a transfer incident",
|
|
356
|
+
messages: [{
|
|
357
|
+
role: "user",
|
|
358
|
+
content: {
|
|
359
|
+
type: "text",
|
|
360
|
+
text: `Investigate transfer "${a.short_code}":
|
|
361
|
+
1. Use coffrify_search_transfers query="${a.short_code}" to locate it.
|
|
362
|
+
2. Call coffrify_get_transfer with the id to fetch full details (scan_status, downloads, password_protected).
|
|
363
|
+
3. Call coffrify_query_audit_log with resource_id=<that id> since="30d" to pull related audit entries.
|
|
364
|
+
4. List any recent webhook deliveries that mention this transfer.
|
|
365
|
+
5. Output a timeline + a diagnosis of what happened.`
|
|
366
|
+
}
|
|
367
|
+
}]
|
|
368
|
+
};
|
|
369
|
+
default:
|
|
370
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
374
|
+
const enabledBuiltins = TOOLS.filter((t) => !disabledBuiltinTools.has(t.name));
|
|
375
|
+
const customTools = [...customActionsByName.values()].map((a) => ({
|
|
376
|
+
name: a.name,
|
|
377
|
+
description: a.runtime_kind === "typescript" ? `[TS, awaiting v0.4 runtime] ${a.description}` : a.description,
|
|
378
|
+
inputSchema: a.input_schema
|
|
379
|
+
}));
|
|
380
|
+
return { tools: [...enabledBuiltins, ...customTools] };
|
|
381
|
+
});
|
|
382
|
+
async function invokeCustomAction(action, input) {
|
|
383
|
+
if (action.runtime_kind === "typescript") {
|
|
384
|
+
return {
|
|
385
|
+
_stub: true,
|
|
386
|
+
message: "TypeScript runtime arrives in @coffrify/mcp v0.4.0 (Vercel Sandbox). Code persisted in DB but not executed yet.",
|
|
387
|
+
action_name: action.name,
|
|
388
|
+
received_input: input
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
let path = action.endpoint_path;
|
|
392
|
+
const queryParams = {};
|
|
393
|
+
let body = null;
|
|
394
|
+
for (const mapping of action.param_mapping ?? []) {
|
|
395
|
+
const value = input[mapping.name];
|
|
396
|
+
if (value === void 0 || value === null) continue;
|
|
397
|
+
if (mapping.source === "path") {
|
|
398
|
+
path = path.replace(`{${mapping.name}}`, encodeURIComponent(String(value)));
|
|
399
|
+
} else if (mapping.source === "query") {
|
|
400
|
+
queryParams[mapping.name] = String(value);
|
|
401
|
+
} else if (mapping.source === "body") {
|
|
402
|
+
body = body ?? {};
|
|
403
|
+
body[mapping.name] = value;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (Object.keys(queryParams).length > 0) {
|
|
407
|
+
const qs = new URLSearchParams(queryParams).toString();
|
|
408
|
+
path = `${path}${path.includes("?") ? "&" : "?"}${qs}`;
|
|
409
|
+
}
|
|
410
|
+
const trimmedPath = path.replace(/^\/v1/, "") || "/";
|
|
411
|
+
return await coffrify.http.request(action.endpoint_method, trimmedPath, body ?? void 0);
|
|
412
|
+
}
|
|
188
413
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
189
414
|
const { name, arguments: args = {} } = request.params;
|
|
190
415
|
try {
|
|
191
416
|
let result;
|
|
417
|
+
const a = args;
|
|
192
418
|
switch (name) {
|
|
193
419
|
case "coffrify_list_transfers":
|
|
194
|
-
result = await coffrify.transfers.list(
|
|
420
|
+
result = await coffrify.transfers.list(a);
|
|
421
|
+
break;
|
|
422
|
+
case "coffrify_search_transfers": {
|
|
423
|
+
const list = await coffrify.transfers.list({ limit: 100 });
|
|
424
|
+
const arr = list.transfers ?? list.data ?? [];
|
|
425
|
+
const q = a.query.toLowerCase();
|
|
426
|
+
const matches = arr.filter(
|
|
427
|
+
(t) => (t.transfer_title ?? "").toLowerCase().includes(q) || (t.short_code ?? "").toLowerCase().startsWith(q)
|
|
428
|
+
).slice(0, a.limit ?? 20);
|
|
429
|
+
result = { matches, total: matches.length };
|
|
195
430
|
break;
|
|
431
|
+
}
|
|
196
432
|
case "coffrify_get_transfer":
|
|
197
|
-
result = await coffrify.transfers.get(
|
|
433
|
+
result = await coffrify.transfers.get(a.id);
|
|
198
434
|
break;
|
|
199
|
-
case "coffrify_create_transfer":
|
|
200
|
-
const a = args;
|
|
435
|
+
case "coffrify_create_transfer":
|
|
201
436
|
result = await coffrify.transfers.create(a.files, {
|
|
202
437
|
expires_in_hours: a.expires_in_hours,
|
|
203
438
|
max_downloads: a.max_downloads,
|
|
@@ -206,68 +441,124 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
206
441
|
burn_after_read: a.burn_after_read
|
|
207
442
|
});
|
|
208
443
|
break;
|
|
209
|
-
}
|
|
210
444
|
case "coffrify_delete_transfer":
|
|
211
|
-
result = await coffrify.transfers.delete(
|
|
445
|
+
result = await coffrify.transfers.delete(a.id);
|
|
446
|
+
break;
|
|
447
|
+
case "coffrify_bulk_delete_transfers": {
|
|
448
|
+
const out = [];
|
|
449
|
+
for (const id of a.ids) {
|
|
450
|
+
try {
|
|
451
|
+
await coffrify.transfers.delete(id);
|
|
452
|
+
out.push({ id, ok: true });
|
|
453
|
+
} catch (e) {
|
|
454
|
+
out.push({ id, ok: false, error: e?.message ?? String(e) });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
result = { results: out, ok_count: out.filter((r) => r.ok).length };
|
|
212
458
|
break;
|
|
459
|
+
}
|
|
213
460
|
case "coffrify_list_webhooks":
|
|
214
461
|
result = await coffrify.webhooks.list();
|
|
215
462
|
break;
|
|
216
|
-
case "coffrify_create_webhook":
|
|
217
|
-
|
|
218
|
-
|
|
463
|
+
case "coffrify_create_webhook":
|
|
464
|
+
result = await coffrify.webhooks.create({
|
|
465
|
+
name: a.name,
|
|
466
|
+
url: a.url,
|
|
467
|
+
events: a.events,
|
|
468
|
+
description: a.description
|
|
469
|
+
});
|
|
219
470
|
break;
|
|
220
|
-
}
|
|
221
471
|
case "coffrify_test_webhook":
|
|
222
|
-
result = await coffrify.webhooks.test(
|
|
472
|
+
result = await coffrify.webhooks.test(a.id);
|
|
223
473
|
break;
|
|
224
|
-
case "coffrify_list_webhook_deliveries":
|
|
225
|
-
const a = args;
|
|
474
|
+
case "coffrify_list_webhook_deliveries":
|
|
226
475
|
result = await coffrify.webhooks.deliveries(a.webhook_id, { limit: a.limit });
|
|
227
476
|
break;
|
|
228
|
-
}
|
|
229
477
|
case "coffrify_replay_webhook_delivery":
|
|
230
|
-
result = await coffrify.webhooks.replay(
|
|
478
|
+
result = await coffrify.webhooks.replay(a.delivery_id);
|
|
479
|
+
break;
|
|
480
|
+
case "coffrify_list_webhook_event_types": {
|
|
481
|
+
const filtered = a.family ? COFFRIFY_EVENT_CATALOG.filter((e) => e.family === a.family) : COFFRIFY_EVENT_CATALOG;
|
|
482
|
+
result = { events: filtered, total: filtered.length };
|
|
231
483
|
break;
|
|
484
|
+
}
|
|
232
485
|
case "coffrify_list_api_keys":
|
|
233
486
|
result = await coffrify.apiKeys.list();
|
|
234
487
|
break;
|
|
235
488
|
case "coffrify_create_api_key":
|
|
236
|
-
result = await coffrify.apiKeys.create(
|
|
489
|
+
result = await coffrify.apiKeys.create(a);
|
|
237
490
|
break;
|
|
238
491
|
case "coffrify_revoke_api_key":
|
|
239
|
-
result = await coffrify.apiKeys.revoke(
|
|
492
|
+
result = await coffrify.apiKeys.revoke(a.id);
|
|
493
|
+
break;
|
|
494
|
+
case "coffrify_rotate_api_key":
|
|
495
|
+
result = await coffrify.http.request(
|
|
496
|
+
"POST",
|
|
497
|
+
`/api-keys/${encodeURIComponent(a.id)}/rotate`,
|
|
498
|
+
{ grace_days: a.grace_days ?? 7 }
|
|
499
|
+
);
|
|
240
500
|
break;
|
|
241
501
|
case "coffrify_query_audit_log": {
|
|
242
|
-
const a = args;
|
|
243
502
|
let since = a.since;
|
|
244
503
|
if (since && /^\d+[hdw]$/.test(since)) {
|
|
245
504
|
const m = since.match(/^(\d+)([hdw])$/);
|
|
246
|
-
const n = parseInt(m[1]);
|
|
505
|
+
const n = parseInt(m[1], 10);
|
|
247
506
|
const ms = m[2] === "h" ? n * 36e5 : m[2] === "d" ? n * 864e5 : n * 7 * 864e5;
|
|
248
507
|
since = new Date(Date.now() - ms).toISOString();
|
|
249
508
|
}
|
|
250
509
|
result = await coffrify.audit.list({ ...a, since });
|
|
251
510
|
break;
|
|
252
511
|
}
|
|
253
|
-
case "coffrify_get_workspace_info":
|
|
512
|
+
case "coffrify_get_workspace_info":
|
|
254
513
|
result = await coffrify.http.request("GET", "/me");
|
|
255
514
|
break;
|
|
256
|
-
|
|
257
|
-
|
|
515
|
+
default: {
|
|
516
|
+
const custom = customActionsByName.get(name);
|
|
517
|
+
if (custom) {
|
|
518
|
+
result = await invokeCustomAction(custom, a);
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
258
521
|
throw new Error(`Unknown tool: ${name}`);
|
|
522
|
+
}
|
|
259
523
|
}
|
|
260
|
-
return {
|
|
261
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
262
|
-
};
|
|
524
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
263
525
|
} catch (e) {
|
|
264
526
|
return {
|
|
265
|
-
content: [{
|
|
266
|
-
|
|
527
|
+
content: [{
|
|
528
|
+
type: "text",
|
|
529
|
+
text: `Error: ${e?.message ?? "unknown"}
|
|
530
|
+
${JSON.stringify(e?.details ?? {}, null, 2)}`
|
|
531
|
+
}],
|
|
267
532
|
isError: true
|
|
268
533
|
};
|
|
269
534
|
}
|
|
270
535
|
});
|
|
536
|
+
async function bootstrapWorkspaceConfig() {
|
|
537
|
+
const httpClient = coffrify.http;
|
|
538
|
+
try {
|
|
539
|
+
const toolsRes = await httpClient.request("GET", "/mcp/tools");
|
|
540
|
+
for (const t of toolsRes?.data ?? []) {
|
|
541
|
+
if (t.disabled) disabledBuiltinTools.add(t.name);
|
|
542
|
+
}
|
|
543
|
+
} catch (e) {
|
|
544
|
+
const msg = e?.message ?? String(e);
|
|
545
|
+
console.error(`[coffrify-mcp] bootstrap: tool-overrides fetch failed (${msg}). Continuing with all builtins enabled.`);
|
|
546
|
+
}
|
|
547
|
+
try {
|
|
548
|
+
const actionsRes = await httpClient.request("GET", "/mcp/custom-actions");
|
|
549
|
+
for (const a of actionsRes?.data ?? []) {
|
|
550
|
+
if (a.is_active) customActionsByName.set(a.name, a);
|
|
551
|
+
}
|
|
552
|
+
} catch (e) {
|
|
553
|
+
const msg = e?.message ?? String(e);
|
|
554
|
+
console.error(`[coffrify-mcp] bootstrap: custom-actions fetch failed (${msg}). Continuing with builtins only.`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
await bootstrapWorkspaceConfig();
|
|
271
558
|
var transport = new StdioServerTransport();
|
|
272
559
|
await server.connect(transport);
|
|
273
|
-
|
|
560
|
+
var enabledBuiltinCount = TOOLS.filter((t) => !disabledBuiltinTools.has(t.name)).length;
|
|
561
|
+
var customCount = customActionsByName.size;
|
|
562
|
+
console.error(
|
|
563
|
+
`[coffrify-mcp] v0.3.0 connected via stdio \u2014 ${enabledBuiltinCount}/${TOOLS.length} builtins enabled, ${customCount} custom action${customCount === 1 ? "" : "s"}.`
|
|
564
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffrify/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Coffrify MCP server — gives Claude/Cursor/Windsurf agents direct access to Coffrify (transfers, webhooks, audit, API keys).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
31
31
|
"zod": "^3.23.8",
|
|
32
|
-
"@coffrify/sdk": "0.
|
|
32
|
+
"@coffrify/sdk": "0.5.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^22.0.0",
|