nogoo9

@nogoo9/mcp-server-cloud-fs

CI Docs Coverage Semgrep
npm NPM Downloads skills.sh License

Cloud replacement for mcp-server-filesystem β€” 30 tools for S3, Azure Blob, and GCS.
Deploy locally via STDIO or remotely over HTTP/WebSocket with OAuth 2.1 auth.
Also available as an npm library and interactive TUI.

πŸ“– Read the full documentation β†’

Buy Me A Coffee

![cloud-fs in Claude Code](docs/public/images/cloud-fs-claude-demo.svg) ![cloud-fs interactive shell](docs/public/images/cloud-fs-cli-demo.svg) **Providers:** Amazon S3 Β· Azure Blob Storage Β· Google Cloud Storage Β· MinIO Β· RustFS Β· Cloudflare R2 Β· Backblaze B2 Β· Wasabi Β· LocalStack Β· SQLite Β· In-Memory --- ## Table of Contents - [What it does](#what-it-does) - [Quick start](#quick-start) - [Interactive Shell](#interactive-shell-cloud-fs) - [Transports](#transports) - [Authentication & Authorization](#authentication--authorization) - [Production Features](#production-features) - [CLI Reference](#cli-reference) - [Provider Setup](#provider-setup) - [MCP Client Config](#mcp-client-config) - [Tool Reference](#tool-reference) - [Architecture: VFS](#architecture-virtual-filesystem-vfs) - [Caching](#caching) - [Programmatic Usage](#programmatic-usage-npm-library) - [MCP Inspector](#mcp-inspector) - [Development & Testing](#development--testing) - [AI Agent Skill](#ai-agent-skill) - [Documentation](#documentation) - [License](#license) --- ## What it does `@nogoo9/mcp-server-cloud-fs` exposes all 14 tools defined by [`mcp-server-filesystem`](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) β€” same tool names, same parameter schemas β€” over cloud object storage. Drop it into any MCP client config that currently points at `mcp-server-filesystem` and your AI assistant gains read/write access to S3, Azure Blob Storage, or Google Cloud Storage buckets. It also includes **5 extended tools** inspired by [claude-code's filesystem tool surface](https://github.com/codeaashu/claude-code/tree/main/src/tools): line-range reads, byte-range chunk reads, in-process regex search (single file and multi-file), server-side copy, and opt-in deletion. Plus **6 cloud-native tools** (presigned URLs, object metadata/tags, tag-based search, version history, version restore), **2 AI-native tools** (schema extraction, file summarization), and **1 macro tool** (`patch_file` for atomic diffs). A **Virtual Filesystem (VFS) layer** provides FUSE-like cache coherence with ETag-based concurrency control, a **shell tool** lets you run POSIX-like commands (`ls`, `grep`, `jq`, `cat | wc`, etc.) against cloud storage, and the package is available as a **programmatic npm library**. ### v0.7.0 Highlights - **Dynamic tool surface reduction**: scope-aware tool filtering β€” clients only see tools they're authorized to use - **DLP content sanitization**: regex-based middleware redacts PII, API keys, and credentials from tool responses (`--enable-dlp`) - **AI-native tools**: `get_file_schema` extracts CSV/JSON structure; `summarize_file` returns compact head/tail previews - **ETag concurrency control**: SHA-256 content-addressable ETags with `expected_etag` conflict detection on `edit_file` and `patch_file` - **`patch_file` macro tool**: atomic read-diff-write in a single tool call β€” supports unified diffs and line-range replacements ### v0.6.0 Highlights - **Cloud-native tools**: presigned URLs, object metadata/tags, version history, version restore - **Byte-range reads**: `read_file_chunk` fetches specific byte ranges without downloading the full object - **Structured audit logging**: tool invocation audit trail with pluggable sinks (stderr, file) - **Multi-provider routing**: `MultiProvider` serves S3 + Azure + GCS from a single server instance - **Azure Managed Identity**: `DefaultAzureCredential` for OIDC and federated auth - **Connection health-check**: startup credential validation with diagnostic reporting - **MCP Resources**: browse cloud storage as native MCP Resources (no tool calls needed) - **Structured errors**: `CloudError` with typed codes and provider-specific error mappers ### v0.5.0 Highlights - **Interactive TUI (`cloud-fs`)**: Terminal shell with `cd`, tab completion, and command history - **Multi-transport**: STDIO (default), Streamable HTTP, and WebSocket - **Dual runtime**: Bun-native and Node.js support (HTTP transport + SQLite provider) - **OAuth 2.1**: Built-in auth server or external IdP token validation - **ext-auth extensions**: Client Credentials (M2M) and Enterprise-Managed Authorization (SSO) - **Production hardening**: Rate limiting, CORS, security headers, health checks, structured logging - **AI agent skill**: Installable `skills/cloud-fs` for Claude Code, Gemini CLI, and other AI assistants --- ## Quick start ### Local (STDIO β€” default) ```bash npx @nogoo9/mcp-server-cloud-fs s3 s3://my-bucket ``` ### Remote (HTTP) ```bash # Bun (zero Express dependency) bunx @nogoo9/mcp-server-cloud-fs s3 s3://my-bucket --transport http --port 3000 # Node.js (requires express peer dep) npx @nogoo9/mcp-server-cloud-fs s3 s3://my-bucket --transport http --port 3000 ``` ### Remote (WebSocket β€” Bun only) ```bash bunx @nogoo9/mcp-server-cloud-fs s3 s3://my-bucket --transport ws --port 3000 ``` ### Demo (no cloud credentials needed) ```bash bunx @nogoo9/mcp-server-cloud-fs memory mem://demo --enable-shell --seed-demo ``` --- ## Interactive Shell (`cloud-fs`) ![cloud-fs interactive shell demo](docs/public/images/cloud-fs-cli-demo.svg) Drop into an interactive terminal for exploring and managing cloud storage β€” no MCP client needed: ```bash # S3 npx -p @nogoo9/mcp-server-cloud-fs cloud-fs s3 s3://my-bucket # In-memory demo with sample files npx -p @nogoo9/mcp-server-cloud-fs cloud-fs memory mem://demo --seed-demo # With a config file (cloud-fs.json in CWD) npx -p @nogoo9/mcp-server-cloud-fs cloud-fs ``` ### Features - **`cd` navigation** β€” `cd data`, `cd ..`, `cd /` with dynamic prompt showing current directory - **Tab completion** β€” context-aware path and command completion - **Command history** β€” persistent across sessions (`~/.cloud-fs_history`) - **Relative paths** β€” all commands resolve relative to `cwd`, just like a real shell - **19 built-in commands** β€” `ls`, `cat`, `head`, `tail`, `cp`, `mv`, `rm`, `mkdir`, `touch`, `stat`, `find`, `grep`, `wc`, `du`, `echo`, `tee`, `diff`, `jq`, `cd` - **Pipes & redirects** β€” `cat config.json | jq '.database'`, `echo hello > file.txt` ### Local development ```bash bun src/cli-tui.ts memory mem://demo --seed-demo ``` --- ## Transports The server supports three MCP transports, selected via `--transport`: | Transport | Flag | Runtime | Use case | |---|---|---|---| | **STDIO** | `--transport stdio` (default) | Bun, Node | Local `npx`, Claude Desktop, Claude Code | | **Streamable HTTP** | `--transport http` | Bun βœ…, Node βœ… | Remote deployment, multi-user, enterprise | | **WebSocket** | `--transport ws` | Bun only | Low-latency bidirectional, real-time apps | ### Streamable HTTP Implements the [MCP Streamable HTTP specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http) with a single `/mcp` endpoint for POST (requests), GET (SSE notifications), and DELETE (session termination). **Dual runtime support:** - **Bun**: Uses `WebStandardStreamableHTTPServerTransport` with `Bun.serve()` directly β€” zero Express dependency, maximum performance. - **Node.js**: Falls back to `StreamableHTTPServerTransport` with Express. Requires `express` as an optional peer dependency (`npm install express`). Runtime is auto-detected at startup. **Session management:** Sessions use **UUID v7** (RFC 9562) β€” time-ordered and K-sortable, making them ideal for logging, debugging, and database indexing. Sessions are tracked via the `Mcp-Session-Id` header. **Resumability:** When an EventStore is configured, clients can reconnect and resume receiving messages from where they left off via the `Last-Event-ID` SSE header. ```bash # Basic HTTP server cloud-fs-mcp s3 s3://my-bucket --transport http --port 3000 # With authentication cloud-fs-mcp s3 s3://my-bucket --transport http --port 3000 --auth builtin --auth-issuer https://my-server.example.com # Production deployment cloud-fs-mcp s3 s3://my-bucket \ --transport http --port 3000 --host 0.0.0.0 \ --auth external --auth-jwks-uri https://login.example.com/.well-known/jwks.json \ --cors-origin https://app.example.com \ --rate-limit 60 --rate-limit-burst 10 \ --security-headers \ --request-logging ``` ### WebSocket Bun-native WebSocket transport using `Bun.serve()` with WebSocket upgrade handling. Provides lower latency than HTTP for high-frequency tool invocations. - JSON-RPC message framing over WebSocket - UUID v7 session assigned on upgrade - WS ping/pong heartbeat for connection liveness ```bash cloud-fs-mcp s3 s3://my-bucket --transport ws --port 3000 ``` --- ## Authentication & Authorization Authentication is **disabled by default** and only applies to HTTP/WS transports. STDIO mode never requires auth. ### Auth modes | Mode | Flag | Description | |---|---|---| | `none` | `--auth none` (default) | No authentication. Suitable for local/trusted networks. | | `builtin` | `--auth builtin` | Self-hosted OAuth 2.1 Authorization Server. The MCP server acts as both AS and RS. | | `external` | `--auth external` | Validate bearer tokens against an external IdP (Okta, Auth0, Keycloak, Azure AD). | ### Built-in OAuth server (`--auth builtin`) The server runs a full OAuth 2.1 Authorization Server using the MCP SDK's `mcpAuthRouter()`: - **Authorization Code + PKCE** (mandatory) β€” interactive user consent flow - **Dynamic Client Registration** β€” MCP clients auto-register on first connect - **Refresh token rotation** β€” single-use refresh tokens prevent replay - **Metadata discovery** β€” `/.well-known/oauth-authorization-server` (RFC 8414) and `/.well-known/oauth-protected-resource` (RFC 9728) ```bash cloud-fs-mcp s3 s3://my-bucket \ --transport http --port 3000 \ --auth builtin --auth-issuer https://my-server.example.com ``` ### External IdP (`--auth external`) The server only validates bearer tokens β€” it does not issue tokens. Use this when you have an existing identity provider. - **JWKS validation** β€” fetches and caches signing keys from your IdP - **JWT verification** β€” checks signature, issuer, audience, expiry - **Token introspection** β€” fallback for opaque tokens ```bash cloud-fs-mcp s3 s3://my-bucket \ --transport http --port 3000 \ --auth external \ --auth-jwks-uri https://login.example.com/.well-known/jwks.json \ --auth-audience https://cloud-fs.example.com ``` ### ext-auth: Client Credentials (M2M) Implements the [MCP Client Credentials extension](https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/oauth-client-credentials.mdx) for machine-to-machine authentication without user interaction. Designed for CI/CD pipelines, background services, and automated workflows. - **JWT assertion auth** (`private_key_jwt` per RFC 7523) β€” recommended - **Client secret auth** (`client_secret_basic`) β€” simpler but less secure - No user interaction required β€” tokens granted based on pre-registered client credentials ```bash cloud-fs-mcp s3 s3://my-bucket \ --transport http --port 3000 \ --auth builtin --auth-issuer https://my-server.example.com \ --auth-client-credentials ``` ### ext-auth: Enterprise-Managed Authorization (SSO) Implements the [MCP Enterprise-Managed Authorization extension](https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/enterprise-managed-authorization.mdx) for seamless SSO via your organization's Identity Provider. **How it works:** 1. User logs in to the MCP Client via the enterprise IdP (OpenID Connect or SAML) 2. Client exchanges the ID Token for an Identity Assertion JWT Authorization Grant (ID-JAG) via Token Exchange (RFC 8693) 3. Client presents the ID-JAG to this server's Authorization Server (RFC 7523) 4. Server validates the ID-JAG and issues an access token **Benefits:** - Zero manual authorization per MCP Server β€” SSO handles everything - Enterprise admins control which users/groups can access which MCP servers - Granular scope enforcement via IdP policies ```bash cloud-fs-mcp s3 s3://my-bucket \ --transport http --port 3000 \ --auth builtin --auth-issuer https://my-server.example.com \ --auth-enterprise-idp https://acme.okta.com ``` ### OAuth Scopes When auth is enabled, tool access is controlled by granular scopes: | Scope | Tools | |---|---| | `cloud-fs:read` | `read_file`, `read_text_file`, `read_media_file`, `read_multiple_files`, `read_file_range`, `read_file_chunk`, `get_presigned_url`, `get_object_metadata`, `list_versions`, `get_file_schema`, `summarize_file` | | `cloud-fs:write` | `write_file`, `edit_file`, `create_directory`, `set_object_tags`, `restore_version`, `patch_file` | | `cloud-fs:delete` | `delete_file` | | `cloud-fs:search` | `search_files`, `grep_file`, `grep_files`, `list_directory`, `list_directory_with_sizes`, `directory_tree`, `search_by_tag` | | `cloud-fs:shell` | `shell` | | `cloud-fs:admin` | All tools + `get_file_info`, `list_allowed_directories` | When `grantedScopes` is set (via OAuth tokens), tools outside the granted scopes are **not registered** β€” they don't appear in `tools/list` at all, reducing LLM prompt token waste and preventing tool hallucination. Tokens with insufficient scopes receive a clear error response indicating which scope is required. --- ## Production Features ### Health Checks HTTP/WS transports expose Kubernetes-convention health endpoints: | Endpoint | Purpose | Success | Failure | |---|---|---|---| | `/healthz` | Liveness β€” is the process alive? | `200 OK` always | Process is dead | | `/readyz` | Readiness β€” can it serve traffic? | `200 OK` after VFS hydration | `503` during startup | Configure your orchestrator's liveness and readiness probes to use these endpoints. ### Rate Limiting Token bucket rate limiting protects against abuse. **Disabled by default.** - **In-memory** β€” per-IP/per-client counters for single-process deployments - **Redis** β€” distributed rate limiting for multi-instance deployments (uses existing optional `ioredis` peer dep) - Returns `429 Too Many Requests` with `Retry-After` header when limits are exceeded ```bash # 60 requests/minute with burst of 10 cloud-fs-mcp s3 s3://my-bucket --transport http \ --rate-limit 60 --rate-limit-burst 10 ``` ### CORS Cross-Origin Resource Sharing for browser-based MCP clients: - Strict origin allowlist β€” no wildcards in production - Exposes `Mcp-Session-Id` and `Mcp-Protocol-Version` headers - Localhost auto-allows `*` when `--host 127.0.0.1` (dev convenience) ```bash cloud-fs-mcp s3 s3://my-bucket --transport http \ --cors-origin https://app.example.com \ --cors-origin https://staging.example.com ``` ### Structured Request Logging JSON audit trail to stderr for compliance and debugging: ```json {"ts":"2026-05-12T12:00:00Z","sessionId":"019...","tool":"read_file","user":"alice@example.com","latencyMs":42,"status":200} ``` Enable with `--request-logging`. ### DNS Rebinding Protection Automatically applied when binding to localhost addresses. Validates the `Host` header against allowed hostnames to prevent DNS rebinding attacks. ### Security Headers Opt-in HTTP response hardening via [nosecone](https://github.com/arcjet/arcjet-js/tree/main/nosecone) β€” a framework-agnostic security headers library. Works with both Bun-native and Node/Express transports. When enabled, every HTTP response includes standard security headers: - `Content-Security-Policy` - `Strict-Transport-Security` - `X-Content-Type-Options: nosniff` - `X-Frame-Options` - `Cross-Origin-Opener-Policy` - `Cross-Origin-Resource-Policy` - `Referrer-Policy` - And more (see [nosecone defaults](https://docs.arcjet.com/nosecone/quick-start)) ```bash # Enable with defaults cloud-fs-mcp s3 s3://my-bucket --transport http --security-headers # Custom configuration via inline JSON cloud-fs-mcp s3 s3://my-bucket --transport http \ --security-headers-config '{"contentSecurityPolicy":false}' # Custom configuration via file cloud-fs-mcp s3 s3://my-bucket --transport http \ --security-headers-config-file ./nosecone.json ``` > **Note:** Requires the `nosecone` peer dependency: `npm install nosecone` --- ## CLI Reference ``` cloud-fs-mcp [root-uri...] [options] ``` ### Providers | Provider | URI format | Description | |---|---|---| | `s3` | `s3://bucket[/prefix]` | AWS S3, and any S3-compatible endpoint (MinIO, RustFS, Cloudflare R2, Backblaze B2, Wasabi, LocalStack, …) | | `azure` | `az://container[/prefix]` | Azure Blob Storage | | `gcs` | `gs://bucket[/prefix]` | Google Cloud Storage | | `memory` | `mem://name` | In-memory (ephemeral, for demos) | | `sqlite` | `sqlite://name` | SQLite (persistent local) | ### Options #### Transport & Network | Flag | Default | Description | |---|---|---| | `--transport ` | `stdio` | Transport protocol | | `--port ` | `3000` | Listen port (http/ws only) | | `--host
` | `127.0.0.1` | Bind address (http/ws only) | #### Authentication | Flag | Default | Description | |---|---|---| | `--auth ` | `none` | Auth mode (http/ws only) | | `--auth-issuer ` | β€” | OAuth issuer URL (builtin mode) | | `--auth-jwks-uri ` | β€” | JWKS URI (external mode) | | `--auth-audience ` | β€” | Expected token audience (external mode) | | `--auth-client-credentials` | `false` | Enable Client Credentials ext-auth flow | | `--auth-enterprise-idp ` | β€” | Enable Enterprise-Managed Authorization | #### Production | Flag | Default | Description | |---|---|---| | `--cors-origin ` | β€” | Allowed CORS origin (repeatable) | | `--rate-limit ` | `0` (off) | Rate limit per client | | `--rate-limit-burst ` | `10` | Burst allowance | | `--request-logging` | `false` | Enable structured JSON request logging | | `--audit-log` | `false` | Enable tool invocation audit logging to stderr | | `--audit-log-file ` | β€” | Write audit log to file (implies `--audit-log`) | | `--security-headers` | `false` | Enable security headers via nosecone | | `--security-headers-config ` | β€” | Inline JSON config for nosecone | | `--security-headers-config-file ` | β€” | Load nosecone config from a JSON file | #### Storage & Cache | Flag | Default | Description | |---|---|---| | `--region ` | β€” | Cloud region (S3, GCS) | | `--endpoint ` | β€” | Custom endpoint for S3-compatible backends | | `--cache-store ` | `memory` | Cache backend | | `--cache-ttl ` | `60` | Cache TTL in seconds | | `--sync-debounce ` | `2000` | Write flush delay in ms | | `--cache-dir ` | β€” | Directory for fs cache store | | `--no-cache` | β€” | Bypass cache entirely (pass-through mode) | | `--gcs-endpoint ` | β€” | Custom endpoint for GCS | | `--sqlite-db ` | β€” | SQLite database file path | | `--ca-file ` | β€” | PEM CA bundle for TLS (S3-compatible + Redis) | #### Tools | Flag | Default | Description | |---|---|---| | `--enable-delete` | `false` | Enable the `delete_file` tool | | `--enable-shell` | `false` | Enable the `shell` tool | | `--enable-dlp` | `false` | Enable DLP content sanitization (redacts PII/secrets from responses) | | `--grep-max-objects ` | `1000` | Max objects `grep_files` scans per call | | `--seed-demo` | `false` | Seed VFS with sample files for demo | Credentials are always sourced from SDK credential chains β€” never CLI flags. --- ## Provider Setup ### AWS S3 Credentials are read from the standard AWS credential chain: `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` env vars, `~/.aws/credentials`, EC2 instance profiles, and so on. ```bash cloud-fs-mcp s3 s3://my-bucket --region us-east-1 ``` ### S3-compatible storage Any S3-compatible backend works via `--endpoint`. All use the standard AWS credential chain (`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`). | Provider | `--endpoint` value | Notes | |---|---|---| | **MinIO** | `http://minio:9000` | Self-hosted; use `--ca-file` for TLS with a private CA | | **RustFS** | `http://rustfs:9000` | Rust-native S3-compatible store | | **LocalStack** | `http://localhost:4566` | Full AWS emulator for local dev and CI | | **Cloudflare R2** | `https://.r2.cloudflarestorage.com` | No egress fees; use an R2 API token as the secret key | | **Backblaze B2** | `https://s3..backblazeb2.com` | Use B2 application key ID / key | | **Wasabi** | `https://s3..wasabisys.com` | Compatible, no egress fees | | **DigitalOcean Spaces** | `https://.digitaloceanspaces.com` | Use a Spaces access key | | **Scaleway** | `https://s3..scw.cloud` | Use a Scaleway access key | | **Ceph RGW** | `http://:` | Enterprise self-hosted; supports all S3 APIs | | **SeaweedFS** | `http://:8333` | High-throughput distributed FS with S3 API | | **Garage** | `http://:3900` | Lightweight distributed S3 for homelabs | ```bash # MinIO export AWS_ACCESS_KEY_ID=minioadmin export AWS_SECRET_ACCESS_KEY=minioadmin cloud-fs-mcp s3 s3://my-bucket --endpoint http://minio:9000 # Cloudflare R2 export AWS_ACCESS_KEY_ID= export AWS_SECRET_ACCESS_KEY= cloud-fs-mcp s3 s3://my-bucket --endpoint https://.r2.cloudflarestorage.com --region auto # Backblaze B2 export AWS_ACCESS_KEY_ID= export AWS_SECRET_ACCESS_KEY= cloud-fs-mcp s3 s3://my-bucket --endpoint https://s3.us-west-004.backblazeb2.com --region us-west-004 # LocalStack export AWS_ACCESS_KEY_ID=test export AWS_SECRET_ACCESS_KEY=test cloud-fs-mcp s3 s3://my-bucket --endpoint http://localhost:4566 --region us-east-1 ``` > **TLS with private CA:** For MinIO, RustFS, or Ceph with a self-signed certificate, add `--ca-file /path/to/ca.pem`. ### Azure Blob Storage Uses `DefaultAzureCredential` β€” works with `AZURE_TENANT_ID` / `AZURE_CLIENT_ID` / `AZURE_CLIENT_SECRET` env vars, managed identity, `az login`, and so on. ```bash cloud-fs-mcp azure az://my-container ``` ### Google Cloud Storage Uses Application Default Credentials (ADC). Set `GOOGLE_APPLICATION_CREDENTIALS` or run `gcloud auth application-default login`. ```bash cloud-fs-mcp gcs gs://my-bucket ``` ### In-Memory (ephemeral) Zero-config, zero-dependency provider. All data lives in a `Map` and is lost when the process exits. ```bash cloud-fs-mcp memory mem://demo --enable-shell ``` ### SQLite (persistent local) Persistent local storage using WAL mode. Dual-runtime: uses `bun:sqlite` on Bun, `better-sqlite3` on Node.js (install as peer dep: `npm install better-sqlite3`). ```bash cloud-fs-mcp sqlite sqlite://my-bucket --sqlite-db /tmp/cloud-fs.db --enable-shell ``` --- ## MCP Client Config ### Claude Desktop `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows): ```json { "mcpServers": { "cloud-fs": { "command": "npx", "args": ["-y", "@nogoo9/mcp-server-cloud-fs", "s3", "s3://my-bucket"] } } } ``` ### Claude Code `.mcp.json` in your project root: ```json { "mcpServers": { "cloud-fs": { "type": "stdio", "command": "npx", "args": ["-y", "@nogoo9/mcp-server-cloud-fs", "s3", "s3://my-bucket"] } } } ``` ### Remote HTTP client ```json { "mcpServers": { "cloud-fs": { "type": "streamable-http", "url": "https://cloud-fs.example.com/mcp" } } } ``` --- ## Tool Reference All paths are cloud URIs β€” e.g. `s3://my-bucket/path/to/file.txt`. The server validates every path against the configured root URIs at startup; requests outside allowed roots are rejected. ### Read tools | Tool | Parameters | Description | |---|---|---| | `read_file` | `path` | Read a file. Binary β†’ base64; text β†’ UTF-8. | | `read_text_file` | `path`, `head?`, `tail?` | Read text file with optional head/tail line limits. | | `read_media_file` | `path` | Read image/media as base64 MCP image content block. | | `read_multiple_files` | `paths` | Read several files in parallel. | | `read_file_range` ✨ | `path`, `offset`, `limit` | Read a 1-based line range with total line count header. | | `read_file_chunk` ✨ | `path`, `start_byte`, `end_byte?`, `encoding?` | Read a byte range without downloading the entire file. Max 10MB. | ### Write tools | Tool | Parameters | Description | |---|---|---| | `write_file` | `path`, `content` | Write/overwrite a file. Flushed after debounce window. | | `edit_file` | `path`, `edits[]`, `dryRun?`, `expected_etag?` | Apply `{ oldText, newText }` edits. Preview with `dryRun`. Optional ETag conflict detection. | ### Directory tools | Tool | Parameters | Description | |---|---|---| | `create_directory` | `path` | Create a directory placeholder and parent prefixes. | | `list_directory` | `path` | List immediate children (like `ls`). | | `list_directory_with_sizes` | `path`, `sortBy?`, `limit?` | List children with sizes. Sort by size or name. | | `directory_tree` | `path`, `excludePatterns?` | Recursive directory tree with glob exclusions. | ### Move, Copy & Delete tools | Tool | Parameters | Description | |---|---|---| | `move_file` | `source`, `destination` | Move file. Server-side copy + delete for same-bucket. | | `copy_file` ✨ | `source`, `destination` | Copy file. Server-side for same-bucket. | | `delete_file` ✨ | `path` | Delete file. **Requires `--enable-delete`.** | ### Search tools | Tool | Parameters | Description | |---|---|---| | `search_files` | `path`, `pattern`, `excludePatterns?` | Glob-based object key search. | | `grep_file` ✨ | `path`, `pattern`, `case_insensitive?` | Regex search in a single file with line numbers. | | `grep_files` ✨ | `path`, `pattern`, `glob?`, `case_insensitive?`, `output_mode?`, `max_objects?` | Regex search across all objects under a path. | ### Info tools | Tool | Parameters | Description | |---|---|---| | `get_file_info` | `path` | File metadata: size, last-modified, content type. | | `list_allowed_directories` | _(none)_ | List configured root URIs. | > ✨ = Extended tool ### Cloud-native tools ☁️ | Tool | Parameters | Description | |---|---|---| | `get_presigned_url` ☁️ | `path`, `operation?`, `expires_in?` | Generate a temporary access URL (default: GET, 1 hour). | | `get_object_metadata` ☁️ | `path` | Size, content type, last modified, custom metadata, and tags. | | `set_object_tags` ☁️ | `path`, `tags` | Replace object tags with key-value pairs. | | `search_by_tag` ☁️ | `path`, `tags`, `recursive?` | Find objects matching tag filters (AND logic). | | `list_versions` ☁️ | `path`, `max_results?`, `page_token?` | Version history with timestamps, sizes, and markers. | | `restore_version` ☁️ | `path`, `version_id` | Restore a previous object version (copies over current). | > ☁️ = Cloud-native tool (requires provider support) ### AI-native tools 🧠 | Tool | Parameters | Description | |---|---|---| | `get_file_schema` 🧠 | `path` | Extract structural schema: CSV column names/types/samples, JSON shape, or text line/byte counts. | | `summarize_file` 🧠 | `path`, `max_lines?` | Compact head/tail preview with file size, line count, and content type. | > 🧠 = AI-native tool (reduces LLM context token waste) ### Macro tools 🩹 | Tool | Parameters | Description | |---|---|---| | `patch_file` 🩹 | `path`, `patch`, `format?`, `expected_etag?` | Apply unified diffs or line-range replacements atomically in a single tool call. | > 🩹 = Macro tool (combines read + transform + write) ### Shell tool ⚑ | Tool | Parameters | Description | |---|---|---| | `shell` ⚑ | `command` | Execute POSIX-like commands. Supports pipes, redirects. **Requires `--enable-shell`.** | **Built-in commands:** `ls`, `cat`, `head`, `tail`, `cp`, `mv`, `rm`, `mkdir`, `touch`, `stat`, `find`, `grep`, `wc`, `du`, `echo`, `tee`, `diff`, `jq`, `cd` #### Relative paths All shell commands (and all other tools) support **relative paths** β€” you don't need to type the full URI every time. Paths without a scheme prefix (`s3://`, `mem://`, etc.) are resolved relative to the first configured root: ```bash # With root s3://my-bucket/data, these are equivalent: shell "cat config.json" shell "cat s3://my-bucket/data/config.json" # Subdirectories work naturally: shell "ls logs/" shell "cat logs/app.log | grep ERROR" # ls with no args lists the root's contents: shell "ls" # find with no args searches from the root: shell "find -name '*.json'" ``` Standard `.` and `..` are normalized, with `..` traversal blocked at the root boundary for security. #### Examples ```bash # List root contents shell "ls -l" # JSON query with jq shell "cat config.json | jq '.database.port'" # Search and count shell "grep -i error server.log | wc -l" # Write files shell "echo hello world > greeting.txt" # Copy and diff shell "cp config.json config.backup.json" shell "diff config.json config.backup.json" # Full URIs still work for cross-root access shell "cat s3://other-bucket/file.txt" ``` > ⚑ Requires `--enable-shell`. `rm` and `mv` additionally require `--enable-delete`. --- ## Architecture: Virtual Filesystem (VFS) All tool operations are mediated through a **Virtual Filesystem (VFS)** layer inspired by [FUSE](https://en.wikipedia.org/wiki/Filesystem_in_Userspace). ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ MCP Tool Handlers (read, write, list, stat, …) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ VirtualFS β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Inodes β”‚ β”‚ DirIndex β”‚ β”‚ Tombstonesβ”‚ β”‚ β”‚ β”‚ (metadata)β”‚ β”‚ (parents) β”‚ β”‚ (deleted) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ ⇣ Overlay (in-memory, persisted to CacheStore) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ CacheStore (Memory / Filesystem / Redis) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ StorageProvider (S3 / Azure Blob / GCS / Memory / SQL) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` | Operation | Resolution | |---|---| | **`get()`** | Cache hit β†’ return. Tombstoned β†’ throw. Else β†’ provider fallback. | | **`stat()`** | Inode overlay β†’ cached content size β†’ provider `headObject`. | | **`list()`** | Provider listing – tombstones + overlay dirIndex entries. | | **`put()`** | Cache set + markDirty + inode update + dirIndex. Immediate visibility. | | **`remove()`** | Provider deleteObject + cache evict + tombstone. Immediate invisibility. | VFS metadata is persisted to the CacheStore under `__vfs__/*` keys. On startup, `VirtualFS.hydrate()` restores state; corrupted data is silently discarded. --- ## Caching | Backend | Flag | Notes | |---|---|---| | **Memory** (default) | `--cache-store memory` | In-process, fast, not shared, not persistent. | | **Filesystem** | `--cache-store fs --cache-dir ` | Survives restarts. | | **Redis** | `--cache-store redis` | Shared, persistent. Requires `ioredis` peer dep. `REDIS_URL` env var. Use `rediss://` for TLS. | **Writes** land in cache immediately (marked dirty), flushed after debounce window (default: 2s). **Graceful shutdown** flushes all dirty entries before exit. **Pass-through mode** (`--no-cache`) sends every operation directly to the provider. > **⚠️ Redis TLS:** The server warns at startup when `REDIS_URL` uses unencrypted `redis://`. Use `rediss://` for TLS-encrypted connections in production. ### Custom CA Certificates Use `--ca-file ` to supply a PEM CA bundle for S3-compatible endpoints (MinIO, RustFS) and Redis running with a private CA: ```bash # S3-compatible with self-signed CA cloud-fs-mcp s3 s3://my-bucket \ --endpoint https://minio.internal:9000 \ --ca-file /etc/ssl/certs/my-ca.pem # Redis with private CA REDIS_URL=rediss://redis.internal:6380 \ cloud-fs-mcp s3 s3://my-bucket --cache-store redis \ --ca-file /etc/ssl/certs/my-ca.pem ``` For runtime-wide CA trust (all providers + Redis), use the standard `NODE_EXTRA_CA_CERTS` env var instead: ```bash NODE_EXTRA_CA_CERTS=/etc/ssl/certs/my-ca.pem cloud-fs-mcp s3 s3://my-bucket ``` --- ## Programmatic Usage (npm library) ```ts import { createMcpServer, VirtualFS, MemoryStore, S3Provider, parseUri, } from "@nogoo9/mcp-server-cloud-fs"; const roots = [parseUri("s3://my-bucket")]; const provider = new S3Provider({ region: "us-east-1" }); const cache = new MemoryStore(provider, { ttlMs: 60_000, syncDebounceMs: 2000 }); const vfs = new VirtualFS(provider, cache); await vfs.hydrate(); const server = createMcpServer({ vfs, roots }); ``` ### Exported API | Export | Description | |---|---| | `createMcpServer(ctx)` | Create a configured MCP server instance | | `executeShell(command, ctx)` | Run POSIX-like commands against the VFS | | `VirtualFS` | FUSE-inspired write-back overlay | | `MemoryStore`, `FilesystemStore`, `createRedisStore`, `PassThroughCache` | Cache backends | | `S3Provider`, `AzureProvider`, `GcsProvider`, `MemoryProvider`, `SqliteProvider`, `MultiProvider` | Storage providers | | `CloudError`, `CloudErrorCode` | Structured cloud-aware error handling | | `checkHealth`, `formatHealthReport` | Connection health-check utilities | | `AuditLogger`, `StderrAuditSink`, `FileAuditSink` | Tool invocation audit logging | | `parseUri`, `toCacheKey`, `resolveToolPath` | Path utilities | | `ShellContext`, `ShellCommandHandler` | Shell extension types | --- ## MCP Inspector ```bash # Quick demo (no credentials needed) bun run inspect:memory # Custom configuration bun run inspect -- s3 s3://my-bucket --region us-east-1 --enable-shell ``` --- ## MCP App: Interactive Shell (xterm.js) Build the app: `bun run build:app` β†’ outputs `dist/app/shell-app.html`. Catppuccin Mocha theme, command history, auto-resize. Renders inside compatible MCP hosts (Claude Desktop) via the MCP Apps extension. --- ## Development & Testing See [CONTRIBUTING.md](CONTRIBUTING.md) for full setup instructions. **AI agent skills** are installed separately after cloning via `bun x skills add semgrep/skills` β€” see [CONTRIBUTING.md](CONTRIBUTING.md#ai-agent-skills) for details. ### Test tiers | Tier | Command | Infra? | |---|---|---| | **Unit** | `bun run test` | No | | **E2E (HTTP)** | `bun run test:e2e:http` | No | | **E2E (Infra)** | `bun run test:e2e:infra` | Docker | | **Integration** | `bun run test:integration` | Docker | | **All** | `bun run test:all` | Docker | ### CI pipeline The CI workflow runs on every push/PR: 1. **ci** job: lint β†’ typecheck β†’ unit tests (with coverage) β†’ HTTP E2E β†’ build 2. **e2e** job: full E2E with Docker Compose (MinIO, Redis) HTTP E2E tests use the in-memory provider and need zero infrastructure, making them fast and reliable for every CI run. --- ## AI Agent Skill ![cloud-fs skill in action](docs/public/images/cloud-fs-claude-demo.svg) The `skills/cloud-fs` directory contains an installable AI agent skill that teaches coding assistants (Claude Code, Gemini CLI, etc.) how to use cloud-fs as a POSIX-like virtual filesystem. Install it to give your assistant fluency with cloud storage commands. ### What it provides - **MCP mode auto-detection** β€” recognizes `mcp__cloud-fs__*` tools and maps user intents to the right tool calls - **Bootstrap flow** β€” walks the user through first-time setup when the MCP server isn't configured yet - **POSIX-to-MCP mapping** β€” translates shell commands (`ls`, `cat`, `grep`, `find`, `cp`, etc.) into the correct MCP tool calls - **Provider credential reference** β€” AWS, Azure, GCS, MinIO/RustFS, SQLite setup guidance - **`.mcp.json` persistence** β€” offers to save provider configuration so future sessions are pre-wired ### Installation ```bash # Claude Code claude mcp add-skill nogoo9/mcp-server-cloud-fs # skills.sh npx skills add nogoo9/mcp-server-cloud-fs # or with bun instead bun x skills add nogoo9/mcp-server-cloud-fs ``` See [CONTRIBUTING.md](CONTRIBUTING.md#ai-agent-skills) for details on the skill system. --- ## Documentation Full documentation is available at **[nogoo9.github.io/mcp-server-cloud-fs](https://nogoo9.github.io/mcp-server-cloud-fs/)**. - πŸ“– Versioned docs for each release (v0.4.0+) - πŸ”„ PR preview docs for open pull requests - πŸ” Full-text search, dark mode, responsive design - Built with [VitePress](https://vitepress.dev/) To run the documentation site locally: ```bash bun run docs:dev # start dev server at http://localhost:5173 bun run docs:build # build static site bun run docs:preview # preview production build ``` --- ## License [PolyForm Shield 1.0.0](LICENSE). Free for any non-competitive use. --- Built with [![Claude](https://img.shields.io/badge/Claude-d97706?logo=anthropic&logoColor=white)](https://claude.ai) [![Gemini](https://img.shields.io/badge/Gemini-8E75FF?logo=googlegemini&logoColor=white)](https://gemini.google.com) [![Antigravity](https://img.shields.io/badge/Antigravity-4285F4?logo=google&logoColor=white)](https://antigravity.google/)