@storage4everyone/mcp 0.1.1 → 0.2.1

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/env.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export declare const ENV_FILES: string[];
2
+ /** Minimal dotenv parser: KEY=value, optional `export`, quotes, and trailing # comments. */
3
+ export declare function parseEnv(text: string): Record<string, string>;
4
+ /**
5
+ * Fills S4E_API_KEY and S4E_BUCKET from the project's .env files when the MCP config does
6
+ * not set them. Values already in `env` always win. Returns the files that supplied a value.
7
+ */
8
+ export declare function loadEnvFiles(dir: string, env?: NodeJS.ProcessEnv): Promise<string[]>;
package/dist/env.js ADDED
@@ -0,0 +1,51 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ // Same order as Next.js and Vite in development: the first file that sets a value wins.
4
+ export const ENV_FILES = ['.env.development.local', '.env.local', '.env.development', '.env'];
5
+ // S4E_API_BASE and S4E_MCP_* are never read from files: a cloned project must not be able
6
+ // to send your key to another server or turn on write tools.
7
+ const FILE_KEYS = ['S4E_API_KEY', 'S4E_BUCKET'];
8
+ /** Minimal dotenv parser: KEY=value, optional `export`, quotes, and trailing # comments. */
9
+ export function parseEnv(text) {
10
+ const out = {};
11
+ for (const line of text.split(/\r?\n/)) {
12
+ const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
13
+ if (!match)
14
+ continue;
15
+ let value = match[2].trim();
16
+ const quote = value[0];
17
+ if ((quote === '"' || quote === "'") && value.lastIndexOf(quote) > 0)
18
+ value = value.slice(1, value.lastIndexOf(quote));
19
+ else
20
+ value = value.replace(/\s+#.*$/, '');
21
+ out[match[1]] = value;
22
+ }
23
+ return out;
24
+ }
25
+ /**
26
+ * Fills S4E_API_KEY and S4E_BUCKET from the project's .env files when the MCP config does
27
+ * not set them. Values already in `env` always win. Returns the files that supplied a value.
28
+ */
29
+ export async function loadEnvFiles(dir, env = process.env) {
30
+ const used = [];
31
+ for (const file of ENV_FILES) {
32
+ let text;
33
+ try {
34
+ text = await readFile(path.join(dir, file), 'utf8');
35
+ }
36
+ catch {
37
+ continue;
38
+ }
39
+ const values = parseEnv(text);
40
+ let supplied = false;
41
+ for (const key of FILE_KEYS) {
42
+ if (!env[key] && values[key]) {
43
+ env[key] = values[key];
44
+ supplied = true;
45
+ }
46
+ }
47
+ if (supplied)
48
+ used.push(file);
49
+ }
50
+ return used;
51
+ }
package/dist/index.js CHANGED
@@ -3,14 +3,28 @@ import { realpath } from 'node:fs/promises';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { Storage4Everyone } from '@storage4everyone/sdk';
5
5
  import { createServer } from './server.js';
6
+ import { loadEnvFiles } from './env.js';
6
7
  // stdout carries the MCP protocol; all diagnostics go to stderr.
8
+ // Claude Code passes the project folder in CLAUDE_PROJECT_DIR and may start servers elsewhere;
9
+ // other agents (VS Code, Gemini CLI) start them in the project folder.
10
+ const project = process.env.CLAUDE_PROJECT_DIR || process.cwd();
11
+ const rootSetting = process.env.S4E_MCP_ROOT || project;
12
+ let root;
13
+ try {
14
+ root = await realpath(rootSetting);
15
+ }
16
+ catch {
17
+ console.error(`S4E_MCP_ROOT folder not found: ${rootSetting}. Create it or set S4E_MCP_ROOT to an existing folder.`);
18
+ process.exit(1);
19
+ }
20
+ const envFiles = await loadEnvFiles(project);
7
21
  if (!process.env.S4E_API_KEY) {
8
- console.error('S4E_API_KEY is not set. Create an API key in the Storage4Everyone dashboard and pass it in the MCP server env.');
22
+ console.error(`S4E_API_KEY is not set. Add S4E_API_KEY=s4e_live_... to .env.local in ${project}, or pass it in the MCP server env. ` +
23
+ 'Create a key in the Storage4Everyone dashboard under API Keys.');
9
24
  process.exit(1);
10
25
  }
11
26
  // No automatic retries: rate-limit errors go back to the agent with their retry time.
12
27
  const client = new Storage4Everyone({ maxRetries: 0 });
13
- const root = await realpath(process.env.S4E_MCP_ROOT || process.cwd());
14
28
  const allowWrite = process.env.S4E_MCP_ALLOW_WRITE === 'true';
15
29
  await createServer({ client, root, allowWrite }).connect(new StdioServerTransport());
16
- console.error(`Storage4Everyone MCP server ready (folder: ${root}, write tools: ${allowWrite ? 'on' : 'off'}).`);
30
+ console.error(`Storage4Everyone MCP server ready (folder: ${root}, settings from: ${envFiles.length ? envFiles.join(', ') : 'MCP env'}, write tools: ${allowWrite ? 'on' : 'off'}).`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storage4everyone/mcp",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Model Context Protocol server that lets AI agents use Storage4Everyone buckets with your API key",
5
5
  "type": "module",
6
6
  "main": "./dist/server.js",