@fil-b/foc-storage-mcp 0.1.9 → 0.2.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/README.md CHANGED
@@ -45,11 +45,24 @@ After installation, update `PRIVATE_KEY` in your config. [Learn more](https://cu
45
45
 
46
46
  ### Claude Code
47
47
 
48
- ```bash
49
- claude mcp add foc-storage -- npx -y @fil-b/foc-storage-mcp
48
+ Add to `.mcp.json`:
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "foc-storage": {
54
+ "command": "npx",
55
+ "args": ["-y", "@fil-b/foc-storage-mcp"],
56
+ "env": {
57
+ "PRIVATE_KEY": "your_private_key_here",
58
+ "FILECOIN_NETWORK": "calibration"
59
+ }
60
+ }
61
+ }
62
+ }
50
63
  ```
51
64
 
52
- Edit `.mcp.json` to add `PRIVATE_KEY`. [Learn more](https://docs.claude.com/en/docs/claude-code/mcp)
65
+ [Learn more](https://docs.claude.com/en/docs/claude-code/mcp)
53
66
 
54
67
  ### Claude Desktop
55
68
 
@@ -188,17 +201,6 @@ Ask naturally in Claude, Cursor, or any MCP client:
188
201
 
189
202
  ## Troubleshooting
190
203
 
191
- **"Unexpected token 'with'" or import syntax error:**
192
-
193
- 1. Verify Node.js >= 20.10.0: `node --version`
194
- 2. Clear npx cache:
195
- - Command: `npx clear-npx-cache`
196
- - Windows: Delete `%LOCALAPPDATA%\npm-cache\_npx`
197
- - Linux/Mac: Delete `~/.npm/_npx`
198
- 3. Force fresh install: `npx --yes @fil-b/foc-storage-mcp@latest`
199
- 4. Restart your IDE
200
- 5. If issue persists, check IDE is using correct Node version
201
-
202
204
  **Server not found:** Verify `npx --version`, check JSON syntax, restart IDE
203
205
 
204
206
  **"PRIVATE_KEY is required":** Add to `env` section, must start with `0x`
@@ -1,17 +1,437 @@
1
1
  #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
+ }) : x)(function(x) {
11
+ if (typeof require !== "undefined") return require.apply(this, arguments);
12
+ throw Error('Dynamic require of "' + x + '" is not supported');
13
+ });
14
+ var __commonJS = (cb, mod) => function __require2() {
15
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+
34
+ // node_modules/.pnpm/dotenv@17.2.3/node_modules/dotenv/package.json
35
+ var require_package = __commonJS({
36
+ "node_modules/.pnpm/dotenv@17.2.3/node_modules/dotenv/package.json"(exports, module) {
37
+ module.exports = {
38
+ name: "dotenv",
39
+ version: "17.2.3",
40
+ description: "Loads environment variables from .env file",
41
+ main: "lib/main.js",
42
+ types: "lib/main.d.ts",
43
+ exports: {
44
+ ".": {
45
+ types: "./lib/main.d.ts",
46
+ require: "./lib/main.js",
47
+ default: "./lib/main.js"
48
+ },
49
+ "./config": "./config.js",
50
+ "./config.js": "./config.js",
51
+ "./lib/env-options": "./lib/env-options.js",
52
+ "./lib/env-options.js": "./lib/env-options.js",
53
+ "./lib/cli-options": "./lib/cli-options.js",
54
+ "./lib/cli-options.js": "./lib/cli-options.js",
55
+ "./package.json": "./package.json"
56
+ },
57
+ scripts: {
58
+ "dts-check": "tsc --project tests/types/tsconfig.json",
59
+ lint: "standard",
60
+ pretest: "npm run lint && npm run dts-check",
61
+ test: "tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000",
62
+ "test:coverage": "tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
63
+ prerelease: "npm test",
64
+ release: "standard-version"
65
+ },
66
+ repository: {
67
+ type: "git",
68
+ url: "git://github.com/motdotla/dotenv.git"
69
+ },
70
+ homepage: "https://github.com/motdotla/dotenv#readme",
71
+ funding: "https://dotenvx.com",
72
+ keywords: [
73
+ "dotenv",
74
+ "env",
75
+ ".env",
76
+ "environment",
77
+ "variables",
78
+ "config",
79
+ "settings"
80
+ ],
81
+ readmeFilename: "README.md",
82
+ license: "BSD-2-Clause",
83
+ devDependencies: {
84
+ "@types/node": "^18.11.3",
85
+ decache: "^4.6.2",
86
+ sinon: "^14.0.1",
87
+ standard: "^17.0.0",
88
+ "standard-version": "^9.5.0",
89
+ tap: "^19.2.0",
90
+ typescript: "^4.8.4"
91
+ },
92
+ engines: {
93
+ node: ">=12"
94
+ },
95
+ browser: {
96
+ fs: false
97
+ }
98
+ };
99
+ }
100
+ });
2
101
 
3
- // src/mcp-server.ts
4
- import { config as config2 } from "dotenv";
102
+ // node_modules/.pnpm/dotenv@17.2.3/node_modules/dotenv/lib/main.js
103
+ var require_main = __commonJS({
104
+ "node_modules/.pnpm/dotenv@17.2.3/node_modules/dotenv/lib/main.js"(exports, module) {
105
+ "use strict";
106
+ var fs3 = __require("fs");
107
+ var path = __require("path");
108
+ var os = __require("os");
109
+ var crypto = __require("crypto");
110
+ var packageJson = require_package();
111
+ var version = packageJson.version;
112
+ var TIPS = [
113
+ "\u{1F510} encrypt with Dotenvx: https://dotenvx.com",
114
+ "\u{1F510} prevent committing .env to code: https://dotenvx.com/precommit",
115
+ "\u{1F510} prevent building .env in docker: https://dotenvx.com/prebuild",
116
+ "\u{1F4E1} add observability to secrets: https://dotenvx.com/ops",
117
+ "\u{1F465} sync secrets across teammates & machines: https://dotenvx.com/ops",
118
+ "\u{1F5C2}\uFE0F backup and recover secrets: https://dotenvx.com/ops",
119
+ "\u2705 audit secrets and track compliance: https://dotenvx.com/ops",
120
+ "\u{1F504} add secrets lifecycle management: https://dotenvx.com/ops",
121
+ "\u{1F511} add access controls to secrets: https://dotenvx.com/ops",
122
+ "\u{1F6E0}\uFE0F run anywhere with `dotenvx run -- yourcommand`",
123
+ "\u2699\uFE0F specify custom .env file path with { path: '/custom/path/.env' }",
124
+ "\u2699\uFE0F enable debug logging with { debug: true }",
125
+ "\u2699\uFE0F override existing env vars with { override: true }",
126
+ "\u2699\uFE0F suppress all logs with { quiet: true }",
127
+ "\u2699\uFE0F write to custom object with { processEnv: myObject }",
128
+ "\u2699\uFE0F load multiple .env files with { path: ['.env.local', '.env'] }"
129
+ ];
130
+ function _getRandomTip() {
131
+ return TIPS[Math.floor(Math.random() * TIPS.length)];
132
+ }
133
+ function parseBoolean(value) {
134
+ if (typeof value === "string") {
135
+ return !["false", "0", "no", "off", ""].includes(value.toLowerCase());
136
+ }
137
+ return Boolean(value);
138
+ }
139
+ function supportsAnsi() {
140
+ return process.stdout.isTTY;
141
+ }
142
+ function dim(text) {
143
+ return supportsAnsi() ? `\x1B[2m${text}\x1B[0m` : text;
144
+ }
145
+ var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
146
+ function parse(src) {
147
+ const obj = {};
148
+ let lines = src.toString();
149
+ lines = lines.replace(/\r\n?/mg, "\n");
150
+ let match;
151
+ while ((match = LINE.exec(lines)) != null) {
152
+ const key = match[1];
153
+ let value = match[2] || "";
154
+ value = value.trim();
155
+ const maybeQuote = value[0];
156
+ value = value.replace(/^(['"`])([\s\S]*)\1$/mg, "$2");
157
+ if (maybeQuote === '"') {
158
+ value = value.replace(/\\n/g, "\n");
159
+ value = value.replace(/\\r/g, "\r");
160
+ }
161
+ obj[key] = value;
162
+ }
163
+ return obj;
164
+ }
165
+ function _parseVault(options) {
166
+ options = options || {};
167
+ const vaultPath = _vaultPath(options);
168
+ options.path = vaultPath;
169
+ const result = DotenvModule.configDotenv(options);
170
+ if (!result.parsed) {
171
+ const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
172
+ err.code = "MISSING_DATA";
173
+ throw err;
174
+ }
175
+ const keys = _dotenvKey(options).split(",");
176
+ const length = keys.length;
177
+ let decrypted;
178
+ for (let i = 0; i < length; i++) {
179
+ try {
180
+ const key = keys[i].trim();
181
+ const attrs = _instructions(result, key);
182
+ decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
183
+ break;
184
+ } catch (error) {
185
+ if (i + 1 >= length) {
186
+ throw error;
187
+ }
188
+ }
189
+ }
190
+ return DotenvModule.parse(decrypted);
191
+ }
192
+ function _warn(message) {
193
+ console.error(`[dotenv@${version}][WARN] ${message}`);
194
+ }
195
+ function _debug(message) {
196
+ console.log(`[dotenv@${version}][DEBUG] ${message}`);
197
+ }
198
+ function _log(message) {
199
+ console.log(`[dotenv@${version}] ${message}`);
200
+ }
201
+ function _dotenvKey(options) {
202
+ if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
203
+ return options.DOTENV_KEY;
204
+ }
205
+ if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
206
+ return process.env.DOTENV_KEY;
207
+ }
208
+ return "";
209
+ }
210
+ function _instructions(result, dotenvKey) {
211
+ let uri;
212
+ try {
213
+ uri = new URL(dotenvKey);
214
+ } catch (error) {
215
+ if (error.code === "ERR_INVALID_URL") {
216
+ const err = new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
217
+ err.code = "INVALID_DOTENV_KEY";
218
+ throw err;
219
+ }
220
+ throw error;
221
+ }
222
+ const key = uri.password;
223
+ if (!key) {
224
+ const err = new Error("INVALID_DOTENV_KEY: Missing key part");
225
+ err.code = "INVALID_DOTENV_KEY";
226
+ throw err;
227
+ }
228
+ const environment = uri.searchParams.get("environment");
229
+ if (!environment) {
230
+ const err = new Error("INVALID_DOTENV_KEY: Missing environment part");
231
+ err.code = "INVALID_DOTENV_KEY";
232
+ throw err;
233
+ }
234
+ const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
235
+ const ciphertext = result.parsed[environmentKey];
236
+ if (!ciphertext) {
237
+ const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
238
+ err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
239
+ throw err;
240
+ }
241
+ return { ciphertext, key };
242
+ }
243
+ function _vaultPath(options) {
244
+ let possibleVaultPath = null;
245
+ if (options && options.path && options.path.length > 0) {
246
+ if (Array.isArray(options.path)) {
247
+ for (const filepath of options.path) {
248
+ if (fs3.existsSync(filepath)) {
249
+ possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
250
+ }
251
+ }
252
+ } else {
253
+ possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
254
+ }
255
+ } else {
256
+ possibleVaultPath = path.resolve(process.cwd(), ".env.vault");
257
+ }
258
+ if (fs3.existsSync(possibleVaultPath)) {
259
+ return possibleVaultPath;
260
+ }
261
+ return null;
262
+ }
263
+ function _resolveHome(envPath) {
264
+ return envPath[0] === "~" ? path.join(os.homedir(), envPath.slice(1)) : envPath;
265
+ }
266
+ function _configVault(options) {
267
+ const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
268
+ const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
269
+ if (debug || !quiet) {
270
+ _log("Loading env from encrypted .env.vault");
271
+ }
272
+ const parsed = DotenvModule._parseVault(options);
273
+ let processEnv = process.env;
274
+ if (options && options.processEnv != null) {
275
+ processEnv = options.processEnv;
276
+ }
277
+ DotenvModule.populate(processEnv, parsed, options);
278
+ return { parsed };
279
+ }
280
+ function configDotenv(options) {
281
+ const dotenvPath = path.resolve(process.cwd(), ".env");
282
+ let encoding = "utf8";
283
+ let processEnv = process.env;
284
+ if (options && options.processEnv != null) {
285
+ processEnv = options.processEnv;
286
+ }
287
+ let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
288
+ let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
289
+ if (options && options.encoding) {
290
+ encoding = options.encoding;
291
+ } else {
292
+ if (debug) {
293
+ _debug("No encoding is specified. UTF-8 is used by default");
294
+ }
295
+ }
296
+ let optionPaths = [dotenvPath];
297
+ if (options && options.path) {
298
+ if (!Array.isArray(options.path)) {
299
+ optionPaths = [_resolveHome(options.path)];
300
+ } else {
301
+ optionPaths = [];
302
+ for (const filepath of options.path) {
303
+ optionPaths.push(_resolveHome(filepath));
304
+ }
305
+ }
306
+ }
307
+ let lastError;
308
+ const parsedAll = {};
309
+ for (const path2 of optionPaths) {
310
+ try {
311
+ const parsed = DotenvModule.parse(fs3.readFileSync(path2, { encoding }));
312
+ DotenvModule.populate(parsedAll, parsed, options);
313
+ } catch (e) {
314
+ if (debug) {
315
+ _debug(`Failed to load ${path2} ${e.message}`);
316
+ }
317
+ lastError = e;
318
+ }
319
+ }
320
+ const populated = DotenvModule.populate(processEnv, parsedAll, options);
321
+ debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
322
+ quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
323
+ if (debug || !quiet) {
324
+ const keysCount = Object.keys(populated).length;
325
+ const shortPaths = [];
326
+ for (const filePath of optionPaths) {
327
+ try {
328
+ const relative = path.relative(process.cwd(), filePath);
329
+ shortPaths.push(relative);
330
+ } catch (e) {
331
+ if (debug) {
332
+ _debug(`Failed to load ${filePath} ${e.message}`);
333
+ }
334
+ lastError = e;
335
+ }
336
+ }
337
+ _log(`injecting env (${keysCount}) from ${shortPaths.join(",")} ${dim(`-- tip: ${_getRandomTip()}`)}`);
338
+ }
339
+ if (lastError) {
340
+ return { parsed: parsedAll, error: lastError };
341
+ } else {
342
+ return { parsed: parsedAll };
343
+ }
344
+ }
345
+ function config2(options) {
346
+ if (_dotenvKey(options).length === 0) {
347
+ return DotenvModule.configDotenv(options);
348
+ }
349
+ const vaultPath = _vaultPath(options);
350
+ if (!vaultPath) {
351
+ _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
352
+ return DotenvModule.configDotenv(options);
353
+ }
354
+ return DotenvModule._configVault(options);
355
+ }
356
+ function decrypt(encrypted, keyStr) {
357
+ const key = Buffer.from(keyStr.slice(-64), "hex");
358
+ let ciphertext = Buffer.from(encrypted, "base64");
359
+ const nonce = ciphertext.subarray(0, 12);
360
+ const authTag = ciphertext.subarray(-16);
361
+ ciphertext = ciphertext.subarray(12, -16);
362
+ try {
363
+ const aesgcm = crypto.createDecipheriv("aes-256-gcm", key, nonce);
364
+ aesgcm.setAuthTag(authTag);
365
+ return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
366
+ } catch (error) {
367
+ const isRange = error instanceof RangeError;
368
+ const invalidKeyLength = error.message === "Invalid key length";
369
+ const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
370
+ if (isRange || invalidKeyLength) {
371
+ const err = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
372
+ err.code = "INVALID_DOTENV_KEY";
373
+ throw err;
374
+ } else if (decryptionFailed) {
375
+ const err = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
376
+ err.code = "DECRYPTION_FAILED";
377
+ throw err;
378
+ } else {
379
+ throw error;
380
+ }
381
+ }
382
+ }
383
+ function populate(processEnv, parsed, options = {}) {
384
+ const debug = Boolean(options && options.debug);
385
+ const override = Boolean(options && options.override);
386
+ const populated = {};
387
+ if (typeof parsed !== "object") {
388
+ const err = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
389
+ err.code = "OBJECT_REQUIRED";
390
+ throw err;
391
+ }
392
+ for (const key of Object.keys(parsed)) {
393
+ if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
394
+ if (override === true) {
395
+ processEnv[key] = parsed[key];
396
+ populated[key] = parsed[key];
397
+ }
398
+ if (debug) {
399
+ if (override === true) {
400
+ _debug(`"${key}" is already defined and WAS overwritten`);
401
+ } else {
402
+ _debug(`"${key}" is already defined and was NOT overwritten`);
403
+ }
404
+ }
405
+ } else {
406
+ processEnv[key] = parsed[key];
407
+ populated[key] = parsed[key];
408
+ }
409
+ }
410
+ return populated;
411
+ }
412
+ var DotenvModule = {
413
+ configDotenv,
414
+ _configVault,
415
+ _parseVault,
416
+ config: config2,
417
+ decrypt,
418
+ parse,
419
+ populate
420
+ };
421
+ module.exports.configDotenv = DotenvModule.configDotenv;
422
+ module.exports._configVault = DotenvModule._configVault;
423
+ module.exports._parseVault = DotenvModule._parseVault;
424
+ module.exports.config = DotenvModule.config;
425
+ module.exports.decrypt = DotenvModule.decrypt;
426
+ module.exports.parse = DotenvModule.parse;
427
+ module.exports.populate = DotenvModule.populate;
428
+ module.exports = DotenvModule;
429
+ }
430
+ });
5
431
 
6
- // src/mastra/index.ts
7
- import { Mastra } from "@mastra/core/mastra";
8
- import { PinoLogger } from "@mastra/loggers";
9
- import { LibSQLStore } from "@mastra/libsql";
432
+ // src/mastra/stdio.ts
10
433
  import { MCPServer } from "@mastra/mcp";
11
434
 
12
- // src/mastra/agents/foc-storage-agent.ts
13
- import { Agent } from "@mastra/core/agent";
14
-
15
435
  // src/mastra/tools/dataset-tools.ts
16
436
  import { createTool } from "@mastra/core";
17
437
 
@@ -19,10 +439,10 @@ import { createTool } from "@mastra/core";
19
439
  import { z as z2 } from "zod";
20
440
 
21
441
  // src/config/index.ts
442
+ var import_dotenv = __toESM(require_main(), 1);
22
443
  import { calibration, mainnet } from "@filoz/synapse-core/chains";
23
- import { config } from "dotenv";
24
444
  import { z } from "zod";
25
- config();
445
+ (0, import_dotenv.config)();
26
446
  var EnvSchema = z.object({
27
447
  PRIVATE_KEY: z.string().min(1, "PRIVATE_KEY is required"),
28
448
  FILECOIN_NETWORK: z.enum(["mainnet", "calibration"]).default("calibration"),
@@ -1565,219 +1985,6 @@ var focStorageTools = {
1565
1985
  };
1566
1986
  var focStorageToolsArray = Object.values(focStorageTools);
1567
1987
 
1568
- // src/mastra/workflows/e2e-file-upload.ts
1569
- import { createStep, createWorkflow } from "@mastra/core/workflows";
1570
- import { z as z4 } from "zod";
1571
- var e2eFileUploadInputSchema = z4.object({
1572
- filePath: z4.string().describe("Absolute path to the file to upload"),
1573
- datasetId: z4.string().optional().describe("Existing dataset ID to use"),
1574
- withCDN: z4.boolean().optional().describe("Enable CDN for faster retrieval").default(false),
1575
- persistenceDays: z4.number().optional().describe("Storage duration in days (default: 180)").default(env.PERSISTENCE_PERIOD_DAYS),
1576
- notificationThresholdDays: z4.number().optional().describe("Notification threshold in days (default: 10)").default(env.RUNOUT_NOTIFICATION_THRESHOLD_DAYS),
1577
- fileMetadata: z4.record(z4.string(), z4.string()).optional().describe("Metadata for the file (max 4 key-value pairs)")
1578
- });
1579
- var checkBalanceStep = createStep({
1580
- id: "checkBalance",
1581
- description: "Check current FIL/USDFC balances and storage metrics",
1582
- inputSchema: e2eFileUploadInputSchema,
1583
- outputSchema: z4.object({
1584
- balances: z4.any(),
1585
- needsPayment: z4.boolean(),
1586
- depositNeeded: z4.number()
1587
- }),
1588
- execute: async ({ inputData, runtimeContext }) => {
1589
- console.log("\u{1F4CA} STEP 1: Checking balances and storage metrics...");
1590
- const fileInfo = await validateFilePath(inputData.filePath);
1591
- const expectedStorageBytes = fileInfo.size;
1592
- const { getBalances: getBalances2 } = focStorageTools;
1593
- const result = await getBalances2.execute({
1594
- context: { storageCapacityBytes: expectedStorageBytes, persistencePeriodDays: inputData.persistenceDays, notificationThresholdDays: inputData.notificationThresholdDays },
1595
- runtimeContext
1596
- });
1597
- if (result.success === false) {
1598
- throw new Error(`Balance check failed: ${result.error || "Unknown error"}`);
1599
- }
1600
- if (!result.checkStorageBalanceResult) {
1601
- throw new Error(`Balance check returned invalid structure`);
1602
- }
1603
- return {
1604
- balances: result.checkStorageBalanceResult,
1605
- needsPayment: !result.checkStorageBalanceResult.isRateSufficient || !result.checkStorageBalanceResult.isLockupSufficient || Number(result.checkStorageBalanceResult.depositNeeded) > 0,
1606
- depositNeeded: Number(result.checkStorageBalanceResult.depositNeeded) || 0
1607
- };
1608
- }
1609
- });
1610
- var processPaymentStep = createStep({
1611
- id: "processPayment",
1612
- description: "Deposit USDFC if insufficient balance detected",
1613
- inputSchema: z4.object({
1614
- balances: z4.any(),
1615
- needsPayment: z4.boolean(),
1616
- depositNeeded: z4.number()
1617
- }),
1618
- outputSchema: z4.object({
1619
- skipped: z4.boolean(),
1620
- txHash: z4.string().optional(),
1621
- depositAmount: z4.string().optional(),
1622
- message: z4.string().optional()
1623
- }),
1624
- execute: async ({ getStepResult, getInitData, runtimeContext }) => {
1625
- const balanceInfo = getStepResult("checkBalance");
1626
- const initData = getInitData();
1627
- const persistenceDays = initData.persistenceDays || 180;
1628
- if (!balanceInfo.needsPayment) {
1629
- console.log("\u2705 STEP 2: Balance and allowances are sufficient, skipping payment");
1630
- return {
1631
- skipped: true,
1632
- message: "Payment not needed - balance and allowances sufficient"
1633
- };
1634
- }
1635
- console.log("\u{1F4B0} STEP 2: Processing payment and/or setting allowances...");
1636
- if (balanceInfo.depositNeeded > 0) {
1637
- console.log(` Deposit needed: ${fromBaseUnits(balanceInfo.depositNeeded, 18)} USDFC`);
1638
- } else {
1639
- console.log(` Deposit sufficient, but allowances need to be set`);
1640
- }
1641
- console.log(` Persistence period: ${persistenceDays} days`);
1642
- const { processPayment: processPayment2 } = focStorageTools;
1643
- const result = await processPayment2.execute({
1644
- context: {
1645
- depositAmount: Number(balanceInfo.depositNeeded)
1646
- },
1647
- runtimeContext
1648
- });
1649
- if (result.success === false || result.error) {
1650
- throw new Error(`Payment failed: ${result.error || "Unknown error"}`);
1651
- }
1652
- console.log(`\u2705 Payment/allowances processed successfully`);
1653
- if (result.txHash) {
1654
- console.log(` TX Hash: ${result.txHash}`);
1655
- }
1656
- if (result.depositAmount) {
1657
- console.log(` Deposit Amount: ${result.depositAmount}`);
1658
- }
1659
- if (result.message) {
1660
- console.log(` ${result.message}`);
1661
- }
1662
- return {
1663
- skipped: false,
1664
- txHash: result.txHash ?? void 0,
1665
- depositAmount: result.depositAmount ?? void 0,
1666
- message: result.message ?? "Payment processed"
1667
- };
1668
- }
1669
- });
1670
- var uploadFileStep = createStep({
1671
- id: "uploadFile",
1672
- description: "Upload file to Filecoin storage",
1673
- inputSchema: z4.object({
1674
- skipped: z4.boolean(),
1675
- txHash: z4.string().optional(),
1676
- depositAmount: z4.string().optional(),
1677
- message: z4.string().optional()
1678
- }),
1679
- outputSchema: z4.object({
1680
- pieceCid: z4.string(),
1681
- txHash: z4.string().optional(),
1682
- fileName: z4.string(),
1683
- fileSize: z4.number(),
1684
- progressLog: z4.array(z4.string()).optional()
1685
- }),
1686
- execute: async ({ getInitData, runtimeContext }) => {
1687
- const initData = getInitData();
1688
- console.log("\u{1F4E4} STEP 3: Uploading file to Filecoin...");
1689
- console.log(` File: ${initData.filePath}`);
1690
- const { uploadFile: uploadFile2 } = focStorageTools;
1691
- const result = await uploadFile2.execute({
1692
- context: {
1693
- filePath: initData.filePath,
1694
- datasetId: initData.datasetId,
1695
- withCDN: initData.withCDN || false,
1696
- autoPayment: false,
1697
- // Already handled in step 2
1698
- metadata: initData.fileMetadata
1699
- },
1700
- runtimeContext
1701
- });
1702
- if (result.success === false || result.error) {
1703
- throw new Error(`File upload failed: ${result.error || "Unknown error"}`);
1704
- }
1705
- console.log(`\u2705 File uploaded successfully`);
1706
- console.log(` Piece CID: ${result.pieceCid}`);
1707
- console.log(` File Name: ${result.fileName}`);
1708
- console.log(` File Size: ${result.fileSize} bytes`);
1709
- return {
1710
- pieceCid: result.pieceCid,
1711
- txHash: result.txHash,
1712
- fileName: result.fileName,
1713
- fileSize: result.fileSize,
1714
- progressLog: result.progressLog
1715
- };
1716
- }
1717
- });
1718
- var summaryStep = createStep({
1719
- id: "summary",
1720
- description: "Generate final summary of the upload process",
1721
- inputSchema: z4.object({
1722
- pieceCid: z4.string(),
1723
- txHash: z4.string().optional(),
1724
- fileName: z4.string(),
1725
- fileSize: z4.number(),
1726
- progressLog: z4.array(z4.string()).optional()
1727
- }),
1728
- outputSchema: z4.object({
1729
- success: z4.boolean(),
1730
- summary: z4.object({
1731
- balance: z4.any(),
1732
- payment: z4.any(),
1733
- upload: z4.any()
1734
- })
1735
- }),
1736
- execute: async ({ getStepResult }) => {
1737
- const balanceInfo = getStepResult("checkBalance");
1738
- const paymentInfo = getStepResult("processPayment");
1739
- const uploadInfo = getStepResult("uploadFile");
1740
- console.log("\n\u{1F389} ============================================");
1741
- console.log(" E2E FILE UPLOAD COMPLETED SUCCESSFULLY");
1742
- console.log("============================================");
1743
- console.log(`\u{1F4CA} Initial Balance: ${balanceInfo.balances.accountStatusMessage}`);
1744
- if (!paymentInfo.skipped) {
1745
- console.log(`\u{1F4B0} Payment Processed: ${paymentInfo.depositAmount} USDFC`);
1746
- console.log(` TX: ${paymentInfo.txHash}`);
1747
- } else {
1748
- console.log(`\u{1F4B0} Payment: Not needed (sufficient balance)`);
1749
- }
1750
- console.log(`\u{1F4E4} File Uploaded: ${uploadInfo.fileName}`);
1751
- console.log(` Size: ${uploadInfo.fileSize} bytes`);
1752
- console.log(` Piece CID: ${uploadInfo.pieceCid}`);
1753
- if (uploadInfo.txHash) {
1754
- console.log(` TX: ${uploadInfo.txHash}`);
1755
- }
1756
- console.log("============================================\n");
1757
- return {
1758
- success: true,
1759
- summary: {
1760
- balance: balanceInfo.balances,
1761
- payment: paymentInfo,
1762
- upload: uploadInfo
1763
- }
1764
- };
1765
- }
1766
- });
1767
- var e2eFileUploadWorkflow = createWorkflow({
1768
- id: "e2eFileUpload",
1769
- description: "Upload a file to Filecoin storage",
1770
- inputSchema: e2eFileUploadInputSchema,
1771
- outputSchema: z4.object({
1772
- success: z4.boolean(),
1773
- summary: z4.object({
1774
- balance: z4.any(),
1775
- payment: z4.any(),
1776
- upload: z4.any()
1777
- })
1778
- })
1779
- }).then(checkBalanceStep).then(processPaymentStep).then(uploadFileStep).then(summaryStep).commit();
1780
-
1781
1988
  // src/mastra/resources/instructions.ts
1782
1989
  var instructions = `You are an AI agent specialized in managing decentralized file storage operations on the Filecoin network using the FOC-Synapse SDK. Your role is to help users store, retrieve, and manage files on Filecoin in a simple, efficient manner.
1783
1990
 
@@ -2011,18 +2218,6 @@ ERROR RESPONSES:
2011
2218
 
2012
2219
  Remember: Your goal is to make decentralized storage as simple as traditional cloud storage, while educating users about the benefits of Filecoin's decentralized approach.`;
2013
2220
 
2014
- // src/mastra/agents/foc-storage-agent.ts
2015
- var focStorageAgent = new Agent({
2016
- name: "FOCStorageAgent",
2017
- description: "AI agent for managing decentralized file storage on Filecoin via FOC-Synapse SDK. Handles file uploads, dataset management, balance checking, and storage operations.",
2018
- instructions,
2019
- model: "openai/gpt-5-mini",
2020
- tools: focStorageTools,
2021
- workflows: {
2022
- e2eFileUpload: e2eFileUploadWorkflow
2023
- }
2024
- });
2025
-
2026
2221
  // src/mastra/resources/index.ts
2027
2222
  var myResources = [
2028
2223
  { uri: "file://instructions", name: "Instructions", mimeType: "application/markdown" }
@@ -2049,10 +2244,10 @@ var focStorageResources = {
2049
2244
  resourceTemplates: async () => myResourceTemplates
2050
2245
  };
2051
2246
 
2052
- // src/mastra/index.ts
2053
- var mcpServer = new MCPServer({
2247
+ // src/mastra/stdio.ts
2248
+ var server = new MCPServer({
2054
2249
  name: "FOC Storage MCP",
2055
- version: "0.1.9",
2250
+ version: "0.2.0",
2056
2251
  description: "Professional-grade MCP server for decentralized file storage on Filecoin Onchain Cloud. Powered by the FOC-Synapse SDK, this server provides AI agents with seamless access to Filecoin's distributed storage network. Upload files with automatic payment handling, organize content in datasets, monitor storage balances, and manage providers - all through intuitive MCP tools. Supports both standard storage and CDN-enabled fast retrieval. Perfect for building AI applications that need persistent, censorship-resistant storage.",
2057
2252
  tools: focStorageTools,
2058
2253
  repository: {
@@ -2065,46 +2260,7 @@ var mcpServer = new MCPServer({
2065
2260
  packageCanonical: "npm",
2066
2261
  resources: focStorageResources
2067
2262
  });
2068
- var mastra = new Mastra({
2069
- agents: {
2070
- focStorageAgent
2071
- },
2072
- workflows: {
2073
- e2eFileUpload: e2eFileUploadWorkflow
2074
- },
2075
- mcpServers: {
2076
- focStorageServer: mcpServer
2077
- },
2078
- storage: new LibSQLStore({
2079
- url: ":memory:"
2080
- }),
2081
- logger: new PinoLogger({
2082
- name: "Mastra",
2083
- level: "silent"
2084
- }),
2085
- bundler: {
2086
- externals: ["@filoz/synapse-sdk"]
2087
- }
2088
- });
2089
-
2090
- // src/mcp-server.ts
2091
- config2();
2092
- async function startMCPServer() {
2093
- try {
2094
- await mcpServer.startStdio();
2095
- } catch (error) {
2096
- console.error("Error starting MCP server:", error);
2097
- process.exit(1);
2098
- }
2099
- }
2100
- process.on("SIGINT", async () => {
2101
- console.error("Shutting down MCP server...");
2102
- await mcpServer.close();
2103
- process.exit(0);
2104
- });
2105
- process.on("SIGTERM", async () => {
2106
- console.error("Shutting down MCP server...");
2107
- await mcpServer.close();
2108
- process.exit(0);
2263
+ server.startStdio().catch((error) => {
2264
+ console.error("Error running MCP server:", error);
2265
+ process.exit(1);
2109
2266
  });
2110
- startMCPServer();
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@fil-b/foc-storage-mcp",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
4
4
  "main": "dist/mcp-server.js",
5
- "bin": "./dist/mcp-server.js",
5
+ "bin": "dist/stdio.js",
6
6
  "scripts": {
7
+ "build:mcp": "tsup src/mastra/stdio.ts --format esm --no-splitting --dts && chmod +x dist/stdio.js",
7
8
  "dev": "mastra dev",
8
9
  "build": "mastra build",
9
10
  "start": "mastra start",
10
11
  "mcp": "tsx src/mcp-server.ts",
11
- "start:mcp": "tsx src/mcp-server.ts",
12
- "build:mcp": "tsup"
12
+ "start:mcp": "tsx src/mcp-server.ts"
13
13
  },
14
14
  "keywords": [
15
15
  "filecoin",
File without changes