srift-transfer 2.2.13 → 2.2.15

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 CHANGED
@@ -4,6 +4,7 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen.svg)](https://nodejs.org/)
6
6
  [![MCP Protocol](https://img.shields.io/badge/MCP-2025--06--18-purple.svg)](https://modelcontextprotocol.io)
7
+ [![Smithery](https://img.shields.io/badge/Smithery-srift%2Fsrift-7c3aed)](https://smithery.ai/servers/srift/srift)
7
8
  [![Encryption: AES-256-GCM](https://img.shields.io/badge/Encryption-AES--256--GCM-green.svg)](https://srift.app)
8
9
  [![Zero Telemetry](https://img.shields.io/badge/telemetry-zero-success.svg)](https://srift.app/privacy)
9
10
 
@@ -37,6 +38,15 @@ The user opens the link in any web browser or runs `curl -OJ https://srift.app/d
37
38
 
38
39
  **Session** transfers are end-to-end encrypted with **AES-256-GCM**, keys derived locally (PBKDF2-SHA256, 100,000 iterations) and never sent to us.
39
40
 
41
+ > **⚠️ The sender's daemon must be alive for the link to work.**
42
+ > SRIFT keeps no server-side copy — the daemon streams the file from your disk
43
+ > on demand. It runs in the background and survives your command exiting, so
44
+ > the link stays live afterwards. But if the daemon stops (machine sleeps or
45
+ > reboots, or `srift daemon stop`) the link returns `503 sender is offline`.
46
+ > This is the direct trade-off for zero retention: nothing is stored, so
47
+ > nothing can be served once the source goes away. If you need a link that
48
+ > outlives your machine, use storage — SRIFT is a relay.
49
+
40
50
  Public `quick-share` links are a deliberate exception, and it's worth being precise: the link is designed to be opened by any browser, `curl` or `wget` — clients that hold no key and run no SRIFT code. End-to-end encryption is therefore impossible on that path by definition. What SRIFT guarantees for quick-share is **zero retention**: bytes stream from the sender straight to the open HTTP response, are never written to SRIFT storage, and are served `Cache-Control: no-store`. If you need the relay to be unable to read the content, use a session transfer instead of a public link.
41
51
 
42
52
  ---
package/dist/daemon.js CHANGED
@@ -17,17 +17,34 @@ var PROTOCOL_VERSION = "2025-06-18";
17
17
  var MCP_TOOLS = [
18
18
  {
19
19
  name: "srift_start_session",
20
+ title: "Start SRIFT Session",
20
21
  description: "Create a new SRIFT secure session. Returns the 7-character session ID and a shareable URL. The host approves all future joins. E2EE keys derive locally from the session ID + optional roomSecret.",
21
22
  inputSchema: {
22
23
  type: "object",
23
24
  properties: {
24
- sessionName: { type: "string", description: "Human-readable session name (optional)" },
25
+ sessionName: { type: "string", description: 'Human-readable session name (optional, e.g. "Project Collab")' },
25
26
  roomSecret: { type: "string", description: "Optional shared secret for extra-strong E2EE key derivation. Never sent to server." }
26
27
  }
28
+ },
29
+ outputSchema: {
30
+ type: "object",
31
+ properties: {
32
+ sessionId: { type: "string", description: "7-character unique session room code" },
33
+ url: { type: "string", description: "Direct browser join URL to share with peers" },
34
+ role: { type: "string", description: "User role in session (host)" }
35
+ },
36
+ required: ["sessionId"]
37
+ },
38
+ annotations: {
39
+ readOnlyHint: false,
40
+ destructiveHint: false,
41
+ audience: ["agent", "user"],
42
+ priority: 0.9
27
43
  }
28
44
  },
29
45
  {
30
46
  name: "srift_join_session",
47
+ title: "Join SRIFT Session",
31
48
  description: "Request to join an existing SRIFT session by its 7-character session ID. The host must approve before transfers/chat work.",
32
49
  inputSchema: {
33
50
  type: "object",
@@ -37,20 +54,77 @@ var MCP_TOOLS = [
37
54
  roomSecret: { type: "string", description: "Optional shared secret matching the host's" }
38
55
  },
39
56
  required: ["sessionId"]
57
+ },
58
+ outputSchema: {
59
+ type: "object",
60
+ properties: {
61
+ sessionId: { type: "string", description: "Session room code" },
62
+ status: { type: "string", description: "Join request status (approved, pending, or joined)" },
63
+ message: { type: "string", description: "Status message or instructions" }
64
+ },
65
+ required: ["sessionId"]
66
+ },
67
+ annotations: {
68
+ readOnlyHint: false,
69
+ destructiveHint: false,
70
+ audience: ["agent", "user"],
71
+ priority: 0.9
40
72
  }
41
73
  },
42
74
  {
43
75
  name: "srift_session_status",
76
+ title: "Get Session Status",
44
77
  description: "Get current session details: id, role (host/guest), connection state, pending join requests, peer count.",
45
- inputSchema: { type: "object", properties: {} }
78
+ inputSchema: { type: "object", properties: {} },
79
+ outputSchema: {
80
+ type: "object",
81
+ properties: {
82
+ sessionId: { type: "string", description: "Active session ID, if any" },
83
+ role: { type: "string", description: 'Active role: "host", "guest", or null' },
84
+ isConnected: { type: "boolean", description: "True if connected to signaling server" },
85
+ peerCount: { type: "number", description: "Number of connected peers in the room" },
86
+ pendingJoins: {
87
+ type: "array",
88
+ description: "List of pending guest join requests waiting for host approval",
89
+ items: {
90
+ type: "object",
91
+ properties: {
92
+ tempUserId: { type: "string", description: "Temporary ID of requesting user" },
93
+ username: { type: "string", description: "Username of requesting user" }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ },
99
+ annotations: {
100
+ readOnlyHint: true,
101
+ destructiveHint: false,
102
+ audience: ["agent", "user"],
103
+ priority: 0.8
104
+ }
46
105
  },
47
106
  {
48
107
  name: "srift_close_session",
108
+ title: "Close Session",
49
109
  description: "Close the active session. Flushes E2EE keys, disconnects signaling, clears chat history.",
50
- inputSchema: { type: "object", properties: {} }
110
+ inputSchema: { type: "object", properties: {} },
111
+ outputSchema: {
112
+ type: "object",
113
+ properties: {
114
+ success: { type: "boolean", description: "True if session was closed cleanly" },
115
+ message: { type: "string", description: "Confirmation message" }
116
+ }
117
+ },
118
+ annotations: {
119
+ readOnlyHint: false,
120
+ destructiveHint: true,
121
+ audience: ["agent", "user"],
122
+ priority: 0.7
123
+ }
51
124
  },
52
125
  {
53
126
  name: "srift_approve_join",
127
+ title: "Approve Join Request",
54
128
  description: "Host-only. Approve a pending guest join request by their temp user ID.",
55
129
  inputSchema: {
56
130
  type: "object",
@@ -58,87 +132,261 @@ var MCP_TOOLS = [
58
132
  tempUserId: { type: "string", description: "Temp user ID from session_status pendingJoins" }
59
133
  },
60
134
  required: ["tempUserId"]
135
+ },
136
+ outputSchema: {
137
+ type: "object",
138
+ properties: {
139
+ success: { type: "boolean", description: "True if guest was approved" },
140
+ tempUserId: { type: "string", description: "Approved user temporary ID" }
141
+ }
142
+ },
143
+ annotations: {
144
+ readOnlyHint: false,
145
+ destructiveHint: false,
146
+ audience: ["agent", "user"],
147
+ priority: 0.8
61
148
  }
62
149
  },
63
150
  {
64
151
  name: "srift_reject_join",
152
+ title: "Reject Join Request",
65
153
  description: "Host-only. Reject a pending guest join request.",
66
154
  inputSchema: {
67
155
  type: "object",
68
156
  properties: {
69
- tempUserId: { type: "string" },
70
- reason: { type: "string", description: "Optional rejection reason" }
157
+ tempUserId: { type: "string", description: "Temp user ID from session_status pendingJoins" },
158
+ reason: { type: "string", description: "Optional rejection reason message sent to guest" }
71
159
  },
72
160
  required: ["tempUserId"]
161
+ },
162
+ outputSchema: {
163
+ type: "object",
164
+ properties: {
165
+ success: { type: "boolean", description: "True if guest join request was rejected" },
166
+ tempUserId: { type: "string", description: "Rejected user temporary ID" }
167
+ }
168
+ },
169
+ annotations: {
170
+ readOnlyHint: false,
171
+ destructiveHint: true,
172
+ audience: ["agent", "user"],
173
+ priority: 0.7
73
174
  }
74
175
  },
75
176
  {
76
177
  name: "srift_kick_user",
178
+ title: "Kick Peer",
77
179
  description: "Host-only. Disconnect and ban a peer from the active room.",
78
180
  inputSchema: {
79
181
  type: "object",
80
- properties: { userId: { type: "string" } },
182
+ properties: {
183
+ userId: { type: "string", description: "User ID of the participant to disconnect and remove" }
184
+ },
81
185
  required: ["userId"]
186
+ },
187
+ outputSchema: {
188
+ type: "object",
189
+ properties: {
190
+ success: { type: "boolean", description: "True if peer was successfully kicked" },
191
+ userId: { type: "string", description: "ID of kicked peer" }
192
+ }
193
+ },
194
+ annotations: {
195
+ readOnlyHint: false,
196
+ destructiveHint: true,
197
+ audience: ["agent", "user"],
198
+ priority: 0.6
82
199
  }
83
200
  },
84
201
  {
85
202
  name: "srift_send_file",
203
+ title: "Send File in Session",
86
204
  description: "Offer a file to the connected peer in an existing session. The peer must accept with srift_accept_transfer. Returns a fileId for monitoring. Transport is auto-selected internally.",
87
205
  inputSchema: {
88
206
  type: "object",
89
207
  properties: {
90
- filePath: { type: "string", description: "Absolute path to the file on disk" }
208
+ filePath: { type: "string", description: "Absolute path to the file on local disk" }
91
209
  },
92
210
  required: ["filePath"]
211
+ },
212
+ outputSchema: {
213
+ type: "object",
214
+ properties: {
215
+ fileId: { type: "string", description: "Unique identifier for the offered file transfer" },
216
+ fileName: { type: "string", description: "Base filename of the offered file" },
217
+ fileSize: { type: "number", description: "Size of file in bytes" }
218
+ }
219
+ },
220
+ annotations: {
221
+ readOnlyHint: false,
222
+ destructiveHint: false,
223
+ audience: ["agent", "user"],
224
+ priority: 0.9
93
225
  }
94
226
  },
95
227
  {
96
228
  name: "srift_accept_transfer",
229
+ title: "Accept File Transfer",
97
230
  description: "Accept a pending file offer from the peer and start downloading.",
98
231
  inputSchema: {
99
232
  type: "object",
100
233
  properties: {
101
- fileId: { type: "string", description: "fileId from the inbound file_offer event" },
234
+ fileId: { type: "string", description: "fileId from the inbound file_offer event or transfer list" },
102
235
  saveDir: { type: "string", description: "Absolute path to destination directory (defaults to CWD)" }
103
236
  },
104
237
  required: ["fileId"]
238
+ },
239
+ outputSchema: {
240
+ type: "object",
241
+ properties: {
242
+ success: { type: "boolean", description: "True if transfer acceptance started successfully" },
243
+ fileId: { type: "string", description: "Accepted transfer ID" },
244
+ saveDir: { type: "string", description: "Destination directory path" }
245
+ }
246
+ },
247
+ annotations: {
248
+ readOnlyHint: false,
249
+ destructiveHint: false,
250
+ audience: ["agent", "user"],
251
+ priority: 0.9
105
252
  }
106
253
  },
107
254
  {
108
255
  name: "srift_list_transfers",
256
+ title: "List Active Transfers",
109
257
  description: "List all active and recent transfers with progress, speed, ETA, and status.",
110
- inputSchema: { type: "object", properties: {} }
258
+ inputSchema: { type: "object", properties: {} },
259
+ outputSchema: {
260
+ type: "object",
261
+ properties: {
262
+ transfers: {
263
+ type: "array",
264
+ description: "List of live or recent file transfers",
265
+ items: {
266
+ type: "object",
267
+ properties: {
268
+ fileId: { type: "string", description: "Transfer unique file ID" },
269
+ name: { type: "string", description: "Filename" },
270
+ size: { type: "number", description: "Size in bytes" },
271
+ progress: { type: "number", description: "Transfer percentage 0-100" },
272
+ status: { type: "string", description: "Transfer status (uploading, downloading, completed, failed)" },
273
+ speedKBps: { type: "number", description: "Current transfer speed in KB/s" },
274
+ etaSeconds: { type: "number", description: "Estimated remaining seconds" }
275
+ }
276
+ }
277
+ }
278
+ }
279
+ },
280
+ annotations: {
281
+ readOnlyHint: true,
282
+ destructiveHint: false,
283
+ audience: ["agent", "user"],
284
+ priority: 0.8
285
+ }
111
286
  },
112
287
  {
113
288
  name: "srift_quick_share",
289
+ title: "Quick Share File",
114
290
  description: 'One-shot helper: if no session is active, creates one; then seeds the file and returns a public DOWNLOAD URL (https://srift.app/d/<token>) the recipient can open in any browser or curl. No install needed on their side. Best for "AI wants to deliver a file to the user with zero ceremony".',
115
291
  inputSchema: {
116
292
  type: "object",
117
293
  properties: {
118
- filePath: { type: "string" },
119
- sessionName: { type: "string" }
294
+ filePath: { type: "string", description: "Absolute path to the file to deliver to the recipient" },
295
+ sessionName: { type: "string", description: "Optional session label for the transfer" }
120
296
  },
121
297
  required: ["filePath"]
298
+ },
299
+ outputSchema: {
300
+ type: "object",
301
+ properties: {
302
+ sessionId: { type: "string", description: "Associated session ID" },
303
+ fileId: { type: "string", description: "Transfer file ID" },
304
+ downloadUrl: { type: "string", description: "Public HTTP download URL for recipient" },
305
+ fileName: { type: "string", description: "Base filename" },
306
+ fileSize: { type: "number", description: "File size in bytes" }
307
+ },
308
+ required: ["downloadUrl"]
309
+ },
310
+ annotations: {
311
+ readOnlyHint: false,
312
+ destructiveHint: false,
313
+ audience: ["agent", "user"],
314
+ priority: 1
122
315
  }
123
316
  },
124
317
  {
125
318
  name: "srift_send_chat",
319
+ title: "Send Encrypted Chat",
126
320
  description: "Send an end-to-end encrypted (AES-256-GCM) chat message to the connected peer.",
127
321
  inputSchema: {
128
322
  type: "object",
129
- properties: { message: { type: "string" } },
323
+ properties: {
324
+ message: { type: "string", description: "Plaintext message string to encrypt and transmit" }
325
+ },
130
326
  required: ["message"]
327
+ },
328
+ outputSchema: {
329
+ type: "object",
330
+ properties: {
331
+ success: { type: "boolean", description: "True if message was sent" },
332
+ timestamp: { type: "string", description: "ISO 8601 transmission timestamp" }
333
+ }
334
+ },
335
+ annotations: {
336
+ readOnlyHint: false,
337
+ destructiveHint: false,
338
+ audience: ["agent", "user"],
339
+ priority: 0.8
131
340
  }
132
341
  },
133
342
  {
134
343
  name: "srift_chat_history",
344
+ title: "Read Chat History",
135
345
  description: "Read the locally-decrypted chat log for the current session.",
136
- inputSchema: { type: "object", properties: {} }
346
+ inputSchema: { type: "object", properties: {} },
347
+ outputSchema: {
348
+ type: "object",
349
+ properties: {
350
+ messages: {
351
+ type: "array",
352
+ description: "Decrypted message log for current session",
353
+ items: {
354
+ type: "object",
355
+ properties: {
356
+ sender: { type: "string", description: "Sender display name" },
357
+ message: { type: "string", description: "Decrypted plaintext content" },
358
+ timestamp: { type: "string", description: "ISO timestamp" }
359
+ }
360
+ }
361
+ }
362
+ }
363
+ },
364
+ annotations: {
365
+ readOnlyHint: true,
366
+ destructiveHint: false,
367
+ audience: ["agent", "user"],
368
+ priority: 0.7
369
+ }
137
370
  },
138
371
  {
139
372
  name: "srift_read_state",
373
+ title: "Read Local State",
140
374
  description: "Return the raw .srift-state.json contents (session + active transfers snapshot). Useful for one-shot polling without subscribing to SSE.",
141
- inputSchema: { type: "object", properties: {} }
375
+ inputSchema: { type: "object", properties: {} },
376
+ outputSchema: {
377
+ type: "object",
378
+ properties: {
379
+ session: { type: "object", description: "Current session state snapshot" },
380
+ activeTransfers: { type: "array", description: "Live transfers array" },
381
+ lastUpdated: { type: "string", description: "Last state write timestamp" }
382
+ }
383
+ },
384
+ annotations: {
385
+ readOnlyHint: true,
386
+ destructiveHint: false,
387
+ audience: ["agent", "user"],
388
+ priority: 0.6
389
+ }
142
390
  }
143
391
  ];
144
392
  var MCP_RESOURCES = [
@@ -254,7 +502,11 @@ srift_quick_share({ filePath: "/abs/path/file.zip" })
254
502
  \u2192 they open it in any browser, or run:
255
503
  curl -OJ <downloadUrl>
256
504
  wget --content-disposition <downloadUrl>
257
- \u2192 keep the daemon running while the download is in progress
505
+ \u2192 IMPORTANT: the local daemon seeds the file. It keeps running in the
506
+ background after your tool call returns, so the link stays live. But if
507
+ the daemon stops (machine sleeps/reboots, or 'srift daemon stop') the link
508
+ returns 503 "sender is offline" \u2014 SRIFT stores no copy server-side.
509
+ Tell the user to download while their machine is on.
258
510
  \`\`\`
259
511
 
260
512
  **Open a long-lived collaboration room:**
@@ -487,7 +739,13 @@ The peer must call srift_accept_transfer with this fileId.`;
487
739
  ``,
488
740
  `Give the URL to the user \u2014 they open it in any browser, or run:`,
489
741
  ` curl -OJ "${url}"`,
490
- `Keep this srift daemon running while they download.`
742
+ ``,
743
+ `The daemon seeds this file from disk and keeps running in the`,
744
+ `background, so the link stays live after this call returns.`,
745
+ `It stops working if the daemon stops (machine sleeps/reboots, or`,
746
+ `\`srift daemon stop\`) \u2014 SRIFT keeps no server-side copy, so the link`,
747
+ `then returns 503 "sender is offline". Tell the user to grab it while`,
748
+ `this machine is on.`
491
749
  ].join("\n");
492
750
  }
493
751
  case "srift_send_chat":
package/dist/index.js CHANGED
@@ -159,17 +159,34 @@ var init_core = __esm({
159
159
  MCP_TOOLS = [
160
160
  {
161
161
  name: "srift_start_session",
162
+ title: "Start SRIFT Session",
162
163
  description: "Create a new SRIFT secure session. Returns the 7-character session ID and a shareable URL. The host approves all future joins. E2EE keys derive locally from the session ID + optional roomSecret.",
163
164
  inputSchema: {
164
165
  type: "object",
165
166
  properties: {
166
- sessionName: { type: "string", description: "Human-readable session name (optional)" },
167
+ sessionName: { type: "string", description: 'Human-readable session name (optional, e.g. "Project Collab")' },
167
168
  roomSecret: { type: "string", description: "Optional shared secret for extra-strong E2EE key derivation. Never sent to server." }
168
169
  }
170
+ },
171
+ outputSchema: {
172
+ type: "object",
173
+ properties: {
174
+ sessionId: { type: "string", description: "7-character unique session room code" },
175
+ url: { type: "string", description: "Direct browser join URL to share with peers" },
176
+ role: { type: "string", description: "User role in session (host)" }
177
+ },
178
+ required: ["sessionId"]
179
+ },
180
+ annotations: {
181
+ readOnlyHint: false,
182
+ destructiveHint: false,
183
+ audience: ["agent", "user"],
184
+ priority: 0.9
169
185
  }
170
186
  },
171
187
  {
172
188
  name: "srift_join_session",
189
+ title: "Join SRIFT Session",
173
190
  description: "Request to join an existing SRIFT session by its 7-character session ID. The host must approve before transfers/chat work.",
174
191
  inputSchema: {
175
192
  type: "object",
@@ -179,20 +196,77 @@ var init_core = __esm({
179
196
  roomSecret: { type: "string", description: "Optional shared secret matching the host's" }
180
197
  },
181
198
  required: ["sessionId"]
199
+ },
200
+ outputSchema: {
201
+ type: "object",
202
+ properties: {
203
+ sessionId: { type: "string", description: "Session room code" },
204
+ status: { type: "string", description: "Join request status (approved, pending, or joined)" },
205
+ message: { type: "string", description: "Status message or instructions" }
206
+ },
207
+ required: ["sessionId"]
208
+ },
209
+ annotations: {
210
+ readOnlyHint: false,
211
+ destructiveHint: false,
212
+ audience: ["agent", "user"],
213
+ priority: 0.9
182
214
  }
183
215
  },
184
216
  {
185
217
  name: "srift_session_status",
218
+ title: "Get Session Status",
186
219
  description: "Get current session details: id, role (host/guest), connection state, pending join requests, peer count.",
187
- inputSchema: { type: "object", properties: {} }
220
+ inputSchema: { type: "object", properties: {} },
221
+ outputSchema: {
222
+ type: "object",
223
+ properties: {
224
+ sessionId: { type: "string", description: "Active session ID, if any" },
225
+ role: { type: "string", description: 'Active role: "host", "guest", or null' },
226
+ isConnected: { type: "boolean", description: "True if connected to signaling server" },
227
+ peerCount: { type: "number", description: "Number of connected peers in the room" },
228
+ pendingJoins: {
229
+ type: "array",
230
+ description: "List of pending guest join requests waiting for host approval",
231
+ items: {
232
+ type: "object",
233
+ properties: {
234
+ tempUserId: { type: "string", description: "Temporary ID of requesting user" },
235
+ username: { type: "string", description: "Username of requesting user" }
236
+ }
237
+ }
238
+ }
239
+ }
240
+ },
241
+ annotations: {
242
+ readOnlyHint: true,
243
+ destructiveHint: false,
244
+ audience: ["agent", "user"],
245
+ priority: 0.8
246
+ }
188
247
  },
189
248
  {
190
249
  name: "srift_close_session",
250
+ title: "Close Session",
191
251
  description: "Close the active session. Flushes E2EE keys, disconnects signaling, clears chat history.",
192
- inputSchema: { type: "object", properties: {} }
252
+ inputSchema: { type: "object", properties: {} },
253
+ outputSchema: {
254
+ type: "object",
255
+ properties: {
256
+ success: { type: "boolean", description: "True if session was closed cleanly" },
257
+ message: { type: "string", description: "Confirmation message" }
258
+ }
259
+ },
260
+ annotations: {
261
+ readOnlyHint: false,
262
+ destructiveHint: true,
263
+ audience: ["agent", "user"],
264
+ priority: 0.7
265
+ }
193
266
  },
194
267
  {
195
268
  name: "srift_approve_join",
269
+ title: "Approve Join Request",
196
270
  description: "Host-only. Approve a pending guest join request by their temp user ID.",
197
271
  inputSchema: {
198
272
  type: "object",
@@ -200,87 +274,261 @@ var init_core = __esm({
200
274
  tempUserId: { type: "string", description: "Temp user ID from session_status pendingJoins" }
201
275
  },
202
276
  required: ["tempUserId"]
277
+ },
278
+ outputSchema: {
279
+ type: "object",
280
+ properties: {
281
+ success: { type: "boolean", description: "True if guest was approved" },
282
+ tempUserId: { type: "string", description: "Approved user temporary ID" }
283
+ }
284
+ },
285
+ annotations: {
286
+ readOnlyHint: false,
287
+ destructiveHint: false,
288
+ audience: ["agent", "user"],
289
+ priority: 0.8
203
290
  }
204
291
  },
205
292
  {
206
293
  name: "srift_reject_join",
294
+ title: "Reject Join Request",
207
295
  description: "Host-only. Reject a pending guest join request.",
208
296
  inputSchema: {
209
297
  type: "object",
210
298
  properties: {
211
- tempUserId: { type: "string" },
212
- reason: { type: "string", description: "Optional rejection reason" }
299
+ tempUserId: { type: "string", description: "Temp user ID from session_status pendingJoins" },
300
+ reason: { type: "string", description: "Optional rejection reason message sent to guest" }
213
301
  },
214
302
  required: ["tempUserId"]
303
+ },
304
+ outputSchema: {
305
+ type: "object",
306
+ properties: {
307
+ success: { type: "boolean", description: "True if guest join request was rejected" },
308
+ tempUserId: { type: "string", description: "Rejected user temporary ID" }
309
+ }
310
+ },
311
+ annotations: {
312
+ readOnlyHint: false,
313
+ destructiveHint: true,
314
+ audience: ["agent", "user"],
315
+ priority: 0.7
215
316
  }
216
317
  },
217
318
  {
218
319
  name: "srift_kick_user",
320
+ title: "Kick Peer",
219
321
  description: "Host-only. Disconnect and ban a peer from the active room.",
220
322
  inputSchema: {
221
323
  type: "object",
222
- properties: { userId: { type: "string" } },
324
+ properties: {
325
+ userId: { type: "string", description: "User ID of the participant to disconnect and remove" }
326
+ },
223
327
  required: ["userId"]
328
+ },
329
+ outputSchema: {
330
+ type: "object",
331
+ properties: {
332
+ success: { type: "boolean", description: "True if peer was successfully kicked" },
333
+ userId: { type: "string", description: "ID of kicked peer" }
334
+ }
335
+ },
336
+ annotations: {
337
+ readOnlyHint: false,
338
+ destructiveHint: true,
339
+ audience: ["agent", "user"],
340
+ priority: 0.6
224
341
  }
225
342
  },
226
343
  {
227
344
  name: "srift_send_file",
345
+ title: "Send File in Session",
228
346
  description: "Offer a file to the connected peer in an existing session. The peer must accept with srift_accept_transfer. Returns a fileId for monitoring. Transport is auto-selected internally.",
229
347
  inputSchema: {
230
348
  type: "object",
231
349
  properties: {
232
- filePath: { type: "string", description: "Absolute path to the file on disk" }
350
+ filePath: { type: "string", description: "Absolute path to the file on local disk" }
233
351
  },
234
352
  required: ["filePath"]
353
+ },
354
+ outputSchema: {
355
+ type: "object",
356
+ properties: {
357
+ fileId: { type: "string", description: "Unique identifier for the offered file transfer" },
358
+ fileName: { type: "string", description: "Base filename of the offered file" },
359
+ fileSize: { type: "number", description: "Size of file in bytes" }
360
+ }
361
+ },
362
+ annotations: {
363
+ readOnlyHint: false,
364
+ destructiveHint: false,
365
+ audience: ["agent", "user"],
366
+ priority: 0.9
235
367
  }
236
368
  },
237
369
  {
238
370
  name: "srift_accept_transfer",
371
+ title: "Accept File Transfer",
239
372
  description: "Accept a pending file offer from the peer and start downloading.",
240
373
  inputSchema: {
241
374
  type: "object",
242
375
  properties: {
243
- fileId: { type: "string", description: "fileId from the inbound file_offer event" },
376
+ fileId: { type: "string", description: "fileId from the inbound file_offer event or transfer list" },
244
377
  saveDir: { type: "string", description: "Absolute path to destination directory (defaults to CWD)" }
245
378
  },
246
379
  required: ["fileId"]
380
+ },
381
+ outputSchema: {
382
+ type: "object",
383
+ properties: {
384
+ success: { type: "boolean", description: "True if transfer acceptance started successfully" },
385
+ fileId: { type: "string", description: "Accepted transfer ID" },
386
+ saveDir: { type: "string", description: "Destination directory path" }
387
+ }
388
+ },
389
+ annotations: {
390
+ readOnlyHint: false,
391
+ destructiveHint: false,
392
+ audience: ["agent", "user"],
393
+ priority: 0.9
247
394
  }
248
395
  },
249
396
  {
250
397
  name: "srift_list_transfers",
398
+ title: "List Active Transfers",
251
399
  description: "List all active and recent transfers with progress, speed, ETA, and status.",
252
- inputSchema: { type: "object", properties: {} }
400
+ inputSchema: { type: "object", properties: {} },
401
+ outputSchema: {
402
+ type: "object",
403
+ properties: {
404
+ transfers: {
405
+ type: "array",
406
+ description: "List of live or recent file transfers",
407
+ items: {
408
+ type: "object",
409
+ properties: {
410
+ fileId: { type: "string", description: "Transfer unique file ID" },
411
+ name: { type: "string", description: "Filename" },
412
+ size: { type: "number", description: "Size in bytes" },
413
+ progress: { type: "number", description: "Transfer percentage 0-100" },
414
+ status: { type: "string", description: "Transfer status (uploading, downloading, completed, failed)" },
415
+ speedKBps: { type: "number", description: "Current transfer speed in KB/s" },
416
+ etaSeconds: { type: "number", description: "Estimated remaining seconds" }
417
+ }
418
+ }
419
+ }
420
+ }
421
+ },
422
+ annotations: {
423
+ readOnlyHint: true,
424
+ destructiveHint: false,
425
+ audience: ["agent", "user"],
426
+ priority: 0.8
427
+ }
253
428
  },
254
429
  {
255
430
  name: "srift_quick_share",
431
+ title: "Quick Share File",
256
432
  description: 'One-shot helper: if no session is active, creates one; then seeds the file and returns a public DOWNLOAD URL (https://srift.app/d/<token>) the recipient can open in any browser or curl. No install needed on their side. Best for "AI wants to deliver a file to the user with zero ceremony".',
257
433
  inputSchema: {
258
434
  type: "object",
259
435
  properties: {
260
- filePath: { type: "string" },
261
- sessionName: { type: "string" }
436
+ filePath: { type: "string", description: "Absolute path to the file to deliver to the recipient" },
437
+ sessionName: { type: "string", description: "Optional session label for the transfer" }
262
438
  },
263
439
  required: ["filePath"]
440
+ },
441
+ outputSchema: {
442
+ type: "object",
443
+ properties: {
444
+ sessionId: { type: "string", description: "Associated session ID" },
445
+ fileId: { type: "string", description: "Transfer file ID" },
446
+ downloadUrl: { type: "string", description: "Public HTTP download URL for recipient" },
447
+ fileName: { type: "string", description: "Base filename" },
448
+ fileSize: { type: "number", description: "File size in bytes" }
449
+ },
450
+ required: ["downloadUrl"]
451
+ },
452
+ annotations: {
453
+ readOnlyHint: false,
454
+ destructiveHint: false,
455
+ audience: ["agent", "user"],
456
+ priority: 1
264
457
  }
265
458
  },
266
459
  {
267
460
  name: "srift_send_chat",
461
+ title: "Send Encrypted Chat",
268
462
  description: "Send an end-to-end encrypted (AES-256-GCM) chat message to the connected peer.",
269
463
  inputSchema: {
270
464
  type: "object",
271
- properties: { message: { type: "string" } },
465
+ properties: {
466
+ message: { type: "string", description: "Plaintext message string to encrypt and transmit" }
467
+ },
272
468
  required: ["message"]
469
+ },
470
+ outputSchema: {
471
+ type: "object",
472
+ properties: {
473
+ success: { type: "boolean", description: "True if message was sent" },
474
+ timestamp: { type: "string", description: "ISO 8601 transmission timestamp" }
475
+ }
476
+ },
477
+ annotations: {
478
+ readOnlyHint: false,
479
+ destructiveHint: false,
480
+ audience: ["agent", "user"],
481
+ priority: 0.8
273
482
  }
274
483
  },
275
484
  {
276
485
  name: "srift_chat_history",
486
+ title: "Read Chat History",
277
487
  description: "Read the locally-decrypted chat log for the current session.",
278
- inputSchema: { type: "object", properties: {} }
488
+ inputSchema: { type: "object", properties: {} },
489
+ outputSchema: {
490
+ type: "object",
491
+ properties: {
492
+ messages: {
493
+ type: "array",
494
+ description: "Decrypted message log for current session",
495
+ items: {
496
+ type: "object",
497
+ properties: {
498
+ sender: { type: "string", description: "Sender display name" },
499
+ message: { type: "string", description: "Decrypted plaintext content" },
500
+ timestamp: { type: "string", description: "ISO timestamp" }
501
+ }
502
+ }
503
+ }
504
+ }
505
+ },
506
+ annotations: {
507
+ readOnlyHint: true,
508
+ destructiveHint: false,
509
+ audience: ["agent", "user"],
510
+ priority: 0.7
511
+ }
279
512
  },
280
513
  {
281
514
  name: "srift_read_state",
515
+ title: "Read Local State",
282
516
  description: "Return the raw .srift-state.json contents (session + active transfers snapshot). Useful for one-shot polling without subscribing to SSE.",
283
- inputSchema: { type: "object", properties: {} }
517
+ inputSchema: { type: "object", properties: {} },
518
+ outputSchema: {
519
+ type: "object",
520
+ properties: {
521
+ session: { type: "object", description: "Current session state snapshot" },
522
+ activeTransfers: { type: "array", description: "Live transfers array" },
523
+ lastUpdated: { type: "string", description: "Last state write timestamp" }
524
+ }
525
+ },
526
+ annotations: {
527
+ readOnlyHint: true,
528
+ destructiveHint: false,
529
+ audience: ["agent", "user"],
530
+ priority: 0.6
531
+ }
284
532
  }
285
533
  ];
286
534
  MCP_RESOURCES = [
@@ -352,7 +600,11 @@ srift_quick_share({ filePath: "/abs/path/file.zip" })
352
600
  \u2192 they open it in any browser, or run:
353
601
  curl -OJ <downloadUrl>
354
602
  wget --content-disposition <downloadUrl>
355
- \u2192 keep the daemon running while the download is in progress
603
+ \u2192 IMPORTANT: the local daemon seeds the file. It keeps running in the
604
+ background after your tool call returns, so the link stays live. But if
605
+ the daemon stops (machine sleeps/reboots, or 'srift daemon stop') the link
606
+ returns 503 "sender is offline" \u2014 SRIFT stores no copy server-side.
607
+ Tell the user to download while their machine is on.
356
608
  \`\`\`
357
609
 
358
610
  **Open a long-lived collaboration room:**
@@ -479,7 +731,13 @@ The peer must call srift_accept_transfer with this fileId.`;
479
731
  ``,
480
732
  `Give the URL to the user \u2014 they open it in any browser, or run:`,
481
733
  ` curl -OJ "${url}"`,
482
- `Keep this srift daemon running while they download.`
734
+ ``,
735
+ `The daemon seeds this file from disk and keeps running in the`,
736
+ `background, so the link stays live after this call returns.`,
737
+ `It stops working if the daemon stops (machine sleeps/reboots, or`,
738
+ `\`srift daemon stop\`) \u2014 SRIFT keeps no server-side copy, so the link`,
739
+ `then returns 503 "sender is offline". Tell the user to grab it while`,
740
+ `this machine is on.`
483
741
  ].join("\n");
484
742
  }
485
743
  case "srift_send_chat":
@@ -2913,7 +3171,7 @@ var __filename = fileURLToPath2(import.meta.url);
2913
3171
  var __dirname = path3.dirname(__filename);
2914
3172
  var DAEMON_PORT3 = parseInt(process.env.SRIFT_DAEMON_PORT || "3822", 10);
2915
3173
  var DAEMON_URL3 = `http://127.0.0.1:${DAEMON_PORT3}`;
2916
- var CLI_VERSION = "2.2.13";
3174
+ var CLI_VERSION = "2.2.15";
2917
3175
  function parseDuration(s) {
2918
3176
  if (!s) return 0;
2919
3177
  const m = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/i.exec(s.trim());
@@ -4538,7 +4796,12 @@ Everything below works without tokens, OAuth, or API keys.
4538
4796
  \u2192 the user opens it in any browser, or downloads via:
4539
4797
  curl -OJ https://srift.app/d/<token>
4540
4798
  wget --content-disposition https://srift.app/d/<token>
4541
- \u2192 keep the daemon running while they download
4799
+ \u2192 the daemon keeps seeding in the background after this command exits.
4800
+ The link stays live while that daemon is running. If it is stopped
4801
+ (srift daemon stop, reboot, laptop sleep) the link returns 503
4802
+ "sender is offline" \u2014 SRIFT keeps no server-side copy of the file.
4803
+ For a link that must outlive your machine, upload it somewhere that
4804
+ does retain the bytes; SRIFT is a relay, not storage.
4542
4805
 
4543
4806
  \u2500\u2500\u2500\u2500 Open a long-lived collaboration room \u2500\u2500\u2500\u2500
4544
4807
  srift session start --name "AI-Collab"
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "srift-transfer",
3
- "version": "2.2.13",
3
+ "version": "2.2.15",
4
+ "mcpName": "app.srift/srift",
4
5
  "description": "Zero-config, zero-token peer-to-peer secure file transfer, encrypted chat, and Model Context Protocol (MCP) server for AI coding agents (Claude, Cursor, Windsurf, Continue, Zed) and developers. End-to-end AES-256-GCM encrypted.",
5
6
  "keywords": [
6
7
  "mcp",