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

nogoo9 / mcp-server-cloud-fs / 25690758441

11 May 2026 06:56PM UTC coverage: 66.53% (+4.1%) from 62.408%
25690758441

push

github

web-flow
release: v0.3.0 — Shell tool, MemoryProvider, SqliteProvider, xterm.js App (#3)

* feat: add shell tool with 17 POSIX-like commands for cloud fs (v0.3.0)

- New `shell` MCP tool gated by `--enable-shell` CLI flag
- 17 built-in commands: ls, cat, head, tail, cp, mv, rm, mkdir, touch,
  stat, find, grep, wc, du, echo, tee, diff
- Pipe support (|), input redirection (<), output redirection (>, >>)
- All commands execute in-process against the VFS — no real shell spawned
- Destructive commands (rm, mv) additionally gated by `--enable-delete`
- Programmatic API: `executeShell(command, ctx)` exported from npm lib
- 48 new unit tests (parser, commands, pipelines, security)
- Bump version to 0.3.0 across package.json, server.json, server.ts

* feat: add MemoryProvider, SqliteProvider, xterm.js MCP App, MCP Inspector scripts

- MemoryProvider: fully in-process Map-based storage for demos/tests (zero deps)
- SqliteProvider: persistent local storage using bun:sqlite (zero external deps, WAL mode)
- xterm.js MCP App: interactive terminal UI via MCP Apps extension (Catppuccin theme, command history)
- MCP Inspector: bun run inspect / inspect:memory scripts for server validation
- Extended ParsedRoot scheme to support mem:// and sqlite:// URIs
- Added --sqlite-db CLI flag for SQLite database file path
- 26 new unit tests for both providers
- Updated README, CHANGELOG, lib.ts exports

* feat: infer content type from magic bytes, fallback to extension

- New shared utility: src/providers/content-type.ts
- Checks buffer magic bytes first (PNG, JPEG, GIF, PDF, ZIP, GZIP, WASM, etc.)
- RIFF disambiguation (WEBP vs WAV)
- Falls back to extension for text formats (no magic bytes, no exception)
- Expanded extension map: 50+ MIME types
- 22 dedicated tests for content-type inference
- Both MemoryProvider and SqliteProvider use the shared utility
- Exported as inferContentType from lib.ts

* refactor: use file-type library for robust magic-byte content detection

- Replace h... (continued)

764 of 1103 new or added lines in 27 files covered. (69.27%)

1946 of 2925 relevant lines covered (66.53%)

24.22 hits per line

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

83.33
/src/tools/shell/commands/diff.ts
1
// src/tools/shell/commands/diff.ts
2

3
import { resolveToolPath } from "../../../path-utils.js";
57✔
4
import type { ShellCommandHandler } from "../types.js";
5

6
/**
7
 * diff <file1> <file2>
8
 *
9
 * Compare two files line by line. Outputs a unified-style diff.
10
 */
11
export const diff: ShellCommandHandler = async (args, ctx, _stdin) => {
43✔
12
        if (args.length < 2) {
20✔
NEW
13
                throw new Error("diff: missing file operand");
×
14
        }
2✔
15

16
        const { root: root1, key: key1 } = resolveToolPath(ctx.roots, args[0]!);
73✔
17
        const { root: root2, key: key2 } = resolveToolPath(ctx.roots, args[1]!);
73✔
18

19
        const [buf1, buf2] = await Promise.all([
44✔
20
                ctx.vfs.get(root1, key1),
29✔
21
                ctx.vfs.get(root2, key2),
26✔
22
        ]);
5✔
23

24
        const lines1 = buf1.toString("utf8").split("\n");
49✔
25
        const lines2 = buf2.toString("utf8").split("\n");
49✔
26

27
        if (buf1.toString("utf8") === buf2.toString("utf8")) {
57✔
28
                return "";
9✔
29
        }
2✔
30

31
        // Simple line-by-line diff (not a full LCS algorithm, but useful for basic comparison)
32
        const output: string[] = [];
20✔
33
        output.push(`--- ${args[0]}`);
32✔
34
        output.push(`+++ ${args[1]}`);
32✔
35

36
        const maxLen = Math.max(lines1.length, lines2.length);
56✔
37
        let chunkStart = -1;
22✔
38
        let chunkLines: string[] = [];
22✔
39

40
        for (let i = 0; i < maxLen; i++) {
36✔
41
                const l1 = i < lines1.length ? lines1[i]! : undefined;
43✔
42
                const l2 = i < lines2.length ? lines2[i]! : undefined;
43✔
43

44
                if (l1 === l2) {
14✔
NEW
45
                        if (chunkLines.length > 0) {
×
NEW
46
                                output.push(`@@ -${chunkStart + 1} +${chunkStart + 1} @@`);
×
NEW
47
                                output.push(...chunkLines);
×
NEW
48
                                chunkLines = [];
×
NEW
49
                                chunkStart = -1;
×
NEW
50
                        }
×
51
                } else {
13✔
52
                        if (chunkStart === -1) chunkStart = i;
49✔
53
                        if (l1 !== undefined) chunkLines.push(`-${l1}`);
59✔
54
                        if (l2 !== undefined) chunkLines.push(`+${l2}`);
59✔
55
                }
56
        }
2✔
57

58
        if (chunkLines.length > 0) {
31✔
59
                output.push(`@@ -${chunkStart + 1} +${chunkStart + 1} @@`);
63✔
60
                output.push(...chunkLines);
29✔
61
        }
2✔
62

63
        return output.join("\n");
23✔
64
};
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