• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nogoo9 / mcp-server-cloud-fs / 25686582918

11 May 2026 05:35PM UTC coverage: 62.408%. Remained the same
25686582918

push

github

eterna2
feat: v0.2.0 — VFS layer, extended tools, npm library, OIDC publishing

- 5 extended tools: read_file_range, grep_file, grep_files, copy_file, delete_file
- Virtual Filesystem (VFS) layer with FUSE-like cache coherence
- npm library entrypoint with TypeScript declarations
- Coveralls CI integration
- E2E test suite with Redis cache
- Refactored GCS/S3/Azure providers (pure constructors, no emulator flags)
- Publish workflow: OIDC Trusted Publishing via Node 24, merged MCP registry
  publish as dependent job
- Shorten description to comply with MCP Registry 100-char limit

605 of 649 new or added lines in 12 files covered. (93.22%)

82 existing lines in 10 files now uncovered.

1182 of 1894 relevant lines covered (62.41%)

23.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.28
/src/tools/write.ts
1
// src/tools/write.ts
2

3
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
import { z } from "zod";
24✔
5
import { resolveToolPath } from "../path-utils.js";
51✔
6
import type { ParsedRoot } from "../providers/interface.js";
7
import type { VirtualFS } from "../vfs.js";
8

9
type Ctx = {
10
        vfs: VirtualFS;
11
        roots: ParsedRoot[];
12
};
13
type ToolResult = {
14
        content: [{ type: "text"; text: string }];
15
        isError?: boolean;
16
};
17

18
export async function handleWriteFile(
6✔
19
        args: { path: string; content: string },
6✔
20
        ctx: Ctx,
4✔
21
): Promise<ToolResult> {
3✔
22
        try {
9✔
23
                const { root, key } = resolveToolPath(ctx.roots, args.path);
64✔
24
                const buffer = Buffer.from(args.content, "utf8");
53✔
25
                await ctx.vfs.put(root, key, buffer);
41✔
26
                return {
14✔
27
                        content: [{ type: "text", text: `Successfully wrote to ${args.path}` }],
75✔
28
                };
2✔
29
        } catch (err) {
17✔
30
                return {
14✔
31
                        isError: true,
20✔
32
                        content: [{ type: "text", text: (err as Error).message }],
50✔
33
                };
2✔
34
        }
35
}
36

37
export async function handleEditFile(
6✔
38
        args: {
6✔
39
                path: string;
40
                edits: Array<{ oldText: string; newText: string }>;
41
                dryRun?: boolean | undefined;
42
        },
43
        ctx: Ctx,
4✔
44
): Promise<ToolResult> {
3✔
45
        try {
9✔
46
                const { root, key } = resolveToolPath(ctx.roots, args.path);
64✔
47

48
                // Read via VFS (cache-first, then provider)
49
                const buffer = await ctx.vfs.get(root, key);
48✔
50
                const original = buffer.toString("utf8");
45✔
51
                let content = original;
27✔
52

53
                // Apply edits sequentially
54
                for (const { oldText, newText } of args.edits) {
53✔
55
                        if (!content.includes(oldText)) {
40✔
56
                                throw new Error(
16✔
57
                                        `edit_file: oldText not found in ${args.path}: ${JSON.stringify(oldText.slice(0, 40))}`,
87✔
58
                                );
1✔
59
                        }
6✔
60
                        content = content.replace(oldText, newText);
48✔
61
                }
4✔
62

63
                if (args.dryRun) {
23✔
64
                        return {
16✔
65
                                content: [
20✔
66
                                        { type: "text", text: formatDiff(args.path, args.edits, original) },
75✔
67
                                ],
7✔
68
                        };
1✔
69
                }
4✔
70

71
                const newBuffer = Buffer.from(content, "utf8");
51✔
72
                await ctx.vfs.put(root, key, newBuffer);
44✔
73
                return {
14✔
74
                        content: [{ type: "text", text: `Successfully edited ${args.path}` }],
73✔
75
                };
2✔
76
        } catch (err) {
17✔
77
                return {
14✔
78
                        isError: true,
20✔
79
                        content: [{ type: "text", text: (err as Error).message }],
50✔
80
                };
1✔
81
        }
82
}
83

84
function formatDiff(
1✔
85
        path: string,
6✔
86
        edits: Array<{ oldText: string; newText: string }>,
7✔
87
        original: string,
10✔
88
): string {
3✔
89
        let out = `Dry run — edits to ${path}:\n\n`;
47✔
90
        for (let i = 0; i < edits.length; i++) {
42✔
91
                const { oldText, newText } = edits[i]!;
42✔
92
                const found = original.includes(oldText);
45✔
93
                out += `Edit ${i + 1}${found ? "" : " (NOT FOUND — would error)"}:\n`;
39✔
94
                for (const line of oldText.split("\n")) out += `- ${line}\n`;
64✔
95
                for (const line of newText.split("\n")) out += `+ ${line}\n`;
64✔
96
                out += "\n";
12✔
97
        }
2✔
98
        return out.trimEnd();
21✔
99
}
100

101
export function registerWriteTools(server: McpServer, ctx: Ctx): void {
×
102
        server.registerTool(
×
103
                "write_file",
×
104
                {
×
105
                        description: "Write content to a file, creating it if it does not exist.",
×
106
                        inputSchema: z.object({ path: z.string(), content: z.string() }),
×
107
                },
×
108
                async (args) => handleWriteFile(args, ctx),
×
109
        );
×
110

×
111
        server.registerTool(
×
112
                "edit_file",
×
113
                {
×
114
                        description:
×
UNCOV
115
                                "Apply text edits to a file. Each edit replaces oldText with newText. Use dryRun to preview.",
×
UNCOV
116
                        inputSchema: z.object({
×
UNCOV
117
                                path: z.string(),
×
UNCOV
118
                                edits: z.array(z.object({ oldText: z.string(), newText: z.string() })),
×
UNCOV
119
                                dryRun: z.boolean().optional(),
×
UNCOV
120
                        }),
×
UNCOV
121
                },
×
UNCOV
122
                async (args) => handleEditFile(args, ctx),
×
123
        );
124
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc