filesystem-mcp 1.0.0 → 1.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/dist/tools/create.js +9 -5
- package/dist/tools/insert.js +12 -6
- package/dist/tools/str-replace.js +8 -3
- package/dist/tools/view.js +5 -3
- package/dist/utils/paths.d.ts +1 -0
- package/dist/utils/paths.js +46 -0
- package/dist/utils/schema.d.ts +9 -0
- package/dist/utils/schema.js +28 -0
- package/package.json +3 -2
package/dist/tools/create.js
CHANGED
|
@@ -38,6 +38,8 @@ const zod_1 = require("zod");
|
|
|
38
38
|
const fs = __importStar(require("node:fs/promises"));
|
|
39
39
|
const path = __importStar(require("node:path"));
|
|
40
40
|
const response_js_1 = require("../utils/response.js");
|
|
41
|
+
const paths_js_1 = require("../utils/paths.js");
|
|
42
|
+
const schema_js_1 = require("../utils/schema.js");
|
|
41
43
|
const description = `Create or overwrite a file with the specified content.
|
|
42
44
|
|
|
43
45
|
- Creates parent directories if they don't exist
|
|
@@ -47,17 +49,19 @@ function registerCreate(server) {
|
|
|
47
49
|
server.registerTool('create', {
|
|
48
50
|
title: 'Create',
|
|
49
51
|
description,
|
|
50
|
-
inputSchema: {
|
|
52
|
+
inputSchema: (0, schema_js_1.strictSchemaWithAliases)({
|
|
51
53
|
path: zod_1.z.string().describe('Absolute path where file will be created'),
|
|
52
|
-
|
|
53
|
-
},
|
|
54
|
+
content: zod_1.z.string().describe('Content to write to the file'),
|
|
55
|
+
}, {
|
|
56
|
+
file_text: 'content',
|
|
57
|
+
}),
|
|
54
58
|
annotations: {
|
|
55
59
|
readOnlyHint: false,
|
|
56
60
|
destructiveHint: true,
|
|
57
61
|
},
|
|
58
62
|
}, async (args) => {
|
|
59
|
-
const targetPath = args.path;
|
|
60
|
-
const content = args
|
|
63
|
+
const targetPath = (0, paths_js_1.expandPath)(args.path);
|
|
64
|
+
const { content } = args;
|
|
61
65
|
// Create parent directories if needed
|
|
62
66
|
const dir = path.dirname(targetPath);
|
|
63
67
|
await fs.mkdir(dir, { recursive: true });
|
package/dist/tools/insert.js
CHANGED
|
@@ -37,34 +37,40 @@ exports.registerInsert = registerInsert;
|
|
|
37
37
|
const zod_1 = require("zod");
|
|
38
38
|
const fs = __importStar(require("node:fs/promises"));
|
|
39
39
|
const response_js_1 = require("../utils/response.js");
|
|
40
|
+
const paths_js_1 = require("../utils/paths.js");
|
|
41
|
+
const schema_js_1 = require("../utils/schema.js");
|
|
40
42
|
const description = `Insert text at a specific line in a file.
|
|
41
43
|
|
|
42
44
|
- insert_line = 0: Insert at the beginning
|
|
43
45
|
- insert_line = N: Insert after line N
|
|
46
|
+
- insert_line = -1: Insert at the end
|
|
44
47
|
- insert_text should typically end with a newline
|
|
45
48
|
- Use absolute paths`;
|
|
46
49
|
function registerInsert(server) {
|
|
47
50
|
server.registerTool('insert', {
|
|
48
51
|
title: 'Insert',
|
|
49
52
|
description,
|
|
50
|
-
inputSchema: {
|
|
53
|
+
inputSchema: (0, schema_js_1.strictSchemaWithAliases)({
|
|
51
54
|
path: zod_1.z.string().describe('Absolute path to file'),
|
|
52
|
-
insert_line: zod_1.z.number().int().min(
|
|
55
|
+
insert_line: zod_1.z.number().int().min(-1).describe('Line number to insert after (0 = beginning, -1 = end)'),
|
|
53
56
|
insert_text: zod_1.z.string().describe('Text to insert (should end with newline)'),
|
|
54
|
-
},
|
|
57
|
+
}, {}),
|
|
55
58
|
annotations: {
|
|
56
59
|
readOnlyHint: false,
|
|
57
60
|
destructiveHint: true,
|
|
58
61
|
},
|
|
59
62
|
}, async (args) => {
|
|
60
|
-
const targetPath = args.path;
|
|
61
|
-
const insertLine = args.insert_line;
|
|
63
|
+
const targetPath = (0, paths_js_1.expandPath)(args.path);
|
|
62
64
|
const insertText = args.insert_text;
|
|
63
65
|
const content = await fs.readFile(targetPath, 'utf-8');
|
|
64
66
|
const lines = content.split('\n');
|
|
67
|
+
// Handle -1 as "end of file"
|
|
68
|
+
// If file ends with newline, insert before the trailing empty element
|
|
69
|
+
const endPosition = content.endsWith('\n') ? lines.length - 1 : lines.length;
|
|
70
|
+
const insertLine = args.insert_line === -1 ? endPosition : args.insert_line;
|
|
65
71
|
// Validate line number
|
|
66
72
|
if (insertLine > lines.length) {
|
|
67
|
-
throw new Error(`insert_line ${
|
|
73
|
+
throw new Error(`insert_line ${args.insert_line} is beyond file length (${lines.length} lines)`);
|
|
68
74
|
}
|
|
69
75
|
// Warn if insert_text doesn't end with newline
|
|
70
76
|
let warning;
|
|
@@ -37,6 +37,8 @@ exports.registerStrReplace = registerStrReplace;
|
|
|
37
37
|
const zod_1 = require("zod");
|
|
38
38
|
const fs = __importStar(require("node:fs/promises"));
|
|
39
39
|
const response_js_1 = require("../utils/response.js");
|
|
40
|
+
const paths_js_1 = require("../utils/paths.js");
|
|
41
|
+
const schema_js_1 = require("../utils/schema.js");
|
|
40
42
|
const description = `Replace an exact string in a file.
|
|
41
43
|
|
|
42
44
|
- old_str must match exactly and be unique in the file
|
|
@@ -47,17 +49,20 @@ function registerStrReplace(server) {
|
|
|
47
49
|
server.registerTool('str_replace', {
|
|
48
50
|
title: 'String Replace',
|
|
49
51
|
description,
|
|
50
|
-
inputSchema: {
|
|
52
|
+
inputSchema: (0, schema_js_1.strictSchemaWithAliases)({
|
|
51
53
|
path: zod_1.z.string().describe('Absolute path to file'),
|
|
52
54
|
old_str: zod_1.z.string().describe('Exact string to find (must be unique)'),
|
|
53
55
|
new_str: zod_1.z.string().optional().describe('Replacement string (omit to delete)'),
|
|
54
|
-
},
|
|
56
|
+
}, {
|
|
57
|
+
old_string: 'old_str',
|
|
58
|
+
new_string: 'new_str',
|
|
59
|
+
}),
|
|
55
60
|
annotations: {
|
|
56
61
|
readOnlyHint: false,
|
|
57
62
|
destructiveHint: true,
|
|
58
63
|
},
|
|
59
64
|
}, async (args) => {
|
|
60
|
-
const targetPath = args.path;
|
|
65
|
+
const targetPath = (0, paths_js_1.expandPath)(args.path);
|
|
61
66
|
const oldStr = args.old_str;
|
|
62
67
|
const newStr = args.new_str ?? '';
|
|
63
68
|
const content = await fs.readFile(targetPath, 'utf-8');
|
package/dist/tools/view.js
CHANGED
|
@@ -38,6 +38,8 @@ const zod_1 = require("zod");
|
|
|
38
38
|
const fs = __importStar(require("node:fs/promises"));
|
|
39
39
|
const path = __importStar(require("node:path"));
|
|
40
40
|
const response_js_1 = require("../utils/response.js");
|
|
41
|
+
const paths_js_1 = require("../utils/paths.js");
|
|
42
|
+
const schema_js_1 = require("../utils/schema.js");
|
|
41
43
|
const description = `View file contents or list directory.
|
|
42
44
|
|
|
43
45
|
For files:
|
|
@@ -53,15 +55,15 @@ function registerView(server) {
|
|
|
53
55
|
server.registerTool('view', {
|
|
54
56
|
title: 'View',
|
|
55
57
|
description,
|
|
56
|
-
inputSchema: {
|
|
58
|
+
inputSchema: (0, schema_js_1.strictSchemaWithAliases)({
|
|
57
59
|
path: zod_1.z.string().describe('Absolute path to file or directory'),
|
|
58
60
|
view_range: zod_1.z.tuple([zod_1.z.number(), zod_1.z.number()]).optional().describe('Line range [start, end] for text files (1-indexed, inclusive)'),
|
|
59
|
-
},
|
|
61
|
+
}, {}),
|
|
60
62
|
annotations: {
|
|
61
63
|
readOnlyHint: true,
|
|
62
64
|
},
|
|
63
65
|
}, async (args) => {
|
|
64
|
-
const targetPath = args.path;
|
|
66
|
+
const targetPath = (0, paths_js_1.expandPath)(args.path);
|
|
65
67
|
const stat = await fs.stat(targetPath);
|
|
66
68
|
if (stat.isDirectory()) {
|
|
67
69
|
const entries = await listDirectory(targetPath, 2);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function expandPath(p: string): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.expandPath = expandPath;
|
|
37
|
+
const os = __importStar(require("node:os"));
|
|
38
|
+
function expandPath(p) {
|
|
39
|
+
if (p.startsWith('~/')) {
|
|
40
|
+
return p.replace('~', os.homedir());
|
|
41
|
+
}
|
|
42
|
+
if (p === '~') {
|
|
43
|
+
return os.homedir();
|
|
44
|
+
}
|
|
45
|
+
return p;
|
|
46
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ZodRawShape, type ZodTypeAny } from 'zod';
|
|
2
|
+
type AliasMap = Record<string, string>;
|
|
3
|
+
/**
|
|
4
|
+
* Creates a strict Zod object schema that:
|
|
5
|
+
* 1. Accepts aliased parameter names (e.g., new_string -> new_str)
|
|
6
|
+
* 2. Rejects any unknown parameters after alias resolution
|
|
7
|
+
*/
|
|
8
|
+
export declare function strictSchemaWithAliases<T extends ZodRawShape>(shape: T, aliases?: AliasMap): ZodTypeAny;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.strictSchemaWithAliases = strictSchemaWithAliases;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Creates a strict Zod object schema that:
|
|
7
|
+
* 1. Accepts aliased parameter names (e.g., new_string -> new_str)
|
|
8
|
+
* 2. Rejects any unknown parameters after alias resolution
|
|
9
|
+
*/
|
|
10
|
+
function strictSchemaWithAliases(shape, aliases = {}) {
|
|
11
|
+
const objectSchema = zod_1.z.object(shape).strict();
|
|
12
|
+
return zod_1.z.preprocess((args) => {
|
|
13
|
+
if (typeof args !== 'object' || args === null) {
|
|
14
|
+
return args;
|
|
15
|
+
}
|
|
16
|
+
const input = args;
|
|
17
|
+
const result = {};
|
|
18
|
+
// Build result object, mapping aliases to canonical names
|
|
19
|
+
for (const key of Object.keys(input)) {
|
|
20
|
+
const canonicalKey = aliases[key] ?? key;
|
|
21
|
+
// Only use alias if canonical key isn't already set
|
|
22
|
+
if (!(canonicalKey in result)) {
|
|
23
|
+
result[canonicalKey] = input[key];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}, objectSchema);
|
|
28
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "filesystem-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "MCP server for filesystem operations (view, create, edit files)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Adam Jones (domdomegg)",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"eslint-config-domdomegg": "^2.0.0",
|
|
33
33
|
"tsconfig-domdomegg": "^1.0.0",
|
|
34
34
|
"typescript": "^5.8.0",
|
|
35
|
-
"vitest": "^3.0.0"
|
|
35
|
+
"vitest": "^3.0.0",
|
|
36
|
+
"zod-to-json-schema": "^3.25.1"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"@modelcontextprotocol/sdk": "^1.24.0",
|