yungle-mcp 0.2.1 → 0.2.3
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.mjs +22 -14
- package/dist/server.d.ts +2 -0
- package/dist/server.mjs +22 -14
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -74,7 +74,7 @@ async function uploadPath(endpoint, target, path, size, doFetch = fetch) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// src/server.ts
|
|
77
|
-
var MCP_VERSION = true ? "0.2.
|
|
77
|
+
var MCP_VERSION = true ? "0.2.3" : "dev";
|
|
78
78
|
function createServer(client, opts = { canWrite: false, canEmail: false, local: false }) {
|
|
79
79
|
const server = new McpServer(
|
|
80
80
|
{ name: "yungle", version: MCP_VERSION },
|
|
@@ -103,7 +103,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
103
103
|
"list_transfers",
|
|
104
104
|
{
|
|
105
105
|
title: "List transfers",
|
|
106
|
-
description: "Answers: what have I sent recently? what is expiring soon? which deliveries has nobody picked up? who did I send that to? Returns recent transfers with size, recipients, download count, expiry date and share link.
|
|
106
|
+
description: "Answers: what have I sent recently? what is expiring soon? which deliveries has nobody picked up? who did I send that to? Returns recent transfers with size, recipients, download count, expiry date and share link.",
|
|
107
107
|
inputSchema: {},
|
|
108
108
|
annotations: { title: "List transfers", readOnlyHint: true }
|
|
109
109
|
},
|
|
@@ -116,8 +116,8 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
116
116
|
"get_transfer",
|
|
117
117
|
{
|
|
118
118
|
title: "Transfer detail",
|
|
119
|
-
description: "Answers: what is in this transfer? was it delivered? has a specific recipient opened it? is it safe to share? Returns the files with their malware-scan verdicts, plus per-recipient delivery and download status
|
|
120
|
-
inputSchema: { id: z.string().describe("Transfer id
|
|
119
|
+
description: "Answers: what is in this transfer? was it delivered? has a specific recipient opened it? is it safe to share? Returns the files with their malware-scan verdicts, plus per-recipient delivery and download status for one transfer id.",
|
|
120
|
+
inputSchema: { id: z.string().describe("Transfer id.") },
|
|
121
121
|
annotations: { title: "Transfer detail", readOnlyHint: true }
|
|
122
122
|
},
|
|
123
123
|
async ({ id }) => ok(wrapUntrusted(await client.getTransfer(id)))
|
|
@@ -126,7 +126,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
126
126
|
"get_transfer_downloads",
|
|
127
127
|
{
|
|
128
128
|
title: "Download receipts",
|
|
129
|
-
description: "Answers: did the client download it? when? how many times? Returns download events with timestamps and per-recipient status.
|
|
129
|
+
description: "Answers: did the client download it? when? how many times? Returns download events with timestamps and per-recipient status. One visit can fetch several files: events that share a sessionId belong to the same visit.",
|
|
130
130
|
inputSchema: { id: z.string() },
|
|
131
131
|
annotations: { title: "Download receipts", readOnlyHint: true }
|
|
132
132
|
},
|
|
@@ -146,7 +146,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
146
146
|
"get_collection",
|
|
147
147
|
{
|
|
148
148
|
title: "Collection detail",
|
|
149
|
-
description: "Answers: what is the link for this collection? how much is in it? when does it expire? Returns one collection with its secret share link, file count, total size and expiry.
|
|
149
|
+
description: "Answers: what is the link for this collection? how much is in it? when does it expire? Returns one collection with its secret share link, file count, total size and expiry.",
|
|
150
150
|
inputSchema: { id: z.string() },
|
|
151
151
|
annotations: { title: "Collection detail", readOnlyHint: true }
|
|
152
152
|
},
|
|
@@ -156,7 +156,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
156
156
|
"list_collection_files",
|
|
157
157
|
{
|
|
158
158
|
title: "Files in a collection",
|
|
159
|
-
description: 'Answers: what files are in this collection? what is in this folder? are the raws uploaded yet? Returns filenames, sizes and types. Omit folderId for every file; pass "root" for the top level only, or a folder id
|
|
159
|
+
description: 'Answers: what files are in this collection? what is in this folder? are the raws uploaded yet? Returns filenames, sizes and types. Omit folderId for every file; pass "root" for the top level only, or a folder id. Filenames only \u2014 never file contents.',
|
|
160
160
|
inputSchema: { id: z.string(), folderId: z.string().optional() },
|
|
161
161
|
annotations: { title: "Files in a collection", readOnlyHint: true }
|
|
162
162
|
},
|
|
@@ -166,7 +166,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
166
166
|
"list_folders",
|
|
167
167
|
{
|
|
168
168
|
title: "Folders in a collection",
|
|
169
|
-
description: "Answers: how is this collection organised? what folders exist? Returns the folder tree. Ordered by depth then path
|
|
169
|
+
description: "Answers: how is this collection organised? what folders exist? Returns the folder tree. Ordered by depth, then path (not a pre-order traversal). Each folder carries its parentId and its full path.",
|
|
170
170
|
inputSchema: { id: z.string() },
|
|
171
171
|
annotations: { title: "Folders in a collection", readOnlyHint: true }
|
|
172
172
|
},
|
|
@@ -199,9 +199,8 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
199
199
|
description: [
|
|
200
200
|
"Prepare a draft transfer for files the user will upload themselves, and return its id.",
|
|
201
201
|
"",
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
"you have in hand, use create_share_link instead."
|
|
202
|
+
"It uploads no files and emails nobody: the user uploads the files and sends the transfer",
|
|
203
|
+
"themselves, in the browser or with the Yungle CLI."
|
|
205
204
|
].join("\n"),
|
|
206
205
|
inputSchema: {
|
|
207
206
|
files: z.array(
|
|
@@ -323,7 +322,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
323
322
|
{
|
|
324
323
|
title: "Email a transfer to recipients",
|
|
325
324
|
description: [
|
|
326
|
-
"Email an existing transfer
|
|
325
|
+
"Email an existing transfer, one already shared as a link, to up to 10",
|
|
327
326
|
"recipients. The user is asked to confirm every send, with the addresses and message",
|
|
328
327
|
"shown to them; if this client cannot ask, nothing is sent and you get the link to pass on."
|
|
329
328
|
].join("\n"),
|
|
@@ -369,9 +368,11 @@ async function startStdioServer() {
|
|
|
369
368
|
const apiKey = process.env.YUNGLE_API_KEY?.trim();
|
|
370
369
|
if (!apiKey) {
|
|
371
370
|
process.stderr.write(
|
|
372
|
-
"YUNGLE_API_KEY is not set. Create a key at https://yungle.co/dashboard/settings/api\n"
|
|
371
|
+
"YUNGLE_API_KEY is not set: listing tools only. Create a key at https://yungle.co/dashboard/settings/api\n"
|
|
373
372
|
);
|
|
374
|
-
|
|
373
|
+
const server2 = createServer(keylessClient(), { canWrite: false, canEmail: false, local: true });
|
|
374
|
+
await server2.connect(new StdioServerTransport());
|
|
375
|
+
return;
|
|
375
376
|
}
|
|
376
377
|
const client = new YungleClient({
|
|
377
378
|
apiKey,
|
|
@@ -397,6 +398,12 @@ async function startStdioServer() {
|
|
|
397
398
|
});
|
|
398
399
|
await server.connect(new StdioServerTransport());
|
|
399
400
|
}
|
|
401
|
+
function keylessClient() {
|
|
402
|
+
const message = "YUNGLE_API_KEY is not set, so this server cannot reach Yungle. Create a key at https://yungle.co/dashboard/settings/api and add it to this MCP server's environment, or connect to the hosted server at https://yungle.co/mcp instead.";
|
|
403
|
+
return new Proxy({}, {
|
|
404
|
+
get: () => () => Promise.reject(new Error(message))
|
|
405
|
+
});
|
|
406
|
+
}
|
|
400
407
|
function ok(payload) {
|
|
401
408
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
402
409
|
}
|
|
@@ -406,5 +413,6 @@ function fail(message) {
|
|
|
406
413
|
export {
|
|
407
414
|
MCP_VERSION,
|
|
408
415
|
createServer,
|
|
416
|
+
keylessClient,
|
|
409
417
|
startStdioServer
|
|
410
418
|
};
|
package/dist/server.d.ts
CHANGED
|
@@ -23,3 +23,5 @@ export declare function createServer(client: YungleClient, opts?: ServerOptions)
|
|
|
23
23
|
* emails nobody and needs only transfers:write.
|
|
24
24
|
*/
|
|
25
25
|
export declare function startStdioServer(): Promise<void>;
|
|
26
|
+
/** A client whose every call explains that there is no key. */
|
|
27
|
+
export declare function keylessClient(): YungleClient;
|
package/dist/server.mjs
CHANGED
|
@@ -345,7 +345,7 @@ async function uploadPath(endpoint, target, path, size, doFetch = fetch) {
|
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
// src/server.ts
|
|
348
|
-
var MCP_VERSION = true ? "0.2.
|
|
348
|
+
var MCP_VERSION = true ? "0.2.3" : "dev";
|
|
349
349
|
function createServer(client, opts = { canWrite: false, canEmail: false, local: false }) {
|
|
350
350
|
const server = new McpServer(
|
|
351
351
|
{ name: "yungle", version: MCP_VERSION },
|
|
@@ -374,7 +374,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
374
374
|
"list_transfers",
|
|
375
375
|
{
|
|
376
376
|
title: "List transfers",
|
|
377
|
-
description: "Answers: what have I sent recently? what is expiring soon? which deliveries has nobody picked up? who did I send that to? Returns recent transfers with size, recipients, download count, expiry date and share link.
|
|
377
|
+
description: "Answers: what have I sent recently? what is expiring soon? which deliveries has nobody picked up? who did I send that to? Returns recent transfers with size, recipients, download count, expiry date and share link.",
|
|
378
378
|
inputSchema: {},
|
|
379
379
|
annotations: { title: "List transfers", readOnlyHint: true }
|
|
380
380
|
},
|
|
@@ -387,8 +387,8 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
387
387
|
"get_transfer",
|
|
388
388
|
{
|
|
389
389
|
title: "Transfer detail",
|
|
390
|
-
description: "Answers: what is in this transfer? was it delivered? has a specific recipient opened it? is it safe to share? Returns the files with their malware-scan verdicts, plus per-recipient delivery and download status
|
|
391
|
-
inputSchema: { id: z.string().describe("Transfer id
|
|
390
|
+
description: "Answers: what is in this transfer? was it delivered? has a specific recipient opened it? is it safe to share? Returns the files with their malware-scan verdicts, plus per-recipient delivery and download status for one transfer id.",
|
|
391
|
+
inputSchema: { id: z.string().describe("Transfer id.") },
|
|
392
392
|
annotations: { title: "Transfer detail", readOnlyHint: true }
|
|
393
393
|
},
|
|
394
394
|
async ({ id }) => ok(wrapUntrusted(await client.getTransfer(id)))
|
|
@@ -397,7 +397,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
397
397
|
"get_transfer_downloads",
|
|
398
398
|
{
|
|
399
399
|
title: "Download receipts",
|
|
400
|
-
description: "Answers: did the client download it? when? how many times? Returns download events with timestamps and per-recipient status.
|
|
400
|
+
description: "Answers: did the client download it? when? how many times? Returns download events with timestamps and per-recipient status. One visit can fetch several files: events that share a sessionId belong to the same visit.",
|
|
401
401
|
inputSchema: { id: z.string() },
|
|
402
402
|
annotations: { title: "Download receipts", readOnlyHint: true }
|
|
403
403
|
},
|
|
@@ -417,7 +417,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
417
417
|
"get_collection",
|
|
418
418
|
{
|
|
419
419
|
title: "Collection detail",
|
|
420
|
-
description: "Answers: what is the link for this collection? how much is in it? when does it expire? Returns one collection with its secret share link, file count, total size and expiry.
|
|
420
|
+
description: "Answers: what is the link for this collection? how much is in it? when does it expire? Returns one collection with its secret share link, file count, total size and expiry.",
|
|
421
421
|
inputSchema: { id: z.string() },
|
|
422
422
|
annotations: { title: "Collection detail", readOnlyHint: true }
|
|
423
423
|
},
|
|
@@ -427,7 +427,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
427
427
|
"list_collection_files",
|
|
428
428
|
{
|
|
429
429
|
title: "Files in a collection",
|
|
430
|
-
description: 'Answers: what files are in this collection? what is in this folder? are the raws uploaded yet? Returns filenames, sizes and types. Omit folderId for every file; pass "root" for the top level only, or a folder id
|
|
430
|
+
description: 'Answers: what files are in this collection? what is in this folder? are the raws uploaded yet? Returns filenames, sizes and types. Omit folderId for every file; pass "root" for the top level only, or a folder id. Filenames only \u2014 never file contents.',
|
|
431
431
|
inputSchema: { id: z.string(), folderId: z.string().optional() },
|
|
432
432
|
annotations: { title: "Files in a collection", readOnlyHint: true }
|
|
433
433
|
},
|
|
@@ -437,7 +437,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
437
437
|
"list_folders",
|
|
438
438
|
{
|
|
439
439
|
title: "Folders in a collection",
|
|
440
|
-
description: "Answers: how is this collection organised? what folders exist? Returns the folder tree. Ordered by depth then path
|
|
440
|
+
description: "Answers: how is this collection organised? what folders exist? Returns the folder tree. Ordered by depth, then path (not a pre-order traversal). Each folder carries its parentId and its full path.",
|
|
441
441
|
inputSchema: { id: z.string() },
|
|
442
442
|
annotations: { title: "Folders in a collection", readOnlyHint: true }
|
|
443
443
|
},
|
|
@@ -470,9 +470,8 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
470
470
|
description: [
|
|
471
471
|
"Prepare a draft transfer for files the user will upload themselves, and return its id.",
|
|
472
472
|
"",
|
|
473
|
-
"
|
|
474
|
-
"
|
|
475
|
-
"you have in hand, use create_share_link instead."
|
|
473
|
+
"It uploads no files and emails nobody: the user uploads the files and sends the transfer",
|
|
474
|
+
"themselves, in the browser or with the Yungle CLI."
|
|
476
475
|
].join("\n"),
|
|
477
476
|
inputSchema: {
|
|
478
477
|
files: z.array(
|
|
@@ -594,7 +593,7 @@ function createServer(client, opts = { canWrite: false, canEmail: false, local:
|
|
|
594
593
|
{
|
|
595
594
|
title: "Email a transfer to recipients",
|
|
596
595
|
description: [
|
|
597
|
-
"Email an existing transfer
|
|
596
|
+
"Email an existing transfer, one already shared as a link, to up to 10",
|
|
598
597
|
"recipients. The user is asked to confirm every send, with the addresses and message",
|
|
599
598
|
"shown to them; if this client cannot ask, nothing is sent and you get the link to pass on."
|
|
600
599
|
].join("\n"),
|
|
@@ -640,9 +639,11 @@ async function startStdioServer() {
|
|
|
640
639
|
const apiKey = process.env.YUNGLE_API_KEY?.trim();
|
|
641
640
|
if (!apiKey) {
|
|
642
641
|
process.stderr.write(
|
|
643
|
-
"YUNGLE_API_KEY is not set. Create a key at https://yungle.co/dashboard/settings/api\n"
|
|
642
|
+
"YUNGLE_API_KEY is not set: listing tools only. Create a key at https://yungle.co/dashboard/settings/api\n"
|
|
644
643
|
);
|
|
645
|
-
|
|
644
|
+
const server2 = createServer(keylessClient(), { canWrite: false, canEmail: false, local: true });
|
|
645
|
+
await server2.connect(new StdioServerTransport());
|
|
646
|
+
return;
|
|
646
647
|
}
|
|
647
648
|
const client = new YungleClient({
|
|
648
649
|
apiKey,
|
|
@@ -668,6 +669,12 @@ async function startStdioServer() {
|
|
|
668
669
|
});
|
|
669
670
|
await server.connect(new StdioServerTransport());
|
|
670
671
|
}
|
|
672
|
+
function keylessClient() {
|
|
673
|
+
const message = "YUNGLE_API_KEY is not set, so this server cannot reach Yungle. Create a key at https://yungle.co/dashboard/settings/api and add it to this MCP server's environment, or connect to the hosted server at https://yungle.co/mcp instead.";
|
|
674
|
+
return new Proxy({}, {
|
|
675
|
+
get: () => () => Promise.reject(new Error(message))
|
|
676
|
+
});
|
|
677
|
+
}
|
|
671
678
|
function ok(payload) {
|
|
672
679
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
673
680
|
}
|
|
@@ -677,6 +684,7 @@ function fail(message) {
|
|
|
677
684
|
export {
|
|
678
685
|
MCP_VERSION,
|
|
679
686
|
createServer,
|
|
687
|
+
keylessClient,
|
|
680
688
|
startStdioServer
|
|
681
689
|
};
|
|
682
690
|
await startStdioServer();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yungle-mcp",
|
|
3
3
|
"mcpName": "co.yungle/yungle",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "MCP server for Yungle: let Claude, Cursor or any MCP client read your file transfers, share files and send them, with your approval.",
|
|
7
7
|
"bin": {
|