filemayor-mcp 4.0.5 → 4.0.7
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 +19 -16
- package/index.mjs +109 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**The FileMayor MCP server.** Drive your intelligent filesystem clerk from Claude, Cursor, Zed, or any [Model Context Protocol](https://modelcontextprotocol.io) client.
|
|
4
4
|
|
|
5
|
-
`v4.0.
|
|
5
|
+
`v4.0.7` · Node ≥20 · [filemayor.com/mcp](https://filemayor.com/mcp)
|
|
6
6
|
|
|
7
7
|
[](https://smithery.ai/server/filemayor-mcp)
|
|
8
8
|
|
|
@@ -29,10 +29,7 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
|
|
|
29
29
|
"mcpServers": {
|
|
30
30
|
"filemayor": {
|
|
31
31
|
"command": "npx",
|
|
32
|
-
"args": ["-y", "filemayor-mcp"]
|
|
33
|
-
"env": {
|
|
34
|
-
"GEMINI_API_KEY": "your-key-here"
|
|
35
|
-
}
|
|
32
|
+
"args": ["-y", "filemayor-mcp"]
|
|
36
33
|
}
|
|
37
34
|
}
|
|
38
35
|
}
|
|
@@ -40,6 +37,8 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
|
|
|
40
37
|
|
|
41
38
|
Restart Claude. You'll see the FileMayor tools in the 🔧 menu.
|
|
42
39
|
|
|
40
|
+
> **No API key needed.** When Claude is the client, Claude IS the AI — it calls `filemayor_explain` to audit the folder, reasons about the moves, and passes them directly to `filemayor_apply`. The `filemayor_plan` tool (which calls an external AI) is only for non-Claude clients.
|
|
41
|
+
|
|
43
42
|
## Wire it into Cursor / Zed / other MCP clients
|
|
44
43
|
|
|
45
44
|
Any client that speaks MCP over stdio works. Point it at `filemayor-mcp` and you're done.
|
|
@@ -51,7 +50,7 @@ Any client that speaks MCP over stdio works. Point it at `filemayor-mcp` and you
|
|
|
51
50
|
| `filemayor_scan` | List every file in a directory tree with size + category. |
|
|
52
51
|
| `filemayor_analyze` | Deep audit: duplicates, bloat, junk, largest dirs. |
|
|
53
52
|
| `filemayor_explain` | Folder health score (0–100), atomic bundles, insights. |
|
|
54
|
-
| `filemayor_plan` | AI-generated plan
|
|
53
|
+
| `filemayor_plan` | For non-Claude clients: AI-generated plan using whichever key is set (`ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, or `OPENAI_API_KEY`). When Claude is the client, skip this — Claude reasons directly. |
|
|
55
54
|
| `filemayor_apply` | Execute the most recent plan from `filemayor_plan`. |
|
|
56
55
|
| `filemayor_rollback` | Reverse the most recent applied plan. |
|
|
57
56
|
| `filemayor_organize` | Deterministic auto-organize by extension (no AI). |
|
|
@@ -104,26 +103,30 @@ Doesn't apply. FileMayor MCP is **Node.js**, not Python. Its single runtime depe
|
|
|
104
103
|
|
|
105
104
|
This is the supply-chain risk that applies to *every* MCP server in principle, ours included. Our mitigations:
|
|
106
105
|
|
|
107
|
-
- The whole server is one file: [`mcp/index.mjs`](https://github.com/Hrypopo/FileMayor/blob/main/mcp/index.mjs) — ~
|
|
106
|
+
- The whole server is one file: [`mcp/index.mjs`](https://github.com/Hrypopo/FileMayor/blob/main/mcp/index.mjs) — ~580 lines. Tool declarations and their `case` handlers sit side-by-side. You can read it end-to-end in about 15 minutes. (`--audit` prints the exact current line count under `verifyBy`, so the number can't drift out of date.)
|
|
108
107
|
- `grep -E "fetch\(|http\.request|https\.request|net\." mcp/index.mjs` returns nothing. The server makes no outbound network calls of its own.
|
|
109
|
-
- The *only* off-machine egress path is the optional
|
|
110
|
-
-
|
|
108
|
+
- The *only* off-machine egress path is the optional AI call inside `filemayor_plan`, gated by an AI provider key (`ANTHROPIC_API_KEY` → Claude, `GEMINI_API_KEY` → Gemini, or `OPENAI_API_KEY` → GPT-4o-mini). The first key present wins. Without any key, the tool returns an error and nothing is sent. With a key, only directory metadata (paths, sizes, extensions) is sent — no file contents. When Claude is the MCP client, `filemayor_plan` is not needed at all.
|
|
109
|
+
- The GitHub repo is the canonical source.
|
|
111
110
|
|
|
112
111
|
### What `--audit` reports
|
|
113
112
|
|
|
114
113
|
```json
|
|
115
114
|
{
|
|
116
115
|
"package": "filemayor-mcp",
|
|
117
|
-
"version": "4.0.
|
|
116
|
+
"version": "4.0.7",
|
|
118
117
|
"transport": { "type": "stdio", "networkListeners": false, "ports": [] },
|
|
119
118
|
"outbound": {
|
|
120
119
|
"defaultEgress": "none",
|
|
121
120
|
"optionalEgress": {
|
|
122
121
|
"enabled": false,
|
|
123
|
-
"
|
|
124
|
-
|
|
122
|
+
"destinations": [
|
|
123
|
+
"https://api.anthropic.com",
|
|
124
|
+
"https://generativelanguage.googleapis.com",
|
|
125
|
+
"https://api.openai.com"
|
|
126
|
+
],
|
|
127
|
+
"trigger": "filemayor_plan tool only (not used when Claude is the MCP client)",
|
|
125
128
|
"payload": "directory metadata only — no file contents",
|
|
126
|
-
"gatedBy": "GEMINI_API_KEY
|
|
129
|
+
"gatedBy": "one of ANTHROPIC_API_KEY / GEMINI_API_KEY / OPENAI_API_KEY (none currently set)"
|
|
127
130
|
}
|
|
128
131
|
},
|
|
129
132
|
"runtimeSafeguards": {
|
|
@@ -137,7 +140,7 @@ This is the supply-chain risk that applies to *every* MCP server in principle, o
|
|
|
137
140
|
"dependencies": { "runtime": ["@modelcontextprotocol/sdk"], "python": false, "starlette": false, "fastapi": false },
|
|
138
141
|
"source": "https://github.com/Hrypopo/FileMayor/blob/main/mcp/index.mjs",
|
|
139
142
|
"verifyBy": [
|
|
140
|
-
"Read mcp/index.mjs end-to-end —
|
|
143
|
+
"Read mcp/index.mjs end-to-end — the report prints the exact current line count here.",
|
|
141
144
|
"grep for outbound calls: rg \"fetch\\(|http\\.request|https\\.request|net\\.\" mcp/index.mjs",
|
|
142
145
|
"Run `npm view filemayor-mcp` and compare the published shasum to the GitHub tag.",
|
|
143
146
|
"Run this same `--audit` after upgrading to detect changes to transport / egress / tool list."
|
|
@@ -149,7 +152,7 @@ This is the supply-chain risk that applies to *every* MCP server in principle, o
|
|
|
149
152
|
|
|
150
153
|
A viral tutorial recommended `pip install mcp-audit && mcp-audit scan` to audit installed MCP servers. As of this release, `mcp-audit` is **not published on PyPI**, and the `mcp-audit` package on npm is a 408-byte hello-world stub (v0.0.1, published April 2025, no functional code). The tutorial directs viewers to comment on a social-media post to receive the "real" tool by direct message — that's a social-engineering pattern, not a verifiable tool. Be skeptical of any installer not on a public registry under a known maintainer.
|
|
151
154
|
|
|
152
|
-
If a real, signed, open-source MCP scanner emerges, we'll link to it from this section and publish the verdict against `filemayor-mcp`. Until then, `--audit` + reading the
|
|
155
|
+
If a real, signed, open-source MCP scanner emerges, we'll link to it from this section and publish the verdict against `filemayor-mcp`. Until then, `--audit` + reading the ~580 lines of source is the trustworthy path.
|
|
153
156
|
|
|
154
157
|
### Hardened-runtime safeguards (the engine, not just the MCP shell)
|
|
155
158
|
|
|
@@ -161,7 +164,7 @@ Every tool that touches disk runs through the same security layers the CLI and D
|
|
|
161
164
|
| Plan-then-apply gate | `_explain` and `_plan` are read-only. Only `_apply` mutates disk. |
|
|
162
165
|
| Journaled moves | Every move is recorded to a write-ahead log on disk. |
|
|
163
166
|
| Rollback | `_rollback` reverses the last applied plan; `_undo_last` rolls back N specific moves. The journal survives crashes. |
|
|
164
|
-
| No-telemetry | The MCP server makes no analytics calls. The only off-machine call is the documented, opt-in
|
|
167
|
+
| No-telemetry | The MCP server makes no analytics calls. The only off-machine call is the documented, opt-in AI planner in `filemayor_plan` (not used when Claude is the client). |
|
|
165
168
|
|
|
166
169
|
## License
|
|
167
170
|
|
package/index.mjs
CHANGED
|
@@ -12,7 +12,17 @@
|
|
|
12
12
|
*
|
|
13
13
|
* All operations honor the same hardened-runtime safeguards as the CLI
|
|
14
14
|
* and Desktop app: path jailing, system-dir blocking, journaled moves,
|
|
15
|
-
* full rollback.
|
|
15
|
+
* full rollback.
|
|
16
|
+
*
|
|
17
|
+
* When Claude is the MCP client, Claude IS the AI — no external API key
|
|
18
|
+
* is needed. filemayor_plan is available for non-Claude clients (Cursor,
|
|
19
|
+
* Zed, scripts) and picks up whichever key is set in the environment, in
|
|
20
|
+
* this priority order:
|
|
21
|
+
* ANTHROPIC_API_KEY → Claude (claude-haiku-4-5-20251001)
|
|
22
|
+
* GEMINI_API_KEY → Gemini (gemini-2.0-flash)
|
|
23
|
+
* OPENAI_API_KEY → GPT-4o-mini
|
|
24
|
+
* The key selects the provider; the core planner (cli/core/ai) dispatches
|
|
25
|
+
* the actual call. See detectAiProvider() below.
|
|
16
26
|
* ═══════════════════════════════════════════════════════════════════
|
|
17
27
|
*/
|
|
18
28
|
|
|
@@ -75,6 +85,24 @@ function checkDir(p) {
|
|
|
75
85
|
return { resolved: v.resolved };
|
|
76
86
|
}
|
|
77
87
|
|
|
88
|
+
// Pick the external AI provider for filemayor_plan from whichever key is
|
|
89
|
+
// set, in priority order. The core planner (cli/core/ai) dispatches on
|
|
90
|
+
// FM_AI_PROVIDER + FM_AI_API_KEY, so the keys below are the single source
|
|
91
|
+
// of truth for what the tool actually supports. Returns null if no key.
|
|
92
|
+
const AI_PROVIDERS = [
|
|
93
|
+
{ env: 'ANTHROPIC_API_KEY', provider: 'anthropic', label: 'Claude (claude-haiku-4-5-20251001)' },
|
|
94
|
+
{ env: 'GEMINI_API_KEY', provider: 'gemini', label: 'Gemini (gemini-2.0-flash)' },
|
|
95
|
+
{ env: 'OPENAI_API_KEY', provider: 'openai', label: 'GPT-4o-mini' },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
function detectAiProvider() {
|
|
99
|
+
for (const p of AI_PROVIDERS) {
|
|
100
|
+
const apiKey = process.env[p.env];
|
|
101
|
+
if (apiKey) return { ...p, apiKey };
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
78
106
|
// ─── Tool definitions ──────────────────────────────────────────────
|
|
79
107
|
|
|
80
108
|
const TOOLS = [
|
|
@@ -115,7 +143,7 @@ const TOOLS = [
|
|
|
115
143
|
},
|
|
116
144
|
{
|
|
117
145
|
name: 'filemayor_plan',
|
|
118
|
-
description: '
|
|
146
|
+
description: 'For non-Claude MCP clients: generates a move plan from a natural-language prompt using an available AI provider (ANTHROPIC_API_KEY → Claude, GEMINI_API_KEY → Gemini, OPENAI_API_KEY → GPT-4o-mini). When Claude IS the client, skip this tool — instead call filemayor_explain to audit the folder, reason about the moves yourself, then pass them directly to filemayor_apply as the "moves" parameter.',
|
|
119
147
|
inputSchema: {
|
|
120
148
|
type: 'object',
|
|
121
149
|
properties: {
|
|
@@ -127,8 +155,24 @@ const TOOLS = [
|
|
|
127
155
|
},
|
|
128
156
|
{
|
|
129
157
|
name: 'filemayor_apply',
|
|
130
|
-
description: 'Execute
|
|
131
|
-
inputSchema: {
|
|
158
|
+
description: 'Execute a move plan and journal every operation for rollback. Two modes: (1) Claude-native — pass a "moves" array of {src, dst} objects that you generated yourself after calling filemayor_explain; (2) external-AI — call filemayor_plan first (Anthropic / Gemini / OpenAI, by key), then call this with no arguments to execute that plan. The journal is written to disk so filemayor_rollback can undo it.',
|
|
159
|
+
inputSchema: {
|
|
160
|
+
type: 'object',
|
|
161
|
+
properties: {
|
|
162
|
+
moves: {
|
|
163
|
+
type: 'array',
|
|
164
|
+
description: 'Optional. Array of move operations to execute. Each entry must have "src" (absolute source path) and "dst" (absolute destination path). When provided, skips filemayor_plan entirely — use this when Claude is generating the plan directly.',
|
|
165
|
+
items: {
|
|
166
|
+
type: 'object',
|
|
167
|
+
properties: {
|
|
168
|
+
src: { type: 'string', description: 'Absolute path of the file to move.' },
|
|
169
|
+
dst: { type: 'string', description: 'Absolute destination path.' },
|
|
170
|
+
},
|
|
171
|
+
required: ['src', 'dst'],
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
132
176
|
},
|
|
133
177
|
{
|
|
134
178
|
name: 'filemayor_rollback',
|
|
@@ -253,17 +297,51 @@ async function handleTool(name, args) {
|
|
|
253
297
|
const c = checkDir(args.path);
|
|
254
298
|
if (c.error) return err(c.error);
|
|
255
299
|
if (!args.prompt) return err('prompt is required');
|
|
256
|
-
const
|
|
257
|
-
if (!
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
300
|
+
const ai = detectAiProvider();
|
|
301
|
+
if (!ai) {
|
|
302
|
+
return err(
|
|
303
|
+
'filemayor_plan needs an AI provider key for non-Claude clients. Set one of:\n' +
|
|
304
|
+
' ANTHROPIC_API_KEY → Claude (claude-haiku-4-5-20251001)\n' +
|
|
305
|
+
' GEMINI_API_KEY → Gemini (gemini-2.0-flash)\n' +
|
|
306
|
+
' OPENAI_API_KEY → GPT-4o-mini\n\n' +
|
|
307
|
+
'If Claude is your MCP client you do NOT need this tool — Claude is already the AI. ' +
|
|
308
|
+
'Instead: call filemayor_explain to audit the folder, reason about the moves yourself, ' +
|
|
309
|
+
'then pass them directly to filemayor_apply as the "moves" parameter.'
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
// Route the core planner to the selected provider. Restore the
|
|
313
|
+
// previous env afterwards so we never leak state between calls.
|
|
314
|
+
const prevProvider = process.env.FM_AI_PROVIDER;
|
|
315
|
+
const prevKey = process.env.FM_AI_API_KEY;
|
|
316
|
+
process.env.FM_AI_PROVIDER = ai.provider;
|
|
317
|
+
process.env.FM_AI_API_KEY = ai.apiKey;
|
|
318
|
+
try {
|
|
319
|
+
const engine = new CureEngine(c.resolved, ai.apiKey);
|
|
320
|
+
const plan = await engine.generatePlan(args.prompt);
|
|
321
|
+
activeCureEngine = engine;
|
|
322
|
+
return ok({ provider: ai.label, ...plan });
|
|
323
|
+
} finally {
|
|
324
|
+
if (prevProvider === undefined) delete process.env.FM_AI_PROVIDER;
|
|
325
|
+
else process.env.FM_AI_PROVIDER = prevProvider;
|
|
326
|
+
if (prevKey === undefined) delete process.env.FM_AI_API_KEY;
|
|
327
|
+
else process.env.FM_AI_API_KEY = prevKey;
|
|
328
|
+
}
|
|
262
329
|
}
|
|
263
330
|
|
|
264
331
|
case 'filemayor_apply': {
|
|
332
|
+
// Claude-native path: caller supplies explicit move list.
|
|
333
|
+
if (Array.isArray(args.moves) && args.moves.length > 0) {
|
|
334
|
+
const applyer = new ApplyEngine();
|
|
335
|
+
const plan = args.moves.map(m => ({ source: m.src, destination: m.dst }));
|
|
336
|
+
const results = await applyer.apply(plan);
|
|
337
|
+
return ok(results);
|
|
338
|
+
}
|
|
339
|
+
// External-planner path: filemayor_plan must have run first.
|
|
265
340
|
if (!activeCureEngine || !activeCureEngine.plan) {
|
|
266
|
-
return err(
|
|
341
|
+
return err(
|
|
342
|
+
'No active plan. Either call filemayor_plan first, or pass a "moves" array directly ' +
|
|
343
|
+
'(recommended when Claude is the client).'
|
|
344
|
+
);
|
|
267
345
|
}
|
|
268
346
|
const applyer = new ApplyEngine();
|
|
269
347
|
const results = await applyer.apply(activeCureEngine.plan);
|
|
@@ -381,7 +459,11 @@ async function handleTool(name, args) {
|
|
|
381
459
|
node: process.version,
|
|
382
460
|
platform: `${process.platform} ${process.arch}`,
|
|
383
461
|
license: getLicenseInfo?.() ?? { tier: 'free', name: 'Free' },
|
|
384
|
-
|
|
462
|
+
aiProviders: {
|
|
463
|
+
anthropic: !!process.env.ANTHROPIC_API_KEY,
|
|
464
|
+
gemini: !!process.env.GEMINI_API_KEY,
|
|
465
|
+
openai: !!process.env.OPENAI_API_KEY,
|
|
466
|
+
},
|
|
385
467
|
});
|
|
386
468
|
}
|
|
387
469
|
|
|
@@ -407,9 +489,16 @@ if (process.argv.includes('--audit')) {
|
|
|
407
489
|
'filemayor_undo_last',
|
|
408
490
|
]);
|
|
409
491
|
|
|
492
|
+
const activeProvider = detectAiProvider();
|
|
493
|
+
const PROVIDER_ENDPOINTS = {
|
|
494
|
+
anthropic: 'https://api.anthropic.com',
|
|
495
|
+
gemini: 'https://generativelanguage.googleapis.com',
|
|
496
|
+
openai: 'https://api.openai.com',
|
|
497
|
+
};
|
|
498
|
+
|
|
410
499
|
const report = {
|
|
411
500
|
package: 'filemayor-mcp',
|
|
412
|
-
version:
|
|
501
|
+
version: PKG_VERSION,
|
|
413
502
|
node: process.version,
|
|
414
503
|
platform: `${process.platform}-${process.arch}`,
|
|
415
504
|
transport: {
|
|
@@ -420,20 +509,21 @@ if (process.argv.includes('--audit')) {
|
|
|
420
509
|
},
|
|
421
510
|
outbound: {
|
|
422
511
|
defaultEgress: 'none',
|
|
423
|
-
optionalEgress:
|
|
512
|
+
optionalEgress: activeProvider
|
|
424
513
|
? {
|
|
425
514
|
enabled: true,
|
|
426
|
-
destination:
|
|
515
|
+
destination: PROVIDER_ENDPOINTS[activeProvider.provider],
|
|
516
|
+
provider: activeProvider.label,
|
|
427
517
|
trigger: 'filemayor_plan tool only',
|
|
428
518
|
payload: 'directory metadata (paths, sizes, extensions). No file contents.',
|
|
429
|
-
gatedBy:
|
|
519
|
+
gatedBy: `${activeProvider.env} environment variable`,
|
|
430
520
|
}
|
|
431
521
|
: {
|
|
432
522
|
enabled: false,
|
|
433
|
-
|
|
523
|
+
destinations: Object.values(PROVIDER_ENDPOINTS),
|
|
434
524
|
trigger: 'filemayor_plan tool only',
|
|
435
525
|
payload: 'directory metadata only — no file contents',
|
|
436
|
-
gatedBy: 'GEMINI_API_KEY
|
|
526
|
+
gatedBy: 'one of ANTHROPIC_API_KEY / GEMINI_API_KEY / OPENAI_API_KEY (none currently set)',
|
|
437
527
|
},
|
|
438
528
|
},
|
|
439
529
|
runtimeSafeguards: {
|
|
@@ -458,7 +548,7 @@ if (process.argv.includes('--audit')) {
|
|
|
458
548
|
},
|
|
459
549
|
source: 'https://github.com/Hrypopo/FileMayor/blob/main/mcp/index.mjs',
|
|
460
550
|
verifyBy: [
|
|
461
|
-
|
|
551
|
+
`Read mcp/index.mjs end-to-end — it's ${(() => { try { return fs.readFileSync(new URL(import.meta.url)).toString().split('\n').length; } catch { return 'a few hundred'; } })()} lines.`,
|
|
462
552
|
"grep for outbound calls: rg \"fetch\\(|http\\.request|https\\.request|net\\.\" mcp/index.mjs",
|
|
463
553
|
"Run `npm view filemayor-mcp` and compare the published shasum to the GitHub tag.",
|
|
464
554
|
"Run this same `--audit` after upgrading to detect changes to transport / egress / tool list.",
|