agentdrop-mcp-server 0.1.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.
@@ -0,0 +1,49 @@
1
+ import type { Agent, Transfer, InboxTransfer, DownloadResponse, CreateTransferResponse } from "./types";
2
+ export declare class AgentDropApiError extends Error {
3
+ code?: string;
4
+ status?: number;
5
+ constructor(message: string, code?: string, status?: number);
6
+ }
7
+ export interface ApiClientOptions {
8
+ apiKey: string;
9
+ agentId: string;
10
+ apiBase?: string;
11
+ }
12
+ export declare class ApiClient {
13
+ private apiKey;
14
+ private agentId;
15
+ private apiBase;
16
+ constructor(options: ApiClientOptions);
17
+ private headers;
18
+ private request;
19
+ /** GET /v1/agents — list agents on the account. */
20
+ getAgents(): Promise<Agent[]>;
21
+ /** GET /v1/agents/:uuid/inbox — list incoming transfers for an agent. */
22
+ getInbox(agentUuid: string, opts?: {
23
+ status?: string;
24
+ limit?: number;
25
+ }): Promise<{
26
+ transfers: InboxTransfer[];
27
+ total: number;
28
+ }>;
29
+ /** GET /v1/transfers/:id — get transfer details. */
30
+ getTransfer(transferId: string): Promise<Transfer>;
31
+ /** GET /v1/transfers/:id/download — get presigned download URLs. */
32
+ downloadTransfer(transferId: string): Promise<DownloadResponse>;
33
+ /**
34
+ * POST /v1/transfers — create a transfer with files.
35
+ * Uses multipart/form-data built with native FormData.
36
+ */
37
+ createTransfer(data: {
38
+ recipient: string;
39
+ files: Array<{
40
+ name: string;
41
+ buffer: Buffer;
42
+ mimeType?: string;
43
+ }>;
44
+ message?: string;
45
+ expiresIn?: string;
46
+ }): Promise<CreateTransferResponse>;
47
+ /** Get the agent_id this client was configured with. */
48
+ getAgentId(): string;
49
+ }
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiClient = exports.AgentDropApiError = void 0;
4
+ const USER_AGENT = "agentdrop-mcp/0.1.0";
5
+ class AgentDropApiError extends Error {
6
+ constructor(message, code, status) {
7
+ super(message);
8
+ this.name = "AgentDropApiError";
9
+ this.code = code;
10
+ this.status = status;
11
+ }
12
+ }
13
+ exports.AgentDropApiError = AgentDropApiError;
14
+ class ApiClient {
15
+ constructor(options) {
16
+ this.apiKey = options.apiKey;
17
+ this.agentId = options.agentId;
18
+ this.apiBase = (options.apiBase || "https://api.agent-drop.com").replace(/\/+$/, "");
19
+ }
20
+ headers() {
21
+ return {
22
+ Authorization: `Bearer ${this.apiKey}`,
23
+ "User-Agent": USER_AGENT,
24
+ };
25
+ }
26
+ async request(method, path, options) {
27
+ const url = new URL(`${this.apiBase}${path}`);
28
+ if (options?.params) {
29
+ for (const [k, v] of Object.entries(options.params)) {
30
+ url.searchParams.set(k, String(v));
31
+ }
32
+ }
33
+ const headers = {
34
+ ...this.headers(),
35
+ ...(options?.extraHeaders || {}),
36
+ };
37
+ const fetchOptions = { method, headers };
38
+ if (options?.body) {
39
+ fetchOptions.body = options.body;
40
+ }
41
+ const resp = await fetch(url.toString(), fetchOptions);
42
+ if (resp.status >= 400) {
43
+ let errMsg = await resp.text();
44
+ let errCode;
45
+ try {
46
+ const errJson = JSON.parse(errMsg);
47
+ const errObj = errJson.error || {};
48
+ errMsg = errObj.message || errMsg;
49
+ errCode = errObj.code;
50
+ }
51
+ catch {
52
+ // plain text error
53
+ }
54
+ throw new AgentDropApiError(errMsg, errCode, resp.status);
55
+ }
56
+ return (await resp.json());
57
+ }
58
+ /** GET /v1/agents — list agents on the account. */
59
+ async getAgents() {
60
+ const data = await this.request("GET", "/v1/agents");
61
+ return data.agents;
62
+ }
63
+ /** GET /v1/agents/:uuid/inbox — list incoming transfers for an agent. */
64
+ async getInbox(agentUuid, opts) {
65
+ const params = {};
66
+ if (opts?.status)
67
+ params.status = opts.status;
68
+ if (opts?.limit)
69
+ params.limit = opts.limit;
70
+ return this.request("GET", `/v1/agents/${agentUuid}/inbox`, { params });
71
+ }
72
+ /** GET /v1/transfers/:id — get transfer details. */
73
+ async getTransfer(transferId) {
74
+ const data = await this.request("GET", `/v1/transfers/${transferId}`);
75
+ return data;
76
+ }
77
+ /** GET /v1/transfers/:id/download — get presigned download URLs. */
78
+ async downloadTransfer(transferId) {
79
+ return this.request("GET", `/v1/transfers/${transferId}/download`, {
80
+ extraHeaders: {
81
+ "X-AgentDrop-Agent": this.agentId,
82
+ },
83
+ });
84
+ }
85
+ /**
86
+ * POST /v1/transfers — create a transfer with files.
87
+ * Uses multipart/form-data built with native FormData.
88
+ */
89
+ async createTransfer(data) {
90
+ const form = new FormData();
91
+ form.append("sender", this.agentId);
92
+ form.append("recipient", data.recipient);
93
+ form.append("mode", "direct");
94
+ form.append("is_encrypted", "false");
95
+ form.append("expires_in", data.expiresIn || "24h");
96
+ form.append("max_downloads", "10");
97
+ if (data.message) {
98
+ form.append("message", data.message);
99
+ }
100
+ for (const file of data.files) {
101
+ const blob = new Blob([file.buffer], {
102
+ type: file.mimeType || "application/octet-stream",
103
+ });
104
+ form.append("files", blob, file.name);
105
+ }
106
+ // Let fetch set Content-Type with boundary automatically
107
+ const resp = await fetch(`${this.apiBase}/v1/transfers`, {
108
+ method: "POST",
109
+ headers: this.headers(),
110
+ body: form,
111
+ });
112
+ if (resp.status >= 400) {
113
+ let errMsg = await resp.text();
114
+ let errCode;
115
+ try {
116
+ const errJson = JSON.parse(errMsg);
117
+ const errObj = errJson.error || {};
118
+ errMsg = errObj.message || errMsg;
119
+ errCode = errObj.code;
120
+ }
121
+ catch {
122
+ // plain text
123
+ }
124
+ throw new AgentDropApiError(errMsg, errCode, resp.status);
125
+ }
126
+ return (await resp.json());
127
+ }
128
+ /** Get the agent_id this client was configured with. */
129
+ getAgentId() {
130
+ return this.agentId;
131
+ }
132
+ }
133
+ exports.ApiClient = ApiClient;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const index_1 = require("../index");
5
+ (0, index_1.main)().catch((err) => {
6
+ console.error("Fatal:", err);
7
+ process.exit(1);
8
+ });
@@ -0,0 +1,37 @@
1
+ /**
2
+ * InboxPoller — Background polling for the MCP server.
3
+ *
4
+ * Uses the AgentDrop SDK's inbox() to check for new transfers,
5
+ * and fires a callback when unseen ones arrive. The MCP server
6
+ * uses this to push `notifications/message` to the client.
7
+ */
8
+ import type { AgentDrop, Transfer } from "agentdrop-sdk";
9
+ export interface InboxPollerOptions {
10
+ client: AgentDrop;
11
+ /** Milliseconds between polls. Default: 30 000 */
12
+ pollInterval?: number;
13
+ /** Called for each new incoming transfer. */
14
+ onNewTransfer: (transfer: Transfer) => void;
15
+ }
16
+ export declare class InboxPoller {
17
+ private client;
18
+ private pollInterval;
19
+ private onNewTransfer;
20
+ private seenIds;
21
+ private timer;
22
+ isRunning: boolean;
23
+ constructor(opts: InboxPollerOptions);
24
+ /**
25
+ * Snapshot current inbox so we don't alert on pre-existing transfers.
26
+ */
27
+ seedSeen(): Promise<void>;
28
+ /**
29
+ * Single poll cycle: fetch inbox, notify on new transfers.
30
+ */
31
+ checkInbox(): Promise<void>;
32
+ /**
33
+ * Start background polling. Seeds seen IDs first, then polls on interval.
34
+ */
35
+ start(): Promise<void>;
36
+ stop(): void;
37
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * InboxPoller — Background polling for the MCP server.
4
+ *
5
+ * Uses the AgentDrop SDK's inbox() to check for new transfers,
6
+ * and fires a callback when unseen ones arrive. The MCP server
7
+ * uses this to push `notifications/message` to the client.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.InboxPoller = void 0;
11
+ class InboxPoller {
12
+ constructor(opts) {
13
+ this.seenIds = new Set();
14
+ this.timer = null;
15
+ this.isRunning = false;
16
+ this.client = opts.client;
17
+ this.pollInterval = opts.pollInterval ?? 30000;
18
+ this.onNewTransfer = opts.onNewTransfer;
19
+ }
20
+ /**
21
+ * Snapshot current inbox so we don't alert on pre-existing transfers.
22
+ */
23
+ async seedSeen() {
24
+ try {
25
+ const transfers = await this.client.inbox({ status: "active", limit: 50 });
26
+ for (const t of transfers) {
27
+ this.seenIds.add(t.id);
28
+ }
29
+ }
30
+ catch {
31
+ // Non-fatal — worst case we notify on old transfers once
32
+ }
33
+ }
34
+ /**
35
+ * Single poll cycle: fetch inbox, notify on new transfers.
36
+ */
37
+ async checkInbox() {
38
+ try {
39
+ const transfers = await this.client.inbox({ status: "active", limit: 20 });
40
+ for (const t of transfers) {
41
+ if (this.seenIds.has(t.id))
42
+ continue;
43
+ this.seenIds.add(t.id);
44
+ this.onNewTransfer(t);
45
+ }
46
+ }
47
+ catch {
48
+ // Swallow — retry on next poll
49
+ }
50
+ }
51
+ /**
52
+ * Start background polling. Seeds seen IDs first, then polls on interval.
53
+ */
54
+ async start() {
55
+ if (this.isRunning)
56
+ return;
57
+ this.isRunning = true;
58
+ await this.seedSeen();
59
+ this.checkInbox(); // immediate first check after seed
60
+ this.timer = setInterval(() => this.checkInbox(), this.pollInterval);
61
+ }
62
+ stop() {
63
+ this.isRunning = false;
64
+ if (this.timer) {
65
+ clearInterval(this.timer);
66
+ this.timer = null;
67
+ }
68
+ }
69
+ }
70
+ exports.InboxPoller = InboxPoller;
@@ -0,0 +1 @@
1
+ export declare function main(): Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.main = main;
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const agentdrop_sdk_1 = require("agentdrop-sdk");
7
+ const send_file_1 = require("./tools/send-file");
8
+ const check_inbox_1 = require("./tools/check-inbox");
9
+ const download_1 = require("./tools/download");
10
+ const list_agents_1 = require("./tools/list-agents");
11
+ const get_transfer_1 = require("./tools/get-transfer");
12
+ const inbox_poller_1 = require("./inbox-poller");
13
+ async function main() {
14
+ const apiKey = process.env.AGENTDROP_API_KEY;
15
+ const agentId = process.env.AGENTDROP_AGENT_ID;
16
+ const agentUuid = process.env.AGENTDROP_AGENT_UUID;
17
+ const apiBase = process.env.AGENTDROP_API_BASE;
18
+ const pollInterval = parseInt(process.env.AGENTDROP_POLL_INTERVAL || "30000", 10);
19
+ if (!apiKey) {
20
+ console.error("Error: AGENTDROP_API_KEY environment variable is required.\n" +
21
+ "Get your API key at https://agent-drop.com/dashboard/api-keys");
22
+ process.exit(1);
23
+ }
24
+ if (!agentId) {
25
+ console.error("Error: AGENTDROP_AGENT_ID environment variable is required.\n" +
26
+ "This is the agent_id of the agent you want to use (e.g. 'my-agent').");
27
+ process.exit(1);
28
+ }
29
+ const client = new agentdrop_sdk_1.AgentDrop({
30
+ apiKey,
31
+ agentId,
32
+ agentUuid,
33
+ apiBase,
34
+ shield: true,
35
+ shieldStrictness: "standard",
36
+ });
37
+ const server = new mcp_js_1.McpServer({
38
+ name: "agentdrop",
39
+ version: "0.2.0",
40
+ });
41
+ // Register tools
42
+ const tools = [
43
+ (0, send_file_1.createSendFileTool)(client),
44
+ (0, check_inbox_1.createCheckInboxTool)(client),
45
+ (0, download_1.createDownloadTool)(client),
46
+ (0, list_agents_1.createListAgentsTool)(client),
47
+ (0, get_transfer_1.createGetTransferTool)(client),
48
+ ];
49
+ for (const tool of tools) {
50
+ server.tool(tool.name, tool.description, tool.schema.shape, async (params) => {
51
+ try {
52
+ return await tool.handler(params);
53
+ }
54
+ catch (err) {
55
+ return {
56
+ content: [
57
+ {
58
+ type: "text",
59
+ text: `Error: ${err.message || String(err)}`,
60
+ },
61
+ ],
62
+ isError: true,
63
+ };
64
+ }
65
+ });
66
+ }
67
+ const transport = new stdio_js_1.StdioServerTransport();
68
+ await server.connect(transport);
69
+ // Start inbox polling after MCP handshake is established
70
+ if (agentUuid) {
71
+ const poller = new inbox_poller_1.InboxPoller({
72
+ client,
73
+ pollInterval,
74
+ onNewTransfer: (transfer) => {
75
+ const fileNames = transfer.files
76
+ .map((f) => f.name)
77
+ .join(", ");
78
+ const from = transfer.sender || "unknown";
79
+ const msg = transfer.message ? ` — "${transfer.message}"` : "";
80
+ server.sendLoggingMessage({
81
+ level: "info",
82
+ logger: "agentdrop-inbox",
83
+ data: `New AgentDrop transfer from "${from}": ${fileNames}${msg} (ID: ${transfer.id})`,
84
+ });
85
+ },
86
+ });
87
+ await poller.start();
88
+ }
89
+ }
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ import type { AgentDrop } from "agentdrop-sdk";
3
+ declare const schema: z.ZodObject<{
4
+ status: z.ZodOptional<z.ZodEnum<["active", "expired", "deleted"]>>;
5
+ limit: z.ZodOptional<z.ZodNumber>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ status?: "active" | "expired" | "deleted" | undefined;
8
+ limit?: number | undefined;
9
+ }, {
10
+ status?: "active" | "expired" | "deleted" | undefined;
11
+ limit?: number | undefined;
12
+ }>;
13
+ export type CheckInboxInput = z.infer<typeof schema>;
14
+ export declare function createCheckInboxTool(client: AgentDrop): {
15
+ name: "check_inbox";
16
+ description: string;
17
+ schema: z.ZodObject<{
18
+ status: z.ZodOptional<z.ZodEnum<["active", "expired", "deleted"]>>;
19
+ limit: z.ZodOptional<z.ZodNumber>;
20
+ }, "strip", z.ZodTypeAny, {
21
+ status?: "active" | "expired" | "deleted" | undefined;
22
+ limit?: number | undefined;
23
+ }, {
24
+ status?: "active" | "expired" | "deleted" | undefined;
25
+ limit?: number | undefined;
26
+ }>;
27
+ handler: (input: CheckInboxInput) => Promise<{
28
+ content: {
29
+ type: "text";
30
+ text: string;
31
+ }[];
32
+ }>;
33
+ };
34
+ export {};
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCheckInboxTool = createCheckInboxTool;
4
+ const zod_1 = require("zod");
5
+ const schema = zod_1.z.object({
6
+ status: zod_1.z
7
+ .enum(["active", "expired", "deleted"])
8
+ .optional()
9
+ .describe("Filter by transfer status. Default: active"),
10
+ limit: zod_1.z
11
+ .number()
12
+ .optional()
13
+ .describe("Maximum number of transfers to return. Default: 50"),
14
+ });
15
+ function createCheckInboxTool(client) {
16
+ return {
17
+ name: "check_inbox",
18
+ description: "Check your AgentDrop inbox for incoming file transfers. Returns a list of transfers sent to your agent.",
19
+ schema,
20
+ handler: async (input) => {
21
+ const transfers = await client.inbox({
22
+ status: input.status,
23
+ limit: input.limit,
24
+ });
25
+ if (transfers.length === 0) {
26
+ return {
27
+ content: [
28
+ {
29
+ type: "text",
30
+ text: `Inbox is empty. No ${input.status || "active"} transfers found.`,
31
+ },
32
+ ],
33
+ };
34
+ }
35
+ const lines = [
36
+ `Found ${transfers.length} transfer(s):`,
37
+ ``,
38
+ ];
39
+ for (const t of transfers) {
40
+ const fileList = t.files.map((f) => f.name).join(", ");
41
+ const totalSize = t.totalSize || 0;
42
+ lines.push(` [${t.id}]`);
43
+ lines.push(` From: ${t.sender}`);
44
+ lines.push(` Files: ${fileList} (${t.files.length} file(s), ${formatBytes(totalSize)})`);
45
+ lines.push(` Status: ${t.status}`);
46
+ lines.push(` Created: ${t.createdAt || "N/A"}`);
47
+ lines.push(` Expires: ${t.expiresAt || "N/A"}`);
48
+ if (t.message) {
49
+ lines.push(` Message: ${t.message}`);
50
+ }
51
+ lines.push(``);
52
+ }
53
+ return {
54
+ content: [{ type: "text", text: lines.join("\n") }],
55
+ };
56
+ },
57
+ };
58
+ }
59
+ function formatBytes(bytes) {
60
+ if (bytes === 0)
61
+ return "0 B";
62
+ const units = ["B", "KB", "MB", "GB"];
63
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
64
+ return `${(bytes / Math.pow(1024, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
65
+ }
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+ import type { AgentDrop } from "agentdrop-sdk";
3
+ declare const schema: z.ZodObject<{
4
+ transfer_id: z.ZodString;
5
+ output_dir: z.ZodOptional<z.ZodString>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ transfer_id: string;
8
+ output_dir?: string | undefined;
9
+ }, {
10
+ transfer_id: string;
11
+ output_dir?: string | undefined;
12
+ }>;
13
+ export type DownloadInput = z.infer<typeof schema>;
14
+ export declare function createDownloadTool(client: AgentDrop): {
15
+ name: "download_transfer";
16
+ description: string;
17
+ schema: z.ZodObject<{
18
+ transfer_id: z.ZodString;
19
+ output_dir: z.ZodOptional<z.ZodString>;
20
+ }, "strip", z.ZodTypeAny, {
21
+ transfer_id: string;
22
+ output_dir?: string | undefined;
23
+ }, {
24
+ transfer_id: string;
25
+ output_dir?: string | undefined;
26
+ }>;
27
+ handler: (input: DownloadInput) => Promise<{
28
+ content: {
29
+ type: "text";
30
+ text: string;
31
+ }[];
32
+ isError?: undefined;
33
+ } | {
34
+ content: {
35
+ type: "text";
36
+ text: string;
37
+ }[];
38
+ isError: boolean;
39
+ }>;
40
+ };
41
+ export {};
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createDownloadTool = createDownloadTool;
7
+ const zod_1 = require("zod");
8
+ const path_1 = __importDefault(require("path"));
9
+ const agentdrop_sdk_1 = require("agentdrop-sdk");
10
+ const schema = zod_1.z.object({
11
+ transfer_id: zod_1.z.string().describe("The transfer ID to download files from"),
12
+ output_dir: zod_1.z
13
+ .string()
14
+ .optional()
15
+ .describe("Directory to save downloaded files. Default: ./downloads"),
16
+ });
17
+ function createDownloadTool(client) {
18
+ return {
19
+ name: "download_transfer",
20
+ description: "Download files from an AgentDrop transfer. The SDK handles decryption and Shield malware scanning automatically.",
21
+ schema,
22
+ handler: async (input) => {
23
+ const outputDir = path_1.default.resolve(input.output_dir || "./downloads");
24
+ // Create a Transfer object the SDK can work with
25
+ const transfer = new agentdrop_sdk_1.Transfer({ id: input.transfer_id });
26
+ try {
27
+ const results = await client.download(transfer, outputDir);
28
+ if (results.length === 0) {
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: `No files downloaded from transfer ${input.transfer_id}.`,
34
+ },
35
+ ],
36
+ };
37
+ }
38
+ const lines = [
39
+ `Downloaded ${results.length} file(s) from transfer ${input.transfer_id}:`,
40
+ ``,
41
+ ];
42
+ for (const file of results) {
43
+ lines.push(` ${file.path}`);
44
+ if (file.warnings && file.warnings.length > 0) {
45
+ for (const w of file.warnings) {
46
+ lines.push(` Warning: ${w}`);
47
+ }
48
+ }
49
+ }
50
+ return {
51
+ content: [{ type: "text", text: lines.join("\n") }],
52
+ };
53
+ }
54
+ catch (err) {
55
+ if (err instanceof agentdrop_sdk_1.ShieldBlockError) {
56
+ return {
57
+ content: [
58
+ {
59
+ type: "text",
60
+ text: `Shield blocked download: ${err.message}`,
61
+ },
62
+ ],
63
+ isError: true,
64
+ };
65
+ }
66
+ throw err;
67
+ }
68
+ },
69
+ };
70
+ }
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ import type { AgentDrop } from "agentdrop-sdk";
3
+ declare const schema: z.ZodObject<{
4
+ transfer_id: z.ZodString;
5
+ }, "strip", z.ZodTypeAny, {
6
+ transfer_id: string;
7
+ }, {
8
+ transfer_id: string;
9
+ }>;
10
+ export type GetTransferInput = z.infer<typeof schema>;
11
+ export declare function createGetTransferTool(client: AgentDrop): {
12
+ name: "get_transfer";
13
+ description: string;
14
+ schema: z.ZodObject<{
15
+ transfer_id: z.ZodString;
16
+ }, "strip", z.ZodTypeAny, {
17
+ transfer_id: string;
18
+ }, {
19
+ transfer_id: string;
20
+ }>;
21
+ handler: (input: GetTransferInput) => Promise<{
22
+ content: {
23
+ type: "text";
24
+ text: string;
25
+ }[];
26
+ }>;
27
+ };
28
+ export {};
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGetTransferTool = createGetTransferTool;
4
+ const zod_1 = require("zod");
5
+ const USER_AGENT = "agentdrop-mcp/0.1.0";
6
+ const schema = zod_1.z.object({
7
+ transfer_id: zod_1.z.string().describe("The transfer ID to look up"),
8
+ });
9
+ function createGetTransferTool(client) {
10
+ return {
11
+ name: "get_transfer",
12
+ description: "Get details about a specific AgentDrop transfer, including file list, status, and download count.",
13
+ schema,
14
+ handler: async (input) => {
15
+ // The SDK doesn't expose a getTransfer() method, so use direct fetch
16
+ const apiBase = client.apiBase || "https://api.agent-drop.com";
17
+ const apiKey = client.apiKey;
18
+ const resp = await fetch(`${apiBase}/v1/transfers/${input.transfer_id}`, {
19
+ headers: {
20
+ Authorization: `Bearer ${apiKey}`,
21
+ "User-Agent": USER_AGENT,
22
+ },
23
+ });
24
+ if (resp.status >= 400) {
25
+ const errText = await resp.text();
26
+ throw new Error(`Failed to get transfer (HTTP ${resp.status}): ${errText}`);
27
+ }
28
+ const t = (await resp.json());
29
+ const fileList = t.files
30
+ .map((f) => `${f.name} (${formatBytes(f.size)})`)
31
+ .join(", ");
32
+ const lines = [
33
+ `Transfer ${t.id}:`,
34
+ ``,
35
+ ` Sender: ${t.sender}`,
36
+ ` Recipient: ${t.recipient}`,
37
+ ` Mode: ${t.mode}`,
38
+ ` Status: ${t.status}`,
39
+ ` Files: ${fileList}`,
40
+ ` Size: ${formatBytes(t.total_size)}`,
41
+ ` Downloads: ${t.downloads}/${t.max_downloads}`,
42
+ ` Encrypted: ${t.is_encrypted ? "yes" : "no"}`,
43
+ ` Created: ${t.created_at}`,
44
+ ` Expires: ${t.expires_at}`,
45
+ ];
46
+ if (t.message) {
47
+ lines.push(` Message: ${t.message}`);
48
+ }
49
+ return {
50
+ content: [{ type: "text", text: lines.join("\n") }],
51
+ };
52
+ },
53
+ };
54
+ }
55
+ function formatBytes(bytes) {
56
+ if (bytes === 0)
57
+ return "0 B";
58
+ const units = ["B", "KB", "MB", "GB"];
59
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
60
+ return `${(bytes / Math.pow(1024, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
61
+ }
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import type { AgentDrop } from "agentdrop-sdk";
3
+ export declare function createListAgentsTool(client: AgentDrop): {
4
+ name: "list_agents";
5
+ description: string;
6
+ schema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
7
+ handler: () => Promise<{
8
+ content: {
9
+ type: "text";
10
+ text: string;
11
+ }[];
12
+ }>;
13
+ };
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createListAgentsTool = createListAgentsTool;
4
+ const zod_1 = require("zod");
5
+ const USER_AGENT = "agentdrop-mcp/0.1.0";
6
+ const schema = zod_1.z.object({});
7
+ function createListAgentsTool(client) {
8
+ return {
9
+ name: "list_agents",
10
+ description: "List your AgentDrop agents and their connection status. Shows all agents registered on your account.",
11
+ schema,
12
+ handler: async () => {
13
+ // The SDK doesn't expose a listAgents() method, so use direct fetch
14
+ const apiBase = client.apiBase || "https://api.agent-drop.com";
15
+ const apiKey = client.apiKey;
16
+ const resp = await fetch(`${apiBase}/v1/agents`, {
17
+ headers: {
18
+ Authorization: `Bearer ${apiKey}`,
19
+ "User-Agent": USER_AGENT,
20
+ },
21
+ });
22
+ if (resp.status >= 400) {
23
+ const errText = await resp.text();
24
+ throw new Error(`Failed to list agents (HTTP ${resp.status}): ${errText}`);
25
+ }
26
+ const data = (await resp.json());
27
+ const agents = data.agents;
28
+ if (agents.length === 0) {
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: "No agents registered on this account.",
34
+ },
35
+ ],
36
+ };
37
+ }
38
+ const lines = [`Found ${agents.length} agent(s):`, ``];
39
+ for (const a of agents) {
40
+ lines.push(` [${a.agent_id}]`);
41
+ lines.push(` UUID: ${a.id}`);
42
+ lines.push(` Name: ${a.name}`);
43
+ lines.push(` Type: ${a.agent_type}`);
44
+ lines.push(` Status: ${a.connection_status}`);
45
+ if (a.last_seen_at) {
46
+ lines.push(` Last seen: ${a.last_seen_at}`);
47
+ }
48
+ lines.push(``);
49
+ }
50
+ return {
51
+ content: [{ type: "text", text: lines.join("\n") }],
52
+ };
53
+ },
54
+ };
55
+ }
@@ -0,0 +1,53 @@
1
+ import { z } from "zod";
2
+ import type { AgentDrop } from "agentdrop-sdk";
3
+ declare const schema: z.ZodObject<{
4
+ recipient: z.ZodString;
5
+ file_path: z.ZodString;
6
+ message: z.ZodOptional<z.ZodString>;
7
+ expires_in: z.ZodOptional<z.ZodString>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ recipient: string;
10
+ file_path: string;
11
+ message?: string | undefined;
12
+ expires_in?: string | undefined;
13
+ }, {
14
+ recipient: string;
15
+ file_path: string;
16
+ message?: string | undefined;
17
+ expires_in?: string | undefined;
18
+ }>;
19
+ export type SendFileInput = z.infer<typeof schema>;
20
+ export declare function createSendFileTool(client: AgentDrop): {
21
+ name: "send_file";
22
+ description: string;
23
+ schema: z.ZodObject<{
24
+ recipient: z.ZodString;
25
+ file_path: z.ZodString;
26
+ message: z.ZodOptional<z.ZodString>;
27
+ expires_in: z.ZodOptional<z.ZodString>;
28
+ }, "strip", z.ZodTypeAny, {
29
+ recipient: string;
30
+ file_path: string;
31
+ message?: string | undefined;
32
+ expires_in?: string | undefined;
33
+ }, {
34
+ recipient: string;
35
+ file_path: string;
36
+ message?: string | undefined;
37
+ expires_in?: string | undefined;
38
+ }>;
39
+ handler: (input: SendFileInput) => Promise<{
40
+ content: {
41
+ type: "text";
42
+ text: string;
43
+ }[];
44
+ isError: boolean;
45
+ } | {
46
+ content: {
47
+ type: "text";
48
+ text: string;
49
+ }[];
50
+ isError?: undefined;
51
+ }>;
52
+ };
53
+ export {};
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createSendFileTool = createSendFileTool;
7
+ const zod_1 = require("zod");
8
+ const promises_1 = __importDefault(require("fs/promises"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const schema = zod_1.z.object({
11
+ recipient: zod_1.z.string().describe("The recipient agent_id (e.g. 'my-agent')"),
12
+ file_path: zod_1.z.string().describe("Absolute path to the file to send"),
13
+ message: zod_1.z.string().optional().describe("Optional message to attach"),
14
+ expires_in: zod_1.z
15
+ .string()
16
+ .optional()
17
+ .describe("Expiry duration (e.g. '24h', '7d'). Default: 24h"),
18
+ });
19
+ function createSendFileTool(client) {
20
+ return {
21
+ name: "send_file",
22
+ description: "Send a file to another AgentDrop agent. The file is encrypted end-to-end via the SDK and delivered to the recipient's inbox.",
23
+ schema,
24
+ handler: async (input) => {
25
+ const filePath = path_1.default.resolve(input.file_path);
26
+ // Verify file exists
27
+ try {
28
+ await promises_1.default.access(filePath);
29
+ }
30
+ catch {
31
+ return {
32
+ content: [
33
+ {
34
+ type: "text",
35
+ text: `Error: File not found at ${filePath}`,
36
+ },
37
+ ],
38
+ isError: true,
39
+ };
40
+ }
41
+ const result = await client.send(input.recipient, [filePath], {
42
+ message: input.message,
43
+ encrypt: true,
44
+ expiresIn: input.expires_in,
45
+ });
46
+ const fileName = path_1.default.basename(filePath);
47
+ const totalSize = result.total_size || 0;
48
+ const lines = [
49
+ `Transfer created successfully.`,
50
+ ``,
51
+ ` ID: ${result.id}`,
52
+ ` Recipient: ${result.recipient}`,
53
+ ` File: ${fileName} (${formatBytes(totalSize)})`,
54
+ ` Status: ${result.status}`,
55
+ ` Expires: ${result.expires_at}`,
56
+ ` Encrypted: ${result.is_encrypted ? "yes" : "no"}`,
57
+ ];
58
+ if (result.url) {
59
+ lines.push(` URL: ${result.url}`);
60
+ }
61
+ if (input.message) {
62
+ lines.push(` Message: ${input.message}`);
63
+ }
64
+ return {
65
+ content: [{ type: "text", text: lines.join("\n") }],
66
+ };
67
+ },
68
+ };
69
+ }
70
+ function formatBytes(bytes) {
71
+ if (bytes === 0)
72
+ return "0 B";
73
+ const units = ["B", "KB", "MB", "GB"];
74
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
75
+ return `${(bytes / Math.pow(1024, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
76
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * MCP-server specific types.
3
+ *
4
+ * The SDK (`agentdrop-sdk`) exports Transfer, AgentDrop, DownloadedFile,
5
+ * ShieldBlockError, etc. These types are only kept for the two tools
6
+ * (list_agents, get_transfer) that still use direct fetch calls.
7
+ */
8
+ /** An agent registered on the account (from GET /v1/agents). */
9
+ export interface Agent {
10
+ id: string;
11
+ agent_id: string;
12
+ agent_type: string;
13
+ name: string;
14
+ description?: string;
15
+ connection_status: string;
16
+ key_version: number;
17
+ last_seen_at: string | null;
18
+ metadata?: Record<string, unknown>;
19
+ created_at: string;
20
+ }
21
+ /** A file within a transfer. */
22
+ export interface TransferFile {
23
+ id: string;
24
+ name: string;
25
+ size: number;
26
+ content_type: string;
27
+ }
28
+ /** A file transfer (from GET /v1/transfers/:id). */
29
+ export interface TransferDetail {
30
+ id: string;
31
+ sender: string;
32
+ recipient: string;
33
+ status: string;
34
+ mode: string;
35
+ message?: string;
36
+ file_count: number;
37
+ total_size: number;
38
+ is_encrypted: boolean;
39
+ auto_delete: boolean;
40
+ files: TransferFile[];
41
+ downloads: number;
42
+ max_downloads: number;
43
+ expires_at: string;
44
+ created_at: string;
45
+ download_url?: string;
46
+ }
47
+ /** API error shape. */
48
+ export interface ApiError {
49
+ error: {
50
+ code: string;
51
+ message: string;
52
+ details?: Array<{
53
+ field: string;
54
+ message: string;
55
+ }>;
56
+ };
57
+ }
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * MCP-server specific types.
4
+ *
5
+ * The SDK (`agentdrop-sdk`) exports Transfer, AgentDrop, DownloadedFile,
6
+ * ShieldBlockError, etc. These types are only kept for the two tools
7
+ * (list_agents, get_transfer) that still use direct fetch calls.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "agentdrop-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for AgentDrop — encrypted file transfer for AI agents",
5
+ "bin": {
6
+ "agentdrop-mcp": "./dist/bin/cli.js"
7
+ },
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist/"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "prepublishOnly": "npm run build",
18
+ "start": "node dist/bin/cli.js"
19
+ },
20
+ "keywords": [
21
+ "agentdrop",
22
+ "mcp",
23
+ "ai",
24
+ "agents",
25
+ "file-transfer",
26
+ "model-context-protocol"
27
+ ],
28
+ "author": "AgentDrop",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/qFlav/AgentDrop"
33
+ },
34
+ "homepage": "https://agent-drop.com",
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.12.0",
40
+ "agentdrop-sdk": "^0.2.2",
41
+ "zod": "^3.24.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.11.0",
45
+ "typescript": "^5.3.0",
46
+ "vitest": "^4.1.1"
47
+ }
48
+ }